Merge branch 'feature/AW-847' into develop
All checks were successful
FarmMaps.Develop/FarmMapsLib/develop This commit looks good
All checks were successful
FarmMaps.Develop/FarmMapsLib/develop This commit looks good
This commit is contained in:
commit
1646119b16
@ -6,7 +6,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ngx-openlayers": "1.0.0-next.13",
|
"ngx-openlayers": "1.0.0-next.13",
|
||||||
"ol": "^6.0.0"
|
"ol": "6.1.1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@angular/core": "^8.2.0",
|
"@angular/core": "^8.2.0",
|
||||||
|
@ -4,8 +4,7 @@ import { IMapState } from '../models/map.state';
|
|||||||
import { IItemLayer } from '../models/item.layer';
|
import { IItemLayer } from '../models/item.layer';
|
||||||
import { IQueryState } from '../models/query.state';
|
import { IQueryState } from '../models/query.state';
|
||||||
import { IItem } from '@farmmaps/common';
|
import { IItem } from '@farmmaps/common';
|
||||||
import { Feature } from 'ol';
|
import { Feature,Style } from 'ol';
|
||||||
import { Extent } from 'ol/extent';
|
|
||||||
|
|
||||||
export const SETSTATE = '[Map] SetState';
|
export const SETSTATE = '[Map] SetState';
|
||||||
export const SETMAPSTATE = '[Map] MapState';
|
export const SETMAPSTATE = '[Map] MapState';
|
||||||
@ -35,6 +34,7 @@ export const SELECTBASELAYER = '[Map] SelectBaseLayers';
|
|||||||
export const SELECTOVERLAYLAYER = '[Map] SelectOverlayLayers';
|
export const SELECTOVERLAYLAYER = '[Map] SelectOverlayLayers';
|
||||||
export const ZOOMTOEXTENT = '[Map] ZoomToExtent';
|
export const ZOOMTOEXTENT = '[Map] ZoomToExtent';
|
||||||
export const DOQUERY = '[Map] DoQuery';
|
export const DOQUERY = '[Map] DoQuery';
|
||||||
|
export const SETSTYLE = '[Map] SetStyle';
|
||||||
|
|
||||||
export class SetState implements Action {
|
export class SetState implements Action {
|
||||||
readonly type = SETSTATE;
|
readonly type = SETSTATE;
|
||||||
@ -204,6 +204,12 @@ export class DoQuery implements Action {
|
|||||||
constructor(public query:IQueryState) { }
|
constructor(public query:IQueryState) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class SetStyle implements Action {
|
||||||
|
readonly type = SETSTYLE;
|
||||||
|
|
||||||
|
constructor(public itemType:string,public style:Style) { }
|
||||||
|
}
|
||||||
|
|
||||||
export type Actions = SetMapState
|
export type Actions = SetMapState
|
||||||
| Init
|
| Init
|
||||||
| SetParent
|
| SetParent
|
||||||
@ -231,5 +237,6 @@ export type Actions = SetMapState
|
|||||||
| ZoomToExtent
|
| ZoomToExtent
|
||||||
| SetState
|
| SetState
|
||||||
| SetViewExtent
|
| SetViewExtent
|
||||||
| DoQuery;
|
| DoQuery
|
||||||
|
| SetStyle;
|
||||||
|
|
||||||
|
@ -58,6 +58,7 @@ import { MapRoutingModule } from './common-map-routing.module';
|
|||||||
import { LegendComponent } from './components/legend/legend.component';
|
import { LegendComponent } from './components/legend/legend.component';
|
||||||
import { LayerVectorImageComponent } from './components/aol/layer-vector-image/layer-vector-image.component';
|
import { LayerVectorImageComponent } from './components/aol/layer-vector-image/layer-vector-image.component';
|
||||||
import { StateSerializerService } from './services/state-serializer.service';
|
import { StateSerializerService } from './services/state-serializer.service';
|
||||||
|
import {FeatureIconService} from './services/feature-icon.service';
|
||||||
import { GeolocationService } from './services/geolocation.service';
|
import { GeolocationService } from './services/geolocation.service';
|
||||||
import {DeviceOrientationService} from './services/device-orientation.service';
|
import {DeviceOrientationService} from './services/device-orientation.service';
|
||||||
import { WidgetStatusComponent } from './components/widget-status/widget-status.component';
|
import { WidgetStatusComponent } from './components/widget-status/widget-status.component';
|
||||||
@ -131,6 +132,7 @@ export {
|
|||||||
AbstractItemListItemComponent,
|
AbstractItemListItemComponent,
|
||||||
AbstractItemListComponent,
|
AbstractItemListComponent,
|
||||||
StateSerializerService,
|
StateSerializerService,
|
||||||
|
FeatureIconService,
|
||||||
GeolocationService,
|
GeolocationService,
|
||||||
DeviceOrientationService,
|
DeviceOrientationService,
|
||||||
IMapState,
|
IMapState,
|
||||||
@ -249,6 +251,7 @@ export class AppCommonMapModule {
|
|||||||
ngModule: AppCommonMapModule,
|
ngModule: AppCommonMapModule,
|
||||||
providers: [
|
providers: [
|
||||||
StateSerializerService,
|
StateSerializerService,
|
||||||
|
FeatureIconService,
|
||||||
GeolocationService,
|
GeolocationService,
|
||||||
DeviceOrientationService,
|
DeviceOrientationService,
|
||||||
{ provide: AbstractFeatureListComponent, useClass: FeatureListCroppingschemeComponent, multi: true },
|
{ provide: AbstractFeatureListComponent, useClass: FeatureListCroppingschemeComponent, multi: true },
|
||||||
|
@ -1,207 +1,159 @@
|
|||||||
import { Component, Host, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges, forwardRef, Inject, InjectionToken } from '@angular/core';
|
import { Component, Host, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges, forwardRef, Inject, InjectionToken } from '@angular/core';
|
||||||
import { LayerVectorComponent, SourceVectorComponent, MapComponent } from 'ngx-openlayers';
|
import { LayerVectorComponent, SourceVectorComponent, MapComponent } from 'ngx-openlayers';
|
||||||
import { ItemService,ItemTypeService,IItem, IItemType } from '@farmmaps/common';
|
import { ItemService,ItemTypeService,IItem, IItemType } from '@farmmaps/common';
|
||||||
|
|
||||||
import { Feature } from 'ol';
|
import { Feature } from 'ol';
|
||||||
import { Point } from 'ol/geom';
|
import { Point } from 'ol/geom';
|
||||||
import { MapBrowserEvent } from 'ol';
|
import { MapBrowserEvent } from 'ol';
|
||||||
import * as style from 'ol/style';
|
import * as style from 'ol/style';
|
||||||
import * as color from 'ol/color';
|
import * as color from 'ol/color';
|
||||||
import * as loadingstrategy from 'ol/loadingstrategy';
|
import * as loadingstrategy from 'ol/loadingstrategy';
|
||||||
import * as condition from 'ol/events/condition';
|
import * as condition from 'ol/events/condition';
|
||||||
import * as extent from 'ol/extent';
|
import * as extent from 'ol/extent';
|
||||||
import {Vector,Cluster} from 'ol/source';
|
import {Vector,Cluster} from 'ol/source';
|
||||||
import {Layer} from 'ol/layer';
|
import {Layer} from 'ol/layer';
|
||||||
import {GeoJSON} from 'ol/format';
|
import {GeoJSON} from 'ol/format';
|
||||||
import {Select} from 'ol/interaction';
|
import {Select} from 'ol/interaction';
|
||||||
|
import {IStyles} from '../../../models/style.cache';
|
||||||
@Component({
|
import {FeatureIconService} from '../../../services/feature-icon.service';
|
||||||
selector: 'fm-map-item-source-vector',
|
|
||||||
template: `<ng-content></ng-content>`,
|
@Component({
|
||||||
providers: [
|
selector: 'fm-map-item-source-vector',
|
||||||
{ provide: SourceVectorComponent , useExisting: forwardRef(() => ItemVectorSourceComponent) }
|
template: `<ng-content></ng-content>`,
|
||||||
]
|
providers: [
|
||||||
})
|
{ provide: SourceVectorComponent , useExisting: forwardRef(() => ItemVectorSourceComponent) }
|
||||||
export class ItemVectorSourceComponent extends SourceVectorComponent implements OnInit, OnChanges {
|
]
|
||||||
instance: Vector;
|
})
|
||||||
private _format: GeoJSON;
|
export class ItemVectorSourceComponent extends SourceVectorComponent implements OnInit, OnChanges {
|
||||||
private _select: Select;
|
instance: Vector;
|
||||||
private _hoverSelect: Select;
|
private _format: GeoJSON;
|
||||||
private _iconScale: number = 0.05;
|
private _select: Select;
|
||||||
@Input() features: Array<Feature>;
|
private _hoverSelect: Select;
|
||||||
@Input() selectedFeature: Feature;
|
private _iconScale: number = 0.05;
|
||||||
@Input() selectedItem: IItem;
|
@Input() features: Array<Feature>;
|
||||||
@Output() onFeaturesSelected: EventEmitter<Feature> = new EventEmitter<Feature>();
|
@Input() selectedFeature: Feature;
|
||||||
private styleCache = {
|
@Input() selectedItem: IItem;
|
||||||
'file': new style.Style({
|
@Input() styles:IStyles;
|
||||||
image: new style.Icon({
|
@Output() onFeaturesSelected: EventEmitter<Feature> = new EventEmitter<Feature>();
|
||||||
anchor: [0.5, 1],
|
private stylesCache:IStyles = {};
|
||||||
scale: 0.05,
|
|
||||||
src: this.getIconImageDataUrl("fa fa-file-o")
|
constructor(@Host() private layer: LayerVectorComponent, private itemService: ItemService, @Host() private map: MapComponent, private itemTypeService: ItemTypeService,private featureIconService$:FeatureIconService) {
|
||||||
}),
|
super(layer);
|
||||||
stroke: new style.Stroke({
|
this._format = new GeoJSON();
|
||||||
color: 'red',
|
}
|
||||||
width: 1
|
|
||||||
}),
|
geometry(feature: Feature) {
|
||||||
fill: new style.Fill({
|
let view = this.map.instance.getView();
|
||||||
color: 'rgba(0, 0, 255, 0.1)'
|
let resolution = view.getResolution();
|
||||||
}),
|
var geometry = feature.getGeometry();
|
||||||
geometry: (feature) => this.geometry(feature)
|
let e = geometry.getExtent();
|
||||||
}),
|
//var size = Math.max((e[2] - e[0]) / resolution, (e[3] - e[1]) / resolution);
|
||||||
'selected': new style.Style({
|
if (resolution > 12) {
|
||||||
image: new style.Icon({
|
geometry = new Point(extent.getCenter(e));
|
||||||
anchor: [0.5, 1],
|
}
|
||||||
scale: 0.08,
|
return geometry;
|
||||||
src: this.getIconImageDataUrl(null)
|
}
|
||||||
}),
|
|
||||||
stroke: new style.Stroke({
|
ngOnInit() {
|
||||||
color: 'red',
|
this.strategy = loadingstrategy.bbox;
|
||||||
width: 3
|
this.format = new GeoJSON();
|
||||||
}),
|
this._select = new Select({
|
||||||
fill: new style.Fill({
|
style: (feature) => {
|
||||||
color: 'rgba(0, 0, 255, 0.1)'
|
return this.stylesCache['selected'];
|
||||||
}),
|
},
|
||||||
geometry: (feature) => this.geometry(feature)
|
hitTolerance: 10,
|
||||||
})
|
layers: [this.layer.instance as Layer]
|
||||||
};
|
});
|
||||||
|
this._hoverSelect = new Select({
|
||||||
constructor(@Host() private layer: LayerVectorComponent, private itemService: ItemService, @Host() private map: MapComponent, private itemTypeService: ItemTypeService) {
|
style: (feature) => {
|
||||||
super(layer);
|
return this.stylesCache['selected'];
|
||||||
this._format = new GeoJSON();
|
},
|
||||||
}
|
hitTolerance: 10,
|
||||||
|
condition: (e: MapBrowserEvent) => {
|
||||||
geometry(feature: Feature) {
|
return e.type == 'pointermove';
|
||||||
let view = this.map.instance.getView();
|
},
|
||||||
let resolution = view.getResolution();
|
layers: [this.layer.instance as Layer]
|
||||||
var geometry = feature.getGeometry();
|
});
|
||||||
let e = geometry.getExtent();
|
this.map.instance.addInteraction(this._select);
|
||||||
//var size = Math.max((e[2] - e[0]) / resolution, (e[3] - e[1]) / resolution);
|
this.map.instance.addInteraction(this._hoverSelect);
|
||||||
if (resolution > 12) {
|
this._select.on('select', (e) => {
|
||||||
geometry = new Point(extent.getCenter(e));
|
if (e.selected.length > 0 && e.selected[0]) {
|
||||||
}
|
this.onFeaturesSelected.emit(e.selected[0]);
|
||||||
return geometry;
|
} else {
|
||||||
}
|
this.onFeaturesSelected.emit(null);
|
||||||
|
}
|
||||||
getIconImageDataUrl(iconClass:string, backgroundColor: string = "#c80a6e",color:string = "#ffffff"): string {
|
});
|
||||||
var canvas = document.createElement('canvas');
|
this.instance = new Vector(this);
|
||||||
canvas.width = 365;
|
this.host.instance.setSource(this.instance);
|
||||||
canvas.height = 560;
|
|
||||||
var ctx = canvas.getContext('2d');
|
this.host.instance.setStyle((feature) => {
|
||||||
ctx.lineWidth = 6;
|
var key = feature.get('itemType') + (this.selectedItem?"_I":"");
|
||||||
ctx.fillStyle = backgroundColor;
|
if (!this.stylesCache[key]) {
|
||||||
ctx.strokeStyle = "#000000";
|
if (this.itemTypeService.itemTypes[key]) {
|
||||||
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");
|
let itemType = this.itemTypeService.itemTypes[key];
|
||||||
ctx.fill(path)
|
let fillColor = color.asArray(itemType.iconColor);
|
||||||
|
fillColor[3] = this.selectedItem?0:0.5;
|
||||||
var iconCharacter = "";
|
this.stylesCache[key] = new style.Style({
|
||||||
if (iconClass != null) {
|
image: itemType.icon ? new style.Icon({
|
||||||
var element = document.createElement("i");
|
anchor: [0.5, 1],
|
||||||
element.style.display = "none";
|
scale: 0.05,
|
||||||
element.className = iconClass;
|
src: this.featureIconService$.getIconImageDataUrl(itemType.icon)
|
||||||
document.body.appendChild(element);
|
}):null,
|
||||||
iconCharacter = getComputedStyle(element, "::before").content.replace(/"/g, '');
|
stroke: new style.Stroke({
|
||||||
let iconFont = "200px " +getComputedStyle(element, "::before").fontFamily
|
color: 'red',
|
||||||
document.body.removeChild(element);
|
width: 1
|
||||||
ctx.strokeStyle = color;
|
}),
|
||||||
ctx.fillStyle = color;
|
fill: new style.Fill({
|
||||||
ctx.lineWidth = 15;
|
color: fillColor
|
||||||
ctx.font = iconFont;
|
}),
|
||||||
var ts = ctx.measureText(iconCharacter);
|
geometry:(feature) => this.geometry(feature)
|
||||||
ctx.fillText(iconCharacter, 182.9 - (ts.width / 2), 250);
|
});
|
||||||
ctx.strokeText(iconCharacter, 182.9 - (ts.width / 2), 250);
|
} else {
|
||||||
}
|
key = 'file';
|
||||||
|
}
|
||||||
return canvas.toDataURL();
|
}
|
||||||
}
|
var styleEntry = this.stylesCache[key];
|
||||||
|
return styleEntry;
|
||||||
ngOnInit() {
|
});
|
||||||
this.strategy = loadingstrategy.bbox;
|
}
|
||||||
this.format = new GeoJSON();
|
|
||||||
this._select = new Select({
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
style: (feature) => {
|
if (changes["features"] && this.instance) {
|
||||||
return this.styleCache['selected'];
|
this.instance.clear(true);
|
||||||
},
|
this._select.getFeatures().clear();
|
||||||
hitTolerance: 10,
|
this.instance.addFeatures(changes["features"].currentValue);
|
||||||
layers: [this.layer.instance as Layer]
|
}
|
||||||
});
|
|
||||||
this._hoverSelect = new Select({
|
if (changes["selectedFeature"] && this.instance) {
|
||||||
style: (feature) => {
|
var features = this._select.getFeatures();
|
||||||
return this.styleCache['selected'];
|
var feature = changes["selectedFeature"].currentValue
|
||||||
},
|
//this.instance.clear(false);
|
||||||
hitTolerance: 10,
|
//this.instance.addFeatures(features.getArray());
|
||||||
condition: (e: MapBrowserEvent) => {
|
features.clear();
|
||||||
return e.type == 'pointermove';
|
if (feature) {
|
||||||
},
|
//this.instance.removeFeature(feature);
|
||||||
layers: [this.layer.instance as Layer]
|
features.push(feature)
|
||||||
});
|
}
|
||||||
this.map.instance.addInteraction(this._select);
|
}
|
||||||
this.map.instance.addInteraction(this._hoverSelect);
|
if (changes["selectedItem"] && this.instance) {
|
||||||
this._select.on('select', (e) => {
|
var item = changes["selectedItem"].currentValue
|
||||||
if (e.selected.length > 0 && e.selected[0]) {
|
if (item) {
|
||||||
this.onFeaturesSelected.emit(e.selected[0]);
|
this.map.instance.removeInteraction(this._hoverSelect);
|
||||||
} else {
|
} else {
|
||||||
this.onFeaturesSelected.emit(null);
|
this.map.instance.addInteraction(this._hoverSelect);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
this.instance = new Vector(this);
|
if (changes["styles"] && this.instance) {
|
||||||
this.host.instance.setSource(this.instance);
|
let styles = changes["styles"].currentValue;
|
||||||
|
for (const key in styles) {
|
||||||
this.host.instance.setStyle((feature) => {
|
if (styles.hasOwnProperty(key)) {
|
||||||
var key = feature.get('itemType') + (this.selectedItem?"_I":"");
|
let style = styles[key];
|
||||||
if (!this.styleCache[key]) {
|
if(style.geometry == null) {
|
||||||
if (this.itemTypeService.itemTypes[key]) {
|
style.setGeometry((feature) => this.geometry(feature));
|
||||||
let itemType = this.itemTypeService.itemTypes[key];
|
}
|
||||||
let fillColor = color.asArray(itemType.iconColor);
|
this.stylesCache[key]=style;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -16,7 +16,8 @@
|
|||||||
searchCollapsed:searchCollapsed$|async,
|
searchCollapsed:searchCollapsed$|async,
|
||||||
clearEnabled:clearEnabled$|async,
|
clearEnabled:clearEnabled$|async,
|
||||||
period:period$|async,
|
period:period$|async,
|
||||||
compassHeading:compassHeading$|async
|
compassHeading:compassHeading$|async,
|
||||||
|
styles:styles$|async
|
||||||
} as state">
|
} as state">
|
||||||
<aol-map #map (moveEnd)="handleOnMoveEnd($event)" (click)="handleOnMouseDown($event)" [ngClass]="{'panel-visible':state.panelVisible}" class="map">
|
<aol-map #map (moveEnd)="handleOnMoveEnd($event)" (click)="handleOnMouseDown($event)" [ngClass]="{'panel-visible':state.panelVisible}" class="map">
|
||||||
<div>
|
<div>
|
||||||
@ -32,7 +33,7 @@
|
|||||||
<fm-map-item-layers [itemLayers]="state.overlayLayers"></fm-map-item-layers>
|
<fm-map-item-layers [itemLayers]="state.overlayLayers"></fm-map-item-layers>
|
||||||
<fm-map-item-layers [itemLayer]="state.selectedItemLayer"></fm-map-item-layers>
|
<fm-map-item-layers [itemLayer]="state.selectedItemLayer"></fm-map-item-layers>
|
||||||
<aol-layer-vector>
|
<aol-layer-vector>
|
||||||
<fm-map-item-source-vector [features]="state.features" (onFeaturesSelected)="handleFeaturesSelected($event)" [selectedFeature]="state.selectedFeature" [selectedItem]="state.selectedItem"></fm-map-item-source-vector>
|
<fm-map-item-source-vector [styles]="state.styles" [features]="state.features" (onFeaturesSelected)="handleFeaturesSelected($event)" [selectedFeature]="state.selectedFeature" [selectedItem]="state.selectedItem"></fm-map-item-source-vector>
|
||||||
</aol-layer-vector>
|
</aol-layer-vector>
|
||||||
<fm-map-gps-location [position]="state.position" [headingTolerance]="20" [showHeading]="true" [heading]="state.compassHeading"></fm-map-gps-location>
|
<fm-map-gps-location [position]="state.position" [headingTolerance]="20" [showHeading]="true" [heading]="state.compassHeading"></fm-map-gps-location>
|
||||||
<div class="control-container">
|
<div class="control-container">
|
||||||
|
@ -14,6 +14,7 @@ import { ISelectedFeatures } from '../../models/selected.features';
|
|||||||
import { IItemLayer } from '../../models/item.layer';
|
import { IItemLayer } from '../../models/item.layer';
|
||||||
import { IQueryState } from '../../models/query.state';
|
import { IQueryState } from '../../models/query.state';
|
||||||
import { IPeriodState } from '../../models/period.state';
|
import { IPeriodState } from '../../models/period.state';
|
||||||
|
import {IStyles} from '../../models/style.cache';
|
||||||
import { IDroppedFile } from '../aol/file-drop-target/file-drop-target.component';
|
import { IDroppedFile } from '../aol/file-drop-target/file-drop-target.component';
|
||||||
import { IMetaData } from '../meta-data-modal/meta-data-modal.component';
|
import { IMetaData } from '../meta-data-modal/meta-data-modal.component';
|
||||||
import { StateSerializerService } from '../../services/state-serializer.service';
|
import { StateSerializerService } from '../../services/state-serializer.service';
|
||||||
@ -74,6 +75,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
|
|||||||
public baseLayersCollapsed:boolean = true;
|
public baseLayersCollapsed:boolean = true;
|
||||||
public overlayLayersCollapsed: boolean = true;
|
public overlayLayersCollapsed: boolean = true;
|
||||||
public extent$: Observable<Extent>;
|
public extent$: Observable<Extent>;
|
||||||
|
public styles$:Observable<IStyles>;
|
||||||
@ViewChild('map', { static: false }) map;
|
@ViewChild('map', { static: false }) map;
|
||||||
|
|
||||||
constructor(private store: Store<mapReducers.State | commonReducers.State>,
|
constructor(private store: Store<mapReducers.State | commonReducers.State>,
|
||||||
@ -146,6 +148,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
|
|||||||
this.period$ = this.store.select(mapReducers.selectGetPeriod);
|
this.period$ = this.store.select(mapReducers.selectGetPeriod);
|
||||||
this.position$ = this.geolocationService.getCurrentPosition();
|
this.position$ = this.geolocationService.getCurrentPosition();
|
||||||
this.compassHeading$ = this.deviceorientationService.getCurrentCompassHeading();
|
this.compassHeading$ = this.deviceorientationService.getCurrentCompassHeading();
|
||||||
|
this.styles$ = this.store.select(mapReducers.selectGetStyles);
|
||||||
|
|
||||||
this.mapState$.pipe(withLatestFrom(this.queryState$)).subscribe((state) => {
|
this.mapState$.pipe(withLatestFrom(this.queryState$)).subscribe((state) => {
|
||||||
this.replaceUrl(state[0], state[1], true);
|
this.replaceUrl(state[0], state[1], true);
|
||||||
|
@ -19,10 +19,15 @@ import {commonReducers} from '@farmmaps/common';
|
|||||||
|
|
||||||
import {commonActions} 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 { FolderService, ItemService } from '@farmmaps/common';
|
||||||
import { tassign } from 'tassign';
|
import { tassign } from 'tassign';
|
||||||
|
|
||||||
|
import {FeatureIconService} from '../services/feature-icon.service';
|
||||||
|
|
||||||
|
import * as style from 'ol/style';
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MapEffects {
|
export class MapEffects {
|
||||||
private _geojsonFormat: GeoJSON;
|
private _geojsonFormat: GeoJSON;
|
||||||
@ -44,10 +49,41 @@ export class MapEffects {
|
|||||||
ofType(mapActions.INIT),
|
ofType(mapActions.INIT),
|
||||||
withLatestFrom(this.store$.select(commonReducers.selectGetRootItems)),
|
withLatestFrom(this.store$.select(commonReducers.selectGetRootItems)),
|
||||||
switchMap(([action, rootItems]) => {
|
switchMap(([action, rootItems]) => {
|
||||||
|
let actions=[];
|
||||||
for (let rootItem of rootItems) {
|
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);
|
return of(newAction);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
constructor(private actions$: Actions, private store$: Store<mapReducers.State>, private folderService$: FolderService, private itemService$: ItemService) {
|
constructor(private actions$: Actions, private store$: Store<mapReducers.State>, private folderService$: FolderService, private itemService$: ItemService,private featureIconService$:FeatureIconService) {
|
||||||
this._geojsonFormat = new GeoJSON();
|
this._geojsonFormat = new GeoJSON();
|
||||||
this._wktFormat = new WKT();
|
this._wktFormat = new WKT();
|
||||||
}
|
}
|
||||||
|
5
projects/common-map/src/fm-map/models/style.cache.ts
Normal file
5
projects/common-map/src/fm-map/models/style.cache.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import {Style} from 'ol';
|
||||||
|
|
||||||
|
export interface IStyles{
|
||||||
|
[id: string]: Style;
|
||||||
|
};
|
@ -4,6 +4,7 @@ import { IItemLayer,ItemLayer} from '../models/item.layer';
|
|||||||
import { IMapState} from '../models/map.state';
|
import { IMapState} from '../models/map.state';
|
||||||
import { IQueryState} from '../models/query.state';
|
import { IQueryState} from '../models/query.state';
|
||||||
import { IPeriodState} from '../models/period.state';
|
import { IPeriodState} from '../models/period.state';
|
||||||
|
import { IStyles} from '../models/style.cache';
|
||||||
import * as mapActions from '../actions/map.actions';
|
import * as mapActions from '../actions/map.actions';
|
||||||
import {commonActions} from '@farmmaps/common';
|
import {commonActions} from '@farmmaps/common';
|
||||||
import { createSelector, createFeatureSelector } from '@ngrx/store';
|
import { createSelector, createFeatureSelector } from '@ngrx/store';
|
||||||
@ -51,7 +52,8 @@ export interface State {
|
|||||||
selectedItemLayer: IItemLayer,
|
selectedItemLayer: IItemLayer,
|
||||||
projection: string,
|
projection: string,
|
||||||
selectedBaseLayer: IItemLayer,
|
selectedBaseLayer: IItemLayer,
|
||||||
selectedOverlayLayer: IItemLayer
|
selectedOverlayLayer: IItemLayer,
|
||||||
|
styles:IStyles
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initialState: State = {
|
export const initialState: State = {
|
||||||
@ -84,7 +86,8 @@ export const initialState: State = {
|
|||||||
projection: "EPSG:3857",
|
projection: "EPSG:3857",
|
||||||
selectedBaseLayer: null,
|
selectedBaseLayer: null,
|
||||||
selectedOverlayLayer: null,
|
selectedOverlayLayer: null,
|
||||||
selectedItemLayer: null
|
selectedItemLayer: null,
|
||||||
|
styles: {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function reducer(state = initialState, action: mapActions.Actions | commonActions.Actions | RouterNavigationAction): State {
|
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, {});
|
return tassign(state, {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case mapActions.SETSTYLE:{
|
||||||
|
let a = action as mapActions.SetStyle;
|
||||||
|
let styles = tassign(state.styles);
|
||||||
|
styles[a.itemType] = a.style;
|
||||||
|
return tassign(state,{styles:styles});
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
@ -336,6 +345,7 @@ export const getSelectedOverlayLayer = (state: State) => state.selectedOverlayLa
|
|||||||
export const getQuery = (state: State) => state.query;
|
export const getQuery = (state: State) => state.query;
|
||||||
export const getSelectedItemLayer = (state: State) => state.selectedItemLayer;
|
export const getSelectedItemLayer = (state: State) => state.selectedItemLayer;
|
||||||
export const getPeriod = (state:State) => state.period;
|
export const getPeriod = (state:State) => state.period;
|
||||||
|
export const getStyles = (state:State) => state.styles;
|
||||||
|
|
||||||
export const selectMapState = createFeatureSelector<State>(MODULE_NAME);
|
export const selectMapState = createFeatureSelector<State>(MODULE_NAME);
|
||||||
export const selectGetMapState= createSelector(selectMapState, getMapState);
|
export const selectGetMapState= createSelector(selectMapState, getMapState);
|
||||||
@ -358,5 +368,6 @@ export const selectGetSelectedOverlayLayer = createSelector(selectMapState, getS
|
|||||||
export const selectGetQuery = createSelector(selectMapState, getQuery);
|
export const selectGetQuery = createSelector(selectMapState, getQuery);
|
||||||
export const selectGetSelectedItemLayer = createSelector(selectMapState, getSelectedItemLayer);
|
export const selectGetSelectedItemLayer = createSelector(selectMapState, getSelectedItemLayer);
|
||||||
export const selectGetPeriod = createSelector(selectMapState, getPeriod);
|
export const selectGetPeriod = createSelector(selectMapState, getPeriod);
|
||||||
|
export const selectGetStyles = createSelector(selectMapState, getStyles);
|
||||||
|
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -4,9 +4,7 @@ import {IItem} from '../models/item'
|
|||||||
import {AppConfig} from '../shared/app.config';
|
import {AppConfig} from '../shared/app.config';
|
||||||
import {HttpClient, HttpXhrBackend} from '@angular/common/http';
|
import {HttpClient, HttpXhrBackend} from '@angular/common/http';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable()
|
||||||
providedIn: 'root',
|
|
||||||
})
|
|
||||||
export class ItemTypeService {
|
export class ItemTypeService {
|
||||||
public itemTypes: IItemTypes;
|
public itemTypes: IItemTypes;
|
||||||
private httpClient: HttpClient;
|
private httpClient: HttpClient;
|
||||||
@ -15,10 +13,6 @@ export class ItemTypeService {
|
|||||||
this.httpClient = new HttpClient(xhrBackend);
|
this.httpClient = new HttpClient(xhrBackend);
|
||||||
}
|
}
|
||||||
|
|
||||||
// itemService.getItemTypes().subscribe((itemTypes) => {
|
|
||||||
// this.itemTypes = itemTypes;
|
|
||||||
// });
|
|
||||||
|
|
||||||
getIcon(itemType: string) {
|
getIcon(itemType: string) {
|
||||||
var icon = "fa fa-file-o";
|
var icon = "fa fa-file-o";
|
||||||
if (this.itemTypes[itemType]) icon = this.itemTypes[itemType].icon;
|
if (this.itemTypes[itemType]) icon = this.itemTypes[itemType].icon;
|
||||||
|
@ -21,6 +21,7 @@ import { MenuComponent } from './menu/menu.component';
|
|||||||
import {RegisterDeviceComponent} from './registerdevice/registerdevice.component';
|
import {RegisterDeviceComponent} from './registerdevice/registerdevice.component';
|
||||||
import { SecureOAuthStorage} from '@farmmaps/common';
|
import { SecureOAuthStorage} from '@farmmaps/common';
|
||||||
import { OAuthStorage } from 'angular-oauth2-oidc';
|
import { OAuthStorage } from 'angular-oauth2-oidc';
|
||||||
|
import {Id4AuthconfigFactory} from './id4AuthconfigFactory';
|
||||||
|
|
||||||
export const BOOTSTRAP_EFFECTS = new InjectionToken('Bootstrap Effects');
|
export const BOOTSTRAP_EFFECTS = new InjectionToken('Bootstrap Effects');
|
||||||
|
|
||||||
|
21
src/app/id4AuthconfigFactory.ts
Normal file
21
src/app/id4AuthconfigFactory.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { IAuthconfigFactory, AppConfig } from '@farmmaps/common';
|
||||||
|
import { AuthConfig } from 'angular-oauth2-oidc';
|
||||||
|
|
||||||
|
export class Id4AuthconfigFactory implements IAuthconfigFactory {
|
||||||
|
constructor() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
getAuthConfig(appConfig: AppConfig): AuthConfig {
|
||||||
|
let authConfig: AuthConfig = new AuthConfig();
|
||||||
|
authConfig.issuer = appConfig.getConfig("issuer");
|
||||||
|
authConfig.redirectUri = window.location.origin + "/cb";
|
||||||
|
authConfig.clientId = appConfig.getConfig("clientId");
|
||||||
|
authConfig.customQueryParams = { audience: appConfig.getConfig("audience") };
|
||||||
|
authConfig.scope = "api offline_access";
|
||||||
|
authConfig.disableAtHashCheck = true;
|
||||||
|
authConfig.responseType = "code";
|
||||||
|
authConfig.requireHttps = appConfig.getConfig("requireHttps");
|
||||||
|
return authConfig;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user