Merge branch 'develop' into feature/vectortileselect

This commit is contained in:
Willem Dantuma
2020-12-18 19:22:33 +01:00
27 changed files with 489 additions and 72 deletions

View File

@@ -16,6 +16,7 @@ export const STARTSEARCHSUCCESS = '[Map] StartSearchSuccess';
export const SELECTFEATURE = '[Map] SelectFeature';
export const SELECTITEM = '[Map] SelectItem';
export const SELECTITEMSUCCESS = '[Map] SelectItemSuccess';
export const SETSELECTEDITEMLAYER = '[Map] SetSelectedItemLayer';
export const SELECTTEMPORALITEMSSUCCESS = '[Map] SelectTemporalItemsSuccess';
export const NEXTTEMPORAL = '[Map] NextTemporal';
export const PREVIOUSTEMPORAL = '[Map] PreviousTemporal';
@@ -32,6 +33,7 @@ export const SETVISIBILITY = '[Map] SetVisibility';
export const SETOPACITY = '[Map] SetOpacity';
export const SETLAYERINDEX = '[Map] SetLayerIndex';
export const REMOVELAYER = '[Map] RemoveLayer';
export const CLEARLAYERS = '[Map] ClearLayers';
export const LOADBASELAYERS = '[Map] LoadLayers';
export const LOADBASELAYERSSUCCESS = '[Map] LoadLayersSuccess';
export const SELECTBASELAYER = '[Map] SelectBaseLayers';
@@ -42,6 +44,7 @@ export const SETSTYLE = '[Map] SetStyle';
export const SHOWLAYERSWITCHER = '[Map] ShowLayerSwitcher';
export const CLEAR = '[Map] Clear';
export const SETREPLACEURL = '[Map] SetReplaceUrl';
export const SETFEATURES = '[Map] SetFeatures'
export class Clear implements Action {
readonly type = CLEAR;
@@ -180,6 +183,12 @@ export class AddLayer implements Action {
constructor(public item:IItem,public layerIndex=-1) { }
}
export class SetSelectedItemLayer implements Action {
readonly type = SETSELECTEDITEMLAYER;
constructor(public item:IItem,public layerIndex=-1) { }
}
export class SetVisibility implements Action {
readonly type = SETVISIBILITY;
@@ -204,6 +213,12 @@ export class RemoveLayer implements Action {
constructor(public itemLayer: IItemLayer) { }
}
export class ClearLayers implements Action {
readonly type = CLEARLAYERS;
constructor() { }
}
export class LoadBaseLayers implements Action {
readonly type = LOADBASELAYERS;
@@ -256,6 +271,12 @@ export class SetReplaceUrl implements Action {
constructor(public replaceUrl:boolean) {}
}
export class SetFeatures implements Action {
readonly type = SETFEATURES;
constructor(public features: Array<Feature>) { }
}
export type Actions = SetMapState
| Init
| Clear
@@ -278,6 +299,7 @@ export type Actions = SetMapState
| SetTimeSpan
| AddLayer
| RemoveLayer
| ClearLayers
| SetVisibility
| SetOpacity
| SetLayerIndex
@@ -291,5 +313,7 @@ export type Actions = SetMapState
| DoQuery
| SetStyle
| ShowLayerSwitcher
| SetReplaceUrl;
| SetReplaceUrl
| SetFeatures
| SetSelectedItemLayer;

View File

@@ -8,6 +8,10 @@ const routes = [
path: '',
component: MapComponent
},
{
path: ':xCenter/:yCenter/:zoom/:rotation/:baseLayer',
component: MapComponent
},
{
path: ':xCenter/:yCenter/:zoom/:rotation/:baseLayer/:queryState',
component: MapComponent

View File

@@ -19,9 +19,10 @@
period:period$|async,
compassHeading:compassHeading$|async,
styles:styles$|async,
selectedFeature:selectedFeature$|async
selectedFeature:selectedFeature$|async,
fullscreen:fullscreen$|async
} as state">
<aol-map #map (moveEnd)="handleOnMoveEnd($event)" (click)="handleOnMouseDown($event)" [ngClass]="{'panel-visible':state.panelVisible}" class="map">
<aol-map #map (moveEnd)="handleOnMoveEnd($event)" (click)="handleOnMouseDown($event)" [ngClass]="{'panel-visible':state.panelVisible,'fullscreen':state.fullscreen}" class="map">
<div>
</div>
@@ -38,7 +39,7 @@
<fm-map-item-source-vector [styles]="state.styles" [features]="state.features" (onFeatureSelected)="handleFeatureClick($event)" (onFeatureHover)="handleFeatureHover($event)" [selectedFeature]="state.selectedFeature" [selectedItem]="state.selectedItem"></fm-map-item-source-vector>
</aol-layer-vector>
<!-- <fm-map-gps-location [position]="state.position" [headingTolerance]="20" [showHeading]="true" [heading]="state.compassHeading"></fm-map-gps-location> -->
<div class="control-container">
<div class="control-container" >
<router-outlet name="map-controls"></router-outlet>
<fm-map-layer-switcher></fm-map-layer-switcher>
<fm-map-pan-to-location [position]="state.position" [mapState]="state.mapState" [animate]="true"></fm-map-pan-to-location>
@@ -46,9 +47,11 @@
</div>
<fm-map-file-drop-target [parentCode]="state.parentCode" (onFileDropped)="handleFileDropped($event)"></fm-map-file-drop-target>
</aol-map>
<fm-map-map-search #mapSearch [openedModalName]="state.openedModalName" (onOpenModal)="handleOpenModal($event)" (onCloseModal)="handleCloseModal()" [ngClass]="{'menuVisible':state.menuVisible}" (onToggleMenu)="handleToggleMenu($event)" (onSearchCollapse)="handleSearchCollapse($event)" (onSearchExpand)="handleSearchExpand($event)" [collapsed]="state.searchCollapsed" [searchMinified]="state.searchMinified" (onSearch)="handleSearch($event)" (onClear)="handleClearSearch($event)" [filterOptions]="state.queryState" [clearEnabled]="state.clearEnabled" [period]="state.period"></fm-map-map-search>
<fm-side-panel [resizeable]="true" [visible]="state.panelVisible" [collapsed]="state.panelCollapsed" [collapsable]="false">
<div class="panel-wrapper">
<div *ngIf="noContent">
<fm-map-map-search #mapSearch [openedModalName]="state.openedModalName" (onOpenModal)="handleOpenModal($event)" (onCloseModal)="handleCloseModal()" [ngClass]="{'menuVisible':state.menuVisible}" (onToggleMenu)="handleToggleMenu($event)" (onSearchCollapse)="handleSearchCollapse($event)" (onSearchExpand)="handleSearchExpand($event)" [collapsed]="state.searchCollapsed" [searchMinified]="state.searchMinified" (onSearch)="handleSearch($event)" (onClear)="handleClearSearch($event)" [filterOptions]="state.queryState" [clearEnabled]="state.clearEnabled" [period]="state.period"></fm-map-map-search>
</div>
<fm-side-panel [resizeable]="true" [visible]="state.panelVisible && noContent" [collapsed]="state.panelCollapsed" [collapsable]="false">
<div class="panel-wrapper" *ngIf="noContent">
<div class="panel-top bg-secondary" *ngIf="!(state.searchMinified)">
</div>
<div class="panel-bottom">
@@ -65,7 +68,10 @@
<div *ngIf="state.queryState?.tags">Cannot find tag <span>{{state.queryState?.tags}}</span></div>
</div>
</div>
</div>
</div>
</fm-side-panel>
<fm-side-panel [resizeable]="true" [visible]="!noContent">
<router-outlet></router-outlet>
</fm-side-panel>
</ng-container>

View File

@@ -49,6 +49,10 @@ aol-map { position:absolute;width:100%;height:calc(100vh + 4rem);}
.control-container {
position: absolute;
right: 1em;
bottom: 8.1em;
}
.fullscreen .control-container {
bottom: 5em;
}

View File

@@ -1,4 +1,4 @@
import { Component, OnInit, OnDestroy, HostListener, ViewChild, AfterViewInit,NgZone } from '@angular/core';
import { Component, OnInit, OnDestroy, HostListener, ViewChild, AfterViewInit,NgZone,ElementRef } from '@angular/core';
import { Location } from '@angular/common';
import { Observable, Subject, Subscription, from,of ,EMPTY } from 'rxjs';
import { withLatestFrom, switchMap,skip } from 'rxjs/operators';
@@ -79,10 +79,13 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
public overlayLayersCollapsed: boolean = true;
public extent$: Observable<Extent> = this.store.select(mapReducers.selectGetExtent);
public styles$:Observable<IStyles> = this.store.select(mapReducers.selectGetStyles);
public fullscreen$: Observable<boolean> = this.store.select(commonReducers.selectGetFullScreen);
private lastUrl = "";
private initialized: boolean = false;
public noContent: boolean = false;
@ViewChild('map') map;
@ViewChild('contentDiv') contentDiv: ElementRef;
constructor(private store: Store<mapReducers.State | commonReducers.State>,
private route: ActivatedRoute,
@@ -162,7 +165,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
this.store.dispatch(new mapActions.DoQuery(queryState));
}
ngOnInit() {
ngOnInit() {
this.initialized = false;
console.debug("Init");
this.store.dispatch(new mapActions.Clear());
@@ -237,23 +240,27 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
ngAfterViewInit() {
console.debug("View init");
if(this.route.children.length == 0) {
this.noContent=true;
}
this.initCustomStyles();
// url to state
let urlMapState = this.getMapStateFromUrl(this.route.snapshot.paramMap);
let urlQueryState = this.getQueryStateFromUrl(this.route.snapshot.paramMap);
if(urlQueryState && urlMapState) {
if(urlQueryState && urlMapState && this.noContent) {
this.store.dispatch(new mapActions.SetState(urlMapState,urlQueryState));
window.localStorage.setItem("FarmMapsCommonMap_mapState",this.serializeMapState(urlMapState));
} else if(urlQueryState) {
} else if(urlQueryState && this.noContent) {
this.store.dispatch(new mapActions.SetQueryState(urlQueryState));
} else {
this.store.dispatch(new mapActions.SetReplaceUrl(true));
}
this.paramSub = this.route.paramMap.pipe(withLatestFrom(this.state$),switchMap(([params,state]) => {
if(this.initialized) {
if(this.initialized && this.noContent) {
let urlQueryState = this.getQueryStateFromUrl(params);
if( this.serializeService.serialize(state.queryState) != this.serializeService.serialize(urlQueryState)) {
return of(new mapActions.SetState(state.mapState,urlQueryState));
@@ -318,24 +325,25 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
}
replaceUrl(mapState: IMapState, queryState: IQueryState, replace: boolean = true) {
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 });
}
if(this.noContent) {
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) {

View File

@@ -150,6 +150,19 @@ export class MapEffects {
return actions;
}));
@Effect()
zoomToExtent2$: Observable<Action> = this.actions$.pipe(
ofType(mapActions.SETFEATURES),
map((action: mapActions.SetFeatures) => {
let extent = createEmpty();
if (extent) {
for (let f of action.features) {
extend(extent, (f as Feature).getGeometry().getExtent());
}
}
return new mapActions.SetExtent(extent);
}));
@Effect()
hideMenu$: Observable<Action> = this.actions$.pipe(
ofType(mapActions.STARTSEARCHSUCCESS),
@@ -181,6 +194,14 @@ export class MapEffects {
}
));
@Effect()
selectItemSuccessSetLayer$: Observable<Action> = this.actions$.pipe(
ofType(mapActions.SELECTITEMSUCCESS),
map((action:mapActions.SelectItemSuccess) =>
new mapActions.SetSelectedItemLayer(action.item)
)
);
@Effect()
selectItemSuccess$: Observable<Action> = this.actions$.pipe(
ofType(mapActions.SELECTITEMSUCCESS),

View File

@@ -138,6 +138,12 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
inSearch:false
});
}
case mapActions.SETFEATURES: {
let a = action as mapActions.SetFeatures;
return tassign(state, {
features: a.features
});
}
case mapActions.SELECTFEATURE: {
let a = action as mapActions.SelectFeature;
return tassign(state, {
@@ -157,18 +163,10 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
}
case mapActions.SELECTITEMSUCCESS: {
let a = action as mapActions.SelectItemSuccess;
var itemLayer = null;
if (a.item && "vnd.farmmaps.itemtype.layer,vnd.farmmaps.itemtype.shape.processed,vnd.farmmaps.itemtype.geotiff.processed".indexOf(a.item.itemType) >=0 ) {
itemLayer = new ItemLayer(a.item);
itemLayer.layerIndex = a.item.data.layers?a.item.data.layers[0].index:-1;
} else if (a.item && a.item.itemType == "vnd.farmmaps.itemtype.temporal") {
itemLayer = new TemporalItemLayer(a.item);
}
return tassign(state, {
inSearch:false,
selectedItem: a.item,
parentItem: a.parentItem,
selectedItemLayer: itemLayer,
panelVisible: a.item != null,
clearEnabled: a.item != null,
searchCollapsed: false,
@@ -176,6 +174,20 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
queryState: tassign(state.queryState, {itemCode:a.item ? a.item.code:null})
});
}
case mapActions.SETSELECTEDITEMLAYER: {
let a = action as mapActions.SetSelectedItemLayer;
var itemLayer = null;
if (a.item && "vnd.farmmaps.itemtype.layer,vnd.farmmaps.itemtype.shape.processed,vnd.farmmaps.itemtype.geotiff.processed".indexOf(a.item.itemType) >=0 ) {
itemLayer = new ItemLayer(a.item);
itemLayer.layerIndex = a.layerIndex>=0?a.layerIndex:a.item.data.layers?a.item.data.layers[0].index:-1;
} else if (a.item && a.item.itemType == "vnd.farmmaps.itemtype.temporal") {
itemLayer = new TemporalItemLayer(a.item);
}
return tassign(state, {
selectedItemLayer: itemLayer,
});
}
case mapActions.SELECTTEMPORALITEMSSUCCESS:{
let a = action as mapActions.SelectTemporalItemsSuccess;
let selectedItemLayer=tassign(state.selectedItemLayer) as TemporalItemLayer;
@@ -316,7 +328,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);
@@ -343,6 +355,9 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
newLayers.splice(i, 1);
return tassign(state, { overlayLayers: newLayers, selectedOverlayLayer: selectedOverlayLayer });
}
case mapActions.CLEARLAYERS: {
return tassign(state, {overlayLayers: [], selectedOverlayLayer: null});
}
case mapActions.SETVISIBILITY: {
let a = action as mapActions.SetVisibility;
let newLayers = state.overlayLayers.slice(0);
@@ -436,7 +451,7 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
let a = action as mapActions.SelectOverlayLayer;
return tassign(state, { selectedOverlayLayer: a.itemLayer });
}
case mapActions.CLEAR: {
let newQueryState = tassign(state.queryState, { query: null, tags: null, itemCode: null, parentCode: null, itemType: null });
return tassign(state, {
@@ -468,7 +483,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});
}
}
default: {
return state;
}