Kaynağa Gözat

✨ add pretty legends

jmacura 4 yıl önce
ebeveyn
işleme
7cfd8a5beb

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

@@ -0,0 +1,91 @@
+import hsv2rgb from 'hsv2rgb';
+import {Injectable} from '@angular/core';
+
+@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 {
+    return [
+      {color: this.perc2color(0), name: '0 %'},
+      {color: this.perc2color(0.25), name: '25 %'},
+      {color: this.perc2color(0.5), name: '50 %'},
+      {color: this.perc2color(0.75), name: '75 %'},
+      {color: this.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);
+  }
+
+  perc2color = (perc: number): string => {
+    perc = perc * 100;
+    let r;
+    let g;
+    const b = 0;
+    if (perc < 50) {
+      r = 255;
+      g = Math.round(5.1 * perc);
+    } else {
+      g = 255;
+      r = Math.round(510 - 5.1 * perc);
+    }
+    // eslint-disable-next-line @typescript-eslint/no-unused-vars
+    const h = r * 0x10000 + g * 0x100 + b * 0x1;
+    return `rgba(${r}, ${g}, ${b}, 0.7)`;
+  };
+
+  /**
+   * 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<{
+  color: string;
+  name: string;
+}>;

+ 3 - 1
src/adjuster/adjuster.module.ts

@@ -12,6 +12,7 @@ import {AdjusterLoaderComponent} from './adjuster-loader.component';
 import {AdjusterService} from './adjuster.service';
 import {AttractivenessClustersService} from './attractiveness-clusters.service';
 import {AttractivenessIndexService} from './attractiveness-index.service';
+import { AdjusterLegendService } from './adjuster-legend.service';
 
 @NgModule({
   schemas: [CUSTOM_ELEMENTS_SCHEMA],
@@ -28,8 +29,9 @@ import {AttractivenessIndexService} from './attractiveness-index.service';
   providers: [
     AttractivenessClustersService,
     AttractivenessIndexService,
-    AdjusterService,
     AdjusterEventService,
+    AdjusterLegendService,
+    AdjusterService,
   ],
 })
 export class AdjusterModule {}

+ 7 - 3
src/adjuster/adjuster.service.ts

@@ -8,9 +8,11 @@ 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 {AdjusterLoaderComponent} from './adjuster-loader.component';
 import {AttractivenessClustersService} from './attractiveness-clusters.service';
 import {AttractivenessIndexService} from './attractiveness-index.service';
+import {nuts} from '../nuts';
 
 @Injectable({providedIn: 'root'})
 export class AdjusterService {
@@ -36,6 +38,7 @@ export class AdjusterService {
 
   constructor(
     public adjusterEventService: AdjusterEventService,
+    public adjusterLegendService: AdjusterLegendService,
     public attractivenessIndexService: AttractivenessIndexService,
     public attractivenessClustersService: AttractivenessClustersService,
     public hsDialogContainerService: HsDialogContainerService,
@@ -159,9 +162,6 @@ export class AdjusterService {
         }*/
         //for (const method of this.methods) {
         this.attractivenessClustersService.processClusters(codeRecordRelations);
-        //TODO: method.layer.getSource().legend_categories = this.adjusterLegendService.createClusterLegend(
-        //  this.numberOfClusters
-        //);
         //}
         console.timeEnd('forEach-Cluster');
         /*let max = 0;
@@ -177,6 +177,10 @@ export class AdjusterService {
         this.attractivity.forEach((a) => {
           this.nutsCodeRecordRelations[a.code] = a;
         });*/
+        // Fake the legend
+        nuts.nuts3ClustersSource.legend_categories = this.adjusterLegendService.createClusterLegend(
+          this.numberOfClusters
+        );
         this._clustersLoaded = true;
         this._clusteringInProcess = false;
         this.adjusterEventService.loaded.next({

+ 23 - 61
src/app.service.ts

@@ -17,26 +17,12 @@ import {HsSidebarService} from 'hslayers-ng/components/sidebar/sidebar.service';
 
 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 {nuts} from './nuts';
 
 @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',
-  ];
   nuts2style = new Style({
     stroke: new Stroke({
       color: '#000000',
@@ -64,13 +50,14 @@ export class AppService {
     style: this.generateStyle(this.adjusterService.method),
     title: 'NUTS3 regions: Clusters',
   });
+  pilotsColor = 'rgba(29, 148, 29, 0.2)';
   pilotsStyle = new Style({
     stroke: new Stroke({
       color: '#1d941d',
       width: 1.5,
     }),
     fill: new Fill({
-      color: 'rgba(29, 148, 29, 0.2)',
+      color: this.pilotsColor,
     }),
   });
   pilotRegions = new VectorLayer({
@@ -87,6 +74,7 @@ export class AppService {
   constructor(
     public adjusterService: AdjusterService,
     public adjusterEventService: AdjusterEventService,
+    public adjusterLegendService: AdjusterLegendService,
     public hsConfig: HsConfig,
     public hsEventBus: HsEventBusService,
     public hsLanguageService: HsLanguageService,
@@ -116,12 +104,13 @@ export class AppService {
         },
       ],
     });
-    // For debugging only
+    /* For debugging only */
     /*this.hsEventBus.olMapLoads.subscribe((map) => {
       map.on('click', this.debugMe);
     });*/
     this.nuts3IndexLayer.set('editable', false);
     this.nuts3IndexLayer.set('queryable', false);
+    this.nuts3IndexLayer.getSource().legend_categories = this.adjusterLegendService.createIndexLegend();
     this.nuts3ClustersLayer.set('popUp', {
       attributes: [
         {attribute: 'CNTR_CODE', label: 'Country'},
@@ -151,12 +140,21 @@ export class AppService {
         },*/
       ],
     });
+    this.pilotRegions.getSource().legend_categories = [
+      {
+        color: this.pilotsColor,
+        name: 'Polirural pilot region',
+      },
+    ];
     this.pilotRegions.set('queryable', false);
     this.adjusterEventService.loaded.subscribe(({success}) => {
       if (success) {
-        this.colorPalette = this.generateRandomColorPalette(
-          adjusterService.clusters.length
+        this.adjusterLegendService.refreshColorPalette(
+          this.adjusterService.numberOfClusters
         );
+        /*this.colorPalette = this.generateRandomColorPalette(
+          adjusterService.clusters.length
+        );*/
       }
     });
     this.adjusterEventService.methodChanged.subscribe((method) => {
@@ -198,29 +196,6 @@ export class AppService {
   }
 
   /**
-   * 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 currently selected method
    * @returns {function} style function
@@ -240,7 +215,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',
@@ -268,7 +245,9 @@ export class AppService {
       return [
         new Style({
           fill: new Fill({
-            color: this.perc2color(feature.get('aggregate')),
+            color: this.adjusterLegendService.perc2color(
+              feature.get('aggregate')
+            ),
           }),
           stroke: new Stroke({
             color: '#FFFFFF',
@@ -279,23 +258,6 @@ export class AppService {
     }
   };
 
-  private perc2color = (perc: number): string => {
-    perc = perc * 100;
-    let r;
-    let g;
-    const b = 0;
-    if (perc < 50) {
-      r = 255;
-      g = Math.round(5.1 * perc);
-    } else {
-      g = 255;
-      r = Math.round(510 - 5.1 * perc);
-    }
-    // eslint-disable-next-line @typescript-eslint/no-unused-vars
-    const h = r * 0x10000 + g * 0x100 + b * 0x1;
-    return `rgba(${r}, ${g}, ${b}, 0.7)`;
-  };
-
   private debugMe(e) {
     console.log(e);
     const feats = e.map.getFeaturesAtPixel(e.pixel);