Create a route editor

master
Araozu 2024-09-03 19:18:51 -05:00
parent 02e31e2df4
commit f75f33611a
6 changed files with 176 additions and 3 deletions

View File

@ -23,7 +23,10 @@
"vite-plugin-solid": "^2.8.2" "vite-plugin-solid": "^2.8.2"
}, },
"dependencies": { "dependencies": {
"@solidjs/router": "^0.14.3",
"@types/file-saver": "^2.0.7",
"@types/leaflet": "^1.9.12", "@types/leaflet": "^1.9.12",
"file-saver": "^2.0.5",
"leaflet": "^1.9.4", "leaflet": "^1.9.4",
"solid-js": "^1.8.11" "solid-js": "^1.8.11"
} }

View File

@ -8,9 +8,18 @@ importers:
.: .:
dependencies: dependencies:
'@solidjs/router':
specifier: ^0.14.3
version: 0.14.3(solid-js@1.8.11)
'@types/file-saver':
specifier: ^2.0.7
version: 2.0.7
'@types/leaflet': '@types/leaflet':
specifier: ^1.9.12 specifier: ^1.9.12
version: 1.9.12 version: 1.9.12
file-saver:
specifier: ^2.0.5
version: 2.0.5
leaflet: leaflet:
specifier: ^1.9.4 specifier: ^1.9.4
version: 1.9.4 version: 1.9.4
@ -599,6 +608,11 @@ packages:
peerDependencies: peerDependencies:
solid-js: ^1.6.12 solid-js: ^1.6.12
'@solidjs/router@0.14.3':
resolution: {integrity: sha512-9p4k4zL2baK/1XRQALbFcaQ4IikjkWmxqYQtFqLzjONUejhL1uqJHtzxB4tZjmNqtRANVRnTDbJfzjvaD9k+pQ==}
peerDependencies:
solid-js: ^1.8.6
'@types/babel__core@7.20.5': '@types/babel__core@7.20.5':
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
@ -614,6 +628,9 @@ packages:
'@types/estree@1.0.5': '@types/estree@1.0.5':
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
'@types/file-saver@2.0.7':
resolution: {integrity: sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==}
'@types/geojson@7946.0.14': '@types/geojson@7946.0.14':
resolution: {integrity: sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==} resolution: {integrity: sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==}
@ -1052,6 +1069,9 @@ packages:
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
engines: {node: ^10.12.0 || >=12.0.0} engines: {node: ^10.12.0 || >=12.0.0}
file-saver@2.0.5:
resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==}
fill-range@7.0.1: fill-range@7.0.1:
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -2479,6 +2499,10 @@ snapshots:
dependencies: dependencies:
solid-js: 1.8.11 solid-js: 1.8.11
'@solidjs/router@0.14.3(solid-js@1.8.11)':
dependencies:
solid-js: 1.8.11
'@types/babel__core@7.20.5': '@types/babel__core@7.20.5':
dependencies: dependencies:
'@babel/parser': 7.22.5 '@babel/parser': 7.22.5
@ -2502,6 +2526,8 @@ snapshots:
'@types/estree@1.0.5': {} '@types/estree@1.0.5': {}
'@types/file-saver@2.0.7': {}
'@types/geojson@7946.0.14': {} '@types/geojson@7946.0.14': {}
'@types/json-schema@7.0.15': {} '@types/json-schema@7.0.15': {}
@ -3127,6 +3153,8 @@ snapshots:
dependencies: dependencies:
flat-cache: 3.2.0 flat-cache: 3.2.0
file-saver@2.0.5: {}
fill-range@7.0.1: fill-range@7.0.1:
dependencies: dependencies:
to-regex-range: 5.0.1 to-regex-range: 5.0.1

View File

@ -1,10 +1,15 @@
import "leaflet/dist/leaflet.css"; import "leaflet/dist/leaflet.css";
import { Index } from "./pages/Index"; import { Index } from "./pages/Index";
import { Route, Router } from "@solidjs/router";
import { Editor } from "./pages/Editor";
export default function() { export default function() {
return ( return (
<> <>
<Index /> <Router>
<Route path="/" component={Index} />
<Route path="/editor" component={Editor} />
</Router>
</> </>
); );
} }

View File

@ -19,9 +19,9 @@
html { html {
background-color: var(--bg); background-color: var(--bg);
color: var(--on-bg);
} }
body { body {
font-family: "Atkinson Hyperlegible", sans-serif; font-family: "Atkinson Hyperlegible", sans-serif;
} }

123
src/pages/Editor.tsx Normal file
View File

@ -0,0 +1,123 @@
import { saveAs } from "file-saver";
import L from "leaflet";
import { createEffect, createSignal, For, onMount } from "solid-js";
let map: L.Map | null = null;
export function Editor() {
const container = <div class="h-[98vh] rounded-lg dark:opacity-60" />;
const [points, setPoints] = createSignal<Array<L.LatLng>>([]);
let currentPolyline: L.Polyline | null = null;
onMount(() => {
map = L.map(container as HTMLElement)
.setView([-16.40171, -71.53040], 13);
L.tileLayer("/tiles/{z}/{x}/{y}.jpg", {
maxZoom: 18,
minZoom: 12,
attribution: "&copy; Map data <a href=\"http://www.openstreetmap.org/copyright\">OpenStreetMap</a>",
}).addTo(map);
// Set up click logic
map.addEventListener("click", (ev) => {
const click_lat_lng = ev.latlng;
// Update the state
setPoints((x) => [...x, click_lat_lng]);
console.log(click_lat_lng);
});
});
// Re-draws the polyline
createEffect(() => {
const p = points();
// Delete the polyline
if (p.length === 0) {
if (currentPolyline !== null) {
currentPolyline.removeFrom(map!);
}
//
} else {
// delete previous polyline, if any
if (currentPolyline !== null) {
currentPolyline.removeFrom(map!);
}
currentPolyline = L.polyline(p, {color: "red"});
currentPolyline.addTo(map!);
}
});
const saveJson = () => {
const jsonData = JSON.stringify(points());
const file = new Blob([jsonData], {type: "application/json"});
saveAs(file, "output.json");
};
return (
<div class="grid grid-cols-[30rem_auto]">
<div>
<h1 class="text-c-primary text-center font-bold text-2xl py-4">AQP combi</h1>
<h2 class="bg-c-primary text-white py-2 px-2 font-bold text-lg">
Creador de rutas
</h2>
<p class="px-2">
Haz click en cualquier parte del mapa para empezar.
<br />
Luego, haz click izquierdo en el mapa para agregar un
punto.
<br />
Haz click en el boton "Retroceder" para borrar el último punto.
<br />
Haz click en el boton "Reiniciar" para borrar todos los puntos.
<br />
Presiona el boton "Guardar" para generar las
coordenadas de la ruta.
</p>
<div class="px-2 py-4">
Puntos:
<br />
<div class="h-40 overflow-scroll p-2 my-2 rounded border-2 border-c-primary">
<For each={points()}>
{(p) => <p class="font-mono">{p.lat},{p.lng}</p>}
</For>
</div>
<button class="bg-c-primary text-white py-2 px-4 rounded mr-2"
onClick={() => {
if (points().length >= 1) {
setPoints((x) => x.slice(0, -1));
}
}}
>
Retroceder
</button>
<button class="bg-c-primary text-white py-2 px-4 rounded mr-2"
onClick={() => {
setPoints([]);
}}
>
Reiniciar
</button>
<button class="bg-c-primary text-white py-2 px-4 rounded mr-2"
onClick={saveJson}
>
Guardar
</button>
</div>
</div>
<div class="py-[0.5vh] pr-2">
<div class="rounded-lg overflow-hidden p-1"
style="box-shadow: inset 0 0 5px 0px var(--main)"
>
{container}
</div>
</div>
</div>
);
}

View File

@ -13,7 +13,6 @@ export function Index() {
.setView([-16.40171, -71.53040], 13); .setView([-16.40171, -71.53040], 13);
g_map = l_map; g_map = l_map;
// tileLayer("http://localhost:34005/tiles/{z}/{x}/{y}.jpg", {
tileLayer("/tiles/{z}/{x}/{y}.jpg", { tileLayer("/tiles/{z}/{x}/{y}.jpg", {
maxZoom: 17, maxZoom: 17,
minZoom: 12, minZoom: 12,
@ -91,6 +90,11 @@ function RouteEl(props: { route: Route, parent_id: number, color: string }) {
if (!res.ok) throw new Error("Error fetching data"); if (!res.ok) throw new Error("Error fetching data");
return res.json(); return res.json();
}); });
const [return_points] = createResource<PointsWrapper>(async() => {
const res = await fetch(`/data/cuenca_${props.parent_id}_ruta_${props.route.id_ruta}_vuelta.json`);
if (!res.ok) throw new Error("Error fetching data");
return res.json();
});
createEffect(() => { createEffect(() => {
if (points.state === "ready") { if (points.state === "ready") {
@ -102,6 +106,16 @@ function RouteEl(props: { route: Route, parent_id: number, color: string }) {
} }
}); });
createEffect(() => {
if (return_points.state === "ready") {
// Render the dots into the map
const coords = return_points()!.data[0]!.ruta_json;
const line = polyline(coords, { color: props.color});
line.addTo(g_map!);
}
});
return ( return (
<div class="pl-10"> <div class="pl-10">
Ruta {props.route.cod_ruta} Ruta {props.route.cod_ruta}