From 9d5cd0fa8894f9b940b6c42f5ec26b0ac81134bf Mon Sep 17 00:00:00 2001 From: Willem Dantuma Date: Fri, 5 Mar 2021 17:19:30 +0100 Subject: [PATCH] Add layer values --- package-lock.json | 18 +++--- package.json | 6 +- .../src/fm-map/actions/map.actions.ts | 35 ++++++++++- .../src/fm-map/common-map.module.ts | 4 +- .../layer-values/layer-values.component.html | 18 ++++++ .../layer-values/layer-values.component.scss | 37 ++++++++++++ .../layer-values.component.spec.ts | 25 ++++++++ .../layer-values/layer-values.component.ts | 59 +++++++++++++++++++ .../fm-map/components/map/map.component.html | 3 +- .../fm-map/components/map/map.component.ts | 7 +++ .../src/fm-map/effects/map.effects.ts | 53 ++++++++++++++++- .../src/fm-map/models/layer.value.ts | 7 +++ .../src/fm-map/reducers/map.reducer.ts | 38 +++++++++++- 13 files changed, 290 insertions(+), 20 deletions(-) create mode 100644 projects/common-map/src/fm-map/components/aol/layer-values/layer-values.component.html create mode 100644 projects/common-map/src/fm-map/components/aol/layer-values/layer-values.component.scss create mode 100644 projects/common-map/src/fm-map/components/aol/layer-values/layer-values.component.spec.ts create mode 100644 projects/common-map/src/fm-map/components/aol/layer-values/layer-values.component.ts create mode 100644 projects/common-map/src/fm-map/models/layer.value.ts diff --git a/package-lock.json b/package-lock.json index e8c6bc1..d4b3f5f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1865,25 +1865,25 @@ } }, "@farmmaps/common": { - "version": "0.0.1-prerelease.560", - "resolved": "https://repository.akkerweb.nl/repository/npm-group/@farmmaps/common/-/common-0.0.1-prerelease.560.tgz", - "integrity": "sha512-87kbYb6bF2J4/IFHl77hii3ab2cbxV0TZYZHk2Td9E6WHZisp9lCASOwye72/bDNsmOaS+YYj4xCEz6eRPfr5A==", + "version": "0.0.1-prerelease.563", + "resolved": "https://repository.akkerweb.nl/repository/npm-group/@farmmaps/common/-/common-0.0.1-prerelease.563.tgz", + "integrity": "sha512-XsPocXZm3/glNaaRF3ojzzbwILmM6HVW6lvLKMDUH/1deUB4EAlTbcj0O1hpkArcDfBs6lRvMXkWpbyT58fxmQ==", "requires": { "tslib": "^2.0.0" } }, "@farmmaps/common-map": { - "version": "0.0.1-prerelease.560", - "resolved": "https://repository.akkerweb.nl/repository/npm-group/@farmmaps/common-map/-/common-map-0.0.1-prerelease.560.tgz", - "integrity": "sha512-DzQYLsYU6rar+syX5DzqhUlKQ3z/2A8l2cWIiDafWMWO0nW4nEb09zmRknFiBk5q+SzuhvSmZzqndYSxA8vY0w==", + "version": "0.0.1-prerelease.563", + "resolved": "https://repository.akkerweb.nl/repository/npm-group/@farmmaps/common-map/-/common-map-0.0.1-prerelease.563.tgz", + "integrity": "sha512-uxjRwwh/8Q/NHF86grwNUC8b0cyHrtAHeVuOYuAtLSjy/DA9k0+C3/S6LgOnvmP0K4Sempc81BLeDvN0eG0cGA==", "requires": { "tslib": "^2.0.0" } }, "@farmmaps/common-map3d": { - "version": "0.0.1-prerelease.560", - "resolved": "https://repository.akkerweb.nl/repository/npm-group/@farmmaps/common-map3d/-/common-map3d-0.0.1-prerelease.560.tgz", - "integrity": "sha512-awp3inFWxmyKm0DK5IxjyiStjBn/6B6yiMezoWjJrPue6ygfYHKGy5O2GKI1gzONS/75skeRqcl2zswBDWK2xw==", + "version": "0.0.1-prerelease.563", + "resolved": "https://repository.akkerweb.nl/repository/npm-group/@farmmaps/common-map3d/-/common-map3d-0.0.1-prerelease.563.tgz", + "integrity": "sha512-njlJgIjhpZll8Q1B/VSe7lwrM47Zc4V40jj8Eb2MVWwW7DrRVhf1qrqTbrNHbOzdi+0oy79/TUKQlYvmeVEkPQ==", "requires": { "tslib": "^2.0.0" } diff --git a/package.json b/package.json index 66d1cea..3322d1b 100644 --- a/package.json +++ b/package.json @@ -19,9 +19,9 @@ "@angular/platform-browser": "~10.2.4", "@angular/platform-browser-dynamic": "~10.2.4", "@angular/router": "~10.2.4", - "@farmmaps/common": ">=0.0.1-prerelease.560 <0.0.1", - "@farmmaps/common-map": ">=0.0.1-prerelease.560 <0.0.1", - "@farmmaps/common-map3d": ">=0.0.1-prerelease.560 <0.0.1", + "@farmmaps/common": ">=0.0.1-prerelease.563 <0.0.1", + "@farmmaps/common-map": ">=0.0.1-prerelease.563 <0.0.1", + "@farmmaps/common-map3d": ">=0.0.1-prerelease.563 <0.0.1", "@microsoft/signalr": "^3.1.3", "@ng-bootstrap/ng-bootstrap": "^7.0", "@ngrx/effects": "^10.0", diff --git a/projects/common-map/src/fm-map/actions/map.actions.ts b/projects/common-map/src/fm-map/actions/map.actions.ts index 17c8c2a..0b085ba 100644 --- a/projects/common-map/src/fm-map/actions/map.actions.ts +++ b/projects/common-map/src/fm-map/actions/map.actions.ts @@ -2,6 +2,7 @@ import { Action } from '@ngrx/store'; import { IMapState } from '../models/map.state'; import { IItemLayer } from '../models/item.layer'; +import { ILayervalue } from '../models/layer.value'; import { IQueryState } from '@farmmaps/common'; import { IItem } from '@farmmaps/common'; import { Feature,Style } from 'ol'; @@ -45,6 +46,10 @@ export const SHOWLAYERSWITCHER = '[Map] ShowLayerSwitcher'; export const CLEAR = '[Map] Clear'; export const SETREPLACEURL = '[Map] SetReplaceUrl'; export const SETFEATURES = '[Map] SetFeatures' +export const SETLAYERVALUESLOCATION = '[Map] SetLayerValuesLocation' +export const TOGGLELAYERVALUESENABLED = '[Map] ToggleLayerValuesEnabled' +export const GETLAYERVALUE = '[Map] GetLayerValue' +export const GETLAYERVALUESUCCESS = '[Map] GetLayerValueSuccess' export class Clear implements Action { readonly type = CLEAR; @@ -277,6 +282,30 @@ export class SetFeatures implements Action { constructor(public features: Array) { } } +export class SetLayerValuesLocation implements Action { + readonly type = SETLAYERVALUESLOCATION; + + constructor(public x:number, public y:number) { } +} + +export class ToggleLayerValuesEnabled implements Action { + readonly type = TOGGLELAYERVALUESENABLED; + + constructor() { } +} + +export class GetLayerValue implements Action { + readonly type = GETLAYERVALUE; + + constructor(public itemLayer:IItemLayer,public x:number,public y:number) { } +} + +export class GetLayerValueSuccess implements Action { + readonly type = GETLAYERVALUESUCCESS; + + constructor(public layervalue:ILayervalue) { } +} + export type Actions = SetMapState | Init | Clear @@ -315,5 +344,9 @@ export type Actions = SetMapState | ShowLayerSwitcher | SetReplaceUrl | SetFeatures - | SetSelectedItemLayer; + | SetSelectedItemLayer + | SetLayerValuesLocation + | ToggleLayerValuesEnabled + | GetLayerValueSuccess + | GetLayerValue; diff --git a/projects/common-map/src/fm-map/common-map.module.ts b/projects/common-map/src/fm-map/common-map.module.ts index dfd2cfb..cf86c72 100644 --- a/projects/common-map/src/fm-map/common-map.module.ts +++ b/projects/common-map/src/fm-map/common-map.module.ts @@ -70,6 +70,7 @@ import {HistogramDetailsComponent} from './components/legend/histogram-details/h import {StatisticsDetailsComponent} from './components/legend/statistics-details/statistics-details.component'; import { ifZoomToShowDirective} from './components/if-zoom-to-show/if-zoom-to-show.directive'; import { ZoomToShowAlert} from './components/zoom-to-show-alert/zoom-to-show-alert.component'; +import { LayerValuesComponent } from './components/aol/layer-values/layer-values.component'; export function LocalStorageSync(reducer: ActionReducer): ActionReducer { const r = function(state, action) { @@ -207,7 +208,8 @@ export { HistogramDetailsComponent, StatisticsDetailsComponent, ifZoomToShowDirective, - ZoomToShowAlert + ZoomToShowAlert, + LayerValuesComponent ], entryComponents: [ FeatureListComponent, diff --git a/projects/common-map/src/fm-map/components/aol/layer-values/layer-values.component.html b/projects/common-map/src/fm-map/components/aol/layer-values/layer-values.component.html new file mode 100644 index 0000000..feeeba4 --- /dev/null +++ b/projects/common-map/src/fm-map/components/aol/layer-values/layer-values.component.html @@ -0,0 +1,18 @@ +
+
+
+
+
{{lonlat$}}
+
    +
  • +
    {{layerValue.layerName}}
    +
    {{layerValue.date|date}}
    +
    {{layerValue.quantity}} {{layerValue.value}}{{layerValue.unit}}
    +
  • +
+ +
No data at location
+
+
+
+
> diff --git a/projects/common-map/src/fm-map/components/aol/layer-values/layer-values.component.scss b/projects/common-map/src/fm-map/components/aol/layer-values/layer-values.component.scss new file mode 100644 index 0000000..f998fad --- /dev/null +++ b/projects/common-map/src/fm-map/components/aol/layer-values/layer-values.component.scss @@ -0,0 +1,37 @@ +.layer-values { + position: absolute; + left: 50%; + top: 30%; +} + +.cross { + display: block; + position: relative; + width: 1em; + height: 1em; + left: -0.5em; + top: -0.5em; +} + +.values-container { + position: relative; + background-color: white; + left: calc( 1em - 1px); + top: -1.3em; + min-width: 15em; +} + +.value-list { + list-style: none; +} + +.pointer { + position: relative; + width: 0px; + height: 0px; + left: 0.5em; + border-top: 0.5em solid transparent; + border-bottom: 0.5em solid transparent; + border-right: 0.5em solid black; +} + diff --git a/projects/common-map/src/fm-map/components/aol/layer-values/layer-values.component.spec.ts b/projects/common-map/src/fm-map/components/aol/layer-values/layer-values.component.spec.ts new file mode 100644 index 0000000..3d02c7e --- /dev/null +++ b/projects/common-map/src/fm-map/components/aol/layer-values/layer-values.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LayerValuesComponent } from './layer-values.component'; + +describe('LayerValuesComponent', () => { + let component: LayerValuesComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ LayerValuesComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(LayerValuesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/common-map/src/fm-map/components/aol/layer-values/layer-values.component.ts b/projects/common-map/src/fm-map/components/aol/layer-values/layer-values.component.ts new file mode 100644 index 0000000..2fb9380 --- /dev/null +++ b/projects/common-map/src/fm-map/components/aol/layer-values/layer-values.component.ts @@ -0,0 +1,59 @@ +import { Component, OnInit,Input,ViewChild,ElementRef,AfterViewInit } from '@angular/core'; +import {IItemLayer} from '../../../models/item.layer'; +import { Store } from '@ngrx/store'; +import * as mapReducers from '../../../reducers/map.reducer'; +import * as mapActions from '../../../actions/map.actions'; +import { MapComponent } from 'ngx-openlayers'; +import { ILayervalue } from '../../../models/layer.value'; +import { Observable,interval } from 'rxjs'; +import { debounce } from 'rxjs/operators'; +import { toLonLat } from 'ol/proj'; +import { toStringHDMS } from 'ol/coordinate'; +import { ClipboardService } from 'ngx-clipboard' +import {GeoJSON,WKT} from 'ol/format'; +import { Point } from 'ol/geom'; + +@Component({ + selector: 'fm-map-layer-values', + templateUrl: './layer-values.component.html', + styleUrls: ['./layer-values.component.scss'] +}) +export class LayerValuesComponent implements OnInit,AfterViewInit { + + @ViewChild('layerValues') containerRef:ElementRef; + offsetX$:number =0; + offsetY$:number =0; + lonlat$: string=""; + wkt$= ""; + layerValues$:Observable> = this.store.select(mapReducers.selectGetLayerValues); + enabled$:Observable = this.store.select(mapReducers.selectGetLayerValuesEnabled); + wktFormat$:WKT; + + constructor( private store: Store,private map: MapComponent,private clipboardService$:ClipboardService) { + this.wktFormat$=new WKT(); + } + + ngOnInit(): void { + + } + + ngAfterViewInit():void { + this.offsetY$ = this.containerRef.nativeElement.offsetTop; + this.offsetX$ = this.containerRef.nativeElement.offsetLeft; + this.map.instance.on('moveend', () => { + this.updateValuesLocation(); + }); + } + + updateValuesLocation() { + var xy = this.map.instance.getCoordinateFromPixel([this.offsetX$,this.offsetY$]) + var lonlat = toLonLat(xy); + this.wkt$ = this.wktFormat$.writeGeometry(new Point(lonlat)) + this.lonlat$ = toStringHDMS(lonlat); + this.store.dispatch(new mapActions.SetLayerValuesLocation(xy[0],xy[1])); + } + + copyToClipboard() { + this.clipboardService$.copy(this.wkt$); + } +} diff --git a/projects/common-map/src/fm-map/components/map/map.component.html b/projects/common-map/src/fm-map/components/map/map.component.html index 9cea51e..19b6e61 100644 --- a/projects/common-map/src/fm-map/components/map/map.component.html +++ b/projects/common-map/src/fm-map/components/map/map.component.html @@ -22,7 +22,7 @@ selectedFeature:selectedFeature$|async, fullscreen:fullscreen$|async } as state"> - +
@@ -40,6 +40,7 @@ +
diff --git a/projects/common-map/src/fm-map/components/map/map.component.ts b/projects/common-map/src/fm-map/components/map/map.component.ts index e1fb671..6046858 100644 --- a/projects/common-map/src/fm-map/components/map/map.component.ts +++ b/projects/common-map/src/fm-map/components/map/map.component.ts @@ -383,6 +383,13 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { }); } + handleShowLayerValues(event: MouseEvent) { + event.stopPropagation(); + this.zone.run(() =>{ + this.store.dispatch(new mapActions.ToggleLayerValuesEnabled()); + }); + } + handleOnDownload(event) { } diff --git a/projects/common-map/src/fm-map/effects/map.effects.ts b/projects/common-map/src/fm-map/effects/map.effects.ts index a0f1c85..08d44c2 100644 --- a/projects/common-map/src/fm-map/effects/map.effects.ts +++ b/projects/common-map/src/fm-map/effects/map.effects.ts @@ -3,8 +3,8 @@ import { Injectable } from '@angular/core'; import { Store, Action } from '@ngrx/store'; import { Effect, Actions,ofType } from '@ngrx/effects'; -import { EMPTY, Observable , of } from 'rxjs'; -import { withLatestFrom, switchMap, map, catchError, mergeMap } from 'rxjs/operators'; +import { EMPTY, Observable , of,merge} from 'rxjs'; +import { withLatestFrom, switchMap, map, catchError, mergeMap,tap } from 'rxjs/operators'; import {GeoJSON,WKT} from 'ol/format'; import {Feature} from 'ol'; @@ -297,6 +297,55 @@ export class MapEffects { return of(newAction); } + @Effect() + getLayerValue$: Observable = this.actions$.pipe( + ofType(mapActions.GETLAYERVALUE), + switchMap((action:mapActions.GetLayerValue) => { + var l = action.itemLayer.item.data["layers"][action.itemLayer.layerIndex]; + var scale = l.scale?l.scale:1; + return this.itemService$.getLayerValue(action.itemLayer.item.code,action.itemLayer.layerIndex,action.x,action.y).pipe( + switchMap((v: number) => { + let a=[]; + if(v) a.push(new mapActions.GetLayerValueSuccess({date:"",value:v*scale,layerName:l.name,quantity:l.quantity,unit:l.unit})); + return a; + })) + } + )); + + @Effect() + updateLayerValuesOnLayerAddedOrRemoved$: Observable = this.actions$.pipe( + ofType(mapActions.ADDLAYER,mapActions.REMOVELAYER,mapActions.SELECTITEM), + withLatestFrom(this.store$.select(mapReducers.selectGetLayerValuesX)), + withLatestFrom(this.store$.select(mapReducers.selectGetLayerValuesY)), + map(([[action,x],y]) => new mapActions.SetLayerValuesLocation(x,y)) + ); + + + @Effect() + getLayerValues$: Observable = this.actions$.pipe( + ofType(mapActions.SETLAYERVALUESLOCATION), + withLatestFrom(this.store$.select(mapReducers.selectGetSelectedItemLayer)), + withLatestFrom(this.store$.select(mapReducers.selectGetLayerValuesEnabled)), + withLatestFrom(this.store$.select(mapReducers.selectGetOverlayLayers)), + switchMap(([[[action, selected], enabled],overlayLayers]) => { + let layers = []; + if(selected) layers.push(selected); + overlayLayers.forEach((ol) => { + if(ol!=selected) layers.push(ol); + }); + let a = action as mapActions.SetLayerValuesLocation; + let actions = []; + if(enabled) { + layers.forEach((ol) => { + if("vnd.farmmaps.itemtype.shape.processed,vnd.farmmaps.itemtype.geotiff.processed".indexOf(ol.item.itemType)>=0) { + actions.push(new mapActions.GetLayerValue(ol,a.x,a.y)); + } + }); + } + return actions; + })); + + @Effect() setState$: Observable = this.actions$.pipe( ofType(mapActions.SETSTATE), diff --git a/projects/common-map/src/fm-map/models/layer.value.ts b/projects/common-map/src/fm-map/models/layer.value.ts new file mode 100644 index 0000000..ac71c3e --- /dev/null +++ b/projects/common-map/src/fm-map/models/layer.value.ts @@ -0,0 +1,7 @@ +export interface ILayervalue { + date:string; + layerName:string; + unit:string; + quantity:string; + value:number; +} \ No newline at end of file diff --git a/projects/common-map/src/fm-map/reducers/map.reducer.ts b/projects/common-map/src/fm-map/reducers/map.reducer.ts index 695fb97..5da6bb3 100644 --- a/projects/common-map/src/fm-map/reducers/map.reducer.ts +++ b/projects/common-map/src/fm-map/reducers/map.reducer.ts @@ -5,6 +5,7 @@ import { IMapState} from '../models/map.state'; import { IQueryState} from '@farmmaps/common'; import { IPeriodState} from '../models/period.state'; import { IStyles} from '../models/style.cache'; +import { ILayervalue } from '../models/layer.value'; import * as mapActions from '../actions/map.actions'; import {commonActions} from '@farmmaps/common'; import { createSelector, createFeatureSelector } from '@ngrx/store'; @@ -59,7 +60,11 @@ export interface State { showLayerSwitcher:boolean, inSearch:boolean, inZoom:boolean, - replaceUrl:boolean + replaceUrl:boolean, + layerValuesX:number, + layerValuesY:number, + layerValuesEnabled:boolean; + layerValues: Array; } export const initialState: State = { @@ -98,7 +103,11 @@ export const initialState: State = { showLayerSwitcher: false, inSearch:false, inZoom:false, - replaceUrl:true + replaceUrl:true, + layerValuesX:0, + layerValuesY:0, + layerValuesEnabled:false, + layerValues:[] } export function reducer(state = initialState, action: mapActions.Actions | commonActions.Actions | RouterNavigationAction): State { @@ -322,7 +331,7 @@ export function reducer(state = initialState, action: mapActions.Actions | commo case mapActions.COLLAPSESEARCH: { return tassign(state, { searchCollapsed: state.panelVisible ? false: true}); } - case commonActions.CLOSEALL: { + case commonActions.CLOSEALL: { return tassign(state, { searchCollapsed: state.panelVisible ? false: true,showLayerSwitcher:false}); } case mapActions.SETEXTENT: { @@ -462,6 +471,7 @@ export function reducer(state = initialState, action: mapActions.Actions | commo selectedFeature: null, queryState: newQueryState, clearEnabled: false, + layerValuesEnabled: false, searchCollapsed: true, searchMinified: false, features: [], @@ -484,6 +494,19 @@ export function reducer(state = initialState, action: mapActions.Actions | commo let a= action as mapActions.SetReplaceUrl; return tassign(state,{replaceUrl:a.replaceUrl}); } + case mapActions.TOGGLELAYERVALUESENABLED: { + return tassign(state,{layerValuesEnabled:!state.layerValuesEnabled}); + } + case mapActions.SETLAYERVALUESLOCATION: { + let a= action as mapActions.SetLayerValuesLocation; + return tassign(state,{layerValuesX: a.x, layerValuesY:a.y,layerValues:[]}); + } + case mapActions.GETLAYERVALUESUCCESS:{ + let a= action as mapActions.GetLayerValueSuccess; + let v = state.layerValues.slice(0); + v.push(a.layervalue); + return tassign(state,{layerValues:v}); + } default: { return state; } @@ -515,6 +538,11 @@ export const getStyles = (state:State) => state.styles; export const getShowLayerSwitcher = (state:State) => state.showLayerSwitcher; export const getInSearch = (state:State) => state.inSearch; export const getState = (state:State) => {return {mapState:state.mapState,queryState:state.queryState,replaceUrl:state.replaceUrl};} +export const getLayerValuesEnabled = (state:State) => state.layerValuesEnabled; +export const getLayerValues = (state:State) => state.layerValues; +export const getLayerValuesX = (state:State) => state.layerValuesX; +export const getLayerValuesY = (state:State) => state.layerValuesY; + export const selectMapState = createFeatureSelector(MODULE_NAME); export const selectGetMapState= createSelector(selectMapState, getMapState); @@ -542,5 +570,9 @@ export const selectGetStyles = createSelector(selectMapState, getStyles); export const selectGetShowLayerSwitcher = createSelector(selectMapState,getShowLayerSwitcher); export const selectGetInSearch = createSelector(selectMapState,getInSearch); export const selectGetState = createSelector(selectMapState,getState); +export const selectGetLayerValuesEnabled = createSelector(selectMapState,getLayerValuesEnabled); +export const selectGetLayerValues = createSelector(selectMapState,getLayerValues); +export const selectGetLayerValuesX = createSelector(selectMapState,getLayerValuesX); +export const selectGetLayerValuesY = createSelector(selectMapState,getLayerValuesY);