diff --git a/projects/common-map/src/fm-map/actions/map.actions.ts b/projects/common-map/src/fm-map/actions/map.actions.ts index cb87f3c..e492005 100644 --- a/projects/common-map/src/fm-map/actions/map.actions.ts +++ b/projects/common-map/src/fm-map/actions/map.actions.ts @@ -5,6 +5,7 @@ import { IItemLayer } from '../models/item.layer'; import { IQueryState } from '@farmmaps/common'; import { IItem } from '@farmmaps/common'; import { Feature,Style } from 'ol'; +import { IListItem } from 'dist/common/public-api'; export const SETSTATE = '[Map] SetState'; export const SETMAPSTATE = '[Map] MapState'; @@ -16,6 +17,10 @@ export const STARTSEARCHSUCCESS = '[Map] StartSearchSuccess'; export const SELECTFEATURE = '[Map] SelectFeature'; export const SELECTITEM = '[Map] SelectItem'; export const SELECTITEMSUCCESS = '[Map] SelectItemSuccess'; +export const SELECTTEMPORALITEMSSUCCESS = '[Map] SelectTemporalItemsSuccess'; +export const NEXTTEMPORAL = '[Map] NextTemporal'; +export const PREVIOUSTEMPORAL = '[Map] PreviousTemporal'; +export const SELECTTEMPORAL = '[Map] SelectTemporal'; export const ADDFEATURESUCCESS = '[Map] AddFeatureSuccess'; export const UPDATEFEATURESUCCESS = '[Map] UpdateFeatureSuccess'; export const EXPANDSEARCH = '[Map] ExpandSearch'; @@ -97,6 +102,30 @@ export class SelectItemSuccess implements Action { constructor(public item: IItem) { } } +export class SelectTemporalItemsSuccess implements Action { + readonly type = SELECTTEMPORALITEMSSUCCESS; + + constructor(public temporalItems: IItem[]) { } +} + +export class NextTemporal implements Action { + readonly type = NEXTTEMPORAL; + + constructor() { } +} + +export class PreviousTemporal implements Action { + readonly type = PREVIOUSTEMPORAL; + + constructor() { } +} + +export class SelectTemporal implements Action { + readonly type = SELECTTEMPORAL; + + constructor(item:IItem) { } +} + export class AddFeatureSuccess implements Action { readonly type = ADDFEATURESUCCESS; @@ -224,6 +253,10 @@ export type Actions = SetMapState | SelectFeature | SelectItem | SelectItemSuccess + | SelectTemporalItemsSuccess + | NextTemporal + | PreviousTemporal + | SelectTemporal | AddFeatureSuccess | UpdateFeatureSuccess | ExpandSearch diff --git a/projects/common-map/src/fm-map/common-map.module.ts b/projects/common-map/src/fm-map/common-map.module.ts index aa84e50..2f51d1b 100644 --- a/projects/common-map/src/fm-map/common-map.module.ts +++ b/projects/common-map/src/fm-map/common-map.module.ts @@ -34,6 +34,7 @@ import { AbstractItemListComponent,ItemListComponent} from './components/item-li import { AbstractSelectedItemComponent, SelectedItemComponent } from './components/selected-item/selected-item.component'; import { SelectedItemCropfieldComponent } from './components/selected-item-cropfield/selected-item-cropfield.component'; import { SelectedItemGeotiffComponent } from './components/selected-item-geotiff/selected-item-geotiff.component'; +import { SelectedItemTemporalComponent} from './components/selected-item-temporal/selected-item-temporal.component'; import {SelectedItemShapeComponent } from './components/selected-item-shape/selected-item-shape.component'; import { SelectedItemContainerComponent } from './components/selected-item-container/selected-item-container.component'; import { AbstractFeatureListFeatureComponent, FeatureListFeatureComponent } from './components/feature-list-feature/feature-list-feature.component'; @@ -116,6 +117,7 @@ export { SelectedItemComponent, SelectedItemCropfieldComponent, SelectedItemGeotiffComponent, + SelectedItemTemporalComponent, SelectedItemShapeComponent, ItemListItemComponent, ItemListItemContainerComponent, @@ -182,6 +184,7 @@ export { SelectedItemComponent, SelectedItemCropfieldComponent, SelectedItemGeotiffComponent, + SelectedItemTemporalComponent, SelectedItemShapeComponent, ItemListItemComponent, ItemListItemContainerComponent, @@ -202,6 +205,7 @@ export { SelectedItemComponent, SelectedItemCropfieldComponent, SelectedItemGeotiffComponent, + SelectedItemTemporalComponent, SelectedItemShapeComponent, ItemListComponent, ItemListItemComponent, @@ -222,6 +226,7 @@ export { SelectedItemComponent, SelectedItemCropfieldComponent, SelectedItemGeotiffComponent, + SelectedItemTemporalComponent, SelectedItemShapeComponent, ItemListItemComponent, ItemListItemContainerComponent, @@ -261,6 +266,7 @@ export class AppCommonMapModule { { provide: AbstractSelectedItemComponent, useClass: SelectedItemComponent, multi: true }, { provide: AbstractSelectedItemComponent, useClass: SelectedItemCropfieldComponent, multi: true }, { provide: AbstractSelectedItemComponent, useClass: SelectedItemGeotiffComponent, multi: true }, + { provide: AbstractSelectedItemComponent, useClass: SelectedItemTemporalComponent, multi: true }, { provide: AbstractSelectedItemComponent, useClass: SelectedItemShapeComponent, multi: true }, { provide: AbstractItemListItemComponent, useClass: ItemListItemComponent, multi: true }, { provide: AbstractItemListComponent, useClass: ItemListComponent, multi: true } diff --git a/projects/common-map/src/fm-map/components/aol/item-layers/item-layers.component.ts b/projects/common-map/src/fm-map/components/aol/item-layers/item-layers.component.ts index 2d4f9d2..7fa7117 100644 --- a/projects/common-map/src/fm-map/components/aol/item-layers/item-layers.component.ts +++ b/projects/common-map/src/fm-map/components/aol/item-layers/item-layers.component.ts @@ -3,7 +3,7 @@ import { HttpClient } from "@angular/common/http"; import { LayerVectorComponent, LayerTileComponent, LayerGroupComponent, MapComponent } from 'ngx-openlayers'; import { ItemService } from '@farmmaps/common'; import { AppConfig } from '@farmmaps/common'; -import { IItemLayer} from '../../../models/item.layer'; +import { IItemLayer,ItemLayer, ITemporalItemLayer} from '../../../models/item.layer'; import { ILayerData} from '../../../models/layer.data'; import { IRenderoutputTiles,IRenderoutputImage,IGradientstop,ILayer,IHistogram} from '../../../models/color.map'; import {Extent} from 'ol/extent'; @@ -19,6 +19,7 @@ import VectorTileSource from 'ol/source/VectorTile'; import VectorTileLayer from 'ol/layer/VectorTile'; import {GeoJSON,MVT} from 'ol/format'; import { from } from 'rxjs'; +import { IItem } from 'dist/common/public-api'; @Component({ selector: 'fm-map-item-layers', @@ -86,83 +87,95 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange }); } - createLayer(itemLayer: IItemLayer): Layer { - var layer: Layer = null; + createGeotiffLayer(item:IItem,itemLayer:IItemLayer):Layer { var layerIndex = -1; - if (itemLayer.item.itemType == 'vnd.farmmaps.itemtype.geotiff.processed') { - layerIndex = itemLayer.layerIndex != -1 ? itemLayer.layerIndex : itemLayer.item.data.layers[0].index; - let source = new XYZ({ maxZoom: 19, minZoom: 1, url: `${this._apiEndPoint}/api/v1/items/${itemLayer.item.code}/tiles/${layerIndex}/{z}/{x}/{y}.png?v=${itemLayer.item.updated.getTime()}` }); + var layer: Layer = null; + layerIndex = itemLayer.layerIndex != -1 ? itemLayer.layerIndex : item.data.layers[0].index; + let source = new XYZ({ maxZoom: 19, minZoom: 1, url: `${this._apiEndPoint}/api/v1/items/${item.code}/tiles/${layerIndex}/{z}/{x}/{y}.png?v=${item.updated.getTime()}` }); + layer = new Tile({ source: source }); + var data = item.data; + var l = (data && data.layers && data.layers.length > 0) ? data.layers[0] : null; + if (l && l.rendering && l.rendering.renderoutputType == "Tiles") { + var rt = l.rendering as IRenderoutputTiles; + let source = new XYZ({ maxZoom: rt.maxzoom, minZoom: rt.minzoom, url: `${this._apiEndPoint}/api/v1/items/${item.code}/tiles/${layerIndex}/{z}/{x}/{y}.png?v=${item.updated.getTime()}` }); layer = new Tile({ source: source }); - var data = itemLayer.item.data; - var l = (data && data.layers && data.layers.length > 0) ? data.layers[0] : null; - if (l && l.rendering && l.rendering.renderoutputType == "Tiles") { - var rt = l.rendering as IRenderoutputTiles; - let source = new XYZ({ maxZoom: rt.maxzoom, minZoom: rt.minzoom, url: `${this._apiEndPoint}/api/v1/items/${itemLayer.item.code}/tiles/${layerIndex}/{z}/{x}/{y}.png?v=${itemLayer.item.updated.getTime()}` }); - layer = new Tile({ source: source }); - } - if (l && l.rendering && l.rendering.renderoutputType == "Image") { - var ri = l.rendering as IRenderoutputImage; - let projection = new Projection({ - code: 'image', - units: 'pixels', - extent: ri.extent - }); - let source = new ImageStatic({ imageExtent: ri.extent, projection: projection, url: `${this._apiEndPoint}/api/v1/items/${itemLayer.item.code}/mapimage/${layerIndex}?v=${itemLayer.item.updated.getTime()}` }); - layer = new Image({ source: source }); - } - } else if (itemLayer.item.itemType == 'vnd.farmmaps.itemtype.shape.processed') { - var data = itemLayer.item.data; - layerIndex = itemLayer.layerIndex != -1 ? itemLayer.layerIndex : itemLayer.item.data.layers[0].index; - var l = itemLayer.item.data.layers[layerIndex]; - if (l && l.rendering && l.rendering.renderoutputType == "VectorTiles") { - var rt = itemLayer.item.data.layers[layerIndex].rendering as IRenderoutputTiles; - layer = new VectorTileLayer({ - declutter: true, - source: new VectorTileSource({ - maxZoom: rt.maxzoom, - minZoom: rt.minzoom, - format: new MVT(), - url: `${this._apiEndPoint}/api/v1/items/${itemLayer.item.code}/vectortiles/{z}/{x}/{y}.pbf?v=${itemLayer.item.updated.getTime()}` - }), - style: (feature) => { - return this.getColorFromGradient(l, feature); - } + } + if (l && l.rendering && l.rendering.renderoutputType == "Image") { + var ri = l.rendering as IRenderoutputImage; + let projection = new Projection({ + code: 'image', + units: 'pixels', + extent: ri.extent + }); + let source = new ImageStatic({ imageExtent: ri.extent, projection: projection, url: `${this._apiEndPoint}/api/v1/items/${item.code}/mapimage/${layerIndex}?v=${item.updated.getTime()}` }); + layer = new Image({ source: source }); + } + return layer; + } + + createShapeLayer(item:IItem,itemLayer:IItemLayer):Layer { + var layerIndex = -1; + var layer: Layer = null; + layerIndex = itemLayer.layerIndex != -1 ? itemLayer.layerIndex : item.data.layers[0].index; + var data = item.data; + var l = (data && data.layers && data.layers.length > 0) ? data.layers[0] : null; + if (l && l.rendering && l.rendering.renderoutputType == "VectorTiles") { + var rt = item.data.layers[layerIndex].rendering as IRenderoutputTiles; + layer = new VectorTileLayer({ + declutter: true, + source: new VectorTileSource({ + maxZoom: rt.maxzoom, + minZoom: rt.minzoom, + format: new MVT(), + url: `${this._apiEndPoint}/api/v1/items/${item.code}/vectortiles/{z}/{x}/{y}.pbf?v=${item.updated.getTime()}` + }), + style: (feature) => { + return this.getColorFromGradient(l, feature); + } + }) + } else if (l && l.rendering && l.rendering.renderoutputType == "Tiles") { + var rt = l.rendering as IRenderoutputTiles; + layer = new Tile({ + source: new XYZ({ + maxZoom: rt.maxzoom, + minZoom: rt.minzoom, + url: `${this._apiEndPoint}/api/v1/items/${item.code}/vectortiles/image_tiles/${layerIndex}/{z}/{x}/{y}.png?v=${item.updated.getTime()}` }) - } else if (l && l.rendering && l.rendering.renderoutputType == "Tiles") { - var rt = l.rendering as IRenderoutputTiles; - layer = new Tile({ - source: new XYZ({ - maxZoom: rt.maxzoom, - minZoom: rt.minzoom, - url: `${this._apiEndPoint}/api/v1/items/${itemLayer.item.code}/vectortiles/image_tiles/${layerIndex}/{z}/{x}/{y}.png?v=${itemLayer.item.updated.getTime()}` - }) - }); - } else { - let __this = this; - let format = new GeoJSON(); - let source = new VectorSource({ - strategy: loadingstrategy.bbox, - loader: function (extent: Extent, resolution: number, projection: Projection) { - var source = this as VectorSource; - __this.itemService.getItemFeatures(itemLayer.item.code, extent, projection.getCode(), layerIndex).subscribe(function (data) { - var features = format.readFeatures(data); - for (let f of features) { - if (f.get("code")) { - f.setId(f.get("code")); - } + }); + } else { + let __this = this; + let format = new GeoJSON(); + let source = new VectorSource({ + strategy: loadingstrategy.bbox, + loader: function (extent: Extent, resolution: number, projection: Projection) { + var source = this as VectorSource; + __this.itemService.getItemFeatures(item.code, extent, projection.getCode(), layerIndex).subscribe(function (data) { + var features = format.readFeatures(data); + for (let f of features) { + if (f.get("code")) { + f.setId(f.get("code")); } - source.addFeatures(features); - }); - } - }); - layer = new VectorLayer({ - source: source, - style: (feature) => { - var key = feature.get("color"); - if (!this.styleCache[key]) { - var color = feature.get("color"); - this.styleCache[key] = new style.Style( - { + } + source.addFeatures(features); + }); + } + }); + layer = new VectorLayer({ + source: source, + style: (feature) => { + var key = feature.get("color"); + if (!this.styleCache[key]) { + var color = feature.get("color"); + this.styleCache[key] = new style.Style( + { + fill: new style.Fill({ + color: color + }), + stroke: new style.Stroke({ + color: color, + width: 1.25 + }), + image: new style.Circle({ fill: new style.Fill({ color: color }), @@ -170,50 +183,58 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange color: color, width: 1.25 }), - image: new style.Circle({ - fill: new style.Fill({ - color: color - }), - stroke: new style.Stroke({ - color: color, - width: 1.25 - }), - radius: 5 - }), - } - ) - } - return this.styleCache[key]; + radius: 5 + }), + } + ) } - }); + return this.styleCache[key]; + } + }); + } + return layer; + } + + createExternalLayer(item:IItem,itemLayer:IItemLayer):Layer { + let data = item.data as ILayerData; + var layer: Layer = null; + switch (data.interfaceType) { + case 'OSM': { + let source = new OSM(); + layer = new Tile({ source: source }); + break; } + case 'BingMaps': { + let source = new BingMaps(data.options); + layer = new Tile({ source: source }); + break; + } + case 'TileWMS': { + let source = new TileWMS(data.options); + layer = new Tile({ source: source }); + break; + } + case 'TileArcGISRest': { + let source = new TileArcGISRest(data.options); + layer = new Tile({ source: source }); + break; + } + default: { + break; + } + } + return layer; + } + + createLayer(itemLayer: IItemLayer): Layer { + var layer: Layer = null; + var layerIndex = -1; + if (itemLayer.item.itemType == 'vnd.farmmaps.itemtype.geotiff.processed') { + layer = this.createGeotiffLayer(itemLayer.item,itemLayer); + } else if (itemLayer.item.itemType == 'vnd.farmmaps.itemtype.shape.processed') { + layer = this.createShapeLayer(itemLayer.item,itemLayer); } else if (itemLayer.item.itemType == 'vnd.farmmaps.itemtype.layer') { - let data = itemLayer.item.data as ILayerData; - switch (data.interfaceType) { - case 'OSM': { - let source = new OSM(); - layer = new Tile({ source: source }); - break; - } - case 'BingMaps': { - let source = new BingMaps(data.options); - layer = new Tile({ source: source }); - break; - } - case 'TileWMS': { - let source = new TileWMS(data.options); - layer = new Tile({ source: source }); - break; - } - case 'TileArcGISRest': { - let source = new TileArcGISRest(data.options); - layer = new Tile({ source: source }); - break; - } - default: { - break; - } - } + layer = this.createExternalLayer(itemLayer.item,itemLayer); } if (layer) { let geometry = new GeoJSON().readGeometry(itemLayer.item.geometry); @@ -229,33 +250,51 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange this.updateLayers(this.itemLayers); } - updateLayers(itemLayers: IItemLayer[]) { - if (itemLayers) { - var olLayers = this.instance.getLayers(); - itemLayers.forEach((itemLayer, index) => { + addOrUpdateOlLayer(itemLayer:IItemLayer,index:number):Layer { + if(!itemLayer) return null; + var olLayers = this.instance.getLayers(); + var layer = itemLayer.layer; + let olIndex = olLayers.getArray().indexOf(layer); + if (olIndex < 0) { + // New layer: we add it to the map + layer = this.createLayer(itemLayer); + if (layer) { + olLayers.insertAt(index, layer); + } + } else if (index !== olIndex) { + // layer has moved inside the layers list + olLayers.removeAt(olIndex); + olLayers.insertAt(index, layer); + } + if(layer) { + itemLayer.layer = layer; + layer.setOpacity(itemLayer.opacity); + layer.setVisible(itemLayer.visible); + } + return layer; + } - var layer = itemLayer.layer; - let olIndex = olLayers.getArray().indexOf(layer); - if (olIndex < 0) { - // New layer: we add it to the map - layer = this.createLayer(itemLayer); - if (layer) { - itemLayer.layer = layer; - } - olLayers.insertAt(index, layer); - } else if (index !== olIndex) { - // layer has moved inside the layers list - olLayers.removeAt(olIndex); - olLayers.insertAt(index, layer); + updateLayers(itemLayers: IItemLayer[]) { + let newLayers: IItemLayer[] = []; + if (itemLayers) { + itemLayers.forEach((itemLayer, index) => { + if(itemLayer.item.itemType == 'vnd.farmmaps.itemtype.temporal') { + let il = itemLayer as ITemporalItemLayer; + let previousLayer = this.addOrUpdateOlLayer(il.previousItemLayer,newLayers.length); + if(previousLayer) newLayers.push(previousLayer); + let selectedLayer = this.addOrUpdateOlLayer(il.selectedItemLayer,newLayers.length); + if(selectedLayer) newLayers.push(selectedLayer); + let nextLayer = this.addOrUpdateOlLayer(il.nextItemLayer,newLayers.length); + if(nextLayer) newLayers.push(nextLayer); + } else { + let layer = this.addOrUpdateOlLayer(itemLayer,newLayers.length); + if(layer) newLayers.push(layer); } - layer.setOpacity(itemLayer.opacity); - layer.setVisible(itemLayer.visible); }); // Remove the layers that have disapeared from childrenLayers - if (olLayers.getLength() > itemLayers.length) { - for (let i = itemLayers.length; i < olLayers.getLength(); i++) { - olLayers.removeAt(i); - } + var olLayers = this.instance.getLayers(); + while(olLayers.getLength() > newLayers.length) { + olLayers.removeAt(newLayers.length); } } } diff --git a/projects/common-map/src/fm-map/components/map/map.component.html b/projects/common-map/src/fm-map/components/map/map.component.html index f6ed7ac..9567fe2 100644 --- a/projects/common-map/src/fm-map/components/map/map.component.html +++ b/projects/common-map/src/fm-map/components/map/map.component.html @@ -57,7 +57,7 @@
- +
Cannot find {{state.queryState?.query}}
diff --git a/projects/common-map/src/fm-map/components/selected-item-container/selected-item-container.component.ts b/projects/common-map/src/fm-map/components/selected-item-container/selected-item-container.component.ts index 5681c68..9e7ebef 100644 --- a/projects/common-map/src/fm-map/components/selected-item-container/selected-item-container.component.ts +++ b/projects/common-map/src/fm-map/components/selected-item-container/selected-item-container.component.ts @@ -2,6 +2,7 @@ import { Component, Input, OnInit, ComponentFactoryResolver, ViewChild, SimpleCh import { IItem } from '@farmmaps/common'; import { AbstractSelectedItemComponent, SelectedItemComponent } from '../selected-item/selected-item.component'; import { WidgetHostDirective } from '../widget-host/widget-host.directive'; +import { IItemLayer } from '../../models/item.layer'; @Component({ @@ -15,6 +16,7 @@ export class SelectedItemContainerComponent { } @Input() item: IItem; + @Input() itemLayer:IItemLayer @ViewChild(WidgetHostDirective, { static: true }) widgetHost: WidgetHostDirective; @@ -41,11 +43,15 @@ export class SelectedItemContainerComponent { const componentRef = viewContainerRef.createComponent(componentFactory); (componentRef.instance).item = this.item; + (componentRef.instance).itemLayer = this.itemLayer; } ngOnChanges(changes: SimpleChanges) { if (changes["item"] && changes["item"].currentValue) { this.loadComponent(); } + if (changes["itemLayer"] && changes["itemLayer"].currentValue) { + this.loadComponent(); + } } } diff --git a/projects/common-map/src/fm-map/components/selected-item-temporal/selected-item-temporal.component.html b/projects/common-map/src/fm-map/components/selected-item-temporal/selected-item-temporal.component.html new file mode 100644 index 0000000..b718c92 --- /dev/null +++ b/projects/common-map/src/fm-map/components/selected-item-temporal/selected-item-temporal.component.html @@ -0,0 +1,30 @@ +
+
+
+
+ + +
+ +
+ +
+
+
diff --git a/projects/common-map/src/fm-map/components/selected-item-temporal/selected-item-temporal.component.scss b/projects/common-map/src/fm-map/components/selected-item-temporal/selected-item-temporal.component.scss new file mode 100644 index 0000000..0dc571f --- /dev/null +++ b/projects/common-map/src/fm-map/components/selected-item-temporal/selected-item-temporal.component.scss @@ -0,0 +1,41 @@ +@import "~bootstrap/scss/bootstrap.scss"; + +.big-icon { + width: 100%; + color: white; + font-size: 9rem; + padding: 3rem; + text-align: center; +} + +.card-title { + font-size: 1rem; +} + +ul { + list-style:none; +} + +li { + margin-top:1rem; +} + +.spacer { + display:none; + height:4rem; +} + +@media screen and (min-width:44rem) { + .spacer { + display:block; + } +} + +.menu-card { + margin-left: -7px; + padding-left: 7px; + margin-right: -7px; + padding-right: 7px; + margin-bottom: 7px; +} + diff --git a/projects/common-map/src/fm-map/components/selected-item-temporal/selected-item-temporal.component.ts b/projects/common-map/src/fm-map/components/selected-item-temporal/selected-item-temporal.component.ts new file mode 100644 index 0000000..b5917bc --- /dev/null +++ b/projects/common-map/src/fm-map/components/selected-item-temporal/selected-item-temporal.component.ts @@ -0,0 +1,73 @@ +import { Component, Injectable } from '@angular/core'; +import { Location } from '@angular/common'; +import { Store } from '@ngrx/store'; +import * as mapReducers from '../../reducers/map.reducer'; +import { commonReducers, ItemTypeService, ItemService, FolderService,IItem } from '@farmmaps/common'; +import { Router } from '@angular/router'; +import { ForItemType } from '../for-item/for-itemtype.decorator'; +import { AbstractSelectedItemComponent } from '../selected-item/selected-item.component'; +import { ITemporalItemLayer} from '../../models/item.layer'; +import * as mapActions from '../../actions/map.actions'; + + +@ForItemType("vnd.farmmaps.itemtype.temporal") +@Injectable() +@Component({ + selector: 'fm-map-selected-item-temporal', + templateUrl: './selected-item-temporal.component.html', + styleUrls: ['./selected-item-temporal.component.scss'] +}) +export class SelectedItemTemporalComponent extends AbstractSelectedItemComponent { + + constructor(store: Store, itemTypeService: ItemTypeService, location: Location, router: Router, private itemService$: ItemService,private folderService$: FolderService) { + super(store, itemTypeService,location,router); + } + public selectedLayer: number = 0; + + onLayerChanged(layerIndex: number) { + this.selectedLayer = layerIndex; + this.store.dispatch(new mapActions.SetLayerIndex(layerIndex)); + } + + hasNext():boolean { + let temporalItemLayer = this.itemLayer as ITemporalItemLayer; + return temporalItemLayer && temporalItemLayer.nextItemLayer != null; + } + + nextDate():Date { + let temporalItemLayer = this.itemLayer as ITemporalItemLayer; + if(temporalItemLayer.nextItemLayer.item) + return temporalItemLayer.nextItemLayer.item.dataDate; + return null; + } + + hasPrevious():boolean { + let temporalItemLayer = this.itemLayer as ITemporalItemLayer; + return temporalItemLayer && temporalItemLayer.previousItemLayer != null; + } + + previousDate():Date { + let temporalItemLayer = this.itemLayer as ITemporalItemLayer; + if(temporalItemLayer.previousItemLayer.item) + return temporalItemLayer.previousItemLayer.item.dataDate; + return null; + } + + handleNextTemporal(event:MouseEvent) { + this.store.dispatch(new mapActions.NextTemporal()); + event.preventDefault(); + } + + handlePreviousTemporal(event:MouseEvent) { + this.store.dispatch(new mapActions.PreviousTemporal()); + event.preventDefault(); + } + + selectedItem():IItem { + let temporalItemLayer = this.itemLayer as ITemporalItemLayer; + if(temporalItemLayer && temporalItemLayer.selectedItemLayer) { + return temporalItemLayer.selectedItemLayer.item; + } + return null; + } +} diff --git a/projects/common-map/src/fm-map/components/selected-item/selected-item.component.ts b/projects/common-map/src/fm-map/components/selected-item/selected-item.component.ts index 2fee740..9d05620 100644 --- a/projects/common-map/src/fm-map/components/selected-item/selected-item.component.ts +++ b/projects/common-map/src/fm-map/components/selected-item/selected-item.component.ts @@ -5,11 +5,13 @@ import * as mapReducers from '../../reducers/map.reducer'; import {AppConfig, commonReducers, IItem, ItemTypeService} from '@farmmaps/common'; import * as mapActions from '../../actions/map.actions'; import {Router} from '@angular/router'; +import { IItemLayer } from '../../models/item.layer'; @Injectable() export abstract class AbstractSelectedItemComponent { @Input() item: IItem + @Input() itemLayer: IItemLayer constructor(public store: Store, public itemTypeService: ItemTypeService, private location: Location, private router: Router) { } diff --git a/projects/common-map/src/fm-map/effects/map.effects.ts b/projects/common-map/src/fm-map/effects/map.effects.ts index 64c20ab..e06a16a 100644 --- a/projects/common-map/src/fm-map/effects/map.effects.ts +++ b/projects/common-map/src/fm-map/effects/map.effects.ts @@ -172,6 +172,24 @@ export class MapEffects { } )); + @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) => + -(b.dataDate.getTime() - a.dataDate.getTime()) + ) + )), + catchError(error => of(new commonActions.Fail(error)))); + } else { + return []; + } + } + )); + @Effect() uploadedItemClick$: Observable = this.actions$.pipe( ofType(commonActions.UPLOADEDFILECLICK), diff --git a/projects/common-map/src/fm-map/models/item.layer.ts b/projects/common-map/src/fm-map/models/item.layer.ts index 7ddf00a..29a5b04 100644 --- a/projects/common-map/src/fm-map/models/item.layer.ts +++ b/projects/common-map/src/fm-map/models/item.layer.ts @@ -1,26 +1,49 @@ -import { IItem } from '@farmmaps/common'; -import {Layer} from 'ol/layer'; - -export interface IItemLayer { - item: IItem, - layer: Layer, - visible: boolean, - legendVisible:boolean, - projection: string, - opacity: number, - layerIndex:number -} - -export class ItemLayer implements IItemLayer { - public item: IItem; - public layer: Layer = null; - public visible: boolean = true; - public legendVisible: boolean = false; - public projection: string; - public opacity: number = 1; - public layerIndex: number = -1; - - constructor(item:IItem) { - this.item = item; - } -} +import { IItem,IListItem } from '@farmmaps/common'; +import {Layer} from 'ol/layer'; + +export interface IItemLayer { + item: IItem, + layer: Layer, + visible: boolean, + legendVisible:boolean, + projection: string, + opacity: number, + layerIndex:number +} + +export class ItemLayer implements IItemLayer { + public item: IItem; + public layer: Layer = null; + public visible: boolean = true; + public legendVisible: boolean = false; + public projection: string; + public opacity: number = 1; + public layerIndex: number = -1; + + constructor(item:IItem,opacity:number = 1, visible:boolean = true) { + this.item = item; + this.opacity = opacity; + this.visible = visible; + } +} + +export interface ITemporalItemLayer extends IItemLayer { + previousItemLayer: IItemLayer, + selectedItemLayer: IItemLayer, + nextItemLayer: IItemLayer, + temporalItems: IItem[], +} + +export class TemporalItemLayer extends ItemLayer implements ITemporalItemLayer { + public previousItemLayer:IItemLayer = null; + public selectedItemLayer:IItemLayer =null; + public nextItemLayer:IItemLayer = null; + public temporalItems:IItem[] = []; + + constructor(item:IItem,opacity:number = 1, visible:boolean = true) { + super(item,opacity,visible) + } +} + + + diff --git a/projects/common-map/src/fm-map/reducers/map.reducer.ts b/projects/common-map/src/fm-map/reducers/map.reducer.ts index 38f9ed6..c40759e 100644 --- a/projects/common-map/src/fm-map/reducers/map.reducer.ts +++ b/projects/common-map/src/fm-map/reducers/map.reducer.ts @@ -1,6 +1,6 @@ import { tassign } from 'tassign'; import { IItem,Item } from '@farmmaps/common'; -import { IItemLayer,ItemLayer} from '../models/item.layer'; +import { IItemLayer,ItemLayer,ITemporalItemLayer,TemporalItemLayer} from '../models/item.layer'; import { IMapState} from '../models/map.state'; import { IQueryState} from '@farmmaps/common'; import { IPeriodState} from '../models/period.state'; @@ -164,6 +164,8 @@ export function reducer(state = initialState, action: mapActions.Actions | commo 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); + } else if (a.item && a.item.itemType == "vnd.farmmaps.itemtype.temporal") { + itemLayer = new TemporalItemLayer(a.item); } return tassign(state, { selectedItem: a.item, @@ -175,6 +177,56 @@ export function reducer(state = initialState, action: mapActions.Actions | commo queryState: tassign(state.queryState, {itemCode:a.item ? a.item.code:null}) }); } + case mapActions.SELECTTEMPORALITEMSSUCCESS:{ + let a = action as mapActions.SelectTemporalItemsSuccess; + let selectedItemLayer=tassign(state.selectedItemLayer) as TemporalItemLayer; + selectedItemLayer.temporalItems = a.temporalItems; + selectedItemLayer.selectedItemLayer = a.temporalItems.length>0?new ItemLayer(a.temporalItems[a.temporalItems.length-1]):null; + selectedItemLayer.previousItemLayer = a.temporalItems.length>1?new ItemLayer(a.temporalItems[a.temporalItems.length-2],0):null; + selectedItemLayer.nextItemLayer = null; + return tassign(state,{selectedItemLayer:tassign(state.selectedItemLayer,selectedItemLayer as ItemLayer)}); + } + case mapActions.NEXTTEMPORAL: { + let temporalLayer = state.selectedItemLayer as ITemporalItemLayer; + if(temporalLayer.temporalItems && temporalLayer.temporalItems.length>0) { + let index = temporalLayer.temporalItems.indexOf(temporalLayer.selectedItemLayer.item); + if(index == (temporalLayer.temporalItems.length-1)) { + return state; + } else { + temporalLayer.previousItemLayer = temporalLayer.selectedItemLayer; + if( temporalLayer.previousItemLayer) temporalLayer.previousItemLayer.opacity=0; + temporalLayer.selectedItemLayer = temporalLayer.nextItemLayer; + if( temporalLayer.selectedItemLayer) temporalLayer.selectedItemLayer.opacity=1; + temporalLayer.nextItemLayer = index+2 < temporalLayer.temporalItems.length ? new ItemLayer(temporalLayer.temporalItems[index+2],0):null; + if( temporalLayer.nextItemLayer) temporalLayer.nextItemLayer.opacity=0; + return tassign(state,{selectedItemLayer:tassign(state.selectedItemLayer,temporalLayer as ItemLayer)}); + } + } else { + return state; + } + } + case mapActions.PREVIOUSTEMPORAL: { + let temporalLayer = state.selectedItemLayer as ITemporalItemLayer; + if(temporalLayer.temporalItems && temporalLayer.temporalItems.length>0) { + let index = temporalLayer.temporalItems.indexOf(temporalLayer.selectedItemLayer.item); + if(index == 0) { + return state; + } else { + temporalLayer.nextItemLayer = temporalLayer.selectedItemLayer; + if( temporalLayer.nextItemLayer) temporalLayer.nextItemLayer.opacity=0; + temporalLayer.selectedItemLayer = temporalLayer.previousItemLayer; + if( temporalLayer.selectedItemLayer) temporalLayer.selectedItemLayer.opacity=1; + temporalLayer.previousItemLayer = index-2 >=0? new ItemLayer(temporalLayer.temporalItems[index-2],0):null; + if( temporalLayer.previousItemLayer) temporalLayer.previousItemLayer.opacity=0; + return tassign(state,{selectedItemLayer:tassign(state.selectedItemLayer,temporalLayer as ItemLayer)}); + } + } else { + return state; + } + } + case mapActions.SELECTTEMPORAL:{ + //todo implement + } case mapActions.STARTSEARCH: { let a = action as mapActions.StartSearch; return tassign(state, {