diff --git a/src/mapper/package.json b/src/mapper/package.json index e1e2839b9..3793f036b 100644 --- a/src/mapper/package.json +++ b/src/mapper/package.json @@ -45,6 +45,7 @@ "@electric-sql/client": "^0.8.0", "@electric-sql/pglite": "^0.2.14", "@hotosm/ui": "0.2.0-b5", + "@maplibre/maplibre-gl-directions": "^0.7.1", "@tiptap/core": "^2.10.3", "@tiptap/pm": "^2.10.3", "@tiptap/starter-kit": "^2.10.3", diff --git a/src/mapper/pnpm-lock.yaml b/src/mapper/pnpm-lock.yaml index 48b13effb..673743a22 100644 --- a/src/mapper/pnpm-lock.yaml +++ b/src/mapper/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: '@hotosm/ui': specifier: 0.2.0-b5 version: 0.2.0-b5(@types/react@18.3.3) + '@maplibre/maplibre-gl-directions': + specifier: ^0.7.1 + version: 0.7.1(@types/geojson@7946.0.14)(maplibre-gl@4.7.1) '@shoelace-style/shoelace': specifier: ^2.15.1 version: 2.18.0(@types/react@18.3.3) @@ -1079,6 +1082,11 @@ packages: resolution: {integrity: sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==} engines: {node: '>=6.0.0'} + '@maplibre/maplibre-gl-directions@0.7.1': + resolution: {integrity: sha512-n2dQkMM1+LO77bnVXXp+rZwD1OVa0UNbP3mgGyDxE6NLFEPU/p+w3G/pxnDfwuapzyHYeevdpYpxrVbwABU4BQ==} + peerDependencies: + maplibre-gl: ^4.0.0 + '@maplibre/maplibre-gl-style-spec@20.4.0': resolution: {integrity: sha512-AzBy3095fTFPjDjmWpR2w6HVRAZJ6hQZUCwk5Plz6EyfnfuQW1odeW5i2Ai47Y6TBA2hQnC+azscjBSALpaWgw==} hasBin: true @@ -1098,6 +1106,11 @@ packages: '@petamoriken/float16@3.9.0': resolution: {integrity: sha512-rYUZ+VFjPHD0NT2JYKj64NxXxrV642IiyaUxxorTEj0S3hT7B5Ixezyc9Fn+XvSk0ETEBp5VWjGIErzh0ug0Xw==} + '@placemarkio/polyline@1.2.0': + resolution: {integrity: sha512-PjXntwUKQFTM/MgXIZHBOtuU2rAkmPgfrIxweOJEf1vyytQJNLDMI4YIRO3LUkt++F4TyAQHjPoRsteYa+gtVQ==} + peerDependencies: + '@types/geojson': '*' + '@playwright/test@1.49.0': resolution: {integrity: sha512-DMulbwQURa8rNIQrf94+jPJQ4FmOVdpE5ZppRNvWVjvhC+6sOeo28r8MgIpQRYouXRtt/FCCXU7zn20jnHR4Qw==} engines: {node: '>=18'} @@ -2830,6 +2843,11 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanoid@5.0.9: + resolution: {integrity: sha512-Aooyr6MXU6HpvvWXKoVoXwKMs/KyVakWwg7xQfv5/S/RIgJMy0Ifa45H9qqYy7pTCszrHzP21Uk4PZq2HpEM8Q==} + engines: {node: ^18 || >=20} + hasBin: true + natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -4723,6 +4741,14 @@ snapshots: '@mapbox/whoots-js@3.1.0': {} + '@maplibre/maplibre-gl-directions@0.7.1(@types/geojson@7946.0.14)(maplibre-gl@4.7.1)': + dependencies: + '@placemarkio/polyline': 1.2.0(@types/geojson@7946.0.14) + maplibre-gl: 4.7.1 + nanoid: 5.0.9 + transitivePeerDependencies: + - '@types/geojson' + '@maplibre/maplibre-gl-style-spec@20.4.0': dependencies: '@mapbox/jsonlint-lines-primitives': 2.0.2 @@ -4748,6 +4774,10 @@ snapshots: '@petamoriken/float16@3.9.0': optional: true + '@placemarkio/polyline@1.2.0(@types/geojson@7946.0.14)': + dependencies: + '@types/geojson': 7946.0.14 + '@playwright/test@1.49.0': dependencies: playwright: 1.49.0 @@ -6655,6 +6685,8 @@ snapshots: nanoid@3.3.8: {} + nanoid@5.0.9: {} + natural-compare@1.4.0: {} node-fetch-native@1.6.4: {} diff --git a/src/mapper/src/assets/images/location.png b/src/mapper/src/assets/images/location.png new file mode 100644 index 000000000..185d0d0ce Binary files /dev/null and b/src/mapper/src/assets/images/location.png differ diff --git a/src/mapper/src/assets/maplibre-directions.ts b/src/mapper/src/assets/maplibre-directions.ts new file mode 100644 index 000000000..8338ffb6e --- /dev/null +++ b/src/mapper/src/assets/maplibre-directions.ts @@ -0,0 +1,68 @@ +// snapline: line from waypoint to road +export const layers = [ + { + id: 'maplibre-gl-directions-snapline', + type: 'line', + source: 'maplibre-gl-directions', + layout: { + 'line-cap': 'round', + 'line-join': 'round', + }, + paint: { + 'line-dasharray': [2, 2], + 'line-color': '#000000', + 'line-opacity': 0.65, + 'line-width': 2, + }, + filter: ['==', ['get', 'type'], 'SNAPLINE'], + }, + + // primary route + { + id: 'maplibre-gl-directions-routeline', + type: 'line', + source: 'maplibre-gl-directions', + layout: { + 'line-cap': 'butt', + 'line-join': 'round', + }, + paint: { + 'line-width': 12, + 'line-opacity': 1, + 'line-color': '#4285f4', + }, + filter: ['==', ['get', 'route'], 'SELECTED'], + }, + + // alternative route + { + id: 'maplibre-gl-directions-alt-routeline', + type: 'line', + source: 'maplibre-gl-directions', + layout: { + 'line-cap': 'butt', + 'line-join': 'round', + }, + paint: { + 'line-width': 12, + 'line-opacity': 0.8, + 'line-color': '#547fff', + }, + filter: ['==', ['get', 'route'], 'ALT'], + }, + + // only apply icon-style to the destination waypoint as we have icon for the origin + { + id: 'maplibre-gl-directions-waypoint', + type: 'symbol', + source: 'maplibre-gl-directions', + layout: { + 'icon-image': 'location', + 'icon-anchor': 'bottom', + 'icon-ignore-placement': true, + 'icon-overlap': 'always', + 'icon-size': 0.5, + }, + filter: ['all', ['==', ['get', 'type'], 'WAYPOINT'], ['==', ['get', 'category'], 'DESTINATION']], + }, +]; diff --git a/src/mapper/src/lib/components/dialog-entities-actions.svelte b/src/mapper/src/lib/components/dialog-entities-actions.svelte index 93701eb5b..fad23fe75 100644 --- a/src/mapper/src/lib/components/dialog-entities-actions.svelte +++ b/src/mapper/src/lib/components/dialog-entities-actions.svelte @@ -19,6 +19,8 @@ const selectedEntity = $derived( entitiesStore.entitiesStatusList?.find((entity) => entity.osmid === selectedEntityOsmId), ); + const selectedEntityCoordinate = $derived(entitiesStore.selectedEntityCoordinate); + const entityToNavigate = $derived(entitiesStore.entityToNavigate); const mapFeature = () => { const xformId = projectData?.odk_form_id; @@ -43,6 +45,13 @@ alertStore.setAlert({ message: 'Requires a mobile phone with ODK Collect.', variant: 'warning' }); } }; + + const navigateToEntity = () => { + if (!entitiesStore.toggleGeolocation) { + entitiesStore.setToggleGeolocation(true); + } + entitiesStore.setEntityToNavigate(selectedEntityCoordinate); + }; {#if isTaskActionModalOpen && selectedTab === 'map' && selectedEntity} @@ -68,7 +77,6 @@