|
@@ -1,17 +1,24 @@
|
|
|
|
|
+/* eslint-disable no-restricted-syntax */
|
|
|
|
|
+/* eslint-disable no-console */
|
|
|
import {HttpClient} from '@angular/common/http';
|
|
import {HttpClient} from '@angular/common/http';
|
|
|
import {Injectable} from '@angular/core';
|
|
import {Injectable} from '@angular/core';
|
|
|
-import {Vector as VectorLayer} from 'ol/layer';
|
|
|
|
|
|
|
+import {forkJoin} from 'rxjs';
|
|
|
|
|
|
|
|
-import {HsDialogContainerService} from 'hslayers-ng/components/layout/dialogs/dialog-container.service';
|
|
|
|
|
-import {HsUtilsService} from 'hslayers-ng/components/utils/utils.service';
|
|
|
|
|
|
|
+import {HsDialogContainerService} from 'hslayers-ng';
|
|
|
|
|
+import {HsToastService} from 'hslayers-ng';
|
|
|
|
|
+import {HsUtilsService} from 'hslayers-ng';
|
|
|
|
|
|
|
|
import attractivenessConfig from '../attractiveness.config.json';
|
|
import attractivenessConfig from '../attractiveness.config.json';
|
|
|
import clusteringMethods from '../data/clustering_methods.json';
|
|
import clusteringMethods from '../data/clustering_methods.json';
|
|
|
import {AdjusterEventService} from './adjuster-event.service';
|
|
import {AdjusterEventService} from './adjuster-event.service';
|
|
|
import {AdjusterLegendService} from './adjuster-legend.service';
|
|
import {AdjusterLegendService} from './adjuster-legend.service';
|
|
|
-import {AdjusterLoaderComponent} from './adjuster-loader.component';
|
|
|
|
|
-import {AttractivenessClustersService} from './attractiveness-clusters.service';
|
|
|
|
|
|
|
+import {AdjusterPresetsService, Factor} from './adjuster-presets.service';
|
|
|
|
|
+import {
|
|
|
|
|
+ AttractivenessClustersService,
|
|
|
|
|
+ MethodDescription,
|
|
|
|
|
+} from './attractiveness-clusters.service';
|
|
|
import {AttractivenessIndexService} from './attractiveness-index.service';
|
|
import {AttractivenessIndexService} from './attractiveness-index.service';
|
|
|
|
|
+import {RDFSubject} from './ontology.model';
|
|
|
import {nuts} from '../nuts';
|
|
import {nuts} from '../nuts';
|
|
|
|
|
|
|
|
@Injectable({providedIn: 'root'})
|
|
@Injectable({providedIn: 'root'})
|
|
@@ -26,22 +33,26 @@ export class AdjusterService {
|
|
|
allowClusters = true;
|
|
allowClusters = true;
|
|
|
/** Used in the UI as a selector */
|
|
/** Used in the UI as a selector */
|
|
|
allowIndex = true;
|
|
allowIndex = true;
|
|
|
- factors = [];
|
|
|
|
|
- clusters = [];
|
|
|
|
|
- numberOfClusters: number;
|
|
|
|
|
- method: string;
|
|
|
|
|
|
|
+ factors: Array<Factor> = [];
|
|
|
|
|
+ layersReady = new Set();
|
|
|
|
|
+ //clusters = [];
|
|
|
|
|
+ //method: string;
|
|
|
methods: Array<MethodDescription>;
|
|
methods: Array<MethodDescription>;
|
|
|
|
|
+ numberOfClusters: number;
|
|
|
private _clusteringInProcess: boolean;
|
|
private _clusteringInProcess: boolean;
|
|
|
private _clustersLoaded: boolean;
|
|
private _clustersLoaded: boolean;
|
|
|
- private _loadInProcess: boolean;
|
|
|
|
|
|
|
+ /** Once instantiated, the load is definitely in process */
|
|
|
|
|
+ private _loadInProcess = true;
|
|
|
private _raiInProcess: boolean;
|
|
private _raiInProcess: boolean;
|
|
|
|
|
|
|
|
constructor(
|
|
constructor(
|
|
|
public adjusterEventService: AdjusterEventService,
|
|
public adjusterEventService: AdjusterEventService,
|
|
|
public adjusterLegendService: AdjusterLegendService,
|
|
public adjusterLegendService: AdjusterLegendService,
|
|
|
|
|
+ public adjusterPresetsService: AdjusterPresetsService,
|
|
|
public attractivenessIndexService: AttractivenessIndexService,
|
|
public attractivenessIndexService: AttractivenessIndexService,
|
|
|
public attractivenessClustersService: AttractivenessClustersService,
|
|
public attractivenessClustersService: AttractivenessClustersService,
|
|
|
public hsDialogContainerService: HsDialogContainerService,
|
|
public hsDialogContainerService: HsDialogContainerService,
|
|
|
|
|
+ public hsToastService: HsToastService,
|
|
|
public hsUtilsService: HsUtilsService,
|
|
public hsUtilsService: HsUtilsService,
|
|
|
public httpClient: HttpClient
|
|
public httpClient: HttpClient
|
|
|
) {
|
|
) {
|
|
@@ -49,15 +60,81 @@ export class AdjusterService {
|
|
|
this.allowedClusteringMethods =
|
|
this.allowedClusteringMethods =
|
|
|
attractivenessConfig?.allowedClusteringMethods ?? [];
|
|
attractivenessConfig?.allowedClusteringMethods ?? [];
|
|
|
this.initialWeights = attractivenessConfig?.initialWeights ?? {};
|
|
this.initialWeights = attractivenessConfig?.initialWeights ?? {};
|
|
|
- this.serviceBaseUrl =
|
|
|
|
|
- attractivenessConfig?.serviceBaseUrl ??
|
|
|
|
|
- 'https://publish.lesprojekt.cz/nodejs/';
|
|
|
|
|
- // 'https://jmacura.ml/ws/' // 'http://localhost:3000/'
|
|
|
|
|
|
|
+ this.serviceBaseUrl = this.adjusterPresetsService.serviceBaseUrl;
|
|
|
|
|
+
|
|
|
this.methods = clusteringMethods.filter((m) =>
|
|
this.methods = clusteringMethods.filter((m) =>
|
|
|
this.allowedClusteringMethods.includes(m.codename)
|
|
this.allowedClusteringMethods.includes(m.codename)
|
|
|
);
|
|
);
|
|
|
- this.method = 'haclustwd2'; //TODO: set in config/or not use at all?
|
|
|
|
|
this.numberOfClusters = 12;
|
|
this.numberOfClusters = 12;
|
|
|
|
|
+
|
|
|
|
|
+ /* Get the ontology file from the service */
|
|
|
|
|
+ this.loadOntology();
|
|
|
|
|
+
|
|
|
|
|
+ /* Wait for all layers to be ready */
|
|
|
|
|
+ this.adjusterEventService.layerReady.subscribe(({name}) => {
|
|
|
|
|
+ console.log(name + ' ready!');
|
|
|
|
|
+ this.layersReady.add(name);
|
|
|
|
|
+ /* Layers for each method + layer for index are ready */
|
|
|
|
|
+ if (this.layersReady.size == this.methods.length + 1) {
|
|
|
|
|
+ this.adjusterEventService.layerReady.complete();
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ /* Ensure that all layers, the loader component and the presets from ontology are ready */
|
|
|
|
|
+ forkJoin({
|
|
|
|
|
+ lyr: this.adjusterEventService.layerReady,
|
|
|
|
|
+ load: this.adjusterEventService.loaderReady,
|
|
|
|
|
+ ont: this.adjusterEventService.ontologyLoads,
|
|
|
|
|
+ }).subscribe(() => {
|
|
|
|
|
+ console.log('Oll layers Korekt! Initializing adjuster...');
|
|
|
|
|
+ //this._loadInProcess = false;
|
|
|
|
|
+ this.init();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ /* Listen to schema changes so the factors can be re-arranged in the view */
|
|
|
|
|
+ this.adjusterPresetsService.schemaChanges.subscribe((newSchema) => {
|
|
|
|
|
+ const orphanedDatasets = [];
|
|
|
|
|
+ this.factors = newSchema.groups.map((group) => {
|
|
|
|
|
+ const datasets = this.adjusterPresetsService.getGroupDatasets(group.id);
|
|
|
|
|
+ if (datasets.length < 3) {
|
|
|
|
|
+ orphanedDatasets.push(...datasets);
|
|
|
|
|
+ }
|
|
|
|
|
+ return {
|
|
|
|
|
+ id: group.id,
|
|
|
|
|
+ labels: group.labels,
|
|
|
|
|
+ weight: this.resetFactorWeights(group.id),
|
|
|
|
|
+ datasets: this.adjusterPresetsService.getGroupDatasets(group.id),
|
|
|
|
|
+ };
|
|
|
|
|
+ });
|
|
|
|
|
+ this.factors = this.factors.filter(
|
|
|
|
|
+ (factor) => factor.datasets.length >= 3
|
|
|
|
|
+ );
|
|
|
|
|
+ this.factors.push({
|
|
|
|
|
+ id: 'others',
|
|
|
|
|
+ labels: [
|
|
|
|
|
+ {'@value': 'Ostatní', '@language': 'cs'},
|
|
|
|
|
+ {'@value': 'Others', '@language': 'en'},
|
|
|
|
|
+ ],
|
|
|
|
|
+ weight: this.resetFactorWeights('others'),
|
|
|
|
|
+ datasets: orphanedDatasets,
|
|
|
|
|
+ });
|
|
|
|
|
+ this.adjusterLegendService.updateIndexLayerPopUps(this.factors);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ /* Listen to problem changes so the datasets can be turned on/off */
|
|
|
|
|
+ this.adjusterPresetsService.problemChanges.subscribe((newProblem) => {
|
|
|
|
|
+ if (!newProblem) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ for (const factor of this.factors) {
|
|
|
|
|
+ for (const dataset of factor.datasets) {
|
|
|
|
|
+ dataset.included = false;
|
|
|
|
|
+ if (newProblem.requiredDatasets.includes(dataset.id)) {
|
|
|
|
|
+ dataset.included = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -81,16 +158,20 @@ export class AdjusterService {
|
|
|
.post(this.serviceBaseUrl + 'eu/scores/', {
|
|
.post(this.serviceBaseUrl + 'eu/scores/', {
|
|
|
factors: this.factors.map((f) => {
|
|
factors: this.factors.map((f) => {
|
|
|
return {
|
|
return {
|
|
|
- factor: f.name,
|
|
|
|
|
|
|
+ factor: f.id,
|
|
|
weight: f.weight,
|
|
weight: f.weight,
|
|
|
datasets: f.datasets
|
|
datasets: f.datasets
|
|
|
.filter((ds) => ds.included)
|
|
.filter((ds) => ds.included)
|
|
|
- .map((ds) => ds.name),
|
|
|
|
|
|
|
+ .map((ds) => ds.id),
|
|
|
};
|
|
};
|
|
|
}),
|
|
}),
|
|
|
})
|
|
})
|
|
|
.toPromise();
|
|
.toPromise();
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
|
|
+ this.hsToastService.createToastPopupMessage(
|
|
|
|
|
+ 'Error loading data',
|
|
|
|
|
+ `Error obtaining data from ${this.serviceBaseUrl}.`
|
|
|
|
|
+ );
|
|
|
console.warn(`Error obtaining data from ${this.serviceBaseUrl}.`);
|
|
console.warn(`Error obtaining data from ${this.serviceBaseUrl}.`);
|
|
|
console.log(error);
|
|
console.log(error);
|
|
|
this._raiInProcess = false;
|
|
this._raiInProcess = false;
|
|
@@ -120,7 +201,10 @@ export class AdjusterService {
|
|
|
codeRecordRelations[a.code.toUpperCase()] = a;
|
|
codeRecordRelations[a.code.toUpperCase()] = a;
|
|
|
});
|
|
});
|
|
|
console.time('forEach-Index');
|
|
console.time('forEach-Index');
|
|
|
- this.attractivenessIndexService.processIndex(codeRecordRelations);
|
|
|
|
|
|
|
+ this.attractivenessIndexService.processIndex(
|
|
|
|
|
+ nuts.nuts3IndexSource,
|
|
|
|
|
+ codeRecordRelations
|
|
|
|
|
+ );
|
|
|
console.timeEnd('forEach-Index');
|
|
console.timeEnd('forEach-Index');
|
|
|
this._raiInProcess = false;
|
|
this._raiInProcess = false;
|
|
|
this.adjusterEventService.loaded.next({
|
|
this.adjusterEventService.loaded.next({
|
|
@@ -131,23 +215,34 @@ export class AdjusterService {
|
|
|
|
|
|
|
|
async calculateClusters(): Promise<void> {
|
|
async calculateClusters(): Promise<void> {
|
|
|
this._clusteringInProcess = true;
|
|
this._clusteringInProcess = true;
|
|
|
|
|
+ /* Pre-process the API params */
|
|
|
|
|
+ const params = [];
|
|
|
|
|
+ for (const factor of this.factors) {
|
|
|
|
|
+ for (const dataset of factor.datasets) {
|
|
|
|
|
+ if (!dataset.included) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ const flattenedDataset = {
|
|
|
|
|
+ id: dataset.id.split('/').slice(-1).pop(), //We do not need full URIs as the URNs are unique across the ontology
|
|
|
|
|
+ factor: factor.id.split('/').slice(-1).pop(), //We do not need full URIs as the URNs are unique across the ontology
|
|
|
|
|
+ weight: factor.weight,
|
|
|
|
|
+ };
|
|
|
|
|
+ params.push(flattenedDataset);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
let data: any;
|
|
let data: any;
|
|
|
try {
|
|
try {
|
|
|
data = await this.httpClient
|
|
data = await this.httpClient
|
|
|
.post(this.serviceBaseUrl + 'eu/clusters/', {
|
|
.post(this.serviceBaseUrl + 'eu/clusters/', {
|
|
|
numberOfClusters: this.numberOfClusters,
|
|
numberOfClusters: this.numberOfClusters,
|
|
|
- factors: this.factors.map((f) => {
|
|
|
|
|
- return {
|
|
|
|
|
- factor: f.name,
|
|
|
|
|
- weight: f.weight,
|
|
|
|
|
- datasets: f.datasets
|
|
|
|
|
- .filter((ds) => ds.included)
|
|
|
|
|
- .map((ds) => ds.name),
|
|
|
|
|
- };
|
|
|
|
|
- }),
|
|
|
|
|
|
|
+ datasets: params,
|
|
|
})
|
|
})
|
|
|
.toPromise();
|
|
.toPromise();
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
|
|
+ this.hsToastService.createToastPopupMessage(
|
|
|
|
|
+ 'Error loading data',
|
|
|
|
|
+ `Error obtaining data from ${this.serviceBaseUrl}.`
|
|
|
|
|
+ );
|
|
|
console.warn(`Error obtaining data from ${this.serviceBaseUrl}.`);
|
|
console.warn(`Error obtaining data from ${this.serviceBaseUrl}.`);
|
|
|
console.log(error);
|
|
console.log(error);
|
|
|
this._clusteringInProcess = false;
|
|
this._clusteringInProcess = false;
|
|
@@ -171,9 +266,15 @@ export class AdjusterService {
|
|
|
clusters.push(region[this.method]);
|
|
clusters.push(region[this.method]);
|
|
|
}
|
|
}
|
|
|
}*/
|
|
}*/
|
|
|
- //for (const method of this.methods) {
|
|
|
|
|
- this.attractivenessClustersService.processClusters(codeRecordRelations);
|
|
|
|
|
- //}
|
|
|
|
|
|
|
+ for (const method of this.methods) {
|
|
|
|
|
+ this.attractivenessClustersService.processClusters(
|
|
|
|
|
+ method,
|
|
|
|
|
+ codeRecordRelations
|
|
|
|
|
+ );
|
|
|
|
|
+ // Fake the legend
|
|
|
|
|
+ (method.layer.getSource() as any).legend_categories =
|
|
|
|
|
+ this.adjusterLegendService.createClusterLegend(this.numberOfClusters);
|
|
|
|
|
+ }
|
|
|
console.timeEnd('forEach-Cluster');
|
|
console.timeEnd('forEach-Cluster');
|
|
|
/*let max = 0;
|
|
/*let max = 0;
|
|
|
this.clusters.forEach((a) => {
|
|
this.clusters.forEach((a) => {
|
|
@@ -189,9 +290,8 @@ export class AdjusterService {
|
|
|
this.nutsCodeRecordRelations[a.code] = a;
|
|
this.nutsCodeRecordRelations[a.code] = a;
|
|
|
});*/
|
|
});*/
|
|
|
// Fake the legend
|
|
// Fake the legend
|
|
|
- nuts.nuts3ClustersSource.legend_categories = this.adjusterLegendService.createClusterLegend(
|
|
|
|
|
- this.numberOfClusters
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ //nuts.nuts3ClustersSource.legend_categories =
|
|
|
|
|
+ // this.adjusterLegendService.createClusterLegend(this.numberOfClusters);
|
|
|
this._clustersLoaded = true;
|
|
this._clustersLoaded = true;
|
|
|
this._clusteringInProcess = false;
|
|
this._clusteringInProcess = false;
|
|
|
this.adjusterEventService.loaded.next({
|
|
this.adjusterEventService.loaded.next({
|
|
@@ -201,6 +301,7 @@ export class AdjusterService {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
async init(): Promise<void> {
|
|
async init(): Promise<void> {
|
|
|
|
|
+ console.log('init');
|
|
|
this._loadInProcess = true;
|
|
this._loadInProcess = true;
|
|
|
let data: any;
|
|
let data: any;
|
|
|
try {
|
|
try {
|
|
@@ -208,6 +309,10 @@ export class AdjusterService {
|
|
|
.get(this.serviceBaseUrl + 'eu/datasets/')
|
|
.get(this.serviceBaseUrl + 'eu/datasets/')
|
|
|
.toPromise();
|
|
.toPromise();
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
|
|
+ this.hsToastService.createToastPopupMessage(
|
|
|
|
|
+ 'Error loading data',
|
|
|
|
|
+ `Web service at ${this.serviceBaseUrl} unavailable!`
|
|
|
|
|
+ );
|
|
|
console.warn(`Web service at ${this.serviceBaseUrl} unavailable!`);
|
|
console.warn(`Web service at ${this.serviceBaseUrl} unavailable!`);
|
|
|
console.log(error);
|
|
console.log(error);
|
|
|
this._loadInProcess = false;
|
|
this._loadInProcess = false;
|
|
@@ -216,7 +321,7 @@ export class AdjusterService {
|
|
|
err: error,
|
|
err: error,
|
|
|
});*/
|
|
});*/
|
|
|
}
|
|
}
|
|
|
- this.factors = data.map((dataset) => {
|
|
|
|
|
|
|
+ /*this.factors = data.map((dataset) => {
|
|
|
return {
|
|
return {
|
|
|
name: dataset.Factor,
|
|
name: dataset.Factor,
|
|
|
weight: this.initialWeights[dataset.Factor] ?? 1,
|
|
weight: this.initialWeights[dataset.Factor] ?? 1,
|
|
@@ -234,13 +339,38 @@ export class AdjusterService {
|
|
|
included: true,
|
|
included: true,
|
|
|
};
|
|
};
|
|
|
});
|
|
});
|
|
|
- });
|
|
|
|
|
|
|
+ });*/
|
|
|
|
|
+ console.log('init done, applying');
|
|
|
this._loadInProcess = false;
|
|
this._loadInProcess = false;
|
|
|
this.apply();
|
|
this.apply();
|
|
|
// In HSL 2.5, setting layer greyscale breaks the print() functionality
|
|
// In HSL 2.5, setting layer greyscale breaks the print() functionality
|
|
|
//this.hsLayerManagerService.setGreyscale(osmLayer);
|
|
//this.hsLayerManagerService.setGreyscale(osmLayer);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ async loadOntology() {
|
|
|
|
|
+ console.log('loading onto');
|
|
|
|
|
+ try {
|
|
|
|
|
+ const onto = await this.httpClient
|
|
|
|
|
+ .get<RDFSubject[]>(this.serviceBaseUrl + 'ontology/')
|
|
|
|
|
+ .toPromise();
|
|
|
|
|
+ this.adjusterEventService.ontologyLoads.next(onto);
|
|
|
|
|
+ this.adjusterEventService.ontologyLoads.complete();
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ this.hsToastService.createToastPopupMessage(
|
|
|
|
|
+ 'Error loading ontology',
|
|
|
|
|
+ `Web service at ${this.serviceBaseUrl} unavailable!`
|
|
|
|
|
+ );
|
|
|
|
|
+ console.warn(`Web service at ${this.serviceBaseUrl} unavailable!`);
|
|
|
|
|
+ console.log(error);
|
|
|
|
|
+ this._loadInProcess = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ console.log('onto loaded');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ resetFactorWeights(factorId: string): number {
|
|
|
|
|
+ return this.initialWeights[factorId] ?? 0.5;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
clustersLoaded(): boolean {
|
|
clustersLoaded(): boolean {
|
|
|
return this._clustersLoaded;
|
|
return this._clustersLoaded;
|
|
|
}
|
|
}
|
|
@@ -266,10 +396,3 @@ export class AdjusterService {
|
|
|
return this._raiInProcess;
|
|
return this._raiInProcess;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-type MethodDescription = {
|
|
|
|
|
- codename: string;
|
|
|
|
|
- layer?: VectorLayer;
|
|
|
|
|
- name: string;
|
|
|
|
|
- type: string;
|
|
|
|
|
-};
|
|
|