sdm-dih.service.ts 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. import {BehaviorSubject} from 'rxjs';
  2. import {HttpClient} from '@angular/common/http';
  3. import {Injectable} from '@angular/core';
  4. import {csv} from 'd3';
  5. import vars2facts from '../assets/data/variables2factors.json';
  6. @Injectable({providedIn: 'root'})
  7. export class SdmDihService {
  8. readonly DATA_PATH = window.location.hostname.includes('localhost')
  9. ? '../assets/data/normalized_data.csv'
  10. : './assets/data/normalized_data.csv';
  11. sdmData;
  12. //aggregatedData = {};
  13. firstYear: string;
  14. lastYear: string;
  15. /**
  16. * TODO: fill this in auto-magically
  17. */
  18. factors = [
  19. 'aggregated',
  20. 'http://www.semanticweb.org/attractiveness/anthropic',
  21. 'http://www.semanticweb.org/attractiveness/economic',
  22. 'http://www.semanticweb.org/attractiveness/natural',
  23. 'http://www.semanticweb.org/attractiveness/social',
  24. ];
  25. domains = [];
  26. regions = [];
  27. scenarios = [];
  28. years = [];
  29. yearsLoaded = false;
  30. dataLoads: BehaviorSubject<boolean> = new BehaviorSubject(false);
  31. constructor(private httpClient: HttpClient) {
  32. this.loadData()
  33. .then(() => {
  34. this.years = Object.keys(this.sdmData);
  35. this.yearsLoaded = true;
  36. // console.log(this.years);
  37. this.firstYear = this.years[0];
  38. this.lastYear = this.years[this.years.length - 1];
  39. this.regions = [
  40. ...new Set(
  41. this.sdmData[this.firstYear].map((yearData) => yearData['MODEL'])
  42. ),
  43. ];
  44. // console.log(this.regions);
  45. this.scenarios = [
  46. ...new Set(
  47. this.sdmData[this.firstYear].map((yearData) => yearData['SCENARIO'])
  48. ),
  49. ];
  50. // console.log(this.scenarios);
  51. this.domains = [
  52. ...new Set(
  53. this.sdmData[this.firstYear].map((yearData) => yearData['DOMAIN'])
  54. ),
  55. ];
  56. // console.log(this.domains);
  57. this.dataLoads.next(true);
  58. })
  59. .catch((err) => {
  60. console.warn('shit happened', err);
  61. });
  62. }
  63. /**
  64. * For given region, year and scenario calculates its aggregated index,
  65. * i.e. the Index of Rural Attractiveness (RAI) by combining its sub-indices
  66. * @param regionData - Data for all the factors in a certain region, certain year and certain scenario
  67. * @returns Aggregated index for the region, year and scenario
  68. */
  69. calcAggregatedIndex(regionData) {
  70. let weightedSum = 0;
  71. let sumWeight = 0;
  72. for (const key of Object.keys(regionData) as any) {
  73. if (
  74. key === 'MODEL' ||
  75. key === 'DOMAIN' ||
  76. key === 'SCENARIO' ||
  77. key === 'TIME_STEP'
  78. ) {
  79. continue;
  80. }
  81. const factorValues = regionData[key];
  82. let sumValue = 0;
  83. let n = 0;
  84. for (const ds of factorValues.datasets) {
  85. sumValue += ds.value;
  86. n += 1;
  87. }
  88. weightedSum += sumValue * factorValues['weight'];
  89. sumWeight += n * factorValues['weight'];
  90. }
  91. return weightedSum / sumWeight;
  92. }
  93. /**
  94. * For given region, given year, given scenario and single factor calculates its index value (a sub-index)
  95. * @param factorData - Data for a single factor in a certain region, certain year and certain scenario
  96. * @returns Sub-index value (between 0 and 1)
  97. */
  98. calcFactorIndex(factorData) {
  99. let sumValue = 0;
  100. let n = 0;
  101. for (const ds of factorData.datasets) {
  102. sumValue += ds.value;
  103. n += 1;
  104. }
  105. return sumValue / n;
  106. }
  107. async loadData() {
  108. const data = await csv(this.DATA_PATH); // Array of objects, each object represents a row, where key is a heading and value is the cell
  109. // console.log(data);
  110. this.sdmData = {};
  111. for (const row of data) {
  112. const timeStep = row['TIME_STEP'];
  113. const regionData = this.splitDataByFactor(row);
  114. if (!this.sdmData[timeStep]) {
  115. this.sdmData[timeStep] = [];
  116. }
  117. this.sdmData[timeStep].push(regionData);
  118. }
  119. // console.log(this.sdmData);
  120. }
  121. perc2color(perc: number): string {
  122. perc = perc * 100;
  123. let r;
  124. let g;
  125. const b = 0;
  126. if (perc < 50) {
  127. r = 255;
  128. g = Math.round(5.1 * perc);
  129. } else {
  130. g = 255;
  131. r = Math.round(510 - 5.1 * perc);
  132. }
  133. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  134. const h = r * 0x10000 + g * 0x100 + b * 0x1;
  135. return `rgba(${r}, ${g}, ${b}, 1)`;
  136. }
  137. /**
  138. * Change format of each input row into a two-level structure where datasets are classified by factors
  139. * @param inputRegionData - row as read by the CSV reader
  140. * @returns data for one region in one point in time
  141. */
  142. splitDataByFactor(inputRegionData) {
  143. const regionData = {};
  144. for (const dataset of Object.keys(inputRegionData)) {
  145. const datasetValue = inputRegionData[dataset];
  146. if (['MODEL', 'DOMAIN', 'SCENARIO', 'TIME_STEP'].includes(dataset)) {
  147. // just copy the MODEL, DOMAIN, SCENARIO and TIME_STEP
  148. regionData[dataset] = datasetValue;
  149. continue;
  150. }
  151. const factor = vars2facts[dataset];
  152. // when no factor is defined for the dataset, then skip this dataset
  153. if (!factor) {
  154. continue;
  155. }
  156. if (!regionData[factor]) {
  157. regionData[factor] = {
  158. weight: 1, //TODO: Allow to change weight of factors?
  159. datasets: [],
  160. };
  161. }
  162. regionData[factor].datasets.push({
  163. name: dataset,
  164. value: parseFloat(datasetValue),
  165. });
  166. }
  167. for (const key of Object.keys(regionData)) {
  168. if (
  169. key === 'MODEL' ||
  170. key === 'DOMAIN' ||
  171. key === 'SCENARIO' ||
  172. key === 'TIME_STEP'
  173. ) {
  174. continue;
  175. }
  176. const values = regionData[key];
  177. if (values === undefined || values.datasets === undefined) {
  178. console.warn('no data for', values, regionData, key);
  179. }
  180. const index = this.calcFactorIndex(values);
  181. values['index'] = index;
  182. }
  183. const aggregated = this.calcAggregatedIndex(regionData);
  184. regionData['aggregated'] = aggregated;
  185. return regionData;
  186. }
  187. }