diff --git a/package-lock.json b/package-lock.json index 762aa1e..be5e96a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index ee5eb07..71ad070 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/projects/ngx-open-map-wrapper/package.json b/projects/ngx-open-map-wrapper/package.json index ff93604..e8eb799 100644 --- a/projects/ngx-open-map-wrapper/package.json +++ b/projects/ngx-open-map-wrapper/package.json @@ -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" diff --git a/projects/ngx-open-map-wrapper/src/lib/adapters/leaflet-adapter.ts b/projects/ngx-open-map-wrapper/src/lib/adapters/leaflet-adapter.ts index 51090c7..c567481 100644 --- a/projects/ngx-open-map-wrapper/src/lib/adapters/leaflet-adapter.ts +++ b/projects/ngx-open-map-wrapper/src/lib/adapters/leaflet-adapter.ts @@ -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 { @@ -18,6 +21,7 @@ export class LeafletAdapter implements IMapAdapter { private deliveryCheckMarker?: Marker; private markers: Record = {}; private popup?: Popup; + private routes: Record = {}; 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 { 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 { } } } + + 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 { 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(); } } diff --git a/projects/ngx-open-map-wrapper/src/lib/adapters/libre-adapter.ts b/projects/ngx-open-map-wrapper/src/lib/adapters/libre-adapter.ts index 2885b2e..f5c48ce 100644 --- a/projects/ngx-open-map-wrapper/src/lib/adapters/libre-adapter.ts +++ b/projects/ngx-open-map-wrapper/src/lib/adapters/libre-adapter.ts @@ -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 { private map!: Map; private deliveryCheckMarker?: Marker; private markers: Record = {}; private popup?: Popup; + private routes: Record = {}; + private routeLayers: Record = {}; init(container: HTMLElement, options: MapOptions): void { this.map = new Map({ container, @@ -25,15 +27,24 @@ export class LibreAdapter implements IMapAdapter { 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 { 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 { } } } + + 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 { 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(); } } diff --git a/projects/ngx-open-map-wrapper/src/lib/adapters/map-adapter.interface.ts b/projects/ngx-open-map-wrapper/src/lib/adapters/map-adapter.interface.ts index d2f08ca..78c8cab 100644 --- a/projects/ngx-open-map-wrapper/src/lib/adapters/map-adapter.interface.ts +++ b/projects/ngx-open-map-wrapper/src/lib/adapters/map-adapter.interface.ts @@ -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 { 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; 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, diff --git a/projects/ngx-open-map-wrapper/src/lib/adapters/map-facade.ts b/projects/ngx-open-map-wrapper/src/lib/adapters/map-facade.ts index a4a54b2..7302b1f 100644 --- a/projects/ngx-open-map-wrapper/src/lib/adapters/map-facade.ts +++ b/projects/ngx-open-map-wrapper/src/lib/adapters/map-facade.ts @@ -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 { 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 { 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 { { 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); diff --git a/yarn.lock b/yarn.lock index 79f6725..bcc5577 100644 --- a/yarn.lock +++ b/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"