diff --git a/src/lib/index.ts b/src/lib/index.ts
index 856f2b6..b1cebb7 100644
--- a/src/lib/index.ts
+++ b/src/lib/index.ts
@@ -1 +1,6 @@
-// place files you want to import through the `$lib` alias in this folder.
+export interface GameData {
+ center: [number, number];
+ lines: [GeoJSON.Feature, string][];
+ stopName: string;
+ stop: GeoJSON.Position;
+}
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index 8f66f1f..fcc9830 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -15,14 +15,11 @@
const hidden = !$page.url.searchParams.has("debug");
- const lignes = data.lines;
- const point = L.latLng(45.75388713, 4.84715287);
+ const lignes = data.gameData.lines;
+ const point = L.latLng(data.gameData.stop[1], data.gameData.stop[0]);
+ const center = L.latLng(data.gameData.center);
- const bbox = lignes.bbox || [0, 0, 0, 0];
- const center = L.latLng((bbox[1] + bbox[3]) / 2, (bbox[0] + bbox[2]) / 2);
-
- const linesJson = lignes.features.map((feature) => {
- let color = "rgb(" + feature.properties?.couleur?.replaceAll(" ", ",") + ")";
+ const linesJson = lignes.map(([feature, color]) => {
return L.geoJSON(feature, { style: { color } });
});
@@ -32,8 +29,11 @@
let lines = $state(true);
let labels = $state(false);
+ let submitted = $state(false);
let map: L.Map | null = $state(null);
+ let playerMarker: L.Marker | null = $state(null);
+
let tileLayer = $derived(
L.tileLayer(
`https://basemaps.cartocdn.com/light_${labels ? "all" : "nolabels"}/{z}/{x}/{y}{r}.png`,
@@ -50,19 +50,21 @@
tileLayer.addTo(map);
if (lines) {
- linesJson.forEach((line) => map && line.addTo(map));
+ linesJson.forEach((line) => line.addTo(map!));
} else {
- linesJson.forEach((line) => map && line.removeFrom(map));
+ linesJson.forEach((line) => line.removeFrom(map!));
}
});
function createMap(node: HTMLElement) {
map = L.map(node).setView(center, 13);
+ playerMarker = L.marker([0, 0]).addTo(map);
- const marker = L.marker([0, 0]);
map.on("click", (e) => {
- if (map) marker.setLatLng(e.latlng).addTo(map);
- latlng = e.latlng;
+ if (map && playerMarker && !submitted) {
+ playerMarker.setLatLng(e.latlng);
+ latlng = e.latlng;
+ }
});
}
@@ -76,8 +78,23 @@
return score * multiplier;
}
+
+ function checkLocation() {
+ submitted = true;
+ if (map) {
+ L.marker(point).bindPopup(data.gameData.stopName).addTo(map).openPopup();
+ }
+ }
+
{data.gameData.stopName}
+
+
+
+
+ {Math.floor(points)} points! Vous etiez à {Math.floor(distance)}m.
+
+
distance: {distance}, points: {points}
@@ -95,6 +112,11 @@
text-align: center;
}
+ .results {
+ color: green;
+ font-size: 20px;
+ }
+
#map {
height: 100%;
width: 100%;
diff --git a/src/routes/+page.ts b/src/routes/+page.ts
index 32fbd51..e6fc043 100644
--- a/src/routes/+page.ts
+++ b/src/routes/+page.ts
@@ -1,13 +1,11 @@
import type { PageLoad } from "./$types";
-
-const linesUrl =
- "https://data.grandlyon.com/geoserver/sytral/ows?SERVICE=WFS&VERSION=2.0.0&request=GetFeature&typename=sytral:tcl_sytral.tcllignemf_2_0_0&outputFormat=application/json&SRSNAME=EPSG:4171&sortBy=gid";
+import type { GameData } from "$lib";
export const load: PageLoad = async ({ fetch }) => {
- const res = await fetch(linesUrl);
- const lines: GeoJSON.FeatureCollection = await res.json();
+ const res = await fetch("/api/data");
+ const gameData: GameData = await res.json();
- return { lines };
+ return { gameData };
};
export const ssr = false;
diff --git a/src/routes/api/data/+server.ts b/src/routes/api/data/+server.ts
new file mode 100644
index 0000000..f65b330
--- /dev/null
+++ b/src/routes/api/data/+server.ts
@@ -0,0 +1,55 @@
+import type { RequestHandler } from "./$types";
+import type { GameData } from "$lib";
+
+const linesUrl =
+ "https://data.grandlyon.com/geoserver/sytral/ows?SERVICE=WFS&VERSION=2.0.0&request=GetFeature&typename=sytral:tcl_sytral.tcllignemf_2_0_0&outputFormat=application/json&SRSNAME=EPSG:4171&sortBy=gid";
+const stopsUrl =
+ "https://data.grandlyon.com/geoserver/sytral/ows?SERVICE=WFS&VERSION=2.0.0&request=GetFeature&typename=sytral:tcl_sytral.tclarret&outputFormat=application/json&SRSNAME=EPSG:4171&sortBy=gid";
+
+let lazyLines: GeoJSON.FeatureCollection | null = null;
+let lazyStops: GeoJSON.FeatureCollection | null = null;
+
+export const GET: RequestHandler = async ({ fetch }) => {
+ const lines: GeoJSON.FeatureCollection =
+ lazyLines || (lazyLines = await fetch(linesUrl).then((r) => r.json()));
+ const stops: GeoJSON.FeatureCollection =
+ lazyStops || (lazyStops = await fetch(stopsUrl).then((r) => r.json()));
+
+ const bbox = lines.bbox ?? [0, 0, 0, 0];
+ const centerLat = (bbox[1] + bbox[3]) / 2;
+ const centerLon = (bbox[0] + bbox[2]) / 2;
+
+ const lineColors: [GeoJSON.Feature, string][] = lines.features.map((f) => {
+ const components = f.properties!.couleur!.split(" ");
+ return [f, `rgb(${components.join(",")})`];
+ });
+
+ const lineCodes = new Set(lines.features.map((f) => f.properties!.code_ligne));
+
+ const crossingStops = stops.features.filter((f) => {
+ if (f.properties?.desserte) {
+ return f.properties.desserte
+ .split(",")
+ .map((d: string) => d.split(":")[0])
+ .some((d: string) => lineCodes.has(d));
+ } else {
+ return false;
+ }
+ });
+
+ const uniqueStops = crossingStops.filter(
+ (f1, i, arr) => arr.findIndex((f2) => f2.properties?.nom === f1.properties?.nom) === i,
+ );
+
+ const randomStop = uniqueStops[Math.floor(Math.random() * uniqueStops.length)];
+ const coords = randomStop.geometry.type === "Point" ? randomStop.geometry.coordinates : [0, 0];
+
+ const data: GameData = {
+ center: [centerLat, centerLon],
+ lines: lineColors,
+ stopName: randomStop.properties!.nom,
+ stop: coords,
+ };
+
+ return Response.json(data);
+};