From 9fb5a9698c422b53f1fb32f654b885017bf0131c Mon Sep 17 00:00:00 2001 From: Willem Dantuma Date: Wed, 28 Oct 2020 13:31:12 +0100 Subject: [PATCH] Refactor map component --- .../src/fm-map/actions/map.actions.ts | 2 +- .../fm-map/components/map/map.component.ts | 141 +++++++++--------- .../src/fm-map/effects/map.effects.ts | 43 +++--- .../src/fm-map/reducers/map.reducer.ts | 60 ++++---- 4 files changed, 123 insertions(+), 123 deletions(-) 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 260e955..2ba6176 100644 --- a/projects/common-map/src/fm-map/actions/map.actions.ts +++ b/projects/common-map/src/fm-map/actions/map.actions.ts @@ -87,7 +87,7 @@ export class StartSearch implements Action { export class StartSearchSuccess implements Action { readonly type = STARTSEARCHSUCCESS; - constructor(public features: Array, public query:IQueryState,public setStateCount:number) { } + constructor(public features: Array, public query:IQueryState) { } } export class SelectFeature implements Action { 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 b7f2d3f..a135768 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,6 +1,6 @@ import { Component, OnInit, OnDestroy, HostListener, ViewChild, AfterViewInit,NgZone } from '@angular/core'; import { Location } from '@angular/common'; -import { Observable, Subject, Subscription, from,of } from 'rxjs'; +import { Observable, Subject, Subscription, from,of ,EMPTY } from 'rxjs'; import { withLatestFrom, switchMap,skip } from 'rxjs/operators'; import { Router, ActivatedRoute, ParamMap } from '@angular/router'; import { Store } from '@ngrx/store'; @@ -66,7 +66,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { public selectedItem$: Observable = this.store.select(mapReducers.selectGetSelectedItem); public parentItem$: Observable =this.store.select(mapReducers.selectGetParentItem); public queryState$: Observable = this.store.select(mapReducers.selectGetQueryState); - public state$:Observable<{mapState:IMapState,queryState:IQueryState,setStateCount:number}> = this.store.select(mapReducers.selectGetState); + public state$:Observable<{mapState:IMapState,queryState:IQueryState}> = 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); @@ -79,8 +79,8 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { public overlayLayersCollapsed: boolean = true; 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 = ""; + private initialized: boolean = false; @ViewChild('map') map; @@ -94,10 +94,10 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { private geolocationService: GeolocationService, private zone: NgZone, private deviceorientationService:DeviceOrientationService) { - this.querySub = this.query$.pipe(skip(1), withLatestFrom(this.mapState$),withLatestFrom(this.setStateCount$)).subscribe(([[queryState,mapState],setStateCount]) =>{ + this.querySub = this.query$.pipe(skip(1), withLatestFrom(this.mapState$)).subscribe(([queryState,mapState]) =>{ if(queryState) { let newQueryState = tassign(mapReducers.initialQueryState); - console.debug(`Do Query ${setStateCount}`); + console.debug(`Do Query`); let urlparts=[]; if (queryState.itemCode && queryState.itemCode != "") { if(queryState.itemType && queryState.itemType!= "") { @@ -115,7 +115,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { if(urlparts.length==0 ) { newQueryState.itemCode = queryState.itemCode; this.zone.run(() => { - this.store.dispatch(new mapActions.SetQueryState(newQueryState,false)); + this.replaceUrl(mapState,newQueryState,false); }) } else { this.router.navigate(urlparts); @@ -157,6 +157,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { } ngOnInit() { + this.initialized = false; console.debug("Init"); this.store.dispatch(new mapActions.Clear()); this.selectedFeatures$.next({x:0,y:0,features:[]}); @@ -234,44 +235,39 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { // 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; - var mapStateChanged = false; - let urlMapState = this.getMapStateFromUrl(params); - if(urlMapState) { - newMapState = urlMapState; - mapStateChanged = this.serializeMapState(state.mapState) != this.serializeMapState(newMapState); + let urlMapState = this.getMapStateFromUrl(this.route.snapshot.paramMap); + let urlQueryState = this.getQueryStateFromUrl(this.route.snapshot.paramMap); + if(urlQueryState && urlMapState) { + this.store.dispatch(new mapActions.SetState(urlMapState,urlQueryState)); + window.localStorage.setItem("FarmMapsCommonMap_mapState",this.serializeMapState(urlMapState)); + } else if(urlQueryState) { + this.store.dispatch(new mapActions.SetQueryState(urlQueryState)); + } else { + this.store.dispatch(new mapActions.SetReplaceUrl(true)); } - let urlQueryState = this.getQueryStateFromUrl(params); - if(urlQueryState) { - newQueryState = urlQueryState; - queryStateChanged = this.serializeService.serialize(state.queryState) != this.serializeService.serialize(urlQueryState); - } - - if(queryStateChanged && mapStateChanged && state.setStateCount ==0) { - return of(new mapActions.SetState(newMapState,newQueryState)); - window.localStorage.setItem("FarmMapsCommonMap_mapState",this.serializeMapState(newMapState)); - } else if(queryStateChanged) { - return of(new mapActions.SetQueryState(newQueryState)); - } return of(new mapActions.SetReplaceUrl(true)); - })).subscribe((action) => { - if(action) { - this.zone.run(() => { - console.debug("Url to state"); - this.store.dispatch(action); - }); - } - }); + this.paramSub = this.route.paramMap.pipe(withLatestFrom(this.state$),switchMap(([params,state]) => { + if(this.initialized) { + let urlQueryState = this.getQueryStateFromUrl(params); + if( this.serializeService.serialize(state.queryState) != this.serializeService.serialize(urlQueryState)) { + return of(new mapActions.SetState(state.mapState,urlQueryState)); + } + } + return EMPTY; + })).subscribe((action) => { + if(action) { + this.zone.run(() => { + console.debug("Url to state"); + this.store.dispatch(action); + }); + } + }); // state to url this.stateSub = this.state$.pipe(switchMap((state) => { let newUrl = this.serializeMapState(state.mapState) + "_" + this.serializeService.serialize(state.queryState); - if(this.lastUrl!=newUrl && state.setStateCount>0) { + if(this.lastUrl!=newUrl) { this.lastUrl=newUrl; return of(state); } @@ -280,14 +276,15 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { } })).subscribe((newUrlState) =>{ if(newUrlState) { - console.debug(`State to url ${newUrlState.setStateCount}`); + console.debug(`State to url`); this.replaceUrl(newUrlState.mapState,newUrlState.queryState,newUrlState.replaceUrl); } }); + this.initialized = true; + // setTimeout(() => { + // this.map.instance.updateSize(); - setTimeout(() => { - this.map.instance.updateSize(); - }, 500); + // }, 500); } handleSearchCollapse(event) { @@ -319,39 +316,49 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { } replaceUrl(mapState: IMapState, queryState: IQueryState, replace: boolean = true) { - let parts =["."]; - parts.push(mapState.xCenter.toFixed(5)); - parts.push(mapState.yCenter.toFixed(5)); - parts.push( mapState.zoom.toFixed(0)); - parts.push( mapState.rotation.toFixed(2)); - if(mapState.baseLayerCode!="") { + + let newMapState = this.serializeMapState(mapState); + let newQueryState = this.serializeService.serialize(queryState); + let currentMapState = this.serializeMapState(this.getMapStateFromUrl(this.route.snapshot.paramMap)); + let urlQueryState = this.getQueryStateFromUrl(this.route.snapshot.paramMap); + let currentQueryState = urlQueryState==null?"":this.serializeService.serialize(urlQueryState); + if(mapState.baseLayerCode!="" && ((newMapState!= currentMapState) || (newQueryState!=currentQueryState))) { + let parts =["."]; + parts.push(mapState.xCenter.toFixed(5)); + parts.push(mapState.yCenter.toFixed(5)); + parts.push( mapState.zoom.toFixed(0)); + parts.push( mapState.rotation.toFixed(2)); parts.push(mapState.baseLayerCode); parts.push( this.serializeService.serialize(queryState)); + console.debug("Replace url",parts); this.router.navigate(parts, { replaceUrl: replace,relativeTo:this.route.parent }); } } - handleOnMoveEnd(event) { - console.debug("Move end"); - this.zone.run(() =>{ - var map = event.map; - var view = map.getView(); - var rotation = view.getRotation(); - var zoom = view.getZoom(); - var center = transform(view.getCenter(), view.getProjection(), "EPSG:4326"); - var extent = view.calculateExtent(this.map.instance.getSize()); - let mapState: IMapState = { xCenter: center[0], yCenter: center[1], zoom: zoom, rotation: rotation, baseLayerCode: null }; - 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) { // 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)); - } + handleOnMoveEnd(event) { + if(this.initialized) { + this.zone.run(() =>{ + console.debug("Move end"); + var map = event.map; + var view = map.getView(); + var rotation = view.getRotation(); + var zoom = view.getZoom(); + var center = transform(view.getCenter(), view.getProjection(), "EPSG:4326"); + var viewExtent = view.calculateExtent(this.map.instance.getSize()); + let mapState: IMapState = { xCenter: center[0], yCenter: center[1], zoom: zoom, rotation: rotation, baseLayerCode: null }; + let state = { mapState: mapState, viewExtent: viewExtent }; + console.debug("Center: ",center[0],center[1] ); + let source = from([state]); + source.pipe(withLatestFrom(this.selectedBaseLayer$)).subscribe(([state, baselayer]) => { + 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.viewExtent)); + } + }); }); - }); + } } handleOnMouseDown(event: MouseEvent) { @@ -366,7 +373,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { } handleClearSearch(event) { - this.store.dispatch(new commonActions.Escape(true, false)); + this.store.dispatch(new mapActions.Clear()); } handleOnDelete(itemLayer: IItemLayer) { 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 d26b101..816fc75 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 { Observable , of, interval } from 'rxjs'; -import { withLatestFrom, switchMap, map, catchError, mergeMap,delayWhen } from 'rxjs/operators'; +import { EMPTY, Observable , of } from 'rxjs'; +import { withLatestFrom, switchMap, map, catchError, mergeMap } from 'rxjs/operators'; import {GeoJSON,WKT} from 'ol/format'; import {Feature} from 'ol'; @@ -112,8 +112,7 @@ export class MapEffects { @Effect() startSearch$: Observable = this.actions$.pipe( ofType(mapActions.STARTSEARCH), - withLatestFrom(this.store$.select(mapReducers.selectgetSetStateCount)), - switchMap(([action,setStateCount]) => { + switchMap((action) => { let a = action as mapActions.StartSearch; var startDate = a.queryState.startDate; var endDate = a.queryState.endDate; @@ -126,7 +125,7 @@ export class MapEffects { f.id = f.properties["code"]; } } - return of(new mapActions.StartSearchSuccess(this._geojsonFormat.readFeatures(features), a.queryState,setStateCount)); + return of(new mapActions.StartSearchSuccess(this._geojsonFormat.readFeatures(features), a.queryState)); } ), catchError(error => of(new commonActions.Fail(error)))); @@ -140,7 +139,6 @@ export class MapEffects { @Effect() zoomToExtent$: Observable = this.actions$.pipe( ofType(mapActions.STARTSEARCHSUCCESS), - delayWhen(action => (action as mapActions.StartSearchSuccess).setStateCount == 1 ? interval(500):interval(0)), mergeMap((action: mapActions.StartSearchSuccess) => { let actions =[]; actions.push(new commonActions.SetMenuVisible(false)); @@ -169,11 +167,10 @@ export class MapEffects { selectItem$: Observable = this.actions$.pipe( ofType(mapActions.SELECTITEM), withLatestFrom(this.store$.select(mapReducers.selectGetSelectedItem)), - withLatestFrom(this.store$.select(mapReducers.getSetStateCount)), - switchMap(([[action, selectedItem],setStateCount]) => { + switchMap(([action, selectedItem]) => { let a = action as mapActions.SelectItem; let itemCode = selectedItem ? selectedItem.code : ""; - if (a.itemCode != itemCode || setStateCount == 1) { + if (a.itemCode != itemCode) { return this.itemService$.getItem(a.itemCode).pipe( switchMap(child => { return this.itemService$.getItem(child.parentCode) @@ -278,21 +275,13 @@ export class MapEffects { } else { newAction= new mapActions.StartSearch(queryState); } - return of(newAction); + } else { - return of(new commonActions.Escape(true,false)); + newAction = new mapActions.Clear(); } + return of(newAction); } - @Effect() - setQueryState$: Observable = this.actions$.pipe( - ofType(mapActions.SETQUERYSTATE), - withLatestFrom(this.store$.select(mapReducers.selectGetInSearch)), - switchMap(([action,inSearch]) => { - let a = action as mapActions.SetQueryState; - return this.getActionFromQueryState(a.queryState,inSearch); - })); - @Effect() setState$: Observable = this.actions$.pipe( ofType(mapActions.SETSTATE), @@ -302,8 +291,20 @@ export class MapEffects { return this.getActionFromQueryState(a.queryState,inSearch); })); + @Effect() + escape$:Observable = this.actions$.pipe( + ofType(commonActions.ESCAPE), + switchMap((action) => { + let a = action as commonActions.Escape; + if(a.escapeKey) { + return of(new mapActions.Clear()); + } else { + return EMPTY; + } + })); + constructor(private actions$: Actions, private store$: Store, private folderService$: FolderService, private itemService$: ItemService,private featureIconService$:FeatureIconService,private itemTypeService$:ItemTypeService) { this._geojsonFormat = new GeoJSON(); this._wktFormat = new WKT(); - } + } } 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 330c224..e6e34d1 100644 --- a/projects/common-map/src/fm-map/reducers/map.reducer.ts +++ b/projects/common-map/src/fm-map/reducers/map.reducer.ts @@ -57,8 +57,8 @@ export interface State { selectedOverlayLayer: IItemLayer, styles:IStyles, showLayerSwitcher:boolean, - setStateCount:number, inSearch:boolean, + inZoom:boolean, replaceUrl:boolean } @@ -96,8 +96,8 @@ export const initialState: State = { selectedItemLayer: null, styles: {}, showLayerSwitcher: false, - setStateCount: 0, inSearch:false, + inZoom:false, replaceUrl:true } @@ -110,16 +110,16 @@ export function reducer(state = initialState, action: mapActions.Actions | commo case mapActions.SETMAPSTATE: { let a = action as mapActions.SetMapState; return tassign(state, { - mapState: a.mapState,setStateCount: state.setStateCount+1 + mapState: a.mapState }); } case mapActions.SETQUERYSTATE: { let a = action as mapActions.SetQueryState; - return tassign(state, { queryState: tassign(a.queryState ),setStateCount: state.setStateCount+1,replaceUrl:a.replaceUrl}); + return tassign(state, { queryState: tassign(a.queryState ),replaceUrl:a.replaceUrl}); } case mapActions.SETSTATE: { let a = action as mapActions.SetState; - return tassign(state, { mapState: tassign(a.mapState), queryState: tassign(a.queryState),setStateCount: state.setStateCount+1}); + return tassign(state, { mapState: tassign(a.mapState), queryState: tassign(a.queryState)}); } case mapActions.SETVIEWEXTENT: { let a = action as mapActions.SetViewExtent; @@ -147,7 +147,7 @@ export function reducer(state = initialState, action: mapActions.Actions | commo case mapActions.SELECTITEM: { let a = action as mapActions.SelectItem; let itemCode = state.selectedItem ? state.selectedItem.code : ""; - let inSearch = (a.itemCode != itemCode || state.setStateCount == 1) + let inSearch = a.itemCode != itemCode; return tassign(state, { selectedItem: null, selectedItemLayer: null, @@ -316,7 +316,7 @@ export function reducer(state = initialState, action: mapActions.Actions | commo case mapActions.SETEXTENT: { let a = action as mapActions.SetExtent; return tassign(state, { extent: a.extent }); - } + } case mapActions.ADDLAYER: { let a = action as mapActions.AddLayer; let itemLayers = state.overlayLayers.slice(0); @@ -436,27 +436,24 @@ export function reducer(state = initialState, action: mapActions.Actions | commo let a = action as mapActions.SelectOverlayLayer; return tassign(state, { selectedOverlayLayer: a.itemLayer }); } - case commonActions.ESCAPE: { - let a = action as commonActions.Escape; + + case mapActions.CLEAR: { let newQueryState = tassign(state.queryState, { query: null, tags: null, itemCode: null, parentCode: null, itemType: null }); - if (a.escapeKey) { - return tassign(state, { - panelVisible: false, - panelCollapsed:false, - selectedItem: null, - selectedItemLayer: null, - selectedFeature: null, - queryState: newQueryState, - clearEnabled: false, - searchCollapsed: true, - searchMinified: false, - features: [], - query:initialState.query, - showLayerSwitcher: false - }); - } else { - return tassign(state, {}); - } + return tassign(state, { + panelVisible: false, + panelCollapsed:false, + selectedItem: null, + selectedItemLayer: null, + selectedFeature: null, + queryState: newQueryState, + clearEnabled: false, + searchCollapsed: true, + searchMinified: false, + features: [], + query:initialState.query, + showLayerSwitcher: false, + extent: null + }); } case mapActions.SETSTYLE:{ let a = action as mapActions.SetStyle; @@ -471,10 +468,7 @@ export function reducer(state = initialState, action: mapActions.Actions | commo case mapActions.SETREPLACEURL: { let a= action as mapActions.SetReplaceUrl; return tassign(state,{replaceUrl:a.replaceUrl}); - } - case mapActions.CLEAR: { - return tassign(state,{setStateCount:0}); - } + } default: { return state; } @@ -504,9 +498,8 @@ export const getSelectedItemLayer = (state: State) => state.selectedItemLayer; export const getPeriod = (state:State) => state.period; 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,replaceUrl:state.replaceUrl};} +export const getState = (state:State) => {return {mapState:state.mapState,queryState:state.queryState,replaceUrl:state.replaceUrl};} export const selectMapState = createFeatureSelector(MODULE_NAME); export const selectGetMapState= createSelector(selectMapState, getMapState); @@ -532,7 +525,6 @@ export const selectGetSelectedItemLayer = createSelector(selectMapState, getSele export const selectGetPeriod = createSelector(selectMapState, getPeriod); 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);