From e2fcba3f9f67e230cda44b8a15d093527c67c5d5 Mon Sep 17 00:00:00 2001 From: Pablo Lopez Date: Tue, 27 Aug 2024 11:54:05 -0300 Subject: [PATCH] Scenario project areas (#1699) * using project area tiles * show project areas * cleanup and fix test * remove dependencies --- src/interface/src/app/app-routing.module.ts | 1 + .../map-project-areas.component.html | 37 ++++++++++++ .../map-project-areas.component.scss | 0 .../map-project-areas.component.spec.ts | 28 ++++++++++ .../map-project-areas.component.ts | 56 +++++++++++++++++++ .../map-rectangle.component.html | 2 +- .../map-stands/map-stands.component.html | 6 +- .../map-stands/map-stands.component.ts | 3 +- .../treatment-map.component.html | 5 ++ .../treatment-map/treatment-map.component.ts | 6 +- .../treatment-overview.component.html | 4 +- .../treatment-overview.component.ts | 1 + src/interface/src/app/types/scenario.types.ts | 3 +- 13 files changed, 144 insertions(+), 8 deletions(-) create mode 100644 src/interface/src/app/treatments/map-project-areas/map-project-areas.component.html create mode 100644 src/interface/src/app/treatments/map-project-areas/map-project-areas.component.scss create mode 100644 src/interface/src/app/treatments/map-project-areas/map-project-areas.component.spec.ts create mode 100644 src/interface/src/app/treatments/map-project-areas/map-project-areas.component.ts diff --git a/src/interface/src/app/app-routing.module.ts b/src/interface/src/app/app-routing.module.ts index e3d7bbdef..176de2886 100644 --- a/src/interface/src/app/app-routing.module.ts +++ b/src/interface/src/app/app-routing.module.ts @@ -118,6 +118,7 @@ const routes: Routes = [ canActivate: [AuthGuard, createFeatureGuard('treatments')], resolve: { treatmentId: numberResolver('treatmentId', ''), + scenarioId: numberResolver('scenarioId', ''), }, loadChildren: () => import('./treatments/treatments.module').then( diff --git a/src/interface/src/app/treatments/map-project-areas/map-project-areas.component.html b/src/interface/src/app/treatments/map-project-areas/map-project-areas.component.html new file mode 100644 index 000000000..7ed69599a --- /dev/null +++ b/src/interface/src/app/treatments/map-project-areas/map-project-areas.component.html @@ -0,0 +1,37 @@ + + + + + + + diff --git a/src/interface/src/app/treatments/map-project-areas/map-project-areas.component.scss b/src/interface/src/app/treatments/map-project-areas/map-project-areas.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/interface/src/app/treatments/map-project-areas/map-project-areas.component.spec.ts b/src/interface/src/app/treatments/map-project-areas/map-project-areas.component.spec.ts new file mode 100644 index 000000000..0dce65240 --- /dev/null +++ b/src/interface/src/app/treatments/map-project-areas/map-project-areas.component.spec.ts @@ -0,0 +1,28 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MapProjectAreasComponent } from './map-project-areas.component'; +import { MockDeclarations } from 'ng-mocks'; +import { + LayerComponent, + VectorSourceComponent, +} from '@maplibre/ngx-maplibre-gl'; + +describe('MapProjectAreasComponent', () => { + let component: MapProjectAreasComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MapProjectAreasComponent], + declarations: MockDeclarations(VectorSourceComponent, LayerComponent), + }).compileComponents(); + + fixture = TestBed.createComponent(MapProjectAreasComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/interface/src/app/treatments/map-project-areas/map-project-areas.component.ts b/src/interface/src/app/treatments/map-project-areas/map-project-areas.component.ts new file mode 100644 index 000000000..121565cc8 --- /dev/null +++ b/src/interface/src/app/treatments/map-project-areas/map-project-areas.component.ts @@ -0,0 +1,56 @@ +import { Component, Input } from '@angular/core'; +import { + FeatureComponent, + GeoJSONSourceComponent, + LayerComponent, + VectorSourceComponent, +} from '@maplibre/ngx-maplibre-gl'; +import { getColorForProjectPosition } from '../../plan/plan-helpers'; +import { LayerSpecification, Map as MapLibreMap } from 'maplibre-gl'; +import { environment } from '../../../environments/environment'; + +@Component({ + selector: 'app-map-project-areas', + standalone: true, + imports: [ + FeatureComponent, + GeoJSONSourceComponent, + LayerComponent, + VectorSourceComponent, + ], + templateUrl: './map-project-areas.component.html', + styleUrl: './map-project-areas.component.scss', +}) +export class MapProjectAreasComponent { + @Input() scenarioId!: number; + @Input() mapLibreMap!: MapLibreMap; + + readonly layerName = 'project_areas_by_scenario'; + readonly tilesUrl = + environment.martin_server + 'project_areas_by_scenario/{z}/{x}/{y}'; + + constructor() {} + + get vectorLayerUrl() { + return this.tilesUrl + `?scenario_id=${this.scenarioId}`; + } + + paint: LayerSpecification['paint'] = { + 'fill-outline-color': '#000', + 'fill-color': this.getFillColors() as any, + 'fill-opacity': 0.5, + }; + + getFillColors() { + const defaultColor = '#00000050'; + const matchExpression: (number | string | string[])[] = [ + 'match', + ['get', 'rank'], + ]; + for (let i = 1; i < 11; i++) { + matchExpression.push(i.toString(), getColorForProjectPosition(i)); + } + matchExpression.push(defaultColor); + return matchExpression; + } +} diff --git a/src/interface/src/app/treatments/map-rectangle/map-rectangle.component.html b/src/interface/src/app/treatments/map-rectangle/map-rectangle.component.html index 391d97b28..483e8f3be 100644 --- a/src/interface/src/app/treatments/map-rectangle/map-rectangle.component.html +++ b/src/interface/src/app/treatments/map-rectangle/map-rectangle.component.html @@ -1,4 +1,4 @@ - + + sourceLayer="project_area_aggregate"> + sourceLayer="stands_by_tx_plan"> + + diff --git a/src/interface/src/app/treatments/treatment-map/treatment-map.component.ts b/src/interface/src/app/treatments/treatment-map/treatment-map.component.ts index 0f50d431a..8022d150f 100644 --- a/src/interface/src/app/treatments/treatment-map/treatment-map.component.ts +++ b/src/interface/src/app/treatments/treatment-map/treatment-map.component.ts @@ -1,5 +1,5 @@ import { Component, Input } from '@angular/core'; -import { JsonPipe, NgForOf } from '@angular/common'; +import { JsonPipe, NgForOf, NgIf } from '@angular/common'; import { DraggableDirective, FeatureComponent, @@ -15,6 +15,7 @@ import { MapRectangleComponent } from '../map-rectangle/map-rectangle.component' import { SelectedStandsState } from './selected-stands.state'; import { MapControlsComponent } from '../map-controls/map-controls.component'; import { environment } from '../../../environments/environment'; +import { MapProjectAreasComponent } from '../map-project-areas/map-project-areas.component'; @Component({ selector: 'app-treatment-map', @@ -31,6 +32,8 @@ import { environment } from '../../../environments/environment'; MapStandsComponent, MapRectangleComponent, MapControlsComponent, + MapProjectAreasComponent, + NgIf, ], providers: [SelectedStandsState], templateUrl: './treatment-map.component.html', @@ -43,6 +46,7 @@ export class TreatmentMapComponent { // TODO: should we keep using prop drilling here? Consider using a provider service to hold these values @Input() projectAreaId: number | null = null; @Input() treatmentPlanId = 0; + @Input() scenarioId: number | null = null; treatedStands: { id: number; assigment: string }[] = []; diff --git a/src/interface/src/app/treatments/treatment-overview/treatment-overview.component.html b/src/interface/src/app/treatments/treatment-overview/treatment-overview.component.html index 9ae8e1ccf..4411d819d 100644 --- a/src/interface/src/app/treatments/treatment-overview/treatment-overview.component.html +++ b/src/interface/src/app/treatments/treatment-overview/treatment-overview.component.html @@ -1,4 +1,6 @@ - + diff --git a/src/interface/src/app/treatments/treatment-overview/treatment-overview.component.ts b/src/interface/src/app/treatments/treatment-overview/treatment-overview.component.ts index d60e41816..a894534c2 100644 --- a/src/interface/src/app/treatments/treatment-overview/treatment-overview.component.ts +++ b/src/interface/src/app/treatments/treatment-overview/treatment-overview.component.ts @@ -16,6 +16,7 @@ import { TreatmentSummaryComponent } from '../treatment-summary/treatment-summar export class TreatmentOverviewComponent implements OnInit { treatmentPlanId: number = this.route.snapshot.data['treatmentId']; treatmentPlan: TreatmentPlan | null = null; + scenarioId: number = this.route.snapshot.data['scenarioId']; constructor( private treatmentsService: TreatmentsService, diff --git a/src/interface/src/app/types/scenario.types.ts b/src/interface/src/app/types/scenario.types.ts index d13214476..30e922002 100644 --- a/src/interface/src/app/types/scenario.types.ts +++ b/src/interface/src/app/types/scenario.types.ts @@ -41,7 +41,8 @@ export interface ScenarioResult { status: ScenarioResultStatus; completed_at: string; result: { - features: FeatureCollection[]; + // this is the FeatureCollection[] + features: FeatureCollection[]; // TODO this is actually Features[] type: string; }; }