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

Feature: Display of drawn feature #371

Merged
merged 18 commits into from
Nov 15, 2023
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
49 changes: 49 additions & 0 deletions elements/drawtools/drawtools.stories.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { html } from "lit";
import "../map/main";
import "./src/main";
import { getDefaultSelectedOption } from "./src/helpers";

export default {
title: "Elements/eox-drawtools",
Expand Down Expand Up @@ -39,3 +40,51 @@ export const MultiPolygon = {
multiple-features
></eox-drawtools>`,
};

/**
* By setting the `show-list` attribute or `showList` property to `true`,
* List of features will be visible
*/
export const MultiPolygonWithList = {
play: async ({ canvasElement }) => {
const EOxMap = canvasElement.querySelector("eox-map");

EOxMap.addSelect(
"draw",
getDefaultSelectedOption("draw-hover", "pointermove")
);
EOxMap.addSelect(
"draw",
getDefaultSelectedOption("draw-click", "click", true)
);
},
render: () => html`
<div style="display: flex">
<eox-map
id="list"
style="width: 500px; height: 300px;"
layers=${JSON.stringify([
{
type: "Vector",
id: "draw",
source: {
type: "Vector",
},
},
{
type: "Tile",
source: {
type: "OSM",
},
},
])}
></eox-map>
<eox-drawtools
for="eox-map#list"
layer="draw"
multiple-features
show-list
></eox-drawtools>
</div>
`,
};
216 changes: 216 additions & 0 deletions elements/drawtools/src/components/list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
import { LitElement, html, nothing } from "lit";
import { keyed } from "lit/directives/keyed.js";
import { styleEOX } from "../style.eox";
import { getDefaultSelectedOption } from "../helpers";

/**
* Display list of features
*
* @element eox-drawtools-list
*/
export class EOxDrawToolsList extends LitElement {
static properties = {
eoxMap: { attribute: false, state: true },
olMap: { attribute: false, state: true },
draw: { attribute: false, state: true },
layer: { type: String },
drawLayer: { attribute: false, state: true },
drawnFeatures: { attribute: false, state: true, type: Array },
modify: { attribute: false, state: true },
unstyled: { type: Boolean },
};

/**
* @type import("../../../map/src/select").EOxSelectInteraction
*/
hoverInteraction;

/**
* @type import("../../../map/src/select").EOxSelectInteraction
*/
clickInteraction;

/**
* @type string | number
*/
hoverId;

/**
* @type string | number
*/
clickId;

constructor() {
super();

/**
* @type import("../../../map/main").EOxMap
*/
this.eoxMap = null;

/**
* @type import("ol").Map
*/
this.olMap = null;

/**
* The current native OpenLayers `draw` interaction
* @type import("ol/interaction").Draw
*/

this.draw = null;

/**
* The layer id of the draw layer
* @default draw
*/
this.layer = "draw";

/**
* The current native OpenLayers draw `layer`
* @type import("ol/layer").Vector<import("ol/source").Vector>
*/

this.drawLayer = null;

/**
* The array of drawn native OpenLayers features. Normally includes only one feature, until multiple feature drawing is enabled.
* @type Array<import("ol").Feature>
*/
this.drawnFeatures = [];

/**
* The current native OpenLayers `modify` interaction
* @type import("ol/interaction").Modify
*/

this.modify = null;

/**
* Render the element without additional styles
*/
this.unstyled = false;
}

/**
* Delete individual feature @param {any} evt
*/
_handleDelete(evt) {
evt.stopPropagation();
const index = evt.target.getAttribute("index");
const feature = this.drawnFeatures[index];
this.drawLayer.getSource().removeFeature(feature);
this.drawnFeatures.splice(index, 1);
this.requestUpdate();
}

/**
* Select and Deselect feature from the list
*
* @param {import("ol").Feature} feature
*/
_handleFeatureSelectAndDeselect(feature) {
const selectedFeatureId = feature.get("id");

// Deselect selected feature
if (this.clickId === selectedFeatureId) {
const newExtent = this.drawLayer.getSource().getExtent();
this.olMap.getView().fit(newExtent, { duration: 750 });
this.clickInteraction.highlightById([]);
}
// Select the clicked feature
else {
this.clickInteraction.highlightById([selectedFeatureId]);
this.olMap
.getView()
.fit(feature.getGeometry().getExtent(), { duration: 750 });
}

this.requestUpdate();
}

firstUpdated() {
const isHoverInteractionExist =
this.eoxMap?.selectInteractions["draw-hover"];
const isClickInteractionExist =
this.eoxMap?.selectInteractions["draw-click"];

/*
* Check if interaction exist or not
* If not initialize a new interaction for hover and click
*/
if (!isHoverInteractionExist)
this.hoverInteraction = this.eoxMap.addSelect(
this.layer,
getDefaultSelectedOption("draw-hover", "pointermove")
);
if (!isClickInteractionExist)
this.clickInteraction = this.eoxMap.addSelect(
this.layer,
getDefaultSelectedOption("draw-click", "click", true)
);

// Event trigger when style change due to interaction
this.hoverInteraction.selectStyleLayer.on("change", () =>
this.requestUpdate()
);
this.clickInteraction.selectStyleLayer.on("change", () =>
this.requestUpdate()
);
}

render() {
this.hoverInteraction = this.eoxMap.selectInteractions["draw-hover"];
this.clickInteraction = this.eoxMap.selectInteractions["draw-click"];

this.hoverId = this.hoverInteraction?.selectedFids[0];
this.clickId = this.clickInteraction?.selectedFids[0];

return html`
<style>
${!this.unstyled && styleEOX}
</style>
<ul>
${this.drawnFeatures.map((feature, i) => {
const featureId = feature.get("id");
const selected =
this.hoverId === featureId || this.clickId === featureId;

return keyed(
i + 1,
html`
<li
class="${selected ? "selected" : nothing}"
@mouseover=${() => {
if (this.clickId === featureId) return;
this.hoverInteraction.highlightById([featureId]);
}}
@mouseout=${() => {
if (this.clickId === featureId) return;
this.hoverInteraction.highlightById([]);
}}
>
<div
class="list"
@click="${() =>
this._handleFeatureSelectAndDeselect(feature)}"
>
<span class="title">Feature #${i + 1}</span>
<button
index=${i}
class="icon small discard"
@click="${this._handleDelete}"
>
${this.unstyled ? "x" : nothing}
</button>
</div>
</li>
`
);
})}
</ul>
`;
}
}

customElements.define("eox-drawtools-list", EOxDrawToolsList);
28 changes: 28 additions & 0 deletions elements/drawtools/src/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Default selected option
* @param {string} id
* @param {"click" | "pointermove"} condition
* @param {Boolean} panIn
* @returns {import("../../map/src/select").SelectOptions}
*/
export const getDefaultSelectedOption = (id, condition, panIn = false) => {
return {
id,
condition,
panIn,
layer: {
type: "Vector",
properties: {
id: "selectLayer",
},
source: {
type: "Vector",
},
style: {
"fill-color": "rgba(51, 153, 204,0.5)",
"stroke-color": "#3399CC",
"stroke-width": 2.5,
},
},
};
};
21 changes: 21 additions & 0 deletions elements/drawtools/src/main.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { LitElement, html, nothing } from "lit";
import "./components/list";
import { style } from "./style";
import { styleEOX } from "./style.eox";

Expand All @@ -17,6 +18,7 @@
layer: { type: String },
modify: { attribute: false, state: true },
multipleFeatures: { attribute: "multiple-features", type: Boolean },
showList: { attribute: "show-list", type: Boolean },
unstyled: { type: Boolean },
};
}
Expand Down Expand Up @@ -83,6 +85,11 @@
*/
this.multipleFeatures = false;

/**
* Show list of features
*/
this.showList = false;

/**
* Render the element without additional styles
*/
Expand Down Expand Up @@ -158,7 +165,7 @@
const mapQuery = document.querySelector(this.for);

this.#eoxMap = /** @type {import("@eox/map/main").EOxMap} */ (mapQuery);
// @ts-ignore

Check warning on line 168 in elements/drawtools/src/main.js

View workflow job for this annotation

GitHub Actions / check-linting (ubuntu-latest, 18)

Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free
this.#olMap = /** @type {import("ol").Map} */ this.#eoxMap.map;

this.drawLayer =
Expand Down Expand Up @@ -198,6 +205,20 @@
discard
</button>
</div>
${this.showList && this.drawnFeatures?.length
? html`<eox-drawtools-list
.eoxMap=${this.#eoxMap}
.olMap=${this.#olMap}
.draw=${this.draw}
.layer=${this.layer}
.drawLayer=${this.drawLayer}
.drawnFeatures=${this.drawnFeatures}
.modify=${this.modify}
.unstyled=${this.unstyled}
@changed=${() => this.requestUpdate()}
>
</eox-drawtools-list>`
: nothing}
`;
}
}
Expand Down
Loading
Loading