Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draggable route markers starts on screen. #217

Merged
merged 1 commit into from
Mar 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 20 additions & 9 deletions web/src/ImpactOneDestinationMode.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script lang="ts">
import type { Feature, FeatureCollection } from "geojson";
import { LngLat, type MapMouseEvent } from "maplibre-gl";
import { onMount } from "svelte";
import { FillLayer, GeoJSON, LineLayer, MapEvents } from "svelte-maplibre";
import {
constructMatchExpression,
Expand All @@ -19,43 +20,53 @@
import { ModalFilterLayer, RenderNeighbourhood } from "./layers";
import {
backend,
ensurePointInVisibleBounds,
mode,
one_destination,
oneDestination,
returnToChooseProject,
route_pt_a,
route_pt_b,
routePtA,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, thanks for cleaning this up. I struggle to switch between underscores and camel-case for Rust/JSON and TS

routePtB,
} from "./stores";

function back() {
$mode = { mode: "neighbourhood" };
}

$: perRoadGj = $backend!.impactToOneDestination($one_destination);
$: perRoadGj = $backend!.impactToOneDestination($oneDestination);

let hovered: Feature | null = null;

$: routeGj = previewRoutes(hovered);

onMount(() => {
// There seems to be a race with the Marker component, so we wait just a bit before updating.
setTimeout(() => {
if ($oneDestination) {
ensurePointInVisibleBounds(oneDestination);
}
}, 10);
});

function previewRoutes(hovered: Feature | null): FeatureCollection {
if (!hovered) {
return emptyGeojson();
}
return $backend!.compareRoute(
new LngLat(hovered.properties!.pt1_x, hovered.properties!.pt1_y),
$one_destination,
$oneDestination,
1.0,
);
}

function compareRoute(f: Feature) {
$route_pt_a = new LngLat(f.properties!.pt1_x, f.properties!.pt1_y);
$route_pt_b = $one_destination;
$routePtA = new LngLat(f.properties!.pt1_x, f.properties!.pt1_y);
$routePtB = $oneDestination;
$mode = { mode: "route", prevMode: "impact-one-destination" };
}

function onRightClick(e: CustomEvent<MapMouseEvent>) {
// Move the first marker, for convenience
$one_destination = e.detail.lngLat;
$oneDestination = e.detail.lngLat;
}
</script>

Expand Down Expand Up @@ -158,6 +169,6 @@

<ModalFilterLayer />

<DotMarker bind:lngLat={$one_destination} draggable>X</DotMarker>
<DotMarker bind:lngLat={$oneDestination} draggable>X</DotMarker>
</div>
</SplitComponent>
20 changes: 15 additions & 5 deletions web/src/RouteMode.svelte
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script lang="ts">
import { onMount } from "svelte";
import { GeoJSON, LineLayer } from "svelte-maplibre";
import { constructMatchExpression } from "svelte-utils/map";
import { SplitComponent } from "svelte-utils/top_bar_layout";
Expand All @@ -20,19 +21,28 @@
} from "./layers";
import {
backend,
ensurePointInVisibleBounds,
mainRoadPenalty,
mode,
returnToChooseProject,
route_pt_a,
route_pt_b,
routePtA,
routePtB,
} from "./stores";

export let prevMode:
| "pick-neighbourhood"
| "neighbourhood"
| "impact-one-destination";

$: gj = $backend!.compareRoute($route_pt_a, $route_pt_b, $mainRoadPenalty);
$: gj = $backend!.compareRoute($routePtA, $routePtB, $mainRoadPenalty);

onMount(() => {
// There seems to be a race with the Marker component, so we wait just a bit before updating.
setTimeout(() => {
ensurePointInVisibleBounds(routePtA);
ensurePointInVisibleBounds(routePtB);
}, 10);
});

function back() {
$mode = { mode: prevMode };
Expand Down Expand Up @@ -126,7 +136,7 @@
/>
</GeoJSON>

<DotMarker bind:lngLat={$route_pt_a} draggable>A</DotMarker>
<DotMarker bind:lngLat={$route_pt_b} draggable>B</DotMarker>
<DotMarker bind:lngLat={$routePtA} draggable>A</DotMarker>
<DotMarker bind:lngLat={$routePtB} draggable>B</DotMarker>
</div>
</SplitComponent>
35 changes: 31 additions & 4 deletions web/src/stores.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import type { Feature, Polygon } from "geojson";
import { LngLat, type LngLatBoundsLike, type Map } from "maplibre-gl";
import {
LngLat,
LngLatBounds,
type LngLatBoundsLike,
type Map,
} from "maplibre-gl";
import { type AreaProps } from "route-snapper-ts";
import { get, writable, type Writable } from "svelte/store";
import type { Backend } from "./wasm";
Expand Down Expand Up @@ -68,9 +73,9 @@ export let showAbout: Writable<boolean> = writable(false);
export let appFocus: Writable<"global" | "cnt"> = writable("global");

export let backend: Writable<Backend | null> = writable(null);
export let route_pt_a: Writable<LngLat> = writable(new LngLat(0, 0));
export let route_pt_b: Writable<LngLat> = writable(new LngLat(0, 0));
export let one_destination: Writable<LngLat> = writable(new LngLat(0, 0));
export let routePtA: Writable<LngLat> = writable(new LngLat(0, 0));
export let routePtB: Writable<LngLat> = writable(new LngLat(0, 0));
export let oneDestination: Writable<LngLat> = writable(new LngLat(0, 0));
export let mainRoadPenalty: Writable<number> = writable(1.0);
// A way for different components to know when internal app state has changed
// and they might need to rerender
Expand Down Expand Up @@ -110,3 +115,25 @@ export function returnToChooseProject() {
}
get(map)?.fitBounds(bounds, { duration: 500 });
}

export function ensurePointInVisibleBounds(point: Writable<LngLat>) {
function randomPoint(bounds: LngLatBounds): LngLat {
const width = bounds.getEast() - bounds.getWest();
let lng = bounds.getWest() + Math.random() * width;

const height = bounds.getNorth() - bounds.getSouth();
let lat = bounds.getSouth() + Math.random() * height;

return new LngLat(lng, lat);
}

const bounds: LngLatBounds | undefined = get(map)?.getBounds();
if (!bounds) {
console.assert(false, "missing map bounds");
return;
}

if (!bounds.contains(get(point))) {
point.set(randomPoint(bounds));
}
}
14 changes: 0 additions & 14 deletions web/src/title/loader.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { Feature, Polygon } from "geojson";
import { LngLat } from "maplibre-gl";
import { RouteTool } from "route-snapper-ts";
import { emptyGeojson } from "svelte-utils/map";
import { overpassQueryForPolygon } from "svelte-utils/overpass";
Expand All @@ -13,9 +12,6 @@ import {
currentProjectKey,
map,
mode,
one_destination,
route_pt_a,
route_pt_b,
} from "../stores";
import { Backend } from "../wasm";

Expand Down Expand Up @@ -160,19 +156,9 @@ export function afterProjectLoaded() {
),
);
get(map)!.fitBounds(get(backend)!.getBounds(), { duration: 500 });
route_pt_a.set(randomPoint());
route_pt_b.set(randomPoint());
one_destination.set(randomPoint());

// Update the URL
let url = new URL(window.location.href);
url.searchParams.set("project", get(currentProjectKey));
window.history.replaceState(null, "", url.toString());
}

function randomPoint(): LngLat {
let bounds = get(backend)!.getBounds();
let lng = bounds[0] + Math.random() * (bounds[2] - bounds[0]);
let lat = bounds[1] + Math.random() * (bounds[3] - bounds[1]);
return new LngLat(lng, lat);
}