import { Injectable } from '@angular/core'; import { Store, Action } from '@ngrx/store'; import { Effect, Actions,ofType } from '@ngrx/effects'; 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'; import { getCenter,createEmpty,extend } from 'ol/extent'; import {Point} from 'ol/geom' import * as mapActions from '../actions/map.actions'; import * as mapReducers from '../reducers/map.reducer'; import {commonReducers} from '@farmmaps/common'; import {commonActions} from '@farmmaps/common'; import { IItem } from '@farmmaps/common'; import { FolderService, ItemService } from '@farmmaps/common'; import { tassign } from 'tassign'; import {FeatureIconService} from '../services/feature-icon.service'; import * as style from 'ol/style'; import { ItemTypeService,IQueryState } from '@farmmaps/common'; @Injectable() export class MapEffects { private _geojsonFormat: GeoJSON; private _wktFormat: WKT; private toPointFeature(updateEvent:commonActions.DeviceUpdateEvent): Feature { var f = this._wktFormat.readFeature(updateEvent.attributes["geometry"],{ dataProjection: 'EPSG:4326', featureProjection: 'EPSG:3857' }); f.setId(updateEvent.itemCode); var centroid = getCenter(f.getGeometry().getExtent()); f.setGeometry(new Point(centroid)); return f; } @Effect() init$: Observable = this.actions$.pipe( ofType(mapActions.INIT), withLatestFrom(this.store$.select(commonReducers.selectGetRootItems)), switchMap(([action, rootItems]) => { let actions=[]; for (let rootItem of rootItems) { if (rootItem.itemType == "UPLOADS_FOLDER") actions.push(new mapActions.SetParent(rootItem.code)); } // initialize default feature styles actions.push(new mapActions.SetStyle('file',new style.Style({ image: new style.Icon({ anchor: [0.5, 1], scale: 0.05, src: this.featureIconService$.getIconImageDataUrl("fa fa-file-o") }), stroke: new style.Stroke({ color: 'red', width: 1 }), fill: new style.Fill({ color: 'rgba(0, 0, 255, 0.1)' }) }))); actions.push(new mapActions.SetStyle('selected',new style.Style({ image: new style.Icon({ anchor: [0.5, 1], scale: 0.08, src: this.featureIconService$.getIconImageDataUrl(null) }), stroke: new style.Stroke({ color: 'red', width: 3 }), fill: new style.Fill({ color: 'rgba(0, 0, 255, 0.1)' }) }))); return actions; } )); @Effect() initBaseLayers$: Observable = this.actions$.pipe( ofType(mapActions.INIT), withLatestFrom(this.store$.select(mapReducers.selectGetProjection)), map(([action, projection]) => new mapActions.LoadBaseLayers(projection))); @Effect() loadBaseLayers$: Observable = this.actions$.pipe( ofType(mapActions.LOADBASELAYERS), switchMap((action: mapActions.LoadBaseLayers) => { return this.itemService$.getItemList("vnd.farmmaps.itemtype.layer", { "isBaseLayer": true }).pipe( map((items: IItem[]) => new mapActions.LoadBaseLayersSuccess(items)), catchError(error => of(new commonActions.Fail(error)))); })); @Effect() startSearch$: Observable = this.actions$.pipe( ofType(mapActions.STARTSEARCH), switchMap((action) => { let a = action as mapActions.StartSearch; var startDate = a.queryState.startDate; var endDate = a.queryState.endDate; var newAction:Observable; if (a.queryState.itemCode || a.queryState.parentCode || a.queryState.itemType || a.queryState.query || a.queryState.tags) { 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) => { for (let f of features.features) { if (f.properties && f.properties["code"]) { f.id = f.properties["code"]; } } return of(new mapActions.StartSearchSuccess(this._geojsonFormat.readFeatures(features), a.queryState)); } ), catchError(error => of(new commonActions.Fail(error)))); } else { return []; } return newAction; })); @Effect() zoomToExtent$: Observable = this.actions$.pipe( ofType(mapActions.STARTSEARCHSUCCESS), 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()); } if(action.features && action.features.length >0) { actions.push(new mapActions.SetExtent(extent)); } } } return actions; })); @Effect() zoomToExtent2$: Observable = 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 = this.actions$.pipe( ofType(mapActions.STARTSEARCHSUCCESS), mergeMap((action: mapActions.StartSearchSuccess) => { return of(new commonActions.SetMenuVisible(false)); })); @Effect() selectItem$: Observable = this.actions$.pipe( ofType(mapActions.SELECTITEM), withLatestFrom(this.store$.select(mapReducers.selectGetSelectedItem)), switchMap(([action, selectedItem]) => { let a = action as mapActions.SelectItem; let itemCode = selectedItem ? selectedItem.code : ""; if (a.itemCode != itemCode) { return this.itemService$.getItem(a.itemCode).pipe( switchMap(child => { return this.itemService$.getItem(child.parentCode) .pipe(map(parent => { return {child, parent}; }),catchError(() => { let parent:IItem = null;return of({child,parent})}) ); }), map(data => new mapActions.SelectItemSuccess(data.child, data.parent)), catchError(error => of(new commonActions.Fail(error)))) } else { return []; } } )); @Effect() selectItemSuccessSetLayer$: Observable = this.actions$.pipe( ofType(mapActions.SELECTITEMSUCCESS), map((action:mapActions.SelectItemSuccess) => new mapActions.SetSelectedItemLayer(action.item) ) ); @Effect() selectItemSuccess$: Observable = this.actions$.pipe( ofType(mapActions.SELECTITEMSUCCESS), switchMap((action:mapActions.SelectItemSuccess) => { return this.itemService$.getFeature(action.item.code, "EPSG:3857").pipe( map((feature: any) => { let f = this._geojsonFormat.readFeature(feature); f.setId(action.item.code); return new mapActions.AddFeatureSuccess(f ); }), catchError(error => of(new commonActions.Fail(error)))); } )); @Effect() selectItemSuccessTemporal$: Observable = this.actions$.pipe( ofType(mapActions.SELECTITEMSUCCESS), switchMap((action:mapActions.SelectItemSuccess) => { if(action.item.itemType == "vnd.farmmaps.itemtype.temporal") { return this.itemService$.getChildItemList(action.item.code,null).pipe( map(items => new mapActions.SelectTemporalItemsSuccess( items.sort((a, b) => -(Date.parse(b.dataDate) - Date.parse(a.dataDate)) ) )), catchError(error => of(new commonActions.Fail(error)))); } else { return []; } } )); @Effect() uploadedItemClick$: Observable = this.actions$.pipe( ofType(commonActions.UPLOADEDFILECLICK), switchMap((action: commonActions.UploadedFileClick) => of(new mapActions.DoQuery(tassign(mapReducers.initialState.query, {itemCode:action.itemCode}))) )); @Effect() featureUpdate$: Observable = this.actions$.pipe( ofType(commonActions.DEVICEUPDATEEVENT), withLatestFrom(this.store$.select(mapReducers.selectGetFeatures)), mergeMap(([action, features]) => { let deviceUpdateEventAction = action as commonActions.DeviceUpdateEvent; var feature: Feature = null; for (let f of features) { if (f.getId() == deviceUpdateEventAction.itemCode) { feature = f; break; } } if (feature) { return of(new mapActions.UpdateFeatureSuccess(this.toPointFeature(deviceUpdateEventAction))); } else { return []; } })); @Effect() itemUpdate$: Observable = this.actions$.pipe( ofType(commonActions.ITEMCHANGEDEVENT), withLatestFrom(this.store$.select(mapReducers.selectGetSelectedItem)), mergeMap(([action, selectedItem]) => { let itemChangedAction = action as commonActions.ItemChangedEvent; if (selectedItem && selectedItem.code == itemChangedAction.itemCode) { return this.itemService$.getItem(itemChangedAction.itemCode).pipe( switchMap(child => { return this.itemService$.getItem(child.parentCode) .pipe(map(parent => { return {child, parent}; }) ); }), map(data => new mapActions.SelectItemSuccess(data.child, data.parent)), catchError(error => of(new commonActions.Fail(error)))); } else { return []; } })); getActionFromQueryState(queryState:IQueryState, inSearch:boolean):Observable|[] { 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); } } else { newAction = new mapActions.Clear(); } return of(newAction); } @Effect() setState$: Observable = this.actions$.pipe( ofType(mapActions.SETSTATE), withLatestFrom(this.store$.select(mapReducers.selectGetInSearch)), switchMap(([action,inSearch]) => { let a = action as mapActions.SetState; 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(); } }