Browse Source

✨ add clustering calculation

jmacura 4 năm trước cách đây
mục cha
commit
0703c22275
2 tập tin đã thay đổi với 164 bổ sung7 xóa
  1. 92 7
      src/adjuster/adjuster.service.ts
  2. 72 0
      src/data/clustering_methods.json

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

@@ -1,18 +1,24 @@
 import {HttpClient} from '@angular/common/http';
 import {Injectable} from '@angular/core';
 
-import {HsDialogContainerService} from 'hslayers-ng/components/layout/dialogs/dialog-container.service';
 import {HsLayerManagerService} from 'hslayers-ng/components/layermanager';
 import {HsUtilsService} from 'hslayers-ng/components/utils/utils.service';
 
+import clusteringMethods from '../data/clustering_methods.json';
 import {obce, osmLayer} from '../app.config';
 
 @Injectable({providedIn: 'root'})
 export class AdjusterService {
   serviceBaseUrl: string;
-  //nutsCodeRecordRelations = {};
-  //attractivity;
-  factors: any = [];
+  factors = [];
+  clusters = [];
+  numberOfClusters;
+  method: string;
+  methods: Array<{
+    codename: string;
+    name: string;
+    type: string;
+  }>;
   private _clusteringInProcess: boolean;
   private _raiInProcess: boolean;
 
@@ -23,10 +29,13 @@ export class AdjusterService {
   ) {
     this.serviceBaseUrl =
       window.location.hostname === 'localhost'
-        ? 'https://jmacura.ml/ws/' // 'http://localhost:3000/'
+        ? 'http://localhost:3000/' // 'https://jmacura.ml/ws/'
         : 'https://publish.lesprojekt.cz/nodejs/';
-    this._raiInProcess = false;
-    this._clusteringInProcess = false;
+    this.methods = clusteringMethods.filter(
+      (m) => m.codename == 'haclustwd2' || m.codename == 'km50l.cluster'
+    );
+    this.method = 'haclustwd2';
+    this.numberOfClusters = 9;
   }
 
   /**
@@ -34,6 +43,11 @@ export class AdjusterService {
    * and applies the returned values
    */
   apply(): void {
+    //this.calculateIndex();
+    this.calculateClusters();
+  }
+
+  calculateIndex(): void {
     const f = () => {
       this._raiInProcess = true;
       this.$http
@@ -132,6 +146,76 @@ export class AdjusterService {
     this.hsUtilsService.debounce(f, 300, false, this)();
   }
 
+  calculateClusters(): void {
+    const f = () => {
+      this._clusteringInProcess = true;
+      this.$http
+        .post(this.serviceBaseUrl + 'clusters/cz', {
+          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),
+            };
+          }),
+        })
+        .toPromise()
+        .then((data: any) => {
+          console.log('data received', data);
+          let logs = 0;
+          let errs = 0;
+          const clusterData = data.response;
+          console.time('forEachObceCluster');
+          obce.forEachFeature((feature) => {
+            // Pair each feature with its clustering data
+            const featureData = clusterData.find(
+              // NOTE: Do NOT add triple equal sign!
+              (item) => item['lau2'] == feature.get('nationalCode')
+            );
+            if (!featureData && errs < 20) {
+              errs++;
+              console.warn(`No data for feature ${feature.get('nationalCode')}`);
+              console.log(feature);
+              return;
+            }
+            logs++;
+            if (logs % 100 == 0) {
+              console.log(`processed ${logs} items`);
+            }
+            Object.keys(featureData).forEach(function (key, index) {
+              if (key !== 'lau2') {
+                feature.set(key, featureData[key]);
+              }
+            });
+          });
+          const clusters = [];
+          for (const region of clusterData) {
+            if (!clusters.includes(region[this.method])) {
+              clusters.push(region[this.method]);
+            }
+          }
+          console.timeEnd('forEachObceCluster');
+          console.log('clustering done!');
+          this.clusters = clusters;
+          this._clusteringInProcess = false;
+          //this.adjusterEventService.clustersLoaded.next({success: true});
+        })
+        .catch((error) => {
+          console.warn(`Error obtaining data from ${this.serviceBaseUrl}.`);
+          console.log(error);
+          this._clusteringInProcess = false;
+          /*this.adjusterEventService.clustersLoaded.next({
+            success: false,
+            err: error,
+          });*/
+        });
+    };
+    this.hsUtilsService.debounce(f, 300, false, this)();
+  }
+
   init(): void {
     this._raiInProcess = true;
     this.$http
@@ -156,6 +240,7 @@ export class AdjusterService {
               };
             });
         });
+        this._raiInProcess = false;
         this.apply();
         this.hsLayerManagerService.setGreyscale(osmLayer);
       })

+ 72 - 0
src/data/clustering_methods.json

@@ -0,0 +1,72 @@
+[
+  {
+    "codename": "km25.cluster",
+    "name": "k-means (25 random sets, Hartigan-Wong method)",
+    "type": "non-hierarchical"
+  },
+  {
+    "codename": "km50hw.cluster",
+    "name": "k-means (50 random sets, Hartigan-Wong method)",
+    "type": "non-hierarchical"
+  },
+  {
+    "codename": "km50l.cluster",
+    "name": "k-means (50 random sets, Lloyd method)",
+    "type": "non-hierarchical"
+  },
+  {
+    "codename": "km50m.cluster",
+    "name": "k-means (50 random sets, MacQueen method)",
+    "type": "non-hierarchical"
+  },
+  {
+    "codename": "kme_eu.cluster",
+    "name": "partitioning (Euclidean distance matrix)",
+    "type": "non-hierarchical"
+  },
+  {
+    "codename": "kme_mn.cluster",
+    "name": "partitioning (Manhattan distance matrix)",
+    "type": "non-hierarchical"
+  },
+  {
+    "codename": "haclust",
+    "name": "complete linkage (Euclidean distance matrix)",
+    "type": "hierarchical"
+  },
+  {
+    "codename": "haclustmin",
+    "name": "complete linkage (Minkowski distance matrix)",
+    "type": "hierarchical"
+  },
+  {
+    "codename": "haclustbin",
+    "name": "complete linkage (asymmetric binary distance matrix)",
+    "type": "hierarchical"
+  },
+  {
+    "codename": "haclustman",
+    "name": "complete linkage (Manhattan distance matrix)",
+    "type": "hierarchical"
+  },
+  {
+    "codename": "haclustmax",
+    "name": "complete linkage (\"Supremum norm\" distance matrix)",
+    "type": "hierarchical"
+  },
+  {
+    "codename": "haclustcan",
+    "name": "complete linkage (Canberra distance matrix)",
+    "type": "hierarchical"
+  },
+  {"codename": "haclustwd2", "name": "Ward2", "type": "hierarchical"},
+  {"codename": "haclustsin", "name": "single linkage", "type": "hierarchical"},
+  {"codename": "haclustcen", "name": "centroid (UPGMC)", "type": "hierarchical"},
+  {"codename": "haclustmed", "name": "median (WPGMC)", "type": "hierarchical"},
+  {"codename": "haclustmcq", "name": "McQuitty (WPGMA)", "type": "hierarchical"},
+  {
+    "codename": "hdclust",
+    "name": "DIANA (DIvisive ANAlysis)",
+    "type": "hierarchical"
+  }
+]