Compare commits

..

9 Commits

Author SHA1 Message Date
Willem Dantuma
bb979e4322 Merge branch 'feature/streamline_flow' into develop
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-04-22 12:27:11 +02:00
Willem Dantuma
d57ffe59e7 Fix style 2020-04-22 12:16:03 +02:00
Willem Dantuma
4dfa2cd96c Fix query 2020-04-22 09:25:10 +02:00
Willem Dantuma
7eef50eb7a Remove uneeded delay 2020-04-22 08:59:04 +02:00
Willem Dantuma
69751540d3 More refactoring, add thematic maps button 2020-04-22 08:11:50 +02:00
Willem Dantuma
ef81b04f4e More refactoring 2020-04-21 14:54:13 +02:00
Willem Dantuma
e41c728fb2 Fix zoom to extent 2020-04-21 13:22:54 +02:00
Willem Dantuma
4e83bc6158 Some refactoring 2020-04-21 12:31:20 +02:00
Willem Dantuma
c6d7f6b0cb Add action logging and some refactoring 2020-04-17 13:26:50 +02:00
8 changed files with 265 additions and 160 deletions

View File

@ -42,6 +42,7 @@ export const DOQUERY = '[Map] DoQuery';
export const SETSTYLE = '[Map] SetStyle'; export const SETSTYLE = '[Map] SetStyle';
export const SHOWLAYERSWITCHER = '[Map] ShowLayerSwitcher'; export const SHOWLAYERSWITCHER = '[Map] ShowLayerSwitcher';
export const CLEAR = '[Map] Clear'; export const CLEAR = '[Map] Clear';
export const SETREPLACEURL = '[Map] SetReplaceUrl';
export class Clear implements Action { export class Clear implements Action {
readonly type = CLEAR; readonly type = CLEAR;
@ -87,7 +88,7 @@ export class StartSearch implements Action {
export class StartSearchSuccess implements Action { export class StartSearchSuccess implements Action {
readonly type = STARTSEARCHSUCCESS; readonly type = STARTSEARCHSUCCESS;
constructor(public features: Array<Feature>, public query:IQueryState) { } constructor(public features: Array<Feature>, public query:IQueryState,public setStateCount:number) { }
} }
export class SelectFeature implements Action { export class SelectFeature implements Action {
@ -165,7 +166,7 @@ export class SetExtent implements Action {
export class SetQueryState implements Action { export class SetQueryState implements Action {
readonly type = SETQUERYSTATE; readonly type = SETQUERYSTATE;
constructor(public queryState: IQueryState) { } constructor(public queryState: IQueryState,public replaceUrl:boolean = true) { }
} }
export class SetTimeSpan implements Action { export class SetTimeSpan implements Action {
@ -251,6 +252,11 @@ export class ShowLayerSwitcher implements Action {
constructor(public show:boolean) {} constructor(public show:boolean) {}
} }
export class SetReplaceUrl implements Action {
readonly type = SETREPLACEURL;
constructor(public replaceUrl:boolean) {}
}
export type Actions = SetMapState export type Actions = SetMapState
| Init | Init
| Clear | Clear
@ -285,5 +291,6 @@ export type Actions = SetMapState
| SetViewExtent | SetViewExtent
| DoQuery | DoQuery
| SetStyle | SetStyle
| ShowLayerSwitcher; | ShowLayerSwitcher
| SetReplaceUrl;

View File

@ -68,7 +68,7 @@ export class ItemVectorSourceComponent extends SourceVectorComponent implements
} else { } else {
evaluatedStyle = this.stylesCache["selected"]; evaluatedStyle = this.stylesCache["selected"];
} }
if(evaluatedStyle && evaluatedStyle.geometry_ == null) { if(evaluatedStyle ) {
evaluatedStyle.setGeometry((feature) => this.geometry(feature)); evaluatedStyle.setGeometry((feature) => this.geometry(feature));
} }
return evaluatedStyle return evaluatedStyle

View File

@ -28,7 +28,7 @@ export abstract class AbstractFeatureListComponent {
} }
getAction(feature:Feature):Action { getAction(feature:Feature):Action {
var newQuery: any = tassign(mapReducers.initialState.query); var newQuery: any = tassign(mapReducers.initialState.queryState);
newQuery.parentCode = feature.get('parentCode'); newQuery.parentCode = feature.get('parentCode');
newQuery.itemCode = feature.get('code'); newQuery.itemCode = feature.get('code');
newQuery.itemType = feature.get('itemType'); newQuery.itemType = feature.get('itemType');

View File

@ -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 { Location } from '@angular/common';
import { Observable, Subject, Subscription,combineLatest, from,interval } from 'rxjs'; import { Observable, Subject, Subscription, from,of } from 'rxjs';
import { debounce, withLatestFrom, first, combineAll,throttle } from 'rxjs/operators'; import { withLatestFrom, switchMap,skip } from 'rxjs/operators';
import { Router, ActivatedRoute, ParamMap, Event } from '@angular/router'; import { Router, ActivatedRoute, ParamMap } from '@angular/router';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
//import { proj,Map } from 'openlayers'; //import { proj,Map } from 'openlayers';
@ -16,7 +16,6 @@ import { IQueryState } from '@farmmaps/common';
import { IPeriodState } from '../../models/period.state'; import { IPeriodState } from '../../models/period.state';
import {IStyles} from '../../models/style.cache'; import {IStyles} from '../../models/style.cache';
import { IDroppedFile } from '../aol/file-drop-target/file-drop-target.component'; 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 { StateSerializerService } from '@farmmaps/common';
import { GeolocationService} from '../../services/geolocation.service'; import { GeolocationService} from '../../services/geolocation.service';
import {DeviceOrientationService} from '../../services/device-orientation.service'; import {DeviceOrientationService} from '../../services/device-orientation.service';
@ -30,7 +29,6 @@ import {commonActions} from '@farmmaps/common';
import {Feature} from 'ol'; import {Feature} from 'ol';
import {Extent,createEmpty,extend } from 'ol/extent'; import {Extent,createEmpty,extend } from 'ol/extent';
import {transform} from 'ol/proj'; import {transform} from 'ol/proj';
import { query } from '@angular/animations';
import { tassign } from 'tassign'; import { tassign } from 'tassign';
import * as style from 'ol/style'; import * as style from 'ol/style';
@ -57,8 +55,9 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
public droppedFile$: Subject<IDroppedFile> = new Subject<IDroppedFile>(); public droppedFile$: Subject<IDroppedFile> = new Subject<IDroppedFile>();
private paramSub: Subscription; private paramSub: Subscription;
private itemTypeSub: Subscription; private itemTypeSub: Subscription;
private mapStateSub: Subscription; private stateSub: Subscription;
private queryStateSub: Subscription; private queryStateSub: Subscription;
private querySub: Subscription;
public parentCode$: Observable<string> =this.store.select(mapReducers.selectGetParentCode); public parentCode$: Observable<string> =this.store.select(mapReducers.selectGetParentCode);
public panelVisible$: Observable<boolean> = this.store.select(mapReducers.selectGetPanelVisible); public panelVisible$: Observable<boolean> = this.store.select(mapReducers.selectGetPanelVisible);
public panelCollapsed$: Observable<boolean> = this.store.select(mapReducers.selectGetPanelCollapsed); public panelCollapsed$: Observable<boolean> = this.store.select(mapReducers.selectGetPanelCollapsed);
@ -66,6 +65,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
public clickedFeature: Subject<Feature> = new Subject<Feature>(); public clickedFeature: Subject<Feature> = new Subject<Feature>();
public selectedItem$: Observable<IItem> = this.store.select(mapReducers.selectGetSelectedItem); public selectedItem$: Observable<IItem> = this.store.select(mapReducers.selectGetSelectedItem);
public queryState$: Observable<IQueryState> = this.store.select(mapReducers.selectGetQueryState); public queryState$: Observable<IQueryState> = this.store.select(mapReducers.selectGetQueryState);
public state$:Observable<{mapState:IMapState,queryState:IQueryState,setStateCount:number}> = this.store.select(mapReducers.selectGetState);
public period$: Observable<IPeriodState> = this.store.select(mapReducers.selectGetPeriod); public period$: Observable<IPeriodState> = this.store.select(mapReducers.selectGetPeriod);
public clearEnabled$: Observable<boolean> = this.store.select(mapReducers.selectGetClearEnabled); public clearEnabled$: Observable<boolean> = this.store.select(mapReducers.selectGetClearEnabled);
public searchCollapsed$: Observable<boolean> = this.store.select(mapReducers.selectGetSearchCollapsed); public searchCollapsed$: Observable<boolean> = this.store.select(mapReducers.selectGetSearchCollapsed);
@ -79,6 +79,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
public extent$: Observable<Extent> = this.store.select(mapReducers.selectGetExtent); public extent$: Observable<Extent> = this.store.select(mapReducers.selectGetExtent);
public styles$:Observable<IStyles> = this.store.select(mapReducers.selectGetStyles); public styles$:Observable<IStyles> = this.store.select(mapReducers.selectGetStyles);
private setStateCount$:Observable<number> = this.store.select(mapReducers.selectgetSetStateCount); private setStateCount$:Observable<number> = this.store.select(mapReducers.selectgetSetStateCount);
private lastUrl = "";
@ViewChild('map') map; @ViewChild('map') map;
@ -92,7 +93,34 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
private geolocationService: GeolocationService, private geolocationService: GeolocationService,
private zone: NgZone, private zone: NgZone,
private deviceorientationService:DeviceOrientationService) { private deviceorientationService:DeviceOrientationService) {
this.querySub = this.query$.pipe(skip(1), withLatestFrom(this.mapState$),withLatestFrom(this.setStateCount$)).subscribe(([[queryState,mapState],setStateCount]) =>{
if(queryState) {
let newQueryState = tassign(mapReducers.initialQueryState);
console.debug(`Do Query ${setStateCount}`);
let urlparts=[];
if (queryState.itemCode && queryState.itemCode != "") {
if(queryState.itemType && queryState.itemType!= "") {
let itemType = this.itemTypeService.itemTypes[queryState.itemType];
if (itemType && itemType.viewer && itemType.viewer == "edit_in_editor" && itemType.editor) {
urlparts.push('/editor');
urlparts.push(itemType.editor);
urlparts.push('item');
urlparts.push(queryState.itemCode);
}
}
} else {
newQueryState= queryState;
}
if(urlparts.length==0 ) {
newQueryState.itemCode = queryState.itemCode;
this.zone.run(() => {
this.store.dispatch(new mapActions.SetQueryState(newQueryState,false));
})
} else {
this.router.navigate(urlparts);
}
}
});
} }
@HostListener('document:keyup', ['$event']) @HostListener('document:keyup', ['$event'])
@ -132,44 +160,6 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
this.store.dispatch(new mapActions.Clear()); this.store.dispatch(new mapActions.Clear());
this.selectedFeatures$.next({x:0,y:0,features:[]}); this.selectedFeatures$.next({x:0,y:0,features:[]});
this.selectedFeatures$.next(null); this.selectedFeatures$.next(null);
this.query$.pipe(withLatestFrom(this.mapState$),withLatestFrom(this.setStateCount$)).subscribe(([[queryState,mapState],setStateCount]) =>{
console.debug(`Do Query ${setStateCount}`);
if(setStateCount>1) {
let newQueryState = tassign(mapReducers.initialQueryState);
let urlparts=[];
if (queryState.itemCode && queryState.itemCode != "") {
if(queryState.itemType && queryState.itemType!= "") {
let itemType = this.itemTypeService.itemTypes[queryState.itemType];
if (itemType && itemType.viewer && itemType.viewer == "edit_in_editor" && itemType.editor) {
urlparts.push('/editor');
urlparts.push(itemType.editor);
urlparts.push('item');
urlparts.push(queryState.itemCode);
}
}
} else {
newQueryState= queryState;
}
if(urlparts.length==0 ) {
newQueryState.itemCode = queryState.itemCode;
this.replaceUrl(mapState,newQueryState,false);
} else {
this.router.navigate(urlparts);
}
}
});
this.mapState$.pipe(withLatestFrom(this.queryState$),withLatestFrom(this.setStateCount$)).subscribe(([[mapState,queryState],setStateCount]) =>{
console.debug(`Mapstate ${setStateCount}`);
if(setStateCount>0) {
this.replaceUrl(mapState,queryState,true);
}
});
this.queryState$.pipe(withLatestFrom(this.mapState$),withLatestFrom(this.setStateCount$)).subscribe(([[queryState,mapState],setStateCount]) =>{
console.debug(`Querystate ${setStateCount}`);
if(setStateCount>0) {
this.replaceUrl(mapState,queryState,true);
}
})
} }
initCustomStyles() { initCustomStyles() {
@ -191,45 +181,109 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
}))); })));
} }
ngAfterViewInit() { round(value:number,decimals:number):number {
console.debug("View init"); let d = Math.pow(10, decimals);
this.initCustomStyles(); return Math.round((value + Number.EPSILON)*d)/d;
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; getMapStateFromUrl(params:ParamMap):IMapState {
var newQueryState: IQueryState = lastQueryState;
var hasUrlmapState = params.has("xCenter") && params.has("yCenter"); var hasUrlmapState = params.has("xCenter") && params.has("yCenter");
var queryStateChanged = false;
if (hasUrlmapState) { if (hasUrlmapState) {
let xCenter = parseFloat(params.get("xCenter")); let xCenter = parseFloat(params.get("xCenter"));
let yCenter = parseFloat(params.get("yCenter")); let yCenter = parseFloat(params.get("yCenter"));
let zoom = parseFloat(params.get("zoom")); let zoom = parseFloat(params.get("zoom"));
let rotation = parseFloat(params.get("rotation")); let rotation = parseFloat(params.get("rotation"));
let baseLayer = params.get("baseLayer")?params.get("baseLayer"):""; let baseLayer = params.get("baseLayer")?params.get("baseLayer"):"";
newMapState = { xCenter: xCenter, yCenter: yCenter, zoom: zoom, rotation: rotation, baseLayerCode: baseLayer } var newMapState = {zoom: zoom, rotation: rotation, xCenter: xCenter, yCenter: yCenter, baseLayerCode: baseLayer };
window.localStorage.setItem("FarmMapsCommonMap_mapState",JSON.stringify(newMapState)); 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")) { if (params.has("queryState")) {
let queryState = params.get("queryState"); let queryState = params.get("queryState");
newQueryState = tassign(mapReducers.initialQueryState); var newQueryState = tassign(mapReducers.initialQueryState);
if (queryState != "") { if (queryState != "") {
newQueryState = this.serializeService.deserialize(queryState); newQueryState = this.serializeService.deserialize(queryState);
queryState = this.serializeService.serialize(newQueryState);
} }
queryStateChanged = this.serializeService.serialize(lastQueryState) != queryState; return newQueryState;
} else {
return null;
} }
let t =0; }
if(setStateCount==0) t=600;
setTimeout(() => { ngAfterViewInit() {
this.zone.run(()=> { console.debug("View init");
if (setStateCount ==0) { this.initCustomStyles();
this.store.dispatch(new mapActions.SetState(newMapState,newQueryState));
// 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 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) { } else if(queryStateChanged) {
this.store.dispatch(new mapActions.SetQueryState(newQueryState)); return of(new mapActions.SetQueryState(newQueryState));
} } return of(new mapActions.SetReplaceUrl(true));
}) })).subscribe((action) => {
},t); 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) {
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,newUrlState.replaceUrl);
}
});
setTimeout(() => { setTimeout(() => {
this.map.instance.updateSize(); this.map.instance.updateSize();
}, 500); }, 500);
@ -272,6 +326,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
if(mapState.baseLayerCode!="") { if(mapState.baseLayerCode!="") {
parts.push(mapState.baseLayerCode); parts.push(mapState.baseLayerCode);
parts.push( this.serializeService.serialize(queryState)); parts.push( this.serializeService.serialize(queryState));
console.debug("Replace url",parts);
this.router.navigate(parts, { replaceUrl: replace,relativeTo:this.route.parent }); this.router.navigate(parts, { replaceUrl: replace,relativeTo:this.route.parent });
} }
} }
@ -289,7 +344,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
let state = { mapState: mapState, extent: extent }; let state = { mapState: mapState, extent: extent };
let source = from([state]); let source = from([state]);
source.pipe(withLatestFrom(this.selectedBaseLayer$),withLatestFrom(this.setStateCount$)).subscribe(([[state, baselayer],setStateCount]) => { 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 }); let newMapState = tassign(state.mapState, { baseLayerCode: baselayer.item.code });
this.store.dispatch(new mapActions.SetMapState(newMapState)); this.store.dispatch(new mapActions.SetMapState(newMapState));
this.store.dispatch(new mapActions.SetViewExtent(state.extent)); this.store.dispatch(new mapActions.SetViewExtent(state.extent));
@ -299,6 +354,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
} }
handleOnMouseDown(event: MouseEvent) { handleOnMouseDown(event: MouseEvent) {
event.stopPropagation();
this.zone.run(() =>{ this.zone.run(() =>{
this.store.dispatch(new mapActions.CollapseSearch()); this.store.dispatch(new mapActions.CollapseSearch());
}); });
@ -341,8 +397,10 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
} }
ngOnDestroy() { ngOnDestroy() {
this.paramSub.unsubscribe(); if (this.paramSub) this.paramSub.unsubscribe();
if (this.itemTypeSub) this.itemTypeSub.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.queryStateSub) this.queryStateSub.unsubscribe();
if (this.querySub) this.querySub.unsubscribe();
}
} }

View File

@ -3,12 +3,12 @@ import { Injectable } from '@angular/core';
import { Store, Action } from '@ngrx/store'; import { Store, Action } from '@ngrx/store';
import { Effect, Actions,ofType } from '@ngrx/effects'; import { Effect, Actions,ofType } from '@ngrx/effects';
import { Observable , of } from 'rxjs'; import { Observable , of, interval } from 'rxjs';
import { withLatestFrom, switchMap, map, catchError, mergeMap } from 'rxjs/operators'; import { withLatestFrom, switchMap, map, catchError, mergeMap,delayWhen } from 'rxjs/operators';
import {GeoJSON,WKT} from 'ol/format'; import {GeoJSON,WKT} from 'ol/format';
import {Feature} from 'ol'; import {Feature} from 'ol';
import { getCenter } from 'ol/extent'; import { getCenter,createEmpty,extend } from 'ol/extent';
import {Point} from 'ol/geom' import {Point} from 'ol/geom'
@ -27,6 +27,7 @@ import {FeatureIconService} from '../services/feature-icon.service';
import * as style from 'ol/style'; import * as style from 'ol/style';
import { ItemTypeService } from '@farmmaps/common'; import { ItemTypeService } from '@farmmaps/common';
import { IQueryState } from 'dist/common/public-api';
@Injectable() @Injectable()
@ -112,34 +113,55 @@ export class MapEffects {
@Effect() @Effect()
startSearch$: Observable<Action> = this.actions$.pipe( startSearch$: Observable<Action> = this.actions$.pipe(
ofType(mapActions.STARTSEARCH), ofType(mapActions.STARTSEARCH),
switchMap((action: mapActions.StartSearch) => { withLatestFrom(this.store$.select(mapReducers.selectgetSetStateCount)),
var startDate = action.queryState.startDate; switchMap(([action,setStateCount]) => {
var endDate = action.queryState.endDate; let a = action as mapActions.StartSearch;
var startDate = a.queryState.startDate;
var endDate = a.queryState.endDate;
var newAction:Observable<Action>; var newAction:Observable<Action>;
if (action.queryState.itemCode || action.queryState.parentCode || action.queryState.itemType || action.queryState.query || action.queryState.tags) { if (a.queryState.itemCode || a.queryState.parentCode || a.queryState.itemType || a.queryState.query || a.queryState.tags) {
newAction= this.itemService$.getFeatures(action.queryState.bbox, "EPSG:3857", action.queryState.query, action.queryState.tags, startDate, endDate, action.queryState.itemType, action.queryState.parentCode).pipe( newAction= this.itemService$.getFeatures(a.queryState.bbox, "EPSG:3857", a.queryState.query, a.queryState.tags, startDate, endDate, a.queryState.itemType, a.queryState.parentCode).pipe(
switchMap((features: any) => { switchMap((features: any) => {
for (let f of features.features) { for (let f of features.features) {
if (f.properties && f.properties["code"]) { if (f.properties && f.properties["code"]) {
f.id = f.properties["code"]; f.id = f.properties["code"];
} }
} }
return of(new mapActions.StartSearchSuccess(this._geojsonFormat.readFeatures(features), action.queryState)); return of(new mapActions.StartSearchSuccess(this._geojsonFormat.readFeatures(features), a.queryState,setStateCount));
} }
), ),
catchError(error => of(new commonActions.Fail(error)))); catchError(error => of(new commonActions.Fail(error))));
} else { } else {
newAction= of(new commonActions.Escape(true,false)); return [];
} }
return newAction; return newAction;
})); }));
@Effect() @Effect()
startSearchSucces$: Observable<Action> = this.actions$.pipe( zoomToExtent$: Observable<Action> = 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));
let extent = createEmpty();
if (!action.query.bboxFilter) {
if (extent) {
for (let f of action.features) {
extend(extent, (f as Feature).getGeometry().getExtent());
}
}
actions.push(new mapActions.SetExtent(extent));
}
return actions;
}));
@Effect()
hideMenu$: Observable<Action> = this.actions$.pipe(
ofType(mapActions.STARTSEARCHSUCCESS), ofType(mapActions.STARTSEARCHSUCCESS),
mergeMap((action: mapActions.StartSearchSuccess) => { mergeMap((action: mapActions.StartSearchSuccess) => {
return [new commonActions.SetMenuVisible(false)]; return of(new commonActions.SetMenuVisible(false));
})); }));
@Effect() @Effect()
@ -198,21 +220,6 @@ export class MapEffects {
switchMap((action: commonActions.UploadedFileClick) => of(new mapActions.DoQuery(tassign(mapReducers.initialState.query, {itemCode:action.itemCode}))) switchMap((action: commonActions.UploadedFileClick) => of(new mapActions.DoQuery(tassign(mapReducers.initialState.query, {itemCode:action.itemCode})))
)); ));
//@Effect()
//itemAdded$: Observable<Action> = this.actions$.pipe(
// ofType(commonActions.ITEMADDEDEVENT),
// withLatestFrom(this.store$.select(mapReducers.selectGetParentCode)),
// mergeMap(([action, parentCode]) => {
// let itemAddedAction = action as commonActions.ItemAddedEvent;
// if (parentCode && itemAddedAction.attributes["parentCode"] == parentCode) {
// return this.itemService$.getFeature(itemAddedAction.itemCode,"EPSG:3857").pipe(
// map((feature: Feature) => new mapActions.AddFeatureSuccess(this.toPointFeature(feature))),
// catchError(error => of(new commonActions.Fail(error))))
// } else
// return [
// ];
// }));
@Effect() @Effect()
featureUpdate$: Observable<Action> = this.actions$.pipe( featureUpdate$: Observable<Action> = this.actions$.pipe(
ofType(commonActions.DEVICEUPDATEEVENT), ofType(commonActions.DEVICEUPDATEEVENT),
@ -248,30 +255,36 @@ export class MapEffects {
} }
})); }));
getActionFromQueryState(queryState:IQueryState, inSearch:boolean):Observable<Action>|[] {
if(!inSearch && (queryState.itemType || queryState.parentCode || queryState.itemCode || queryState.query || queryState.tags)) {
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 of(new commonActions.Escape(true,false));
}
}
@Effect() @Effect()
setQueryState$: Observable<Action> = this.actions$.pipe( setQueryState$: Observable<Action> = this.actions$.pipe(
ofType(mapActions.SETQUERYSTATE), ofType(mapActions.SETQUERYSTATE),
switchMap((action: mapActions.SetQueryState) => { withLatestFrom(this.store$.select(mapReducers.selectGetInSearch)),
var newAction:Action; switchMap(([action,inSearch]) => {
if (action.queryState.itemCode && action.queryState.itemCode != "") { let a = action as mapActions.SetQueryState;
newAction= new mapActions.SelectItem(action.queryState.itemCode); return this.getActionFromQueryState(a.queryState,inSearch);
} else {
newAction= new mapActions.StartSearch(action.queryState);
}
return of(newAction);
})); }));
@Effect() @Effect()
setState$: Observable<Action> = this.actions$.pipe( setState$: Observable<Action> = this.actions$.pipe(
ofType(mapActions.SETSTATE), ofType(mapActions.SETSTATE),
switchMap((action: mapActions.SetState) => { withLatestFrom(this.store$.select(mapReducers.selectGetInSearch)),
var newAction:Action; switchMap(([action,inSearch]) => {
if (action.queryState.itemCode && action.queryState.itemCode != "") { let a = action as mapActions.SetState;
newAction= new mapActions.SelectItem(action.queryState.itemCode); return this.getActionFromQueryState(a.queryState,inSearch);
} else {
newAction= new mapActions.StartSearch(action.queryState);
}
return of(newAction);
})); }));
constructor(private actions$: Actions, private store$: Store<mapReducers.State>, private folderService$: FolderService, private itemService$: ItemService,private featureIconService$:FeatureIconService,private itemTypeService$:ItemTypeService) { constructor(private actions$: Actions, private store$: Store<mapReducers.State>, private folderService$: FolderService, private itemService$: ItemService,private featureIconService$:FeatureIconService,private itemTypeService$:ItemTypeService) {

View File

@ -10,7 +10,7 @@ import {commonActions} from '@farmmaps/common';
import { createSelector, createFeatureSelector } from '@ngrx/store'; import { createSelector, createFeatureSelector } from '@ngrx/store';
import {Feature} from 'ol'; import {Feature} from 'ol';
import { createEmpty, extend} from 'ol/extent';
import { ROUTER_NAVIGATION, RouterNavigationAction } from '@ngrx/router-store'; import { ROUTER_NAVIGATION, RouterNavigationAction } from '@ngrx/router-store';
@ -56,7 +56,9 @@ export interface State {
selectedOverlayLayer: IItemLayer, selectedOverlayLayer: IItemLayer,
styles:IStyles, styles:IStyles,
showLayerSwitcher:boolean, showLayerSwitcher:boolean,
setStateCount:number setStateCount:number,
inSearch:boolean,
replaceUrl:boolean
} }
export const initialState: State = { export const initialState: State = {
@ -73,7 +75,7 @@ export const initialState: State = {
}, },
viewExtent:[], viewExtent:[],
queryState: tassign(initialQueryState), queryState: tassign(initialQueryState),
query: tassign(initialQueryState), query: null,
parentCode: null, parentCode: null,
features: [], features: [],
panelVisible: false, panelVisible: false,
@ -92,7 +94,9 @@ export const initialState: State = {
selectedItemLayer: null, selectedItemLayer: null,
styles: {}, styles: {},
showLayerSwitcher: false, showLayerSwitcher: false,
setStateCount: 0 setStateCount: 0,
inSearch:false,
replaceUrl:true
} }
export function reducer(state = initialState, action: mapActions.Actions | commonActions.Actions | RouterNavigationAction): State { export function reducer(state = initialState, action: mapActions.Actions | commonActions.Actions | RouterNavigationAction): State {
@ -109,7 +113,7 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
} }
case mapActions.SETQUERYSTATE: { case mapActions.SETQUERYSTATE: {
let a = action as mapActions.SetQueryState; let a = action as mapActions.SetQueryState;
return tassign(state, { queryState: tassign(a.queryState ),setStateCount: state.setStateCount+1}); return tassign(state, { queryState: tassign(a.queryState ),setStateCount: state.setStateCount+1,replaceUrl:a.replaceUrl});
} }
case mapActions.SETSTATE: { case mapActions.SETSTATE: {
let a = action as mapActions.SetState; let a = action as mapActions.SetState;
@ -127,18 +131,9 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
} }
case mapActions.STARTSEARCHSUCCESS: { case mapActions.STARTSEARCHSUCCESS: {
let a = action as mapActions.StartSearchSuccess; let a = action as mapActions.StartSearchSuccess;
let extent = state.extent;
if (!action.query.bboxFilter) {
extent = createEmpty();
if (extent) {
for (let f of action.features) {
extend(extent, (f as Feature).getGeometry().getExtent());
}
}
}
return tassign(state, { return tassign(state, {
features: a.features, features: a.features,
extent:extent inSearch:false
}); });
} }
case mapActions.SELECTFEATURE: { case mapActions.SELECTFEATURE: {
@ -148,10 +143,14 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
}); });
} }
case mapActions.SELECTITEM: { case mapActions.SELECTITEM: {
let a = action as mapActions.SelectItem;
let itemCode = state.selectedItem ? state.selectedItem.code : "";
let inSearch = (a.itemCode != itemCode || state.setStateCount == 1)
return tassign(state, { return tassign(state, {
selectedItem: null, selectedItem: null,
selectedItemLayer: null, selectedItemLayer: null,
features:[] features:[],
inSearch:inSearch
}); });
} }
case mapActions.SELECTITEMSUCCESS: { case mapActions.SELECTITEMSUCCESS: {
@ -164,6 +163,7 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
itemLayer = new TemporalItemLayer(a.item); itemLayer = new TemporalItemLayer(a.item);
} }
return tassign(state, { return tassign(state, {
inSearch:false,
selectedItem: a.item, selectedItem: a.item,
selectedItemLayer: itemLayer, selectedItemLayer: itemLayer,
panelVisible: a.item != null, panelVisible: a.item != null,
@ -255,21 +255,24 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
} }
case mapActions.STARTSEARCH: { case mapActions.STARTSEARCH: {
let a = action as mapActions.StartSearch; let a = action as mapActions.StartSearch;
let panelVisible = a.queryState.itemCode!=null ||a.queryState.itemType!=null||a.queryState.parentCode!=null || a.queryState.query != null || a.queryState.tags != null;
return tassign(state, { return tassign(state, {
selectedItem: null, selectedItem: null,
features:[], features:[],
selectedItemLayer:null, selectedItemLayer:null,
queryState: tassign(a.queryState), searchCollapsed: !panelVisible,
searchCollapsed: false, panelVisible: panelVisible,
panelVisible: true, clearEnabled: panelVisible,
clearEnabled: true, searchMinified: panelVisible,
searchMinified: true, inSearch:panelVisible
}); });
} }
case commonActions.FAIL:{
return tassign(state,{inSearch:false});
}
case mapActions.DOQUERY: { case mapActions.DOQUERY: {
let a = action as mapActions.DoQuery; let a = action as mapActions.DoQuery;
return tassign(state, { return tassign(state, {
setStateCount:state.setStateCount+1,
query: tassign(a.query, { bbox: a.query.bboxFilter ? state.viewExtent : [] })}); query: tassign(a.query, { bbox: a.query.bboxFilter ? state.viewExtent : [] })});
} }
case mapActions.ADDFEATURESUCCESS: { case mapActions.ADDFEATURESUCCESS: {
@ -455,8 +458,12 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
let a = action as mapActions.ShowLayerSwitcher; let a = action as mapActions.ShowLayerSwitcher;
return tassign(state,{showLayerSwitcher:a.show}); return tassign(state,{showLayerSwitcher:a.show});
} }
case mapActions.SETREPLACEURL: {
let a= action as mapActions.SetReplaceUrl;
return tassign(state,{replaceUrl:a.replaceUrl});
}
case mapActions.CLEAR: { case mapActions.CLEAR: {
return tassign(state,{setStateCount:0,features:[],selectedFeature:null,selectedItem:null}); return tassign(state,{setStateCount:0});
} }
default: { default: {
return state; return state;
@ -487,6 +494,8 @@ export const getPeriod = (state:State) => state.period;
export const getStyles = (state:State) => state.styles; export const getStyles = (state:State) => state.styles;
export const getShowLayerSwitcher = (state:State) => state.showLayerSwitcher; export const getShowLayerSwitcher = (state:State) => state.showLayerSwitcher;
export const getSetStateCount = (state:State) => state.setStateCount; 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 selectMapState = createFeatureSelector<State>(MODULE_NAME); export const selectMapState = createFeatureSelector<State>(MODULE_NAME);
export const selectGetMapState= createSelector(selectMapState, getMapState); export const selectGetMapState= createSelector(selectMapState, getMapState);
@ -512,5 +521,7 @@ export const selectGetPeriod = createSelector(selectMapState, getPeriod);
export const selectGetStyles = createSelector(selectMapState, getStyles); export const selectGetStyles = createSelector(selectMapState, getStyles);
export const selectGetShowLayerSwitcher = createSelector(selectMapState,getShowLayerSwitcher); export const selectGetShowLayerSwitcher = createSelector(selectMapState,getShowLayerSwitcher);
export const selectgetSetStateCount = createSelector(selectMapState,getSetStateCount); export const selectgetSetStateCount = createSelector(selectMapState,getSetStateCount);
export const selectGetInSearch = createSelector(selectMapState,getInSearch);
export const selectGetState = createSelector(selectMapState,getState);

View File

@ -11,7 +11,7 @@ import { AppCommonMapModule} from '@farmmaps/common-map';
import {AppRootComponent} from './app.component'; import {AppRootComponent} from './app.component';
import {StoreModule, Store} from '@ngrx/store'; import {StoreModule, ActionReducer,MetaReducer} from '@ngrx/store';
import {EffectsModule, EffectSources} from '@ngrx/effects'; import {EffectsModule, EffectSources} from '@ngrx/effects';
import { StoreRouterConnectingModule} from '@ngrx/router-store'; import { StoreRouterConnectingModule} from '@ngrx/router-store';
@ -48,6 +48,18 @@ export function provideBootstrapEffects(effects: Type<any>[]) {
]; ];
} }
// console.log all actions
export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
return function(state, action) {
console.debug('-- State', state);
console.debug('-- Action', action);
return reducer(state, action);
};
}
export const metaReducers: MetaReducer<any>[] = [debug];
@NgModule({ @NgModule({
declarations: [ declarations: [
AppRootComponent, AppRootComponent,
@ -61,7 +73,7 @@ export function provideBootstrapEffects(effects: Type<any>[]) {
AppCommonServiceModule.forRoot(), AppCommonServiceModule.forRoot(),
AppCommonMapModule.forRoot(), AppCommonMapModule.forRoot(),
BrowserModule, BrowserModule,
StoreModule.forRoot({},{runtimeChecks: { // TODO fix this should all be true StoreModule.forRoot({},{metaReducers,runtimeChecks: { // TODO fix this should all be true
strictStateImmutability: false, strictStateImmutability: false,
strictActionImmutability: false, strictActionImmutability: false,
strictStateSerializability: false, strictStateSerializability: false,

View File

@ -5,6 +5,10 @@
<div class="icon rounded-circle farm-icon"><i class="fm fm-farm" aria-hidden="true"></i></div> <div class="icon rounded-circle farm-icon"><i class="fm fm-farm" aria-hidden="true"></i></div>
<div class="caption" i18n>Farms</div> <div class="caption" i18n>Farms</div>
</div> </div>
<div class="shortcut-icon" (click)="handlePredefinedQuery($event,{itemType:'vnd.farmmaps.itemtype.layer'})">
<div class="icon rounded-circle thematic-maps-icon"><i class="fa fa-map" aria-hidden="true"></i></div>
<div class="caption" i18n>Thematic maps</div>
</div>
</div> </div>
</div> </div>
</div> </div>