From fe10c4577b787fd34fbf530f71cbfc53a9940ada Mon Sep 17 00:00:00 2001 From: Pablo Lopez Date: Fri, 23 Aug 2024 13:17:29 -0300 Subject: [PATCH 1/5] bug fix max zoom terrain basemap (#1695) --- src/interface/src/app/map/map.constants.ts | 2 +- src/interface/src/app/map/map.tiles.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interface/src/app/map/map.constants.ts b/src/interface/src/app/map/map.constants.ts index f7ec2af88..382863cb9 100644 --- a/src/interface/src/app/map/map.constants.ts +++ b/src/interface/src/app/map/map.constants.ts @@ -64,6 +64,6 @@ export const BOUNDARY_LAYER_HOVER_STYLES: L.PathOptions = { export const FrontendConstants = { MAP_INITIAL_ZOOM: 9, MAP_MIN_ZOOM: 7, - MAP_MAX_ZOOM: 19, + MAP_MAX_ZOOM: 18, MAP_DATA_LAYER_OPACITY: 0.7, } as const; diff --git a/src/interface/src/app/map/map.tiles.ts b/src/interface/src/app/map/map.tiles.ts index b12fc1a95..e061a5710 100644 --- a/src/interface/src/app/map/map.tiles.ts +++ b/src/interface/src/app/map/map.tiles.ts @@ -6,7 +6,7 @@ export function terrainTiles() { return L.tileLayer( 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Terrain_Base/MapServer/tile/{z}/{y}/{x}', { - maxZoom: 13, + maxNativeZoom: 13, attribution: 'Tiles © Esri — Source: USGS, Esri, TANA, DeLorme, and NPS', zIndex: 0, From 66a9a7771c6c0988d6e9f6c3ef658d867d2709c0 Mon Sep 17 00:00:00 2001 From: George Silva <863039+george-silva@users.noreply.github.com> Date: Fri, 23 Aug 2024 15:29:01 -0300 Subject: [PATCH 2/5] fix project areas layer (#1698) --- .../martin/sql/martin_project_areas_by_scenario.sql | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/planscape/martin/sql/martin_project_areas_by_scenario.sql b/src/planscape/martin/sql/martin_project_areas_by_scenario.sql index 84189e430..7fb05d64c 100644 --- a/src/planscape/martin/sql/martin_project_areas_by_scenario.sql +++ b/src/planscape/martin/sql/martin_project_areas_by_scenario.sql @@ -2,25 +2,23 @@ CREATE OR REPLACE FUNCTION martin_project_areas_by_scenario(z integer, x integer RETURNS bytea AS $$ DECLARE p_mvt bytea; - p_intersecting_area geometry; - p_stand_size varchar; BEGIN SELECT INTO p_mvt ST_AsMVT(tile, 'project_areas_by_scenario', 4096, 'geom') FROM ( SELECT pa.id as "id", - pa.scenario_id as "scenario_id", + pa.scenario_id::int as "scenario_id", pa.name, COALESCE(pa.data, '{}'::jsonb) ->> 'treatment_rank' as "rank", ST_AsMVTGeom( ST_Transform(pa.geometry, 3857), ST_TileEnvelope(z, x, y), 4096, 64, true) AS geom - FROM stands_stand ss + FROM planning_projectarea pa WHERE pa.deleted_at is NULL AND - pa.scenario_id = (query_params::jsonb)->>'scenario_id' AND + pa.scenario_id = (query_params->>'scenario_id')::int AND pa.geometry && ST_Transform(ST_TileEnvelope(z, x, y, margin => (64.0 / 4096)), 4269) ) as tile WHERE geom IS NOT NULL; From e2fcba3f9f67e230cda44b8a15d093527c67c5d5 Mon Sep 17 00:00:00 2001 From: Pablo Lopez Date: Tue, 27 Aug 2024 11:54:05 -0300 Subject: [PATCH 3/5] 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; }; } From 1b2c33581090f9d0a46631fb7f6622fe878c3a06 Mon Sep 17 00:00:00 2001 From: Mark <100235124+lastminutediorama@users.noreply.github.com> Date: Tue, 27 Aug 2024 10:29:05 -0500 Subject: [PATCH 4/5] update faq link (#1697) --- .../src/app/standalone/learn-more/learn-more.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interface/src/app/standalone/learn-more/learn-more.component.html b/src/interface/src/app/standalone/learn-more/learn-more.component.html index 8dd9dcdd9..f444fd04b 100644 --- a/src/interface/src/app/standalone/learn-more/learn-more.component.html +++ b/src/interface/src/app/standalone/learn-more/learn-more.component.html @@ -9,7 +9,7 @@ title="Learn more about planscape" content="Visit our FAQs to learn more" iconsrc="../../assets/svg/icons/info-circle-dark.svg" - outboundLink="https://www.planscape.org/faqs"> + outboundLink="https://www.planscape.org/documentation/faq/"> Date: Tue, 27 Aug 2024 12:48:10 -0300 Subject: [PATCH 5/5] replace proj_id/position on scenario results for treatment_rank (#1689) --- src/interface/src/app/plan/plan-helpers.ts | 16 +++++----------- .../src/app/plan/plan-map/plan-map.component.ts | 6 +++--- .../project-areas/project-areas.component.html | 4 ++-- .../project-areas/project-areas.component.ts | 2 +- 4 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/interface/src/app/plan/plan-helpers.ts b/src/interface/src/app/plan/plan-helpers.ts index cfea2d952..b45a2aba1 100644 --- a/src/interface/src/app/plan/plan-helpers.ts +++ b/src/interface/src/app/plan/plan-helpers.ts @@ -11,8 +11,6 @@ import { } from './project-areas/project-areas.component'; import { DEFAULT_AREA_COLOR, PROJECT_AREA_COLORS } from '@shared'; -export const NOTE_SAVE_INTERVAL = 5000; - export const POLLING_INTERVAL = 3000; // sizes in hectares @@ -25,10 +23,10 @@ export const STAND_SIZES: Record = { export function parseResultsToProjectAreas( results: ScenarioResult ): ProjectAreaReport[] { - return results.result.features.map((featureCollection, i) => { + return results.result.features.map((featureCollection) => { const props = featureCollection.properties; return { - id: i + 1, + rank: props.treatment_rank, acres: props.area_acres, percentTotal: props.pct_area, estimatedCost: props.total_cost, @@ -55,15 +53,11 @@ export function parseResultsToTotals( ); } -/** - * - * @param position rank position (1 based index) of scenario projection - */ -export function getColorForProjectPosition(position: number) { - if (position < 1) { +export function getColorForProjectPosition(rank: number) { + if (rank < 1) { return DEFAULT_AREA_COLOR; } - return PROJECT_AREA_COLORS[(position - 1) % PROJECT_AREA_COLORS.length]; + return PROJECT_AREA_COLORS[(rank - 1) % PROJECT_AREA_COLORS.length]; } export function findQuestionOnTreatmentGoalsConfig( diff --git a/src/interface/src/app/plan/plan-map/plan-map.component.ts b/src/interface/src/app/plan/plan-map/plan-map.component.ts index b0fda7fb2..7c89c7f71 100644 --- a/src/interface/src/app/plan/plan-map/plan-map.component.ts +++ b/src/interface/src/app/plan/plan-map/plan-map.component.ts @@ -324,7 +324,7 @@ export class PlanMapComponent implements OnInit, AfterViewInit, OnDestroy { this.projectAreasLayer = L.geoJSON(shapes, { style: (shape) => ({ color: '#000', - fillColor: getColorForProjectPosition(shape?.properties.proj_id), + fillColor: getColorForProjectPosition(shape?.properties.treatment_rank), fillOpacity: 0.4, weight: 1.5, }), @@ -333,7 +333,7 @@ export class PlanMapComponent implements OnInit, AfterViewInit, OnDestroy { if (feature.geometry.type === 'Polygon') { center = polylabel(feature.geometry.coordinates, 0.0005); addTooltipAtCenter( - feature.properties.proj_id.toString(), + feature.properties.treatment_rank.toString(), center, this.map ); @@ -341,7 +341,7 @@ export class PlanMapComponent implements OnInit, AfterViewInit, OnDestroy { feature.geometry.coordinates.forEach((positions) => { center = polylabel(positions, 0.005); addTooltipAtCenter( - feature.properties.proj_id.toString(), + feature.properties.treatment_rank.toString(), center, this.map ); diff --git a/src/interface/src/app/plan/project-areas/project-areas.component.html b/src/interface/src/app/plan/project-areas/project-areas.component.html index af3234bbe..558c66acd 100644 --- a/src/interface/src/app/plan/project-areas/project-areas.component.html +++ b/src/interface/src/app/plan/project-areas/project-areas.component.html @@ -34,8 +34,8 @@

Project Areas

- {{ area.id }} + [style.border-left-color]="getColorByPosition(area.rank)"> + {{ area.rank }} {{ area.acres | number: '1.0-0' }} {{ area.percentTotal | percent: '1.0-2' }} diff --git a/src/interface/src/app/plan/project-areas/project-areas.component.ts b/src/interface/src/app/plan/project-areas/project-areas.component.ts index f41b0b8da..31962ea6c 100644 --- a/src/interface/src/app/plan/project-areas/project-areas.component.ts +++ b/src/interface/src/app/plan/project-areas/project-areas.component.ts @@ -6,7 +6,7 @@ import { import { PROJECT_AREA_COLORS } from '@shared'; export interface ProjectAreaReport { - id: number; + rank: number; acres: number; percentTotal: number; estimatedCost: number;