adjuster.service.ts 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. import {HttpClient} from '@angular/common/http';
  2. import {Injectable} from '@angular/core';
  3. import {HsDialogContainerService} from 'hslayers-ng/components/layout/dialogs/dialog-container.service';
  4. import {HsUtilsService} from 'hslayers-ng/components/utils/utils.service';
  5. // import attractivity from '../Attractivity.json';
  6. import {AdjusterEventService} from './adjuster-event.service';
  7. import {AdjusterLoaderComponent} from './adjuster-loader.component';
  8. import {nuts} from '../nuts';
  9. //import {factors} from './factors.js';
  10. @Injectable({providedIn: 'root'})
  11. export class AdjusterService {
  12. serviceBaseUrl: string;
  13. factors = [];
  14. clusters = [];
  15. numberOfClusters = 12;
  16. method: string;
  17. methods = [
  18. {
  19. codename: 'km25.cluster',
  20. name: 'k-means (25 random sets, Hartigan-Wong method)',
  21. type: 'non-hierarchical',
  22. },
  23. {
  24. codename: 'km50hw.cluster',
  25. name: 'k-means (50 random sets, Hartigan-Wong method)',
  26. type: 'non-hierarchical',
  27. },
  28. {
  29. codename: 'km50l.cluster',
  30. name: 'k-means (50 random sets, Lloyd method)',
  31. type: 'non-hierarchical',
  32. },
  33. {
  34. codename: 'km50m.cluster',
  35. name: 'k-means (50 random sets, MacQueen method)',
  36. type: 'non-hierarchical',
  37. },
  38. {
  39. codename: 'kme_eu.cluster',
  40. name: 'partitioning (Euclidean distance matrix)',
  41. type: 'non-hierarchical',
  42. },
  43. {
  44. codename: 'kme_mn.cluster',
  45. name: 'partitioning (Manhattan distance matrix)',
  46. type: 'non-hierarchical',
  47. },
  48. {
  49. codename: 'haclust',
  50. name: 'complete linkage (Euclidean distance matrix)',
  51. type: 'hierarchical',
  52. },
  53. {
  54. codename: 'haclustmin',
  55. name: 'complete linkage (Minkowski distance matrix)',
  56. type: 'hierarchical',
  57. },
  58. {
  59. codename: 'haclustbin',
  60. name: 'complete linkage (asymmetric binary distance matrix)',
  61. type: 'hierarchical',
  62. },
  63. {
  64. codename: 'haclustman',
  65. name: 'complete linkage (Manhattan distance matrix)',
  66. type: 'hierarchical',
  67. },
  68. {
  69. codename: 'haclustmax',
  70. name: 'complete linkage ("Supremum norm" distance matrix)',
  71. type: 'hierarchical',
  72. },
  73. {
  74. codename: 'haclustcan',
  75. name: 'complete linkage (Canberra distance matrix)',
  76. type: 'hierarchical',
  77. },
  78. /*{
  79. codename: 'haclustcom',
  80. name: 'complete linkage (Euclidean distance matrix)',
  81. type: 'hierarchical',
  82. },*/
  83. {codename: 'haclustwd2', name: 'Ward2', type: 'hierarchical'},
  84. {codename: 'haclustsin', name: 'single linkage', type: 'hierarchical'},
  85. {codename: 'haclustcen', name: 'centroid (UPGMC)', type: 'hierarchical'},
  86. {codename: 'haclustmed', name: 'median (WPGMC)', type: 'hierarchical'},
  87. {codename: 'haclustmcq', name: 'McQuitty (WPGMA)', type: 'hierarchical'},
  88. {
  89. codename: 'hdclust',
  90. name: 'DIANA (DIvisive ANAlysis)',
  91. type: 'hierarchical',
  92. },
  93. ];
  94. private _clusteringInProcess: boolean;
  95. constructor(
  96. public adjusterEventService: AdjusterEventService,
  97. public hsDialogContainerService: HsDialogContainerService,
  98. public hsUtilsService: HsUtilsService,
  99. public httpClient: HttpClient
  100. ) {
  101. this.serviceBaseUrl =
  102. window.location.hostname === 'localhost'
  103. ? 'https://jmacura.ml/ws/' // 'http://localhost:3000/'
  104. : 'https://publish.lesprojekt.cz/nodejs/';
  105. this.method = 'haclustwd2';
  106. }
  107. /**
  108. * Sends a request to polirural-attractiveness-service
  109. * and applies the returned values
  110. */
  111. apply(): void {
  112. this.hsDialogContainerService.create(AdjusterLoaderComponent, {});
  113. const f = () => {
  114. this._clusteringInProcess = true;
  115. this.httpClient
  116. .post(this.serviceBaseUrl + 'clusters', {
  117. numberOfClusters: this.numberOfClusters,
  118. factors: this.factors.map((f) => {
  119. return {
  120. factor: f.name,
  121. weight: f.weight,
  122. datasets: f.datasets
  123. .filter((ds) => ds.included)
  124. .map((ds) => ds.name),
  125. };
  126. }),
  127. })
  128. .toPromise()
  129. .then((data: any) => {
  130. const clusterData = data.response;
  131. /*let max = 0;
  132. this.clusters.forEach((a) => {
  133. if (a.aggregate > max) {
  134. max = a.aggregate;
  135. }
  136. });
  137. const normalizer = 1 / max;
  138. this.attractivity.forEach((a) => {
  139. a.aggregate *= normalizer;
  140. });
  141. this.attractivity.forEach((a) => {
  142. this.nutsCodeRecordRelations[a.code] = a;
  143. });*/
  144. nuts.nuts3Source.forEachFeature((feature) => {
  145. // Pair each feature with its clustering data
  146. const featureData = clusterData.find(
  147. (item) => item['nuts_id'] === feature.get('NUTS_ID')
  148. );
  149. if (!featureData) {
  150. console.warn(`No data for feature ${feature.get('NUTS_ID')}`);
  151. console.log(feature);
  152. return;
  153. }
  154. Object.keys(featureData).forEach(function (key, index) {
  155. if (key !== 'nuts_id') {
  156. feature.set(key, featureData[key]);
  157. }
  158. });
  159. });
  160. const clusters = [];
  161. for (const region of clusterData) {
  162. if (!clusters.includes(region[this.method])) {
  163. clusters.push(region[this.method]);
  164. }
  165. }
  166. this.clusters = clusters;
  167. this._clusteringInProcess = false;
  168. this.adjusterEventService.clustersLoaded.next({success: true});
  169. })
  170. .catch((error) => {
  171. console.warn(`Error obtaining data from ${this.serviceBaseUrl}.`);
  172. console.log(error);
  173. this._clusteringInProcess = false;
  174. this.adjusterEventService.clustersLoaded.next({
  175. success: false,
  176. err: error,
  177. });
  178. });
  179. };
  180. this.hsUtilsService.debounce(f, 300, false, this)();
  181. }
  182. init(): void {
  183. this._clusteringInProcess = true;
  184. this.httpClient
  185. .get(this.serviceBaseUrl + 'datasets')
  186. .toPromise()
  187. .then((data: any) => {
  188. this.factors = data.map((dataset) => {
  189. return {name: dataset.Factor, weight: 1, datasets: []};
  190. });
  191. this.factors = this.hsUtilsService.removeDuplicates(
  192. this.factors,
  193. 'name'
  194. );
  195. this.factors.forEach((factor) => {
  196. factor.datasets = data
  197. .filter((ds) => ds.Factor === factor.name)
  198. .map((ds) => {
  199. return {
  200. name: ds.Name,
  201. desc: ds.Description,
  202. included: true,
  203. };
  204. });
  205. });
  206. this.apply();
  207. })
  208. .catch((error) => {
  209. console.warn(`Web service at ${this.serviceBaseUrl} unavailable!`);
  210. console.log(error);
  211. this._clusteringInProcess = false;
  212. this.adjusterEventService.clustersLoaded.next({
  213. success: false,
  214. err: error,
  215. });
  216. });
  217. }
  218. /**
  219. * @returns {boolean} true if clustering is in process, false otherwise
  220. */
  221. isClusteringInProcess(): boolean {
  222. return this._clusteringInProcess;
  223. }
  224. }