Skip to content

Commit

Permalink
Rewrite the route snapper (#8)
Browse files Browse the repository at this point in the history
Totally rewrite the route and area tools
  • Loading branch information
dabreegster authored Nov 25, 2024
1 parent 224ac17 commit b0d5fb5
Show file tree
Hide file tree
Showing 24 changed files with 957 additions and 521 deletions.
58 changes: 9 additions & 49 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,11 @@
"@turf/nearest-point-on-line": "^7.0.0",
"govuk-frontend": "^5.4.1",
"govuk-svelte": "github:acteng/govuk-svelte",
"maplibre-draw-polygon": "github:dabreegster/maplibre-draw-polygon",
"maplibre-gl": "^4.0.2",
"route-snapper": "^0.4.1",
"route-snapper": "^0.4.9",
"route-snapper-ts": "^0.0.8",
"sass": "^1.77.8",
"svelte-maplibre": "^0.9.7",
"svelte-maplibre": "^0.9.14",
"uuid": "^10.0.0"
},
"svelte": "./dist/index.js",
Expand Down
File renamed without changes
1 change: 0 additions & 1 deletion src/lib/assets/polygon_snapped.svg

This file was deleted.

98 changes: 70 additions & 28 deletions src/lib/draw/EditMode.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@
import type { Feature, LineString, Point, Polygon } from "geojson";
import {
mode,
polygonTool,
routeTool,
featureProps,
pointPosition,
setPrecision,
} from "$lib/draw/stores";
import { waypoints as routeWaypoints } from "./route/stores";
import { waypoints as areaWaypoints, calculateArea } from "./area/stores";
import type { FeatureWithID, Schemes } from "$lib/draw/types";
import { type Config } from "$lib/config";
import { onDestroy, onMount } from "svelte";
import PointControls from "./point/PointControls.svelte";
import PolygonControls from "./polygon/PolygonControls.svelte";
import RouteControls from "./route/RouteControls.svelte";
import SnapPolygonControls from "./snap_polygon/SnapPolygonControls.svelte";
import AreaControls from "./area/AreaControls.svelte";
import type { AreaProps, RouteProps } from "route-snapper-ts";
import type { Writable } from "svelte/store";
Expand All @@ -41,28 +41,47 @@
unsavedFeature = JSON.parse(JSON.stringify(feature));
if (feature.geometry.type == "LineString") {
// TODO Update route-snapper-ts to use Except<route_name> or otherwise pick the important properties
$routeTool?.editExistingRoute(
feature as unknown as Feature<LineString, RouteProps>,
);
$routeTool?.addEventListenerSuccess(onSuccess);
$routeTool?.addEventListenerUpdated(onUpdate);
$routeTool?.addEventListenerFailure(onFailure);
if (feature.properties.waypoints) {
// Transform into the correct format
$routeWaypoints = feature.properties.waypoints.map((waypt) => {
return {
point: [waypt.lon, waypt.lat],
snapped: waypt.snapped,
};
});
} else {
// Imported from another file. Assume every point is freehand; the user
// can manually turn into snapped if needed.
$routeWaypoints = feature.geometry.coordinates.map((pt) => {
return {
point: JSON.parse(JSON.stringify(pt)),
snapped: false,
};
});
}
controls = "route";
} else if (feature.geometry.type == "Polygon") {
if (feature.properties.waypoints) {
$routeTool?.editExistingArea(feature as Feature<Polygon, AreaProps>);
$routeTool?.addEventListenerSuccess(onSuccess);
$routeTool?.addEventListenerUpdated(onUpdate);
$routeTool?.addEventListenerFailure(onFailure);
controls = "snapped-polygon";
// Transform into the correct format
$areaWaypoints = feature.properties.waypoints.map((waypt) => {
return {
point: [waypt.lon, waypt.lat],
snapped: waypt.snapped,
};
});
} else {
$polygonTool?.editExisting(feature as Feature<Polygon>);
$polygonTool?.addEventListenerSuccess(onSuccess);
$polygonTool?.addEventListenerUpdated(onUpdate);
$polygonTool?.addEventListenerFailure(onFailure);
controls = "freehand-polygon";
// Imported from another file. Assume every point is freehand; the user
// can manually turn into snapped if needed. Ignore any holes.
$areaWaypoints = feature.geometry.coordinates[0].map((pt) => {
return {
point: JSON.parse(JSON.stringify(pt)),
snapped: false,
};
});
$areaWaypoints.pop();
$areaWaypoints = $areaWaypoints;
}
controls = "area";
} else if (feature.geometry.type == "Point") {
$pointPosition = JSON.parse(JSON.stringify(feature.geometry.coordinates));
controls = "point";
Expand All @@ -74,9 +93,6 @@
$routeTool?.stop();
$routeTool?.clearEventListeners();
$polygonTool?.stop();
$polygonTool?.clearEventListeners();
gjSchemes.update((gj) => {
let featureToBeUpdated = gj.features.find((f) => f.id == id);
// If the feature was deleted, stop
Expand Down Expand Up @@ -148,6 +164,34 @@
mode.set({ mode: "list" });
}
function finishRoute() {
// Don't constantly update unsavedFeature for routes; it'll do unnecessary extra work.
if ($routeTool) {
try {
let out = $routeTool.inner.calculateRoute($routeWaypoints);
unsavedFeature = JSON.parse(out);
} catch (err) {
console.warn(`Finishing route failed: ${err}`);
}
}
finish();
}
function finishArea() {
// Don't constantly update unsavedFeature for areas; it'll do unnecessary extra work.
if ($routeTool && $areaWaypoints.length >= 3) {
try {
unsavedFeature = calculateArea(
$routeTool,
$areaWaypoints,
) as FeatureWithID<F>;
} catch (err) {
console.warn(`Finishing area failed: ${err}`);
}
}
finish();
}
function cancel() {
unsavedFeature = null;
mode.set({ mode: "list" });
Expand All @@ -157,9 +201,7 @@
{#if controls == "point"}
<PointControls {finish} {cancel} />
{:else if controls == "route"}
<RouteControls extendRoute={false} {finish} {cancel} />
{:else if controls == "freehand-polygon"}
<PolygonControls {finish} {cancel} />
{:else if controls == "snapped-polygon"}
<SnapPolygonControls {finish} {cancel} />
<RouteControls finish={finishRoute} {cancel} />
{:else if controls == "area"}
<AreaControls finish={finishArea} {cancel} />
{/if}
2 changes: 2 additions & 0 deletions src/lib/draw/TinyRadio.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// A list of [value, label] representing the choices
export let choices: [string, string][];
export let style = "";
// The current value
export let value: string;
Expand All @@ -15,6 +16,7 @@
<div
class="govuk-radios govuk-radios--inline govuk-radios--small"
data-module="govuk-radios"
{style}
>
{#each choices as [thisValue, thisLabel]}
<div class="govuk-radios__item">
Expand Down
36 changes: 8 additions & 28 deletions src/lib/draw/Toolbox.svelte
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
<script lang="ts" generics="F, S">
import { mode, polygonTool, routeTool } from "./stores";
import { mode, routeTool } from "./stores";
import imageIcon from "$lib/assets/image.svg";
import pointIcon from "$lib/assets/point.svg";
import polygonFreehandIcon from "$lib/assets/polygon_freehand.svg";
import polygonSnappedIcon from "$lib/assets/polygon_snapped.svg";
import areaIcon from "$lib/assets/area.svg";
import routeIcon from "$lib/assets/route.svg";
import splitRouteIcon from "$lib/assets/split_route.svg";
import streetViewIcon from "$lib/assets/street_view.svg";
Expand All @@ -14,26 +13,19 @@
import EditMode from "./EditMode.svelte";
import ImageMode from "./image/ImageMode.svelte";
import PointMode from "./point/PointMode.svelte";
import PolygonMode from "./polygon/PolygonMode.svelte";
import RouteMode from "./route/RouteMode.svelte";
import SplitRouteMode from "./route/SplitRouteMode.svelte";
import SnapPolygonMode from "./snap_polygon/SnapPolygonMode.svelte";
import AreaMode from "./area/AreaMode.svelte";
import StreetViewMode from "./StreetViewMode.svelte";
import { onDestroy } from "svelte";
import { PolygonTool } from "maplibre-draw-polygon";
import RouteSnapperLoader from "./route/RouteSnapperLoader.svelte";
import ToolButton from "./ToolButton.svelte";
export let cfg: Config<F, S>;
export let gjSchemes: Writable<Schemes<F, S>>;
export let routeSnapperUrl: string;
$: if ($map && !$polygonTool) {
polygonTool.set(new PolygonTool($map));
}
onDestroy(() => {
$polygonTool?.tearDown();
$routeTool?.tearDown();
});
</script>
Expand All @@ -58,19 +50,9 @@ repeatedly load anything. Make sure this is only created once, then just hidden.
<img src={routeIcon} alt="New route" />
New route
</ToolButton>
<ToolButton
setMode={{ mode: "new-freehand-polygon" }}
disabled={!$polygonTool}
>
<img src={polygonFreehandIcon} alt="New area (freehand)" />
New area (freehand)
</ToolButton>
<ToolButton
setMode={{ mode: "new-snapped-polygon" }}
disabled={!$routeTool}
>
<img src={polygonSnappedIcon} alt="New area (snapped)" />
New area (snapped)
<ToolButton setMode={{ mode: "new-area" }} disabled={!$routeTool}>
<img src={areaIcon} alt="New area" />
New area
</ToolButton>
<ToolButton setMode={{ mode: "split-route" }}>
<img src={splitRouteIcon} alt="Split route" />
Expand All @@ -97,10 +79,8 @@ repeatedly load anything. Make sure this is only created once, then just hidden.
<PointMode {cfg} {gjSchemes} />
{:else if $mode.mode == "new-route"}
<RouteMode {cfg} {gjSchemes} />
{:else if $mode.mode == "new-freehand-polygon"}
<PolygonMode {cfg} {gjSchemes} />
{:else if $mode.mode == "new-snapped-polygon"}
<SnapPolygonMode {cfg} {gjSchemes} />
{:else if $mode.mode == "new-area"}
<AreaMode {cfg} {gjSchemes} />
{:else if $mode.mode == "split-route"}
<SplitRouteMode {cfg} {gjSchemes} />
{:else if $mode.mode == "set-image"}
Expand Down
Loading

0 comments on commit b0d5fb5

Please sign in to comment.