Explorar o código

🐛 ensure layers are ready for calculations

fixes #8 (hopefully for good)
jmacura %!s(int64=4) %!d(string=hai) anos
pai
achega
dc95cdabe9

+ 2 - 0
src/adjuster/adjuster-event.service.ts

@@ -3,7 +3,9 @@ import {Subject} from 'rxjs';
 
 @Injectable({providedIn: 'root'})
 export class AdjusterEventService {
+  layerReady: Subject<{name: string}> = new Subject();
   loaded: Subject<{success: boolean; type: string; err?}> = new Subject();
+  loaderReady: Subject<void> = new Subject();
   methodChanged: Subject<string> = new Subject();
   constructor() {}
 }

+ 12 - 3
src/adjuster/adjuster-loader.component.ts

@@ -1,23 +1,32 @@
-import {Component, Input, ViewRef} from '@angular/core';
+import {AfterViewInit, Component, Input, ViewRef} from '@angular/core';
 
 import {HsDialogComponent} from 'hslayers-ng/components/layout/dialogs/dialog-component.interface';
 import {HsDialogContainerService} from 'hslayers-ng/components/layout/dialogs/dialog-container.service';
 
+import {AdjusterEventService} from './adjuster-event.service';
 import {AdjusterService} from './adjuster.service';
 
 @Component({
   selector: 'pra-adjuster-loader',
   template: require('./adjuster-loader.directive.html'),
 })
-export class AdjusterLoaderComponent implements HsDialogComponent {
+export class AdjusterLoaderComponent
+  implements HsDialogComponent, AfterViewInit {
   @Input() data;
   viewRef: ViewRef;
   constructor(
     public hsDialogContainerService: HsDialogContainerService,
-    public adjusterService: AdjusterService
+    public adjusterService: AdjusterService,
+    public adjusterEventService: AdjusterEventService
   ) {
     /*this.adjusterEventService.loaded.subscribe(() => {
       this.hsDialogContainerService.destroy(this);
     });*/
   }
+
+  ngAfterViewInit(): void {
+    // The next is necessary, otherwise the forkJoin() would hang up immediately
+    this.adjusterEventService.loaderReady.next();
+    this.adjusterEventService.loaderReady.complete();
+  }
 }

+ 2 - 2
src/adjuster/adjuster.component.ts

@@ -1,4 +1,4 @@
-import {Component, ViewRef} from '@angular/core';
+import {Component, OnInit, ViewRef} from '@angular/core';
 
 import {HsDialogContainerService} from 'hslayers-ng/components/layout/dialogs/dialog-container.service';
 import {HsLayoutService} from 'hslayers-ng/components/layout/layout.service';
@@ -12,7 +12,7 @@ import {AdjusterService} from './adjuster.service';
   selector: 'pra-adjuster',
   template: require('./adjuster.directive.html'),
 })
-export class AdjusterComponent implements HsPanelComponent {
+export class AdjusterComponent implements HsPanelComponent, OnInit {
   data: any;
   //descriptionVisible: boolean;
   errorMsg: string;

+ 30 - 8
src/adjuster/adjuster.service.ts

@@ -1,6 +1,7 @@
 import {HttpClient} from '@angular/common/http';
 import {Injectable} from '@angular/core';
 import {Vector as VectorLayer} from 'ol/layer';
+import {forkJoin} from 'rxjs';
 
 import {HsConfig} from 'hslayers-ng/config.service';
 import {HsEventBusService} from 'hslayers-ng/components/core/event-bus.service';
@@ -27,12 +28,14 @@ export class AdjusterService {
   /** Used in the UI as a selector */
   allowIndex = true;
   factors = [];
-  numberOfClusters: number;
+  layersReady = new Set();
   //method: string;
   methods: Array<MethodDescription>;
+  numberOfClusters: number;
   private _clusteringInProcess: boolean;
   private _clustersLoaded: boolean;
-  private _loadInProcess: boolean;
+  /** Once instantiated, the load is definitely in process */
+  private _loadInProcess = true;
   private _raiInProcess: boolean;
 
   constructor(
@@ -45,7 +48,7 @@ export class AdjusterService {
     public hsUtilsService: HsUtilsService,
     public $http: HttpClient
   ) {
-    // First safely set configurable properties
+    /* First safely set configurable properties */
     this.allowedClusteringMethods =
       attractivenessConfig?.allowedClusteringMethods ?? [];
     this.initialWeights = attractivenessConfig?.initialWeights ?? {};
@@ -58,6 +61,25 @@ export class AdjusterService {
       this.allowedClusteringMethods.includes(m.codename)
     );
     this.numberOfClusters = 9;
+
+    /* 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 and also the loader component are ready */
+    forkJoin({
+      lyr: this.adjusterEventService.layerReady,
+      load: this.adjusterEventService.loaderReady,
+    }).subscribe(() => {
+      console.log('Oll layers Korekt! Initializing adjuster...');
+      this.init();
+    });
   }
 
   /**
@@ -89,7 +111,7 @@ export class AdjusterService {
       })
       .toPromise()
       .then((attractivenessData: any[]) => {
-        // Spread the 'aggregate' value between 0 and 1
+        /* Spread the 'aggregate' value between 0 and 1 */
         const min = attractivenessData.reduce((a, b) =>
           a.aggregate < b.aggregate ? a : b
         ).aggregate;
@@ -102,8 +124,8 @@ export class AdjusterService {
           a.aggregate *= coefficient;
           a.aggregate += constant;
         });
-        // Store relation between region and its data in a hash-table-like structure
-        // more memory consuming, but faster then find()
+        /* Store relation between region and its data in a hash-table-like structure
+         * More memory consuming, but faster then find() */
         const codeRecordRelations = {};
         attractivenessData.forEach((a) => {
           codeRecordRelations[a.code] = a;
@@ -147,8 +169,8 @@ export class AdjusterService {
       .toPromise()
       .then((data: any) => {
         const clusterData = data.response;
-        // Store relation between region and its data in a hash-table-like structure
-        // more memory consuming, but much faster then find()
+        /* Store relation between region and its data in a hash-table-like structure
+         * more memory consuming, but much faster then find() */
         const codeRecordRelations = {};
         clusterData.forEach((c) => {
           codeRecordRelations[c.lau2] = c;

+ 1 - 0
src/app.config.ts

@@ -294,6 +294,7 @@ export const krajeLayer = new VectorLayer({
 });
 
 export const AppConfig = {
+  useProxy: false,
   //proxyPrefix: '../8085/',
   geonamesUser: env.geonamesUser,
   default_layers: [cartodbLayer, osmLayer, mcr500Layer, ortofotoLayer],

+ 32 - 4
src/app.service.ts

@@ -18,7 +18,7 @@ import {AdjusterComponent} from './adjuster/adjuster.component';
 import {AdjusterEventService} from './adjuster/adjuster-event.service';
 import {AdjusterLegendService} from './adjuster/adjuster-legend.service';
 import {AdjusterService} from './adjuster/adjuster.service';
-import {krajeLayer, obceIndexLayer, okresyLayer, masLayer} from './app.config';
+import {krajeLayer, masLayer, obceIndexLayer, okresyLayer} from './app.config';
 
 @Injectable({providedIn: 'root'})
 export class AppService {
@@ -34,6 +34,7 @@ export class AppService {
     public hsPanelContainerService: HsPanelContainerService,
     public hsSidebarService: HsSidebarService
   ) {
+    this.prepareLayers();
     this.adjusterEventService.loaded.subscribe(({success}) => {
       if (success) {
         this.adjusterLegendService.refreshColorPalette(
@@ -44,7 +45,12 @@ export class AppService {
     this.hsEventBus.layoutLoads.subscribe(() => {
       this.init();
     });
-    this.prepareLayers();
+    this.hsEventBus.olMapLoads.subscribe(() => {
+      this.ensureLayerIsLoaded(obceIndexLayer);
+      for (const method of this.adjusterService.methods) {
+        this.ensureLayerIsLoaded(method.layer);
+      }
+    });
   }
 
   init(): void {
@@ -87,13 +93,21 @@ export class AppService {
           ],
         },
       });
+      method.layer
+        .getSource()
+        .on('featuresloadend', () =>
+          this.adjusterEventService.layerReady.next({name: method.codename})
+        );
       this.hsConfig.default_layers.push(method.layer);
     }
     // obceIndexLayer, okresyLayer and krajeLayer must be pushed in this order
     // so they will display in correct order
     this.hsConfig.default_layers.push(obceIndexLayer);
-    //There is something rotten in these lines. .getSource() makes thing even more unpredictable
-    obceIndexLayer.on('featuresloadend', this.adjusterService.init());
+    obceIndexLayer
+      .getSource()
+      .on('featuresloadend', () =>
+        this.adjusterEventService.layerReady.next({name: 'index'})
+      );
     obceIndexLayer.getSource().legend_categories = this.adjusterLegendService.createIndexLegend();
     this.hsConfig.default_layers.push(masLayer);
     this.hsConfig.default_layers.push(okresyLayer);
@@ -101,6 +115,20 @@ export class AppService {
   }
 
   /**
+   * Layer must be turned visible so it is loaded into the map.
+   * This method ensures that layers critical for further computations
+   * are for at least a moment turned visible and thus loaded properly.
+   * @param {VectorLayer} layer One of layers used to calculate index or clusters
+   */
+  ensureLayerIsLoaded(layer: VectorLayer): void {
+    const layerVisible = layer.getVisible();
+    if (!layerVisible) {
+      layer.setVisible(true);
+      setTimeout(() => layer.setVisible(false), 10);
+    }
+  }
+
+  /**
    * @description Function factory for generating style functions based on different clustering methods
    * @param {string} method selected method
    * @returns {function} style function