|
|
@ -32,8 +32,9 @@ import { BehaviorSubject, Observable } from 'rxjs'; |
|
|
import { filter } from 'rxjs/operators'; |
|
|
import { filter } from 'rxjs/operators'; |
|
|
import { Polyline } from './polyline'; |
|
|
import { Polyline } from './polyline'; |
|
|
import { Polygon } from './polygon'; |
|
|
import { Polygon } from './polygon'; |
|
|
import { createTooltip, safeExecute } from '@home/components/widget/lib/maps/maps-utils'; |
|
|
import { createTooltip, parseArray, parseData, safeExecute } from '@home/components/widget/lib/maps/maps-utils'; |
|
|
import { WidgetContext } from '@home/models/widget-component.models'; |
|
|
import { WidgetContext } from '@home/models/widget-component.models'; |
|
|
|
|
|
import { DatasourceData } from '@shared/models/widget.models'; |
|
|
|
|
|
|
|
|
export default abstract class LeafletMap { |
|
|
export default abstract class LeafletMap { |
|
|
|
|
|
|
|
|
@ -247,32 +248,75 @@ export default abstract class LeafletMap { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Markers
|
|
|
updateData(data: DatasourceData[], drawRoutes: boolean, showPolygon: boolean) { |
|
|
updateMarkers(markersData: FormattedData[], callback?) { |
|
|
this.ready$.subscribe(() => { |
|
|
markersData.filter(mdata => !!this.convertPosition(mdata)).forEach(data => { |
|
|
if (drawRoutes) { |
|
|
|
|
|
this.updatePolylines(parseArray(data), false); |
|
|
|
|
|
} |
|
|
|
|
|
if (showPolygon) { |
|
|
|
|
|
this.updatePolygons(parseData(data), false); |
|
|
|
|
|
} |
|
|
|
|
|
this.updateMarkers(parseData(data), false); |
|
|
|
|
|
this.updateBoundsInternal(drawRoutes, showPolygon); |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private updateBoundsInternal(drawRoutes: boolean, showPolygon: boolean) { |
|
|
|
|
|
this.bounds = new L.LatLngBounds(null, null); |
|
|
|
|
|
if (drawRoutes) { |
|
|
|
|
|
this.polylines.forEach((polyline) => { |
|
|
|
|
|
this.bounds.extend(polyline.leafletPoly.getBounds()); |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
if (showPolygon) { |
|
|
|
|
|
this.polygons.forEach((polygon) => { |
|
|
|
|
|
this.bounds.extend(polygon.leafletPoly.getBounds()); |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
this.markers.forEach((marker) => { |
|
|
|
|
|
this.bounds.extend(marker.leafletMarker.getLatLng()); |
|
|
|
|
|
}); |
|
|
|
|
|
this.fitBounds(this.bounds); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Markers
|
|
|
|
|
|
updateMarkers(markersData: FormattedData[], updateBounds = true, callback?) { |
|
|
|
|
|
const rawMarkers = markersData.filter(mdata => !!this.convertPosition(mdata)); |
|
|
|
|
|
this.ready$.subscribe(() => { |
|
|
|
|
|
const keys: string[] = []; |
|
|
|
|
|
rawMarkers.forEach(data => { |
|
|
if (data.rotationAngle || data.rotationAngle === 0) { |
|
|
if (data.rotationAngle || data.rotationAngle === 0) { |
|
|
const currentImage = this.options.useMarkerImageFunction ? |
|
|
const currentImage = this.options.useMarkerImageFunction ? |
|
|
safeExecute(this.options.markerImageFunction, |
|
|
safeExecute(this.options.markerImageFunction, |
|
|
[data, this.options.markerImages, markersData, data.dsIndex]) : this.options.currentImage; |
|
|
[data, this.options.markerImages, markersData, data.dsIndex]) : this.options.currentImage; |
|
|
const style = currentImage ? 'background-image: url(' + currentImage.url + ');' : ''; |
|
|
const style = currentImage ? 'background-image: url(' + currentImage.url + ');' : ''; |
|
|
this.options.icon = L.divIcon({ |
|
|
this.options.icon = L.divIcon({ |
|
|
html: `<div class="arrow"
|
|
|
html: `<div class="arrow"
|
|
|
style="transform: translate(-10px, -10px) |
|
|
style="transform: translate(-10px, -10px) |
|
|
rotate(${data.rotationAngle}deg); |
|
|
rotate(${data.rotationAngle}deg); |
|
|
${style}"><div>` |
|
|
${style}"><div>` |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} else { |
|
|
else { |
|
|
this.options.icon = null; |
|
|
this.options.icon = null; |
|
|
|
|
|
} |
|
|
} |
|
|
if (this.markers.get(data.entityName)) { |
|
|
if (this.markers.get(data.entityName)) { |
|
|
this.updateMarker(data.entityName, data, markersData, this.options) |
|
|
this.updateMarker(data.entityName, data, markersData, this.options) |
|
|
|
|
|
} else { |
|
|
|
|
|
this.createMarker(data.entityName, data, markersData, this.options as MarkerSettings, updateBounds, callback); |
|
|
} |
|
|
} |
|
|
else { |
|
|
keys.push(data.entityName); |
|
|
this.createMarker(data.entityName, data, markersData, this.options as MarkerSettings, callback); |
|
|
}); |
|
|
|
|
|
const toDelete: string[] = []; |
|
|
|
|
|
this.markers.forEach((v, mKey) => { |
|
|
|
|
|
if (!keys.includes(mKey)) { |
|
|
|
|
|
toDelete.push(mKey); |
|
|
} |
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
toDelete.forEach((key) => { |
|
|
|
|
|
this.deleteMarker(key); |
|
|
|
|
|
}); |
|
|
|
|
|
this.markersData = markersData; |
|
|
}); |
|
|
}); |
|
|
this.markersData = markersData; |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
dragMarker = (e, data = {}) => { |
|
|
dragMarker = (e, data = {}) => { |
|
|
@ -280,21 +324,20 @@ export default abstract class LeafletMap { |
|
|
this.saveMarkerLocation({ ...data, ...this.convertToCustomFormat(e.target._latlng) }); |
|
|
this.saveMarkerLocation({ ...data, ...this.convertToCustomFormat(e.target._latlng) }); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private createMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings, callback?) { |
|
|
private createMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings, |
|
|
this.ready$.subscribe(() => { |
|
|
updateBounds = true, callback?) { |
|
|
const newMarker = new Marker(this.convertPosition(data), settings, data, dataSources, this.dragMarker); |
|
|
const newMarker = new Marker(this.convertPosition(data), settings, data, dataSources, this.dragMarker); |
|
|
if (callback) |
|
|
if (callback) |
|
|
newMarker.leafletMarker.on('click', () => { callback(data, true) }); |
|
|
newMarker.leafletMarker.on('click', () => { callback(data, true) }); |
|
|
if (this.bounds) |
|
|
if (this.bounds && updateBounds) |
|
|
this.fitBounds(this.bounds.extend(newMarker.leafletMarker.getLatLng())); |
|
|
this.fitBounds(this.bounds.extend(newMarker.leafletMarker.getLatLng())); |
|
|
this.markers.set(key, newMarker); |
|
|
this.markers.set(key, newMarker); |
|
|
if (this.options.useClusterMarkers) { |
|
|
if (this.options.useClusterMarkers) { |
|
|
this.markersCluster.addLayer(newMarker.leafletMarker); |
|
|
this.markersCluster.addLayer(newMarker.leafletMarker); |
|
|
} |
|
|
} |
|
|
else { |
|
|
else { |
|
|
this.map.addLayer(newMarker.leafletMarker); |
|
|
this.map.addLayer(newMarker.leafletMarker); |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private updateMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings) { |
|
|
private updateMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings) { |
|
|
@ -306,8 +349,9 @@ export default abstract class LeafletMap { |
|
|
if (settings.showTooltip) { |
|
|
if (settings.showTooltip) { |
|
|
marker.updateMarkerTooltip(data); |
|
|
marker.updateMarkerTooltip(data); |
|
|
} |
|
|
} |
|
|
if (settings.useClusterMarkers) |
|
|
if (settings.useClusterMarkers) { |
|
|
this.markersCluster.refreshClusters() |
|
|
this.markersCluster.refreshClusters() |
|
|
|
|
|
} |
|
|
marker.setDataSources(data, dataSources); |
|
|
marker.setDataSources(data, dataSources); |
|
|
marker.updateMarkerIcon(settings); |
|
|
marker.updateMarkerIcon(settings); |
|
|
} |
|
|
} |
|
|
@ -315,7 +359,11 @@ export default abstract class LeafletMap { |
|
|
deleteMarker(key: string) { |
|
|
deleteMarker(key: string) { |
|
|
let marker = this.markers.get(key)?.leafletMarker; |
|
|
let marker = this.markers.get(key)?.leafletMarker; |
|
|
if (marker) { |
|
|
if (marker) { |
|
|
this.map.removeLayer(marker); |
|
|
if (this.options.useClusterMarkers) { |
|
|
|
|
|
this.markersCluster.removeLayer(marker); |
|
|
|
|
|
} else { |
|
|
|
|
|
this.map.removeLayer(marker); |
|
|
|
|
|
} |
|
|
this.markers.delete(key); |
|
|
this.markers.delete(key); |
|
|
marker = null; |
|
|
marker = null; |
|
|
} |
|
|
} |
|
|
@ -346,41 +394,49 @@ export default abstract class LeafletMap { |
|
|
|
|
|
|
|
|
// Polyline
|
|
|
// Polyline
|
|
|
|
|
|
|
|
|
updatePolylines(polyData: FormattedData[][], data?: FormattedData) { |
|
|
updatePolylines(polyData: FormattedData[][], updateBounds = true, data?: FormattedData) { |
|
|
|
|
|
const keys: string[] = []; |
|
|
polyData.forEach((dataSource: FormattedData[]) => { |
|
|
polyData.forEach((dataSource: FormattedData[]) => { |
|
|
data = data || dataSource[0]; |
|
|
data = data || dataSource[0]; |
|
|
if (dataSource.length && data.entityName === dataSource[0].entityName) { |
|
|
if (dataSource.length && data.entityName === dataSource[0].entityName) { |
|
|
if (this.polylines.get(data.entityName)) { |
|
|
if (this.polylines.get(data.entityName)) { |
|
|
this.updatePolyline(data, dataSource, this.options); |
|
|
this.updatePolyline(data, dataSource, this.options, updateBounds); |
|
|
|
|
|
} else { |
|
|
|
|
|
this.createPolyline(data, dataSource, this.options, updateBounds); |
|
|
} |
|
|
} |
|
|
else { |
|
|
keys.push(data.entityName); |
|
|
this.createPolyline(data, dataSource, this.options); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
else { |
|
|
|
|
|
if (data) |
|
|
|
|
|
this.removePolyline(dataSource[0]?.entityName) |
|
|
|
|
|
} |
|
|
} |
|
|
}) |
|
|
}); |
|
|
|
|
|
const toDelete: string[] = []; |
|
|
|
|
|
this.polylines.forEach((v, mKey) => { |
|
|
|
|
|
if (!keys.includes(mKey)) { |
|
|
|
|
|
toDelete.push(mKey); |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
toDelete.forEach((key) => { |
|
|
|
|
|
this.removePolyline(key); |
|
|
|
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
createPolyline(data: FormattedData, dataSources: FormattedData[], settings: PolylineSettings) { |
|
|
createPolyline(data: FormattedData, dataSources: FormattedData[], settings: PolylineSettings, updateBounds = true) { |
|
|
this.ready$.subscribe(() => { |
|
|
this.ready$.subscribe(() => { |
|
|
const poly = new Polyline(this.map, |
|
|
const poly = new Polyline(this.map, |
|
|
dataSources.map(el => this.convertPosition(el)).filter(el => !!el), data, dataSources, settings); |
|
|
dataSources.map(el => this.convertPosition(el)).filter(el => !!el), data, dataSources, settings); |
|
|
const bounds = poly.leafletPoly.getBounds(); |
|
|
if (updateBounds) { |
|
|
this.fitBounds(bounds); |
|
|
const bounds = poly.leafletPoly.getBounds(); |
|
|
|
|
|
this.fitBounds(bounds); |
|
|
|
|
|
} |
|
|
this.polylines.set(data.entityName, poly); |
|
|
this.polylines.set(data.entityName, poly); |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
updatePolyline(data: FormattedData, dataSources: FormattedData[], settings: PolylineSettings) { |
|
|
updatePolyline(data: FormattedData, dataSources: FormattedData[], settings: PolylineSettings, updateBounds = true) { |
|
|
this.ready$.subscribe(() => { |
|
|
this.ready$.subscribe(() => { |
|
|
const poly = this.polylines.get(data.entityName); |
|
|
const poly = this.polylines.get(data.entityName); |
|
|
const oldBounds = poly.leafletPoly.getBounds(); |
|
|
const oldBounds = poly.leafletPoly.getBounds(); |
|
|
poly.updatePolyline(dataSources.map(el => this.convertPosition(el)).filter(el => !!el), data, dataSources, settings); |
|
|
poly.updatePolyline(dataSources.map(el => this.convertPosition(el)).filter(el => !!el), data, dataSources, settings); |
|
|
const newBounds = poly.leafletPoly.getBounds(); |
|
|
const newBounds = poly.leafletPoly.getBounds(); |
|
|
if (oldBounds.toBBoxString() !== newBounds.toBBoxString()) { |
|
|
if (updateBounds && oldBounds.toBBoxString() !== newBounds.toBBoxString()) { |
|
|
this.fitBounds(newBounds); |
|
|
this.fitBounds(newBounds); |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
@ -399,40 +455,60 @@ export default abstract class LeafletMap { |
|
|
|
|
|
|
|
|
// Polygon
|
|
|
// Polygon
|
|
|
|
|
|
|
|
|
updatePolygons(polyData: FormattedData[]) { |
|
|
updatePolygons(polyData: FormattedData[], updateBounds = true) { |
|
|
|
|
|
const keys: string[] = []; |
|
|
polyData.forEach((data: FormattedData) => { |
|
|
polyData.forEach((data: FormattedData) => { |
|
|
if (data && data.hasOwnProperty(this.options.polygonKeyName)) { |
|
|
if (data && data.hasOwnProperty(this.options.polygonKeyName)) { |
|
|
if (typeof (data[this.options.polygonKeyName]) === 'string') { |
|
|
if (typeof (data[this.options.polygonKeyName]) === 'string') { |
|
|
data[this.options.polygonKeyName] = JSON.parse(data[this.options.polygonKeyName]) as LatLngTuple[]; |
|
|
data[this.options.polygonKeyName] = JSON.parse(data[this.options.polygonKeyName]) as LatLngTuple[]; |
|
|
} |
|
|
} |
|
|
if (this.polygons.get(data.$datasource.entityName)) { |
|
|
if (this.polygons.get(data.entityName)) { |
|
|
this.updatePolygon(data, polyData, this.options); |
|
|
this.updatePolygon(data, polyData, this.options, updateBounds); |
|
|
} |
|
|
} else { |
|
|
else { |
|
|
this.createPolygon(data, polyData, this.options, updateBounds); |
|
|
this.createPolygon(data, polyData, this.options); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
keys.push(data.entityName); |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
const toDelete: string[] = []; |
|
|
|
|
|
this.polygons.forEach((v, mKey) => { |
|
|
|
|
|
if (!keys.includes(mKey)) { |
|
|
|
|
|
toDelete.push(mKey); |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
toDelete.forEach((key) => { |
|
|
|
|
|
this.removePolygon(key); |
|
|
|
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
createPolygon(polyData: FormattedData, dataSources: FormattedData[], settings: PolygonSettings) { |
|
|
createPolygon(polyData: FormattedData, dataSources: FormattedData[], settings: PolygonSettings, updateBounds = true) { |
|
|
this.ready$.subscribe(() => { |
|
|
this.ready$.subscribe(() => { |
|
|
const polygon = new Polygon(this.map, polyData, dataSources, settings); |
|
|
const polygon = new Polygon(this.map, polyData, dataSources, settings); |
|
|
const bounds = polygon.leafletPoly.getBounds(); |
|
|
if (updateBounds) { |
|
|
this.fitBounds(bounds); |
|
|
const bounds = polygon.leafletPoly.getBounds(); |
|
|
this.polygons.set(polyData.$datasource.entityName, polygon); |
|
|
this.fitBounds(bounds); |
|
|
|
|
|
} |
|
|
|
|
|
this.polygons.set(polyData.entityName, polygon); |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
updatePolygon(polyData: FormattedData, dataSources: FormattedData[], settings: PolygonSettings) { |
|
|
updatePolygon(polyData: FormattedData, dataSources: FormattedData[], settings: PolygonSettings, updateBounds = true) { |
|
|
this.ready$.subscribe(() => { |
|
|
this.ready$.subscribe(() => { |
|
|
const poly = this.polygons.get(polyData.entityName); |
|
|
const poly = this.polygons.get(polyData.entityName); |
|
|
const oldBounds = poly.leafletPoly.getBounds(); |
|
|
const oldBounds = poly.leafletPoly.getBounds(); |
|
|
poly.updatePolygon(polyData, dataSources, settings); |
|
|
poly.updatePolygon(polyData, dataSources, settings); |
|
|
const newBounds = poly.leafletPoly.getBounds(); |
|
|
const newBounds = poly.leafletPoly.getBounds(); |
|
|
if (oldBounds.toBBoxString() !== newBounds.toBBoxString()) { |
|
|
if (updateBounds && oldBounds.toBBoxString() !== newBounds.toBBoxString()) { |
|
|
this.fitBounds(newBounds); |
|
|
this.fitBounds(newBounds); |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
removePolygon(name: string) { |
|
|
|
|
|
const poly = this.polygons.get(name); |
|
|
|
|
|
if (poly) { |
|
|
|
|
|
this.map.removeLayer(poly.leafletPoly); |
|
|
|
|
|
this.polygons.delete(name); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|