Skip to content

Commit

Permalink
feat: add visual feedback if a layer is outside its min/max zoom
Browse files Browse the repository at this point in the history
* added zoom layer state in stories

* added state to layer

* added state check to helper

* clean code a bit

* arg fix in stories

* added opacity also to look it like hidden

* prettier

* re render fix

* CHANGELOG prettier fix

* update example for zoom layer state

* added  to layer control

* set default zoom level to 1

* test added to zoom state layer

* fix comment typo

* fix title typo

* fix id for docs

implements #248
  • Loading branch information
srijitcoder authored Nov 14, 2023
1 parent cf06736 commit 09f5288
Show file tree
Hide file tree
Showing 8 changed files with 275 additions and 7 deletions.
55 changes: 55 additions & 0 deletions elements/layercontrol/layercontrol.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,61 @@ export const Tabs = {
`,
};

/**
* Zoom layer state based on `minZoom` and `maxZoom`.
* The color change state only visible when `showZoomLayerState` is set inside layer properties.
*/
export const ZoomLayerState = {
args: {
showZoomLayerState: true,
},
render: (args) => html`
<div style="display: flex">
<eox-layercontrol
.showZoomLayerState=${args.showZoomLayerState}
for="eox-map#zoomstate"
></eox-layercontrol>
<eox-map
id="zoomstate"
style="width: 600px; height: 300px; margin-left: 7px;"
zoom="1"
layers=${JSON.stringify([
{
type: "Vector",
properties: {
title: "Regions",
id: "regions",
},
source: {
type: "Vector",
url: "https://openlayers.org/data/vector/ecoregions.json",
format: "GeoJSON",
attributions: "Regions: @ openlayers.org",
},
minZoom: 2,
},
{
type: "Tile",
properties: {
id: "WIND",
title: "WIND",
},
source: {
type: "TileWMS",
url: "https://services.sentinel-hub.com/ogc/wms/0635c213-17a1-48ee-aef7-9d1731695a54",
params: {
LAYERS: "AWS_VIS_WIND_V_10M",
},
},
maxZoom: 9,
},
])}
>
</eox-map>
</div>
`,
};

/**
* Unstyled version of the Element
*/
Expand Down
80 changes: 79 additions & 1 deletion elements/layercontrol/src/components/layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { when } from "lit/directives/when.js";
import { live } from "lit/directives/live.js";
import "./layerTools";
import { checkbox } from "../../../../utils/styles/checkbox";
import {
isLayerVisibleBasedOnZoomState,
isZoomLayerStateRequired,
} from "../helpers";

/**
* A single layer display
Expand All @@ -12,12 +16,19 @@ import { checkbox } from "../../../../utils/styles/checkbox";
export class EOxLayerControlLayer extends LitElement {
static properties = {
layer: { attribute: false },
map: { attribute: false, state: true },
titleProperty: { attribute: "title-property", type: String },
showZoomLayerState: { attribute: false, type: Boolean },
tools: { attribute: false },
unstyled: { type: Boolean },
noShadow: { type: Boolean },
};

/**
* @type Boolean
*/
currLayerVisibilityBasedOnZoom = true;

constructor() {
super();

Expand All @@ -28,11 +39,23 @@ export class EOxLayerControlLayer extends LitElement {
*/
this.layer = null;

/**
* The native OL map
* @type {import("ol").Map}
* @see {@link https://openlayers.org/en/latest/apidoc/module-ol_Map-Map.html}
*/
this.map = null;

/**
* The layer title property
*/
this.titleProperty = "title";

/**
* Show layer state based on zoom level or not
*/
this.showZoomLayerState = false;

/**
* @type Array<string>
*/
Expand All @@ -48,6 +71,52 @@ export class EOxLayerControlLayer extends LitElement {
this.noShadow = true;
}

/**
* Check and update layer visibility based on zoom level
* and only update DOM if visibility gets updated
*/
updateLayerZoomVisibility() {
const newLayerVisibilityBasedOnZoom = isLayerVisibleBasedOnZoomState(
this.layer,
this.map,
this.showZoomLayerState
);

let visibilityChanged = false;

// Checking if visibility changed or not and updating `currLayerVisibilityBasedOnZoom`
if (!newLayerVisibilityBasedOnZoom && this.currLayerVisibilityBasedOnZoom)
(this.currLayerVisibilityBasedOnZoom = false), (visibilityChanged = true);
else if (
newLayerVisibilityBasedOnZoom &&
!this.currLayerVisibilityBasedOnZoom
)
(this.currLayerVisibilityBasedOnZoom = true), (visibilityChanged = true);

// if visibilityChanged trigger UI update
if (visibilityChanged) {
this.requestUpdate();
this.dispatchEvent(
new CustomEvent("change:resolution", { bubbles: true })
);
}
}

/**
* Check and update layer zoom visibility at beginning
* and register "change:resolution" ones at the beginning if `showZoomLayerState` is present
*/
firstUpdated() {
if (isZoomLayerStateRequired(this.layer, this.showZoomLayerState)) {
this.updateLayerZoomVisibility();

// Initialize change:resolution event ones
this.map
.getView()
.on("change:resolution", () => this.updateLayerZoomVisibility());
}
}

createRenderRoot() {
return this.noShadow ? this : super.createRenderRoot();
}
Expand All @@ -61,7 +130,12 @@ export class EOxLayerControlLayer extends LitElement {
${when(
this.layer,
() => html`
<div class="layer ${this.layer.getVisible() ? "visible" : ""}">
<div
class="layer ${this.layer.getVisible() ? "visible" : ""}
${!this.currLayerVisibilityBasedOnZoom
? "zoom-state-invisible"
: ""}"
>
<label
class="${this.layer.get("layerControlDisable") ? "disabled" : ""}"
>
Expand Down Expand Up @@ -126,6 +200,10 @@ export class EOxLayerControlLayer extends LitElement {
eox-layercontrol-layer {
width: 100%;
}
.layer.zoom-state-invisible {
background: #d2e2ee;
opacity: 0.3;
}
.layer {
width: 100%;
align-items: center;
Expand Down
9 changes: 9 additions & 0 deletions elements/layercontrol/src/components/layerGroup.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export class EOxLayerControlLayerGroup extends LitElement {
idProperty: { attribute: "id-property" },
map: { attribute: false, state: true },
titleProperty: { attribute: "title-property", type: String },
showZoomLayerState: { attribute: false, type: Boolean },
tools: { attribute: false },
unstyled: { type: Boolean },
noShadow: { type: Boolean },
Expand Down Expand Up @@ -46,6 +47,11 @@ export class EOxLayerControlLayerGroup extends LitElement {
*/
this.titleProperty = "title";

/**
* Show layer state based on zoom level or not
*/
this.showZoomLayerState = false;

/**
* @type Array<string>
*/
Expand Down Expand Up @@ -80,7 +86,9 @@ export class EOxLayerControlLayerGroup extends LitElement {
<eox-layercontrol-layer
.noShadow=${true}
.layer=${this.group}
.map=${this.map}
.titleProperty=${this.titleProperty}
.showZoomLayerState=${this.showZoomLayerState}
.tools=${this.tools}
.unstyled=${this.unstyled}
@changed=${() => this.requestUpdate()}
Expand All @@ -92,6 +100,7 @@ export class EOxLayerControlLayerGroup extends LitElement {
.layers=${this.group.getLayers()}
.map=${this.map}
.titleProperty=${this.titleProperty}
.showZoomLayerState=${this.showZoomLayerState}
.tools=${this.tools}
.unstyled=${this.unstyled}
@changed=${() => this.requestUpdate()}
Expand Down
9 changes: 9 additions & 0 deletions elements/layercontrol/src/components/layerList.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export class EOxLayerControlLayerList extends LitElement {
layers: { attribute: false },
map: { attribute: false, state: true },
titleProperty: { attribute: "title-property", type: String },
showZoomLayerState: { attribute: false, type: Boolean },
tools: { attribute: false },
unstyled: { type: Boolean },
noShadow: { type: Boolean },
Expand Down Expand Up @@ -53,6 +54,11 @@ export class EOxLayerControlLayerList extends LitElement {
*/
this.titleProperty = "title";

/**
* Show layer state based on zoom level or not
*/
this.showZoomLayerState = false;

/**
* Render the element without additional styles
*/
Expand Down Expand Up @@ -114,6 +120,7 @@ export class EOxLayerControlLayerList extends LitElement {
.idProperty=${this.idProperty}
.map=${this.map}
.titleProperty=${this.titleProperty}
.showZoomLayerState=${this.showZoomLayerState}
.tools=${this.tools}
.unstyled=${this.unstyled}
@changed=${() => this.requestUpdate()}
Expand All @@ -124,7 +131,9 @@ export class EOxLayerControlLayerList extends LitElement {
<eox-layercontrol-layer
.noShadow=${true}
.layer=${layer}
.map=${this.map}
.titleProperty=${this.titleProperty}
.showZoomLayerState=${this.showZoomLayerState}
.tools=${this.tools}
.unstyled=${this.unstyled}
@changed=${() => {
Expand Down
39 changes: 39 additions & 0 deletions elements/layercontrol/src/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,42 @@ export const getLayerType = (layer, map) => {
? "vector"
: "raster";
};

/**
* Returns whether zoom layer state be enabled or not for a particular layer
* @param {import("ol/layer").Layer | import("ol/layer").Group} layer
* @param {Boolean} showZoomLayerState
* @returns {Boolean} state
*/
export const isZoomLayerStateRequired = (layer, showZoomLayerState) => {
const minZoom = layer.get("minZoom");
const maxZoom = layer.get("maxZoom");

if (showZoomLayerState && (minZoom !== -Infinity || maxZoom !== Infinity))
return true;
else return false;
};

/**
* Returns layer visibility state based on minZoom and maxZoon
* with respective of current zoom level
* @param {import("ol/layer").Layer | import("ol/layer").Group} layer
* @param {import("ol").Map} map
* @param {Boolean} showZoomLayerState
* @returns {Boolean} state
*/
export const isLayerVisibleBasedOnZoomState = (
layer,
map,
showZoomLayerState
) => {
if (!layer || !map) return false;

if (!isZoomLayerStateRequired(layer, showZoomLayerState)) return true;

const minZoom = layer.get("minZoom");
const maxZoom = layer.get("maxZoom");
const zoom = map.getView().getZoom();

return zoom > minZoom && zoom < maxZoom ? true : false;
};
7 changes: 7 additions & 0 deletions elements/layercontrol/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export class EOxLayerControl extends LitElement {
idProperty: { attribute: "id-property" },
map: { attribute: false, state: true },
titleProperty: { attribute: "title-property", type: String },
showZoomLayerState: { attribute: false, type: Boolean },
tools: { attribute: false },
unstyled: { type: Boolean },
styleOverride: { type: String },
Expand Down Expand Up @@ -69,6 +70,11 @@ export class EOxLayerControl extends LitElement {
*/
this.titleProperty = "title";

/**
* Show layer state based on zoom level or not
*/
this.showZoomLayerState = false;

/**
* Layer tools
*/
Expand Down Expand Up @@ -112,6 +118,7 @@ export class EOxLayerControl extends LitElement {
.layers=${this.map.getLayers()}
.map=${this.map}
.titleProperty=${this.titleProperty}
.showZoomLayerState=${this.showZoomLayerState}
.tools=${this.tools}
.unstyled=${this.unstyled}
@changed=${
Expand Down
22 changes: 20 additions & 2 deletions elements/layercontrol/test/_mockMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class MockLayer {
});
}
get(prop) {
return this.properties[prop];
return this.properties[prop] || this[prop];
}
getLayers() {
return this.layers || new MockCollection([]);
Expand All @@ -32,6 +32,8 @@ class MockLayer {
id: "foo",
title: "layer",
};
minZoom = -Infinity;
maxZoom = Infinity;
set(prop, value) {
this.properties[prop] = value;
}
Expand Down Expand Up @@ -71,7 +73,9 @@ export class MockMap extends HTMLElement {
super();
}
layers;
events = {};
events = {
"change:resolution": [],
};
map = {
getInteractions: () => ({
getArray: () => [{}],
Expand All @@ -83,6 +87,20 @@ export class MockMap extends HTMLElement {
return new MockCollection([]);
}
},
getEvents: () => this.events,
getView: () => ({
getZoom: () => this.zoom,
on: (type, listener) => {
this.events = {
...this.events,
[type]: [...this.events[type], listener],
};
},
}),
};
zoom = 1;
setZoom = (z) => {
this.zoom = z;
};
setLayers = (layers) => {
this.layers = new MockCollection(layers);
Expand Down
Loading

0 comments on commit 09f5288

Please sign in to comment.