|
|
@ -14,12 +14,12 @@ |
|
|
/// limitations under the License.
|
|
|
/// limitations under the License.
|
|
|
///
|
|
|
///
|
|
|
|
|
|
|
|
|
import L, { LatLngTuple } from 'leaflet'; |
|
|
import L, { LatLngTuple, LatLngBounds, Point } from 'leaflet'; |
|
|
|
|
|
|
|
|
import 'leaflet-providers'; |
|
|
import 'leaflet-providers'; |
|
|
import 'leaflet.markercluster/dist/MarkerCluster.css' |
|
|
import 'leaflet.markercluster/dist/MarkerCluster.css'; |
|
|
import 'leaflet.markercluster/dist/MarkerCluster.Default.css' |
|
|
import 'leaflet.markercluster/dist/MarkerCluster.Default.css'; |
|
|
import 'leaflet.markercluster/dist/leaflet.markercluster' |
|
|
import LM from 'leaflet.markercluster/dist/leaflet.markercluster'; |
|
|
|
|
|
|
|
|
import { MapSettings, MarkerSettings, FormattedData, UnitedMapSettings, PolygonSettings, PolylineSettings } from './map-models'; |
|
|
import { MapSettings, MarkerSettings, FormattedData, UnitedMapSettings, PolygonSettings, PolylineSettings } from './map-models'; |
|
|
import { Marker } from './markers'; |
|
|
import { Marker } from './markers'; |
|
|
@ -34,7 +34,7 @@ export default abstract class LeafletMap { |
|
|
markers: Map<string, Marker> = new Map(); |
|
|
markers: Map<string, Marker> = new Map(); |
|
|
polylines: Map<string, Polyline> = new Map(); |
|
|
polylines: Map<string, Polyline> = new Map(); |
|
|
polygons: Map<string, Polygon> = new Map(); |
|
|
polygons: Map<string, Polygon> = new Map(); |
|
|
dragMode = true; |
|
|
dragMode = false; |
|
|
map: L.Map; |
|
|
map: L.Map; |
|
|
map$: BehaviorSubject<L.Map> = new BehaviorSubject(null); |
|
|
map$: BehaviorSubject<L.Map> = new BehaviorSubject(null); |
|
|
ready$: Observable<L.Map> = this.map$.pipe(filter(map => !!map)); |
|
|
ready$: Observable<L.Map> = this.map$.pipe(filter(map => !!map)); |
|
|
@ -43,6 +43,7 @@ export default abstract class LeafletMap { |
|
|
bounds: L.LatLngBounds; |
|
|
bounds: L.LatLngBounds; |
|
|
newMarker: L.Marker; |
|
|
newMarker: L.Marker; |
|
|
datasources: FormattedData[]; |
|
|
datasources: FormattedData[]; |
|
|
|
|
|
markersCluster: LM.markerClusterGroup; |
|
|
|
|
|
|
|
|
constructor(public $container: HTMLElement, options: UnitedMapSettings) { |
|
|
constructor(public $container: HTMLElement, options: UnitedMapSettings) { |
|
|
this.options = options; |
|
|
this.options = options; |
|
|
@ -50,13 +51,38 @@ export default abstract class LeafletMap { |
|
|
|
|
|
|
|
|
public initSettings(options: MapSettings) { |
|
|
public initSettings(options: MapSettings) { |
|
|
const { initCallback, |
|
|
const { initCallback, |
|
|
disableScrollZooming, }: MapSettings = options; |
|
|
disableScrollZooming, |
|
|
|
|
|
useClusterMarkers, |
|
|
|
|
|
zoomOnClick, |
|
|
|
|
|
showCoverageOnHover, |
|
|
|
|
|
removeOutsideVisibleBounds, |
|
|
|
|
|
animate, |
|
|
|
|
|
chunkedLoading, |
|
|
|
|
|
maxClusterRadius, |
|
|
|
|
|
maxZoom }: MapSettings = options; |
|
|
if (disableScrollZooming) { |
|
|
if (disableScrollZooming) { |
|
|
this.map.scrollWheelZoom.disable(); |
|
|
this.map.scrollWheelZoom.disable(); |
|
|
} |
|
|
} |
|
|
if (initCallback) { |
|
|
if (initCallback) { |
|
|
setTimeout(options.initCallback, 0); |
|
|
setTimeout(options.initCallback, 0); |
|
|
} |
|
|
} |
|
|
|
|
|
if (useClusterMarkers) { |
|
|
|
|
|
const clusteringSettings: LM.MarkerClusterGroupOptions = { |
|
|
|
|
|
zoomToBoundsOnClick: zoomOnClick, |
|
|
|
|
|
showCoverageOnHover, |
|
|
|
|
|
removeOutsideVisibleBounds, |
|
|
|
|
|
animate, |
|
|
|
|
|
chunkedLoading |
|
|
|
|
|
}; |
|
|
|
|
|
if (maxClusterRadius && maxClusterRadius > 0) { |
|
|
|
|
|
clusteringSettings.maxClusterRadius = Math.floor(maxClusterRadius); |
|
|
|
|
|
} |
|
|
|
|
|
if (maxZoom && maxZoom >= 0 && maxZoom < 19) { |
|
|
|
|
|
clusteringSettings.disableClusteringAtZoom = Math.floor(maxZoom); |
|
|
|
|
|
} |
|
|
|
|
|
this.markersCluster = LM.markerClusterGroup(clusteringSettings); |
|
|
|
|
|
this.ready$.subscribe(map => map.addLayer(this.markersCluster)); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
addMarkerControl() { |
|
|
addMarkerControl() { |
|
|
@ -65,7 +91,7 @@ export default abstract class LeafletMap { |
|
|
let addMarker: L.Control; |
|
|
let addMarker: L.Control; |
|
|
this.map.on('mouseup', (e: L.LeafletMouseEvent) => { |
|
|
this.map.on('mouseup', (e: L.LeafletMouseEvent) => { |
|
|
mousePositionOnMap = e.latlng; |
|
|
mousePositionOnMap = e.latlng; |
|
|
}) |
|
|
}); |
|
|
const dragListener = (e: L.DragEndEvent) => { |
|
|
const dragListener = (e: L.DragEndEvent) => { |
|
|
if (e.type === 'dragend' && mousePositionOnMap) { |
|
|
if (e.type === 'dragend' && mousePositionOnMap) { |
|
|
const newMarker = L.marker(mousePositionOnMap).addTo(this.map); |
|
|
const newMarker = L.marker(mousePositionOnMap).addTo(this.map); |
|
|
@ -80,10 +106,17 @@ export default abstract class LeafletMap { |
|
|
this.saveMarkerLocation(updatedEnttity); |
|
|
this.saveMarkerLocation(updatedEnttity); |
|
|
this.map.removeLayer(newMarker); |
|
|
this.map.removeLayer(newMarker); |
|
|
this.deleteMarker(ds.entityName); |
|
|
this.deleteMarker(ds.entityName); |
|
|
this.createMarker(ds.entityName, updatedEnttity, this.datasources, this.options, false); |
|
|
this.createMarker(ds.entityName, updatedEnttity, this.datasources, this.options); |
|
|
} |
|
|
} |
|
|
datasourcesList.append(dsItem); |
|
|
datasourcesList.append(dsItem); |
|
|
}) |
|
|
}); |
|
|
|
|
|
const deleteBtn = document.createElement('a'); |
|
|
|
|
|
deleteBtn.appendChild(document.createTextNode('Delete position')); |
|
|
|
|
|
deleteBtn.setAttribute('color', 'red'); |
|
|
|
|
|
deleteBtn.onclick = () => { |
|
|
|
|
|
this.map.removeLayer(newMarker); |
|
|
|
|
|
} |
|
|
|
|
|
datasourcesList.append(deleteBtn); |
|
|
const popup = L.popup(); |
|
|
const popup = L.popup(); |
|
|
popup.setContent(datasourcesList); |
|
|
popup.setContent(datasourcesList); |
|
|
newMarker.bindPopup(popup).openPopup(); |
|
|
newMarker.bindPopup(popup).openPopup(); |
|
|
@ -96,6 +129,7 @@ export default abstract class LeafletMap { |
|
|
img.src = `assets/add_location.svg`; |
|
|
img.src = `assets/add_location.svg`; |
|
|
img.style.width = '32px'; |
|
|
img.style.width = '32px'; |
|
|
img.style.height = '32px'; |
|
|
img.style.height = '32px'; |
|
|
|
|
|
img.title = 'Drag and drop to add marker'; |
|
|
img.onclick = this.dragMarker; |
|
|
img.onclick = this.dragMarker; |
|
|
img.draggable = true; |
|
|
img.draggable = true; |
|
|
const draggableImg = new L.Draggable(img); |
|
|
const draggableImg = new L.Draggable(img); |
|
|
@ -106,13 +140,10 @@ export default abstract class LeafletMap { |
|
|
onRemove(map) { |
|
|
onRemove(map) { |
|
|
}, |
|
|
}, |
|
|
dragMarker: this.dragMarker |
|
|
dragMarker: this.dragMarker |
|
|
|
|
|
|
|
|
} as any); |
|
|
} as any); |
|
|
|
|
|
|
|
|
L.control.addMarker = (opts) => { |
|
|
L.control.addMarker = (opts) => { |
|
|
return new L.Control.AddMarker(opts); |
|
|
return new L.Control.AddMarker(opts); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
addMarker = L.control.addMarker({ position: 'topright' }).addTo(this.map); |
|
|
addMarker = L.control.addMarker({ position: 'topright' }).addTo(this.map); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@ -164,9 +195,9 @@ export default abstract class LeafletMap { |
|
|
return this.map.getCenter(); |
|
|
return this.map.getCenter(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
fitBounds(bounds, useDefaultZoom = false) { |
|
|
fitBounds(bounds: LatLngBounds, useDefaultZoom = false, padding?: LatLngTuple) { |
|
|
if (bounds.isValid()) { |
|
|
if (bounds.isValid()) { |
|
|
if ((this.options.dontFitMapBounds || useDefaultZoom) && this.options.defaultZoomLevel) { |
|
|
if ((!this.options.fitMapBounds || useDefaultZoom) && this.options.defaultZoomLevel) { |
|
|
this.map.setZoom(this.options.defaultZoomLevel, { animate: false }); |
|
|
this.map.setZoom(this.options.defaultZoomLevel, { animate: false }); |
|
|
this.map.panTo(bounds.getCenter(), { animate: false }); |
|
|
this.map.panTo(bounds.getCenter(), { animate: false }); |
|
|
} else { |
|
|
} else { |
|
|
@ -175,7 +206,7 @@ export default abstract class LeafletMap { |
|
|
this.map.setZoom(this.options.minZoomLevel, { animate: false }); |
|
|
this.map.setZoom(this.options.minZoomLevel, { animate: false }); |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
this.map.fitBounds(bounds, { padding: [50, 50], animate: false }); |
|
|
this.map.fitBounds(bounds, { padding: padding || [50, 50], animate: false }); |
|
|
} |
|
|
} |
|
|
this.bounds = this.bounds.extend(bounds); |
|
|
this.bounds = this.bounds.extend(bounds); |
|
|
} |
|
|
} |
|
|
@ -193,8 +224,8 @@ export default abstract class LeafletMap { |
|
|
|
|
|
|
|
|
convertToCustomFormat(position: L.LatLng): object { |
|
|
convertToCustomFormat(position: L.LatLng): object { |
|
|
return { |
|
|
return { |
|
|
[this.options.latKeyName]: position.lat, |
|
|
[this.options.latKeyName]: position.lat % 180, |
|
|
[this.options.lngKeyName]: position.lng |
|
|
[this.options.lngKeyName]: position.lng % 180 |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -225,11 +256,17 @@ 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, setFocus = true) { |
|
|
private createMarker(key: string, data: FormattedData, dataSources: FormattedData[], settings: MarkerSettings) { |
|
|
this.ready$.subscribe(() => { |
|
|
this.ready$.subscribe(() => { |
|
|
const newMarker = new Marker(this.map, this.convertPosition(data), settings, data, dataSources, () => { }, this.dragMarker); |
|
|
const newMarker = new Marker(this.convertPosition(data), settings, data, dataSources, this.dragMarker); |
|
|
this.fitBounds(this.bounds.extend(newMarker.leafletMarker.getLatLng()), setFocus); |
|
|
this.fitBounds(this.bounds.extend(newMarker.leafletMarker.getLatLng()), settings.draggableMarker && this.markers.size > 2); |
|
|
this.markers.set(key, newMarker); |
|
|
this.markers.set(key, newMarker); |
|
|
|
|
|
if (this.options.useClusterMarkers) { |
|
|
|
|
|
this.markersCluster.addLayer(newMarker.leafletMarker); |
|
|
|
|
|
} |
|
|
|
|
|
else { |
|
|
|
|
|
this.map.addLayer(newMarker.leafletMarker); |
|
|
|
|
|
} |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -242,6 +279,8 @@ export default abstract class LeafletMap { |
|
|
if (settings.showTooltip) { |
|
|
if (settings.showTooltip) { |
|
|
marker.updateMarkerTooltip(data); |
|
|
marker.updateMarkerTooltip(data); |
|
|
} |
|
|
} |
|
|
|
|
|
if (settings.useClusterMarkers) |
|
|
|
|
|
this.markersCluster.refreshClusters() |
|
|
marker.setDataSources(data, dataSources); |
|
|
marker.setDataSources(data, dataSources); |
|
|
marker.updateMarkerIcon(settings); |
|
|
marker.updateMarkerIcon(settings); |
|
|
} |
|
|
} |
|
|
@ -320,7 +359,9 @@ export default abstract class LeafletMap { |
|
|
|
|
|
|
|
|
updatePolygon(key: string, data: LatLngTuple[], dataSources: DatasourceData[], settings: PolygonSettings) { |
|
|
updatePolygon(key: string, data: LatLngTuple[], dataSources: DatasourceData[], settings: PolygonSettings) { |
|
|
this.ready$.subscribe(() => { |
|
|
this.ready$.subscribe(() => { |
|
|
this.polygons.get(key).updatePolygon(data, dataSources, settings); |
|
|
const poly = this.polygons.get(key); |
|
|
|
|
|
poly.updatePolygon(data, dataSources, settings); |
|
|
|
|
|
this.fitBounds(poly.leafletPoly.getBounds()); |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|