Create a route editor
This commit is contained in:
parent
02e31e2df4
commit
f75f33611a
@ -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"
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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
123
src/pages/Editor.tsx
Normal 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: "© 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>
|
||||||
|
);
|
||||||
|
}
|
@ -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}
|
||||||
|
Loading…
Reference in New Issue
Block a user