Compare commits
10 Commits
28cbb1791b
...
2491560cd2
Author | SHA1 | Date | |
---|---|---|---|
2491560cd2 | |||
f6cdf09974 | |||
eb24c56bf6 | |||
5d5d13f0b7 | |||
bac8157aec | |||
81ca9d1bd6 | |||
f16f1360e3 | |||
edf0abe86a | |||
047a6ad218 | |||
955510dae8 |
@ -4,7 +4,7 @@
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"start": "vite",
|
||||
"dev": "vite",
|
||||
"dev": "vite --host",
|
||||
"build": "vite build",
|
||||
"serve": "vite preview"
|
||||
},
|
||||
|
@ -2,21 +2,9 @@
|
||||
{
|
||||
"id": 1,
|
||||
"name": "1",
|
||||
"color": "#ea4fb2"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "2",
|
||||
"color": "black"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "3",
|
||||
"color": "#eab308"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "4",
|
||||
"color": "#ff7300"
|
||||
"district": "Hunter/Tiabaya",
|
||||
"color": "#ea4fb2",
|
||||
"hover_bg": "#ea4fb2",
|
||||
"hover_on_bg": "white"
|
||||
}
|
||||
]
|
File diff suppressed because one or more lines are too long
13
public/n/routes_5.json
Normal file
13
public/n/routes_5.json
Normal file
File diff suppressed because one or more lines are too long
@ -3,6 +3,7 @@ import { Index } from "./pages/Index";
|
||||
import { Route, Router } from "@solidjs/router";
|
||||
import { Editor } from "./pages/Editor";
|
||||
import { Index2 } from "./pages/Index2";
|
||||
import { Arrow } from "./pages/Arrow";
|
||||
|
||||
export default function() {
|
||||
return (
|
||||
@ -11,6 +12,7 @@ export default function() {
|
||||
<Route path="/" component={Index} />
|
||||
<Route path="/new" component={Index2} />
|
||||
<Route path="/editor" component={Editor} />
|
||||
<Route path="/arrow" component={Arrow} />
|
||||
</Router>
|
||||
</>
|
||||
);
|
||||
|
@ -5,22 +5,40 @@ export interface Line {
|
||||
id: number
|
||||
/** E.g.: `10` */
|
||||
name: string
|
||||
/** name of the district of origin */
|
||||
district: string
|
||||
/** color of the routes on the map */
|
||||
// map_color: string
|
||||
/** Hex code */
|
||||
color: string
|
||||
hover_bg: string
|
||||
hover_on_bg: string
|
||||
}
|
||||
|
||||
export type Routes = Array<Route>
|
||||
|
||||
export type Point = [number, number]
|
||||
export type ArrowPoints = [Point,Point,Point,Point]
|
||||
|
||||
export interface Route {
|
||||
/** Name that differentiates this route from others
|
||||
* from the same line.
|
||||
*
|
||||
* May contain any combinatior of letters and numbers.
|
||||
*
|
||||
* E.g.: `A`, `B2-A`
|
||||
/**
|
||||
* Name that identifies this route
|
||||
*/
|
||||
name: string
|
||||
/** `[lat,lng]`, stored as arrays to save space in JSON */
|
||||
points: Array<[number, number]>
|
||||
/**
|
||||
* Set of points that buses running this route go to
|
||||
*/
|
||||
departure: Array<Point>
|
||||
/**
|
||||
* Arrows that indicate in which direction the buses go
|
||||
*/
|
||||
departure_arrows: Array<ArrowPoints>
|
||||
/**
|
||||
* Set of points that buses running this route go to
|
||||
*/
|
||||
return: Array<Point>
|
||||
/**
|
||||
* Arrows that indicate in which direction the buses go
|
||||
*/
|
||||
return_arrows: Array<ArrowPoints>
|
||||
}
|
||||
|
||||
|
195
src/pages/Arrow.tsx
Normal file
195
src/pages/Arrow.tsx
Normal file
@ -0,0 +1,195 @@
|
||||
import L from "leaflet";
|
||||
import { createEffect, createSignal, onMount } from "solid-js";
|
||||
|
||||
let map: L.Map | null = null;
|
||||
|
||||
export function Arrow() {
|
||||
const container = <div class="h-[98vh] rounded-lg dark:opacity-60" />;
|
||||
const [first_point, set_first_point] = createSignal<L.LatLng | null>(null);
|
||||
const [second_point, set_second_point] = createSignal<L.LatLng | null>(null);
|
||||
const [points, setPoints] = createSignal<Array<[number, number]>>([]);
|
||||
let arrows: Array<[L.LatLng, L.LatLng, L.LatLng, L.LatLng]> = [];
|
||||
|
||||
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 point = ev.latlng;
|
||||
|
||||
if (first_point() === null) {
|
||||
// Set first point
|
||||
set_first_point(point);
|
||||
return;
|
||||
}
|
||||
if (second_point() === null) {
|
||||
set_second_point(point);
|
||||
|
||||
// Compute the triangle
|
||||
const p1 = first_point()!;
|
||||
|
||||
const points = compute_triangle_points(p1, point);
|
||||
|
||||
// Draw the arrow.
|
||||
L.polygon(points, { color: "red" }).addTo(map!);
|
||||
arrows.push(points);
|
||||
|
||||
// Reset
|
||||
set_first_point(null);
|
||||
set_second_point(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let current_polyline: L.Polyline | null = null;
|
||||
createEffect(() => {
|
||||
const dots = points().map(([lat, lng]) => ({ lat, lng }));
|
||||
if (dots.length === 0) {
|
||||
current_polyline?.removeFrom(map!);
|
||||
current_polyline = null;
|
||||
}
|
||||
|
||||
current_polyline = L.polyline(dots, { color: "red" });
|
||||
current_polyline.addTo(map!);
|
||||
});
|
||||
|
||||
|
||||
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 flechas
|
||||
</h2>
|
||||
|
||||
<p class="px-2">
|
||||
Ingresá una ruta.
|
||||
<br />
|
||||
Haz click en un punto de la ruta (punto inicial)
|
||||
<br />
|
||||
Haz click en otro punto de la ruta (punto final)
|
||||
<br />
|
||||
Un triangulo se creará en el medio, apuntdando hacia
|
||||
el punto final
|
||||
</p>
|
||||
|
||||
<div class="px-2 py-4">
|
||||
Ruta:
|
||||
<br />
|
||||
<textarea
|
||||
class="inline-block w-full bg-black text-white p-1 my-2 rounded"
|
||||
name="" id=""
|
||||
onInput={(e) => setPoints(JSON.parse(e.target.value))}
|
||||
/>
|
||||
|
||||
Puntos:
|
||||
|
||||
<div>
|
||||
Punto 1: {first_point()?.lat ?? "<vacio>"}, {first_point()?.lng ?? "<vacio>"}
|
||||
<br />
|
||||
Punto 2: {second_point()?.lat ?? "<vacio>"}, {second_point()?.lng ?? "<vacio>"}
|
||||
</div>
|
||||
|
||||
<button class="bg-c-primary text-white py-2 px-4 rounded mr-2"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(JSON.stringify(arrows))
|
||||
.then(() => alert("copiado"))
|
||||
.catch(() => alert("error copiando"));
|
||||
}}
|
||||
>
|
||||
Copiar flechas
|
||||
</button>
|
||||
<button class="bg-c-primary text-white py-2 px-4 rounded mr-2"
|
||||
onClick={() => {
|
||||
const confirmation = confirm("¿Estas seguro? Se perderán todos los puntos");
|
||||
if (confirmation) {
|
||||
setPoints([]);
|
||||
set_first_point(null);
|
||||
set_second_point(null);
|
||||
arrows = [];
|
||||
}
|
||||
}}
|
||||
>
|
||||
Reiniciar
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* p0
|
||||
* /\
|
||||
* /__\
|
||||
* p1 p2
|
||||
*
|
||||
* [p0, p1, p2]
|
||||
*
|
||||
* Calculates the points for a isosceles triangle
|
||||
* that points toward the passed line
|
||||
*
|
||||
* @param start The starting point
|
||||
*/
|
||||
function compute_triangle_points(start: L.LatLng, end: L.LatLng): [L.LatLng, L.LatLng, L.LatLng, L.LatLng] {
|
||||
// Determines the size of the triangle
|
||||
const delta = (start.lat < end.lat) ? 0.0002 : -0.0002;
|
||||
|
||||
const { lat: x1, lng: y1 } = start;
|
||||
const { lat: x2, lng: y2 } = end;
|
||||
// Middle point
|
||||
const [x3, y3] = [(x1 + x2) / 2, (y1 + y2) / 2];
|
||||
|
||||
// Compute the angle of the rect
|
||||
// what do you call the "pendiente" of a rect?
|
||||
const pendiente = (y2 - y1) / (x2 - x1);
|
||||
const angle = Math.atan(pendiente);
|
||||
|
||||
// Vector pointing upwards
|
||||
const [vx, vy] = [delta, 0];
|
||||
|
||||
// Rotate that vector to get the delta for each component
|
||||
const [nvx, nvy] = [
|
||||
(vx * Math.cos(angle)) - (vy * Math.sin(angle)),
|
||||
(vx * Math.sin(angle)) + (vy * Math.cos(angle)),
|
||||
];
|
||||
// Round to 5 decimals
|
||||
const [x_delta, y_delta] = [
|
||||
(Math.round(nvx * 100000)) / 100000,
|
||||
(Math.round(nvy * 100000)) / 100000,
|
||||
];
|
||||
|
||||
// Use x_delta and y_delta to get the neccesary points
|
||||
|
||||
// Top
|
||||
const [x4, y4] = [x3 + x_delta, y3 + y_delta];
|
||||
// Bottom
|
||||
const [x5, y5] = [x3 - x_delta, y3 - y_delta];
|
||||
// Left
|
||||
const [x6, y6] = [x5 + y_delta, y5 - x_delta];
|
||||
// Right
|
||||
const [x7, y7] = [x5 - y_delta, y5 + x_delta];
|
||||
|
||||
return [
|
||||
new L.LatLng(x4, y4),
|
||||
new L.LatLng(x6, y6),
|
||||
new L.LatLng(x3, y3),
|
||||
new L.LatLng(x7, y7),
|
||||
];
|
||||
}
|
||||
|
@ -1,14 +1,22 @@
|
||||
import { saveAs } from "file-saver";
|
||||
import L from "leaflet";
|
||||
import { createEffect, createSignal, For, onMount } from "solid-js";
|
||||
import { Accessor, createEffect, createResource, createSignal, For, onMount, Setter } from "solid-js";
|
||||
import { Line, Route } from "../new_types";
|
||||
|
||||
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>>([]);
|
||||
const [route_name, set_route_name] = createSignal("");
|
||||
const [departure_points, set_departure_points] = createSignal<Array<L.LatLng>>([]);
|
||||
const [return_points, set_return_points] = createSignal<Array<L.LatLng>>([]);
|
||||
|
||||
let currentPolyline: L.Polyline | null = null;
|
||||
const [lines] = createResource<Array<Line>>(async() => {
|
||||
const data = await fetch("/n/lines.json");
|
||||
if (!data.ok) {
|
||||
throw new Error("Error fetching");
|
||||
}
|
||||
return data.json();
|
||||
});
|
||||
|
||||
onMount(() => {
|
||||
map = L.map(container as HTMLElement)
|
||||
@ -17,111 +25,63 @@ export function Editor() {
|
||||
L.tileLayer("/tiles/{z}/{x}/{y}.jpg", {
|
||||
maxZoom: 18,
|
||||
minZoom: 12,
|
||||
attribution: "© Map data <a href=\"http://www.openstreetmap.org/copyright\">OpenStreetMap</a>",
|
||||
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().map((obj) => [obj.lat,obj.lng]));
|
||||
const file = new Blob([jsonData], {type: "application/json"});
|
||||
saveAs(file, "output.json");
|
||||
const gen_route = () => {
|
||||
const new_route: Route = {
|
||||
name: route_name(),
|
||||
departure: departure_points().map(({lat,lng}) => [lat,lng]),
|
||||
departure_arrows: [],
|
||||
return: return_points().map(({lat,lng}) => [lat,lng]),
|
||||
return_arrows: [],
|
||||
};
|
||||
|
||||
const copyPoints = () => {
|
||||
const jsonData = JSON.stringify(points().map((obj) => [obj.lat,obj.lng]));
|
||||
navigator.clipboard.writeText(jsonData)
|
||||
.then(() => alert("Copiado"))
|
||||
.catch(() => alert("Error al copiar."));
|
||||
navigator.clipboard.writeText(JSON.stringify(new_route))
|
||||
.then(() => alert("Copied to clipboard"))
|
||||
.catch(() => alert("Error copying to clipboard"));
|
||||
};
|
||||
|
||||
return (
|
||||
<div class="grid grid-cols-[30rem_auto]">
|
||||
<div class="grid grid-cols-[20rem_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>}
|
||||
<div class="px-2">
|
||||
<p>Selecciona una linea:</p>
|
||||
<select name="line_selection" class="bg-c-bg text-c-on-bg w-full border border-c-on-bg rounded p-2">
|
||||
<For each={lines()}>
|
||||
{(line) => <option value=":D">{line.id} - {line.district}</option>}
|
||||
</For>
|
||||
</select>
|
||||
<p class="pt-2 pb-1">Nombre de la ruta (ejm: B2-A):</p>
|
||||
<input type="text" class="bg-c-bg text-c-on-bg w-full border border-c-on-bg rounded p-2"
|
||||
value={route_name()}
|
||||
onInput={(e) => set_route_name(e.target.value)}
|
||||
/>
|
||||
|
||||
<RouteSelector route_type="Ida" district={"TODO"} color="red"
|
||||
points={departure_points}
|
||||
setPoints={set_departure_points}
|
||||
/>
|
||||
<RouteSelector route_type="Vuelta" district={"TODO"} color="blue"
|
||||
points={return_points}
|
||||
setPoints={set_return_points}
|
||||
/>
|
||||
|
||||
<div class="text-left">
|
||||
|
||||
<button
|
||||
class="bg-green-400 text-black py-1 px-4 rounded"
|
||||
onClick={gen_route}
|
||||
>
|
||||
Copiar JSON
|
||||
</button>
|
||||
</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={() => {
|
||||
const confirmation = confirm("¿Estas seguro? Se perderán todos los puntos");
|
||||
if (confirmation) setPoints([]);
|
||||
}}
|
||||
>
|
||||
Reiniciar
|
||||
</button>
|
||||
<button class="bg-c-primary text-white py-2 px-4 rounded mr-2"
|
||||
onClick={copyPoints}
|
||||
>
|
||||
Copiar puntos
|
||||
</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">
|
||||
@ -134,3 +94,86 @@ export function Editor() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function RouteSelector(props: {
|
||||
route_type: "Ida"|"Vuelta",
|
||||
district: string,
|
||||
color: string,
|
||||
points: Accessor<Array<L.LatLng>>,
|
||||
setPoints: Setter<Array<L.LatLng>>,
|
||||
}) {
|
||||
const [active, setActive] = createSignal(false);
|
||||
const [points, setPoints] = [props.points, props.setPoints];
|
||||
let current_polyline: L.Polyline | undefined = undefined;
|
||||
|
||||
const click_listener: L.LeafletMouseEventHandlerFn = (ev) => {
|
||||
const latlng = ev.latlng;
|
||||
setPoints((x) => [...x, latlng]);
|
||||
};
|
||||
|
||||
const toggle_status = () => {
|
||||
const new_status = !(active());
|
||||
setActive(new_status);
|
||||
|
||||
if (new_status === true) {
|
||||
map!.addEventListener("click", click_listener);
|
||||
} else {
|
||||
map!.removeEventListener("click", click_listener);
|
||||
}
|
||||
};
|
||||
|
||||
createEffect(() => {
|
||||
const latlngs = points();
|
||||
if (latlngs.length === 0) {
|
||||
current_polyline?.removeFrom(map!);
|
||||
current_polyline = undefined;
|
||||
} else {
|
||||
current_polyline?.removeFrom(map!);
|
||||
current_polyline = L.polyline(latlngs, {color: props.color});
|
||||
current_polyline.addTo(map!);
|
||||
}
|
||||
});
|
||||
|
||||
const pop = () => {
|
||||
setPoints((p) => p.slice(0, -1));
|
||||
};
|
||||
|
||||
const active_classes = () => `${active() ? "border-green-400" : "border-transparent"}`;
|
||||
const button_classes = () => `${active() ? "animate-pulse" : ""}`;
|
||||
const direction_text = () => {
|
||||
if (props.route_type === "Ida") return `Desde ${props.district} hacia el centro`;
|
||||
else return `Desde el centro hacia ${props.district}`;
|
||||
};
|
||||
return (
|
||||
<div class={`p-1 rounded my-2 border-2 ${active_classes()}`}>
|
||||
<h2 class="text-lg underline">Ruta de {props.route_type}</h2>
|
||||
<p>{direction_text()}</p>
|
||||
|
||||
<div class="border border-c-on-bg rounded p-1 my-2">
|
||||
Puntos:
|
||||
<br />
|
||||
<div class="h-32 overflow-x-hidden overflow-y-scroll">
|
||||
<For each={points()}>
|
||||
{(point) => <div class="whitespace-nowrap overflow-hidden text-sm">{point.lat}; {point.lng}</div>}
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-right">
|
||||
<button
|
||||
class={"bg-green-400 text-black py-1 px-2 mr-1 rounded"}
|
||||
onClick={pop}
|
||||
>
|
||||
Retroceder
|
||||
</button>
|
||||
<button
|
||||
class={`bg-green-400 text-black py-1 px-4 rounded ${button_classes()}`}
|
||||
onClick={toggle_status}
|
||||
>
|
||||
{active() ? "Seleccionando..." : "Empezar selección"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -97,23 +97,20 @@ function RouteEl(props: { route: Route, parent_id: number, color: string }) {
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
if (points.state === "ready") {
|
||||
// Render the dots into the map
|
||||
if (!points()) return;
|
||||
const coords = points()!.data[0]!.ruta_json;
|
||||
|
||||
const line = polyline(coords, { color: props.color});
|
||||
line.addTo(g_map!);
|
||||
}
|
||||
});
|
||||
|
||||
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 (
|
||||
|
@ -1,28 +1,28 @@
|
||||
import { Map, map, polyline, tileLayer } from "leaflet";
|
||||
import { createResource, For, onMount, Suspense } from "solid-js";
|
||||
import L from "leaflet";
|
||||
import { createEffect, createResource, createSignal, For, onMount, Suspense } from "solid-js";
|
||||
import { LineSegmentsIcon } from "../icons/LineSegmentsIcon";
|
||||
import { Line, Lines, Route, Routes } from "../new_types";
|
||||
|
||||
let g_map: Map | null = null;
|
||||
let global_map: L.Map | null = null;
|
||||
const [global_count, set_global_count] = createSignal(0);
|
||||
|
||||
export function Index2() {
|
||||
const container = <div class="h-[98vh] rounded-lg dark:opacity-60" />;
|
||||
const container = <div class="md:h-[98vh] h-[65vh] md:rounded-lg dark:opacity-60" />;
|
||||
|
||||
onMount(() => {
|
||||
const l_map = map(container as HTMLElement)
|
||||
global_map = L.map(container as HTMLElement)
|
||||
.setView([-16.40171, -71.53040], 13);
|
||||
g_map = l_map;
|
||||
|
||||
tileLayer("/tiles/{z}/{x}/{y}.jpg", {
|
||||
L.tileLayer("/tiles/{z}/{x}/{y}.jpg", {
|
||||
maxZoom: 17,
|
||||
minZoom: 12,
|
||||
attribution: "© Map data <a href=\"http://www.openstreetmap.org/copyright\">OpenStreetMap</a>",
|
||||
}).addTo(l_map);
|
||||
attribution: "Map data © <a href=\"http://www.openstreetmap.org/copyright\">OpenStreetMap</a>",
|
||||
}).addTo(global_map);
|
||||
});
|
||||
|
||||
return (
|
||||
<div class="grid grid-cols-[15rem_auto]">
|
||||
<div class="h-screen overflow-scroll">
|
||||
<div class="grid md:grid-cols-[15rem_auto] md:grid-rows-none grid-rows-[65vh_35vh]">
|
||||
<div class="md:h-screen overflow-scroll">
|
||||
<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">
|
||||
@ -31,8 +31,8 @@ export function Index2() {
|
||||
|
||||
<LinesEl />
|
||||
</div>
|
||||
<div class="py-[0.5vh] pr-2">
|
||||
<div class="rounded-lg overflow-hidden p-1"
|
||||
<div class="md:py-[0.5vh] md:pr-2">
|
||||
<div class="md:rounded-lg overflow-hidden md:p-1"
|
||||
style="box-shadow: inset 0 0 5px 0px var(--main)"
|
||||
>
|
||||
{container}
|
||||
@ -68,15 +68,21 @@ function LineEl(props: { line: Line }) {
|
||||
});
|
||||
|
||||
return (
|
||||
<div style={`color: ${props.line.color}`}>
|
||||
<div
|
||||
style={`color: ${props.line.color};--hover-bg: ${props.line.hover_bg};--hover-on-bg: ${props.line.hover_on_bg}`}
|
||||
>
|
||||
<LineSegmentsIcon class="px-1" fill={props.line.color} />
|
||||
<span class="px-2">
|
||||
<span class="px-2" title={props.line.district}>
|
||||
Linea {props.line.name}
|
||||
</span>
|
||||
<Suspense>
|
||||
<For each={routesData() ?? []}>
|
||||
{(r) => (
|
||||
<RouteEl route={r} color={props.line.color} />
|
||||
<RouteEl
|
||||
line_name={props.line.name}
|
||||
route={r}
|
||||
color={props.line.color}
|
||||
/>
|
||||
)}
|
||||
</For>
|
||||
</Suspense>
|
||||
@ -84,17 +90,132 @@ function LineEl(props: { line: Line }) {
|
||||
);
|
||||
}
|
||||
|
||||
function RouteEl(props: { route: Route, color: string }) {
|
||||
// Render the dots into the map
|
||||
const coords = props.route.points.map(([lat,lng]) => ({lat, lng}));
|
||||
const line = polyline(coords, { color: props.color});
|
||||
line.addTo(g_map!);
|
||||
function RouteEl(props: {
|
||||
line_name: string,
|
||||
route: Route,
|
||||
color: string,
|
||||
}) {
|
||||
const [departure_active, set_departure_active] = createSignal(false);
|
||||
const [return_active, set_return_active] = createSignal(false);
|
||||
const [departure_count, set_departure_count] = createSignal(0);
|
||||
const [return_count, set_return_count] = createSignal(0);
|
||||
|
||||
// Create departure and return polylines
|
||||
const departure_polyline = L.polyline(props.route.departure, { color: props.color });
|
||||
const return_polyline = L.polyline(props.route.return, { color: props.color });
|
||||
|
||||
// Rended the lines into the map
|
||||
createEffect(() => {
|
||||
if (global_count() === 0 || departure_count() > 0) {
|
||||
departure_polyline.addTo(global_map!);
|
||||
} else {
|
||||
departure_polyline.removeFrom(global_map!);
|
||||
}
|
||||
});
|
||||
createEffect(() => {
|
||||
// if global count === 0, then no route is focused.
|
||||
// in that case, all routes should be rendered
|
||||
if (global_count() === 0 || return_count() > 0) {
|
||||
return_polyline.addTo(global_map!);
|
||||
} else {
|
||||
return_polyline.removeFrom(global_map!);
|
||||
}
|
||||
});
|
||||
|
||||
const toggle_departure = () => {
|
||||
const currently_active = departure_active();
|
||||
if (currently_active) {
|
||||
set_global_count((c) => c - 1);
|
||||
set_departure_count((c) => c - 1);
|
||||
set_departure_active(false);
|
||||
} else {
|
||||
set_global_count((c) => c + 1);
|
||||
set_departure_count((c) => c + 1);
|
||||
set_departure_active(true);
|
||||
}
|
||||
};
|
||||
const toggle_return = () => {
|
||||
const currently_active = return_active();
|
||||
if (currently_active) {
|
||||
set_global_count((c) => c - 1);
|
||||
set_return_count((c) => c - 1);
|
||||
set_return_active(false);
|
||||
} else {
|
||||
set_global_count((c) => c + 1);
|
||||
set_return_count((c) => c + 1);
|
||||
set_return_active(true);
|
||||
}
|
||||
};
|
||||
|
||||
const departure_classes = () => {
|
||||
if (departure_count() > 0) {
|
||||
return "bg-r-hover-bg text-r-hover-on-bg";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
const return_classes = () => {
|
||||
if (return_count() > 0) {
|
||||
return "bg-r-hover-bg text-r-hover-on-bg";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div class="pl-10">
|
||||
<button
|
||||
class="grid grid-cols-[auto_2.75rem_3.5rem] w-full text-left cursor-pointer"
|
||||
>
|
||||
<span
|
||||
class={"pl-10 border-2 border-transparent hover:border-r-hover-bg"}
|
||||
onMouseEnter={() => {
|
||||
set_global_count((c) => c + 1);
|
||||
set_departure_count((c) => c + 1);
|
||||
set_return_count((c) => c + 1);
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
set_global_count((c) => c - 1);
|
||||
set_departure_count((c) => c - 1);
|
||||
set_return_count((c) => c - 1);
|
||||
}}
|
||||
onClick={() => {
|
||||
if (departure_count() === 1) {
|
||||
toggle_departure();
|
||||
}
|
||||
if (return_count() === 1) {
|
||||
toggle_return();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Ruta {props.route.name}
|
||||
</div>
|
||||
</span>
|
||||
<span
|
||||
class={`text-center border-2 border-transparent ${departure_classes()}`}
|
||||
onMouseEnter={() => {
|
||||
set_global_count((c) => c + 1);
|
||||
set_departure_count((c) => c + 1);
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
set_global_count((c) => c - 1);
|
||||
set_departure_count((c) => c - 1);
|
||||
}}
|
||||
onClick={toggle_departure}
|
||||
>
|
||||
Ida
|
||||
</span>
|
||||
<span
|
||||
class={`text-center border-2 border-transparent ${return_classes()}`}
|
||||
onMouseEnter={() => {
|
||||
set_global_count((c) => c + 1);
|
||||
set_return_count((c) => c + 1);
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
set_global_count((c) => c - 1);
|
||||
set_return_count((c) => c - 1);
|
||||
}}
|
||||
onClick={toggle_return}
|
||||
>
|
||||
Vuelta
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
@ -7,6 +7,11 @@ module.exports = {
|
||||
extend: {
|
||||
colors: {
|
||||
"c-primary": "var(--main)",
|
||||
"c-bg": "var(--bg)",
|
||||
"c-on-bg": "var(--on-bg)",
|
||||
// colors for the routes colors
|
||||
"r-hover-bg": "var(--hover-bg)",
|
||||
"r-hover-on-bg": "var(--hover-on-bg)",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user