Skip to content

Commit

Permalink
Use a marker for the point tool (#7)
Browse files Browse the repository at this point in the history
* WIP: Redesign the point tool to use a marker

* Get double click working and rearrange how the marker is managed

* Move escape key handler, call setPrecision

* Rename vars
  • Loading branch information
dabreegster authored Nov 5, 2024
1 parent 764a0da commit d57a27c
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 137 deletions.
23 changes: 13 additions & 10 deletions src/lib/draw/EditMode.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
import type { Feature, LineString, Point, Polygon } from "geojson";
import {
mode,
pointTool,
polygonTool,
routeTool,
featureProps,
pointPosition,
setPrecision,
} from "$lib/draw/stores";
import type { FeatureWithID, Schemes } from "$lib/draw/types";
import { type Config } from "$lib/config";
Expand Down Expand Up @@ -63,18 +64,12 @@
controls = "freehand-polygon";
}
} else if (feature.geometry.type == "Point") {
// No need to pass in the existing feature.geometry; it's the same as
// where the cursor is anyway
$pointTool?.start();
$pointTool?.addEventListenerSuccess(onSuccess);
// No auto-save for updates
$pointTool?.addEventListenerFailure(onFailure);
$pointPosition = JSON.parse(JSON.stringify(feature.geometry.coordinates));
controls = "point";
}
});
onDestroy(() => {
$pointTool?.stop();
$pointTool?.clearEventListeners();
$pointPosition = null;
$routeTool?.stop();
$routeTool?.clearEventListeners();
Expand All @@ -101,6 +96,14 @@
});
});
// Listen to updates for points
$: updatePoint($pointPosition);
function updatePoint(pt: [number, number] | null) {
if (unsavedFeature?.geometry.type == "Point" && pt) {
unsavedFeature.geometry.coordinates = setPrecision(pt);
}
}
function onSuccess(feature: Feature<Point | Polygon | LineString>) {
feature.properties ??= {};
// Let onDestroy apply the update
Expand Down Expand Up @@ -152,7 +155,7 @@
</script>

{#if controls == "point"}
<PointControls editingExisting {cancel} />
<PointControls {finish} {cancel} />
{:else if controls == "route"}
<RouteControls extendRoute={false} {finish} {cancel} />
{:else if controls == "freehand-polygon"}
Expand Down
9 changes: 2 additions & 7 deletions src/lib/draw/Toolbox.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts" generics="F, S">
import { mode, pointTool, polygonTool, routeTool } from "./stores";
import { mode, polygonTool, 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";
Expand All @@ -20,7 +20,6 @@
import SnapPolygonMode from "./snap_polygon/SnapPolygonMode.svelte";
import StreetViewMode from "./StreetViewMode.svelte";
import { onDestroy } from "svelte";
import { PointTool } from "../draw/point/point_tool";
import { PolygonTool } from "maplibre-draw-polygon";
import RouteSnapperLoader from "./route/RouteSnapperLoader.svelte";
import ToolButton from "./ToolButton.svelte";
Expand All @@ -29,15 +28,11 @@
export let gjSchemes: Writable<Schemes<F, S>>;
export let routeSnapperUrl: string;
$: if ($map && !$pointTool) {
pointTool.set(new PointTool($map));
}
$: if ($map && !$polygonTool) {
polygonTool.set(new PolygonTool($map));
}
onDestroy(() => {
$pointTool?.tearDown();
$polygonTool?.tearDown();
$routeTool?.tearDown();
});
Expand All @@ -55,7 +50,7 @@ repeatedly load anything. Make sure this is only created once, then just hidden.
</div>

<div class="toolbar">
<ToolButton setMode={{ mode: "new-point" }} disabled={!$pointTool}>
<ToolButton setMode={{ mode: "new-point" }}>
<img src={pointIcon} alt="New point" />
New point
</ToolButton>
Expand Down
75 changes: 69 additions & 6 deletions src/lib/draw/point/PointControls.svelte
Original file line number Diff line number Diff line change
@@ -1,28 +1,91 @@
<script lang="ts">
import { HelpButton } from "$lib/common";
import { SecondaryButton } from "govuk-svelte";
import { DefaultButton, SecondaryButton } from "govuk-svelte";
import FixedButtonGroup from "../FixedButtonGroup.svelte";
import { pointPosition } from "$lib/draw/stores";
import { MapEvents, Marker } from "svelte-maplibre";
import type { MapMouseEvent } from "maplibre-gl";
import { map } from "$lib/config";
import { onMount, onDestroy } from "svelte";
export let editingExisting: boolean;
export let finish: () => void;
export let cancel: () => void;
onMount(() => {
if ($map) {
$map.doubleClickZoom.disable();
}
});
onDestroy(() => {
if ($map) {
$map.doubleClickZoom.enable();
}
});
function onClick(e: CustomEvent<MapMouseEvent>) {
if (!$pointPosition) {
$pointPosition = e.detail.lngLat.toArray();
}
}
function onDoubleClick() {
finish();
}
function keyDown(e: KeyboardEvent) {
if (e.key === "Escape") {
e.stopPropagation();
cancel();
}
}
</script>

<svelte:window on:keydown={keyDown} />

<MapEvents on:click={onClick} on:dblclick={onDoubleClick} />

{#if $pointPosition}
<Marker draggable bind:lngLat={$pointPosition}>
<span class="dot" />
</Marker>
{/if}

<div style="float: right">
<FixedButtonGroup>
<DefaultButton on:click={finish} style="margin-bottom: 0px">
Finish
</DefaultButton>
<SecondaryButton on:click={cancel} noBottomMargin>Cancel</SecondaryButton>

<HelpButton>
<ul>
{#if editingExisting}
<li>Click to move the point here</li>
{#if $pointPosition}
<li>Click and drag the point to move it</li>
{:else}
<li>Click to add a new point</li>
<li>Click the map to add a point</li>
{/if}
<li>
Press <b>Escape</b>
<b>Double click</b>
the map to finish or press
<b>Escape</b>
to cancel
</li>
</ul>
</HelpButton>
</FixedButtonGroup>
</div>

<style>
.dot {
width: 30px;
height: 30px;
border-radius: 50%;
display: inline-block;
background: red;
}
.dot:hover {
border: 1px solid black;
cursor: pointer;
}
</style>
47 changes: 31 additions & 16 deletions src/lib/draw/point/PointMode.svelte
Original file line number Diff line number Diff line change
@@ -1,36 +1,51 @@
<script lang="ts" generics="F, S">
import type { Feature, Point } from "geojson";
import type { Point } from "geojson";
import {
mode,
pointTool,
newFeatureId,
getArbitrarySchemeRef,
featureProps,
pointPosition,
setPrecision,
} from "$lib/draw/stores";
import { onDestroy, onMount } from "svelte";
import { onMount, onDestroy } from "svelte";
import PointControls from "./PointControls.svelte";
import { type Config } from "$lib/config";
import type { FeatureWithID } from "$lib/draw/types";
import type { Schemes } from "$lib/draw/types";
import { map, type Config } from "$lib/config";
import type { FeatureWithID, Schemes } from "$lib/draw/types";
import type { Writable } from "svelte/store";
export let cfg: Config<F, S>;
export let gjSchemes: Writable<Schemes<F, S>>;
onMount(() => {
$pointTool!.start();
$pointTool!.addEventListenerSuccess(onSuccess);
$pointTool!.addEventListenerFailure(onFailure);
if ($map) {
$map.getCanvas().style.cursor = "crosshair";
}
});
onDestroy(() => {
$pointTool!.stop();
$pointTool!.clearEventListeners();
$pointPosition = null;
if ($map) {
$map.getCanvas().style.cursor = "inherit";
}
});
function onSuccess(feature: Feature<Point>) {
feature.properties ||= {};
let f = feature as FeatureWithID<F, Point>;
f.properties = { ...f.properties, ...$featureProps };
$: if ($pointPosition && $map) {
$map.getCanvas().style.cursor = "inherit";
}
function onSuccess() {
if (!$pointPosition) {
return;
}
let f = {
type: "Feature",
geometry: {
type: "Point",
coordinates: setPrecision($pointPosition),
},
properties: { ...$featureProps },
} as FeatureWithID<F, Point>;
gjSchemes.update((gj) => {
f.id = newFeatureId(gj);
f.properties.scheme_reference = getArbitrarySchemeRef(gj);
Expand All @@ -47,4 +62,4 @@
}
</script>

<PointControls editingExisting={false} cancel={onFailure} />
<PointControls finish={onSuccess} cancel={onFailure} />
96 changes: 0 additions & 96 deletions src/lib/draw/point/point_tool.ts

This file was deleted.

4 changes: 2 additions & 2 deletions src/lib/draw/stores.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ import {
type UserSettings,
isStreetViewImagery,
} from "./types";
import { PointTool } from "./point/point_tool";
import { PolygonTool } from "maplibre-draw-polygon";
import { RouteTool } from "route-snapper-ts";
import type { Position } from "geojson";
import { v4 as uuidv4 } from "uuid";
import { randomSchemeColor } from "./colors";

export const pointTool: Writable<PointTool | null> = writable(null);
export const polygonTool: Writable<PolygonTool | null> = writable(null);
// A global singleton, with the route tool loaded for the current map. It's
// null before it's loaded.
export const routeTool: Writable<RouteTool | null> = writable(null);
// When the point tool is active, represents the marker position, once it's set.
export const pointPosition: Writable<[number, number] | null> = writable(null);

// scheme_references to hide
export const hideSchemes: Writable<Set<string>> = writable(new Set());
Expand Down

0 comments on commit d57a27c

Please sign in to comment.