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; public itemTypes$: Observable<{ [id: string]: IItemType }>; public mapState$: Observable; public features$: Observable>; public overlayLayers$: Observable>; public selectedOverlayLayer$: Observable; public selectedItemLayer$: Observable; public baseLayers$: Observable>; public selectedBaseLayer$: Observable; public projection$: Observable; public selectedFeatures$: Subject = new Subject(); public droppedFile$: Subject = new Subject(); private paramSub: Subscription; private itemTypeSub: Subscription; private mapStateSub: Subscription; private queryStateSub: Subscription; public parentCode$: Observable; public panelVisible$: Observable; public panelCollapsed$: Observable; public selectedFeature$: Observable; public selectedItem$: Observable; public queryState$: Observable; public period$: Observable; public clearEnabled$: Observable; public searchCollapsed$: Observable; public searchMinified$: Observable; public menuVisible$: Observable; public query$: Observable; public position$: Observable; public compassHeading$: Observable; public baseLayersCollapsed:boolean = true; public overlayLayersCollapsed: boolean = true; public extent$: Observable; @ViewChild('map', { static: false }) map; constructor(private store: Store, 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); queryState = this.serializeService.serialize(newQueryState); } queryStateChanged = this.lastQueryState != queryState; this.lastQueryState = queryState; } if (mapStateChanged && queryStateChanged) { console.debug("Both states"); this.store.dispatch(new mapActions.SetState(newMapState, newQueryState)); } else if (mapStateChanged) { console.debug("Map state"); this.store.dispatch(new mapActions.SetMapState(newMapState)); } else if (queryStateChanged) { console.debug("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(); } }