Skip to content

Commit

Permalink
Merge branch 'upload-scenarios-landing-page' of ssh://github.com/OurP…
Browse files Browse the repository at this point in the history
…lanscape/Planscape into upload-scenarios-landing-page
  • Loading branch information
lastminutediorama committed Dec 22, 2024
2 parents bc61f8c + 1e77a96 commit 63ea51f
Show file tree
Hide file tree
Showing 29 changed files with 541 additions and 137 deletions.
3 changes: 2 additions & 1 deletion src/interface/angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@
"shpjs",
"geojson-rbush",
"polylabel",
"file-saver"
"file-saver",
"canvg"
]
},
"configurations": {
Expand Down
1 change: 1 addition & 0 deletions src/interface/src/app/map/map-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export class MapManager {
weight: 5,
},
templineStyle: {
radius: 0,
color: '#000',
weight: 5,
},
Expand Down
10 changes: 9 additions & 1 deletion src/interface/src/app/map/map.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ import { getPlanPath } from '../plan/plan-helpers';
import { InvalidLinkDialogComponent } from './invalid-link-dialog/invalid-link-dialog.component';
import { Location } from '@angular/common';
import { MatDialog } from '@angular/material/dialog';
import { AnalyticsService } from '@services/analytics.service';

@UntilDestroy()
@Component({
Expand Down Expand Up @@ -187,7 +188,8 @@ export class MapComponent implements AfterViewInit, OnDestroy, OnInit, DoCheck {
private cdr: ChangeDetectorRef,
private route: ActivatedRoute,
private shareMapService: ShareMapService,
private location: Location
private location: Location,
private analyticsService: AnalyticsService
) {
this.sessionService.mapViewOptions$
.pipe(take(1))
Expand Down Expand Up @@ -651,6 +653,12 @@ export class MapComponent implements AfterViewInit, OnDestroy, OnInit, DoCheck {
);
this.showUploader = false;
this.addDrawingControlToAllMaps();
this.analyticsService.emitEvent(
'shapefiles_uploaded_explore',
'file_uploaded',
'Upload Area',
1
);
},
error: () => {
this.showAreaTooComplexError();
Expand Down
70 changes: 63 additions & 7 deletions src/interface/src/app/treatments/direct-impacts.state.service.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,45 @@
import { BehaviorSubject } from 'rxjs';
import {
BehaviorSubject,
combineLatest,
map,
Observable,
of,
switchMap,
} from 'rxjs';
import {
DEFAULT_SLOT,
ImpactsMetric,
ImpactsMetricSlot,
Metric,
METRICS,
} from './metrics';
import { MapGeoJSONFeature } from 'maplibre-gl';
import { PrescriptionAction } from './prescriptions';

export class DirectImpactsStateService {
public _activeMetric$ = new BehaviorSubject<ImpactsMetric>({
metric: METRICS[0],
slot: DEFAULT_SLOT,
private _reportMetrics$ = new BehaviorSubject<
Record<ImpactsMetricSlot, Metric>
>({
blue: METRICS[0],
purple: METRICS[1],
orange: METRICS[2],
green: METRICS[3],
});

public activeMetric$ = this._activeMetric$.asObservable();
public reportMetrics$ = this._reportMetrics$.asObservable();

private _activeSlot$ = new BehaviorSubject<ImpactsMetricSlot>(DEFAULT_SLOT);

public activeMetric$: Observable<ImpactsMetric> = this._activeSlot$.pipe(
switchMap((slot) =>
this.reportMetrics$.pipe(
map((metrics) => ({
metric: metrics[slot],
slot: slot,
}))
)
)
);

private _filteredTreatmentTypes$ = new BehaviorSubject<PrescriptionAction[]>(
[]
Expand All @@ -23,6 +48,25 @@ export class DirectImpactsStateService {
private _activeStand$ = new BehaviorSubject<MapGeoJSONFeature | null>(null);
public activeStand$ = this._activeStand$.asObservable();

// todo: placeholder to fill once we have project area filter
projectArea$ = of('All Project Areas');

private _showTreatmentPrescription$ = new BehaviorSubject(false);
public showTreatmentPrescription$ =
this._showTreatmentPrescription$.asObservable();

mapPanelTitle$ = combineLatest([
this.activeMetric$,
this.projectArea$,
this.showTreatmentPrescription$,
]).pipe(
map(([activeMetric, pa, showTreatment]) =>
showTreatment
? 'Applied Treatment Prescription'
: `${activeMetric.metric.label} for ${pa}`
)
);

constructor() {}

setActiveStand(standData: MapGeoJSONFeature) {
Expand All @@ -34,10 +78,22 @@ export class DirectImpactsStateService {
}

setActiveMetric(mapMetric: ImpactsMetric) {
this._activeMetric$.next(mapMetric);
this._activeSlot$.next(mapMetric.slot);
this.updateReportMetric(mapMetric);
}

updateReportMetric(mapMetric: ImpactsMetric) {
this._reportMetrics$.next({
...this._reportMetrics$.value,
[mapMetric.slot]: mapMetric.metric,
});
}

isActiveSlot(slot: ImpactsMetricSlot) {
return this._activeMetric$.value.slot === slot;
return this._activeSlot$.value === slot;
}

setShowTreatmentPrescription(show: boolean) {
this._showTreatmentPrescription$.next(show);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
<div class="direct-impacts">
<div class="title-row">
<h4 class="title">Direct Treatment Impacts</h4>
<mat-slide-toggle [(ngModel)]="showTreatmentPrescription"
<mat-slide-toggle
[checked]="(showTreatmentPrescription$ | async) || false"
(change)="saveShowTreatmentPrescription($event)"
>View treatment prescription
</mat-slide-toggle>
<button sg-button variant="ghost" icon="download" [outlined]="true">
Expand All @@ -36,6 +38,8 @@ <h4 class="title">Direct Treatment Impacts</h4>

<div class="metric-selection">
<app-metric-filters
[selectedOptions]="(filterOptions$ | async) || []"
(metricUpdated)="updateReportMetric($event)"
(metricSelected)="activateMetric($event)"></app-metric-filters>
</div>

Expand All @@ -49,31 +53,34 @@ <h4 class="title">Direct Treatment Impacts</h4>
[paddedContent]="false"
[buttons]="[{ icon: 'open_in_full', actionName: 'expand' }]"
(clickedButton)="expandStandChart()">
<div panelTitle>{{ standChartPanelTitle$ | async }} /</div>
<div panelTitle>{{ standChartPanelTitle$ | async }}</div>
<app-stand-data-chart></app-stand-data-chart>
</sg-panel>

<sg-panel
class="map-panel"
[ngClass]="{ prescriptions: showTreatmentPrescription }"
[ngClass]="{ prescriptions: showTreatmentPrescription$ | async }"
[paddedContent]="false"
[buttons]="[
{ icon: 'layers', actionName: 'layers' },
{ icon: 'open_in_full', actionName: 'expand' }
]">
]"
(clickedButton)="expandMaps()">
<div panelTitle>{{ mapPanelTitle$ | async }}</div>
<app-direct-impacts-synced-maps
*ngIf="!showTreatmentPrescription"></app-direct-impacts-synced-maps>
*ngIf="
(showTreatmentPrescription$ | async) === false
"></app-direct-impacts-synced-maps>
<app-treatment-map
[showProjectAreaTooltips]="false"
*ngIf="showTreatmentPrescription"></app-treatment-map>
*ngIf="showTreatmentPrescription$ | async"></app-treatment-map>
<app-treatment-legend
*ngIf="showTreatmentPrescription"></app-treatment-legend>
*ngIf="showTreatmentPrescription$ | async"></app-treatment-legend>
</sg-panel>
<sg-panel
class="legend-panel"
[paddedContent]="false"
*ngIf="!showTreatmentPrescription">
*ngIf="(showTreatmentPrescription$ | async) === false">
<app-direct-impacts-map-legend></app-direct-impacts-map-legend>
</sg-panel>
</section>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,24 @@ import { SharedModule } from '@shared';

import { TreatmentsState } from '../treatments.state';
import { ActivatedRoute, Router } from '@angular/router';
import { catchError, combineLatest, map, of, switchMap } from 'rxjs';
import { catchError, map, switchMap } from 'rxjs';
import { SelectedStandsState } from '../treatment-map/selected-stands.state';
import { TreatedStandsState } from '../treatment-map/treated-stands.state';
import { MapConfigState } from '../treatment-map/map-config.state';
import { getMergedRouteData } from '../treatments-routing-data';
import { DirectImpactsMapComponent } from '../direct-impacts-map/direct-impacts-map.component';
import { DirectImpactsSyncedMapsComponent } from '../direct-impacts-synced-maps/direct-impacts-synced-maps.component';
import { ButtonComponent, ModalComponent, PanelComponent } from '@styleguide';
import {
ButtonComponent,
ModalComponent,
PanelComponent,
TreatmentTypeIconComponent,
} from '@styleguide';
import { MatIconModule } from '@angular/material/icon';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import {
MatSlideToggleChange,
MatSlideToggleModule,
} from '@angular/material/slide-toggle';
import { FormsModule } from '@angular/forms';
import { TreatmentMapComponent } from '../treatment-map/treatment-map.component';
import { TreatmentLegendComponent } from '../treatment-legend/treatment-legend.component';
Expand All @@ -29,12 +37,11 @@ import { ImpactsMetric } from '../metrics';
import { DirectImpactsMapLegendComponent } from '../direct-impacts-map-legend/direct-impacts-map-legend.component';
import { DirectImpactsStateService } from '../direct-impacts.state.service';
import { StandDataChartComponent } from '../stand-data-chart/stand-data-chart.component';
import { MapGeoJSONFeature } from 'maplibre-gl';
import { Chart } from 'chart.js';
import ChartDataLabels from 'chartjs-plugin-datalabels';
import { TreatmentTypeIconComponent } from '../../../styleguide/treatment-type-icon/treatment-type-icon.component';
import { ExpandedStandDataChartComponent } from '../expanded-stand-data-chart/expanded-stand-data-chart.component';
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import { ExpandedDirectImpactMapComponent } from '../expanded-direct-impact-map/expanded-direct-impact-map.component';

@Component({
selector: 'app-direct-impacts',
Expand Down Expand Up @@ -110,7 +117,8 @@ export class DirectImpactsComponent implements OnInit, OnDestroy {
breadcrumbs$ = this.treatmentsState.breadcrumbs$;
treatmentPlan$ = this.treatmentsState.treatmentPlan$;

showTreatmentPrescription = false;
showTreatmentPrescription$ =
this.directImpactsStateService.showTreatmentPrescription$;

standChartPanelTitle$ = this.directImpactsStateService.activeStand$.pipe(
map((activeStand) => {
Expand All @@ -125,21 +133,19 @@ export class DirectImpactsComponent implements OnInit, OnDestroy {
map((m) => m.metric)
);

// todo: placeholder to fill once we have project area filter
projectArea$ = of('All Project Areas');

mapPanelTitle$ = combineLatest([
this.directImpactsStateService.activeMetric$,
this.projectArea$,
]).pipe(
map(([activeMetric, pa]) => `${activeMetric.metric.label} for ${pa}`)
filterOptions$ = this.directImpactsStateService.reportMetrics$.pipe(
map((metrics) => Object.values(metrics).map((metric) => metric.id))
);

mapPanelTitle$ = this.directImpactsStateService.mapPanelTitle$;

activateMetric(data: ImpactsMetric) {
this.directImpactsStateService.setActiveMetric(data);
}

getValues(activeStand: MapGeoJSONFeature) {}
updateReportMetric(data: ImpactsMetric) {
this.directImpactsStateService.updateReportMetric(data);
}

ngOnInit(): void {
// Register the plugin only when this component is initialized
Expand All @@ -156,4 +162,14 @@ export class DirectImpactsComponent implements OnInit, OnDestroy {
injector: this.injector, // Pass the current injector to the dialog
});
}

expandMaps() {
this.dialog.open(ExpandedDirectImpactMapComponent, {
injector: this.injector, // Pass the current injector to the dialog
});
}

saveShowTreatmentPrescription(value: MatSlideToggleChange) {
this.directImpactsStateService.setShowTreatmentPrescription(value.checked);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<sg-modal
[title]="(mapPanelTitle$ | async) || ''"
primaryButtonText="Create"
[scrollableContent]="false"
[showBorders]="true"
[hasFooter]="false"
(clickedClose)="close()"
width="full"
class="expanded-maps-modal">
<div modalBodyContent class="expanded-maps">
<app-direct-impacts-synced-maps
*ngIf="
(showTreatmentPrescription$ | async) === false
"></app-direct-impacts-synced-maps>
<app-treatment-map
[showProjectAreaTooltips]="false"
*ngIf="showTreatmentPrescription$ | async"></app-treatment-map>
<app-treatment-legend
*ngIf="showTreatmentPrescription$ | async"></app-treatment-legend>
</div>
</sg-modal>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@import "mixins";

.expanded-maps {
height: 90vh;
position: relative;
}

::ng-deep {
sg-modal.expanded-maps-modal {

&.full {
width: 80vw;
}

.header {
@include h5();
height: 39px;
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { ExpandedDirectImpactMapComponent } from './expanded-direct-impact-map.component';
import { MockProvider } from 'ng-mocks';
import { DirectImpactsStateService } from '../direct-impacts.state.service';
import { BehaviorSubject } from 'rxjs';
import { MatDialogRef } from '@angular/material/dialog';

describe('ExpandedDirectImpactMapComponent', () => {
let component: ExpandedDirectImpactMapComponent;
let fixture: ComponentFixture<ExpandedDirectImpactMapComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ExpandedDirectImpactMapComponent],
providers: [
MockProvider(DirectImpactsStateService, {
activeStand$: new BehaviorSubject(null),
}),
{ provide: MatDialogRef, useValue: {} },
],
}).compileComponents();

fixture = TestBed.createComponent(ExpandedDirectImpactMapComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Loading

0 comments on commit 63ea51f

Please sign in to comment.