diff --git a/package.json b/package.json index 68f0b3623ad19079164953497dc30a1d33496627..4fa3e5383f7a0c8d850164842feb8b752f91bddf 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,7 @@ "intl": "1.2.5", "ionicons": "2.0.1", "iso8601-js-period": "^0.2.1", - "leaflet": "^1.7.1", + "leaflet": "1.2.0", "leaflet-area-select": "^1.0.5", "leaflet-draw": "^1.0.4", "leaflet-timedimension": "^1.1.1", diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 6382d50dfbc81a6cd579220a6872accb1290a78f..ec97c75cb484bf81c48492dc8f23df2c48e17540 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -98,7 +98,7 @@ import { DatastorageService } from './pages/dashboard-management/services/datast 'dashboard-management', 'PRIVATE_DASHBOARDS', 'SHARED_DASHBOARDS', - 'PUBLIC_DASHBOARDS'] + 'PUBLIC_DASHBOARDS'], }, CITIZEN: { view: [ @@ -111,13 +111,13 @@ import { DatastorageService } from './pages/dashboard-management/services/datast 'tecnalia', 'PRIVATE_DASHBOARDS', 'SHARED_DASHBOARDS', - 'PUBLIC_DASHBOARDS'] + 'PUBLIC_DASHBOARDS'], }, ANONYMOUS: { view: [ 'home', 'about', - 'PUBLIC_DASHBOARDS'] + 'PUBLIC_DASHBOARDS'], }, }, }) diff --git a/src/app/pages/pages-menu.ts b/src/app/pages/pages-menu.ts index 25d8dc8905cb00bc29fd1c80a4130c2f79e97af5..a459db5b390afebf8d40f1d4c076aa0bd6abb722 100644 --- a/src/app/pages/pages-menu.ts +++ b/src/app/pages/pages-menu.ts @@ -193,6 +193,10 @@ export const MENU_ITEMS: NbMenuItem[] = [ title: "Simulations", link: "/pages/simulation-wizard/simulations", }, + { + title: "Create Simulation", + link: "/pages/simulation-wizard/simulation-creation", + }, { title: "ML Module", link: "/pages/simulation-wizard/ml-module", @@ -202,9 +206,17 @@ export const MENU_ITEMS: NbMenuItem[] = [ link: "/pages/simulation-wizard/recommender", }, { - title: "Edit Network Map", - link: "/pages/simulation-wizard/edit-network-map", + title: "Visualizations", + link: "/pages/simulation-wizard/visualizations", + }, + { + title: 'Edit Network Map', + link: '/pages/simulation-wizard/edit-network-map', }, + // { + // title: 'Single Map Animated', + // link: '/pages/simulation-wizard/single-map-bravo', + // }, ], }, { diff --git a/src/app/pages/simulation-wizard/edit-network-map/edit-network-map.component.html b/src/app/pages/simulation-wizard/edit-network-map/edit-network-map.component.html index c5d42e7f7d4c1ced7286e60d63490075d770bfc6..9297f023dde723d0591926e671356eb427079d3c 100644 --- a/src/app/pages/simulation-wizard/edit-network-map/edit-network-map.component.html +++ b/src/app/pages/simulation-wizard/edit-network-map/edit-network-map.component.html @@ -18,8 +18,7 @@ outlined status="primary" (click)="onAddSelectedBikeLanes()" - [disabled]="btnAddSelectedBikeLanesDisabled" - [nbSpinner]="loading" + [disabled]="!allLinestrings.length" > Add Selected Bike Lanes </button> diff --git a/src/app/pages/simulation-wizard/edit-network-map/edit-network-map.component.ts b/src/app/pages/simulation-wizard/edit-network-map/edit-network-map.component.ts index b18f641d8631d715756cbafdba3fd1e8f7ed62da..042fa7ed85912120b99b915bc94691f57c1f43a3 100644 --- a/src/app/pages/simulation-wizard/edit-network-map/edit-network-map.component.ts +++ b/src/app/pages/simulation-wizard/edit-network-map/edit-network-map.component.ts @@ -1,7 +1,7 @@ -import { Component } from "@angular/core"; +import { Component, OnInit } from "@angular/core"; import * as L from "leaflet"; import "leaflet-draw"; -import { NetworkService } from "../utils/services/network.service"; +import { SimulationService } from "../utils/services/simulation.service"; import { NbToastrService } from "@nebular/theme"; @Component({ @@ -9,14 +9,13 @@ import { NbToastrService } from "@nebular/theme"; templateUrl: "./edit-network-map.component.html", styleUrls: ["./edit-network-map.component.scss"], }) -export class EditNetworkMapComponent { +export class EditNetworkMapComponent implements OnInit { public map: L.Map; + public lastLinestring: Object; public allLinestrings: Object[] = []; public allLayers: L.Layer[] = []; geoJsonStyleWeight: number = 4; geoJsonStyleOpacity: number = 0.5; - btnAddSelectedBikeLanesDisabled: boolean = true; - loading: boolean = false; options = { layers: [ L.tileLayer("http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { @@ -29,10 +28,12 @@ export class EditNetworkMapComponent { }; constructor( - private toastrService: NbToastrService, - private networkService: NetworkService + private simulationService: SimulationService, + private toastrService: NbToastrService ) {} + ngOnInit() {} + onMapReady(map: L.Map) { this.map = map; const drawControl = new L.Control.Draw({ @@ -56,70 +57,37 @@ export class EditNetworkMapComponent { : this.highlightFeature(e.target); }); this.map.addLayer(this.allLayers[this.allLayers.length - 1]); + this.lastLinestring = e.layer.editing.latlngs[0]; this.allLinestrings.push(e.layer.editing.latlngs[0]); - this.showInfoNotify(); + this.toastrService.show( + `Currently ${this.allLinestrings.length} polylines.`, + "Polyline Added", + { + status: "info", + } + ); } }); } onAddSelectedBikeLanes() { - this.btnAddSelectedBikeLanesDisabled = true; - this.loading = true; - let tmpFeatures = []; - - this.allLayers.forEach((layer) => { + this.allLayers.forEach((layer, index) => { if (layer["highlighted"] == true) { - let coordinatesWithOneBracketLess = []; - layer["_latlngs"].forEach((point) => { - coordinatesWithOneBracketLess.push([point.lng, point.lat]); - }); - tmpFeatures.push({ - type: "Feature", - properties: {}, - geometry: { - type: "Polygon", - coordinates: coordinatesWithOneBracketLess, - }, - }); + console.log("🚀☢ ~ layer high", index, layer["highlighted"]); } }); - - const tmpGeojson: JSON = <JSON>(<unknown>{ - type: "FeatureCollection", - features: tmpFeatures, + this.toastrService.show("Sending to server (WIP)", "Notification", { + status: "info", }); - - this.toastrService.show(`Sending to server`, `Info`, { - status: "primary", - }); - this.networkService.postGenerateBikeLanes(tmpGeojson, true, 1.5).subscribe( - (res) => { - this.toastrService.show(`Server replies success`, `Success`, { - status: "success", - }); - this.btnAddSelectedBikeLanesDisabled = false; - this.loading = false; - }, - (error) => { - console.log("🚀☢ ~ error", error); - this.toastrService.show( - `${error["message"]}`, - `${error["statusText"]}`, - { status: "danger" } - ); - this.btnAddSelectedBikeLanesDisabled = false; - this.loading = false; - } - ); } onReset() { + this.lastLinestring = undefined; this.allLinestrings = []; this.allLayers.forEach((element) => { this.map.removeLayer(element); }); this.allLayers = []; - this.btnAddSelectedBikeLanesDisabled = true; this.toastrService.show("Cleared", "Reset", { status: "danger", }); @@ -131,8 +99,6 @@ export class EditNetworkMapComponent { weight: 10, opacity: 1.0, }); - this.btnAddSelectedBikeLanesDisabled = false; - this.showInfoNotify(); } private resetFeature(target) { @@ -141,38 +107,5 @@ export class EditNetworkMapComponent { weight: this.geoJsonStyleWeight, opacity: this.geoJsonStyleOpacity, }); - this.evaluateAtLeastOneHighlighted(); - } - - public evaluateAtLeastOneHighlighted() { - let atLeastOneHighlighted: boolean = false; - - this.allLayers.forEach((layer, index) => { - if (layer["highlighted"] == true) { - atLeastOneHighlighted = true; - } - }); - - if (atLeastOneHighlighted) { - this.btnAddSelectedBikeLanesDisabled = false; - } else { - this.btnAddSelectedBikeLanesDisabled = true; - } - } - - public showInfoNotify() { - let n = 0; - this.allLayers.forEach((layer, index) => { - if (layer["highlighted"] == true) { - n++; - } - }); - this.toastrService.show( - `Currently drawn ${this.allLinestrings.length} polylines, ${n} of which selected.`, - "Polyline Added", - { - status: "info", - } - ); } } diff --git a/src/app/pages/simulation-wizard/existing-simulations/existing-simulations.component.html b/src/app/pages/simulation-wizard/existing-simulations/existing-simulations.component.html index 034dc8a78eaa6f113a1a9ee6dae6219e4e00cd62..f869a54b4b93db7be4211ccac87ef83ee377b414 100644 --- a/src/app/pages/simulation-wizard/existing-simulations/existing-simulations.component.html +++ b/src/app/pages/simulation-wizard/existing-simulations/existing-simulations.component.html @@ -1,18 +1,5 @@ <section class="rounded-border"> - <div class="d-flex justify-content-between mb-1"> - <h6>Select Simulation:</h6> - <button - nbButton - outline - shape="round" - status="primary" - (click)="onAddSimulation()" - id="button-add-simulation" - nbTooltip="Add New Simulation" - > - <nb-icon icon="folder-add-outline"></nb-icon> - </button> - </div> + <h6>Select Simulation:</h6> <nb-card *ngIf="simulations.length == 0"> <nb-card-body> <div> @@ -81,14 +68,14 @@ </section> <section class="buttons"> <div class="mt-2 mb-2 rounded-border"> - <h6>Create travel demand model dataset</h6> + <h6>Create travel dataset</h6> <div class="fragment-1 mt-2 mb-2"> <div class="rounded-border"> <div class="button-envelope"> <button nbButton (click)="onPreprocessData()" - nbTooltip="Select at least one simulation before using this button. You will likely need to click Generate Population later. GET http://localhost:8082/traffic-simulation/scripts/city/{city}/preprocess_data" + nbTooltip="Select at least one simulation before using this button. You will likely need to click Generate Population later." [disabled]="!selectedSimulation || preprocessDataBtnDisabled" > preprocess data @@ -119,7 +106,7 @@ <button nbButton (click)="onGeneratePopulation()" - nbTooltip="Preprocess Data must be completed before you can use this button. You will likely have to click Generate Travel Demand Model later. GET localhost:8082/traffic-simulation/scripts/city/{city}/generate_population" + nbTooltip="Preprocess Data must be completed before you can use this button. You will likely have to click Generate Travel Demand later." [disabled]="generatePopulationBtnDisabled" > Generate Population @@ -156,10 +143,10 @@ <input type="text" nbInput - placeholder="Travel Demand Model Name" - formControlName="travelDemandModelName" - [status]="checkValid(travelDemandModelName) ? 'danger' : 'basic'" - nbTooltip="Travel Demand Model Name is required" + placeholder="Plan Name" + formControlName="planName" + [status]="checkValid(planName) ? 'danger' : 'basic'" + nbTooltip="Plan Name is required" /> </div> <div class="mt-2"> @@ -178,13 +165,13 @@ <div class="button-envelope"> <button nbButton - nbTooltip="A simulation must be selected in order to use this button. POST localhost:8082/traffic-simulation/scripts/city/{city}/travel_demand" + nbTooltip="A simulation must be selected in order to use this button." [disabled]=" !selectedSimulation || generateTravelDemandForm.status == 'INVALID' " > - Generate Travel Demand Model</button + Generate Travel Demand</button ><nb-icon [icon]=" generateTravelDemand.status == 'running' @@ -260,7 +247,7 @@ <button nbButton (click)="onCreateCalibrationDatasets()" - nbTooltip="Calibration for selected date. A Simulation must be selected. POST http://localhost:8083/dss/calibration_preprocess" + nbTooltip="Calibration for selected date. A Simulation must be selected." [disabled]=" !selectedSimulation || createCalibrationDatasetsBtnDisabled || @@ -297,15 +284,13 @@ </div> <nb-select placeholder="Select Calibration" class="mb-2" appearance="filled"> - <nb-option *ngFor="let p of travelDemandModels" [value]="p">{{ - p - }}</nb-option> + <nb-option *ngFor="let p of plans" [value]="p">{{ p }}</nb-option> </nb-select> <div class="mb-2 button-envelope"> <button nbButton (click)="onRunSimulation()" - nbTooltip="This button will run Preprocess Data, Generate Population, Generate Travel Demand Model and Run the Simulation. GET http://localhost:8082/traffic-simulation/simulation/{id}/run" + nbTooltip="This button will run Preprocess Data, Generate Population, Generate Travel Demand and Run the Simulation" [disabled]="!selectedSimulation || runSimulationBtnDisabled" > Run simulation</button @@ -335,7 +320,7 @@ <button nbButton (click)="onCalculateKPIS()" - nbTooltip="Select at least one simulation before using this button. GET localhost:8083/dss/kpis/{cityId}/{id}" + nbTooltip="Select at least one simulation before using this button." [disabled]="calculateKPISBtnDisabled" > Calculate KPIs</button @@ -362,62 +347,33 @@ </nb-icon> </div> <div class="mb-2 mt-2 div-dss rounded-border"> - <h6>Compare for DSS <nb-icon icon="map-outline"></nb-icon></h6> + <h6>Compare for DSS <nb-icon icon="clock-outline"></nb-icon></h6> <div class="inner"> - <div class="d-flex flex-grow-1"> - <div class="half-width"> - <span - *ngIf="selectedSimulation; else elseBlock" - class="selectImitation active" - > - {{ selectedSimulation.name }} - </span> - </div> - <div class="half-width"> - <ng-template #elseBlock> - <span class="selectImitation"> Select a Simulation </span> - </ng-template> - <ng-container - *ngIf=" - selectedSimulation == selectedCompareWithSimulation && - selectedSimulation; - else elseSelect - " - > - <nb-select - placeholder="Compare With Simulation" - appearance="filled" - [(selected)]="selectedCompareWithSimulation" - style="outline: 1px solid #d33a26" - > - <nb-option *ngFor="let s of compareWithSimulations" [value]="s">{{ - s.name - }}</nb-option> - </nb-select> - </ng-container> - <ng-template #elseSelect> - <nb-select - placeholder="Compare With Simulation" - appearance="filled" - [(selected)]="selectedCompareWithSimulation" - > - <nb-option *ngFor="let s of compareWithSimulations" [value]="s">{{ - s.name - }}</nb-option> - </nb-select> - </ng-template> - </div> - </div> + <nb-select + placeholder="Select Baseline Simulation" + appearance="filled" + [(selected)]="selectedBaselineSimulation" + > + </nb-select> + <nb-select + placeholder="Compare With Simulation" + appearance="filled" + [(selected)]="selectedCompareWithSimulation" + > + <nb-option *ngFor="let s of simulations" [value]="s">{{ + s.name + }}</nb-option> + </nb-select> <div class="button-envelope"> <button nbButton (click)="onRunDss()" - nbTooltip="Select two different simulations for comparison to use this button. POST http://localhost:8083/dss/kpi_eval/{city}" + nbTooltip="Select two simulations for comparison to use this button." [disabled]=" runDssBtnDisabled || - !selectedSimulation || + !selectedBaselineSimulation || !selectedCompareWithSimulation || - selectedSimulation == selectedCompareWithSimulation + selectedBaselineSimulation == selectedCompareWithSimulation " > Run Dss</button @@ -445,4 +401,45 @@ </div> </div> </div> + + <div class="mb-2"> + <button + nbButton + (click)="onPrepareVisualization()" + nbTooltip="Select at least one simulation before using this button." + [disabled]="prepareVisualizationBtnDisabled" + > + Prepare visulization - dva tipa, pajek, spider, in pa geojsoni</button + ><nb-icon + [icon]=" + prepareVisualization.status == 'running' + ? 'loader-outline' + : prepareVisualization.status == 'error' + ? 'close-outline' + : prepareVisualization.status == 'created' + ? '' + : 'checkmark-circle-2-outline' + " + [status]=" + prepareVisualization.status == 'error' + ? 'danger' + : prepareVisualization.status == 'running' + ? '' + : 'success' + " + [ngClass]="{ 'icon-spin': prepareVisualization.status == 'running' }" + class="icon" + > + </nb-icon> + <span> + <button + nbButton + status="warning" + (click)="onReset()" + nbTooltip="Reset Selection" + > + reset + </button> + </span> + </div> </section> diff --git a/src/app/pages/simulation-wizard/existing-simulations/existing-simulations.component.scss b/src/app/pages/simulation-wizard/existing-simulations/existing-simulations.component.scss index 94921191dab37f1b6e86e07de4982388c79ca1ba..8af6ab692aad22592042654c93b457d9aad1460a 100644 --- a/src/app/pages/simulation-wizard/existing-simulations/existing-simulations.component.scss +++ b/src/app/pages/simulation-wizard/existing-simulations/existing-simulations.component.scss @@ -6,9 +6,6 @@ box-shadow: 0 0 1px #e1e1e1; border-radius: 6px; } - #button-add-simulation { - min-width: unset; - } section.buttons { margin-top: 1rem; .fragment-1 { @@ -24,44 +21,27 @@ margin-bottom: 8px; } - .datepicker .inner { - display: flex; - align-items: flex-end; - & > *:not(:last-child) { - margin-right: 20px; - nb-icon { - margin-right: 5px; - } - } - } - + .datepicker .inner, .div-dss .inner { display: flex; align-items: flex-end; & > *:not(:last-child) { - margin-right: 8px; + margin-right: 20px; nb-icon { margin-right: 5px; } } - .half-width { - margin: 0 4px 0 2px; - flex: 1; - span { - width: 100%; - display: inline-block; - cursor: not-allowed; - } - nb-select { - max-width: unset; - width: 100%; - } - } .button-envelope { - margin-bottom: 5px; + margin-top: 0; + margin-bottom: 0; } } + .button-envelope { + margin-top: 8px; + margin-bottom: 8px; + } + @media screen and (max-width: 900px) { .fragment-1 { display: block; @@ -102,13 +82,13 @@ padding-bottom: 0.25rem; span { margin-right: 15px; - min-width: 280px; + min-width: 240px; } span.text-hint { min-width: 210px; } span.text-description { - min-width: 220px; + min-width: 200px; } button { margin-right: 15px; @@ -124,15 +104,10 @@ box-shadow: 0 2px 1px -1px rgba(0, 0, 0, 0.4), 0 1px 1px 0 rgba(0, 0, 0, 0.28), 0 1px 3px 0 rgba(0, 0, 0, 0.24); border-color: #000; - color: #fff; - background: nb-theme(color-primary-500); - button { - background: #fff !important; - } + background: #a6f0a64f; span.text-hint, span.text-description { font-weight: bold; - color: #fff; } } .toggle-envelope { @@ -174,50 +149,3 @@ transform: rotate(360deg); } } - -.selectImitation { - appearance: button; - background-color: #edf1f7; - border-bottom-color: rgb(247, 249, 252); - border-bottom-left-radius: 0px; - border-bottom-right-radius: 0px; - border-bottom-style: solid; - border-bottom-width: 1px; - border-left-color: rgb(247, 249, 252); - border-left-style: solid; - border-left-width: 1px; - border-right-color: rgb(247, 249, 252); - border-right-style: solid; - border-right-width: 1px; - border-top-color: rgb(247, 249, 252); - border-top-left-radius: 0px; - border-top-right-radius: 0px; - border-top-style: solid; - border-top-width: 1px; - box-sizing: border-box; - color: #929bb3; - cursor: normal; - font-family: Roboto; - font-size: 15px; - font-weight: 400; - line-height: 24px; - margin-bottom: 0px; - margin-left: 0px; - margin-right: 0px; - margin-top: 0px; - min-width: 96px; - overflow-x: hidden; - overflow-y: hidden; - padding-bottom: 7px; - padding-left: 18px; - padding-right: 35.2px; - padding-top: 7px; - position: relative; - text-align: left; - text-overflow: ellipsis; - white-space: nowrap; - width: 240px; - &.active { - color: rgb(34, 43, 69); - } -} 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 8c9a7afdf3834a635ffeb386ec426502519712c3..371278fbe30545b85fb4694418d27ea89a948f67 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 @@ -1,6 +1,7 @@ -import { Component, EventEmitter, OnInit, Output } from "@angular/core"; +import { Component, Input, OnDestroy, OnInit } from "@angular/core"; import { Simulation } from "../utils/data/simulation"; import { SimulationService } from "../utils/services/simulation.service"; +import { CityService } from "../utils/services/city.service"; import { animate, state, @@ -11,17 +12,11 @@ import { } from "@angular/animations"; import { socketStatusAnimation } from "../utils/animations/socket-status.animation"; import { RxStompService } from "@stomp/ng2-stompjs"; -import { - NbToastrService, - NbDateService, - NbDialogService, -} from "@nebular/theme"; -import { FormControl } from "@angular/forms"; +import { NbToastrService, NbDateService } from "@nebular/theme"; +import { Form, FormControl } from "@angular/forms"; import { AbstractControl, FormBuilder, Validators } from "@angular/forms"; import { Network } from "../utils/data/network"; import { NetworkService } from "../utils/services/network.service"; -import { SimulationCreationComponent } from "../simulation-creation/simulation-creation.component"; -import { City } from "../utils/data/city"; @Component({ selector: "ngx-existing-simulations", @@ -60,17 +55,12 @@ import { City } from "../utils/data/city"; socketStatusAnimation, ], }) -export class ExistingSimulationsComponent implements OnInit { - selectedCity: City = undefined; +export class ExistingSimulationsComponent implements OnInit, OnDestroy { + @Input() selectedCity: string; simulations: Simulation[] = []; - compareWithSimulations: Simulation[] = []; selectedSimulation: Simulation; - @Output() selectedSimulationToEmit = new EventEmitter<Simulation>(); networks: Network[] = []; - travelDemandModels: Object[] = [ - "Travel Demand Model 1 (hardcoded)", - "Travel Demand Model 2 (hardcoded)", - ]; + plans: Object[] = ["Plan 1 (hardcoded)", "Plan 2 (hardcoded)"]; selectedNetwork: Network = undefined; preprocessData: any = { status: "", @@ -87,10 +77,14 @@ export class ExistingSimulationsComponent implements OnInit { calculateKPIS: any = { status: "", }; + selectedBaselineSimulation: Simulation; selectedCompareWithSimulation: Simulation; runDss: any = { status: "", }; + prepareVisualization: any = { + status: "", + }; createCalibrationDatasets: any = { status: "", }; @@ -107,21 +101,21 @@ export class ExistingSimulationsComponent implements OnInit { runSimulationBtnDisabled: boolean; calculateKPISBtnDisabled: boolean; runDssBtnDisabled: boolean; + prepareVisualizationBtnDisabled: boolean; selectedScenario: number; generateTravelDemandForm = this.formBuilder.group({ - travelDemandModelName: ["", Validators.required], + planName: ["", Validators.required], selectedNetwork: ["", Validators.required], }); - dialogRef: any; constructor( private simulationService: SimulationService, + private cityService: CityService, private rxStompService: RxStompService, private toastrService: NbToastrService, private formBuilder: FormBuilder, private networkService: NetworkService, - protected dateService: NbDateService<Date>, - private dialogService: NbDialogService + protected dateService: NbDateService<Date> ) { this.datePickerDateMin = new Date(2020, 0, 1); this.datePickerDateMax = this.dateService.addMonth( @@ -145,7 +139,8 @@ export class ExistingSimulationsComponent implements OnInit { }); console.log( - "rxStompService is probably working but server never sends updated because simulation is not triggered" + "%c stomp is probably working but server never sends updated because simulation is not triggered", + "background: dodgerblue; font-weight: bold; color: black; font-family: serif" ); this.rxStompService @@ -199,18 +194,11 @@ export class ExistingSimulationsComponent implements OnInit { } onSelect(simulation) { - this.selectedCity = simulation.city; this.selectedSimulation = simulation; - this.selectedSimulationToEmit.emit(simulation); - this.compareWithSimulations = []; - - this.simulations.forEach((sim) => { - if (simulation.city.cityId == sim.city["cityId"]) { - if (simulation.id != sim.id) { - this.compareWithSimulations.push(sim); - } - } - }); + console.log( + `%chere we should request for something that returtns us GEOJSONs`, + "background: dodgerblue; font-weight: bold; color: black" + ); } onPreprocessData() { @@ -218,78 +206,87 @@ export class ExistingSimulationsComponent implements OnInit { this.preprocessData.status = "running"; this.generatePopulation.status = "created"; this.generateTravelDemand.status = "created"; - this.simulationService - .getPreprocessData(this.selectedCity.cityId) - .subscribe( - (res) => { - this.generatePopulationBtnDisabled = false; - this.preprocessData.status = "success"; - }, - (error) => { - console.log("🚀☢ ~ error", error); - this.showToast( - `${error["message"]}`, - `${error["error"]["error"]}`, - "danger" - ); - this.preprocessData.status = "error"; - } - ); + this.simulationService.getPreprocessData("bilbao").subscribe( + (res) => { + console.log("Res: ", res); + this.generatePopulationBtnDisabled = false; + this.preprocessData.status = "success"; + }, + (error) => { + this.preprocessData.status = "error"; + } + ); + console.log( + "%c preprocessData", + "background: burlywood; font-weight: bold; color: black; font-family: serif" + ); } onGeneratePopulation() { this.generatePopulationBtnDisabled = true; this.generatePopulation.status = "running"; this.showToast("This should take about 20 seconds", "Info", "info"); - this.simulationService - .getGeneratePopulation(this.selectedCity.cityId) - .subscribe( - (res) => { - this.generatePopulation.status = ""; - }, - (error) => { - console.log("🚀☢ ~ error", error); - this.showToast( - `${error["message"]}`, - `${error["error"]["error"]}`, - "danger" - ); - this.generatePopulation.status = "error"; - } - ); + this.simulationService.getGeneratePopulation("bilbao").subscribe( + (res) => { + console.log("Res: ", res); + this.generatePopulation.status = ""; + }, + (error) => { + this.generatePopulation.status = "error"; + } + ); + console.log( + "%c onGeneratePopulation", + "background: darkred; font-weight: bold; color: black; font-family: serif" + ); } onGenerateTravelDemand() { this.generateTravelDemand.status = "running"; this.showToast("This should take about 10 seconds", "Info", "info"); + console.log( + "%c🚀 also nota bene that we re sending id s as strings", + "background: hotpink; color: black; font-weight: bold; font-family: serif" + ); + console.log("🚀 ~ file: existing-simulations", this.selectedSimulation); + // # TODO remove hardcoded to bilbao this.simulationService .postTravelDemand( - this.selectedCity.cityId, - this.generateTravelDemandForm.value.travelDemandModelName, + "bilbao", + this.generateTravelDemandForm.value.planName, this.generateTravelDemandForm.value.selectedNetwork.id, this.selectedSimulation.id ) .subscribe( (res) => { + console.log("Res: ", res); this.generateTravelDemand.status = ""; }, (error) => { - console.log("🚀☢ ~ error", error); - this.showToast( - `${error["message"]}`, - `${error["error"]["error"]}`, - "danger" - ); this.generateTravelDemand.status = "error"; } ) .add(() => { this.preprocessDataBtnDisabled = false; }); + console.log( + "%c onGenerateTravelDemand", + "background: mediumvioletred; font-weight: bold; color: black; font-family: serif" + ); } onCreateCalibrationDatasets() { + console.log( + "%c onCreateCalibrationDatasets", + "background: orchid; font-weight: bold; color: black; font-family: serif" + ); + + console.log( + "%c is it fine to chain requests like this?", + "background: darkorange; font-weight: bold; color: black; font-family: serif" + ); + // #TODO think whether is fine to chain requests like this this.createCalibrationDatasetsBtnDisabled = true; this.createCalibrationDatasets.status = "running"; this.showToast( @@ -303,6 +300,8 @@ export class ExistingSimulationsComponent implements OnInit { this.simulationService .getNetworkFromSimulation(this.selectedSimulation.id) .subscribe((res) => { + console.log("🚀 ~ res.id", res.id); + this.simulationService .postCalibrationPreprocess( this.datePickerFormControlFrom.value, @@ -312,8 +311,15 @@ export class ExistingSimulationsComponent implements OnInit { .subscribe( (res) => { endTime = new Date(); + console.log( + "🚀 ~ file: existing-simulations.component.ts ~ line 243 ~ ExistingSimulationsComponent ~ onCreateCalibrationDatasets ~ res", + res + ); let timeDiff = endTime - startTime; timeDiff /= 1000; + console.log( + `🚀 ~ file: It took ${timeDiff} seconds to complete.` + ); this.showToast( `add/finally statement - postCalibrationPreprocess is over. It took ${timeDiff} seconds to complete.`, @@ -323,16 +329,12 @@ export class ExistingSimulationsComponent implements OnInit { this.createCalibrationDatasets.status = ""; }, (error) => { - console.log("🚀☢ ~ error", error); - this.showToast( - `${error["message"]}`, - `${error["error"]["error"]}`, - "danger" - ); + this.showToast(error.error, "Error", "danger"); this.createCalibrationDatasets.status = "error"; } ) .add(() => { + // finally this.createCalibrationDatasetsBtnDisabled = false; }); }); @@ -342,18 +344,20 @@ export class ExistingSimulationsComponent implements OnInit { this.runSimulationBtnDisabled = true; this.runSimulation.status = "running"; this.showToast("This may take a while ...", "Info", "info"); + console.log( + "%c onRunSimulation", + "background: saddlebrown; font-weight: bold; color: black; font-family: serif" + ); this.simulationService .runSimulation(this.selectedSimulation) .subscribe( (res) => { - console.log("currently doing nothing with result"); + console.log( + "🚀 ~ file: existing-simulations.component.ts ~ line 48 ~ ExistingSimulationsComponent ~ this.simulationService.runSimulation ~ res", + res + ); }, (error) => { - this.showToast( - `${error["message"]}`, - `${error["error"]["error"]}`, - "danger" - ); this.runSimulation.status = "error"; } ) @@ -365,34 +369,27 @@ export class ExistingSimulationsComponent implements OnInit { onCalculateKPIS() { this.calculateKPISBtnDisabled = true; this.calculateKPIS.status = "running"; - this.showToast( - "this should take about 40 seconds to complete.", - "Info", - "info" + console.log( + "%c onCalculateKPIS", + "background: sienna; font-weight: bold; color: black; font-family: serif" ); - let startTime, endTime; - startTime = new Date(); - console.log("🚀☢ ~ this.selectedSimulation", this.selectedSimulation); + // #TODO remove bilbao and the number 1 this.simulationService - .getCalculateKPIs(this.selectedCity.cityId, this.selectedSimulation.id) + .getCalculateKPIs("bilbao", 1) .subscribe( (res) => { - console.log("🚀☢ ~ res", res); - this.calculateKPIS.status = ""; - endTime = new Date(); - let timeDiff = endTime - startTime; - timeDiff /= 1000; - this.showToast( - `onCalculateKPIS is over. - It took ${timeDiff} seconds to complete.`, - "Success", - "success" + console.log( + "🚀 ~ file: existing-simulations.component.ts ~ line 221 ~ ExistingSimulationsComponent ~ this.simulationService.getCalculateKPIs ~ res", + res ); }, (error) => { - console.log("🚀☢ ~ error", error); + console.log( + "🚀 ~ file: existing-simulations.component.ts ~ line 226 ~ ExistingSimulationsComponent ~ this.simulationService.getCalculateKPIs ~ error", + error + ); this.calculateKPIS.status = "error"; - this.showToast(`${error["message"]}`, `${error["name"]}`, "danger"); + this.showToast(error.error, "Error", "danger"); } ) .add(() => { @@ -403,34 +400,29 @@ export class ExistingSimulationsComponent implements OnInit { onRunDss() { this.runDssBtnDisabled = true; this.runDss.status = "running"; + console.log( + "%c onRunDss", + "background: peru; font-weight: bold; color: black; font-family: serif" + ); this.simulationService .postRunDss( - [this.selectedSimulation.id, this.selectedCompareWithSimulation.id], - this.selectedCity.cityId + [ + this.selectedBaselineSimulation.id, + this.selectedCompareWithSimulation.id, + ], + this.selectedCity ) .subscribe( (res) => { - this.showToast( - `onRunDss is over. Deselect the simulation and select it again to have newly generated file loaded.`, - "Success", - "success" + console.log( + "🚀 ~ file: existing-simulations.component.ts ~ line 233 ~ ExistingSimulationsComponent ~ this.simulationService.postRunDss ~ res", + res ); - this.runDss.status = ""; - - // rudimentary way of concincing our-spider-chart to redownload evaluated json - this.selectedSimulationToEmit.emit(undefined); - setTimeout(() => { - console.log( - "🚀☢ ~ this.selectedSimulation.id.toString()", - this.selectedSimulation - ); - this.selectedSimulationToEmit.emit(this.selectedSimulation); - this.showToast(`Updating spider chart.`, "Info", "info"); - }, 0); + this.prepareVisualizationBtnDisabled = false; }, (error) => { this.runDss.status = "error"; - this.showToast(`Working, please wait`, `Info`, "info"); + this.showToast(error.error, "Error", "danger"); } ) .add(() => { @@ -438,6 +430,16 @@ export class ExistingSimulationsComponent implements OnInit { }); } + onPrepareVisualization() { + this.prepareVisualizationBtnDisabled = true; + this.prepareVisualization.status = "running"; + console.log( + "%c onPrepareVisualization", + "background: orangered; font-weight: bold; color: black; font-family: serif" + ); + this.prepareVisualizationBtnDisabled = false; + } + onReset() { this.selectedSimulation = undefined; this.generateTravelDemandForm.reset(); @@ -456,6 +458,11 @@ export class ExistingSimulationsComponent implements OnInit { } this.resetButtonsAndStatuses(); + + console.log( + "🚀 ~ file: existing-simulations.component.ts ~ line 223 ~ ExistingSimulationsComponent ~ onReset ~ this.selectedSimulation", + this.selectedSimulation + ); } resetButtonsAndStatuses() { @@ -466,16 +473,24 @@ export class ExistingSimulationsComponent implements OnInit { this.runSimulation.status = ""; this.calculateKPIS.status = ""; this.runDss.status = ""; + this.prepareVisualization.status = ""; this.preprocessDataBtnDisabled = false; this.generatePopulationBtnDisabled = true; this.calculateKPISBtnDisabled = false; this.runDssBtnDisabled = false; + this.prepareVisualizationBtnDisabled = true; } showToast(msg: string, title: string, status: string) { this.toastrService.show(msg, title, { status: status }); } + ngOnDestroy(): void { + console.log( + "Removed various stompservices with import { Subscription } from 'rxjs';; private topicSubscription: Subscription; this.topicSubscription.unsubscribe()" + ); + } + onAnimationEventDone(event: AnimationEvent, index: number) { if ( event.toState == "show" && @@ -511,8 +526,8 @@ export class ExistingSimulationsComponent implements OnInit { } } - get travelDemandModelName() { - return this.generateTravelDemandForm.get("travelDemandModelName"); + get planName() { + return this.generateTravelDemandForm.get("planName"); } get networkId() { @@ -522,8 +537,4 @@ export class ExistingSimulationsComponent implements OnInit { checkValid(control: AbstractControl) { return control.invalid && (control.dirty || control.touched); } - - onAddSimulation(): void { - this.dialogRef = this.dialogService.open(SimulationCreationComponent, {}); - } } diff --git a/src/app/pages/simulation-wizard/ml-module/ml-module.component.ts b/src/app/pages/simulation-wizard/ml-module/ml-module.component.ts index cfecc97603d383a9c3d338dd214387f890d282fa..6473f19cb9b4c69ff744f609dcefff09bd20ac13 100644 --- a/src/app/pages/simulation-wizard/ml-module/ml-module.component.ts +++ b/src/app/pages/simulation-wizard/ml-module/ml-module.component.ts @@ -1,22 +1,16 @@ -import { Component } from "@angular/core"; +import { Component, OnInit } from "@angular/core"; import { environment } from "../../../../environments/environment"; -import { ConfigService } from "@ngx-config/core"; @Component({ selector: "ngx-ml-module", templateUrl: "./ml-module.component.html", styleUrls: ["./ml-module.component.scss"], }) -export class MlModuleComponent { - constructor(configService: ConfigService) { - this.storage_url = - configService.getSettings("traffic_simulation_storage_url") + "/storage"; - this.file_url = - configService.getSettings("traffic_simulation_storage_url") + - "/storage/ml-experiments"; - } - storage_url: any; - file_url: any; +export class MlModuleComponent implements OnInit { + constructor() {} + + storage_url = `${environment.storageAPIUrl}`; + file_url = this.storage_url + "/ml-experiments"; visualizationBtnDisabled: boolean = true; mlBtnDisabled: boolean = true; recommendationBtnDisabled: boolean = true; @@ -43,6 +37,8 @@ export class MlModuleComponent { }, ]; + ngOnInit(): void {} + onExperiment(experiment) { this.selectedExperiment = experiment; this.visualizationBtnDisabled = false; diff --git a/src/app/pages/simulation-wizard/our-spider-chart/our-spider-chart.component.html b/src/app/pages/simulation-wizard/our-spider-chart/our-spider-chart.component.html index 7f9b077dd808c4ef8e5b663db49ffa4e655d587e..34def687384a2ba0249c1d829f035649c570509a 100644 --- a/src/app/pages/simulation-wizard/our-spider-chart/our-spider-chart.component.html +++ b/src/app/pages/simulation-wizard/our-spider-chart/our-spider-chart.component.html @@ -1,29 +1,34 @@ -<nb-card id="ourSpiderCard" size="giant"> - <nb-card-header *ngIf="selectedSimulation == undefined; else elseBlock" - >Spider Chart - select a simulation first - <nb-icon icon="alert-triangle-outline"></nb-icon - ></nb-card-header> - <ng-template #elseBlock> +<div class="row justify-content-center align-items-center"> + <nb-card size="giant" style="width: 100%"> <nb-card-header - >Spider Chart <i>{{ selectedSimulationId }}</i></nb-card-header - > - </ng-template> - <nb-card-body *ngIf="chartjsData && errorMsg == undefined; else noChartBlock"> - <chart type="radar" [data]="chartjsData" [options]="chartjsOptions"></chart> - </nb-card-body> - <nb-card-footer *ngIf="chartjsData && errorMsg == undefined"> - <div> - <div *ngFor="let item of allKpis" class="checkbox-envelope"> - <nb-checkbox [checked]="item.isChecked" (change)="onInputChange(item)"> - {{ item.label }} + >Comparison + <br /> + <span class="subtitle-2 text-hint">radar chart</span> + </nb-card-header> + <nb-card-body> + <chart + type="radar" + [data]="chartjsData" + [options]="chartjsOptions" + ></chart> + </nb-card-body> + </nb-card> + <nb-card> + <form [formGroup]="form"> + <div + formArrayName="checkboxes" + *ngFor="let c of checkboxesFormArray.controls; let i = index" + style="display: inline-block; width: 33%" + > + <nb-checkbox + id="checkboxNOx-{{ i }}" + [formControlName]="i" + (change)="onCheckboxChange($event)" + [checked]="checked[i]" + > + {{ checkboxItems[i] }} </nb-checkbox> - </ng-container> - </div> - <div class="d-flex"> - <span class="flex-grow-1"></span> - <button nbButton status="warning" (click)="deselectAll()"> - deselect all - </button> - </div> + </div> + </form> </nb-card> </div> diff --git a/src/app/pages/simulation-wizard/our-spider-chart/our-spider-chart.component.scss b/src/app/pages/simulation-wizard/our-spider-chart/our-spider-chart.component.scss index b98ac594803d7410bef79bb925739191a8ee2b52..2db1686771260317dce2113e1ef5a102501ddc38 100644 --- a/src/app/pages/simulation-wizard/our-spider-chart/our-spider-chart.component.scss +++ b/src/app/pages/simulation-wizard/our-spider-chart/our-spider-chart.component.scss @@ -8,12 +8,21 @@ min-width: 768px; } ::ng-deep { - // button { - // margin-top: 20px; - // width: 100%; - // } + button { + margin-top: 20px; + width: 100%; + } form { padding: 20px; } + // * { + + // } + // p { + + // } + // canvas.chartjs-render-monitor { + + // } } } diff --git a/src/app/pages/simulation-wizard/our-spider-chart/our-spider-chart.component.ts b/src/app/pages/simulation-wizard/our-spider-chart/our-spider-chart.component.ts index 7cc382c2923c5e43acd8a6688ccc45d87946356e..6601c255b2afdcc561723329da9315b5e8902844 100644 --- a/src/app/pages/simulation-wizard/our-spider-chart/our-spider-chart.component.ts +++ b/src/app/pages/simulation-wizard/our-spider-chart/our-spider-chart.component.ts @@ -1,178 +1,147 @@ -import { AfterViewInit, Component } from "@angular/core"; +import { Component, OnInit, OnDestroy } from "@angular/core"; import { Observable } from "rxjs"; +import { FormBuilder, FormGroup, FormArray, FormControl } from "@angular/forms"; import { SimulationService } from "../utils/services/simulation.service"; -import { NbToastrService } from "@nebular/theme"; -import { Simulation } from "../utils/data/simulation"; @Component({ selector: "ngx-our-spider-chart", templateUrl: "./our-spider-chart.component.html", styleUrls: ["./our-spider-chart.component.scss"], }) -export class OurSpiderChartComponent implements AfterViewInit { +export class OurSpiderChartComponent implements OnInit, OnDestroy { chartjsOptions: any; chartjsData: {}; responseFromApi = {}; - allKpis: { label: string; value: string; isChecked: boolean }[] = []; - checkedKpis: boolean[] = []; - @Input() selectedSimulation: Simulation; - colorA: string = "rgb(255, 99, 132)"; - colorB: string = "rgb(75, 192, 192)"; - colorA_reducedOpacity: string = "rgba(255, 99, 132, 0.2)"; - colorB_reducedOpacity: string = "rgba(75, 192, 192, 0.2)"; - entries: string[][] = []; - errorMsg: string = undefined; - allDeselected: boolean = false; - - constructor(private simulationService: SimulationService) { + respondedKeys = []; + themeSubscription: any; + form: FormGroup; + checkboxItems = []; + checked: boolean[] = []; + colors: any = ["#FF0000", "#228822", "#3333FF", "#007700"]; + get checkboxesFormArray() { + return this.form.controls.checkboxes as FormArray; + } + + constructor( + private simulationService: SimulationService, + private formBuilder: FormBuilder + ) { this.chartjsOptions = { responsive: true, maintainAspectRatio: false, - scaleFontColor: "#fff", + scaleFontColor: "white", legend: { labels: { - fontColor: "#000", - }, - }, - elements: { - line: { - borderWidth: 3, + fontColor: "black", }, }, scale: { pointLabels: { - fontSize: 12, + fontSize: 14, fontColor: "#262626", }, + gridLines: { + color: "#888", + }, + angleLines: { + color: "#888", + }, ticks: { beginAtZero: true, max: 4, min: 0, - stepSize: 1, - }, + stepSize: 1 + } }, }; + this.form = this.formBuilder.group({ + checkboxes: new FormArray([]), + }); + this.addCheckboxesToForm(); } - - ngOnChanges(changes: SimpleChanges) { - if (changes.selectedSimulation.currentValue) this.loadDataFromApi(); + ngOnInit(): void { } + async ngAfterViewInit() { + await this.loadDataFromApi().toPromise(); } + ngOnDestroy(): void { } + loadDataFromApi(): Observable<any> { + this.simulationService.getEvaluated([1, 2]).subscribe({ + next: (data) => { + // console.log("🚀 ~ file: our-spider-chart.component.ts ~ line 141 ~ OurSpiderChartComponent ~ this.simulationService.getEvaluated ~ data", data) - loadDataFromApi() { - this.simulationService - .postEvaluated([this.selectedSimulation["id"]]) - .subscribe({ - next: (data) => { - this.toastrService.show( - "Evaluated.json for spider chart successfully loaded.", - "Loaded", - { status: "success" } - ); - this.errorMsg = undefined; - this.responseFromApi = data; - let tmpDatasets = []; - let tmpLabels = []; - let values_A = []; - let values_B = []; - const rootKeys = Object.keys(this.responseFromApi); - const l = this.responseFromApi[rootKeys[0]]; - const m = Object.keys(l); - - for (let j = 0; j < 2; j++) { - let l = data[outerKeys[j]]; - let m = Object.keys(l)[0]; - let innerKeys = data[outerKeys[j]][m]; - let onlyKeys = Object.keys(innerKeys); - let onlyValues = Object.values(data[outerKeys[j]][m]); - - for (let i = 0; i < onlyValues.length; i++) { - if (/^[0-4]$/.test(onlyValues[i] as string)) { - } else { - console.log("🚀☢ Setting 0"); - onlyValues[i] = 0; - } - } - - for (let n = 0; n < onlyKeys.length; n++) { - if (tmpLabels.indexOf(onlyKeys[n]) === -1) - tmpLabels.push(onlyKeys[n]); - } - - tmpDatasets.push({ - data: onlyValues, - label: `${j}`, - borderColor: this.colors[j] + "80", - backgroundColor: this.colors[j] + "22", + this.responseFromApi = data; + + let tmpDatasets = []; + let tmpLabels = []; + this.removeAllCheckboxesFromForm(); + + console.log(typeof (data)) + console.log(Object.entries(data)[0][1]["sentArray"]) + + let tmpData = []; + const myKeys = Object.keys(Object.entries(data)[0][1]["sentArray"]) + console.log("🚀 ~ file: our-spider-chart.component.ts ~ line 158 ~ OurSpiderChartComponent ~ this.simulationService.getEvaluated ~ myKeys", myKeys) + + for (let j = 0; j < myKeys.length; j++) { + if (tmpLabels.indexOf(myKeys[j]) === -1) + tmpLabels.push(myKeys[j]); + + this.respondedKeys.push({ + id: j, + name: myKeys[j], + value: (Object.entries(data))[0][1]["sentArray"][myKeys[j]], }); + + tmpData.push((Object.entries(data))[0][1]["sentArray"][myKeys[j]]); } + + tmpDatasets.push({ + data: tmpData, + label: Object.keys(Object.entries(data)[0][1]["sentArray"]), + borderColor: this.colors[1] + "80", + backgroundColor: this.colors[1] + "22", + }); + + + this.addCheckboxesToForm(); + this.chartjsData = { labels: tmpLabels, datasets: tmpDatasets, }; - - while (this.allKpis.length > 0) { - this.allKpis.pop(); - } - tmpLabels.forEach((element) => { - this.allKpis.push({ - label: element, - value: element, - isChecked: true, - }); - }); - }, - error: (error) => { - console.log("🚀☢ ~ Error", error); - const el = document.querySelectorAll( - ".chartjs-size-monitor" - )[0] as HTMLElement; - el.insertAdjacentHTML( - "afterend", - `<div style='margin: -1rem -1.5rem; background: #eee; padding: 15px; position: absolute; width: 100%; height: 100%;'><div style='position: relative; text-align: center; left: 50%; top: 50%; transform: translate(-50%, -50%);'><h4>Working on it, please wait ...</h4></div></div>` - ); }, }); return new Observable(); } - - public deselectAll() { - let index = this.allKpis.length; - while (index--) { - this.allKpis[index].isChecked = false; - } - this.updateSpiderChart(); - } - public onInputChange(item: any) { - let index = this.allKpis.length; - while (index--) { - if (this.allKpis[index].label == item.label) { - this.allKpis[index].isChecked = !this.allKpis[index].isChecked; - break; - } - } - this.updateSpiderChart(); - } - private updateSpiderChart() { + onCheckboxChange(e) { + const selectedCheckboxNames = this.form.value.checkboxes + .map((checked, i) => (checked ? this.checkboxItems[i] : null)) + .filter((v) => v !== null); + const selectedCheckboxIds = this.form.value.checkboxes + .map((checked, i) => (checked ? i : null)) + .filter((v) => v !== null); let tmpLabels = []; let tmpDatasets = []; - let values_A = []; - let values_B = []; - for (let i = 0; i < this.allKpis.length; i++) { - if (this.allKpis[i].isChecked == true) { - tmpLabels.push(this.allKpis[i]["label"]); - } + for (let i = 0; i < selectedCheckboxIds.length; i++) { + tmpLabels.push(this.respondedKeys[selectedCheckboxIds[i]].name); } for (let i = 0; i < Object.keys(this.responseFromApi).length; i++) { let tmpData = []; - const responseValues = Object.values(this.responseFromApi)[i]; - const rootKey = Object.keys(responseValues)[0]; - const thisEvaluatedData = responseValues[rootKey]; - - tmpLabels.forEach((item) => { - tmpData.push(thisEvaluatedData[item]); - }); + const myKeys = this.getKeys( + this.responseFromApi[Object.keys(this.responseFromApi)[i]] + ); + + for (let j = 0; j < myKeys.length; j++) { + if (selectedCheckboxNames.includes(myKeys[j])) { + tmpData.push( + this.responseFromApi[Object.keys(this.responseFromApi)[i]][ + myKeys[j] + ] + ); + } + } tmpDatasets.push({ data: tmpData, @@ -182,33 +151,34 @@ export class OurSpiderChartComponent implements AfterViewInit { }); } - tmpDatasets.push({ - data: values_A, - label: m[0], - fill: true, - backgroundColor: this.colorA_reducedOpacity, - borderColor: this.colorA, - pointBackgroundColor: this.colorA, - pointBorderColor: "#fff", - pointHoverBackgroundColor: "#fff", - pointHoverBorderColor: this.colorA, - }); - - tmpDatasets.push({ - data: values_B, - label: m[1], - fill: true, - backgroundColor: this.colorB_reducedOpacity, - borderColor: this.colorB, - pointBackgroundColor: this.colorB, - pointBorderColor: "#fff", - pointHoverBackgroundColor: "#fff", - pointHoverBorderColor: this.colorB, - }); - this.chartjsData = { labels: tmpLabels, datasets: tmpDatasets, }; } + private addCheckboxesToForm() { + this.checkboxItems.forEach(() => + this.checkboxesFormArray.push(new FormControl(true)) + ); + } + private removeAllCheckboxesFromForm() { + let count = 1000; + while (this.checkboxItems.length > 0) { + this.checkboxItems.splice(this.checkboxItems.length - 1, 1); + count--; + if (count < 1) { + break; + } + } + this.checkboxesFormArray.clear(); + } + getKeys(obj) { + return Object.keys(obj).reduce((r, k) => { + r.push(k); + + if (Object(obj[k]) === obj[k]) r.push(...this.getKeys(obj[k])); + + return r; + }, []); + } } diff --git a/src/app/pages/simulation-wizard/recommender-page/recommender-page.component.html b/src/app/pages/simulation-wizard/recommender-page/recommender-page.component.html index 86cfc1a94d92f9299415aa574d9315e31beae1f8..2344f4929d125015375f5953ec15125dc6572598 100644 --- a/src/app/pages/simulation-wizard/recommender-page/recommender-page.component.html +++ b/src/app/pages/simulation-wizard/recommender-page/recommender-page.component.html @@ -14,6 +14,7 @@ placeholder="Select KPI 1" formControlName="kpi_1_name" class="select select-wide" + (ngModelChange)="onSelect()" > <nb-option *ngFor="let kpi_name of kpisArr" [value]="kpi_name">{{ kpi_name @@ -25,6 +26,7 @@ placeholder="Optimize KPI" formControlName="kpi_1_min_or_max" class="select select-narrow" + (ngModelChange)="onSelect()" > <nb-option value="min">Minimize</nb-option> <nb-option value="max">Maximize</nb-option> @@ -36,6 +38,7 @@ placeholder="Select KPI 2" formControlName="kpi_2_name" class="select select-wide" + (ngModelChange)="onSelect()" > <nb-option *ngFor="let kpi_name of kpisArr" [value]="kpi_name">{{ kpi_name @@ -47,6 +50,7 @@ placeholder="Optimize KPI" formControlName="kpi_2_min_or_max" class="select select-narrow" + (ngModelChange)="onSelect()" > <nb-option value="min">Minimize</nb-option> <nb-option value="max">Maximize</nb-option> @@ -58,6 +62,7 @@ nbButton type="submit" status="primary" + [disabled]="btnSubmitDisabled" nbTooltip="Choose all four selections to use this button." > Optimize diff --git a/src/app/pages/simulation-wizard/recommender-page/recommender-page.component.ts b/src/app/pages/simulation-wizard/recommender-page/recommender-page.component.ts index 7a40989efefa0c904b0b6344564dd66beb67296a..c4d4c5799c890ded698b98b9bb591cabb8a092ad 100644 --- a/src/app/pages/simulation-wizard/recommender-page/recommender-page.component.ts +++ b/src/app/pages/simulation-wizard/recommender-page/recommender-page.component.ts @@ -271,6 +271,7 @@ export class RecommenderPageComponent ], }, }; + btnSubmitDisabled: boolean = true; leafletControlHamburger: HTMLElement = null; constructor( @@ -281,12 +282,7 @@ export class RecommenderPageComponent ngOnChanges(): void {} ngOnInit(): void { - // # TODO getSelectedCityNotBehavior will get the city set in traffic simulation's .env file this.cityService.getSelectedCityNotBehavior().subscribe((city) => { - console.log( - "%cgetSelectedCityNotBehavior will get the city set in traffic simulation's .env file", - "background: crimson; color: black; font-weight: bold;" - ); this.city = city; this.kpis = this.city_kpi_ids[this.city.cityId]; for (let l = 0; l < Object.entries(this.kpis).length; l++) { @@ -295,15 +291,6 @@ export class RecommenderPageComponent }); this.extractDataForChart(); this.populateStringForButtons(); - - this.myFormGroup.controls["kpi_1_name"].setValue( - this.city_kpi_ids.bilbao["1"] - ); - this.myFormGroup.controls["kpi_1_min_or_max"].setValue("max"); - this.myFormGroup.controls["kpi_2_name"].setValue( - this.city_kpi_ids.bilbao["4"] - ); - this.myFormGroup.controls["kpi_2_min_or_max"].setValue("min"); } ngAfterViewInit(): void { @@ -318,6 +305,19 @@ export class RecommenderPageComponent this.map.setView(new L.LatLng(43.25974033178419, -2.947339117090451), 13); } + onSelect() { + if ( + this.myFormGroup.get("kpi_1_name").value == undefined || + this.myFormGroup.get("kpi_1_min_or_max").value == undefined || + this.myFormGroup.get("kpi_2_name").value == undefined || + this.myFormGroup.get("kpi_2_min_or_max").value == undefined + ) { + this.btnSubmitDisabled = true; + } else { + this.btnSubmitDisabled = false; + } + } + myFormSubmit(): void { console.log( "🚀☢ ~ his.myFormGroup", @@ -398,7 +398,7 @@ export class RecommenderPageComponent label: "Solution 2", data: [ { - x: 72, + x: 100, y: 22, }, ], @@ -447,6 +447,7 @@ export class RecommenderPageComponent let text = ""; Object.entries(feature["properties"]).forEach((key, value) => { + console.log("🚀☢ ~ key", key, value); text += `${key}: ${value} <br>`; }); @@ -456,6 +457,8 @@ export class RecommenderPageComponent weight: 2, opacity: 1.0, color: "#3388ff", + //fillOpacity: 0, + //fillColor: '#FFF' }); } @@ -467,6 +470,8 @@ export class RecommenderPageComponent weight: 1, opacity: 0.7, color: "#3388ff", + //fillOpacity: 0.7, + //fillColor: '#FFF', }); } } 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 685abc84ab5e76b0d0dad95912590b7926d4a742..96c0dd8aa692a8466cc55fedac7a237d5e0004e8 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 @@ -1,82 +1,80 @@ -<nb-card - [nbSpinner]="geojsonLoading" - nbSpinnerSize="giant" - nbSpinnerStatus="basic" -> - <nb-card-body> - <div - leaflet - id="map" - #map - [leafletOptions]="options" - (leafletMapReady)="onMapReady($event)" - > +<div class="row d-flex justify-content-between align-items-center"> + <nb-card class="col-12"> + <nb-card-header + >{{ header }} + <button nbButton hero status="warning" (click)="changeToAmsterdam()"> + change to amsterdam + </button> + <button nbButton hero status="info" (click)="changeToBilbao()"> + change to bilbao + </button> + <button nbButton hero status="success" (click)="changeToHelsinki()"> + change to helsinki + </button> + <button nbButton hero status="primary" (click)="changeToMessina()"> + change to messina + </button> + </nb-card-header> + <nb-card-body height="1000em"> <div + leaflet + #map + [leafletOptions]="options" + [leafletLayersControl]="hardcodedCheckboxlayersControl" + [leafletLayers]="layers" + (leafletMapReady)="onMapReady($event)" + ></div> + <!-- <div + class="row" style=" - z-index: 1003; - position: absolute; - top: 10px; - left: 57px; - background-color: #fffd; - border-radius: 0.25rem; + visibility: visible; + background: #ffffffcc !important; + padding: 8px 16px; " > - <button - nbButton - status="primary" - size="small" - (click)="onLoad('bikeability')" - nbTooltip="Select a simulation in order to use this button. GET http://localhost:8083/dss/geojson/{id}/selectedKpi" - [disabled]="selectedSimulation == undefined" - [nbSpinner]="geojsonLoading" - style="margin: 2px;" - > - load bikeability geojson - </button> - <button - nbButton - status="primary" - size="small" - (click)="onLoad('bikeIntensity')" - nbTooltip="Select a simulation in order to use this button. GET http://localhost:8083/dss/geojson/{id}/selectedKpi" - [disabled]="selectedSimulation == undefined" - [nbSpinner]="geojsonLoading" - style="margin: 2px;" - > - load bike intensity geojson - </button> - <button - nbButton - status="primary" - size="small" - (click)="onLoad('bikeSafety')" - nbTooltip="Select a simulation in order to use this button. GET http://localhost:8083/dss/geojson/{id}/selectedKpi" - [disabled]="selectedSimulation == undefined" - [nbSpinner]="geojsonLoading" - style="margin: 2px;" - > - load bike safety geojson - </button> - <button - nbButton - status="primary" - size="small" - (click)="onLoad('pollution')" - nbTooltip="Select a simulation in order to use this button. GET http://localhost:8083/dss/geojson/{id}/selectedKpi" - [disabled]="selectedSimulation == undefined" - [nbSpinner]="geojsonLoading" - style="margin: 2px;" - > - load pollution geojson - </button> - </div> - </div> - <div id="grayscaleInput"> - <input - type="range" - [(ngModel)]="grayscale" - (change)="grayscaleChange()" - /> - </div> - </nb-card-body> -</nb-card> + <div class="col-md-12"> + <div> + <nb-checkbox + id="checkboxCO2" + [checked]="false" + [(ngModel)]="isCheckboxCO2Checked" + (change)="onCheckboxChange($event)" + > + CO2 + </nb-checkbox> + </div> + <div> + <nb-checkbox + id="checkboxCO" + [checked]="false" + [(ngModel)]="isCheckboxCOChecked" + (change)="onCheckboxChange($event)" + > + CO + </nb-checkbox> + </div> + <div> + <nb-checkbox + id="checkboxNOx" + [checked]="false" + [(ngModel)]="isCheckboxNOxChecked" + (change)="onCheckboxChange($event)" + > + NOx + </nb-checkbox> + </div> + <div> + <nb-checkbox + id="checkboxCarCount" + [checked]="false" + [(ngModel)]="isCheckboxCarCountChecked" + (change)="onCheckboxChange($event)" + > + car count + </nb-checkbox> + </div> + </div> + </div> --> + </nb-card-body> + </nb-card> +</div> 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 deleted file mode 100644 index 85b498233ac8eafef6dcceb5800427d396c7c688..0000000000000000000000000000000000000000 --- a/src/app/pages/simulation-wizard/results-map/results-map.component.scss +++ /dev/null @@ -1,29 +0,0 @@ -@import "../../../@theme/styles/themes"; - -@include nb-install-component() { - nb-card-body { - padding: 0; - } - - ::ng-deep .leaflet-top, - ::ng-deep .leaflet-bottom { - z-index: 997; - } - - ::ng-deep .leaflet-container { - width: 100%; - height: nb-theme(card-height-large); - } - #grayscaleInput { - background: #fffd; - display: inline-block; - position: absolute; - bottom: 53px; - right: 10px; - z-index: 1002; - padding: 4px 3px 0 3px; - } -} -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 fc37eb9af54a23665e5f1278fe518e6356e24671..62be8e36ab78e6cfffe2f9adc639f1a0175a724c 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,17 +1,62 @@ -import { Component, Input } from "@angular/core"; +import { + ChangeDetectorRef, + Component, + OnInit, + AfterViewInit, + SimpleChanges, + Input, + OnChanges, +} from "@angular/core"; + import * as L from "leaflet"; -import { SimulationService } from "../utils/services/simulation.service"; -import * as MapColors from "./map-colors-local"; -import { NbToastrService } from "@nebular/theme"; -import { Simulation } from "../utils/data/simulation"; +import AmstBaselineVehicleCount from "../../../../assets/map/AMST/amsterdam-baseline-vehicleCount.json"; +import AmstScenarioVehicleCount from "../../../../assets/map/AMST/amsterdam-scenario-vehicleCount.json"; +import BilbBaselineEmission from "../../../../assets/map/bilbao-baseline-emission-sample.json"; +import BilbScenarioEmission from "../../../../assets/map/bilbao-scenario-emission-sample.json"; +import HelsinkiBaselineVehicleCount from "../../../../assets/map/helsinki-baseline-vehicleCount-sample.json"; +import HelsinkiScenarioVehicleCount from "../../../../assets/map/helsinki-scenario-vehicleCount-sample.json"; +import MessinaJson from "../../../../assets/map/MESSINA/choropleth.json"; +import MessinaSantoStefano from "../../../../assets/map/MESSINA/santostefanodibriga-bus.json"; +import { Scenario } from "../utils/data/scenario"; +import AmsterdamChoropleth from "../../../../assets/map/AMSTERDAM/amsterdam22districts.json" @Component({ selector: "ngx-results-map", templateUrl: "./results-map.component.html", styleUrls: ["./results-map.component.scss"], }) -export class ResultsMapComponent { - public map: L.Map; +export class ResultsMapComponent implements OnInit, AfterViewInit, OnChanges { + @Input() selectedScenario: Scenario; + @Input() selectedSimulationId: number; + @Input() cityName: string; + + constructor(private cdRef: ChangeDetectorRef) {} + + map: L.Map; + mapLegend = new L.Control({ position: "bottomright" }); + // isCheckboxCO2Checked = false; + // isCheckboxCOChecked = false; + // isCheckboxNOxChecked = false; + // isCheckboxCarCountChecked = false; + header: string = ""; + // mock data + districtPopDensityKm2 = [ + 10, 100, 1000, 10000, 243, 565, 3234, 2354, 5346, 111, 430, 10, 100, 1000, + 10000, 243, 565, 3234, 2354, 5346, 111, 430, 10, + ]; // cooresponds with districts in amsterdam22districts.json id-s + districtPopAvgAge = [ + 30, 40, 50, 60, 70, 80, 90, 69, 420, 12, 67, 30, 40, 50, 60, 70, 80, 90, 69, + 420, 12, + ]; // cooresponds with districts in amsterdam22districts.json id-s + districtEducationLevelAvg = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, + ]; + districtIncomeAvg = [ + 10000, 22000, 42000, 200, 55000, 12, 10000, 22000, 42000, 200, 550000, 12, + 10000, 22000, 42000, 200, 55000, 12, 100000, 220000, 42000, 200, 55000, 12, + 69000, + ]; + options = { layers: [ L.tileLayer("http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { @@ -19,174 +64,369 @@ export class ResultsMapComponent { attribution: "...", }), ], - zoom: 5, - center: L.latLng({ lat: 50, lng: 14 }), + zoom: 11, }; - mapLegend = new L.Control({ position: "bottomright" }); + + icon = new L.Icon({ + iconSize: [25, 41], + iconAnchor: [13, 41], + iconUrl: "assets/img/markers/marker-icon.png", + iconRetinaUrl: "assets/img/markers/marker-icon-2x.png", + shadowUrl: "assets/img/markers/marker-shadow.png", + }); + + layer = L.marker([52.372664783594274, 4.8950958251953125], { + icon: this.icon + }).bindPopup("Amsterdam"); + layerChoropleth: L.GeoJSON; + layers = [this.layer]; + + // LAYERS + // #region L.geojson + ChoroplethAmsterdam22Districts = L.geoJSON(<any>AmsterdamChoropleth, { + style: (feature) => ({ + weight: 1, + color: "#3388ff", + opacity: 0.7, + fillOpacity: 0.12, + fillColor: "#46064a", + }), + onEachFeature: (feature, layer) => + layer.on({ + mouseover: (e) => this.highlightFeature(e, feature), + mouseout: (e) => this.resetFeature(e, feature), + }), + }); + ChoroplethAmsterdam22DistrictsDensity = L.geoJSON(<any>AmsterdamChoropleth, { + style: (feature) => ({ + weight: 1, + color: "#3388ff", + opacity: 0.5, + fillOpacity: 0.6, + fillColor: this.getColorDensity(this.districtPopDensityKm2[feature.id]), + }), + onEachFeature: (feature, layer) => + layer.on({ + mouseover: (e) => this.highlightFeature(e, feature), + mouseout: (e) => this.resetFeature(e, feature), + }), + }); + ChoroplethAmsterdam22DistrictsAge = L.geoJSON(<any>AmsterdamChoropleth, { + style: (feature) => ({ + weight: 1, + color: "#3388ff", + opacity: 0.5, + fillOpacity: 0.6, + fillColor: this.getColorAge(this.districtPopAvgAge[feature.id]), + }), + onEachFeature: (feature, layer) => + layer.on({ + mouseover: (e) => this.highlightFeature(e, feature), + mouseout: (e) => this.resetFeature(e, feature), + }), + }); + ChoroplethAmsterdam22DistrictsEducation = L.geoJSON( + <any>AmsterdamChoropleth, + { + style: (feature) => ({ + weight: 1, + color: "#3388ff", + opacity: 0.5, + fillOpacity: 0.6, + fillColor: this.getColorEducation( + this.districtEducationLevelAvg[feature.id] + ), + }), + onEachFeature: (feature, layer) => + layer.on({ + mouseover: (e) => this.highlightFeature(e, feature), + mouseout: (e) => this.resetFeature(e, feature), + }), + } + ); + ChoroplethAmsterdam22DistrictsIncome = L.geoJSON(<any>AmsterdamChoropleth, { + style: (feature) => ({ + weight: 1, + color: "#3388ff", + opacity: 0.5, + fillOpacity: 0.6, + fillColor: this.getColorIncome(this.districtIncomeAvg[feature.id]), + }), + onEachFeature: (feature, layer) => + layer.on({ + mouseover: (e) => this.highlightFeature(e, feature), + mouseout: (e) => this.resetFeature(e, feature), + }), + }); + AmstBaselineVehicleCount = L.geoJSON(<any>AmstBaselineVehicleCount, { + style: (feature) => ({ + weight: 5, + color: this.getColor(feature.properties, "vehicleCount"), + opacity: 0.8, + }), + }); + AmstScenarioVehicleCount = L.geoJSON(<any>AmstScenarioVehicleCount, { + style: (feature) => ({ + weight: 5, + color: this.getColor(feature.properties, "vehicleCount"), + opacity: 0.8, + }), + }); + BilbBaselineCarCounts = L.geoJSON(<any>BilbBaselineEmission, { + style: (feature) => ({ + weight: 5, + color: this.getColor(feature.properties, "carCount"), + opacity: 0.8, + }), + }); + BilbScenarioCarCounts = L.geoJSON(<any>BilbScenarioEmission, { + style: (feature) => ({ + weight: 5, + color: this.getColor(feature.properties, "carCount"), + opacity: 0.8, + }), + }); + BilbBaselineCO2 = L.geoJSON(<any>BilbBaselineEmission, { + style: (feature) => ({ + weight: 5, + color: this.getColor(feature.properties, "CO2"), + opacity: 0.8, + }), + }); + BilbScenarioCO2 = L.geoJSON(<any>BilbScenarioEmission, { + style: (feature) => ({ + weight: 5, + color: this.getColor(feature.properties, "CO2"), + opacity: 0.8, + }), + }); + BilbBaselineNO2 = L.geoJSON(<any>BilbBaselineEmission, { + style: (feature) => ({ + weight: 5, + color: this.getColor(feature.properties, "NO2"), + opacity: 0.8, + }), + }); + BilbScenarioNO2 = L.geoJSON(<any>BilbScenarioEmission, { + style: (feature) => ({ + weight: 5, + color: this.getColor(feature.properties, "NO2"), + opacity: 0.8, + }), + }); + BilbBaselineNOX = L.geoJSON(<any>BilbBaselineEmission, { + style: (feature) => ({ + weight: 5, + color: this.getColor(feature.properties, "NOX"), + opacity: 0.8, + }), + }); + BilbScenarioNOX = L.geoJSON(<any>BilbScenarioEmission, { + style: (feature) => ({ + weight: 5, + color: this.getColor(feature.properties, "NOX"), + opacity: 0.8, + }), + }); + public HelsinkiBaselineVehicleCount = L.geoJSON( + <any>HelsinkiBaselineVehicleCount, + { + style: (feature) => ({ + weight: 5, + color: this.getColor(feature.properties, "vehicleCount"), + opacity: 0.8, + }), + } + ); + public HelsinkiScenarioVehicleCount = L.geoJSON( + <any>HelsinkiScenarioVehicleCount, + { + style: (feature) => ({ + weight: 5, + color: this.getColor(feature.properties, "vehicleCount"), + opacity: 0.8, + }), + } + ); + public myMessina = L.geoJSON(<any>MessinaJson, { + style: (feature) => ({ + weight: 1, + color: "#5af", + opacity: 0.5, + fillOpacity: 0.6, + fillColor: "#6CF", // ((feature.properties && feature.properties.density) ? this.getColor(feature.properties.density, undefined) : "#3388ff"), + }), + }); + public myMessinaSantoStefano = L.geoJSON(<any>MessinaSantoStefano, { + style: (feature) => ({ + weight: 1, + color: "#5af", + opacity: 0.5, + fillOpacity: 0.6, + fillColor: "#6cf", // ((feature.properties && feature.properties.density) ? this.getColor(feature.properties.density, undefined) : "#3388ff"), + }), + }); + // #endregion + radioButtonLayersControl: any; - checkedRadioButton: string = ""; - geoJsonStyleWeight: number = 5; - geoJsonStyleOpacity: number = 0.8; - maxKeyValues: Object = {}; - ratiosBetweenGrades: number[] = [25.6, 12.8, 6.4, 3.2, 1.6]; - grayscale: any = 50; - geojsonLoading: boolean = false; - @Input() selectedSimulation: Simulation; - - constructor( - private simulationService: SimulationService, - private toastrService: NbToastrService - ) { } + + hardcodedCheckboxlayersControl: any = { + overlays: { + "Amsterdam districts": this.ChoroplethAmsterdam22Districts, + "Amsterdam districts (Colored by Density)": + this.ChoroplethAmsterdam22DistrictsDensity, + "Amsterdam districts (Colored by Age)": + this.ChoroplethAmsterdam22DistrictsAge, + "Amsterdam districts (Colored by Education)": + this.ChoroplethAmsterdam22DistrictsEducation, + "Amsterdam districts (Colored by Income)": + this.ChoroplethAmsterdam22DistrictsIncome, + }, + }; + + ngOnInit(): void { + console.log("we got duplicate layer controls, layercontrol and devlayercontrol") + this.layerChoropleth = this.ChoroplethAmsterdam22Districts; + } + + ngOnChanges(changes: SimpleChanges) { + this.selectedScenario = changes.selectedScenario.currentValue; + this.setHeader(); + if (this.selectedScenario) { + // Amsterdam + if (this.selectedScenario.name.toLowerCase().includes("amsterdam")) { + this.changeToAmsterdam(); + } + // Bilbao + else if (this.selectedScenario.name.toLowerCase().includes("bilbao")) { + this.changeToBilbao(); + } + // Helsinki + else if (this.selectedScenario.name.toLowerCase().includes("helsinki")) { + this.changeToHelsinki(); + } + // // Messina + else if (this.selectedScenario.name.toLowerCase().includes("messina")) { + this.changeToMessina(); + } else { + console.log("%cCity string not matched"); + } + } + } + + ngAfterViewInit(): void { + this.setHeader(); + + if (this.selectedScenario) { + // Amsterdam + if (this.selectedScenario.name.toLowerCase().includes("amsterdam")) { + this.changeToAmsterdam(); + } + // Bilbao + else if (this.selectedScenario.name.toLowerCase().includes("bilbao")) { + this.changeToBilbao(); + } + // Helsinki + else if (this.selectedScenario.name.toLowerCase().includes("helsinki")) { + this.changeToHelsinki(); + } + // Messina + else if (this.selectedScenario.name.toLowerCase().includes("messina")) { + this.changeToMessina(); + } else { + console.log("%cCity string not matched"); + } + } else { + console.log( + "%cScenario string not present", + "background: lightcoral; font-weight: bold; color: black; font-family: serif" + ); + } + } + onMapReady(map: L.Map) { this.map = map; - this.grayscaleChange(); setTimeout(() => { this.map.invalidateSize(); + this.map.addLayer(this.layerChoropleth); + this.setMapLocation(); }, 50); - } - - onLoad(selectedKpi: string) { - this.setMapZoom() - if (this.radioButtonLayersControl) - this.radioButtonLayersControl.remove(this.map); + map.on("overlayadd", (eventLayer) => { + //console.log(eventLayer); + if (eventLayer["name"].includes("Density")) this.setLegend("Density"); + else if (eventLayer["name"].includes("Age")) this.setLegend("Age"); + else if (eventLayer["name"].includes("Education")) + this.setLegend("Education"); + else if (eventLayer["name"].includes("Income")) this.setLegend("Income"); + }); - this.toastrService.show("This might take a while", "Info", { - status: "info", + map.on("overlayremove", (eventLayer) => { + if ( + eventLayer["name"].includes("Density") || + eventLayer["name"].includes("Age") || + eventLayer["name"].includes("Education") || + eventLayer["name"].includes("Income") + ) + // we only remove legends when we remove layers with legends + this.map.removeControl(this.mapLegend); }); - this.geojsonLoading = true; - console.log("remove later the -2") - this.simulationService.getGeojson((this.selectedSimulation["id"]-2).toString(), selectedKpi).subscribe( - (res) => { - console.log("🚀☢ ~ res", res) - if (res["message"] == "Calculating the KPI visualization.") { - this.toastrService.show( - "Server says: Calculating the KPI visualization. GEOJSON loading will not be completed.", - "Warning", - { - status: "danger", - } - ); - this.geojsonLoading = false; - return; - } - if (res["message"] == "Please prepare visualizations first.") { - this.toastrService.show( - "The visualization is not prepared yet, try again in a few minutes.", - "Warning", - { - status: "danger", - } - ); - this.geojsonLoading = false; - return; - } - this.toastrService.show("", "Geojson Loaded", { - status: "success", - }); - - const keys = this.extractKeys(res); - const propertyGeojsons = this.populatePropertyGeojsons(res, keys); - let obj = {}; - - for (let i = 0; i < propertyGeojsons.length; i++) { - if ( - propertyGeojsons[i].myKey == "link_id" || - propertyGeojsons[i].myKey == "modes" || - 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.geojsonLoading = false; - }, - (error) => { - this.toastrService.show(error["message"], "Error Getting Geojson", { - status: "danger", - }); - this.geojsonLoading = false; - }, - ); + } + // Unused + onCheckboxChange(e) { + console.log(e); } // LEGEND setLegend(legendType) { this.mapLegend.onAdd = (map) => { - let direction: string; - if ( - legendType == "PM" || - legendType == "CO2_rep" || - legendType == "CO" || - legendType == "CO2_TOTAL" || - legendType == "HC" || - legendType == "NOx" || - legendType == "pedestrianTravelTime" - ) { - direction = "lowToHigh"; - } else { - direction = "highToLow"; - } - var div = L.DomUtil.create("div"); - const grades = [ - 0, - this.limitMyDigits( - this.maxKeyValues["maxValue_" + legendType] / - this.ratiosBetweenGrades[0] - ), - this.limitMyDigits( - this.maxKeyValues["maxValue_" + legendType] / - this.ratiosBetweenGrades[1] - ), - this.limitMyDigits( - this.maxKeyValues["maxValue_" + legendType] / - this.ratiosBetweenGrades[2] - ), - this.limitMyDigits( - this.maxKeyValues["maxValue_" + legendType] / - this.ratiosBetweenGrades[3] - ), - this.limitMyDigits( - this.maxKeyValues["maxValue_" + legendType] / - this.ratiosBetweenGrades[4] - ), - ]; - - 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++) { + var labels = []; + + if (legendType === "Density") { + let grades = [200, 400, 800, 1600, 3200]; + // loop through our density intervals and generate a label with a colored square for each interval + for (let i = 0; i < grades.length; i++) { div.innerHTML += "<p style='background:white; display: inline; font-size:17px'> <span style='color:" + - MapColors.getColorForLegend(i, grades) + + this.getColorDensity(grades[i] + 1) + "'>■</span> " + grades[i] + (grades[i + 1] ? "–" + grades[i + 1] + "</p>" : "+</p>"); } - } else { - for (let i = 5; i > 0; i--) { + } else if (legendType === "Age") { + let grades = [30, 40, 50, 60, 75, 90]; + // loop through our density intervals and generate a label with a colored square for each interval + for (let i = 0; i < grades.length; i++) { + div.innerHTML += + "<p style='background:white; display: inline; font-size:17px'> <span style='color:" + + this.getColorAge(grades[i] + 1) + + "'>■</span> " + + grades[i] + + (grades[i + 1] ? "–" + grades[i + 1] + "</p>" : "+</p>"); + } + } else if (legendType == "Education") { + let grades = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + // loop through our density intervals and generate a label with a colored square for each interval + for (let i = 0; i < grades.length; i++) { div.innerHTML += "<p style='background:white; display: inline; font-size:17px'> <span style='color:" + - MapColors.getColorForLegend(5 - i, grades) + + this.getColorEducation(grades[i] + 1) + + "'>■</span> " + + grades[i] + + (grades[i + 1] ? "–" + grades[i + 1] + "</p>" : "+</p>"); + } + } else if (legendType == "Income") { + let grades = [5000, 20000, 50000, 80000, 120000]; + // loop through our density intervals and generate a label with a colored square for each interval + for (let i = 0; i < grades.length; i++) { + div.innerHTML += + "<p style='background:white; display: inline; font-size:17px'> <span style='color:" + + this.getColorIncome(grades[i] + 1) + "'>■</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; @@ -194,350 +434,299 @@ export class ResultsMapComponent { this.mapLegend.addTo(this.map); } + // CORE FUNCTIONS 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"]) { - const tmp = this.limitMyDigits(feature["cityWide"]["pollution"][p]); - if (p == this.checkedRadioButton) { - text += `<u><strong>${p}:</strong> ${tmp}</u><br>`; - } else { - text += `<strong>${p}:</strong> ${tmp}<br>`; - } - text += `<span style='display: inline-block; margin-bottom: 7px; color: #666'>(unrounded) ${feature["cityWide"]["pollution"][p]}</span><br>`; - } - for (let p in feature["cityWide"]["traffic"]) { - const tmp = this.limitMyDigits(feature["cityWide"]["traffic"][p]); - if (p == this.checkedRadioButton) { - text += `<u><strong>${p}:</strong> ${tmp}</u><br>`; - } else { - text += `<strong>${p}:</strong> ${tmp}<br>`; - } - text += `<span style='display: inline-block; margin-bottom: 7px; color: #666'>(unrounded) ${feature["cityWide"]["traffic"][p]}</span><br>`; - } - } - - Object.entries(feature).forEach(element => { - if (element[0] == this.checkedRadioButton) { - text += `<u><strong>${element[0]}:</strong> ${element[1]}</u><br>`; - } else { - text += `<strong>${element[0]}:</strong> ${element[1]}<br>`; - } - text += `<span style='display: inline-block; margin-bottom: 7px; color: #666'>(unrounded) ${element[1]}</span><br>`; - }); + let text = + "Average population density: " + + this.districtPopDensityKm2[feature.id] + + " people/m2<br>" + + "Average age: " + + this.districtPopAvgAge[feature.id] + + " years<br>" + + "Average education level: " + + this.districtEducationLevelAvg[feature.id] + + "<br>" + + "Average income: " + + this.districtIncomeAvg[feature.id] + + " EUR per year"; - if (!text.length) - text = "<span style='color: lightslategray';>No data available.</span>"; layer.bindPopup(text); + layer.openPopup(); layer.setStyle({ - weight: 10, + weight: 2, opacity: 1.0, + color: "#3388ff", + //fillOpacity: 0, + //fillColor: '#FFF' }); } private resetFeature(e, feature) { const layer = e.target; + layer.closePopup(); layer.setStyle({ - weight: this.geoJsonStyleWeight, - opacity: this.geoJsonStyleOpacity, + weight: 1, + opacity: 0.7, + color: "#3388ff", + //fillOpacity: 0.7, + //fillColor: '#FFF', }); } - public extractKeys(jsonFile) { - console.log("🚀☢ ~ jsonFile", jsonFile) - let allKeys = []; - for (let f = 0; f < jsonFile["features"].length && f < 3498; f++) { - let iterator; - Object.keys(jsonFile["features"][f]["properties"]).forEach(element => { - if (!allKeys.includes(element)) { - allKeys.push(element); - } - }) - if (jsonFile["features"][f]["properties"]["capacity"]) { - if (!allKeys.includes("capacity")) { - allKeys.push("capacity"); - } - } - if (jsonFile["features"][f]["properties"]["cityWide"] && jsonFile["features"][f]["properties"]["cityWide"]["pollution"]) { - iterator = Object.keys( - jsonFile["features"][f]["properties"]["cityWide"]["pollution"] - ); - for (let g = 0; g < iterator.length; g++) { - if (!allKeys.includes(iterator[g])) { - allKeys.push(iterator[g]); - } + public getLayer(title, l): L.GeoJSON { + let layer = L.geoJSON(l, { + onEachFeature: (feature, layer) => { + let text = ""; + for (let p in feature.properties) { + text += `${p}: ${feature.properties[p]}\n`; } - } - if (jsonFile["features"][f]["properties"]["cityWide"] && jsonFile["features"][f]["properties"]["cityWide"]["traffic"]) { - iterator = Object.keys( - jsonFile["features"][f]["properties"]["cityWide"]["traffic"] - ); - for (let g = 0; g < iterator.length; g++) { - if (!allKeys.includes(iterator[g])) { - allKeys.push(iterator[g]); - } - } - } + layer.bindPopup(text); + }, + style: function () { + if (title == "2020") + return { color: "#248175", weight: 7, opacity: 0.8 }; + else return { color: "#242c81", weight: 5, opacity: 0.8 }; + }, + }).on("popupclose", ($event) => {}); + + layer["_id"] = title; + return layer; + } + + setLayerToFront($event) { + this.map.removeLayer(this.layerChoropleth); + + if ($event == "2018") { + this.map.addLayer(this.layerChoropleth); + } else if ($event == "2020") { + this.map.addLayer(this.layerChoropleth); + } else { + this.map.addLayer(this.layerChoropleth); } - return allKeys; + + this.map.invalidateSize(); + this.cdRef.detectChanges(); } - public populatePropertyGeojsons(jsonFile, keys) { - let res = []; - this.populateMaxKeyValues(jsonFile, keys); - - for (let f = 0; f < keys.length; f++) { - let tmp: Object = {}; - 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]}`], - this.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), - }), - }); - } 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]}`], - this.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), - }), - }); - } 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]}`], - this.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), - }), - }); - } else { - tmp = L.geoJSON(<any>jsonFile, { - style: (feature) => ({ - weight: this.geoJsonStyleWeight, - color: MapColors.getColor( - feature["properties"], - keys[f], - this.maxKeyValues[`maxValue_${keys[f]}`], - this.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), - }), - }); - } - tmp["myKey"] = keys[f]; - res.push(tmp); + setMapLocation() { + console.warn("city name: ", this.cityName); + this.setHeader(); + + if (this.cityName.toLowerCase().includes("amsterdam")) { + this.map.setView(new L.LatLng(52, 5), 10); + } else if (this.cityName.toLowerCase().includes("bilbao")) { + this.map.setView(new L.LatLng(43, -3), 10); + } else if (this.cityName.toLowerCase().includes("helsinki")) { + this.map.setView(new L.LatLng(60, 25), 10); + } else if (this.cityName.toLowerCase().includes("messina")) { + this.map.setView(new L.LatLng(38, 15), 10); + } else if (this.cityName.toLowerCase().includes("spassk-dalny")) { + this.map.setView(new L.LatLng(44, 132), 10); + } else { + console.log( + "%csomething went wrong", + "background: red; font-weight: bold; color: black" + ); } - return res; } - public addListenerToRadioButtonLayersControl() { - this.map.on("baselayerchange", (e) => { - if (e["name"] == "CO") { - this.setLegend("CO"); - this.checkedRadioButton = "CO"; - } else if (e["name"] == "CO2_TOTAL") { - this.setLegend("CO2_TOTAL"); - this.checkedRadioButton = "CO2_TOTAL"; - } else if (e["name"] == "HC") { - this.setLegend("HC"); - this.checkedRadioButton = "HC"; - } else if (e["name"] == "NOx") { - this.setLegend("NOx"); - this.checkedRadioButton = "NOx"; - } else if (e["name"] == "PM") { - this.setLegend("PM"); - this.checkedRadioButton = "PM"; - } else if (e["name"] == "CO2_rep") { - this.setLegend("CO2_rep"); - this.checkedRadioButton = "CO2_rep"; - } else if (e["name"] == "dailyInternalBikeTravels") { - this.setLegend("dailyInternalBikeTravels"); - this.checkedRadioButton = "dailyInternalBikeTravels"; - } else if (e["name"] == "pedestrianTravelTime") { - this.setLegend("pedestrianTravelTime"); - this.checkedRadioButton = "pedestrianTravelTime"; - } else if (e["name"] == "capacity") { - this.setLegend("capacity"); - this.checkedRadioButton = "capacity"; - } else { - this.setLegend(undefined); - this.checkedRadioButton = undefined; - } - }); + // moves map to Amsterdam center and TODO load the amsterdam data + changeToAmsterdam() { + if (this.map) { + if (this.radioButtonLayersControl) this.radioButtonLayersControl.remove(this.map); + this.map.setView(new L.LatLng(52.4, 4.9), 11); + this.radioButtonLayersControl = new L.Control.Layers( + { + AmstBaselineVehicleCount: this.AmstBaselineVehicleCount, + AmstScenarioVehicleCount: this.AmstScenarioVehicleCount, + }, + null, + { collapsed: false } + ); + this.map.addControl(this.radioButtonLayersControl); + } } - public populateMaxKeyValues(jsonFile: Object, keys: Object[]) { - for (let g = 0; g < jsonFile["features"].length; g++) { - for (let f = 0; f < keys.length; f++) { - if (jsonFile["features"][g]["properties"]["cityWide"] && jsonFile["features"][g]["properties"]["cityWide"]["pollution"]) { - if (this.maxKeyValues[`maxValue_${keys[f]}`] == undefined) { - this.maxKeyValues[`maxValue_${keys[f]}`] = - jsonFile["features"][g]["properties"]["cityWide"]["pollution"][ - keys[f] - ]; - } - if ( - jsonFile["features"][g]["properties"]["cityWide"]["pollution"][ - keys[f] - ] > this.maxKeyValues[`maxValue_${keys[f]}`] - ) { - this.maxKeyValues[`maxValue_${keys[f]}`] = - jsonFile["features"][g]["properties"]["cityWide"]["pollution"][ - keys[f] - ]; - } - } - if (jsonFile["features"][g]["properties"]["cityWide"] && jsonFile["features"][g]["properties"]["cityWide"]["traffic"]) { - if (this.maxKeyValues[`maxValue_${keys[f]}`] == undefined) { - this.maxKeyValues[`maxValue_${keys[f]}`] = - jsonFile["features"][g]["properties"]["cityWide"]["traffic"][ - keys[f] - ]; - } - if ( - jsonFile["features"][g]["properties"]["cityWide"]["traffic"][ - keys[f] - ] > this.maxKeyValues[`maxValue_${keys[f]}`] - ) { - this.maxKeyValues[`maxValue_${keys[f]}`] = - jsonFile["features"][g]["properties"]["cityWide"]["traffic"][ - keys[f] - ]; - } - } - if (jsonFile["features"][g]["properties"]["capacity"]) { - const parsed = parseInt( - jsonFile["features"][g]["properties"]["capacity"] - ); - if (parsed != 9999) { - if (this.maxKeyValues[`maxValue_capacity`] == undefined) { - this.maxKeyValues[`maxValue_capacity`] = parsed; - } - if (parsed > this.maxKeyValues[`maxValue_capacity`]) { - this.maxKeyValues[`maxValue_capacity`] = parsed; - } - } - } - if (jsonFile["features"][g]["properties"][keys[f]]) { - if (this.maxKeyValues[`maxValue_${keys[f]}`] == undefined) { - this.maxKeyValues[`maxValue_${keys[f]}`] = - jsonFile["features"][g]["properties"][keys[f]] - } - if ( - jsonFile["features"][g]["properties"][keys[f]] > this.maxKeyValues[`maxValue_${keys[f]}`] - ) { - this.maxKeyValues[`maxValue_${keys[f]}`] = - jsonFile["features"][g]["properties"][keys[f]] - } - } - } + // moves map to Bilbao center, zooms and loads the bilbao data + changeToBilbao() { + if (this.map) { + if (this.radioButtonLayersControl) this.radioButtonLayersControl.remove(this.map); + this.map.setView(new L.LatLng(43.25974033178419, -2.947339117090451), 15); + // TODO replace with backend calls + this.radioButtonLayersControl = new L.Control.Layers( + { + "Baseline Vehicle Counts": this.BilbBaselineCarCounts, + "Baseline CO2 emissions": this.BilbBaselineCO2, + "Baseline NO2 emissions": this.BilbBaselineNO2, + "Baseline NOX emissions": this.BilbBaselineNOX, + "Scenario-1 Vehicle Counts": this.BilbScenarioCarCounts, + "Scenario-1 CO2 emissions": this.BilbScenarioCO2, + "Scenario-1 NO2 emissions": this.BilbScenarioNO2, + "Scenario-1 NOX emissions": this.BilbScenarioNOX, + }, + null, + { collapsed: false } + ); + this.map.addControl(this.radioButtonLayersControl); } } - public limitMyDigits(n: number): number { - if (n > 1000) { - return Math.round(n); - } else if (n > 100) { - return Math.round(n); - } else if (n > 10) { - return Math.floor(Math.round(n * 10)) / 10; - } else if (n > 1) { - return Math.floor(Math.round(n * 100)) / 100; - } else if (n > 0.1) { - return Math.floor(Math.round(n * 1000)) / 1000; - } else if (n > 0.01) { - return Math.floor(Math.round(n * 10000)) / 10000; - } else if (n > 0.001) { - return Math.floor(Math.round(n * 100000)) / 100000; - } else if (n > 0.0001) { - return Math.floor(Math.round(n * 1000000)) / 1000000; - } else if (n > 0.00001) { - return Math.floor(Math.round(n * 10000000)) / 10000000; - } else { - return n; + // moves map to Helsinki and TODO loads the data + changeToHelsinki() { + if (this.map) { + if (this.radioButtonLayersControl) this.radioButtonLayersControl.remove(this.map); + this.map.setView(new L.LatLng(60.1, 24.9), 11); + this.radioButtonLayersControl = new L.Control.Layers( + { + "HelsinkiBaselineVehicleCount - currently all red": + this.HelsinkiBaselineVehicleCount, + "HelsinkiScenarioVehicleCount - currently all red": + this.HelsinkiScenarioVehicleCount, + }, + null, + { collapsed: false } + ); + this.map.addControl(this.radioButtonLayersControl); } } - grayscaleChange() { - const el = <HTMLElement>( - document.querySelectorAll(".leaflet-pane.leaflet-tile-pane")[0] - ); - el.style.filter = "grayscale(" + (100 - this.grayscale) + "%)"; + // // moves map to Messina and loads the data + changeToMessina() { + if (this.map) { + if (this.radioButtonLayersControl) this.radioButtonLayersControl.remove(this.map); + this.map.setView(new L.LatLng(38.2, 15.5), 11); + this.radioButtonLayersControl = new L.Control.Layers({ + "Messina Sample Choropleth": this.myMessina, + "Messina Santo Stefano di Briga": this.myMessinaSantoStefano, + }); + } } - setMapZoom() { - if (this.selectedSimulation["city"]["cityId"] == "amsterdam") { - this.map.setView(new L.LatLng(52.4, 4.9), 9); - } else if (this.selectedSimulation["city"]["cityId"] == "bilbao") { - this.map.setView(new L.LatLng(43.3, -2.9), 9); - } else if (this.selectedSimulation["city"]["cityId"] == "helsinki") { - this.map.setView(new L.LatLng(60.2, 24.9), 9); - } else if (this.selectedSimulation["city"]["cityId"] == "messina") { - this.map.setView(new L.LatLng(38.2, 15.6), 9); + public setHeader() { + // #TODO will header be automatically updated in html when scenario change? + if (this.selectedScenario) this.header = this.selectedScenario.name; + } + + // returns color based on value TODO add color axis scaling to data + // #region color + public getColor(properties: any, pro: any): string { + let value = undefined; + for (let key in properties) { + if (key == pro) { + value = properties[key]; + } + } + if (pro == "carCount" || pro == "vehicleCount") { + return value > 256 + ? "#3f53c5" + : value > 64 + ? "#97b8ff" + : value > 16 + ? "#dcddde" + : value > 4 + ? "#ea7c61" + : "#bc212e"; + } else if (pro == "CO2") { + return value > 4000 + ? "#3f53c5" + : value > 1000 + ? "#97b8ff" + : value > 250 + ? "#dcddde" + : value > 62 + ? "#ea7c61" + : "#bc212e"; + } else if (pro == "NOX") { + return value > 8 + ? "#3f53c5" + : value > 2 + ? "#97b8ff" + : value > 0.5 + ? "#dcddde" + : value > 0.0125 + ? "#ea7c61" + : "#bc212e"; + } else if (pro == "NO2") { + return value > 4 + ? "#2b83ba" + : value > 1 + ? "#97b8ff" + : value > 0.25 + ? "#dcddde" + : value > 0.00625 + ? "#ea7c61" + : "#bc212e"; } else { - this.toastrService.show( - "Unexpected value for city name.", - "Error", - { - status: "danger", - } - ); + return value > 256 + ? "#2b83ba" + : value > 64 + ? "#97b8ff" + : value > 16 + ? "#dcddde" + : value > 4 + ? "#ea7c61" + : "#bc212e"; } } + + public getColorDensity(d: number): string { + return d > 6400 + ? "#fa0000" + : d > 3200 + ? "#ff2929" + : d > 1600 + ? "#ff5252" + : d > 800 + ? "#ff7575" + : d > 400 + ? "#ff9191" + : d > 200 + ? "#ffb5b5" + : "#ffd6d6"; + } + + public getColorAge(d: number): string { + return d > 90 + ? "#08589e" + : d > 75 + ? "#2b8cbe" + : d > 60 + ? "#4eb3d3" + : d > 50 + ? "#7bccc4" + : d > 40 + ? "#a8ddb5" + : d > 30 + ? "#ccebc5" + : "#f0f9e8"; + } + + public getColorEducation(d: number): string { + return d > 7 + ? "#00ff37" + : d > 5 + ? "#3dff67" + : d > 3 + ? "#70ff8f" + : d > 1 + ? "#8affa3" + : "#c4ffd1"; + } + + public getColorIncome(d: number): string { + return d > 12000 + ? "#f2ff00" + : d > 80000 + ? "#f4ff30" + : d > 50000 + ? "#f6ff54" + : d > 20000 + ? "#f8ff75" + : d > 5000 + ? "#fbffb3" + : "#fafcd2"; + } + // #endregion } diff --git a/src/app/pages/simulation-wizard/results/results.component.html b/src/app/pages/simulation-wizard/results/results.component.html index cfad705cea0c9c174183b9e4d219e45b3775ff89..927aa2a4138ae1024412fcc2bf804b4bdba78b57 100644 --- a/src/app/pages/simulation-wizard/results/results.component.html +++ b/src/app/pages/simulation-wizard/results/results.component.html @@ -1,11 +1,26 @@ <section style="position: relative"> - <ngx-existing-simulations - (selectedSimulationToEmit)="setSelectedSimulation($event)" - > + + <span>City: </span> + <nb-select + placeholder="Select City" + style="margin-bottom: 16px; min-width: 240px" + appearance="filled" + [(selected)]="selectedCity"> + <nb-option value="amsterdam">Amsterdam</nb-option> + <nb-option value="bilbao">Bilbao</nb-option> + <nb-option value="helsinki">Helsinki</nb-option> + <nb-option value="messina">Messina</nb-option> + </nb-select> + + <ngx-existing-simulations [selectedCity]="selectedCity"> </ngx-existing-simulations> + <ngx-results-map - [selectedSimulation]="selectedSimulation" - ></ngx-results-map> - <ngx-our-spider-chart [selectedSimulation]="selectedSimulation"> + [selectedScenario]="selectedScenario" + [cityName]="'selectedCity'"> + </ngx-results-map> + + <ngx-our-spider-chart> </ngx-our-spider-chart> + </section> diff --git a/src/app/pages/simulation-wizard/results/results.component.ts b/src/app/pages/simulation-wizard/results/results.component.ts index 114188779b2324d0813e1fe5743d71bb5a65923d..6f28adf838f75257eee2d1b672c8ba4e1ba7aa9d 100644 --- a/src/app/pages/simulation-wizard/results/results.component.ts +++ b/src/app/pages/simulation-wizard/results/results.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from "@angular/core"; import { ComService } from "../utils/services/com.service"; import { Scenario } from "../utils/data/scenario"; -import { Simulation } from "../utils/data/simulation"; +import { SimulationService } from "../utils/services/simulation.service"; @Component({ selector: "ngx-results", @@ -12,11 +12,15 @@ export class ResultsComponent implements OnInit { scenarios: Scenario[]; selectedScenarioId?: number; selectedScenario?: Scenario; - selectedSimulation: Simulation; - constructor(private comService: ComService) { } + selectedCity: string = "bilbao"; + constructor( + private comService: ComService, + private simulationService: SimulationService + ) {} - ngOnInit(): void { } + ngOnInit(): void {} getSelectedScenario(): Scenario { + console.log("selected scenario:", this.selectedScenarioId); return this.selectedScenario; } selectScenario(scenarioId: number) { @@ -38,7 +42,4 @@ export class ResultsComponent implements OnInit { if (!found) console.error("could not find selected scenario with ID ", scenarioId); } - setSelectedSimulation(s: Simulation) { - this.selectedSimulation = s - } } diff --git a/src/app/pages/simulation-wizard/simulation-creation/simulation-creation.component.html b/src/app/pages/simulation-wizard/simulation-creation/simulation-creation.component.html index 53bff9ac7b71ca3dba10adf8351b427032766e58..c8e1d9e649ad54fbc10c87f1d2e69187efa00794 100644 --- a/src/app/pages/simulation-wizard/simulation-creation/simulation-creation.component.html +++ b/src/app/pages/simulation-wizard/simulation-creation/simulation-creation.component.html @@ -1,8 +1,8 @@ -<section id="simulation-creation-container"> - <nb-card> - <nb-card-header> Add New Simulation </nb-card-header> - <nb-card-body> - <form [formGroup]="addSimulationForm" (ngSubmit)="addSimulationSubmit()"> +<nb-card> + <nb-card-header> Add simulation </nb-card-header> + <nb-card-body> + <form [formGroup]="addSimulationForm" (ngSubmit)="addSimulationSubmit()"> + <div class="inner"> <input type="text" nbInput @@ -12,6 +12,7 @@ [status]="checkValid(name) ? 'danger' : 'basic'" nbTooltip="Name is required" [nbTooltipTrigger]="$any(checkValid(name) ? 'hint' : 'noop')" + (ngModelChange)="onInputNameChange($event)" /> <input type="text" @@ -21,33 +22,42 @@ name="simulationDescription" /> <nb-select + placeholder="Select Scenario" formControlName="selectedScenario" (ngModelChange)="onSelectScenarioChange($event)" - [(selected)]="selectedScenario" > - <nb-option *ngFor="let scenario of scenarios" [value]="scenario">{{ + <nb-option *ngFor="let scenario of scenarios" [value]="scenario.id">{{ scenario.name }}</nb-option> </nb-select> <nb-select + placeholder="Select Network" formControlName="selectedNetwork" - [(selected)]="selectedNetwork" + (ngModelChange)="onSelectNetworkChange($event)" > - <nb-option *ngFor="let network of networks" [value]="network">{{ + <nb-option *ngFor="let network of networks" [value]="network.id">{{ network.name }}</nb-option> </nb-select> + <nb-select + placeholder="Select Plan" + formControlName="selectedPlan" + (ngModelChange)="onSelectPlanChange($event)" + > + <nb-option *ngFor="let plan of plans" [value]="plan">{{ + plan + }}</nb-option> + </nb-select> <button nbButton type="submit" - [disabled]="!addSimulationForm.get('name').value" + [disabled]="addSimulationBtnDisabled" status="primary" style="margin-top: auto" > - confirm + add simulation </button> - <button nbButton status="danger" (click)="onDiscard()">discard</button> - </form> - </nb-card-body> - </nb-card> -</section> + </div> + </form> + </nb-card-body> +</nb-card> diff --git a/src/app/pages/simulation-wizard/simulation-creation/simulation-creation.component.scss b/src/app/pages/simulation-wizard/simulation-creation/simulation-creation.component.scss index 8e52339fb277873f62d4f6b19f13e7141d1a287a..4d71df76aefe1c9be2d5beb918d532a4ff513b27 100644 --- a/src/app/pages/simulation-wizard/simulation-creation/simulation-creation.component.scss +++ b/src/app/pages/simulation-wizard/simulation-creation/simulation-creation.component.scss @@ -1,9 +1,9 @@ -#simulation-creation-container { - padding: 1.25rem 2rem; - form { - display: flex; - height: 28em; - justify-content: center; +form { + display: flex; + height: 30em; + justify-content: center; + + .inner { flex-basis: 40%; display: flex; flex-direction: column; diff --git a/src/app/pages/simulation-wizard/simulation-creation/simulation-creation.component.ts b/src/app/pages/simulation-wizard/simulation-creation/simulation-creation.component.ts index 610534e83a220064e9d09cbab54381b1595e0aa6..ba2886b22b47ba4bffc78075d123a82503f069e8 100644 --- a/src/app/pages/simulation-wizard/simulation-creation/simulation-creation.component.ts +++ b/src/app/pages/simulation-wizard/simulation-creation/simulation-creation.component.ts @@ -11,7 +11,6 @@ import { ScenarioService } from "../utils/services/scenario.service"; import { SimulationService } from "../utils/services/simulation.service"; import { Scenario } from "../utils/data/scenario"; import { Simulation } from "../utils/data/simulation"; -import { NbToastrService, NbDialogRef } from "@nebular/theme"; @Component({ selector: "ngx-simulation-creation", @@ -24,8 +23,11 @@ export class SimulationCreationComponent implements OnInit { city: City; scenarios: Scenario[] = []; simulations: Simulation[] = []; + plans: Object[] = []; + addSimulationBtnDisabled: boolean = true; + addSimulationForm = this.fb.group({ - name: ["Sample name", Validators.required], + name: ["", Validators.required], description: [""], selectedScenario: { value: undefined, @@ -33,11 +35,13 @@ export class SimulationCreationComponent implements OnInit { }, selectedNetwork: { value: undefined, - disabled: false, + disabled: true, + }, + selectedPlan: { + value: "Dummy", + disabled: true, }, }); - selectedScenario; - selectedNetwork; constructor( private networkService: NetworkService, @@ -45,9 +49,7 @@ export class SimulationCreationComponent implements OnInit { private fb: FormBuilder, private scenarioService: ScenarioService, private simulationService: SimulationService, - private rxStompService: RxStompService, - private toastrService: NbToastrService, - protected dialogRef: NbDialogRef<any> + private rxStompService: RxStompService ) {} ngOnInit(): void { @@ -58,6 +60,10 @@ export class SimulationCreationComponent implements OnInit { this.rxStompService .watch("/topic/networkProcessingStatus") .subscribe((updatedNetworkFrame) => { + console.log( + "🚀 ~ file: simulation-creation.component.ts ~ line 90 ~ SimulationCreationComponent ~ .subscribe ~ updatedNetworkFrame", + updatedNetworkFrame + ); let updatedNetwork = JSON.parse(updatedNetworkFrame.body); let index = this.networks.findIndex( (network) => network.id == updatedNetwork.id @@ -75,71 +81,63 @@ export class SimulationCreationComponent implements OnInit { } }); - this.scenarioService.getScenarios().subscribe( - (data) => { - this.scenarios = data; - this.selectedScenario = data[0]; - }, - (error) => { - this.toastrService.show( - `${error["message"]}`, - `${error["error"]["error"]}`, - { - status: "danger", - } - ); - } - ); + this.scenarioService.getScenarios().subscribe((data) => { + this.scenarios = data; + }); this.networkService.getNetworks().subscribe( (data) => { + console.log( + "🚀 ~ file: simulation-creation.component.ts ~ line 117 ~ SimulationCreationComponent ~ this.networkService.getNetworks ~ data", + data + ); this.networks = data; - this.selectedNetwork = data[0]; }, (error) => { console.log( - "🚀 ~ file: simulation-creation.component.ts ~ line 105 ~ SimulationCreationComponent ~ this.networkService.getNetworks ~ error", + "🚀 ~ file: simulation-creation.component.ts ~ line 118 ~ SimulationCreationComponent ~ this.networkService.getNetworks ~ error", error ); - this.toastrService.show( - `${error["message"]}`, - `${error["error"]["error"]}`, - { - status: "danger", - } - ); } ); + + this.simulationService + .getPlans(<any>this.addSimulationForm.get("selectedNetwork")) + .subscribe((res) => { + this.plans = res; + }); } addSimulationSubmit() { - let sim = new Simulation(); - sim.name = this.addSimulationForm.controls.name.value; - sim.description = this.addSimulationForm.controls.description.value; - sim.networkId = this.selectedScenario.id; - sim.scenarioId = this.selectedNetwork.id; - this.simulationService.createSimulation(sim).subscribe( - (res) => { - this.toastrService.show(`${res["message"]}`, "Success", { - status: "success", - }); - }, - (error: object) => { - this.toastrService.show( - `${error["message"]}`, - `${error["error"]["error"]}`, - { - status: "danger", - } - ); - } + // #TODO + console.log( + "%c why city is udnefined? ", + "background: olivedrab; font-weight: bold; color: black; font-family: serif" + ); + console.log("🚀", this.addSimulationForm.controls.name.value); + console.log("🚀", this.addSimulationForm.controls.description.value); + console.log("🚀", this.addSimulationForm.controls.selectedScenario.value); + console.log("🚀", this.addSimulationForm.controls.selectedNetwork.value); + console.log("🚀", this.addSimulationForm.controls.selectedPlan.value); + + console.log( + "%c sample ", + "background: crimson; font-weight: bold; color: black; font-family: serif" ); + let sim = new Simulation(); + sim.name = "alvarez"; + sim.description = "butragueno"; + sim.networkId = 1; + sim.scenarioId = 1; + this.simulationService.createSimulation(sim).subscribe((res) => { + console.log("🚀 ~ file:~ res", res); + }); } onSelectNetwork(id: number) { this.networkService.getNetwork(id).subscribe((res) => { console.log( - "🚀 ~ file: simulation-creation.component.ts ~ line 155 ~ SimulationCreationComponent ~ this.networkService.getNetwork ~ res", + "🚀 ~ file: simulation-creation.component.ts ~ line 222 ~ SimulationCreationComponent ~ this.networkService.getNetwork ~ res", res.name, res.id, res.description, @@ -150,10 +148,22 @@ export class SimulationCreationComponent implements OnInit { }); } + onInputNameChange(evt) { + this.computeAddSimulationBtnDisabled(); + } + onSelectScenarioChange(evt) { this.addSimulationForm.controls.selectedNetwork.enable(); } + onSelectNetworkChange(evt) { + this.addSimulationForm.controls.selectedPlan.enable(); + } + + onSelectPlanChange(evt) { + this.computeAddSimulationBtnDisabled(); + } + onAnimationEventDone(event: AnimationEvent, index: number) { if ( event.toState == "show" && @@ -206,7 +216,14 @@ export class SimulationCreationComponent implements OnInit { return control.invalid && (control.dirty || control.touched); } - onDiscard() { - this.dialogRef.close(); + computeAddSimulationBtnDisabled() { + if ( + this.addSimulationForm.controls.name.value.length > 0 && + this.addSimulationForm.controls.selectedPlan.value + ) { + this.addSimulationBtnDisabled = false; + } else { + this.addSimulationBtnDisabled = true; + } } } diff --git a/src/app/pages/simulation-wizard/simulation-wizard-routing.module.ts b/src/app/pages/simulation-wizard/simulation-wizard-routing.module.ts index 77630ce741b6d872d3084c83af2f73425713cd4d..a197fd2768c5257236e250e588aaee964a7dfe47 100644 --- a/src/app/pages/simulation-wizard/simulation-wizard-routing.module.ts +++ b/src/app/pages/simulation-wizard/simulation-wizard-routing.module.ts @@ -4,6 +4,8 @@ import { NetworkCreationComponent } from "./network-creation/network-creation.co import { ResultsComponent } from "./results/results.component"; import { ScenariosComponent } from "./scenarios/scenarios.component"; import { OurSpiderChartComponent } from "./our-spider-chart/our-spider-chart.component"; +import { SimulationCreationComponent } from "./simulation-creation/simulation-creation.component"; +import { VisualizationsComponent } from "./visualizations/visualizations.component"; import { MlModuleComponent } from "./ml-module/ml-module.component"; import { RecommenderPageComponent } from "./recommender-page/recommender-page.component"; import { EditNetworkMapComponent } from "./edit-network-map/edit-network-map.component"; @@ -33,6 +35,14 @@ const routes: Routes = [ path: "spider", component: OurSpiderChartComponent, }, + { + path: "simulation-creation", + component: SimulationCreationComponent, + }, + { + path: "visualizations", + component: VisualizationsComponent, + }, { path: "recommender", component: RecommenderPageComponent, diff --git a/src/app/pages/simulation-wizard/simulation-wizard.module.ts b/src/app/pages/simulation-wizard/simulation-wizard.module.ts index caddaef5e9068636a177ed33d8e0dfc825e71e02..13099f141ee4ad454e3f9ce35a5fa421628a8f9b 100644 --- a/src/app/pages/simulation-wizard/simulation-wizard.module.ts +++ b/src/app/pages/simulation-wizard/simulation-wizard.module.ts @@ -21,9 +21,6 @@ import { NbToastrModule, NbDatepickerModule, NbProgressBarModule, - NbDialogModule, - NbSpinnerComponent, - NbSpinnerModule, } from "@nebular/theme"; import { InjectableRxStompConfig, @@ -46,23 +43,16 @@ SelectArea; // Dirty trick to load leaflet area select import { OurSpiderChartComponent } from "./our-spider-chart/our-spider-chart.component"; import { SimulationCreationComponent } from "./simulation-creation/simulation-creation.component"; import { ExistingSimulationsComponent } from "./existing-simulations/existing-simulations.component"; +import { VisualizationsComponent } from "./visualizations/visualizations.component"; import { MlModuleComponent } from "./ml-module/ml-module.component"; +import { VisualizationsGeojsonComponent } from "./visualizations/visualizations-geojson/visualizations-geojson.component"; import { RecommenderPageComponent } from "./recommender-page/recommender-page.component"; import { EditNetworkMapComponent } from "./edit-network-map/edit-network-map.component"; -import { ConfigService } from '@ngx-config/core'; -export class MyRxStompConfig extends InjectableRxStompConfig { - constructor(private configService: ConfigService) { - super(); - this.brokerURL = `${configService.getSettings("traffic_simulation_stomp_url")}`; - this.heartbeatIncoming = 0; - this.heartbeatOutgoing = 10000; - this.reconnectDelay = 500; - this.debug = (msg: string): void => { - environment.showWebSocketDebug && console.debug(msg); - }; - } -} +const rxStompConfig: InjectableRxStompConfig = { + brokerURL: environment.baseStompURL, + debug: (msg) => environment.showWebSocketDebug && console.debug(msg), +}; @NgModule({ imports: [ @@ -93,8 +83,6 @@ export class MyRxStompConfig extends InjectableRxStompConfig { NbToastrModule, NbDatepickerModule, NbProgressBarModule, - NbDialogModule.forRoot(), - NbSpinnerModule, ], declarations: [ NetworkCreationComponent, @@ -105,15 +93,16 @@ export class MyRxStompConfig extends InjectableRxStompConfig { OurSpiderChartComponent, SimulationCreationComponent, ExistingSimulationsComponent, + VisualizationsComponent, MlModuleComponent, + VisualizationsGeojsonComponent, RecommenderPageComponent, EditNetworkMapComponent, ], providers: [ { - provide: InjectableRxStompConfig, - useClass: MyRxStompConfig, - deps: [ConfigService] + provide: InjectableRxStompConfig, + useValue: rxStompConfig, }, { provide: RxStompService, diff --git a/src/app/pages/simulation-wizard/utils/data/map-colors.ts b/src/app/pages/simulation-wizard/utils/data/map-colors.ts index ee403d237afb019a38e7a8c2b509bc8bfd6b253e..1534288168a0d6ab0a9e96308d978d6b9c7dab78 100644 --- a/src/app/pages/simulation-wizard/utils/data/map-colors.ts +++ b/src/app/pages/simulation-wizard/utils/data/map-colors.ts @@ -1,11 +1,15 @@ -import { brink_values } from "../../results-map/brink_values"; -// sunset N11 scheme: 35b499; 4a7ab7; 6da5cc; 98cae0; c2e3ee; eaebcc; fed98b; fdb366; f57d4a; dc3d2d; a50026 -const color_0 = "#a50026"; -const color_1 = "#f57d4a"; -const color_2 = "#fed98b"; -const color_3 = "#c2e3ee"; -const color_4 = "#6da5cc"; -const color_5 = "#35b499"; +import { brink_values } from "../../visualizations/visualizations-geojson/brink_values"; +// const color_0 = "#ff0202"; +// const color_1 = "#e51452"; +// const color_2 = "#733d73"; +// const color_3 = "#51467c"; +// const color_4 = "#0029fe"; +const color_0 = "#dd0000"; +const color_1 = "#ff5600"; +const color_2 = "orange"; +const color_3 = "yellow"; +const color_4 = "greenyellow"; +const color_5 = "springgreen"; export function getColor(properties: any, pro: any): string { let value = undefined; diff --git a/src/app/pages/simulation-wizard/utils/data/simulation.ts b/src/app/pages/simulation-wizard/utils/data/simulation.ts index a19507c15e884a2a64502e80db8b9c332cab2eaf..5d05f099ba000bc84ac24d3555d7d2533bc9d7cb 100644 --- a/src/app/pages/simulation-wizard/utils/data/simulation.ts +++ b/src/app/pages/simulation-wizard/utils/data/simulation.ts @@ -1,17 +1,17 @@ export class Simulation { - id: number; - name: string; - description: string; - city: string; - status: string; - scenarioId: number; - networkId: number; - animationState: string; - statusWSMessage: string; + id: number; + name: string; + description: string; + scenarioId: number; + networkId: number; + status: string; - static deserialize(input: any): Simulation { - let simulation = new Simulation(); - Object.assign(simulation, input); - return simulation; - } -} + animationState: string; + statusWSMessage: string; + + static deserialize(input: any): Simulation { + let simulation = new Simulation() + Object.assign(simulation, input); + return simulation; + } +} \ No newline at end of file diff --git a/src/app/pages/simulation-wizard/utils/services/city.service.ts b/src/app/pages/simulation-wizard/utils/services/city.service.ts index cfb3780ee4256343b224484e992b338f892194f6..27465485c08caeaa0889479a27cfe0db2e82b11b 100644 --- a/src/app/pages/simulation-wizard/utils/services/city.service.ts +++ b/src/app/pages/simulation-wizard/utils/services/city.service.ts @@ -2,23 +2,20 @@ import { HttpClient } from "@angular/common/http"; import { Injectable } from "@angular/core"; import { BehaviorSubject, Observable, of } from "rxjs"; import { map, tap } from "rxjs/operators"; +import { environment } from "../../../../../environments/environment"; import { City } from "../data/city"; import { Network } from "../data/network"; import { Scenario } from "../data/scenario"; import { tapDebugApi } from "./tap-debug"; -import { ConfigService } from "@ngx-config/core"; @Injectable({ providedIn: "root" }) export class CityService { - private url: any; + private url = `${environment.baseAPIUrl}/traffic-simulation/city`; private selectedCityIndex: number; private selectedCity$ = new BehaviorSubject<City | null>(null); cities: City[]; - constructor(private httpClient: HttpClient, configService: ConfigService) { - this.url = - configService.getSettings("traffic_simulation_base_url") + "/city"; - } + constructor(private httpClient: HttpClient) {} public setSelectedCity(cityId: string) { this.selectedCityIndex = this.cities.findIndex( @@ -40,7 +37,10 @@ export class CityService { } getSelectedCityNotBehavior(): Observable<City> { - return this.httpClient.get<City>(`${this.url}/selected`).pipe(tapDebugApi); + return this.httpClient.get<City>(`${this.url}/selected`).pipe( + tap((res) => console.log(res)), + tapDebugApi + ); } getCities(): Observable<City[]> { @@ -77,5 +77,11 @@ export class CityService { public async initialize() { localStorage.clear(); this.cities = await this.getCities().toPromise(); + // TODO: + console.log("currently commented/disabled"); + // this.getSelectedCity().subscribe(city => { + // if (city) environment.showAPIDebug && console.debug(city); + // else this.setSelectedCity(localStorage.getItem("selectedCityId") || "BILBAO"); + // }); } } diff --git a/src/app/pages/simulation-wizard/utils/services/com.service.ts b/src/app/pages/simulation-wizard/utils/services/com.service.ts index f2f33c99a8517bc787e008441ab5388710e4296a..eb731979a73c9fe605bf3adda92ad59bd003f349 100644 --- a/src/app/pages/simulation-wizard/utils/services/com.service.ts +++ b/src/app/pages/simulation-wizard/utils/services/com.service.ts @@ -4,18 +4,14 @@ import { Observable } from "rxjs"; import { tap } from "rxjs/operators"; import { Simulation } from "../data/simulation"; import { environment } from "../../../../../environments/environment"; -import { ConfigService } from "@ngx-config/core"; @Injectable({ providedIn: "root", }) export class ComService { - private traffic_sim_server: any; + private traffic_sim_server = `${environment.baseAPIUrl}/traffic-simulation/network`; - constructor(private http: HttpClient, configService: ConfigService) { - this.traffic_sim_server = - configService.getSettings("traffic_simulation_base_url") + "/network"; - } + constructor(private http: HttpClient) {} sendGetResultsVis(simId: number): Observable<any> { const url: string = `${this.traffic_sim_server}/simulation/${simId}/results/vis-trips`; diff --git a/src/app/pages/simulation-wizard/utils/services/network.service.ts b/src/app/pages/simulation-wizard/utils/services/network.service.ts index 9d5a0db7113edfe9400bd34838f84842e67f9668..7111797283dbd344efe1a005091c8b14fb03ac07 100644 --- a/src/app/pages/simulation-wizard/utils/services/network.service.ts +++ b/src/app/pages/simulation-wizard/utils/services/network.service.ts @@ -1,28 +1,21 @@ import { HttpClient, HttpErrorResponse, - HttpHeaders, } from "@angular/common/http"; import { Injectable } from "@angular/core"; import { Observable, throwError } from "rxjs"; import { map, catchError } from "rxjs/operators"; +import { environment } from "../../../../../environments/environment"; import { Network } from "../data/network"; import { tapDebugApi } from "./tap-debug"; -import { ConfigService } from "@ngx-config/core"; @Injectable({ providedIn: "root", }) export class NetworkService { - private baseAPIUrl: any; - private dssUrl: any; + private baseAPIUrl = `${environment.baseAPIUrl}/traffic-simulation/network`; - constructor(private httpClient: HttpClient, configService: ConfigService) { - this.baseAPIUrl = - configService.getSettings("traffic_simulation_base_url") + "/network"; - this.dssUrl = - configService.getSettings("traffic_simulation_dss_url") + "/dss"; - } + constructor(private httpClient: HttpClient) {} handleError(error: HttpErrorResponse) { let errorMessage = "Some sort of error."; @@ -52,33 +45,9 @@ export class NetworkService { } getNetwork(id: number): Observable<Network> { - return this.httpClient - .get<Network>(`${this.baseAPIUrl}/${id}`) - .pipe(tapDebugApi, catchError(this.handleError)); - } - - postGenerateBikeLanes( - geometry: JSON, - twoLane: boolean, - laneSeparation: number - ): Observable<any> { - const headers = new HttpHeaders().append("Accept", "application/json"); - return this.httpClient - .post( - `${this.dssUrl}/generate_bike_lanes/`, - { - geometry: geometry, - twoLane: twoLane, - laneSeparation: laneSeparation, - }, - { - headers: headers, - } - ) - .pipe( - map((res) => { - return res; - }) - ); + return this.httpClient.get<Network>(`${this.baseAPIUrl}/${id}`).pipe( + tapDebugApi, + catchError(this.handleError) + ) } } diff --git a/src/app/pages/simulation-wizard/utils/services/scenario.service.ts b/src/app/pages/simulation-wizard/utils/services/scenario.service.ts index 023b9bc20942f27899b34b7d00ceeceb81d9b5cc..fe5c0ec419a5f6cbfb70cc481c4bcec80a90a0ed 100644 --- a/src/app/pages/simulation-wizard/utils/services/scenario.service.ts +++ b/src/app/pages/simulation-wizard/utils/services/scenario.service.ts @@ -2,46 +2,37 @@ import { HttpClient, HttpHeaders } from "@angular/common/http"; import { Injectable } from "@angular/core"; import { Observable } from "rxjs"; import { map, tap } from "rxjs/operators"; +import { environment } from "../../../../../environments/environment"; import { Scenario } from "../data/scenario"; import { Simulation } from "../data/simulation"; import { tapDebugApi } from "./tap-debug"; -import { ConfigService } from "@ngx-config/core"; @Injectable({ - providedIn: "root", + providedIn: 'root', }) export class ScenarioService { - private baseApiUrl: any; + private baseApiUrl = `${environment.baseAPIUrl}/traffic-simulation/scenario`; - constructor(private httpClient: HttpClient, configService: ConfigService) { - this.baseApiUrl = - configService.getSettings("traffic_simulation_base_url") + "/scenario"; - } + constructor(private httpClient: HttpClient) { } - createScenario(scenario: Scenario) { - return this.httpClient.post(this.baseApiUrl, scenario).pipe( - map((scenario) => Scenario.deserialize(scenario)), - tapDebugApi - ); - } + createScenario(scenario: Scenario) { + return this.httpClient.post(this.baseApiUrl, scenario).pipe( + map(scenario => Scenario.deserialize(scenario)), + tapDebugApi + ); + } - getScenarios(): Observable<Scenario[]> { - return this.httpClient.get<Scenario[]>(this.baseApiUrl).pipe( - map<any[], Scenario[]>((scenarios) => - scenarios.map((scenario) => Scenario.deserialize(scenario)) - ), - tapDebugApi - ); - } + getScenarios(): Observable<Scenario[]> { + return this.httpClient.get<Scenario[]>(this.baseApiUrl).pipe( + map<any[], Scenario[]>(scenarios => scenarios.map(scenario => Scenario.deserialize(scenario))), + tapDebugApi + ); + } - getScenarioSimulations(id: number): Observable<Simulation[]> { - return this.httpClient - .get<Simulation[]>(`${this.baseApiUrl}/${id}/simulations`) - .pipe( - map<any[], Simulation[]>((simulations) => - simulations.map((simulation) => Simulation.deserialize(simulation)) - ), - tapDebugApi - ); - } -} + getScenarioSimulations(id: number): Observable<Simulation[]> { + return this.httpClient.get<Simulation[]>(`${this.baseApiUrl}/${id}/simulations`).pipe( + map<any[], Simulation[]>(simulations => simulations.map(simulation => Simulation.deserialize(simulation))), + tapDebugApi + ); + } +} \ No newline at end of file diff --git a/src/app/pages/simulation-wizard/utils/services/simulation.service.ts b/src/app/pages/simulation-wizard/utils/services/simulation.service.ts index c3500ee9197358495f98c9bcf8f3d9545fa35ae7..4026b77656462bed82053ec1dc554e59ea5a7c30 100644 --- a/src/app/pages/simulation-wizard/utils/services/simulation.service.ts +++ b/src/app/pages/simulation-wizard/utils/services/simulation.service.ts @@ -2,29 +2,20 @@ import { HttpClient } from "@angular/common/http"; import { Injectable } from "@angular/core"; import { Observable, throwError } from "rxjs"; import { map, tap, catchError } from "rxjs/operators"; +import { environment } from "../../../../../environments/environment"; import { Simulation } from "../data/simulation"; import { tapDebugApi } from "./tap-debug"; -import { ConfigService } from "@ngx-config/core"; @Injectable({ providedIn: "root", }) export class SimulationService { - private ts_simulation_url: any; - private ts_url: any; - private storage_url: any; - private dssUrl: any; - - constructor(private httpClient: HttpClient, configService: ConfigService) { - this.ts_simulation_url = - configService.getSettings("traffic_simulation_base_url") + "/simulation"; - this.ts_url = configService.getSettings("traffic_simulation_base_url"); - this.storage_url = configService.getSettings( - "traffic_simulation_storage_url" - ); - this.dssUrl = - configService.getSettings("traffic_simulation_dss_url"); - } + private ts_simulation_url = `${environment.baseAPIUrl}/traffic-simulation/simulation`; + private ts_url = `${environment.baseAPIUrl}/traffic-simulation`; + private storage_url = `${environment.storageAPIUrl}`; + private dssUrl = `${environment.dssUrl}`; + + constructor(private httpClient: HttpClient) {} createSimulation(simulation: Simulation): Observable<Simulation> { return this.httpClient.post(this.ts_simulation_url, simulation).pipe( @@ -58,10 +49,18 @@ export class SimulationService { ); } - postEvaluated(arrayToBeSent: any): Observable<any> { - return this.httpClient.post(`${this.storage_url}/storage/dss/evaluated`, { - ids: arrayToBeSent, + /* requests evaluated kpis for multiple simulations + * arrayToBeSent: array of simulation ids, e.g. [1,2] + */ + getEvaluated(arrayToBeSent: any): Observable<any> { + let formData = new FormData(); + arrayToBeSent.map((m) => { + formData.append("ids[]", m.toString()); }); + + return this.httpClient + .post(`${this.storage_url}/dss/evaluated`, formData) + .pipe(tap((res) => console.log("getEvaluated ", res))); } getPreprocessData(city_id: string): Observable<any> { @@ -113,7 +112,7 @@ export class SimulationService { getCalculateKPIs(cityId: string, id: number): Observable<any> { return this.httpClient - .get(`${this.dssUrl}/dss/kpis/${id}`, {}) + .get(`${this.dssUrl}/dss/kpis/${cityId}/${id}`, {}) .pipe( map((res) => { return res; @@ -131,8 +130,8 @@ export class SimulationService { ): Observable<any> { return this.httpClient .post(`${this.dssUrl}/dss/kpi_eval/${cityId}`, { - baseline: baselineId, - compare: compareId, + baselineId: baselineId, + compareId: compareId, }) .pipe( map((res) => { @@ -150,13 +149,12 @@ export class SimulationService { dateTo: Date, networkId: number ): Observable<any> { - let formData = new FormData(); - formData.append("date_from", this.formatDateToOurSpec(dateFrom).toString()); - formData.append("date_to", this.formatDateToOurSpec(dateTo).toString()); - formData.append("network_id", networkId.toString()); - return this.httpClient - .post(`${this.dssUrl}/dss/calibration_preprocess`, formData) + .post(`${this.dssUrl}/dss/calibration_preprocess`, { + date_from: this.formatDateToOurSpec(dateFrom), + date_to: this.formatDateToOurSpec(dateTo), + network_id: networkId, + }) .pipe( map((res) => { return res; @@ -192,9 +190,87 @@ export class SimulationService { ); } - getTravelDemandModel(networkId: string): Observable<any> { + dobiVzorcek_0(): Observable<JSON> { + return this.httpClient.get(`${this.storage_url}/vzorcek-geojson-0`).pipe( + map((res) => { + return res; + }), + tapDebugApi + ); + } + + dobiVzorcek_1(): Observable<JSON> { + return this.httpClient.get(`${this.storage_url}/vzorcek-geojson-1`).pipe( + map((res) => { + return res; + }), + tapDebugApi + ); + } + + dobi_amsterdam_baseline_vehicleCount(): Observable<JSON> { return this.httpClient - .get(`${this.storage_url}/storage/plans/${networkId}`) + .get(`${this.storage_url}/amsterdam-baseline-vehicleCount`) + .pipe( + map((res) => { + return res; + }), + tapDebugApi + ); + } + + dobi_amsterdam22districts(): Observable<JSON> { + return this.httpClient.get(`${this.storage_url}/amsterdam22districts`).pipe( + map((res) => { + return res; + }), + tapDebugApi + ); + } + + dobi_bilbao_baseline_emission_sample(): Observable<JSON> { + return this.httpClient + .get(`${this.storage_url}/bilbao-baseline-emission-sample`) + .pipe( + map((res) => { + return res; + }), + tapDebugApi + ); + } + dobi_bilbao_scenario_emission_sample(): Observable<JSON> { + return this.httpClient + .get(`${this.storage_url}/bilbao-scenario-emission-sample`) + .pipe( + map((res) => { + return res; + }), + tapDebugApi + ); + } + dobi_helsinki_baseline_vehicleCount_sample(): Observable<JSON> { + return this.httpClient + .get(`${this.storage_url}/helsinki-baseline-vehicleCount-sample`) + .pipe( + map((res) => { + return res; + }), + tapDebugApi + ); + } + dobi_helsinki_scenario_vehicleCount_sample(): Observable<JSON> { + return this.httpClient + .get(`${this.storage_url}/helsinki-scenario-vehicleCount-sample`) + .pipe( + map((res) => { + return res; + }), + tapDebugApi + ); + } + dobi_santostefanodibriga_bus(): Observable<JSON> { + return this.httpClient + .get(`${this.storage_url}/santostefanodibriga-bus`) .pipe( map((res) => { return res; @@ -203,9 +279,9 @@ export class SimulationService { ); } - getGeojson(id: string, selectedKpi: string): Observable<JSON> { + getPlans(networkId: string): Observable<any> { return this.httpClient - .get(`${this.dssUrl}/dss/geojson/${id}/${selectedKpi}`) + .get(`${this.storage_url}/storage/plans/${networkId}`) .pipe( map((res) => { return res; @@ -213,4 +289,13 @@ export class SimulationService { tapDebugApi ); } + + getGeojson(id: string): Observable<JSON> { + return this.httpClient.get(`${this.dssUrl}/dss/geojson/${id}`).pipe( + map((res) => { + return res; + }), + tapDebugApi + ); + } } diff --git a/src/app/pages/simulation-wizard/visualizations/visualizations-geojson/visualizations-geojson.component.scss b/src/app/pages/simulation-wizard/visualizations/visualizations-geojson/visualizations-geojson.component.scss index 20f8751b2b49f5c8c42576d87feb0737f3d7e40d..2de0d60646f252873761fc6de129551637b67397 100644 --- a/src/app/pages/simulation-wizard/visualizations/visualizations-geojson/visualizations-geojson.component.scss +++ b/src/app/pages/simulation-wizard/visualizations/visualizations-geojson/visualizations-geojson.component.scss @@ -14,5 +14,9 @@ width: 100%; height: nb-theme(card-height-large); } - +} + +button { + width: 23%; + margin: 0 1%; } \ No newline at end of file diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index 8a8c51e533401ca33fa227622692d14580061bb4..44b42f23a113afe83a6a2e61b0d39c05fc1316fd 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -5,6 +5,15 @@ */ export const environment = { production: true, + baseAPIUrl: 'http://localhost:8082', + storageAPIUrl: 'http://localhost:8081', + baseStompURL: 'ws://localhost:8082/gs-guide-websocket', + dssUrl: 'http://localhost:8083', showAPIDebug: false, - showWebSocketDebug: true + showWebSocketDebug: true, + ftpHost: '127.0.0.1', + ftpPort: '2121', + ftpUser: 'urbanite', + ftpPassword: 'password', + ftpDirectory: 'data' }; diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 6046cfcb3650709e021e0aed260e1ecc6b231cea..b17b0debd095c2476cac5a2bd85bec1b4ce02fbe 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -10,6 +10,15 @@ export const environment = { production: false, + baseAPIUrl: 'http://localhost:8082', + storageAPIUrl: 'http://localhost:8081', + baseStompURL: 'ws://localhost:8082/gs-guide-websocket', + dssUrl: 'http://localhost:8083', showAPIDebug: false, - showWebSocketDebug: true + showWebSocketDebug: true, + ftpHost: '127.0.0.1', + ftpPort: '2121', + ftpUser: 'urbanite', + ftpPassword: 'password', + ftpDirectory: 'data' };