324 lines
14 KiB
TypeScript
324 lines
14 KiB
TypeScript
import { Component, OnInit, OnDestroy, HostListener, Inject, ViewChild, AfterViewInit,ChangeDetectorRef,NgZone } from '@angular/core';
|
|
import { Location } from '@angular/common';
|
|
import { Observable, Subject, Subscription,combineLatest, from } from 'rxjs';
|
|
import { debounce, withLatestFrom, first, combineAll } from 'rxjs/operators';
|
|
import { Router, ActivatedRoute, ParamMap, Event } from '@angular/router';
|
|
import { Store } from '@ngrx/store';
|
|
//import { proj,Map } from 'openlayers';
|
|
|
|
// Map
|
|
import * as mapReducers from '../../reducers/map.reducer';
|
|
import * as mapActions from '../../actions/map.actions';
|
|
import { IMapState} from '../../models/map.state';
|
|
import { ISelectedFeatures } from '../../models/selected.features';
|
|
import { IItemLayer } from '../../models/item.layer';
|
|
import { IQueryState } from '../../models/query.state';
|
|
import { IPeriodState } from '../../models/period.state';
|
|
import { IDroppedFile } from '../aol/file-drop-target/file-drop-target.component';
|
|
import { IMetaData } from '../meta-data-modal/meta-data-modal.component';
|
|
import { StateSerializerService } from '../../services/state-serializer.service';
|
|
import { GeolocationService} from '../../services/geolocation.service';
|
|
import {DeviceOrientationService} from '../../services/device-orientation.service';
|
|
|
|
// AppCommon
|
|
import { ResumableFileUploadService, ItemTypeService } from '@farmmaps/common';
|
|
import { IItemType, IItem } from '@farmmaps/common';
|
|
import {commonReducers} from '@farmmaps/common';
|
|
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';
|
|
|
|
|
|
@Component({
|
|
selector: 'fm-map-map',
|
|
templateUrl: './map.component.html',
|
|
styleUrls: ['./map.component.scss']
|
|
})
|
|
|
|
export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
|
|
title: string = 'Map';
|
|
public openedModalName$: Observable<string>;
|
|
public itemTypes$: Observable<{ [id: string]: IItemType }>;
|
|
public mapState$: Observable<IMapState>;
|
|
public features$: Observable<Array<Feature>>;
|
|
public overlayLayers$: Observable<Array<IItemLayer>>;
|
|
public selectedOverlayLayer$: Observable<IItemLayer>;
|
|
public selectedItemLayer$: Observable<IItemLayer>;
|
|
public baseLayers$: Observable<Array<IItemLayer>>;
|
|
public selectedBaseLayer$: Observable<IItemLayer>;
|
|
public projection$: Observable<string>;
|
|
public selectedFeatures$: Subject<ISelectedFeatures> = new Subject<ISelectedFeatures>();
|
|
public droppedFile$: Subject<IDroppedFile> = new Subject<IDroppedFile>();
|
|
private paramSub: Subscription;
|
|
private itemTypeSub: Subscription;
|
|
private mapStateSub: Subscription;
|
|
private queryStateSub: Subscription;
|
|
public parentCode$: Observable<string>;
|
|
public panelVisible$: Observable<boolean>;
|
|
public panelCollapsed$: Observable<boolean>;
|
|
public selectedFeature$: Observable<Feature>;
|
|
public selectedItem$: Observable<IItem>;
|
|
public queryState$: Observable<IQueryState>;
|
|
public period$: Observable<IPeriodState>;
|
|
public clearEnabled$: Observable<boolean>;
|
|
public searchCollapsed$: Observable<boolean>;
|
|
public searchMinified$: Observable<boolean>;
|
|
public menuVisible$: Observable<boolean>;
|
|
public query$: Observable<IQueryState>;
|
|
public position$: Observable<Position>;
|
|
public compassHeading$: Observable<number>;
|
|
public baseLayersCollapsed:boolean = true;
|
|
public overlayLayersCollapsed: boolean = true;
|
|
public extent$: Observable<Extent>;
|
|
@ViewChild('map', { static: false }) map;
|
|
|
|
constructor(private store: Store<mapReducers.State | commonReducers.State>,
|
|
private route: ActivatedRoute,
|
|
private router: Router,
|
|
private uploadService: ResumableFileUploadService,
|
|
private serializeService: StateSerializerService,
|
|
public itemTypeService: ItemTypeService,
|
|
private location: Location,
|
|
private geolocationService: GeolocationService,
|
|
private zone: NgZone,
|
|
private deviceorientationService:DeviceOrientationService) {
|
|
}
|
|
|
|
@HostListener('document:keyup', ['$event'])
|
|
escapeClose(event: KeyboardEvent) {
|
|
let x = event.keyCode;
|
|
if (x === 27) {
|
|
this.handleCloseModal()
|
|
}
|
|
}
|
|
|
|
handleOpenModal(modalName: string) {
|
|
this.store.dispatch(new commonActions.OpenModal(modalName));
|
|
}
|
|
|
|
handleCloseModal() {
|
|
this.store.dispatch(new commonActions.CloseModal());
|
|
}
|
|
|
|
handleFileDropped(droppedFile: IDroppedFile) {
|
|
this.uploadService.addFiles(droppedFile.files, droppedFile.event, { parentCode:droppedFile.parentCode, geometry:droppedFile.geometry });
|
|
}
|
|
|
|
handleFeaturesSelected(feature: Feature) {
|
|
if (feature) {
|
|
let newQuery = tassign(mapReducers.initialQueryState, { itemCode: feature.get('code') });
|
|
this.store.dispatch(new mapActions.DoQuery(newQuery));
|
|
}
|
|
}
|
|
|
|
handleSearch(queryState: IQueryState) {
|
|
this.store.dispatch(new mapActions.DoQuery(queryState));
|
|
}
|
|
|
|
ngOnInit() {
|
|
this.store.dispatch(new mapActions.Init());
|
|
this.selectedFeatures$.next({x:0,y:0,features:[]});
|
|
this.mapState$ = this.store.select(mapReducers.selectGetMapState);
|
|
this.parentCode$ = this.store.select(mapReducers.selectGetParentCode);
|
|
this.features$ = this.store.select(mapReducers.selectGetFeatures);
|
|
this.overlayLayers$ = this.store.select(mapReducers.selectGetOverlayLayers);
|
|
this.selectedOverlayLayer$ = this.store.select(mapReducers.selectGetSelectedOverlayLayer);
|
|
this.baseLayers$ = this.store.select(mapReducers.selectGetBaseLayers);
|
|
this.projection$ = this.store.select(mapReducers.selectGetProjection);
|
|
this.selectedBaseLayer$ = this.store.select(mapReducers.selectGetSelectedBaseLayer);
|
|
this.panelVisible$ = this.store.select(mapReducers.selectGetPanelVisible);
|
|
this.panelCollapsed$ = this.store.select(mapReducers.selectGetPanelCollapsed);
|
|
this.selectedFeature$ = this.store.select(mapReducers.selectGetSelectedFeature);
|
|
this.selectedItem$ = this.store.select(mapReducers.selectGetSelectedItem);
|
|
this.queryState$ = this.store.select(mapReducers.selectGetQueryState);
|
|
this.clearEnabled$ = this.store.select(mapReducers.selectGetClearEnabled);
|
|
this.searchCollapsed$ = this.store.select(mapReducers.selectGetSearchCollapsed);
|
|
this.searchMinified$ = this.store.select(mapReducers.selectGetSearchMinified);
|
|
this.openedModalName$ = this.store.select(commonReducers.selectOpenedModalName);
|
|
this.query$ = this.store.select(mapReducers.selectGetQuery);
|
|
this.extent$ = this.store.select(mapReducers.selectGetExtent);
|
|
this.selectedFeatures$.next(null);
|
|
this.selectedItemLayer$ = this.store.select(mapReducers.selectGetSelectedItemLayer);
|
|
this.period$ = this.store.select(mapReducers.selectGetPeriod);
|
|
this.position$ = this.geolocationService.getCurrentPosition();
|
|
this.compassHeading$ = this.deviceorientationService.getCurrentCompassHeading();
|
|
|
|
this.mapState$.pipe(withLatestFrom(this.queryState$)).subscribe((state) => {
|
|
this.replaceUrl(state[0], state[1], true);
|
|
});
|
|
this.query$.pipe(withLatestFrom(this.mapState$)).subscribe((state) => {
|
|
this.replaceUrl(state[1], state[0],false);
|
|
});
|
|
}
|
|
|
|
private stateSetCount: number = 0;
|
|
private lastQueryState: string = this.serializeService.serialize(mapReducers.initialQueryState);
|
|
private lastMapState: string = "";
|
|
ngAfterViewInit() {
|
|
this.paramSub = this.route.paramMap.subscribe((params: ParamMap) => {
|
|
//console.log("Param sub");
|
|
var newMapState: IMapState = null;
|
|
var newQueryState: IQueryState = null;
|
|
var mapStateChanged = false;
|
|
var queryStateChanged = false;
|
|
if (params.has("xCenter") && params.has("yCenter")) {
|
|
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 }
|
|
mapStateChanged = this.lastMapState != JSON.stringify(newMapState) && this.stateSetCount == 0;
|
|
this.lastMapState = JSON.stringify(newMapState);
|
|
window.localStorage.setItem("FarmMapsCommonMap_mapState",this.lastMapState);
|
|
}
|
|
if (params.has("queryState")) {
|
|
let queryState = params.get("queryState");
|
|
newQueryState = tassign(mapReducers.initialQueryState);
|
|
if (queryState != "") {
|
|
newQueryState = this.serializeService.deserialize(queryState);
|
|
}
|
|
queryStateChanged = this.lastQueryState != queryState;
|
|
this.lastQueryState = queryState;
|
|
}
|
|
|
|
if (mapStateChanged && queryStateChanged) {
|
|
//console.log("Both states");
|
|
this.store.dispatch(new mapActions.SetState(newMapState, newQueryState));
|
|
} else if (mapStateChanged) {
|
|
//console.log("Map state");
|
|
this.store.dispatch(new mapActions.SetMapState(newMapState));
|
|
} else if (queryStateChanged) {
|
|
//console.log("Query state");
|
|
this.store.dispatch(new mapActions.SetQueryState(newQueryState));
|
|
}
|
|
this.stateSetCount += 1;
|
|
});
|
|
setTimeout(() => {
|
|
this.map.instance.updateSize();
|
|
}, 500);
|
|
}
|
|
|
|
handleSearchCollapse(event) {
|
|
this.store.dispatch(new mapActions.CollapseSearch());
|
|
}
|
|
|
|
handleSearchExpand(event) {
|
|
this.store.dispatch(new mapActions.ExpandSearch());
|
|
}
|
|
|
|
handleToggleMenu(event) {
|
|
this.store.dispatch(new commonActions.ToggleMenu());
|
|
}
|
|
|
|
handleToggleBaseLayers(event:MouseEvent) {
|
|
this.baseLayersCollapsed = !this.baseLayersCollapsed;
|
|
event.preventDefault();
|
|
}
|
|
|
|
handleToggleOverlayLayers(event: MouseEvent) {
|
|
this.overlayLayersCollapsed = !this.overlayLayersCollapsed;
|
|
event.preventDefault();
|
|
}
|
|
|
|
handlePredefinedQuery(event: MouseEvent, query: any) {
|
|
event.preventDefault();
|
|
var queryState = tassign(mapReducers.initialQueryState, query);
|
|
this.store.dispatch(new mapActions.DoQuery(queryState));
|
|
}
|
|
|
|
handleTrijntjeClick(event: MouseEvent, query: any) {
|
|
event.preventDefault();
|
|
var queryState = tassign(mapReducers.initialQueryState, query);
|
|
var mapState = JSON.parse(this.lastMapState);
|
|
this.router.navigate(["app","trijntje" , mapState.xCenter.toFixed(5), mapState.yCenter.toFixed(5), mapState.zoom, mapState.rotation.toFixed(2), mapState.baseLayerCode, this.serializeService.serialize(queryState)], { replaceUrl: false });
|
|
}
|
|
|
|
replaceUrl(mapState: IMapState, queryState: IQueryState, replace: boolean = true) {
|
|
console.debug(`Replace url : Baselayer(${mapState.baseLayerCode}) Querystate(${this.serializeService.serialize(queryState)})`);
|
|
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!="") {
|
|
parts.push(mapState.baseLayerCode);
|
|
parts.push( this.serializeService.serialize(queryState));
|
|
this.router.navigate(parts, { replaceUrl: replace,relativeTo:this.route.parent });
|
|
}
|
|
}
|
|
|
|
handleOnMoveEnd(event) {
|
|
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.queryState$)).subscribe(([[state, baselayer], queryState]) => {
|
|
if (mapState && baselayer && queryState) {
|
|
let newMapState = tassign(state.mapState, { baseLayerCode: baselayer.item.code });
|
|
this.replaceUrl(newMapState, tassign(queryState, { bbox: queryState.bboxFilter ? state.extent : queryState.bbox }));
|
|
this.store.dispatch(new mapActions.SetViewExtent(state.extent));
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
handleOnMouseDown(event: MouseEvent) {
|
|
this.zone.run(() =>{
|
|
this.store.dispatch(new mapActions.CollapseSearch());
|
|
});
|
|
}
|
|
|
|
handleOnDownload(event) {
|
|
|
|
}
|
|
|
|
handleClearSearch(event) {
|
|
this.store.dispatch(new commonActions.Escape(true, false));
|
|
}
|
|
|
|
handleOnDelete(itemLayer: IItemLayer) {
|
|
this.store.dispatch(new mapActions.RemoveLayer(itemLayer));
|
|
}
|
|
|
|
handleOnToggleVisibility(itemLayer: IItemLayer) {
|
|
this.store.dispatch(new mapActions.SetVisibility(itemLayer,!itemLayer.visible));
|
|
}
|
|
|
|
handleOnSetOpacity(event:{ layer: IItemLayer,opacity:number }) {
|
|
this.store.dispatch(new mapActions.SetOpacity(event.layer, event.opacity));
|
|
}
|
|
|
|
handleZoomToExtent(itemLayer: IItemLayer) {
|
|
var extent = createEmpty();
|
|
extend(extent, itemLayer.layer.getExtent());
|
|
if (extent) {
|
|
this.store.dispatch(new mapActions.SetExtent(extent));
|
|
}
|
|
}
|
|
|
|
handleSelectBaseLayer(itemLayer: IItemLayer) {
|
|
this.store.dispatch(new mapActions.SelectBaseLayer(itemLayer));
|
|
}
|
|
|
|
handleSelectOverlayLayer(itemLayer: IItemLayer) {
|
|
this.store.dispatch(new mapActions.SelectOverlayLayer(itemLayer));
|
|
}
|
|
|
|
ngOnDestroy() {
|
|
this.paramSub.unsubscribe();
|
|
if (this.itemTypeSub) this.itemTypeSub.unsubscribe();
|
|
if (this.mapStateSub) this.mapStateSub.unsubscribe();
|
|
if (this.queryStateSub) this.queryStateSub.unsubscribe(); }
|
|
}
|