Added subpar car-first routing
This commit is contained in:
@@ -10,11 +10,14 @@ defineProps<{
|
||||
maxClusterRadius: number
|
||||
disableClusteringAtZoom: number
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{ nudgeGeolocation: [] }>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<LMarkerClusterGroup v-if="parkings.length > 0" :visible :max-cluster-radius :disable-clustering-at-zoom>
|
||||
<BikeMarker v-once v-for="park in parkings" :key="park.id_parc_velo" :park :group />
|
||||
<BikeMarker @nudge-geolocation="emit('nudgeGeolocation')" v-once v-for="park in parkings" :key="park.id_parc_velo"
|
||||
:park :group />
|
||||
</LMarkerClusterGroup>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -10,12 +10,13 @@ import type { ApiResponse } from '@/types/Api'
|
||||
import type { BikeParking } from '@/types/Bikes'
|
||||
import { API_BASE_URL, API_LIMIT, MAP_TILELAYER_URL, SpotAccess, SpotType } from '@/utils/const'
|
||||
import { useQuery } from '@pinia/colada'
|
||||
import { LControlZoom, LIcon, LMap, LMarker, LTileLayer } from '@vue-leaflet/vue-leaflet'
|
||||
import { LControlZoom, LGeoJson, LIcon, LMap, LMarker, LTileLayer } from '@vue-leaflet/vue-leaflet'
|
||||
import BikeFilters from './BikeFilters.vue'
|
||||
import { computed, ref, useTemplateRef } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import BikeClusterLayer from './BikeClusterLayer.vue'
|
||||
import { PhCircleNotch, PhMapPin } from '@phosphor-icons/vue'
|
||||
import { useRouting } from '@/composables/useRouting'
|
||||
|
||||
// Map setup
|
||||
const mapRef = useTemplateRef<{ leafletObject: Map }>('map')
|
||||
@@ -91,6 +92,11 @@ function handleClickGeoloc() {
|
||||
mapRef.value?.leafletObject.flyTo([userCoords.value.latitude, userCoords.value.longitude], 12)
|
||||
}
|
||||
}
|
||||
|
||||
// Routing
|
||||
const { activeRoute } = useRouting()
|
||||
const showGeolocationNudge = ref(false)
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -100,6 +106,12 @@ function handleClickGeoloc() {
|
||||
:nb-covered="coveredParkingsSpots" :nb-korrigo="premiumParkingsSpots" @click-geoloc="handleClickGeoloc" />
|
||||
</Transition>
|
||||
|
||||
<Transition>
|
||||
<div v-if="showGeolocationNudge" class="...">
|
||||
Activez la géolocalisation pour obtenir un itinéraire
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<main class="relative z-0 h-screen w-screen grid place-items-center">
|
||||
<div v-if="state.status === 'pending'" class="flex flex-col items-center gap-3 text-sm">
|
||||
<span>Chargement des parkings… {{ progress }}%</span>
|
||||
@@ -116,18 +128,20 @@ function handleClickGeoloc() {
|
||||
<LControlZoom position="bottomright" />
|
||||
<LTileLayer v-once :url="MAP_TILELAYER_URL" layer-type="base" />
|
||||
|
||||
<LGeoJson v-if="activeRoute" :geojson="activeRoute" />
|
||||
|
||||
<LMarker v-if="userCoords" :lat-lng="[userCoords.latitude, userCoords.longitude]">
|
||||
<LIcon>
|
||||
<PhMapPin size="24" weight="fill" class="text-amber-400" />
|
||||
</LIcon>
|
||||
</LMarker>
|
||||
|
||||
<BikeClusterLayer :parkings="coveredParkings" group="covered" :visible="filterCovered" :max-cluster-radius
|
||||
:disable-clustering-at-zoom />
|
||||
<BikeClusterLayer :parkings="nonCoveredParkings" group="non-covered" :visible="filterUncovered"
|
||||
:max-cluster-radius :disable-clustering-at-zoom />
|
||||
<BikeClusterLayer :parkings="premiumParkings" group="premium" :visible="filterKorrigo" :max-cluster-radius
|
||||
:disable-clustering-at-zoom />
|
||||
<BikeClusterLayer @nudge-geolocation="showGeolocationNudge = true" :parkings="coveredParkings" group="covered"
|
||||
:visible="filterCovered" :max-cluster-radius :disable-clustering-at-zoom />
|
||||
<BikeClusterLayer @nudge-geolocation="showGeolocationNudge = true" :parkings="nonCoveredParkings"
|
||||
group="non-covered" :visible="filterUncovered" :max-cluster-radius :disable-clustering-at-zoom />
|
||||
<BikeClusterLayer @nudge-geolocation="showGeolocationNudge = true" :parkings="premiumParkings" group="premium"
|
||||
:visible="filterKorrigo" :max-cluster-radius :disable-clustering-at-zoom />
|
||||
</LMap>
|
||||
</template>
|
||||
</main>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<script lang="ts" setup>
|
||||
import { cn } from '@/utils/cn';
|
||||
import { useRouting } from '@/composables/useRouting';
|
||||
import { SPOTS_MAX, useMap } from '@/stores/map';
|
||||
import type { BikeParking } from '@/types/Bikes';
|
||||
import { cn } from '@/utils/cn';
|
||||
import { BOXED_KEY, COVERED_KEY, KORRIGO_KEY, UNCOVERED_KEY } from '@/utils/const';
|
||||
import { PhBicycle, PhLockKey, PhSquareHalf, PhWarehouse } from '@phosphor-icons/vue';
|
||||
import { LIcon, LMarker, LPopup } from '@vue-leaflet/vue-leaflet';
|
||||
import type { PointTuple } from 'leaflet';
|
||||
import { computed, ref } from 'vue';
|
||||
import { UNCOVERED_KEY, COVERED_KEY, BOXED_KEY, KORRIGO_KEY } from '@/utils/const';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { SPOTS_MAX, useMap } from '@/stores/map';
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
park: BikeParking
|
||||
@@ -40,10 +41,28 @@ const isMarkerVisibleEquipments = computed(() => {
|
||||
})
|
||||
|
||||
const isMarkerVisible = computed(() => isMarkerVisibleLayers.value && isMarkerVisibleEquipments.value)
|
||||
|
||||
// Routing
|
||||
const { routeTo } = useRouting()
|
||||
const { userCoords } = storeToRefs(useMap())
|
||||
|
||||
const emit = defineEmits<{
|
||||
nudgeGeolocation: []
|
||||
}>()
|
||||
|
||||
async function handleMarkerClick() {
|
||||
if (!userCoords.value) {
|
||||
emit('nudgeGeolocation')
|
||||
return
|
||||
}
|
||||
|
||||
await routeTo(props.park)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<LMarker :lat-lng="[park.geo_point_2d.lat, park.geo_point_2d.lon]" :visible="isMarkerVisible">
|
||||
<LMarker :lat-lng="[park.geo_point_2d.lat, park.geo_point_2d.lon]" :visible="isMarkerVisible"
|
||||
@click="handleMarkerClick">
|
||||
<LPopup :options="{ offset: popupOffset, maxWidth: 520, minWidth: 240 }">
|
||||
<div>
|
||||
<h2 class="font-bold">
|
||||
|
||||
27
src/composables/useRouting.ts
Normal file
27
src/composables/useRouting.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useMap } from '@/stores/map'
|
||||
import type { BikeParking } from '@/types/Bikes'
|
||||
|
||||
export function useRouting() {
|
||||
const { userCoords, activeRoute, isRoutingLoading } = storeToRefs(useMap())
|
||||
|
||||
async function routeTo(park: BikeParking) {
|
||||
if (!userCoords.value) return false // caller handles the nudge
|
||||
|
||||
isRoutingLoading.value = true
|
||||
activeRoute.value = null
|
||||
|
||||
const { latitude: uLat, longitude: uLon } = userCoords.value
|
||||
const { lat: pLat, lon: pLon } = park.geo_point_2d
|
||||
|
||||
const url = `https://router.project-osrm.org/route/v1/bike/${uLon},${uLat};${pLon},${pLat}?geometries=geojson&overview=full`
|
||||
const data = await fetch(url).then((r) => r.json())
|
||||
|
||||
activeRoute.value = data.routes[0].geometry
|
||||
isRoutingLoading.value = false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return { routeTo, activeRoute, isRoutingLoading }
|
||||
}
|
||||
@@ -36,6 +36,10 @@ export const useMap = defineStore('map', () => {
|
||||
|
||||
const spotsRange = ref<[number, number]>([SPOTS_MIN, SPOTS_MAX])
|
||||
|
||||
// Routing
|
||||
const activeRoute = ref<GeoJSON.LineString | null>(null)
|
||||
const isRoutingLoading = ref(false)
|
||||
|
||||
return {
|
||||
zoom,
|
||||
minZoom,
|
||||
@@ -52,5 +56,7 @@ export const useMap = defineStore('map', () => {
|
||||
filterCargoOnly,
|
||||
filterStdOnly,
|
||||
spotsRange,
|
||||
activeRoute,
|
||||
isRoutingLoading,
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user