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:
David Nguyen 2025-10-30 23:01:58 -04:00
parent 2cc110adc3
commit 30cc53f9ad
Signed by: david.nguyen
GPG Key ID: D5FB5A5715829326
8 changed files with 438 additions and 21 deletions

90
package-lock.json generated
View File

@ -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",

View File

@ -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",

View File

@ -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"

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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,

View File

@ -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);

View File

@ -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"