feat: add initial game implementation
All checks were successful
deploy to cloudflare pages / deploy (push) Successful in 33s
All checks were successful
deploy to cloudflare pages / deploy (push) Successful in 33s
This commit is contained in:
parent
4919a5258d
commit
4f7d603848
4 changed files with 99 additions and 19 deletions
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -15,14 +15,11 @@
|
||||||
|
|
||||||
const hidden = !$page.url.searchParams.has("debug");
|
const hidden = !$page.url.searchParams.has("debug");
|
||||||
|
|
||||||
const lignes = data.lines;
|
const lignes = data.gameData.lines;
|
||||||
const point = L.latLng(45.75388713, 4.84715287);
|
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 linesJson = lignes.map(([feature, color]) => {
|
||||||
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(" ", ",") + ")";
|
|
||||||
return L.geoJSON(feature, { style: { color } });
|
return L.geoJSON(feature, { style: { color } });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -32,8 +29,11 @@
|
||||||
|
|
||||||
let lines = $state(true);
|
let lines = $state(true);
|
||||||
let labels = $state(false);
|
let labels = $state(false);
|
||||||
|
let submitted = $state(false);
|
||||||
|
|
||||||
let map: L.Map | null = $state(null);
|
let map: L.Map | null = $state(null);
|
||||||
|
let playerMarker: L.Marker | null = $state(null);
|
||||||
|
|
||||||
let tileLayer = $derived(
|
let tileLayer = $derived(
|
||||||
L.tileLayer(
|
L.tileLayer(
|
||||||
`https://basemaps.cartocdn.com/light_${labels ? "all" : "nolabels"}/{z}/{x}/{y}{r}.png`,
|
`https://basemaps.cartocdn.com/light_${labels ? "all" : "nolabels"}/{z}/{x}/{y}{r}.png`,
|
||||||
|
@ -50,19 +50,21 @@
|
||||||
tileLayer.addTo(map);
|
tileLayer.addTo(map);
|
||||||
|
|
||||||
if (lines) {
|
if (lines) {
|
||||||
linesJson.forEach((line) => map && line.addTo(map));
|
linesJson.forEach((line) => line.addTo(map!));
|
||||||
} else {
|
} else {
|
||||||
linesJson.forEach((line) => map && line.removeFrom(map));
|
linesJson.forEach((line) => line.removeFrom(map!));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function createMap(node: HTMLElement) {
|
function createMap(node: HTMLElement) {
|
||||||
map = L.map(node).setView(center, 13);
|
map = L.map(node).setView(center, 13);
|
||||||
|
playerMarker = L.marker([0, 0]).addTo(map);
|
||||||
|
|
||||||
const marker = L.marker([0, 0]);
|
|
||||||
map.on("click", (e) => {
|
map.on("click", (e) => {
|
||||||
if (map) marker.setLatLng(e.latlng).addTo(map);
|
if (map && playerMarker && !submitted) {
|
||||||
|
playerMarker.setLatLng(e.latlng);
|
||||||
latlng = e.latlng;
|
latlng = e.latlng;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,8 +78,23 @@
|
||||||
|
|
||||||
return score * multiplier;
|
return score * multiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkLocation() {
|
||||||
|
submitted = true;
|
||||||
|
if (map) {
|
||||||
|
L.marker(point).bindPopup(data.gameData.stopName).addTo(map).openPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<h1 class="banner">{data.gameData.stopName}</h1>
|
||||||
|
|
||||||
|
<div class="banner" hidden={submitted}><button onclick={checkLocation}>SUBMIT</button></div>
|
||||||
|
|
||||||
|
<div class="banner results" hidden={!submitted}>
|
||||||
|
{Math.floor(points)} points! Vous etiez à {Math.floor(distance)}m.
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="banner" {hidden}>
|
<div class="banner" {hidden}>
|
||||||
<span>distance: {distance}, points: {points}</span>
|
<span>distance: {distance}, points: {points}</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -95,6 +112,11 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.results {
|
||||||
|
color: green;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
#map {
|
#map {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
import type { PageLoad } from "./$types";
|
import type { PageLoad } 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";
|
|
||||||
|
|
||||||
export const load: PageLoad = async ({ fetch }) => {
|
export const load: PageLoad = async ({ fetch }) => {
|
||||||
const res = await fetch(linesUrl);
|
const res = await fetch("/api/data");
|
||||||
const lines: GeoJSON.FeatureCollection = await res.json();
|
const gameData: GameData = await res.json();
|
||||||
|
|
||||||
return { lines };
|
return { gameData };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ssr = false;
|
export const ssr = false;
|
||||||
|
|
55
src/routes/api/data/+server.ts
Normal file
55
src/routes/api/data/+server.ts
Normal file
|
@ -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<string>(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);
|
||||||
|
};
|
Loading…
Add table
Add a link
Reference in a new issue