Skip to content
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
84 changes: 71 additions & 13 deletions src/components/my-map/controls.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import Map from "ol/Map";
import { Control, ScaleLine } from "ol/control";
import { Control, ScaleLine, Zoom } from "ol/control";
import "ol/ol.css";
import "ol-ext/dist/ol-ext.css";
import northArrowIcon from "./icons/north-arrow-n.svg";
import trashCanIcon from "./icons/trash-can.svg";
import addIcon from "./icons/add.svg";
import removeIcon from "./icons/remove.svg";
import filterCenterFocusIcon from "./icons/filter-center-focus.svg";
import deleteIcon from "./icons/delete.svg";
import infoIcon from "./icons/info.svg";
import closeIcon from "./icons/close.svg";
import printIcon from "./icons/printer.svg";
import PrintDialog from "ol-ext/control/PrintDialog";
import { Options } from "ol-ext/control/PrintDialog";
Expand Down Expand Up @@ -33,30 +38,83 @@ export function northArrowControl() {
return new Control({ element: element });
}

export function resetControl(listener: any, icon: string) {
function makeIcon(src: string) {
const image = document.createElement("img");
image.src = src;
image.alt = "";
return image;
}

function makeButton(title: string, listener: () => void, icon?: string) {
const button = document.createElement("button");
button.title = "Reset map view";
button.type = "button";
button.title = title;

if (icon === "unicode") {
button.innerHTML = "↺";
} else {
const image = document.createElement("img");
image.className = "reset-icon";
image.src = trashCanIcon;
button.appendChild(image);
}
if (icon) button.appendChild(makeIcon(icon));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice to retire lingering unicode here 😅 💅


// this is an internal event listener, so doesn't need to be removed later
// ref https://lit.dev/docs/components/lifecycle/#disconnectedcallback
button.addEventListener("click", listener, false);

return button;
}

function makeButtonControl(className: string, button: HTMLButtonElement) {
const element = document.createElement("div");
element.className = "reset-control ol-unselectable ol-control";
element.className = `${className} ol-unselectable ol-control`;
element.appendChild(button);

return new Control({ element: element });
}

function makeResetButton(listener: () => void) {
const button = makeButton("Reset map view", listener, filterCenterFocusIcon);
button.classList.add("reset-button");
return button;
}

/**
* Icons for the default OL attribution control's expand & collapse button
*/
export function attributionLabels() {
return {
label: makeIcon(infoIcon),
collapseLabel: makeIcon(closeIcon),
};
}

/**
* Default OL zoom control with a 'reset view' button between zoom in & out
*/
export class ZoomWithResetControl extends Zoom {
constructor(listener: () => void, showReset = true) {
super({
zoomInLabel: makeIcon(addIcon),
zoomOutLabel: makeIcon(removeIcon),
});
if (showReset) {
this.element.insertBefore(
makeResetButton(listener),
this.element.lastElementChild,
);
}
}
}

/**
* Standalone 'reset view' control, used when the zoom control is disabled
*/
export function resetControl(listener: () => void) {
return makeButtonControl("reset-control", makeResetButton(listener));
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice and flexible, thanks for accounting for various "modes" here 🙌


export function deleteControl(listener: () => void) {
return makeButtonControl(
"delete-control",
makeButton("Delete all points", listener, deleteIcon),
);
}

PrintDialog.prototype.scales = {
// @ts-ignore
100: "1/100",
Expand Down
2 changes: 1 addition & 1 deletion src/components/my-map/icons/README.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Icons are sourced from [Font-GIS](https://viglino.github.io/font-gis/?fg=arrow-o) and [Carbon Design System](https://carbondesignsystem.com/guidelines/icons/library)
Icons are sourced from [Font-GIS](https://viglino.github.io/font-gis/?fg=arrow-o), [Carbon Design System](https://carbondesignsystem.com/guidelines/icons/library) and [Material Icons](https://mui.com/material-ui/material-icons/) (Apache 2.0)
4 changes: 4 additions & 0 deletions src/components/my-map/icons/add.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/components/my-map/icons/close.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/components/my-map/icons/delete.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/components/my-map/icons/filter-center-focus.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/components/my-map/icons/info.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/components/my-map/icons/remove.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 0 additions & 12 deletions src/components/my-map/icons/trash-can.svg

This file was deleted.

78 changes: 58 additions & 20 deletions src/components/my-map/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,13 @@ import { Vector as VectorSource, XYZ } from "ol/source";
import { Circle, Fill, Icon, Stroke, Style, Text } from "ol/style";
import View from "ol/View";
import {
attributionLabels,
deleteControl,
northArrowControl,
PrintControl,
resetControl,
scaleControl,
ZoomWithResetControl,
} from "./controls";
import {
configureDraw,
Expand Down Expand Up @@ -239,12 +242,21 @@ export class MyMap extends LitElement {
@property({ type: Boolean })
hideResetControl = false;

/**
* @deprecated - the reset control now always resets the view only, use `hideDeleteControl` to control deleting drawings
*/
@property({ type: Boolean })
resetViewOnly = false;

/**
* @deprecated - the reset control always uses the "filter center focus" icon now, and deleting drawings is handled by the separate delete control
*/

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We've been doing alpha pre-releases, so these deprecation notes are perfect 👍

@property({ type: String })
resetControlImage: ResetControlImageEnum = "unicode";

@property({ type: Boolean })
hideDeleteControl = false;

@property({ type: Boolean })
staticMode = false;

Expand Down Expand Up @@ -377,8 +389,9 @@ export class MyMap extends LitElement {
attribution: true,
attributionOptions: {
collapsed: this.collapseAttributions,
...attributionLabels(),
},
zoom: !this.staticMode,
zoom: false, // added below with a custom reset button
rotate: false, // alternatively uses custom prop `showNorthArrow`
}),
interactions: defaultInteractions({
Expand All @@ -404,31 +417,52 @@ export class MyMap extends LitElement {
const draw = configureDraw(this.drawType, this.drawPointer, this.drawColor);
const modify = configureModify(this.drawPointer, this.drawColor);

// Add a custom 'reset' control to the map
// Ensure the draw interaction is active, keeping snap as the last interaction added
const enableDraw = () => {
if (map.getInteractions().getArray().includes(draw)) return;
map.addInteraction(draw);
map.removeInteraction(snap);
map.addInteraction(snap);
};

// Track whether a shape is part-way through being drawn
let isSketching = false;
draw.on("drawstart", () => (isSketching = true));
draw.on(["drawend", "drawabort"], () => (isSketching = false));

const handleDelete = () => {
if (isSketching) draw.abortDrawing();
if (drawingSource.getFeatures().length === 0) return;

drawingSource.clear();
this.dispatch("geojsonChange", {});
enableDraw();
};

// Reset the view port of the map based on available data or center/zoom by default
const handleReset = () => {
// Reset the view port of the map based on available data or center/zoom by default
if (this.showFeaturesAtPoint) {
fitToData(map, outlineSource, this.featureBuffer);
} else if (geojsonSource.getFeatures().length > 0) {
fitToData(map, geojsonSource, this.geojsonBuffer);
} else if (this.resetViewOnly && drawingSource.getFeatures().length > 0) {
} else if (this.drawMode && drawingSource.getFeatures().length > 0) {
fitToData(map, drawingSource, this.drawGeojsonDataBuffer);
} else {
map.getView().setCenter(centerCoordinate);
map.getView().setZoom(this.zoom);
}

// If in drawMode, also clear features from the drawingSource by default
if (this.drawMode && !this.resetViewOnly) {
drawingSource.clear();
this.dispatch("geojsonChange", {});
map.addInteraction(draw);
map.addInteraction(snap);
}
};

if (!this.hideResetControl) {
map.addControl(resetControl(handleReset, this.resetControlImage));
if (!this.staticMode) {
map.addControl(
new ZoomWithResetControl(handleReset, !this.hideResetControl),
);
} else if (!this.hideResetControl) {
map.addControl(resetControl(handleReset));
}

if (this.drawMode && !this.hideDeleteControl) {
map.addControl(deleteControl(handleDelete));
}

// add custom scale line and north arrow controls to the map
Expand Down Expand Up @@ -905,9 +939,11 @@ export class MyMap extends LitElement {
<div
id="${this.id}"
class="map"
role="${this.staticMode && !this.collapseAttributions
? "presentation"
: "application"}"
role="${
this.staticMode && !this.collapseAttributions
? "presentation"
: "application"
}"
tabindex="${this.staticMode && !this.collapseAttributions ? -1 : 0}"
data-testid="${this.dataTestId}"
/>`
Expand All @@ -918,9 +954,11 @@ export class MyMap extends LitElement {
<div
id="${this.id}"
class="map"
role="${this.staticMode && !this.collapseAttributions
? "presentation"
: "application"}"
role="${
this.staticMode && !this.collapseAttributions
? "presentation"
: "application"
}"
tabindex="${this.staticMode && !this.collapseAttributions ? -1 : 0}"
data-testid="${this.dataTestId}"
/>`;
Expand Down
14 changes: 12 additions & 2 deletions src/components/my-map/my-map.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,24 @@ const meta: Meta = {
},
},
hideResetControl: {
description: "Hides the reset/re-centre control button.",
description: "Hides the reset view button (between zoom in & out).",
control: "boolean",
table: {
category: "Basic",
type: { summary: "Boolean" },
defaultValue: { summary: "false" },
},
},
hideDeleteControl: {
description:
"Hides the delete all drawings button (bottom left, drawMode only).",
control: "boolean",
table: {
category: "Drawing",
type: { summary: "Boolean" },
defaultValue: { summary: "false" },
},
},
showScale: {
description: "Shows a scale bar on the map.",
control: "boolean",
Expand Down Expand Up @@ -436,7 +446,7 @@ export const DrawMode: Story = {

/**
* Load a polygon onto the drawing canvas with the ability to continue
* modifying it. Click 'reset' to erase and draw fresh.
* modifying it. Click 'delete' to erase and draw fresh.
*/
export const DrawModeWithInitialShape: Story = {
name: "Drawing: load initial shape",
Expand Down
Loading
Loading