Add custom icon support and routing functionality to map adapters
- Add icon parameter to IMapAdapter.addMarker() supporting DivIcon (Leaflet) and HTMLElement (MapLibre) - Implement routing with leaflet-routing-machine and @maplibre/maplibre-gl-directions - Add marker management methods: hasMarker, updateMarkerIcon, updateMarkerPopup, addMarkerClickHandler - Add route management methods: addRoute, updateRoute, removeRoute, removeAllRoutes - Update MapFacade to proxy all new marker and route management methods - Add RouteOptions interface with customizable line styles and waypoint options 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: David Nguyen <david.nguyen@goutezplanb.com>
This commit is contained in:
parent
2cc110adc3
commit
30cc53f9ad
90
package-lock.json
generated
90
package-lock.json
generated
@ -17,8 +17,10 @@
|
||||
"@angular/platform-server": "^19.2.0",
|
||||
"@angular/router": "^19.2.0",
|
||||
"@angular/ssr": "^19.2.15",
|
||||
"@maplibre/maplibre-gl-directions": "^0.8.0",
|
||||
"express": "^4.18.2",
|
||||
"leaflet": "^2.0.0-alpha.1",
|
||||
"leaflet-routing-machine": "^3.2.12",
|
||||
"maplibre-gl": "^5.7.0",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
@ -31,6 +33,7 @@
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/jasmine": "~5.1.0",
|
||||
"@types/leaflet": "^1.9.20",
|
||||
"@types/leaflet-routing-machine": "^3.2.9",
|
||||
"@types/node": "^18.18.0",
|
||||
"jasmine-core": "~5.6.0",
|
||||
"karma": "~6.4.0",
|
||||
@ -3940,6 +3943,12 @@
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@mapbox/corslite": {
|
||||
"version": "0.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/corslite/-/corslite-0.0.7.tgz",
|
||||
"integrity": "sha512-w/uS474VFjmqQ7fFWIMZINQM1BAQxDLuoJaZZIPES1BmeYpCtlh9MtbFxKGGDAsfvut8/HircIsVvEYRjQ+iMg==",
|
||||
"license": "BSD"
|
||||
},
|
||||
"node_modules/@mapbox/geojson-rewind": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz",
|
||||
@ -3967,6 +3976,17 @@
|
||||
"integrity": "sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@mapbox/polyline": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/polyline/-/polyline-0.2.0.tgz",
|
||||
"integrity": "sha512-GCddO0iw6AzOQqZgBmjEQI9Pgo40/yRgkTkikGctE01kNBN0ThWYuAnTD+hRWrAWMV6QJ0rNm4m8DAsaAXE7Pg==",
|
||||
"bin": {
|
||||
"polyline": "bin/polyline.bin.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/tiny-sdf": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.0.7.tgz",
|
||||
@ -3999,6 +4019,37 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@maplibre/maplibre-gl-directions": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-directions/-/maplibre-gl-directions-0.8.0.tgz",
|
||||
"integrity": "sha512-C0f+Nomj+vVPnTANJaKW7q3I+DqXfQT/huY8h9d22IFK6xEYvfe93ksX/PwNV39wp/PLOqNVcm3yWpQJfK4JVQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@placemarkio/polyline": "^1.2.0",
|
||||
"nanoid": "^5.0.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"maplibre-gl": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@maplibre/maplibre-gl-directions/node_modules/nanoid": {
|
||||
"version": "5.1.6",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz",
|
||||
"integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18 || >=20"
|
||||
}
|
||||
},
|
||||
"node_modules/@maplibre/maplibre-gl-style-spec": {
|
||||
"version": "24.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-24.3.0.tgz",
|
||||
@ -5115,6 +5166,18 @@
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@placemarkio/polyline": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@placemarkio/polyline/-/polyline-1.2.0.tgz",
|
||||
"integrity": "sha512-PjXntwUKQFTM/MgXIZHBOtuU2rAkmPgfrIxweOJEf1vyytQJNLDMI4YIRO3LUkt++F4TyAQHjPoRsteYa+gtVQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/geojson": "*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/geojson": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/plugin-json": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz",
|
||||
@ -5845,6 +5908,16 @@
|
||||
"@types/geojson": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/leaflet-routing-machine": {
|
||||
"version": "3.2.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/leaflet-routing-machine/-/leaflet-routing-machine-3.2.9.tgz",
|
||||
"integrity": "sha512-5c42q5R/8MoZyu7OmeUF8ARg37YLn1abFO3BXXVHvGK3/QEI9VattMYvGbEvjpS9+9MwE4l1iDrdZ+YT9Bs7cg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/leaflet": "^1.9"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/mime": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
|
||||
@ -10270,6 +10343,17 @@
|
||||
"integrity": "sha512-2EJU27z/wljOgQCyybRkfrm5Xc3uy6huKehh0UAPsrAdwnSMxaplsqCl9cXPAuDm6D7uL6PCznYMDVIsaAdSdA==",
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/leaflet-routing-machine": {
|
||||
"version": "3.2.12",
|
||||
"resolved": "https://registry.npmjs.org/leaflet-routing-machine/-/leaflet-routing-machine-3.2.12.tgz",
|
||||
"integrity": "sha512-HLde58G1YtD9xSIzZavJ6BPABZaV1hHeGst8ouhzuxmSC3s32NVtADT+njbIUMW1maHRCrsgTk/E4hz5QH7FrA==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@mapbox/corslite": "0.0.7",
|
||||
"@mapbox/polyline": "^0.2.0",
|
||||
"osrm-text-instructions": "^0.13.2"
|
||||
}
|
||||
},
|
||||
"node_modules/less": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/less/-/less-4.2.2.tgz",
|
||||
@ -11939,6 +12023,12 @@
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/osrm-text-instructions": {
|
||||
"version": "0.13.4",
|
||||
"resolved": "https://registry.npmjs.org/osrm-text-instructions/-/osrm-text-instructions-0.13.4.tgz",
|
||||
"integrity": "sha512-ge4ZTIetMQKAHKq2MwWf83ntzdJN20ndRKRaVNoZ3SkDkBNO99Qddz7r6+hrVx38I+ih6Rk5T1yslczAB6Q9Pg==",
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/p-limit": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz",
|
||||
|
||||
@ -20,8 +20,10 @@
|
||||
"@angular/platform-server": "^19.2.0",
|
||||
"@angular/router": "^19.2.0",
|
||||
"@angular/ssr": "^19.2.15",
|
||||
"@maplibre/maplibre-gl-directions": "^0.8.0",
|
||||
"express": "^4.18.2",
|
||||
"leaflet": "^2.0.0-alpha.1",
|
||||
"leaflet-routing-machine": "^3.2.12",
|
||||
"maplibre-gl": "^5.7.0",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
@ -34,6 +36,7 @@
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/jasmine": "~5.1.0",
|
||||
"@types/leaflet": "^1.9.20",
|
||||
"@types/leaflet-routing-machine": "^3.2.9",
|
||||
"@types/node": "^18.18.0",
|
||||
"jasmine-core": "~5.6.0",
|
||||
"karma": "~6.4.0",
|
||||
|
||||
@ -20,7 +20,9 @@
|
||||
"@angular/common": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"@angular/core": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"maplibre-gl": "^5.0.0",
|
||||
"leaflet": "^1.0.0 || ^2.0.0"
|
||||
"leaflet": "^1.0.0 || ^2.0.0",
|
||||
"leaflet-routing-machine": "^3.0.0",
|
||||
"@maplibre/maplibre-gl-directions": "^0.8.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
|
||||
@ -1,16 +1,19 @@
|
||||
import {
|
||||
import L,{
|
||||
Map,
|
||||
TileLayer,
|
||||
Marker,
|
||||
Popup,
|
||||
polygon
|
||||
polygon, DivIcon
|
||||
} from 'leaflet';
|
||||
import 'leaflet-routing-machine';
|
||||
|
||||
import {
|
||||
getLngLat,
|
||||
IMapAdapter,
|
||||
LatLng,
|
||||
MapOptions, Zone,
|
||||
MapOptions,
|
||||
Zone,
|
||||
RouteOptions,
|
||||
} from './map-adapter.interface';
|
||||
|
||||
export class LeafletAdapter implements IMapAdapter<Marker> {
|
||||
@ -18,6 +21,7 @@ export class LeafletAdapter implements IMapAdapter<Marker> {
|
||||
private deliveryCheckMarker?: Marker;
|
||||
private markers: Record<string, Marker> = {};
|
||||
private popup?: Popup;
|
||||
private routes: Record<string, any> = {};
|
||||
|
||||
init(container: HTMLElement, options: MapOptions): void {
|
||||
this.map = new Map(container).setView(options.center, options.zoom);
|
||||
@ -35,22 +39,26 @@ export class LeafletAdapter implements IMapAdapter<Marker> {
|
||||
this.map.setZoom(zoom);
|
||||
}
|
||||
|
||||
addMarker(latLng: LatLng, options: { id?: string; color?: string }) {
|
||||
if (options.id) {
|
||||
addMarker(latLng: LatLng, options?: { id?: string; color?: string ; icon? : DivIcon }) {
|
||||
const markerOptions: L.MarkerOptions = {};
|
||||
if (options?.icon) {
|
||||
markerOptions.icon = options.icon;
|
||||
}
|
||||
|
||||
if (options?.id) {
|
||||
if (this.markers[options.id]) {
|
||||
this.map.removeLayer(this.markers[options.id]);
|
||||
}
|
||||
const marker = new Marker(latLng)
|
||||
const marker = new Marker(latLng, markerOptions);
|
||||
marker.addTo(this.map);
|
||||
this.markers[options.id] = marker;
|
||||
} else {
|
||||
if (this.deliveryCheckMarker) {
|
||||
this.map.removeLayer(this.deliveryCheckMarker);
|
||||
}
|
||||
this.deliveryCheckMarker = new Marker(latLng).addTo(this.map);
|
||||
this.deliveryCheckMarker = new Marker(latLng, markerOptions).addTo(this.map);
|
||||
}
|
||||
}
|
||||
|
||||
getMarker(id: string): Marker | undefined {
|
||||
return this.markers[id];
|
||||
}
|
||||
@ -72,6 +80,31 @@ export class LeafletAdapter implements IMapAdapter<Marker> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hasMarker(id: string): boolean {
|
||||
return !!this.markers[id];
|
||||
}
|
||||
|
||||
updateMarkerIcon(id: string, icon: any): void {
|
||||
const marker = this.markers[id];
|
||||
if (marker) {
|
||||
marker.setIcon(icon);
|
||||
}
|
||||
}
|
||||
|
||||
updateMarkerPopup(id: string, popup: any): void {
|
||||
const marker = this.markers[id];
|
||||
if (marker) {
|
||||
marker.bindPopup(popup);
|
||||
}
|
||||
}
|
||||
|
||||
addMarkerClickHandler(id: string, handler: (e: any) => void): void {
|
||||
const marker = this.markers[id];
|
||||
if (marker) {
|
||||
marker.on('click', handler);
|
||||
}
|
||||
}
|
||||
addZone(zones: Zone[]): void
|
||||
{
|
||||
this.updateZone(zones);
|
||||
@ -128,12 +161,67 @@ export class LeafletAdapter implements IMapAdapter<Marker> {
|
||||
this.popup = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
addRoute(id: string, waypoints: LatLng[], options?: RouteOptions): void {
|
||||
this.removeRoute(id);
|
||||
|
||||
const routeOptions: any = {
|
||||
waypoints: waypoints.map(wp => L.latLng(wp[0], wp[1])),
|
||||
lineOptions: {
|
||||
styles: [
|
||||
{
|
||||
color: options?.lineStyle?.color || '#3388ff',
|
||||
opacity: options?.lineStyle?.opacity ?? 1,
|
||||
weight: options?.lineStyle?.weight || 4
|
||||
}
|
||||
],
|
||||
extendToWaypoints: false,
|
||||
addWaypoints: false,
|
||||
missingRouteTolerance: 10
|
||||
},
|
||||
createMarker: options?.showMarkers ? undefined : () => false,
|
||||
draggableWaypoints: options?.draggableWaypoints ?? false,
|
||||
routeWhileDragging: false,
|
||||
summaryTemplate: '',
|
||||
distanceTemplate: '',
|
||||
timeTemplate: '',
|
||||
show: true
|
||||
};
|
||||
|
||||
if (options?.serviceUrl) {
|
||||
console.log('serviceUrl', options);
|
||||
routeOptions.router = L.Routing.osrmv1({
|
||||
serviceUrl: options.serviceUrl
|
||||
});
|
||||
}
|
||||
|
||||
const control = L.Routing
|
||||
.control(routeOptions)
|
||||
.addTo(this.map);
|
||||
this.routes[id] = control;
|
||||
}
|
||||
|
||||
updateRoute(id: string, waypoints: LatLng[], options?: RouteOptions): void {
|
||||
this.addRoute(id, waypoints, options);
|
||||
}
|
||||
|
||||
removeRoute(id: string): void {
|
||||
if (this.routes[id]) {
|
||||
this.routes[id].remove();
|
||||
delete this.routes[id];
|
||||
}
|
||||
}
|
||||
|
||||
removeAllRoutes(): void {
|
||||
Object.keys(this.routes).forEach(id => this.removeRoute(id));
|
||||
}
|
||||
|
||||
on(type: string, event: (e: any) => void): void
|
||||
{
|
||||
this.map.on(type, event);
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
this.removeAllRoutes();
|
||||
this.map.remove();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
import { Map, Marker, NavigationControl, GeoJSONSource, Popup } from 'maplibre-gl';
|
||||
import {getLngLat, IMapAdapter, LatLng, MapOptions, Zone} from './map-adapter.interface';
|
||||
|
||||
import {getLngLat, IMapAdapter, LatLng, MapOptions, Zone, RouteOptions} from './map-adapter.interface';
|
||||
import MapLibreGlDirections from "@maplibre/maplibre-gl-directions";
|
||||
export class LibreAdapter implements IMapAdapter<Marker> {
|
||||
private map!: Map;
|
||||
private deliveryCheckMarker?: Marker;
|
||||
private markers: Record<string, Marker> = {};
|
||||
private popup?: Popup;
|
||||
private routes: Record<string, any> = {};
|
||||
private routeLayers: Record<string, [string, string]> = {};
|
||||
init(container: HTMLElement, options: MapOptions): void {
|
||||
this.map = new Map({
|
||||
container,
|
||||
@ -25,15 +27,24 @@ export class LibreAdapter implements IMapAdapter<Marker> {
|
||||
this.map.setZoom(zoom);
|
||||
}
|
||||
|
||||
addMarker(latLng: LatLng, options: { id?: string; color?: string }) {
|
||||
addMarker(latLng: LatLng, options?: { id?: string; color?: string; icon?: HTMLElement }) {
|
||||
const coords = getLngLat(latLng);
|
||||
|
||||
if (options.id) {
|
||||
const markerOptions: any = {};
|
||||
if (options?.icon) {
|
||||
markerOptions.element = options.icon;
|
||||
} else if (options?.color) {
|
||||
markerOptions.color = options.color;
|
||||
} else {
|
||||
markerOptions.color = 'red';
|
||||
}
|
||||
|
||||
if (options?.id) {
|
||||
if (this.markers[options.id]) {
|
||||
this.markers[options.id].remove();
|
||||
}
|
||||
|
||||
const marker = new Marker({ color: options.color || 'red' })
|
||||
const marker = new Marker(markerOptions)
|
||||
.setLngLat(coords)
|
||||
.addTo(this.map);
|
||||
this.markers[options.id] = marker;
|
||||
@ -41,7 +52,7 @@ export class LibreAdapter implements IMapAdapter<Marker> {
|
||||
if (this.deliveryCheckMarker) {
|
||||
this.deliveryCheckMarker.remove();
|
||||
}
|
||||
this.deliveryCheckMarker = new Marker({ color: options?.color || 'red' })
|
||||
this.deliveryCheckMarker = new Marker(markerOptions)
|
||||
.setLngLat(coords)
|
||||
.addTo(this.map);
|
||||
}
|
||||
@ -71,6 +82,31 @@ export class LibreAdapter implements IMapAdapter<Marker> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hasMarker(id: string): boolean {
|
||||
return !!this.markers[id];
|
||||
}
|
||||
|
||||
updateMarkerIcon(id: string, icon: HTMLElement): void {
|
||||
const marker = this.markers[id];
|
||||
if (marker) {
|
||||
marker.getElement().replaceWith(icon);
|
||||
}
|
||||
}
|
||||
|
||||
updateMarkerPopup(id: string, popup: Popup): void {
|
||||
const marker = this.markers[id];
|
||||
if (marker) {
|
||||
marker.setPopup(popup);
|
||||
}
|
||||
}
|
||||
|
||||
addMarkerClickHandler(id: string, handler: (e: any) => void): void {
|
||||
const marker = this.markers[id];
|
||||
if (marker) {
|
||||
marker.getElement().addEventListener('click', handler);
|
||||
}
|
||||
}
|
||||
addZone(zones: Zone[]): void
|
||||
{
|
||||
this.updateZone(zones);
|
||||
@ -166,11 +202,95 @@ export class LibreAdapter implements IMapAdapter<Marker> {
|
||||
this.popup = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
addRoute(id: string, waypoints: LatLng[], options?: RouteOptions): void {
|
||||
this.removeRoute(id);
|
||||
|
||||
// Convert waypoints to GeoJSON LineString format [lng, lat]
|
||||
const coordinates = waypoints.map(wp => getLngLat(wp));
|
||||
|
||||
const sourceId = `route-${id}`;
|
||||
const layerId = `route-layer-${id}`;
|
||||
|
||||
// Add the route as a GeoJSON source
|
||||
this.map.addSource(sourceId, {
|
||||
type: 'geojson',
|
||||
data: {
|
||||
type: 'Feature',
|
||||
properties: {},
|
||||
geometry: {
|
||||
type: 'LineString',
|
||||
coordinates: coordinates
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add the route layer
|
||||
this.map.addLayer({
|
||||
id: layerId,
|
||||
type: 'line',
|
||||
source: sourceId,
|
||||
layout: {
|
||||
'line-join': 'round',
|
||||
'line-cap': 'round'
|
||||
},
|
||||
paint: {
|
||||
'line-color': options?.lineStyle?.color || '#3388ff',
|
||||
'line-width': options?.lineStyle?.weight || 4,
|
||||
'line-opacity': options?.lineStyle?.opacity ?? 1
|
||||
}
|
||||
});
|
||||
|
||||
this.routeLayers[id] = [sourceId, layerId];
|
||||
|
||||
// Optionally add markers for waypoints
|
||||
if (options?.showMarkers) {
|
||||
waypoints.forEach((wp, index) => {
|
||||
const markerId = `${id}-waypoint-${index}`;
|
||||
this.addMarker(wp, { id: markerId, color: options.lineStyle?.color || '#3388ff' });
|
||||
this.routes[markerId] = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
updateRoute(id: string, waypoints: LatLng[], options?: RouteOptions): void {
|
||||
this.addRoute(id, waypoints, options);
|
||||
}
|
||||
|
||||
removeRoute(id: string): void {
|
||||
if (this.routeLayers[id]) {
|
||||
const [sourceId, layerId] = this.routeLayers[id];
|
||||
|
||||
if (this.map.getLayer(layerId)) {
|
||||
this.map.removeLayer(layerId);
|
||||
}
|
||||
|
||||
if (this.map.getSource(sourceId)) {
|
||||
this.map.removeSource(sourceId);
|
||||
}
|
||||
|
||||
delete this.routeLayers[id];
|
||||
}
|
||||
|
||||
// Remove waypoint markers if they exist
|
||||
Object.keys(this.routes).forEach(key => {
|
||||
if (key.startsWith(`${id}-waypoint-`)) {
|
||||
this.removeMarker(key);
|
||||
delete this.routes[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
removeAllRoutes(): void {
|
||||
Object.keys(this.routeLayers).forEach(id => this.removeRoute(id));
|
||||
}
|
||||
|
||||
on(type: string, event: (e: any) => void): void
|
||||
{
|
||||
this.map.on(type, event);
|
||||
}
|
||||
destroy(): void {
|
||||
this.removeAllRoutes();
|
||||
this.map.remove();
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,18 +24,47 @@ export function getLngLat(latLng: LatLng): [number, number] {
|
||||
return [latLng[1], latLng[0]];
|
||||
}
|
||||
|
||||
export interface RouteLineStyle {
|
||||
color?: string;
|
||||
opacity?: number;
|
||||
weight?: number;
|
||||
}
|
||||
|
||||
export interface RouteOptions {
|
||||
serviceUrl?: string;
|
||||
lineStyle?: RouteLineStyle;
|
||||
showMarkers?: boolean;
|
||||
draggableWaypoints?: boolean;
|
||||
}
|
||||
|
||||
export interface IMapAdapter<TMarker = any> {
|
||||
init(container: HTMLElement, options: MapOptions): void;
|
||||
setCenter(latLng: LatLng): void;
|
||||
setZoom(zoom: number): void;
|
||||
addMarker(latLng: LatLng, options?: { color?: string }): void;
|
||||
|
||||
// Marker management
|
||||
addMarker(latLng: LatLng, options?: {id?: string; color?: string; icon?: any}): void;
|
||||
getMarker(id: string): TMarker | undefined;
|
||||
getAllMarkers(): Record<string, TMarker>;
|
||||
removeMarker(id?: string): void;
|
||||
hasMarker(id: string): boolean;
|
||||
updateMarkerIcon(id: string, icon: any): void;
|
||||
updateMarkerPopup(id: string, popup: any): void;
|
||||
addMarkerClickHandler(id: string, handler: (e: any) => void): void;
|
||||
|
||||
// Zone management
|
||||
addZone(zone: Zone[]): void;
|
||||
updateZone(zone: Zone[]): void;
|
||||
openZonePopup(zone: Zone) : void
|
||||
closePopup(): void;
|
||||
|
||||
// Route management
|
||||
addRoute(id: string, waypoints: LatLng[], options?: RouteOptions): void;
|
||||
updateRoute(id: string, waypoints: LatLng[], options?: RouteOptions): void;
|
||||
removeRoute(id: string): void;
|
||||
removeAllRoutes(): void;
|
||||
|
||||
// Utilities
|
||||
destroy(): void;
|
||||
on(
|
||||
type: string,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import {IMapAdapter, MapOptions, LatLng, getLngLat, Zone} from './map-adapter.interface';
|
||||
import {IMapAdapter, MapOptions, LatLng, getLngLat, Zone, RouteOptions} from './map-adapter.interface';
|
||||
import { LibreAdapter } from './libre-adapter';
|
||||
import { LeafletAdapter } from './leaflet-adapter';
|
||||
|
||||
@ -30,7 +30,7 @@ export class MapFacade implements IMapAdapter<any> {
|
||||
this.adapter.setZoom(zoom);
|
||||
}
|
||||
|
||||
addMarker(latLng: LatLng, options: { id?: string; color?: string }): void {
|
||||
addMarker(latLng: LatLng, options: { id?: string; color?: string; icon?: any}): void {
|
||||
this.adapter.addMarker(latLng, options);
|
||||
}
|
||||
|
||||
@ -45,6 +45,23 @@ export class MapFacade implements IMapAdapter<any> {
|
||||
removeMarker(id?: string): void {
|
||||
this.adapter.removeMarker(id);
|
||||
}
|
||||
|
||||
hasMarker(id: string): boolean {
|
||||
return this.adapter.hasMarker(id);
|
||||
}
|
||||
|
||||
updateMarkerIcon(id: string, icon: any): void {
|
||||
this.adapter.updateMarkerIcon(id, icon);
|
||||
}
|
||||
|
||||
updateMarkerPopup(id: string, popup: any): void {
|
||||
this.adapter.updateMarkerPopup(id, popup);
|
||||
}
|
||||
|
||||
addMarkerClickHandler(id: string, handler: (e: any) => void): void {
|
||||
this.adapter.addMarkerClickHandler(id, handler);
|
||||
}
|
||||
|
||||
addZone(zones: Zone[]): void
|
||||
{
|
||||
this.adapter.addZone(zones);
|
||||
@ -61,6 +78,23 @@ export class MapFacade implements IMapAdapter<any> {
|
||||
{
|
||||
this.adapter.closePopup();
|
||||
}
|
||||
|
||||
addRoute(id: string, waypoints: LatLng[], options?: RouteOptions): void {
|
||||
this.adapter.addRoute(id, waypoints, options);
|
||||
}
|
||||
|
||||
updateRoute(id: string, waypoints: LatLng[], options?: RouteOptions): void {
|
||||
this.adapter.updateRoute(id, waypoints, options);
|
||||
}
|
||||
|
||||
removeRoute(id: string): void {
|
||||
this.adapter.removeRoute(id);
|
||||
}
|
||||
|
||||
removeAllRoutes(): void {
|
||||
this.adapter.removeAllRoutes();
|
||||
}
|
||||
|
||||
on(type: string, event: (e: any) => void): void
|
||||
{
|
||||
this.adapter.on(type, event);
|
||||
|
||||
55
yarn.lock
55
yarn.lock
@ -1524,6 +1524,11 @@
|
||||
resolved "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.2.6.tgz"
|
||||
integrity sha512-XlqVtILonQnG+9fH2N3Aytria7P/1fwDgDhl29rde96uH2sLB8CHORIf2PfuLVzFQJ7Uqp8py9AYwr3ZUCFfWg==
|
||||
|
||||
"@mapbox/corslite@0.0.7":
|
||||
version "0.0.7"
|
||||
resolved "https://registry.npmjs.org/@mapbox/corslite/-/corslite-0.0.7.tgz"
|
||||
integrity sha512-w/uS474VFjmqQ7fFWIMZINQM1BAQxDLuoJaZZIPES1BmeYpCtlh9MtbFxKGGDAsfvut8/HircIsVvEYRjQ+iMg==
|
||||
|
||||
"@mapbox/geojson-rewind@^0.5.2":
|
||||
version "0.5.2"
|
||||
resolved "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz"
|
||||
@ -1542,6 +1547,11 @@
|
||||
resolved "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-1.1.0.tgz"
|
||||
integrity sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ==
|
||||
|
||||
"@mapbox/polyline@^0.2.0":
|
||||
version "0.2.0"
|
||||
resolved "https://registry.npmjs.org/@mapbox/polyline/-/polyline-0.2.0.tgz"
|
||||
integrity sha512-GCddO0iw6AzOQqZgBmjEQI9Pgo40/yRgkTkikGctE01kNBN0ThWYuAnTD+hRWrAWMV6QJ0rNm4m8DAsaAXE7Pg==
|
||||
|
||||
"@mapbox/tiny-sdf@^2.0.7":
|
||||
version "2.0.7"
|
||||
resolved "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.0.7.tgz"
|
||||
@ -1566,6 +1576,14 @@
|
||||
resolved "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz"
|
||||
integrity sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==
|
||||
|
||||
"@maplibre/maplibre-gl-directions@^0.8.0":
|
||||
version "0.8.0"
|
||||
resolved "https://registry.npmjs.org/@maplibre/maplibre-gl-directions/-/maplibre-gl-directions-0.8.0.tgz"
|
||||
integrity sha512-C0f+Nomj+vVPnTANJaKW7q3I+DqXfQT/huY8h9d22IFK6xEYvfe93ksX/PwNV39wp/PLOqNVcm3yWpQJfK4JVQ==
|
||||
dependencies:
|
||||
"@placemarkio/polyline" "^1.2.0"
|
||||
nanoid "^5.0.6"
|
||||
|
||||
"@maplibre/maplibre-gl-style-spec@^24.3.0":
|
||||
version "24.3.0"
|
||||
resolved "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-24.3.0.tgz"
|
||||
@ -1932,6 +1950,13 @@
|
||||
resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz"
|
||||
integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
|
||||
|
||||
"@placemarkio/polyline@^1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.npmjs.org/@placemarkio/polyline/-/polyline-1.2.0.tgz"
|
||||
integrity sha512-PjXntwUKQFTM/MgXIZHBOtuU2rAkmPgfrIxweOJEf1vyytQJNLDMI4YIRO3LUkt++F4TyAQHjPoRsteYa+gtVQ==
|
||||
dependencies:
|
||||
"@types/geojson" "*"
|
||||
|
||||
"@rollup/plugin-json@^6.1.0":
|
||||
version "6.1.0"
|
||||
resolved "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz"
|
||||
@ -2272,7 +2297,14 @@
|
||||
resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz"
|
||||
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
|
||||
|
||||
"@types/leaflet@^1.9.20":
|
||||
"@types/leaflet-routing-machine@^3.2.9":
|
||||
version "3.2.9"
|
||||
resolved "https://registry.npmjs.org/@types/leaflet-routing-machine/-/leaflet-routing-machine-3.2.9.tgz"
|
||||
integrity sha512-5c42q5R/8MoZyu7OmeUF8ARg37YLn1abFO3BXXVHvGK3/QEI9VattMYvGbEvjpS9+9MwE4l1iDrdZ+YT9Bs7cg==
|
||||
dependencies:
|
||||
"@types/leaflet" "^1.9"
|
||||
|
||||
"@types/leaflet@^1.9", "@types/leaflet@^1.9.20":
|
||||
version "1.9.21"
|
||||
resolved "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.21.tgz"
|
||||
integrity sha512-TbAd9DaPGSnzp6QvtYngntMZgcRk+igFELwR2N99XZn7RXUdKgsXMR+28bUO0rPsWp8MIu/f47luLIQuSLYv/w==
|
||||
@ -4637,6 +4669,15 @@ launch-editor@^2.6.1:
|
||||
picocolors "^1.1.1"
|
||||
shell-quote "^1.8.3"
|
||||
|
||||
leaflet-routing-machine@^3.2.12:
|
||||
version "3.2.12"
|
||||
resolved "https://registry.npmjs.org/leaflet-routing-machine/-/leaflet-routing-machine-3.2.12.tgz"
|
||||
integrity sha512-HLde58G1YtD9xSIzZavJ6BPABZaV1hHeGst8ouhzuxmSC3s32NVtADT+njbIUMW1maHRCrsgTk/E4hz5QH7FrA==
|
||||
dependencies:
|
||||
"@mapbox/corslite" "0.0.7"
|
||||
"@mapbox/polyline" "^0.2.0"
|
||||
osrm-text-instructions "^0.13.2"
|
||||
|
||||
leaflet@^2.0.0-alpha.1:
|
||||
version "2.0.0-alpha.1"
|
||||
resolved "https://registry.npmjs.org/leaflet/-/leaflet-2.0.0-alpha.1.tgz"
|
||||
@ -4842,7 +4883,7 @@ make-fetch-happen@^14.0.0, make-fetch-happen@^14.0.2, make-fetch-happen@^14.0.3:
|
||||
promise-retry "^2.0.1"
|
||||
ssri "^12.0.0"
|
||||
|
||||
maplibre-gl@^5.7.0:
|
||||
maplibre-gl@^5.0.0, maplibre-gl@^5.7.0:
|
||||
version "5.10.0"
|
||||
resolved "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.10.0.tgz"
|
||||
integrity sha512-eLhlX8Fnpaoo7+uGqggZmXmZld6WrbzOJUPB7G8JB8XpminlTnrQTtXilMHduR8fsNVxrzD8yRRqEoajONc8LQ==
|
||||
@ -5150,6 +5191,11 @@ nanoid@^3.3.11, nanoid@^3.3.8:
|
||||
resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz"
|
||||
integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==
|
||||
|
||||
nanoid@^5.0.6:
|
||||
version "5.1.6"
|
||||
resolved "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz"
|
||||
integrity sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==
|
||||
|
||||
needle@^3.1.0:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz"
|
||||
@ -5418,6 +5464,11 @@ ordered-binary@^1.5.3:
|
||||
resolved "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.6.0.tgz"
|
||||
integrity sha512-IQh2aMfMIDbPjI/8a3Edr+PiOpcsB7yo8NdW7aHWVaoR/pcDldunMvnnwbk/auPGqmKeAdxtZl7MHX/QmPwhvQ==
|
||||
|
||||
osrm-text-instructions@^0.13.2:
|
||||
version "0.13.4"
|
||||
resolved "https://registry.npmjs.org/osrm-text-instructions/-/osrm-text-instructions-0.13.4.tgz"
|
||||
integrity sha512-ge4ZTIetMQKAHKq2MwWf83ntzdJN20ndRKRaVNoZ3SkDkBNO99Qddz7r6+hrVx38I+ih6Rk5T1yslczAB6Q9Pg==
|
||||
|
||||
p-limit@^2.2.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user