cluster selection
This commit is contained in:
+6
-2
@@ -8,7 +8,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const router = useRoute();
|
const router = useRoute();
|
||||||
const store = useMresoStore();
|
const store = useMresoStore();
|
||||||
watchEffect(() => {
|
watch(
|
||||||
|
() => router.params.line_id,
|
||||||
|
() => {
|
||||||
if (router.params.line_id === undefined) {
|
if (router.params.line_id === undefined) {
|
||||||
store.selectedRouteIds = [];
|
store.selectedRouteIds = [];
|
||||||
}
|
}
|
||||||
@@ -16,7 +18,9 @@ watchEffect(() => {
|
|||||||
store.selectedRouteIds = [];
|
store.selectedRouteIds = [];
|
||||||
}
|
}
|
||||||
store.selectedRouteIds = [router.params.line_id];
|
store.selectedRouteIds = [router.params.line_id];
|
||||||
});
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css">
|
<style lang="css">
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
'icon-overlap': 'always',
|
'icon-overlap': 'always',
|
||||||
'text-overlap': 'always',
|
'text-overlap': 'always',
|
||||||
}"
|
}"
|
||||||
@click="clusterClick"
|
|
||||||
@mouseenter="clusterEnter"
|
@mouseenter="clusterEnter"
|
||||||
@mouseleave="clusterLeave"
|
@mouseleave="clusterLeave"
|
||||||
/>
|
/>
|
||||||
@@ -32,9 +31,10 @@
|
|||||||
Direction {{ data.pattern.lastStopName }}
|
Direction {{ data.pattern.lastStopName }}
|
||||||
<div v-for="time in data.times" :key="time.tripId">
|
<div v-for="time in data.times" :key="time.tripId">
|
||||||
{{
|
{{
|
||||||
time.realtimeArrival -
|
stopTimeToRealTime(
|
||||||
(Math.floor(new Date().getTime() / 1000) -
|
time.realtimeArrival,
|
||||||
time.serviceDay)
|
time.serviceDay,
|
||||||
|
)
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -48,7 +48,7 @@ import { useMap } from "@indoorequal/vue-maplibre-gl";
|
|||||||
import { Popup } from "maplibre-gl";
|
import { Popup } from "maplibre-gl";
|
||||||
|
|
||||||
const cluster_label = {
|
const cluster_label = {
|
||||||
"text-field": ["get", "id"],
|
"text-field": ["get", "name"],
|
||||||
};
|
};
|
||||||
const map = useMap("main");
|
const map = useMap("main");
|
||||||
const popupDiv = useTemplateRef("popup-div");
|
const popupDiv = useTemplateRef("popup-div");
|
||||||
@@ -65,13 +65,26 @@ const cluster_paint = {
|
|||||||
|
|
||||||
const popupData = ref<StopTimesPattern[]>([]);
|
const popupData = ref<StopTimesPattern[]>([]);
|
||||||
const popup = new Popup().setMaxWidth("500px").addTo(map.map!);
|
const popup = new Popup().setMaxWidth("500px").addTo(map.map!);
|
||||||
|
const router = useRoute();
|
||||||
|
const store = useMresoStore();
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
popup.off("close", closePopupEventHandler);
|
||||||
popup.remove();
|
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) {
|
async function clusterClick(e: FeatureCollection) {
|
||||||
popupData.value = [];
|
popup.off("close", closePopupEventHandler);
|
||||||
const feature = e.features[0];
|
const feature = e.features[0];
|
||||||
if (!feature || !feature.properties) {
|
if (!feature || !feature.properties) {
|
||||||
return;
|
return;
|
||||||
@@ -80,26 +93,63 @@ async function clusterClick(e: FeatureCollection) {
|
|||||||
if (!code) {
|
if (!code) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// let data;
|
console.log(code);
|
||||||
const coordinates = feature.geometry.coordinates.slice();
|
navigateTo(`/lines/${router.params.line_id}/cluster/${code}`);
|
||||||
// [data, popupData.value] = await Promise.all([
|
popup.remove();
|
||||||
// $fetch<FeatureCollection<Point>>(
|
popup.on("close", closePopupEventHandler);
|
||||||
// `https://data.mobilites-m.fr/api/clusters/${code}/stops`,
|
}
|
||||||
// ),
|
|
||||||
|
|
||||||
// ]);
|
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[]>(
|
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: {
|
headers: {
|
||||||
origin: "PersonalProjectMreso",
|
origin: "PersonalProjectMreso",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
if (!popupDiv.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
popup.setDOMContent(popupDiv.value).setLngLat(coordinates).addTo(map.map!);
|
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() {
|
function clusterEnter() {
|
||||||
map.map!.getCanvas().style.cursor = "pointer";
|
map.map!.getCanvas().style.cursor = "pointer";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ await store.fetchData();
|
|||||||
const shownClusters = ref<FeatureCollection<Point> | null>(null);
|
const shownClusters = ref<FeatureCollection<Point> | null>(null);
|
||||||
const image = useSvgImage();
|
const image = useSvgImage();
|
||||||
const map = useMap("main");
|
const map = useMap("main");
|
||||||
|
|
||||||
watchEffect(async () => {
|
watchEffect(async () => {
|
||||||
const allClusters = [];
|
const allClusters = [];
|
||||||
for (const routeId of store.selectedRouteIds) {
|
for (const routeId of store.selectedRouteIds) {
|
||||||
@@ -47,9 +48,10 @@ watchEffect(async () => {
|
|||||||
shownClusters.value = {
|
shownClusters.value = {
|
||||||
type: "FeatureCollection",
|
type: "FeatureCollection",
|
||||||
features: allClusters
|
features: allClusters
|
||||||
.map((c) => store.clustersById[c.id])
|
.map((c) => store.clustersByCode[c.code])
|
||||||
.filter((c) => c !== undefined),
|
.filter((c) => c !== undefined),
|
||||||
};
|
};
|
||||||
|
|
||||||
const firstPoint = shownClusters.value.features[0];
|
const firstPoint = shownClusters.value.features[0];
|
||||||
if (!firstPoint) {
|
if (!firstPoint) {
|
||||||
map.map?.flyTo({ center, zoom });
|
map.map?.flyTo({ center, zoom });
|
||||||
|
|||||||
@@ -3,3 +3,7 @@
|
|||||||
<SidebarContainer />
|
<SidebarContainer />
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</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]),
|
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)) {
|
if (!("features" in state.clusters)) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(
|
||||||
state.clusters.features.map((l) => [l.properties!.id, l]),
|
state.clusters.features.map((l) => [l.properties!.code, l]),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,4 +4,21 @@ export default defineNuxtConfig({
|
|||||||
devtools: { enabled: false },
|
devtools: { enabled: false },
|
||||||
modules: ["@nuxt/eslint", "nuxt-maplibre", "@pinia/nuxt"],
|
modules: ["@nuxt/eslint", "nuxt-maplibre", "@pinia/nuxt"],
|
||||||
ssr: true,
|
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