| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- import {BehaviorSubject} from 'rxjs';
- import {HttpClient} from '@angular/common/http';
- import {Injectable} from '@angular/core';
- import {csv} from 'd3';
- import vars2facts from '../assets/data/variables2factors.json';
- @Injectable({providedIn: 'root'})
- export class SdmDihService {
- readonly DATA_PATH = window.location.hostname.includes('localhost')
- ? '../assets/data/normalized_data.csv'
- : './assets/data/normalized_data.csv';
- sdmData;
- //aggregatedData = {};
- firstYear: string;
- lastYear: string;
- /**
- * TODO: fill this in auto-magically
- */
- factors = [
- 'aggregated',
- 'http://www.semanticweb.org/attractiveness/anthropic',
- 'http://www.semanticweb.org/attractiveness/economic',
- 'http://www.semanticweb.org/attractiveness/natural',
- 'http://www.semanticweb.org/attractiveness/social',
- ];
- domains = [];
- regions = [];
- scenarios = [];
- years = [];
- yearsLoaded = false;
- dataLoads: BehaviorSubject<boolean> = new BehaviorSubject(false);
- constructor(private httpClient: HttpClient) {
- this.loadData()
- .then(() => {
- this.years = Object.keys(this.sdmData);
- this.yearsLoaded = true;
- // console.log(this.years);
- this.firstYear = this.years[0];
- this.lastYear = this.years[this.years.length - 1];
- this.regions = [
- ...new Set(
- this.sdmData[this.firstYear].map((yearData) => yearData['MODEL'])
- ),
- ];
- // console.log(this.regions);
- this.scenarios = [
- ...new Set(
- this.sdmData[this.firstYear].map((yearData) => yearData['SCENARIO'])
- ),
- ];
- // console.log(this.scenarios);
- this.domains = [
- ...new Set(
- this.sdmData[this.firstYear].map((yearData) => yearData['DOMAIN'])
- ),
- ];
- // console.log(this.domains);
- this.dataLoads.next(true);
- })
- .catch((err) => {
- console.warn('shit happened', err);
- });
- }
- /**
- * For given region, year and scenario calculates its aggregated index,
- * i.e. the Index of Rural Attractiveness (RAI) by combining its sub-indices
- * @param regionData - Data for all the factors in a certain region, certain year and certain scenario
- * @returns Aggregated index for the region, year and scenario
- */
- calcAggregatedIndex(regionData) {
- let weightedSum = 0;
- let sumWeight = 0;
- for (const key of Object.keys(regionData) as any) {
- if (
- key === 'MODEL' ||
- key === 'DOMAIN' ||
- key === 'SCENARIO' ||
- key === 'TIME_STEP'
- ) {
- continue;
- }
- const factorValues = regionData[key];
- let sumValue = 0;
- let n = 0;
- for (const ds of factorValues.datasets) {
- sumValue += ds.value;
- n += 1;
- }
- weightedSum += sumValue * factorValues['weight'];
- sumWeight += n * factorValues['weight'];
- }
- return weightedSum / sumWeight;
- }
- /**
- * For given region, given year, given scenario and single factor calculates its index value (a sub-index)
- * @param factorData - Data for a single factor in a certain region, certain year and certain scenario
- * @returns Sub-index value (between 0 and 1)
- */
- calcFactorIndex(factorData) {
- let sumValue = 0;
- let n = 0;
- for (const ds of factorData.datasets) {
- sumValue += ds.value;
- n += 1;
- }
- return sumValue / n;
- }
- async loadData() {
- 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
- // console.log(data);
- this.sdmData = {};
- for (const row of data) {
- const timeStep = row['TIME_STEP'];
- const regionData = this.splitDataByFactor(row);
- if (!this.sdmData[timeStep]) {
- this.sdmData[timeStep] = [];
- }
- this.sdmData[timeStep].push(regionData);
- }
- // console.log(this.sdmData);
- }
- 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}, 1)`;
- }
- /**
- * Change format of each input row into a two-level structure where datasets are classified by factors
- * @param inputRegionData - row as read by the CSV reader
- * @returns data for one region in one point in time
- */
- splitDataByFactor(inputRegionData) {
- const regionData = {};
- for (const dataset of Object.keys(inputRegionData)) {
- const datasetValue = inputRegionData[dataset];
- if (['MODEL', 'DOMAIN', 'SCENARIO', 'TIME_STEP'].includes(dataset)) {
- // just copy the MODEL, DOMAIN, SCENARIO and TIME_STEP
- regionData[dataset] = datasetValue;
- continue;
- }
- const factor = vars2facts[dataset];
- // when no factor is defined for the dataset, then skip this dataset
- if (!factor) {
- continue;
- }
- if (!regionData[factor]) {
- regionData[factor] = {
- weight: 1, //TODO: Allow to change weight of factors?
- datasets: [],
- };
- }
- regionData[factor].datasets.push({
- name: dataset,
- value: parseFloat(datasetValue),
- });
- }
- for (const key of Object.keys(regionData)) {
- if (
- key === 'MODEL' ||
- key === 'DOMAIN' ||
- key === 'SCENARIO' ||
- key === 'TIME_STEP'
- ) {
- continue;
- }
- const values = regionData[key];
- if (values === undefined || values.datasets === undefined) {
- console.warn('no data for', values, regionData, key);
- }
- const index = this.calcFactorIndex(values);
- values['index'] = index;
- }
- const aggregated = this.calcAggregatedIndex(regionData);
- regionData['aggregated'] = aggregated;
- return regionData;
- }
- }
|