diff --git a/src/app/pages/simulation-wizard/existing-simulations/existing-simulations.component.ts b/src/app/pages/simulation-wizard/existing-simulations/existing-simulations.component.ts index 71cd0947304ba3a6eb189cc26424cffd58ce2c5e..2e2beae8b0d19e7c7c19e7f2c366e506469dec0b 100644 --- a/src/app/pages/simulation-wizard/existing-simulations/existing-simulations.component.ts +++ b/src/app/pages/simulation-wizard/existing-simulations/existing-simulations.component.ts @@ -416,9 +416,10 @@ export class ExistingSimulationsComponent implements OnInit { .subscribe( (res) => { this.showToast( - `onRunDss is over. Deselect the simulation and select it again to have newly generated file loaded.`, + `onRunDss is over.`, "Success", - "success" + "success", + 15000 ); this.runDss.status = ""; @@ -426,12 +427,12 @@ export class ExistingSimulationsComponent implements OnInit { this.selectedSimulationToEmit.emit(undefined); setTimeout(() => { this.selectedSimulationToEmit.emit(this.selectedSimulation); - this.showToast(`Updating spider chart.`, "Info", "info"); + this.showToast(`Updating spider chart.`, "Info", "info", 8000); }, 0); }, (error) => { this.runDss.status = "error"; - this.showToast(`Working, please wait`, `Info`, "info"); + this.showToast(`Working, please wait`, `Info`, "info", 8000); } ) .add(() => { diff --git a/src/app/pages/simulation-wizard/results-map/map-colors-local.ts b/src/app/pages/simulation-wizard/results-map/map-colors-local.ts index 5c9d98d4c9272c386d7108b29d9ce0adba979f47..33842fa15510410f8bbb7516ec8e88411ae44c10 100644 --- a/src/app/pages/simulation-wizard/results-map/map-colors-local.ts +++ b/src/app/pages/simulation-wizard/results-map/map-colors-local.ts @@ -6,14 +6,19 @@ const color_2 = "#fed98b"; const color_3 = "#c2e3ee"; const color_4 = "#6da5cc"; const color_5 = "#35b499"; +// const color_5 = "darkseagreen"; +// const color_4 = "cornflowerblue"; +// const color_3 = "lightblue"; +// const color_2 = "navajowhite"; +// const color_1 = "orange"; +// const color_0 = "red"; const color_capacity_equal_9999 = "#ffffff00"; const color_value_undefined = "#9efcff33"; export function getColor( properties: any, pro: any, - max: number, - ratiosBetweenGrades: number[], + brinkValues: number[], isCapacityEqual9999: boolean ): string { if (isCapacityEqual9999 == true) { @@ -21,7 +26,11 @@ export function getColor( } let value = undefined; - if (pro == "pedestrianTravelTime") { + if ( + pro == "pedestrianTravelTime" || + pro == "dailyInternalBikeTravels" || + pro == "capacity" + ) { value = properties; } else { for (let key in properties) { @@ -33,15 +42,15 @@ export function getColor( if (value == undefined) return color_value_undefined; - return value > max / ratiosBetweenGrades[4] + return value > brinkValues[4] ? color_0 - : value > max / ratiosBetweenGrades[3] + : value > brinkValues[3] ? color_1 - : value > max / ratiosBetweenGrades[2] + : value > brinkValues[2] ? color_2 - : value > max / ratiosBetweenGrades[1] + : value > brinkValues[1] ? color_3 - : value > max / ratiosBetweenGrades[0] + : value > brinkValues[0] ? color_4 : color_5; } @@ -49,8 +58,7 @@ export function getColor( export function getColorHighToLow( properties: any, pro: any, - max: number, - ratiosBetweenGrades: number[], + brinkValues: number[], isCapacityEqual9999: boolean ): string { if (isCapacityEqual9999 == true) { @@ -58,9 +66,11 @@ export function getColorHighToLow( } let value = undefined; - if (pro == "dailyInternalBikeTravels") { - value = properties; - } else if (pro == "capacity") { + if ( + pro == "pedestrianTravelTime" || + pro == "dailyInternalBikeTravels" || + pro == "capacity" + ) { value = properties; } else { for (let key in properties) { @@ -72,32 +82,45 @@ export function getColorHighToLow( if (value == undefined) return color_value_undefined; - return value > max / ratiosBetweenGrades[4] + return value > brinkValues[4] ? color_5 - : value > max / ratiosBetweenGrades[3] + : value > brinkValues[3] ? color_4 - : value > max / ratiosBetweenGrades[2] + : value > brinkValues[2] ? color_3 - : value > max / ratiosBetweenGrades[1] + : value > brinkValues[1] ? color_2 - : value > max / ratiosBetweenGrades[0] + : value > brinkValues[0] ? color_1 : color_0; } -export function getColorForLegend( - d: number, - current_brink_values: object -): string { - return current_brink_values[d] > current_brink_values[4] +export function getColorForLegend(id: number): string { + return id == 5 ? color_0 - : current_brink_values[d] > current_brink_values[3] + : id == 4 ? color_1 - : current_brink_values[d] > current_brink_values[2] + : id == 3 ? color_2 - : current_brink_values[d] > current_brink_values[1] + : id == 2 ? color_3 - : current_brink_values[d] > current_brink_values[0] + : id == 1 ? color_4 : color_5; } + +export function getColorForLegendHighToLow(id: number): string { + return id == 5 + ? color_5 + : id == 4 + ? color_4 + : id == 3 + ? color_3 + : id == 2 + ? color_2 + : id == 1 + ? color_1 + : color_0; +} + +export const colorsAll = [color_0, color_1, color_2, color_3, color_4, color_5]; diff --git a/src/app/pages/simulation-wizard/results-map/results-map.component.html b/src/app/pages/simulation-wizard/results-map/results-map.component.html index 49c0484015a56142d16d776be3e60c76b9dacea5..19dd08327b076c533d3fbc14d07f3284b9929576 100644 --- a/src/app/pages/simulation-wizard/results-map/results-map.component.html +++ b/src/app/pages/simulation-wizard/results-map/results-map.component.html @@ -56,13 +56,90 @@ </nb-card-footer> </nb-card> </ng-template> + <div id="grayscaleInput"> + <input + type="range" + [(ngModel)]="grayscale" + (change)="grayscaleChange()" + /> + </div> </div> - <div id="grayscaleInput"> - <input - type="range" - [(ngModel)]="grayscale" - (change)="grayscaleChange()" - /> + <div id="inputsLegendContainer"> + <form + [formGroup]="newBrinkValuesForm" + (ngSubmit)="onApplyNewValuesSubmit()" + > + <div> + <div> + <label for="">1st color, up to</label> + <input + nbInput + size="tiny" + type="number" + min="0" + max="1000000" + formControlName="legend_0" + /> + </div> + <div> + <label for="">2nd color, up to</label> + <input + nbInput + size="tiny" + type="number" + min="0" + max="1000000" + formControlName="legend_1" + /> + </div> + <div> + <label for="">3rd color, up to</label> + <input + nbInput + size="tiny" + type="number" + min="0" + max="1000000" + formControlName="legend_2" + /> + </div> + <div> + <label for="">4th color, up to</label> + <input + nbInput + size="tiny" + type="number" + min="0" + max="1000000" + formControlName="legend_3" + /> + </div> + <div> + <label for="">5th color, up to</label> + <input + nbInput + size="tiny" + type="number" + min="0" + max="1000000" + formControlName="legend_4" + /> + </div> + <div> + <label for="">6th color, up to unlimited</label> + </div> + </div> + <div> + <button + nbButton + status="primary" + type="submit" + [disabled]="!newBrinkValuesForm.dirty" + > + Apply new values + </button> + </div> + </form> </div> </nb-card-body> </nb-card> diff --git a/src/app/pages/simulation-wizard/results-map/results-map.component.scss b/src/app/pages/simulation-wizard/results-map/results-map.component.scss index e4d50741c5455ed20a4558101ebb779db4598d3d..0b6ed8eca5a9f4d7491efdbc36c4661d7d1de974 100644 --- a/src/app/pages/simulation-wizard/results-map/results-map.component.scss +++ b/src/app/pages/simulation-wizard/results-map/results-map.component.scss @@ -28,7 +28,7 @@ padding: 4px 3px 0 3px; } #loadGeojsonButtonsContainer { - z-index: 997; + z-index: 1003; position: absolute; top: 5px; left: 57px; @@ -42,6 +42,36 @@ text-transform: capitalize; } } + #inputsLegendContainer { + padding: 10px; + form { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + div:nth-of-type(6) label { + width: 100%; + text-align: center; + } + div label { + display: inline-block; + width: 140px; + border-radius: 0.25rem; + text-align: right; + font-weight: bold; + padding: 3px 7px; + } + input { + margin-bottom: 2px; + margin-left: 5px; + height: 1rem; + width: 10rem; + } + div:nth-child(2) button { + margin-left: 8px; + } + } + } } nb-radio { display: inline; diff --git a/src/app/pages/simulation-wizard/results-map/results-map.component.ts b/src/app/pages/simulation-wizard/results-map/results-map.component.ts index 68ac09b17d33fd0a92087d26167d7d682d355158..b6f6fa16baf3c8bad7aff30db00ae59d9476a5eb 100644 --- a/src/app/pages/simulation-wizard/results-map/results-map.component.ts +++ b/src/app/pages/simulation-wizard/results-map/results-map.component.ts @@ -1,4 +1,10 @@ -import { Component, Input, TemplateRef, OnChanges } from "@angular/core"; +import { + Component, + Input, + TemplateRef, + OnChanges, + OnInit, +} from "@angular/core"; import * as L from "leaflet"; import { SimulationService } from "../utils/services/simulation.service"; import * as MapColors from "./map-colors-local"; @@ -6,13 +12,15 @@ import { legendInverted, ratiosBetweenGrades } from "./results-map-settings"; import { NbToastrService, NbDialogService } from "@nebular/theme"; import { Simulation } from "../utils/data/simulation"; import { NetworkService } from "../utils/services/network.service"; +import { FormControl, FormGroup } from "@angular/forms"; @Component({ selector: "ngx-results-map", templateUrl: "./results-map.component.html", styleUrls: ["./results-map.component.scss"], }) -export class ResultsMapComponent implements OnChanges { +export class ResultsMapComponent implements OnInit, OnChanges { + @Input() selectedSimulation: Simulation; public map: L.Map; options = { layers: [ @@ -25,17 +33,26 @@ export class ResultsMapComponent implements OnChanges { center: L.latLng({ lat: 50, lng: 14 }), }; mapLegend = new L.Control({ position: "bottomright" }); - radioButtonLayersControl: any; - loadGeojsons: Object[] = []; checkedRadioButton: string = ""; + extractingKeysLoopLimit: number = 3498; + geojsonLoading: boolean = false; geoJsonStyleWeight: number = 5; geoJsonStyleOpacity: number = 0.8; + grayscale: number = 50; + loadGeojsons: Object[] = []; maxKeyValues: Object = {}; - grayscale: any = 50; - geojsonLoading: boolean = false; - @Input() selectedSimulation: Simulation; + brinkValues: Object = {}; + newBrinkValuesForm = new FormGroup({ + legend_0: new FormControl(undefined), + legend_1: new FormControl(undefined), + legend_2: new FormControl(undefined), + legend_3: new FormControl(undefined), + legend_4: new FormControl(undefined), + legend_5: new FormControl(undefined), + }); + newBrinkValuesApplied: boolean = false; + radioButtonLayersControl: any; selectedGeojson: string = ""; - extractingKeysLoopLimit: number = 3498; constructor( private simulationService: SimulationService, @@ -43,9 +60,14 @@ export class ResultsMapComponent implements OnChanges { private dialogService: NbDialogService, private networkService: NetworkService ) {} + ngOnInit(): void { + this.newBrinkValuesForm.disable(); + } + onMapReady(map: L.Map) { this.map = map; this.grayscaleChange(); + setTimeout(() => { this.map.invalidateSize(); this.networkService.getMapInfo().subscribe((res) => { @@ -55,23 +77,31 @@ export class ResultsMapComponent implements OnChanges { // setTimeout(() => { // console.log( // "%cTemporary", - // "background: crimson; font-weight: bold; color: black; font-family: serif; padding: 0 10px;" - // ); - // const celkomplet: HTMLElement = document.querySelectorAll( - // ".appearance-filled.size-medium.shape-rectangle.status-primary.ng-star-inserted.nb-transition" - // )[1] as HTMLElement; - // celkomplet.click(); - // }, 995); - // setTimeout(() => { - // console.log( - // "%cTemporary", // "background: peru; font-weight: bold; color: black; font-family: serif; padding: 0 10px;" // ); // const celkomplet: HTMLElement = document.querySelectorAll( // ".buttons" // )[0] as HTMLElement; // celkomplet.style.display = "none"; + // }, 825); + // setTimeout(() => { + // const celkomplet1: HTMLElement = document.querySelectorAll( + // ".appearance-filled.size-medium.shape-rectangle.status-primary.ng-star-inserted.nb-transition" + // )[1] as HTMLElement; + // celkomplet1.click(); // }, 1000); + // setTimeout(() => { + // const celkomplet2: HTMLElement = document.querySelectorAll( + // "#loadGeojsonButtonsContainer button" + // )[1] as HTMLElement; + // celkomplet2.click(); + // }, 1050); + // setTimeout(() => { + // const celkomplet3: HTMLElement = document.querySelectorAll( + // ".leaflet-control-layers-selector" + // )[8] as HTMLElement; + // celkomplet3.click(); + // }, 2228); } onLoadClick(selectedKpi: string) { @@ -93,10 +123,7 @@ export class ResultsMapComponent implements OnChanges { .getGeojson(this.selectedSimulation["id"].toString(), selectedKpi) .subscribe((res) => { this.loadGeojsons[index]["response"] = res; - this.handleResponse( - selectedKpi, - this.loadGeojsons[index]["response"] - ); + this.handleResponse(this.loadGeojsons[index]["response"]); }), (error) => { this.toastrService.show( @@ -109,25 +136,16 @@ export class ResultsMapComponent implements OnChanges { this.geojsonLoading = false; }; } else { - this.handleResponse( - selectedKpi, - this.loadGeojsons[index]["response"] - ); + this.handleResponse(this.loadGeojsons[index]["response"]); } } }); } - handleResponse(selectedKpi, res) { - this.loadGeojsons.forEach((geojson, index) => { - if (geojson["buttonValue"] == selectedKpi) { - this.loadGeojsons[index]["response"] = res; - } - }); - + handleResponse(res) { if (res["message"] == "Calculating...") { this.toastrService.show( - "Server says: Calculating the KPI visualization. GEOJSON loading will not be completed. When it will be ready, you will need to refresh the page.", + "Server says: Calculating the KPI visualization. GEOJSON loading will not be completed.", "Geojson not ready", { status: "danger", @@ -180,124 +198,35 @@ export class ResultsMapComponent implements OnChanges { if ( propertyGeojsons[i].myKey == "link_id" || propertyGeojsons[i].myKey == "modes" || - propertyGeojsons[i].myKey == "internal_travel_by_mode" || - propertyGeojsons[i].myKey == "pt_vehicles" - ) { - console.log("Ignoring key ", propertyGeojsons[i].myKey); + propertyGeojsons[i].myKey == "internal_travel_by_mode" + ) continue; - } obj[`${keys[i]}`] = propertyGeojsons[i]; } + this.radioButtonLayersControl = new L.Control.Layers(obj, null, { collapsed: false, }); this.map.addControl(this.radioButtonLayersControl); - this.addListenerToRadioButtonLayersControl(); + this.addListenerToRadioButtonLayersControlAndSetLegend(); this.geojsonLoading = false; } - setLegend(legendType) { - if (this.map != undefined && legendType == undefined) { - this.map.removeControl(this.mapLegend); - return; - } - if (this.map == undefined) { - return; - } - - this.mapLegend.onAdd = (map) => { - let direction: string; - if (legendInverted.indexOf(legendType) == -1) { - direction = "lowToHigh"; - } else { - direction = "highToLow"; - } - - if ( - this.maxKeyValues["maxValue_" + legendType] == undefined || - this.maxKeyValues["maxValue_" + legendType] == 0 - ) { - this.toastrService.info( - "Geojson not displayed because all values are undefined or zero!", - "Geojson not displayed" - ); - let div = L.DomUtil.create("div"); - div.innerHTML += `<p style='background:white; display: inline; font-size:17px; padding: 0 7px; background: #ff3d71'>Values for ${legendType} kpi are undefined or zero!</p>`; - return div; - } + onApplyNewValuesSubmit() { + // here we are forcing the user to make his choices again + if (this.radioButtonLayersControl) + this.radioButtonLayersControl.remove(this.map); + this.resetLayersAndButton(); - var div = L.DomUtil.create("div"); - const grades = [ - 0, - this.limitMyDigits( - this.maxKeyValues["maxValue_" + legendType] / ratiosBetweenGrades[0] - ), - this.limitMyDigits( - this.maxKeyValues["maxValue_" + legendType] / ratiosBetweenGrades[1] - ), - this.limitMyDigits( - this.maxKeyValues["maxValue_" + legendType] / ratiosBetweenGrades[2] - ), - this.limitMyDigits( - this.maxKeyValues["maxValue_" + legendType] / ratiosBetweenGrades[3] - ), - this.limitMyDigits( - this.maxKeyValues["maxValue_" + legendType] / ratiosBetweenGrades[4] - ), - ]; + this.selectedGeojson = undefined; - if (direction == "lowToHigh") { - div.innerHTML += - "<p style='background:white; display: inline; font-size:17px'> <span style='color:" + - MapColors.getColorForLegend(0, grades) + - "'>■</span> <" + - grades[1] + - "</p>"; - for (let i = 1; i < 6; i++) { - div.innerHTML += - "<p style='background:white; display: inline; font-size:17px'> <span style='color:" + - MapColors.getColorForLegend(i, grades) + - "'>■</span> " + - grades[i] + - (grades[i + 1] ? "–" + grades[i + 1] + "</p>" : "+</p>"); - } - } else { - for (let i = 5; i > 0; i--) { - div.innerHTML += - "<p style='background:white; display: inline; font-size:17px'> <span style='color:" + - MapColors.getColorForLegend(5 - i, grades) + - "'>■</span> " + - grades[i] + - (grades[i + 1] ? "–" + grades[i + 1] + "</p>" : "+</p>"); - } - div.innerHTML += - "<p style='background:white; display: inline; font-size:17px'> <span style='color:" + - MapColors.getColorForLegend(5, grades) + - "'>■</span> <" + - grades[1] + - "</p>"; - } - - return div; - }; - this.mapLegend.addTo(this.map); + // when the user will newly click on things, new geojsons will be created + this.newBrinkValuesApplied = true; } private highlightFeature(e, feature) { const layer = e.target; let text = ""; - if (feature["capacity"]) { - if (feature["capacity"] == "9999.0" || feature["capacity"] == "9999") { - text += `<span style='display: inline-block; margin-bottom: 7px; font-weight: bold;'>Oops! Looks like you clicked on an transparent bus line with capacity of 9999!</span><br>`; - } - const tmp = this.limitMyDigits(feature["capacity"]); - if ("capacity" == this.checkedRadioButton) { - text += `<u><strong>capacity:</strong> ${tmp}</u><br>`; - } else { - text += `<strong>capacity:</strong> ${tmp}<br>`; - } - text += `<span style='display: inline-block; margin-bottom: 7px; color: #666'>(unrounded) ${feature["capacity"]}</span><br>`; - } if (feature["cityWide"]) { for (let p in feature["cityWide"]["pollution"]) { @@ -321,13 +250,27 @@ export class ResultsMapComponent implements OnChanges { } Object.entries(feature).forEach((element) => { - const tmp = this.limitMyDigits(element[1] as number).toString(); - if (element[0] == this.checkedRadioButton) { - text += `<u><strong>${element[0]}:</strong> ${tmp}</u><br>`; + if (element[0] == "capacity") { + // exception for capacity checking for value 9999 + if (feature["capacity"] == "9999.0" || feature["capacity"] == "9999") { + text += `<span style='display: inline-block; margin-bottom: 7px; font-weight: bold;'>Oops! Looks like you clicked on an transparent bus line with capacity of 9999!</span><br>`; + } + const tmp = this.limitMyDigits(feature["capacity"]); + if ("capacity" == this.checkedRadioButton) { + text += `<u><strong>capacity:</strong> ${tmp}</u><br>`; + } else { + text += `<strong>capacity:</strong> ${tmp}<br>`; + } + text += `<span style='display: inline-block; margin-bottom: 7px; color: #666'>(unrounded) ${feature["capacity"]}</span><br>`; } else { - text += `<strong>${element[0]}:</strong> ${tmp}<br>`; + const tmp = this.limitMyDigits(element[1] as number).toString(); + if (element[0] == this.checkedRadioButton) { + text += `<u><strong>${element[0]}:</strong> ${tmp}</u><br>`; + } else { + text += `<strong>${element[0]}:</strong> ${tmp}<br>`; + } + text += `<span style='display: inline-block; margin-bottom: 7px; color: #666'>(unrounded) ${element[1]}</span><br>`; } - text += `<span style='display: inline-block; margin-bottom: 7px; color: #666'>(unrounded) ${element[1]}</span><br>`; }); if (!text.length) @@ -339,7 +282,7 @@ export class ResultsMapComponent implements OnChanges { }); } - private resetFeature(e, feature) { + private resetFeature(e) { const layer = e.target; layer.setStyle({ @@ -393,6 +336,7 @@ export class ResultsMapComponent implements OnChanges { } } } + jsonFile.allKeys = allKeys; return allKeys; } @@ -412,94 +356,191 @@ export class ResultsMapComponent implements OnChanges { features: [], }); } else { - if (keys[f] == "capacity") { - tmp = L.geoJSON(<any>jsonFile, { - style: (feature) => ({ - weight: this.geoJsonStyleWeight, - color: MapColors.getColorHighToLow( - feature["properties"]["capacity"], - keys[f], - this.maxKeyValues[`maxValue_${keys[f]}`], - ratiosBetweenGrades, - parseInt(feature["properties"]["capacity"]) == 9999 - ? true - : false - ), - opacity: this.geoJsonStyleOpacity, - }), - onEachFeature: (feature, layer) => - layer.on({ - mouseover: (e) => this.highlightFeature(e, feature.properties), - mouseout: (e) => this.resetFeature(e, feature.properties), + if (this.newBrinkValuesApplied) { + let l = []; + l.push(this.newBrinkValuesForm.get("legend_0").value); + l.push(this.newBrinkValuesForm.get("legend_1").value); + l.push(this.newBrinkValuesForm.get("legend_2").value); + l.push(this.newBrinkValuesForm.get("legend_3").value); + l.push(this.newBrinkValuesForm.get("legend_4").value); + this.brinkValues[keys[f]] = l; + + if (keys[f] == "capacity") { + tmp = L.geoJSON(<any>jsonFile, { + style: (feature) => ({ + weight: this.geoJsonStyleWeight, + color: MapColors.getColorHighToLow( + feature["properties"]["capacity"], + keys[f], + l, + parseInt(feature["properties"]["capacity"]) == 9999 + ? true + : false + ), + opacity: this.geoJsonStyleOpacity, }), - }); - } else if (keys[f] == "dailyInternalBikeTravels") { - tmp = L.geoJSON(<any>jsonFile, { - style: (feature) => ({ - weight: this.geoJsonStyleWeight, - color: MapColors.getColorHighToLow( - feature["properties"]["cityWide"]["traffic"] - .dailyInternalBikeTravels || - feature["properties"].dailyInternalBikeTravels, - keys[f], - this.maxKeyValues[`maxValue_${keys[f]}`], - ratiosBetweenGrades, - parseInt(feature["properties"]["capacity"]) == 9999 - ? true - : false - ), - opacity: this.geoJsonStyleOpacity, - }), - onEachFeature: (feature, layer) => - layer.on({ - mouseover: (e) => this.highlightFeature(e, feature.properties), - mouseout: (e) => this.resetFeature(e, feature.properties), + onEachFeature: (feature, layer) => + layer.on({ + mouseover: (e) => + this.highlightFeature(e, feature.properties), + mouseout: (e) => this.resetFeature(e), + }), + }); + } else if ( + keys[f] == "dailyInternalBikeTravels" || + keys[f] == "pedestrianTravelTime" + ) { + tmp = L.geoJSON(<any>jsonFile, { + style: (feature) => ({ + weight: this.geoJsonStyleWeight, + color: MapColors.getColor( + feature["properties"]["cityWide"]["traffic"][keys[f]] || + feature["properties"][keys[f]], + keys[f], + l, + false + ), + opacity: this.geoJsonStyleOpacity, }), - }); - } else if (keys[f] == "pedestrianTravelTime") { - tmp = L.geoJSON(<any>jsonFile, { - style: (feature) => ({ - weight: this.geoJsonStyleWeight, - color: MapColors.getColor( - feature["properties"]["cityWide"]["traffic"] - .pedestrianTravelTime || - feature["properties"].pedestrianTravelTime, - keys[f], - this.maxKeyValues[`maxValue_${keys[f]}`], - ratiosBetweenGrades, - parseInt(feature["properties"]["capacity"]) == 9999 - ? true - : false - ), - opacity: this.geoJsonStyleOpacity, - }), - onEachFeature: (feature, layer) => - layer.on({ - mouseover: (e) => this.highlightFeature(e, feature.properties), - mouseout: (e) => this.resetFeature(e, feature.properties), + onEachFeature: (feature, layer) => + layer.on({ + mouseover: (e) => + this.highlightFeature(e, feature.properties), + mouseout: (e) => this.resetFeature(e), + }), + }); + } else if (legendInverted.indexOf(keys[f]) > -1) { + tmp = L.geoJSON(<any>jsonFile, { + style: (feature) => ({ + weight: this.geoJsonStyleWeight, + color: MapColors.getColorHighToLow( + feature["properties"], + keys[f], + l, + false + ), + opacity: this.geoJsonStyleOpacity, }), - }); - } else { - tmp = L.geoJSON(<any>jsonFile, { - style: (feature) => ({ - weight: this.geoJsonStyleWeight, - color: MapColors.getColor( - feature["properties"], - keys[f], - this.maxKeyValues[`maxValue_${keys[f]}`], - ratiosBetweenGrades, - parseInt(feature["properties"]["capacity"]) == 9999 - ? true - : false - ), - opacity: this.geoJsonStyleOpacity, - }), - onEachFeature: (feature, layer) => - layer.on({ - mouseover: (e) => this.highlightFeature(e, feature.properties), - mouseout: (e) => this.resetFeature(e, feature.properties), + onEachFeature: (feature, layer) => + layer.on({ + mouseover: (e) => + this.highlightFeature(e, feature.properties), + mouseout: (e) => this.resetFeature(e), + }), + }); + } else { + tmp = L.geoJSON(<any>jsonFile, { + style: (feature) => ({ + weight: this.geoJsonStyleWeight, + color: MapColors.getColor( + feature["properties"], + keys[f], + l, + false + ), + opacity: this.geoJsonStyleOpacity, }), + onEachFeature: (feature, layer) => + layer.on({ + mouseover: (e) => + this.highlightFeature(e, feature.properties), + mouseout: (e) => this.resetFeature(e), + }), + }); + } + } else { + // default, newBrinkValuesForm not applied + let l = []; + ratiosBetweenGrades.forEach((r) => { + l.push( + this.roundMyNumbers(this.maxKeyValues[`maxValue_${keys[f]}`] / r) + ); }); + this.brinkValues[keys[f]] = l; + + if (keys[f] == "capacity") { + tmp = L.geoJSON(<any>jsonFile, { + style: (feature) => ({ + weight: this.geoJsonStyleWeight, + color: MapColors.getColorHighToLow( + feature["properties"]["capacity"], + keys[f], + l, + parseInt(feature["properties"]["capacity"]) == 9999 + ? true + : false + ), + opacity: this.geoJsonStyleOpacity, + }), + onEachFeature: (feature, layer) => + layer.on({ + mouseover: (e) => + this.highlightFeature(e, feature.properties), + mouseout: (e) => this.resetFeature(e), + }), + }); + } else if ( + keys[f] == "dailyInternalBikeTravels" || + keys[f] == "pedestrianTravelTime" + ) { + tmp = L.geoJSON(<any>jsonFile, { + style: (feature) => ({ + weight: this.geoJsonStyleWeight, + color: MapColors.getColor( + feature["properties"]["cityWide"]["traffic"][keys[f]] || + feature["properties"][keys[f]], + keys[f], + l, + false + ), + opacity: this.geoJsonStyleOpacity, + }), + onEachFeature: (feature, layer) => + layer.on({ + mouseover: (e) => + this.highlightFeature(e, feature.properties), + mouseout: (e) => this.resetFeature(e), + }), + }); + } else if (legendInverted.indexOf(keys[f]) > -1) { + tmp = L.geoJSON(<any>jsonFile, { + style: (feature) => ({ + weight: this.geoJsonStyleWeight, + color: MapColors.getColorHighToLow( + feature["properties"], + keys[f], + l, + false + ), + opacity: this.geoJsonStyleOpacity, + }), + onEachFeature: (feature, layer) => + layer.on({ + mouseover: (e) => + this.highlightFeature(e, feature.properties), + mouseout: (e) => this.resetFeature(e), + }), + }); + } else { + tmp = L.geoJSON(<any>jsonFile, { + style: (feature) => ({ + weight: this.geoJsonStyleWeight, + color: MapColors.getColor( + feature["properties"], + keys[f], + l, + false + ), + opacity: this.geoJsonStyleOpacity, + }), + onEachFeature: (feature, layer) => + layer.on({ + mouseover: (e) => + this.highlightFeature(e, feature.properties), + mouseout: (e) => this.resetFeature(e), + }), + }); + } } } tmp["myKey"] = keys[f]; @@ -508,14 +549,18 @@ export class ResultsMapComponent implements OnChanges { return res; } - public addListenerToRadioButtonLayersControl() { + public addListenerToRadioButtonLayersControlAndSetLegend() { this.map.on("baselayerchange", (e) => { this.setLegend(e["name"]); this.checkedRadioButton = e["name"]; + this.newBrinkValuesForm.enable(); }); } public populateMaxKeyValues(jsonFile: Object, keys: Object[]) { + console.log( + " if two or more geojsons have 'capacity', then the it will not work correctly" + ); for (let g = 0; g < jsonFile["features"].length; g++) { for (let f = 0; f < keys.length; f++) { if ( @@ -614,6 +659,114 @@ export class ResultsMapComponent implements OnChanges { } } + public roundMyNumbers(n: number): number { + if (n > 1000) { + return Math.floor(n / 1000) * 1000; + } else if (n > 100) { + return Math.floor(n / 100) * 100; + } else if (n > 10) { + return Math.floor(n / 10) * 10; + } else if (n > 1) { + return Math.floor(n / 1) * 1; + } else if (n > 0.1) { + return Math.floor(n * 10) / 10; + } else if (n > 0.01) { + return Math.floor(n * 100) / 100; + } else if (n > 0.001) { + return Math.floor(n * 1000) / 1000; + } else if (n > 0.0001) { + return Math.floor(n * 10000) / 10000; + } else if (n > 0.00001) { + return Math.floor(n * 100000) / 100000; + } else { + return n; + } + } + + setLegend(legendType) { + if (this.map != undefined && legendType == undefined) { + this.map.removeControl(this.mapLegend); + return; + } + if (this.map == undefined) { + return; + } + + this.mapLegend.onAdd = (map) => { + let direction: string; + if (legendInverted.indexOf(legendType) == -1) { + direction = "lowToHigh"; + } else { + direction = "highToLow"; + } + + if ( + this.maxKeyValues["maxValue_" + legendType] == undefined || + this.maxKeyValues["maxValue_" + legendType] == 0 + ) { + this.toastrService.info( + "Geojson not displayed because all values are undefined or zero!", + "Geojson not displayed" + ); + let div = L.DomUtil.create("div"); + div.innerHTML += `<p style='background:white; display: inline; font-size:17px; padding: 0 7px; background: #ff3d71'>Values for ${legendType} kpi are undefined or zero!</p>`; + return div; + } + + var div = L.DomUtil.create("div"); + + this.setInputsLegendContainerColors(direction); + + if (direction == "lowToHigh") { + div.innerHTML += + "<p style='background:white; display: inline; font-size:17px'> <span style='color:" + + MapColors.getColorForLegend(0) + + "'>■</span> <" + + this.brinkValues[legendType][0] + + "</p>"; + for (let i = 1; i < 6; i++) { + div.innerHTML += + "<p style='background:white; display: inline; font-size:17px'> <span style='color:" + + MapColors.getColorForLegend(i) + + "'>■</span> " + + this.brinkValues[legendType][i - 1] + + (this.brinkValues[legendType][i] + ? "–" + this.brinkValues[legendType][i] + "</p>" + : "+</p>"); + + this.newBrinkValuesForm + .get(`legend_${i - 1}`) + .setValue(`${this.brinkValues[legendType][i - 1]}`); + } + } else { + div.innerHTML += + "<p style='background:white; display: inline; font-size:17px'> <span style='color:" + + MapColors.getColorForLegendHighToLow(0) + + "'>■</span> <" + + this.brinkValues[legendType][0] + + "</p>"; + + for (let i = 0; i < 5; i++) { + div.innerHTML += + "<p style='background:white; display: inline; font-size:17px'> <span style='color:" + + MapColors.getColorForLegendHighToLow(i + 1) + + "'>■</span> " + + this.brinkValues[legendType][i] + + (this.brinkValues[legendType][i + 1] + ? "–" + this.brinkValues[legendType][i + 1] + "</p>" + : "+</p>"); + + this.newBrinkValuesForm + .get(`legend_${i}`) + .setValue(`${this.brinkValues[legendType][i]}`); + } + } + + return div; + }; + this.mapLegend.addTo(this.map); + } + grayscaleChange() { const el = <HTMLElement>( document.querySelectorAll(".leaflet-pane.leaflet-tile-pane")[0] @@ -622,16 +775,105 @@ export class ResultsMapComponent implements OnChanges { } setLoadGeojsons() { - this.networkService.getKpiVisInfo().subscribe((res) => { - this.loadGeojsons = []; - res.forEach((but) => { - this.loadGeojsons.push({ - response: undefined, - buttonValue: but["buttonValue"], - buttonLabel: but["buttonLabel"], + if (this.selectedSimulation != undefined) { + if (this.selectedSimulation["city"]["cityId"] == "amsterdam") { + this.loadGeojsons = [ + { + response: undefined, + buttonValue: "bikeability", + buttonLabel: "bikeability", + }, + { + response: undefined, + buttonValue: "bikeIntensity", + buttonLabel: "bike Intensity", + }, + { + response: undefined, + buttonValue: "bikeSafety", + buttonLabel: "bike Safety", + }, + { + response: undefined, + buttonValue: "pollution", + buttonLabel: "pollution", + }, + ]; + } else if (this.selectedSimulation["city"]["cityId"] == "bilbao") { + this.loadGeojsons = [ + { + response: undefined, + buttonValue: "bikeability", + buttonLabel: "bikeability", + }, + { + response: undefined, + buttonValue: "bikeIntensity", + buttonLabel: "bike Intensity", + }, + { + response: undefined, + buttonValue: "bikeSafety", + buttonLabel: "bike Safety", + }, + { + response: undefined, + buttonValue: "pollution", + buttonLabel: "pollution", + }, + ]; + } else if (this.selectedSimulation["city"]["cityId"] == "helsinki") { + this.loadGeojsons = [ + { + response: undefined, + buttonValue: "congestionsAndBottlenecks", + buttonLabel: "congestions And Bottlenecks", + }, + { + response: undefined, + buttonValue: "harbourAreaTrafficFlow", + buttonLabel: "harbour Area Traffic Flow", + }, + { + response: undefined, + buttonValue: "accousticPollution", + buttonLabel: "accoustic Pollution", + }, + { + response: undefined, + buttonValue: "pollution", + buttonLabel: "pollution", + }, + ]; + } else if (this.selectedSimulation["city"]["cityId"] == "messina") { + this.loadGeojsons = [ + { + response: undefined, + buttonValue: "bikeability", + buttonLabel: "bikeability", + }, + { + response: undefined, + buttonValue: "bikeIntensity", + buttonLabel: "bike Intensity", + }, + { + response: undefined, + buttonValue: "bikeSafety", + buttonLabel: "bike Safety", + }, + { + response: undefined, + buttonValue: "pollution", + buttonLabel: "pollution", + }, + ]; + } else { + this.toastrService.show("Unexpected value for city name.", "Error", { + status: "danger", }); - }); - }); + } + } } openOverviewOfIndicators(dialog: TemplateRef<any>) { @@ -641,14 +883,10 @@ export class ResultsMapComponent implements OnChanges { } resetLayersAndButton() { - let timeout = 100; if (this.map != undefined) { this.map.eachLayer((layer) => { if (layer["feature"] != undefined) { - setTimeout(() => { - this.map.removeLayer(layer); - timeout += 50; - }, timeout); + this.map.removeLayer(layer); } }); @@ -675,4 +913,38 @@ export class ResultsMapComponent implements OnChanges { res["zoom"] ); } + + setInputsLegendContainerColors(direction: string): void { + if (direction == "lowToHigh") { + const legendLabels = document.querySelectorAll( + "#inputsLegendContainer > form > div > div > label" + ); + for (let i = 0; i < legendLabels.length; i++) { + (legendLabels[i] as HTMLElement).style.backgroundColor = + MapColors.colorsAll[legendLabels.length - i - 1]; + } + const inputsLegend = document.querySelectorAll( + "#inputsLegendContainer > form > div > div > input" + ); + for (let i = 0; i < inputsLegend.length; i++) { + (inputsLegend[i] as HTMLElement).style.backgroundColor = + MapColors.colorsAll[inputsLegend.length - i]; + } + } else { + const legendLabels = document.querySelectorAll( + "#inputsLegendContainer > form > div > div > label" + ); + for (let i = 0; i < legendLabels.length; i++) { + (legendLabels[i] as HTMLElement).style.backgroundColor = + MapColors.colorsAll[i]; + } + const inputsLegend = document.querySelectorAll( + "#inputsLegendContainer > form > div > div > input" + ); + for (let i = 0; i < inputsLegend.length; i++) { + (inputsLegend[i] as HTMLElement).style.backgroundColor = + MapColors.colorsAll[i]; + } + } + } }