cluster selection
This commit is contained in:
+13
-9
@@ -8,15 +8,19 @@
|
||||
<script setup lang="ts">
|
||||
const router = useRoute();
|
||||
const store = useMresoStore();
|
||||
watchEffect(() => {
|
||||
if (router.params.line_id === undefined) {
|
||||
store.selectedRouteIds = [];
|
||||
}
|
||||
if (typeof router.params.line_id !== "string") {
|
||||
store.selectedRouteIds = [];
|
||||
}
|
||||
store.selectedRouteIds = [router.params.line_id];
|
||||
});
|
||||
watch(
|
||||
() => router.params.line_id,
|
||||
() => {
|
||||
if (router.params.line_id === undefined) {
|
||||
store.selectedRouteIds = [];
|
||||
}
|
||||
if (typeof router.params.line_id !== "string") {
|
||||
store.selectedRouteIds = [];
|
||||
}
|
||||
store.selectedRouteIds = [router.params.line_id];
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="css">
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
'icon-overlap': 'always',
|
||||
'text-overlap': 'always',
|
||||
}"
|
||||
@click="clusterClick"
|
||||
@mouseenter="clusterEnter"
|
||||
@mouseleave="clusterLeave"
|
||||
/>
|
||||
@@ -32,9 +31,10 @@
|
||||
Direction {{ data.pattern.lastStopName }}
|
||||
<div v-for="time in data.times" :key="time.tripId">
|
||||
{{
|
||||
time.realtimeArrival -
|
||||
(Math.floor(new Date().getTime() / 1000) -
|
||||
time.serviceDay)
|
||||
stopTimeToRealTime(
|
||||
time.realtimeArrival,
|
||||
time.serviceDay,
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
@@ -48,7 +48,7 @@ import { useMap } from "@indoorequal/vue-maplibre-gl";
|
||||
import { Popup } from "maplibre-gl";
|
||||
|
||||
const cluster_label = {
|
||||
"text-field": ["get", "id"],
|
||||
"text-field": ["get", "name"],
|
||||
};
|
||||
const map = useMap("main");
|
||||
const popupDiv = useTemplateRef("popup-div");
|
||||
@@ -65,13 +65,26 @@ const cluster_paint = {
|
||||
|
||||
const popupData = ref<StopTimesPattern[]>([]);
|
||||
const popup = new Popup().setMaxWidth("500px").addTo(map.map!);
|
||||
const router = useRoute();
|
||||
const store = useMresoStore();
|
||||
|
||||
onUnmounted(() => {
|
||||
popup.off("close", closePopupEventHandler);
|
||||
popup.remove();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => router.params.cluster_id,
|
||||
async () => {
|
||||
popup.off("close", closePopupEventHandler);
|
||||
await routerParamChange();
|
||||
popup.on("close", closePopupEventHandler);
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
async function clusterClick(e: FeatureCollection) {
|
||||
popupData.value = [];
|
||||
popup.off("close", closePopupEventHandler);
|
||||
const feature = e.features[0];
|
||||
if (!feature || !feature.properties) {
|
||||
return;
|
||||
@@ -80,26 +93,63 @@ async function clusterClick(e: FeatureCollection) {
|
||||
if (!code) {
|
||||
return;
|
||||
}
|
||||
// let data;
|
||||
const coordinates = feature.geometry.coordinates.slice();
|
||||
// [data, popupData.value] = await Promise.all([
|
||||
// $fetch<FeatureCollection<Point>>(
|
||||
// `https://data.mobilites-m.fr/api/clusters/${code}/stops`,
|
||||
// ),
|
||||
console.log(code);
|
||||
navigateTo(`/lines/${router.params.line_id}/cluster/${code}`);
|
||||
popup.remove();
|
||||
popup.on("close", closePopupEventHandler);
|
||||
}
|
||||
|
||||
// ]);
|
||||
async function routerParamChange() {
|
||||
if (router.params.cluster_id === undefined) {
|
||||
popup.remove();
|
||||
return;
|
||||
}
|
||||
if (typeof router.params.cluster_id !== "string") {
|
||||
return;
|
||||
}
|
||||
const cluster = store.clustersByCode[router.params.cluster_id];
|
||||
if (!cluster) {
|
||||
return;
|
||||
}
|
||||
const code = cluster.properties?.code;
|
||||
if (!code) {
|
||||
return;
|
||||
}
|
||||
popupData.value = [];
|
||||
|
||||
const coordinates = cluster.geometry.coordinates.slice();
|
||||
|
||||
popupData.value = await $fetch<StopTimesPattern[]>(
|
||||
`https://data.mobilites-m.fr/api/routers/default/index/clusters/${feature.properties.code}/stoptimes?showCancelledTrips=false&route=${route.id}`,
|
||||
`https://data.mobilites-m.fr/api/routers/default/index/clusters/${code}/stoptimes?showCancelledTrips=false&route=${route.id}`,
|
||||
{
|
||||
headers: {
|
||||
origin: "PersonalProjectMreso",
|
||||
},
|
||||
},
|
||||
);
|
||||
if (!popupDiv.value) {
|
||||
return;
|
||||
}
|
||||
popup.setDOMContent(popupDiv.value).setLngLat(coordinates).addTo(map.map!);
|
||||
}
|
||||
|
||||
function closePopupEventHandler() {
|
||||
navigateTo(`/lines/${router.params.line_id}`);
|
||||
}
|
||||
|
||||
function stopTimeToRealTime(realtimeArrival: number, serviceDay: number) {
|
||||
const seconds =
|
||||
realtimeArrival -
|
||||
(Math.floor(new Date().getTime() / 1000) - serviceDay);
|
||||
if (seconds < 0) {
|
||||
return "00:00:00";
|
||||
}
|
||||
const date = new Date(0);
|
||||
date.setSeconds(seconds); // specify value for SECONDS here
|
||||
const timeString = date.toISOString().substring(11, 19);
|
||||
return timeString;
|
||||
}
|
||||
|
||||
function clusterEnter() {
|
||||
map.map!.getCanvas().style.cursor = "pointer";
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ await store.fetchData();
|
||||
const shownClusters = ref<FeatureCollection<Point> | null>(null);
|
||||
const image = useSvgImage();
|
||||
const map = useMap("main");
|
||||
|
||||
watchEffect(async () => {
|
||||
const allClusters = [];
|
||||
for (const routeId of store.selectedRouteIds) {
|
||||
@@ -47,9 +48,10 @@ watchEffect(async () => {
|
||||
shownClusters.value = {
|
||||
type: "FeatureCollection",
|
||||
features: allClusters
|
||||
.map((c) => store.clustersById[c.id])
|
||||
.map((c) => store.clustersByCode[c.code])
|
||||
.filter((c) => c !== undefined),
|
||||
};
|
||||
|
||||
const firstPoint = shownClusters.value.features[0];
|
||||
if (!firstPoint) {
|
||||
map.map?.flyTo({ center, zoom });
|
||||
|
||||
@@ -3,3 +3,7 @@
|
||||
<SidebarContainer />
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
definePageMeta({ key: "lines" });
|
||||
</script>
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<template>
|
||||
<main>
|
||||
<SidebarContainer />
|
||||
</main>
|
||||
</template>
|
||||
@@ -1,5 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<SidebarContainer />
|
||||
</div>
|
||||
</template>
|
||||
+2
-2
@@ -35,12 +35,12 @@ export const useMresoStore = defineStore("mresoStore", {
|
||||
state.lines.features.map((l) => [l.properties!.id, l]),
|
||||
);
|
||||
},
|
||||
clustersById(state): { [id: string]: Feature<Point> } {
|
||||
clustersByCode(state): { [id: string]: Feature<Point> } {
|
||||
if (!("features" in state.clusters)) {
|
||||
return {};
|
||||
}
|
||||
return Object.fromEntries(
|
||||
state.clusters.features.map((l) => [l.properties!.id, l]),
|
||||
state.clusters.features.map((l) => [l.properties!.code, l]),
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
+21
-4
@@ -1,7 +1,24 @@
|
||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||
export default defineNuxtConfig({
|
||||
compatibilityDate: "2025-07-15",
|
||||
devtools: { enabled: false },
|
||||
modules: ["@nuxt/eslint", "nuxt-maplibre", "@pinia/nuxt"],
|
||||
ssr: true,
|
||||
compatibilityDate: "2025-07-15",
|
||||
devtools: { enabled: false },
|
||||
modules: ["@nuxt/eslint", "nuxt-maplibre", "@pinia/nuxt"],
|
||||
ssr: true,
|
||||
hooks: {
|
||||
"pages:extend"(pages) {
|
||||
// add a route
|
||||
pages.push({
|
||||
name: "lines",
|
||||
path: "/lines/:line_id()",
|
||||
file: "~/pages/index.vue",
|
||||
meta: { key: "lines" },
|
||||
});
|
||||
pages.push({
|
||||
name: "lines/cluster",
|
||||
path: "/lines/:line_id()/cluster/:cluster_id()",
|
||||
file: "~/pages/index.vue",
|
||||
meta: { key: "lines" },
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user