app.service.ts 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import hsv2rgb from 'hsv2rgb';
  2. import {Feature} from 'ol';
  3. import {Fill, Stroke, Style} from 'ol/style';
  4. import {Injectable} from '@angular/core';
  5. import {TopoJSON} from 'ol/format';
  6. import {Vector as VectorLayer} from 'ol/layer';
  7. import {Vector as VectorSource} from 'ol/source';
  8. import {HsConfig} from 'hslayers-ng/config.service';
  9. import {HsEventBusService} from 'hslayers-ng/components/core/event-bus.service';
  10. import {HsLanguageService} from 'hslayers-ng/components/language/language.service';
  11. import {HsLayerManagerService} from 'hslayers-ng/components/layermanager';
  12. import {HsLayoutService} from 'hslayers-ng/components/layout/layout.service';
  13. import {HsPanelContainerService} from 'hslayers-ng/components/layout/panels/panel-container.service';
  14. import {HsSidebarService} from 'hslayers-ng/components/sidebar/sidebar.service';
  15. import {AdjusterComponent} from './adjuster/adjuster.component';
  16. import {AdjusterEventService} from './adjuster/adjuster-event.service';
  17. import {AdjusterService} from './adjuster/adjuster.service';
  18. import {krajeLayer, obce, obceIndexLayer, okresyLayer} from './app.config';
  19. @Injectable({providedIn: 'root'})
  20. export class AppService {
  21. // https://colorbrewer2.org/?type=qualitative&scheme=Paired&n=12
  22. colorPalette = [
  23. '#a6cee3',
  24. '#1f78b4',
  25. '#b2df8a',
  26. '#33a02c',
  27. '#fb9a99',
  28. '#e31a1c',
  29. '#fdbf6f',
  30. '#ff7f00',
  31. '#cab2d6',
  32. '#6a3d9a',
  33. '#ffff99',
  34. '#b15928',
  35. ];
  36. constructor(
  37. public adjusterService: AdjusterService,
  38. public adjusterEventService: AdjusterEventService,
  39. public hsConfig: HsConfig,
  40. public hsEventBus: HsEventBusService,
  41. public hsLanguageService: HsLanguageService,
  42. public hsLayerManagerService: HsLayerManagerService,
  43. public hsLayoutService: HsLayoutService,
  44. public hsPanelContainerService: HsPanelContainerService,
  45. public hsSidebarService: HsSidebarService
  46. ) {
  47. this.adjusterEventService.loaded.subscribe(({success}) => {
  48. if (success) {
  49. this.colorPalette = this.generateRandomColorPalette(
  50. this.adjusterService.numberOfClusters
  51. );
  52. }
  53. });
  54. this.hsEventBus.layoutLoads.subscribe(() => {
  55. this.init();
  56. });
  57. this.prepareLayers();
  58. }
  59. init(): void {
  60. this.hsLanguageService.setLanguage('cs');
  61. this.hsSidebarService.buttons.push({
  62. panel: 'adjuster',
  63. module: 'pra.adjuster',
  64. order: 0,
  65. title: () =>
  66. this.hsLanguageService.getTranslation('ADJUSTER.adjustFactors'),
  67. description: 'Adjust factors for computation',
  68. icon: 'icon-analytics-piechart',
  69. });
  70. this.hsPanelContainerService.create(AdjusterComponent, {});
  71. this.hsLayoutService.setDefaultPanel('adjuster');
  72. }
  73. /**
  74. * Create separate layer fo each clustering method and add it to default_layers
  75. */
  76. prepareLayers(): void {
  77. for (const method of this.adjusterService.methods) {
  78. method.layer = new VectorLayer({
  79. source: new VectorSource({
  80. format: new TopoJSON({dataProjection: 'EPSG:5514'}),
  81. url: require('./data/obce_cr_20210310_5p_5514.topojson').default,
  82. overlaps: false,
  83. }),
  84. editor: {editable: false},
  85. visible: true,
  86. style: this.generateStyle(method.codename),
  87. title: `Obce ČR: ${method.name.replaceAll(/\((.+?)\)/g, '')} clusters`,
  88. attributions: ['CC-BY ČÚZK, 2021'],
  89. popUp: {
  90. attributes: [
  91. {
  92. attribute: method.codename,
  93. label: 'ID shluku',
  94. },
  95. ],
  96. },
  97. });
  98. this.hsConfig.default_layers.push(method.layer);
  99. }
  100. // obceIndexLayer, okresyLayer and krajeLayer must be pushed in this order
  101. // so they will display in correct order
  102. this.hsConfig.default_layers.push(obceIndexLayer);
  103. this.hsConfig.default_layers.push(okresyLayer);
  104. this.hsConfig.default_layers.push(krajeLayer);
  105. }
  106. /**
  107. * https://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/
  108. * @private
  109. * @description Only generates random colors if the current palette does not provide enough colors for all the clusters
  110. * @param {number} colorCount Number of colors to randomly generate
  111. * @returns {Array<Array<number> | string>} Array of RGB colors
  112. */
  113. private generateRandomColorPalette(colorCount: number) {
  114. const palette = this.colorPalette;
  115. const goldenRatioConjugate = 0.618033988749895;
  116. let i = palette.length;
  117. while (i < colorCount) {
  118. let h = Math.random();
  119. h += goldenRatioConjugate;
  120. h %= 1;
  121. h *= 360;
  122. palette.push(hsv2rgb(h, 0.5, 0.95));
  123. i++;
  124. }
  125. return palette;
  126. //return `rgba(${r}, ${g}, ${b}, 0.7)`;
  127. }
  128. /**
  129. * @description Function factory for generating style functions based on different clustering methods
  130. * @param {string} method selected method
  131. * @returns {function} style function
  132. */
  133. private generateStyle(method: string) {
  134. return (feature: Feature): Style => {
  135. if (isNaN(feature.get(method))) {
  136. return new Style({
  137. fill: new Fill({
  138. color: '#FFF',
  139. }),
  140. stroke: new Stroke({
  141. color: '#3399CC',
  142. width: 0.25,
  143. }),
  144. });
  145. } else {
  146. return new Style({
  147. fill: new Fill({
  148. color: this.colorPalette[feature.get(method) - 1],
  149. }),
  150. stroke: new Stroke({
  151. color: '#FFF',
  152. width: 0.25,
  153. }),
  154. });
  155. }
  156. };
  157. }
  158. }