From 4e83bc615804726cfe048eb7b4aabe6b2e92805f Mon Sep 17 00:00:00 2001 From: Willem Dantuma Date: Tue, 21 Apr 2020 12:31:20 +0200 Subject: [PATCH] Some refactoring --- .../fm-map/components/map/map.component.ts | 167 +++++++++++------- .../src/fm-map/effects/map.effects.ts | 43 ++--- .../src/fm-map/reducers/map.reducer.ts | 8 +- 3 files changed, 129 insertions(+), 89 deletions(-) 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 1ab4af7..be31c0c 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 @@ -1,8 +1,8 @@ -import { Component, OnInit, OnDestroy, HostListener, Inject, ViewChild, AfterViewInit,ChangeDetectorRef,NgZone } from '@angular/core'; +import { Component, OnInit, OnDestroy, HostListener, ViewChild, AfterViewInit,NgZone } from '@angular/core'; import { Location } from '@angular/common'; -import { Observable, Subject, Subscription,combineLatest, from,interval } from 'rxjs'; -import { debounce, withLatestFrom, first, combineAll,throttle } from 'rxjs/operators'; -import { Router, ActivatedRoute, ParamMap, Event } from '@angular/router'; +import { Observable, Subject, Subscription, from,of } from 'rxjs'; +import { withLatestFrom, switchMap } from 'rxjs/operators'; +import { Router, ActivatedRoute, ParamMap } from '@angular/router'; import { Store } from '@ngrx/store'; //import { proj,Map } from 'openlayers'; @@ -16,7 +16,6 @@ import { IQueryState } from '@farmmaps/common'; import { IPeriodState } from '../../models/period.state'; import {IStyles} from '../../models/style.cache'; import { IDroppedFile } from '../aol/file-drop-target/file-drop-target.component'; -import { IMetaData } from '../meta-data-modal/meta-data-modal.component'; import { StateSerializerService } from '@farmmaps/common'; import { GeolocationService} from '../../services/geolocation.service'; import {DeviceOrientationService} from '../../services/device-orientation.service'; @@ -30,7 +29,6 @@ import {commonActions} from '@farmmaps/common'; import {Feature} from 'ol'; import {Extent,createEmpty,extend } from 'ol/extent'; import {transform} from 'ol/proj'; -import { query } from '@angular/animations'; import { tassign } from 'tassign'; import * as style from 'ol/style'; @@ -57,7 +55,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { public droppedFile$: Subject = new Subject(); private paramSub: Subscription; private itemTypeSub: Subscription; - private mapStateSub: Subscription; + private stateSub: Subscription; private queryStateSub: Subscription; private querySub: Subscription; public parentCode$: Observable =this.store.select(mapReducers.selectGetParentCode); @@ -67,6 +65,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { public clickedFeature: Subject = new Subject(); public selectedItem$: Observable = this.store.select(mapReducers.selectGetSelectedItem); public queryState$: Observable = this.store.select(mapReducers.selectGetQueryState); + public state$:Observable<{mapState:IMapState,queryState:IQueryState,setStateCount:number}> = this.store.select(mapReducers.selectGetState); public period$: Observable = this.store.select(mapReducers.selectGetPeriod); public clearEnabled$: Observable = this.store.select(mapReducers.selectGetClearEnabled); public searchCollapsed$: Observable = this.store.select(mapReducers.selectGetSearchCollapsed); @@ -80,6 +79,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { public extent$: Observable = this.store.select(mapReducers.selectGetExtent); public styles$:Observable = this.store.select(mapReducers.selectGetStyles); private setStateCount$:Observable = this.store.select(mapReducers.selectgetSetStateCount); + private lastUrl = ""; @ViewChild('map') map; @@ -121,19 +121,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { } } } - }); - this.mapStateSub = this.mapState$.pipe(withLatestFrom(this.queryState$),withLatestFrom(this.setStateCount$)).subscribe(([[mapState,queryState],setStateCount]) =>{ - if(setStateCount>0) { - console.debug(`Mapstate ${setStateCount}`); - this.replaceUrl(mapState,queryState,true); - } - }); - this.queryStateSub = this.queryState$.pipe(withLatestFrom(this.mapState$),withLatestFrom(this.setStateCount$)).subscribe(([[queryState,mapState],setStateCount]) =>{ - if(setStateCount>0) { - console.debug(`Querystate ${setStateCount}`); - this.replaceUrl(mapState,queryState,true); - } - }) + }); } @HostListener('document:keyup', ['$event']) @@ -172,8 +160,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { console.debug("Init"); this.store.dispatch(new mapActions.Clear()); this.selectedFeatures$.next({x:0,y:0,features:[]}); - this.selectedFeatures$.next(null); - + this.selectedFeatures$.next(null); } initCustomStyles() { @@ -195,47 +182,107 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { }))); } + round(value:number,decimals:number):number { + let d = Math.pow(10, decimals); + return Math.round((value + Number.EPSILON)*d)/d; + } + + getMapStateFromUrl(params:ParamMap):IMapState { + var hasUrlmapState = params.has("xCenter") && params.has("yCenter"); + if (hasUrlmapState) { + let xCenter = parseFloat(params.get("xCenter")); + let yCenter = parseFloat(params.get("yCenter")); + let zoom = parseFloat(params.get("zoom")); + let rotation = parseFloat(params.get("rotation")); + let baseLayer = params.get("baseLayer")?params.get("baseLayer"):""; + var newMapState = {zoom: zoom, rotation: rotation, xCenter: xCenter, yCenter: yCenter, baseLayerCode: baseLayer }; + return newMapState; + } else { + return null; + } + } + + normalizeMapState(mapState:IMapState):IMapState { + if(!mapState) return null; + return {zoom: this.round(mapState.zoom,0), + rotation: this.round(mapState.rotation,2), + xCenter: this.round(mapState.xCenter,5), + yCenter: this.round(mapState.yCenter,5), + baseLayerCode: mapState.baseLayerCode }; + } + + serializeMapState(mapState:IMapState):string { + return JSON.stringify(this.normalizeMapState(mapState)); + } + + getQueryStateFromUrl(params:ParamMap):IQueryState { + if (params.has("queryState")) { + let queryState = params.get("queryState"); + var newQueryState = tassign(mapReducers.initialQueryState); + if (queryState != "") { + newQueryState = this.serializeService.deserialize(queryState); + } + return newQueryState; + } else { + return null; + } + } + ngAfterViewInit() { console.debug("View init"); this.initCustomStyles(); - this.paramSub = this.route.paramMap.pipe(withLatestFrom(this.setStateCount$),withLatestFrom(this.queryState$),withLatestFrom(this.mapState$)).subscribe( ([[[params,setStateCount],lastQueryState],lastMapState]) => { - console.debug(`Url change ${setStateCount}`); - var newMapState: IMapState = lastMapState; - var newQueryState: IQueryState = lastQueryState; - var hasUrlmapState = params.has("xCenter") && params.has("yCenter"); + + // url to state + + this.paramSub = this.route.paramMap.pipe(withLatestFrom(this.state$),switchMap(([params,state]) => { + var newMapState: IMapState = state.mapState; + var newQueryState: IQueryState = state.queryState; + var queryStateChanged = false; - if (hasUrlmapState) { - let xCenter = parseFloat(params.get("xCenter")); - let yCenter = parseFloat(params.get("yCenter")); - let zoom = parseFloat(params.get("zoom")); - let rotation = parseFloat(params.get("rotation")); - let baseLayer = params.get("baseLayer")?params.get("baseLayer"):""; - newMapState = { xCenter: xCenter, yCenter: yCenter, zoom: zoom, rotation: rotation, baseLayerCode: baseLayer } - window.localStorage.setItem("FarmMapsCommonMap_mapState",JSON.stringify(newMapState)); + var mapStateChanged = false; + let urlMapState = this.getMapStateFromUrl(params); + if(urlMapState) { + newMapState = urlMapState; + mapStateChanged = this.serializeMapState(state.mapState) != this.serializeMapState(newMapState); } - if (params.has("queryState")) { - let queryState = params.get("queryState"); - newQueryState = tassign(mapReducers.initialQueryState); - if (queryState != "") { - newQueryState = this.serializeService.deserialize(queryState); - queryState = this.serializeService.serialize(newQueryState); - } - queryStateChanged = this.serializeService.serialize(lastQueryState) != queryState; - } - console.debug(newQueryState); - console.debug(queryStateChanged?"Changed":""); - let t =0; - if(setStateCount==0) t=600; - setTimeout(() => { - this.zone.run(()=> { - if (setStateCount ==0) { - this.store.dispatch(new mapActions.SetState(newMapState,newQueryState)); - } else if(queryStateChanged) { - this.store.dispatch(new mapActions.SetQueryState(newQueryState)); - } - }) - },t); + + let urlQueryState = this.getQueryStateFromUrl(params); + if(urlQueryState) { + newQueryState = urlQueryState; + queryStateChanged = this.serializeService.serialize(state.queryState) != this.serializeService.serialize(urlQueryState); + } + + if(queryStateChanged || mapStateChanged) { + return of(new mapActions.SetState(newMapState,newQueryState)); + } else { + return of(null); + } + })).subscribe((action) => { + if(action) { + window.localStorage.setItem("FarmMapsCommonMap_mapState",this.serializeMapState(action.mapState)); + console.debug("Url to state"); + this.store.dispatch(action); + } }); + + // state to url + + this.stateSub = this.state$.pipe(withLatestFrom(this.route.paramMap),switchMap(([state,params]) => { + let newUrl = this.serializeMapState(state.mapState) + "_" + this.serializeService.serialize(state.queryState); + if(this.lastUrl!=newUrl && state.setStateCount>0) { + this.lastUrl=newUrl; + return of(state); + } + else { + return of(null); + } + })).subscribe((newUrlState) =>{ + if(newUrlState) { + console.debug(`State to url ${newUrlState.setStateCount}`); + this.replaceUrl(newUrlState.mapState,newUrlState.queryState,true); + } + }); + setTimeout(() => { this.map.instance.updateSize(); }, 500); @@ -278,7 +325,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { if(mapState.baseLayerCode!="") { parts.push(mapState.baseLayerCode); parts.push( this.serializeService.serialize(queryState)); - console.debug("Replace url",mapState,queryState,replace); + console.debug("Replace url",parts); this.router.navigate(parts, { replaceUrl: replace,relativeTo:this.route.parent }); } } @@ -296,7 +343,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { let state = { mapState: mapState, extent: extent }; let source = from([state]); source.pipe(withLatestFrom(this.selectedBaseLayer$),withLatestFrom(this.setStateCount$)).subscribe(([[state, baselayer],setStateCount]) => { - if (mapState && baselayer && setStateCount > 0) { // do not react on first move + if (mapState && baselayer) { // do not react on first move let newMapState = tassign(state.mapState, { baseLayerCode: baselayer.item.code }); this.store.dispatch(new mapActions.SetMapState(newMapState)); this.store.dispatch(new mapActions.SetViewExtent(state.extent)); @@ -350,7 +397,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { ngOnDestroy() { if (this.paramSub) this.paramSub.unsubscribe(); if (this.itemTypeSub) this.itemTypeSub.unsubscribe(); - if (this.mapStateSub) this.mapStateSub.unsubscribe(); + if (this.stateSub) this.stateSub.unsubscribe(); if (this.queryStateSub) this.queryStateSub.unsubscribe(); if (this.querySub) this.querySub.unsubscribe(); } 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 a0207e0..3c4ac09 100644 --- a/projects/common-map/src/fm-map/effects/map.effects.ts +++ b/projects/common-map/src/fm-map/effects/map.effects.ts @@ -27,6 +27,7 @@ import {FeatureIconService} from '../services/feature-icon.service'; import * as style from 'ol/style'; import { ItemTypeService } from '@farmmaps/common'; +import { IQueryState } from 'dist/common/public-api'; @Injectable() @@ -248,23 +249,27 @@ export class MapEffects { } })); + getActionFromQueryState(queryState:IQueryState, inSearch:boolean):Observable|[] { + if(!inSearch && (queryState.itemType || queryState.parentCode || queryState.itemType)) { + var newAction:Action; + if (queryState.itemCode && queryState.itemCode != "") { + newAction= new mapActions.SelectItem(queryState.itemCode); + } else { + newAction= new mapActions.StartSearch(queryState); + } + return of(newAction); + } else { + return []; + } + } + @Effect() setQueryState$: Observable = this.actions$.pipe( ofType(mapActions.SETQUERYSTATE), withLatestFrom(this.store$.select(mapReducers.selectGetInSearch)), switchMap(([action,inSearch]) => { - if(!inSearch) { - let a = action as mapActions.SetQueryState; - var newAction:Action; - if (a.queryState.itemCode && a.queryState.itemCode != "") { - newAction= new mapActions.SelectItem(a.queryState.itemCode); - } else { - newAction= new mapActions.StartSearch(a.queryState); - } - return of(newAction); - } else { - return []; - } + let a = action as mapActions.SetQueryState; + return this.getActionFromQueryState(a.queryState,inSearch); })); @Effect() @@ -272,18 +277,8 @@ export class MapEffects { ofType(mapActions.SETSTATE), withLatestFrom(this.store$.select(mapReducers.selectGetInSearch)), switchMap(([action,inSearch]) => { - if(!inSearch) { - let a = action as mapActions.SetQueryState; - var newAction:Action; - if (a.queryState.itemCode && a.queryState.itemCode != "") { - newAction= new mapActions.SelectItem(a.queryState.itemCode); - } else { - newAction= new mapActions.StartSearch(a.queryState); - } - return of(newAction); - } else { - return []; - } + let a = action as mapActions.SetState; + return this.getActionFromQueryState(a.queryState,inSearch); })); constructor(private actions$: Actions, private store$: Store, private folderService$: FolderService, private itemService$: ItemService,private featureIconService$:FeatureIconService,private itemTypeService$:ItemTypeService) { 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 15ccad4..1e0e9ce 100644 --- a/projects/common-map/src/fm-map/reducers/map.reducer.ts +++ b/projects/common-map/src/fm-map/reducers/map.reducer.ts @@ -466,11 +466,7 @@ export function reducer(state = initialState, action: mapActions.Actions | commo case mapActions.SHOWLAYERSWITCHER:{ let a = action as mapActions.ShowLayerSwitcher; return tassign(state,{showLayerSwitcher:a.show}); - } - - case commonActions.INITUSER: { - return tassign(state,{setStateCount:0,features:[],selectedFeature:null,selectedItem:null}); - } + } default: { return state; } @@ -501,6 +497,7 @@ export const getStyles = (state:State) => state.styles; export const getShowLayerSwitcher = (state:State) => state.showLayerSwitcher; export const getSetStateCount = (state:State) => state.setStateCount; export const getInSearch = (state:State) => state.inSearch; +export const getState = (state:State) => {return {mapState:state.mapState,queryState:state.queryState,setStateCount:state.setStateCount};} export const selectMapState = createFeatureSelector(MODULE_NAME); export const selectGetMapState= createSelector(selectMapState, getMapState); @@ -527,5 +524,6 @@ export const selectGetStyles = createSelector(selectMapState, getStyles); export const selectGetShowLayerSwitcher = createSelector(selectMapState,getShowLayerSwitcher); export const selectgetSetStateCount = createSelector(selectMapState,getSetStateCount); export const selectGetInSearch = createSelector(selectMapState,getInSearch); +export const selectGetState = createSelector(selectMapState,getState);