diff --git a/projects/common-map/package.json b/projects/common-map/package.json
index e204ec4..dddff35 100644
--- a/projects/common-map/package.json
+++ b/projects/common-map/package.json
@@ -6,7 +6,7 @@
},
"dependencies": {
"ngx-openlayers": "1.0.0-next.13",
- "ol": "^6.0.0"
+ "ol": "6.1.1"
},
"peerDependencies": {
"@angular/core": "^8.2.0",
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 fb4b0ab..b573a25 100644
--- a/projects/common-map/src/fm-map/actions/map.actions.ts
+++ b/projects/common-map/src/fm-map/actions/map.actions.ts
@@ -4,8 +4,7 @@ import { IMapState } from '../models/map.state';
import { IItemLayer } from '../models/item.layer';
import { IQueryState } from '../models/query.state';
import { IItem } from '@farmmaps/common';
-import { Feature } from 'ol';
-import { Extent } from 'ol/extent';
+import { Feature,Style } from 'ol';
export const SETSTATE = '[Map] SetState';
export const SETMAPSTATE = '[Map] MapState';
@@ -35,6 +34,7 @@ export const SELECTBASELAYER = '[Map] SelectBaseLayers';
export const SELECTOVERLAYLAYER = '[Map] SelectOverlayLayers';
export const ZOOMTOEXTENT = '[Map] ZoomToExtent';
export const DOQUERY = '[Map] DoQuery';
+export const SETSTYLE = '[Map] SetStyle';
export class SetState implements Action {
readonly type = SETSTATE;
@@ -204,6 +204,12 @@ export class DoQuery implements Action {
constructor(public query:IQueryState) { }
}
+export class SetStyle implements Action {
+ readonly type = SETSTYLE;
+
+ constructor(public itemType:string,public style:Style) { }
+}
+
export type Actions = SetMapState
| Init
| SetParent
@@ -231,5 +237,6 @@ export type Actions = SetMapState
| ZoomToExtent
| SetState
| SetViewExtent
- | DoQuery;
+ | DoQuery
+ | SetStyle;
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 b35a061..78d0635 100644
--- a/projects/common-map/src/fm-map/common-map.module.ts
+++ b/projects/common-map/src/fm-map/common-map.module.ts
@@ -58,6 +58,7 @@ import { MapRoutingModule } from './common-map-routing.module';
import { LegendComponent } from './components/legend/legend.component';
import { LayerVectorImageComponent } from './components/aol/layer-vector-image/layer-vector-image.component';
import { StateSerializerService } from './services/state-serializer.service';
+import {FeatureIconService} from './services/feature-icon.service';
import { GeolocationService } from './services/geolocation.service';
import {DeviceOrientationService} from './services/device-orientation.service';
import { WidgetStatusComponent } from './components/widget-status/widget-status.component';
@@ -131,6 +132,7 @@ export {
AbstractItemListItemComponent,
AbstractItemListComponent,
StateSerializerService,
+ FeatureIconService,
GeolocationService,
DeviceOrientationService,
IMapState,
@@ -249,6 +251,7 @@ export class AppCommonMapModule {
ngModule: AppCommonMapModule,
providers: [
StateSerializerService,
+ FeatureIconService,
GeolocationService,
DeviceOrientationService,
{ provide: AbstractFeatureListComponent, useClass: FeatureListCroppingschemeComponent, multi: true },
diff --git a/projects/common-map/src/fm-map/components/aol/item-vector-source/item-vector-source.component.ts b/projects/common-map/src/fm-map/components/aol/item-vector-source/item-vector-source.component.ts
index 4c43acb..08518ab 100644
--- a/projects/common-map/src/fm-map/components/aol/item-vector-source/item-vector-source.component.ts
+++ b/projects/common-map/src/fm-map/components/aol/item-vector-source/item-vector-source.component.ts
@@ -1,207 +1,146 @@
-import { Component, Host, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges, forwardRef, Inject, InjectionToken } from '@angular/core';
-import { LayerVectorComponent, SourceVectorComponent, MapComponent } from 'ngx-openlayers';
-import { ItemService,ItemTypeService,IItem, IItemType } from '@farmmaps/common';
-
-import { Feature } from 'ol';
-import { Point } from 'ol/geom';
-import { MapBrowserEvent } from 'ol';
-import * as style from 'ol/style';
-import * as color from 'ol/color';
-import * as loadingstrategy from 'ol/loadingstrategy';
-import * as condition from 'ol/events/condition';
-import * as extent from 'ol/extent';
-import {Vector,Cluster} from 'ol/source';
-import {Layer} from 'ol/layer';
-import {GeoJSON} from 'ol/format';
-import {Select} from 'ol/interaction';
-
-@Component({
- selector: 'fm-map-item-source-vector',
- template: ``,
- providers: [
- { provide: SourceVectorComponent , useExisting: forwardRef(() => ItemVectorSourceComponent) }
- ]
-})
-export class ItemVectorSourceComponent extends SourceVectorComponent implements OnInit, OnChanges {
- instance: Vector;
- private _format: GeoJSON;
- private _select: Select;
- private _hoverSelect: Select;
- private _iconScale: number = 0.05;
- @Input() features: Array;
- @Input() selectedFeature: Feature;
- @Input() selectedItem: IItem;
- @Output() onFeaturesSelected: EventEmitter = new EventEmitter();
- private styleCache = {
- 'file': new style.Style({
- image: new style.Icon({
- anchor: [0.5, 1],
- scale: 0.05,
- src: this.getIconImageDataUrl("fa fa-file-o")
- }),
- stroke: new style.Stroke({
- color: 'red',
- width: 1
- }),
- fill: new style.Fill({
- color: 'rgba(0, 0, 255, 0.1)'
- }),
- geometry: (feature) => this.geometry(feature)
- }),
- 'selected': new style.Style({
- image: new style.Icon({
- anchor: [0.5, 1],
- scale: 0.08,
- src: this.getIconImageDataUrl(null)
- }),
- stroke: new style.Stroke({
- color: 'red',
- width: 3
- }),
- fill: new style.Fill({
- color: 'rgba(0, 0, 255, 0.1)'
- }),
- geometry: (feature) => this.geometry(feature)
- })
- };
-
- constructor(@Host() private layer: LayerVectorComponent, private itemService: ItemService, @Host() private map: MapComponent, private itemTypeService: ItemTypeService) {
- super(layer);
- this._format = new GeoJSON();
- }
-
- geometry(feature: Feature) {
- let view = this.map.instance.getView();
- let resolution = view.getResolution();
- var geometry = feature.getGeometry();
- let e = geometry.getExtent();
- //var size = Math.max((e[2] - e[0]) / resolution, (e[3] - e[1]) / resolution);
- if (resolution > 12) {
- geometry = new Point(extent.getCenter(e));
- }
- return geometry;
- }
-
- getIconImageDataUrl(iconClass:string, backgroundColor: string = "#c80a6e",color:string = "#ffffff"): string {
- var canvas = document.createElement('canvas');
- canvas.width = 365;
- canvas.height = 560;
- var ctx = canvas.getContext('2d');
- ctx.lineWidth = 6;
- ctx.fillStyle = backgroundColor;
- ctx.strokeStyle = "#000000";
- var path = new Path2D("m182.9 551.7c0 0.1 0.2 0.3 0.2 0.3s175.2-269 175.2-357.4c0-130.1-88.8-186.7-175.4-186.9-86.6 0.2-175.4 56.8-175.4 186.9 0 88.4 175.3 357.4 175.3 357.4z");
- ctx.fill(path)
-
- var iconCharacter = "";
- if (iconClass != null) {
- var element = document.createElement("i");
- element.style.display = "none";
- element.className = iconClass;
- document.body.appendChild(element);
- iconCharacter = getComputedStyle(element, "::before").content.replace(/"/g, '');
- let iconFont = "200px " +getComputedStyle(element, "::before").fontFamily
- document.body.removeChild(element);
- ctx.strokeStyle = color;
- ctx.fillStyle = color;
- ctx.lineWidth = 15;
- ctx.font = iconFont;
- var ts = ctx.measureText(iconCharacter);
- ctx.fillText(iconCharacter, 182.9 - (ts.width / 2), 250);
- ctx.strokeText(iconCharacter, 182.9 - (ts.width / 2), 250);
- }
-
- return canvas.toDataURL();
- }
-
- ngOnInit() {
- this.strategy = loadingstrategy.bbox;
- this.format = new GeoJSON();
- this._select = new Select({
- style: (feature) => {
- return this.styleCache['selected'];
- },
- hitTolerance: 10,
- layers: [this.layer.instance as Layer]
- });
- this._hoverSelect = new Select({
- style: (feature) => {
- return this.styleCache['selected'];
- },
- hitTolerance: 10,
- condition: (e: MapBrowserEvent) => {
- return e.type == 'pointermove';
- },
- layers: [this.layer.instance as Layer]
- });
- this.map.instance.addInteraction(this._select);
- this.map.instance.addInteraction(this._hoverSelect);
- this._select.on('select', (e) => {
- if (e.selected.length > 0 && e.selected[0]) {
- this.onFeaturesSelected.emit(e.selected[0]);
- } else {
- this.onFeaturesSelected.emit(null);
- }
- });
- this.instance = new Vector(this);
- this.host.instance.setSource(this.instance);
-
- this.host.instance.setStyle((feature) => {
- var key = feature.get('itemType') + (this.selectedItem?"_I":"");
- if (!this.styleCache[key]) {
- if (this.itemTypeService.itemTypes[key]) {
- let itemType = this.itemTypeService.itemTypes[key];
- let fillColor = color.asArray(itemType.iconColor);
- fillColor[3] = this.selectedItem?0:0.5;
- this.styleCache[key] = new style.Style({
- image: itemType.icon ? new style.Icon({
- anchor: [0.5, 1],
- scale: 0.05,
- src: this.getIconImageDataUrl(itemType.icon)
- }):null,
- stroke: new style.Stroke({
- color: 'red',
- width: 1
- }),
- fill: new style.Fill({
- color: fillColor
- }),
- geometry: (feature) => this.geometry(feature)
- });
- } else {
- key = 'file';
- }
- }
- var styleEntry = this.styleCache[key];
- return styleEntry;
- });
- }
-
- ngOnChanges(changes: SimpleChanges) {
- if (changes["features"] && this.instance) {
- this.instance.clear(true);
- this._select.getFeatures().clear();
- this.instance.addFeatures(changes["features"].currentValue);
- }
-
- if (changes["selectedFeature"] && this.instance) {
- var features = this._select.getFeatures();
- var feature = changes["selectedFeature"].currentValue
- //this.instance.clear(false);
- //this.instance.addFeatures(features.getArray());
- features.clear();
- if (feature) {
- //this.instance.removeFeature(feature);
- features.push(feature)
- }
- }
- if (changes["selectedItem"] && this.instance) {
- var item = changes["selectedItem"].currentValue
- if (item) {
- this.map.instance.removeInteraction(this._hoverSelect);
- } else {
- this.map.instance.addInteraction(this._hoverSelect);
- }
- }
- }
-}
+import { Component, Host, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges, forwardRef, Inject, InjectionToken } from '@angular/core';
+import { LayerVectorComponent, SourceVectorComponent, MapComponent } from 'ngx-openlayers';
+import { ItemService,ItemTypeService,IItem, IItemType } from '@farmmaps/common';
+
+import { Feature } from 'ol';
+import { Point } from 'ol/geom';
+import { MapBrowserEvent } from 'ol';
+import * as style from 'ol/style';
+import * as color from 'ol/color';
+import * as loadingstrategy from 'ol/loadingstrategy';
+import * as condition from 'ol/events/condition';
+import * as extent from 'ol/extent';
+import {Vector,Cluster} from 'ol/source';
+import {Layer} from 'ol/layer';
+import {GeoJSON} from 'ol/format';
+import {Select} from 'ol/interaction';
+import {IStyleCache} from '../../../models/style.cache';
+import {FeatureIconService} from '../../../services/feature-icon.service';
+
+@Component({
+ selector: 'fm-map-item-source-vector',
+ template: ``,
+ providers: [
+ { provide: SourceVectorComponent , useExisting: forwardRef(() => ItemVectorSourceComponent) }
+ ]
+})
+export class ItemVectorSourceComponent extends SourceVectorComponent implements OnInit, OnChanges {
+ instance: Vector;
+ private _format: GeoJSON;
+ private _select: Select;
+ private _hoverSelect: Select;
+ private _iconScale: number = 0.05;
+ @Input() features: Array;
+ @Input() selectedFeature: Feature;
+ @Input() selectedItem: IItem;
+ @Output() onFeaturesSelected: EventEmitter = new EventEmitter();
+ private styleCache:IStyleCache = {};
+
+ constructor(@Host() private layer: LayerVectorComponent, private itemService: ItemService, @Host() private map: MapComponent, private itemTypeService: ItemTypeService,private featureIconService$:FeatureIconService) {
+ super(layer);
+ this._format = new GeoJSON();
+ }
+
+ geometry(feature: Feature) {
+ let view = this.map.instance.getView();
+ let resolution = view.getResolution();
+ var geometry = feature.getGeometry();
+ let e = geometry.getExtent();
+ //var size = Math.max((e[2] - e[0]) / resolution, (e[3] - e[1]) / resolution);
+ if (resolution > 12) {
+ geometry = new Point(extent.getCenter(e));
+ }
+ return geometry;
+ }
+
+ ngOnInit() {
+ this.strategy = loadingstrategy.bbox;
+ this.format = new GeoJSON();
+ this._select = new Select({
+ style: (feature) => {
+ return this.styleCache['selected'];
+ },
+ hitTolerance: 10,
+ layers: [this.layer.instance as Layer]
+ });
+ this._hoverSelect = new Select({
+ style: (feature) => {
+ return this.styleCache['selected'];
+ },
+ hitTolerance: 10,
+ condition: (e: MapBrowserEvent) => {
+ return e.type == 'pointermove';
+ },
+ layers: [this.layer.instance as Layer]
+ });
+ this.map.instance.addInteraction(this._select);
+ this.map.instance.addInteraction(this._hoverSelect);
+ this._select.on('select', (e) => {
+ if (e.selected.length > 0 && e.selected[0]) {
+ this.onFeaturesSelected.emit(e.selected[0]);
+ } else {
+ this.onFeaturesSelected.emit(null);
+ }
+ });
+ this.instance = new Vector(this);
+ this.host.instance.setSource(this.instance);
+
+ this.host.instance.setStyle((feature) => {
+ var key = feature.get('itemType') + (this.selectedItem?"_I":"");
+ if (!this.styleCache[key]) {
+ if (this.itemTypeService.itemTypes[key]) {
+ let itemType = this.itemTypeService.itemTypes[key];
+ let fillColor = color.asArray(itemType.iconColor);
+ fillColor[3] = this.selectedItem?0:0.5;
+ this.styleCache[key] = new style.Style({
+ image: itemType.icon ? new style.Icon({
+ anchor: [0.5, 1],
+ scale: 0.05,
+ src: this.featureIconService$.getIconImageDataUrl(itemType.icon)
+ }):null,
+ stroke: new style.Stroke({
+ color: 'red',
+ width: 1
+ }),
+ fill: new style.Fill({
+ color: fillColor
+ }),
+ });
+ } else {
+ key = 'file';
+ }
+ }
+ var styleEntry = this.styleCache[key];
+ styleEntry.geometry = (feature) => this.geometry(feature);
+ return styleEntry;
+ });
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ if (changes["features"] && this.instance) {
+ this.instance.clear(true);
+ this._select.getFeatures().clear();
+ this.instance.addFeatures(changes["features"].currentValue);
+ }
+
+ if (changes["selectedFeature"] && this.instance) {
+ var features = this._select.getFeatures();
+ var feature = changes["selectedFeature"].currentValue
+ //this.instance.clear(false);
+ //this.instance.addFeatures(features.getArray());
+ features.clear();
+ if (feature) {
+ //this.instance.removeFeature(feature);
+ features.push(feature)
+ }
+ }
+ if (changes["selectedItem"] && this.instance) {
+ var item = changes["selectedItem"].currentValue
+ if (item) {
+ this.map.instance.removeInteraction(this._hoverSelect);
+ } else {
+ this.map.instance.addInteraction(this._hoverSelect);
+ }
+ }
+ }
+}
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 cf35add..1c6facf 100644
--- a/projects/common-map/src/fm-map/effects/map.effects.ts
+++ b/projects/common-map/src/fm-map/effects/map.effects.ts
@@ -19,10 +19,15 @@ import {commonReducers} from '@farmmaps/common';
import {commonActions} from '@farmmaps/common';
-import { IListItem, IItem } 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';
+
+
@Injectable()
export class MapEffects {
private _geojsonFormat: GeoJSON;
@@ -44,10 +49,41 @@ export class MapEffects {
ofType(mapActions.INIT),
withLatestFrom(this.store$.select(commonReducers.selectGetRootItems)),
switchMap(([action, rootItems]) => {
+ let actions=[];
for (let rootItem of rootItems) {
- if (rootItem.itemType == "UPLOADS_FOLDER") return of(new mapActions.SetParent(rootItem.code));
+ if (rootItem.itemType == "UPLOADS_FOLDER") actions.push(new mapActions.SetParent(rootItem.code));
}
- return [];
+ // 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;
}
));
@@ -231,7 +267,7 @@ export class MapEffects {
return of(newAction);
}));
- constructor(private actions$: Actions, private store$: Store, private folderService$: FolderService, private itemService$: ItemService) {
+ constructor(private actions$: Actions, private store$: Store, private folderService$: FolderService, private itemService$: ItemService,private featureIconService$:FeatureIconService) {
this._geojsonFormat = new GeoJSON();
this._wktFormat = new WKT();
}
diff --git a/projects/common-map/src/fm-map/models/style.cache.ts b/projects/common-map/src/fm-map/models/style.cache.ts
new file mode 100644
index 0000000..96e5b9c
--- /dev/null
+++ b/projects/common-map/src/fm-map/models/style.cache.ts
@@ -0,0 +1,5 @@
+import {Style} from 'ol';
+
+export interface IStyleCache{
+ [id: string]: Style;
+};
\ No newline at end of file
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 9374631..6641f22 100644
--- a/projects/common-map/src/fm-map/reducers/map.reducer.ts
+++ b/projects/common-map/src/fm-map/reducers/map.reducer.ts
@@ -4,6 +4,7 @@ import { IItemLayer,ItemLayer} from '../models/item.layer';
import { IMapState} from '../models/map.state';
import { IQueryState} from '../models/query.state';
import { IPeriodState} from '../models/period.state';
+import { IStyleCache} from '../models/style.cache';
import * as mapActions from '../actions/map.actions';
import {commonActions} from '@farmmaps/common';
import { createSelector, createFeatureSelector } from '@ngrx/store';
@@ -51,7 +52,8 @@ export interface State {
selectedItemLayer: IItemLayer,
projection: string,
selectedBaseLayer: IItemLayer,
- selectedOverlayLayer: IItemLayer
+ selectedOverlayLayer: IItemLayer,
+ styleCache:IStyleCache
}
export const initialState: State = {
@@ -84,7 +86,8 @@ export const initialState: State = {
projection: "EPSG:3857",
selectedBaseLayer: null,
selectedOverlayLayer: null,
- selectedItemLayer: null
+ selectedItemLayer: null,
+ styleCache: {}
}
export function reducer(state = initialState, action: mapActions.Actions | commonActions.Actions | RouterNavigationAction): State {
@@ -310,6 +313,12 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
return tassign(state, {});
}
}
+ case mapActions.SETSTYLE:{
+ let a = action as mapActions.SetStyle;
+ let styles = state.styleCache;
+ styles[a.itemType] = a.style;
+ return tassign(state,{styleCache:styles});
+ }
default: {
return state;
}
diff --git a/projects/common-map/src/fm-map/services/feature-icon.service.ts b/projects/common-map/src/fm-map/services/feature-icon.service.ts
new file mode 100644
index 0000000..70a5a17
--- /dev/null
+++ b/projects/common-map/src/fm-map/services/feature-icon.service.ts
@@ -0,0 +1,41 @@
+import { Injectable} from '@angular/core';
+import { Feature } from 'ol';
+import { Point } from 'ol/geom';
+import * as extent from 'ol/extent';
+
+
+@Injectable()
+export class FeatureIconService {
+
+ getIconImageDataUrl(iconClass:string, backgroundColor: string = "#c80a6e",color:string = "#ffffff"): string {
+ var canvas = document.createElement('canvas');
+ canvas.width = 365;
+ canvas.height = 560;
+ var ctx = canvas.getContext('2d');
+ ctx.lineWidth = 6;
+ ctx.fillStyle = backgroundColor;
+ ctx.strokeStyle = "#000000";
+ var path = new Path2D("m182.9 551.7c0 0.1 0.2 0.3 0.2 0.3s175.2-269 175.2-357.4c0-130.1-88.8-186.7-175.4-186.9-86.6 0.2-175.4 56.8-175.4 186.9 0 88.4 175.3 357.4 175.3 357.4z");
+ ctx.fill(path)
+
+ var iconCharacter = "";
+ if (iconClass != null) {
+ var element = document.createElement("i");
+ element.style.display = "none";
+ element.className = iconClass;
+ document.body.appendChild(element);
+ iconCharacter = getComputedStyle(element, "::before").content.replace(/"/g, '');
+ let iconFont = "200px " +getComputedStyle(element, "::before").fontFamily
+ document.body.removeChild(element);
+ ctx.strokeStyle = color;
+ ctx.fillStyle = color;
+ ctx.lineWidth = 15;
+ ctx.font = iconFont;
+ var ts = ctx.measureText(iconCharacter);
+ ctx.fillText(iconCharacter, 182.9 - (ts.width / 2), 250);
+ ctx.strokeText(iconCharacter, 182.9 - (ts.width / 2), 250);
+ }
+
+ return canvas.toDataURL();
+ }
+}
\ No newline at end of file
diff --git a/projects/common/src/fm/services/itemtype.service.ts b/projects/common/src/fm/services/itemtype.service.ts
index 426ea45..f81f1ad 100644
--- a/projects/common/src/fm/services/itemtype.service.ts
+++ b/projects/common/src/fm/services/itemtype.service.ts
@@ -4,9 +4,7 @@ import {IItem} from '../models/item'
import {AppConfig} from '../shared/app.config';
import {HttpClient, HttpXhrBackend} from '@angular/common/http';
-@Injectable({
- providedIn: 'root',
-})
+@Injectable()
export class ItemTypeService {
public itemTypes: IItemTypes;
private httpClient: HttpClient;
@@ -15,10 +13,6 @@ export class ItemTypeService {
this.httpClient = new HttpClient(xhrBackend);
}
- // itemService.getItemTypes().subscribe((itemTypes) => {
- // this.itemTypes = itemTypes;
- // });
-
getIcon(itemType: string) {
var icon = "fa fa-file-o";
if (this.itemTypes[itemType]) icon = this.itemTypes[itemType].icon;