2019-10-28 13:18:25 +00:00
|
|
|
import { Component, Host, Input, Output, EventEmitter, Optional, QueryList, OnInit, AfterViewInit, OnChanges, SimpleChanges, SkipSelf, forwardRef, Inject, InjectionToken } from '@angular/core';
|
|
|
|
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, ILayerData, IRenderoutputTiles, IRenderoutputImage, IGradientstop, ILayer, IHistogram } from '../../../models';
|
|
|
|
|
|
|
|
import {Extent} from 'ol/extent';
|
|
|
|
import Projection from 'ol/proj/Projection';
|
|
|
|
import * as proj from 'ol/proj';
|
|
|
|
import * as loadingstrategy from 'ol/loadingstrategy';
|
|
|
|
import * as style from 'ol/style';
|
|
|
|
import {Tile,Layer,Image} from 'ol/layer';
|
|
|
|
import {XYZ,ImageStatic,OSM,BingMaps,TileWMS,TileArcGISRest} from 'ol/source';
|
|
|
|
import {Vector as VectorSource} from 'ol/source';
|
|
|
|
import { Vector as VectorLayer } from 'ol/layer';
|
|
|
|
import VectorTileSource from 'ol/source/VectorTile';
|
|
|
|
import VectorTileLayer from 'ol/layer/VectorTile';
|
|
|
|
import {GeoJSON,MVT} from 'ol/format';
|
|
|
|
import { from } from 'rxjs';
|
|
|
|
|
|
|
|
@Component({
|
2019-11-04 12:34:00 +00:00
|
|
|
selector: 'fm-map-item-layers',
|
2019-10-28 13:18:25 +00:00
|
|
|
template: `<ng-content></ng-content>`,
|
|
|
|
providers: [
|
|
|
|
{ provide: LayerGroupComponent, useExisting: forwardRef(() => ItemLayersComponent) }
|
|
|
|
]
|
|
|
|
})
|
|
|
|
|
|
|
|
export class ItemLayersComponent extends LayerGroupComponent implements OnChanges, OnInit {
|
|
|
|
@Input() itemLayers: IItemLayer[];
|
|
|
|
@Input() itemLayer: IItemLayer;
|
|
|
|
private _apiEndPoint: string;
|
|
|
|
|
|
|
|
constructor(private itemService: ItemService, @Host() private map: MapComponent, public appConfig: AppConfig) {
|
|
|
|
super(map);
|
2019-11-01 14:57:50 +00:00
|
|
|
this._apiEndPoint = appConfig.getConfig("apiEndPoint");
|
2019-10-28 13:18:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private styleCache = {}
|
|
|
|
|
|
|
|
componentToHex(c) {
|
|
|
|
var hex = c.toString(16);
|
|
|
|
return hex.length == 1 ? "0" + hex : hex;
|
|
|
|
}
|
|
|
|
|
|
|
|
rgbaToHex(r, g, b,a) {
|
|
|
|
return "#" + this.componentToHex(r) + this.componentToHex(g) + this.componentToHex(b) + this.componentToHex(a);
|
|
|
|
}
|
|
|
|
|
|
|
|
getColorFromGradient(layer: ILayer, feature): style.Style {
|
|
|
|
var value = feature.get(layer.name);
|
|
|
|
var gradient: IGradientstop[] = layer.renderer.colorMap.gradient;
|
|
|
|
var histogram: IHistogram = layer.renderer.band.histogram;
|
|
|
|
var index = (value - histogram.min) / histogram.max;
|
|
|
|
var min = gradient[0];
|
|
|
|
var max = gradient[gradient.length - 1];
|
|
|
|
for (var n = 0; n < gradient.length; n++) {
|
|
|
|
var s = gradient[n];
|
|
|
|
if (s.relativestop <= index && min.relativestop < s.relativestop && n < gradient.length - 1) min = s;
|
|
|
|
if (s.relativestop >= index && max.relativestop > s.relativestop && n > 0) max = s;
|
|
|
|
}
|
|
|
|
var i = index - min.relativestop;
|
|
|
|
var size = max.relativestop - min.relativestop;
|
|
|
|
var alpha = Math.round( min.color.alpha + ((max.color.alpha - min.color.alpha) * i / size));
|
|
|
|
var red = Math.round(min.color.red + ((max.color.red - min.color.red) * i / size));
|
|
|
|
var green = Math.round(min.color.green + ((max.color.green - min.color.green) * i / size));
|
|
|
|
var blue = Math.round(min.color.blue + ((max.color.blue - min.color.blue) * i / size));
|
|
|
|
|
|
|
|
return new style.Style(
|
|
|
|
{
|
|
|
|
image: new style.Circle({
|
|
|
|
fill: new style.Fill({
|
|
|
|
color: this.rgbaToHex(red,green,blue,alpha)
|
|
|
|
}),
|
|
|
|
radius: 3
|
|
|
|
}),
|
|
|
|
fill: new style.Fill({
|
|
|
|
color: this.rgbaToHex(red, green, blue, alpha)
|
|
|
|
}),
|
|
|
|
stroke: new style.Stroke({
|
|
|
|
color: this.rgbaToHex(red, green, blue, alpha),
|
|
|
|
width: 1.25
|
|
|
|
}),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
createLayer(itemLayer: IItemLayer): Layer {
|
|
|
|
var layer: Layer = null;
|
|
|
|
if (itemLayer.item.itemType == 'vnd.farmmaps.itemtype.geotiff.processed') {
|
|
|
|
let source = new XYZ({ maxZoom: 19, minZoom: 1, url: `${this._apiEndPoint}/api/v1/items/${itemLayer.item.code}/tiles/{z}/{x}/{y}.png?v=${itemLayer.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/{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?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;
|
|
|
|
var 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 == "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"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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
|
|
|
|
}),
|
|
|
|
stroke: new style.Stroke({
|
|
|
|
color: color,
|
|
|
|
width: 1.25
|
|
|
|
}),
|
|
|
|
radius: 5
|
|
|
|
}),
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
return this.styleCache[key];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
} 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (layer) {
|
|
|
|
let geometry = new GeoJSON().readGeometry(itemLayer.item.geometry);
|
|
|
|
let extent = geometry ? proj.transformExtent(geometry.getExtent(), 'EPSG:4326', 'EPSG:3857') : null;
|
|
|
|
if (extent) layer.setExtent(extent);
|
|
|
|
}
|
|
|
|
|
|
|
|
return layer;
|
|
|
|
}
|
|
|
|
|
|
|
|
ngOnInit() {
|
|
|
|
super.ngOnInit();
|
|
|
|
this.updateLayers(this.itemLayers);
|
|
|
|
}
|
|
|
|
|
|
|
|
updateLayers(itemLayers: IItemLayer[]) {
|
|
|
|
if (itemLayers) {
|
|
|
|
var olLayers = this.instance.getLayers();
|
|
|
|
itemLayers.forEach((itemLayer, index) => {
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ngOnChanges(changes: SimpleChanges) {
|
|
|
|
if (this.instance) {
|
|
|
|
if (changes['itemLayers']) {
|
|
|
|
var itemLayers = changes['itemLayers'].currentValue as IItemLayer[];
|
|
|
|
this.updateLayers(itemLayers);
|
|
|
|
}
|
|
|
|
if (changes['itemLayer']) {
|
|
|
|
var itemLayer = changes['itemLayer'].currentValue as IItemLayer;
|
|
|
|
if(itemLayer) {
|
|
|
|
this.updateLayers([itemLayer]);
|
|
|
|
} else {
|
|
|
|
this.updateLayers([]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|