Forráskód Böngészése

✨ add meaningful legend for clustered layers

+ refactor: move style-related stuff to adjuster-legend service
jmacura 4 éve
szülő
commit
5a33490f85

+ 53 - 0
src/adjuster/adjuster-legend.service.ts

@@ -1,9 +1,25 @@
+import hsv2rgb from 'hsv2rgb';
 import {Injectable} from '@angular/core';
 
 import {perc2color} from '../app.config';
 
 @Injectable({providedIn: 'root'})
 export class AdjusterLegendService {
+  /** https://colorbrewer2.org/?type=qualitative&scheme=Paired&n=12 */
+  colorPalette = [
+    '#a6cee3',
+    '#1f78b4',
+    '#b2df8a',
+    '#33a02c',
+    '#fb9a99',
+    '#e31a1c',
+    '#fdbf6f',
+    '#ff7f00',
+    '#cab2d6',
+    '#6a3d9a',
+    '#ffff99',
+    '#b15928',
+  ];
   constructor() {}
 
   createIndexLegend(): FakeLegend {
@@ -15,6 +31,43 @@ export class AdjusterLegendService {
       {color: perc2color(1), name: '100 %'},
     ];
   }
+
+  createClusterLegend(count: number): FakeLegend {
+    const legend = [];
+    let i = 0;
+    while (i < count) {
+      legend.push({color: this.colorPalette[i], name: `Cluster ${i + 1}`});
+      i++;
+    }
+    return legend;
+  }
+
+  refreshColorPalette(count: number): void {
+    this.colorPalette = this.generateRandomColorPalette(count);
+  }
+
+  /**
+   * https://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/
+   * @private
+   * @description Only generates random colors if the current palette does not provide enough colors for all the clusters
+   * @param {number} colorCount Number of colors to randomly generate
+   * @returns {Array<Array<number> | string>} Array of RGB colors
+   */
+  private generateRandomColorPalette(colorCount: number): Array<string> {
+    const palette = this.colorPalette;
+    const goldenRatioConjugate = 0.618033988749895;
+    let i = palette.length;
+    while (i < colorCount) {
+      let h = Math.random();
+      h += goldenRatioConjugate;
+      h %= 1;
+      h *= 360;
+      palette.push(hsv2rgb(h, 0.5, 0.95));
+      i++;
+    }
+    return palette;
+    //return `rgba(${r}, ${g}, ${b}, 0.7)`;
+  }
 }
 
 type FakeLegend = Array<{

+ 5 - 0
src/adjuster/adjuster.service.ts

@@ -11,6 +11,7 @@ import {HsUtilsService} from 'hslayers-ng/components/utils/utils.service';
 import attractivenessConfig from '../attractiveness.config.json';
 import clusteringMethods from '../data/clustering_methods.json';
 import {AdjusterEventService} from './adjuster-event.service';
+import {AdjusterLegendService} from './adjuster-legend.service';
 import {obce, obceIndexLayer, osmLayer} from '../app.config';
 
 @Injectable({providedIn: 'root'})
@@ -36,6 +37,7 @@ export class AdjusterService {
 
   constructor(
     public adjusterEventService: AdjusterEventService,
+    public adjusterLegendService: AdjusterLegendService,
     public hsConfig: HsConfig,
     public hsEventBus: HsEventBusService,
     public hsLayerMetadataService: HsLayerManagerMetadataService,
@@ -176,6 +178,9 @@ export class AdjusterService {
         console.time('forEach-Cluster');
         for (const method of this.methods) {
           this.processClusters(method, codeRecordRelations);
+          method.layer.getSource().legend_categories = this.adjusterLegendService.createClusterLegend(
+            this.numberOfClusters
+          );
         }
         // Another implementation of the loop above, yet too slow
         //const clusters = [];

+ 5 - 43
src/app.service.ts

@@ -22,22 +22,6 @@ import {krajeLayer, obceIndexLayer, okresyLayer} from './app.config';
 
 @Injectable({providedIn: 'root'})
 export class AppService {
-  // https://colorbrewer2.org/?type=qualitative&scheme=Paired&n=12
-  colorPalette = [
-    '#a6cee3',
-    '#1f78b4',
-    '#b2df8a',
-    '#33a02c',
-    '#fb9a99',
-    '#e31a1c',
-    '#fdbf6f',
-    '#ff7f00',
-    '#cab2d6',
-    '#6a3d9a',
-    '#ffff99',
-    '#b15928',
-  ];
-
   constructor(
     public adjusterService: AdjusterService,
     public adjusterEventService: AdjusterEventService,
@@ -52,7 +36,7 @@ export class AppService {
   ) {
     this.adjusterEventService.loaded.subscribe(({success}) => {
       if (success) {
-        this.colorPalette = this.generateRandomColorPalette(
+        this.adjusterLegendService.refreshColorPalette(
           this.adjusterService.numberOfClusters
         );
       }
@@ -109,36 +93,12 @@ export class AppService {
     // so they will display in correct order
     this.hsConfig.default_layers.push(obceIndexLayer);
     obceIndexLayer.on('featuresloadend', this.adjusterService.init());
-    const indexLegend = this.adjusterLegendService.createIndexLegend();
-    obceIndexLayer.getSource().legend_categories = indexLegend;
+    obceIndexLayer.getSource().legend_categories = this.adjusterLegendService.createIndexLegend();
     this.hsConfig.default_layers.push(okresyLayer);
     this.hsConfig.default_layers.push(krajeLayer);
   }
 
   /**
-   * https://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/
-   * @private
-   * @description Only generates random colors if the current palette does not provide enough colors for all the clusters
-   * @param {number} colorCount Number of colors to randomly generate
-   * @returns {Array<Array<number> | string>} Array of RGB colors
-   */
-  private generateRandomColorPalette(colorCount: number) {
-    const palette = this.colorPalette;
-    const goldenRatioConjugate = 0.618033988749895;
-    let i = palette.length;
-    while (i < colorCount) {
-      let h = Math.random();
-      h += goldenRatioConjugate;
-      h %= 1;
-      h *= 360;
-      palette.push(hsv2rgb(h, 0.5, 0.95));
-      i++;
-    }
-    return palette;
-    //return `rgba(${r}, ${g}, ${b}, 0.7)`;
-  }
-
-  /**
    * @description Function factory for generating style functions based on different clustering methods
    * @param {string} method selected method
    * @returns {function} style function
@@ -158,7 +118,9 @@ export class AppService {
       } else {
         return new Style({
           fill: new Fill({
-            color: this.colorPalette[feature.get(method) - 1],
+            color: this.adjusterLegendService.colorPalette[
+              feature.get(method) - 1
+            ],
           }),
           stroke: new Stroke({
             color: '#FFF',