From ddce5a4b564b197b16bf87150b38e76c28d55d70 Mon Sep 17 00:00:00 2001 From: mo-martinwilson Date: Fri, 25 Oct 2024 16:52:13 +0100 Subject: [PATCH 1/7] add file structure --- src/index.js | 2 ++ src/l-tile-layer-wms.js | 16 ++++++++++++++++ src/l-tile-layer-wms.test.js | 1 + 3 files changed, 19 insertions(+) create mode 100644 src/l-tile-layer-wms.js create mode 100644 src/l-tile-layer-wms.test.js diff --git a/src/index.js b/src/index.js index dc34e85..825d3cc 100644 --- a/src/index.js +++ b/src/index.js @@ -7,6 +7,7 @@ import LMarker from "./l-marker.js"; import LOverlayLayers from "./l-overlay-layers.js"; import LPopup from "./l-popup.js"; import LTileLayer from "./l-tile-layer.js"; +import LTileLayerWMS from "./l-tile-layer-wms.js"; import LLatLngBounds from "./l-lat-lng-bounds.js"; import LImageOverlay from "./l-image-overlay.js"; import LVideoOverlay from "./l-video-overlay.js"; @@ -28,6 +29,7 @@ const init = (() => { customElements.define("l-overlay-layers", LOverlayLayers); customElements.define("l-layer-group", LLayerGroup); customElements.define("l-tile-layer", LTileLayer); + customElements.define("l-tile-layer-wms", LTileLayerWMS); customElements.define("l-marker-cluster-group", LMarkerClusterGroup); customElements.define("l-marker", LMarker); customElements.define("l-popup", LPopup); diff --git a/src/l-tile-layer-wms.js b/src/l-tile-layer-wms.js new file mode 100644 index 0000000..c260159 --- /dev/null +++ b/src/l-tile-layer-wms.js @@ -0,0 +1,16 @@ +// @ts-check +import LLayer from "./l-layer.js"; + +class LTileLayerWMS extends LLayer { + constructor() { + super(); + this.layer = null; + } + + connectedCallback() { + // do stuff + } + +} + +export default LTileLayerWMS; diff --git a/src/l-tile-layer-wms.test.js b/src/l-tile-layer-wms.test.js new file mode 100644 index 0000000..ca7c489 --- /dev/null +++ b/src/l-tile-layer-wms.test.js @@ -0,0 +1 @@ +// Write tests here (first, preferably) From 859d8e11c72b3ca1b058ef89725549a5e99f5e4a Mon Sep 17 00:00:00 2001 From: mo-martinwilson Date: Fri, 25 Oct 2024 16:57:26 +0100 Subject: [PATCH 2/7] update LTileLayerWMS to extend TileLayer --- src/l-tile-layer-wms.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/l-tile-layer-wms.js b/src/l-tile-layer-wms.js index c260159..fbd18aa 100644 --- a/src/l-tile-layer-wms.js +++ b/src/l-tile-layer-wms.js @@ -1,16 +1,15 @@ // @ts-check -import LLayer from "./l-layer.js"; +import LTileLayer from "./l-tile-layer.js"; -class LTileLayerWMS extends LLayer { +class LTileLayerWMS extends LTileLayer { constructor() { super(); this.layer = null; } connectedCallback() { - // do stuff + // do stuff } - } export default LTileLayerWMS; From 0cafb2578c1285c645e3825e108aa3950a06535b Mon Sep 17 00:00:00 2001 From: mo-martinwilson Date: Wed, 30 Oct 2024 11:08:57 +0000 Subject: [PATCH 3/7] populate l-tile-layer-wms.js --- src/l-tile-layer-wms.js | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/src/l-tile-layer-wms.js b/src/l-tile-layer-wms.js index fbd18aa..41b4a49 100644 --- a/src/l-tile-layer-wms.js +++ b/src/l-tile-layer-wms.js @@ -1,15 +1,44 @@ // @ts-check -import LTileLayer from "./l-tile-layer.js"; +import { tileLayer } from "leaflet"; +import LLayer from "./l-layer.js"; +import { layerConnected } from "./events.js"; +import { htmlAttribute, optional, parse, partial } from "./parse.js"; -class LTileLayerWMS extends LTileLayer { +class LTileLayerWMS extends LLayer { constructor() { super(); this.layer = null; } connectedCallback() { - // do stuff + const urlTemplate = parse(htmlAttribute("url-template"), this) + + const name = this.getAttribute("name"); + const schema = partial({ + // Leaflet.tileLayer default options: https://leafletjs.com/reference.html#tilelayer-wms-layers + layers: htmlAttribute("layers"), + styles: optional(htmlAttribute("styles")), + format: optional(htmlAttribute("format")), + transparent: optional(htmlAttribute("transparent")), + version: optional(htmlAttribute("version")), + crs: optional(htmlAttribute("crs")), + uppercase: optional(htmlAttribute("uppercase")), + + // Inherited option from Layer: https://leafletjs.com/reference.html#tilelayer-wms-attribution + attribution: optional(htmlAttribute("attribution")), + + // Optional options + options: optional(htmlAttribute("options")), + }); + + const wmsOptions = parse(schema, this); + this.layer = tileLayer.wms(urlTemplate, { ...wmsOptions }); + const event = new CustomEvent(layerConnected, { + detail: { name, layer: this.layer }, + bubbles: true, + }); + this.dispatchEvent(event); } + } - export default LTileLayerWMS; From af76c7ef7eefb60d843416129c509864dc879082 Mon Sep 17 00:00:00 2001 From: mo-martinwilson Date: Wed, 30 Oct 2024 11:09:11 +0000 Subject: [PATCH 4/7] populate l-tile-layer-wms.test.js --- src/l-tile-layer-wms.test.js | 59 +++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/src/l-tile-layer-wms.test.js b/src/l-tile-layer-wms.test.js index ca7c489..48f6b3f 100644 --- a/src/l-tile-layer-wms.test.js +++ b/src/l-tile-layer-wms.test.js @@ -1 +1,58 @@ -// Write tests here (first, preferably) +// @vitest-environment happy-dom +import { tileLayer } from "leaflet"; +import { it, expect } from "vitest"; +import { layerConnected } from "./events"; +import "./index"; + +it("should create an l-tile-layer-wms with the correct options", async () => { + const urlTemplate = "http://ows.mundialis.de/services/service?"; + const el = document.createElement("l-tile-layer-wms"); + el.setAttribute("url-template", urlTemplate); + el.setAttribute("layers", "example-wms-layer"); + + let promise = new Promise((resolve) => { + el.addEventListener(layerConnected, (ev) => { + resolve(ev.detail); + }); + }); + document.body.appendChild(el); + + const actual = await promise; + const expected = { + name: null, + layer: tileLayer.wms(urlTemplate, { layers: "example-wms-layer" }), + }; + expect(actual).toEqual(expected); +}); + +it.each([ + ["http://example.com/wms", "styles", "default"], + ["http://example.com/wms", "format", "image/png"], + ["http://example.com/wms", "transparent", "true"] +])("should handle WMS options %s %s", (urlTemplate, key, value) => { + const el = document.createElement("l-tile-layer-wms"); + el.setAttribute("url-template", urlTemplate); + el.setAttribute("layers", "defaultLayer"); + el.setAttribute(key, value); + document.body.appendChild(el); + + const actual = el.layer; + const expected = tileLayer.wms(urlTemplate, { layers: "defaultLayer", [key]: value }); + expect(actual).toEqual(expected); +}); + + +it("should support attribution", () => { + const urlTemplate = "http://example.com/wms"; + const attribution = "© OpenStreetMap contributors"; + const layers="example-wms-layer" + const el = document.createElement("l-tile-layer-wms"); + el.setAttribute("url-template", urlTemplate); + el.setAttribute("attribution", attribution); + el.setAttribute("layers", layers); + document.body.appendChild(el); + + const actual = el.layer; + const expected = tileLayer.wms(urlTemplate, { attribution, layers }); + expect(actual).toEqual(expected); +}); From 57560532efa209c6ca43690e36d2989d4a3dddf1 Mon Sep 17 00:00:00 2001 From: mo-martinwilson Date: Wed, 30 Oct 2024 12:24:17 +0000 Subject: [PATCH 5/7] update l-tile-layer-wms.js to accept nonStandardOptions as JSON string, and parse accordingly. Also written tests and formatted. --- src/l-tile-layer-wms.js | 28 +++++++++++++++------- src/l-tile-layer-wms.test.js | 45 ++++++++++++++++++++++++++++++++---- 2 files changed, 60 insertions(+), 13 deletions(-) diff --git a/src/l-tile-layer-wms.js b/src/l-tile-layer-wms.js index 41b4a49..6569e83 100644 --- a/src/l-tile-layer-wms.js +++ b/src/l-tile-layer-wms.js @@ -11,8 +11,8 @@ class LTileLayerWMS extends LLayer { } connectedCallback() { - const urlTemplate = parse(htmlAttribute("url-template"), this) - + const urlTemplate = parse(htmlAttribute("url-template"), this); + const name = this.getAttribute("name"); const schema = partial({ // Leaflet.tileLayer default options: https://leafletjs.com/reference.html#tilelayer-wms-layers @@ -26,19 +26,31 @@ class LTileLayerWMS extends LLayer { // Inherited option from Layer: https://leafletjs.com/reference.html#tilelayer-wms-attribution attribution: optional(htmlAttribute("attribution")), - - // Optional options - options: optional(htmlAttribute("options")), }); - const wmsOptions = parse(schema, this); - this.layer = tileLayer.wms(urlTemplate, { ...wmsOptions }); + const standardOptions = parse(schema, this); + const nonStandardOptionsElement = this.getAttribute("options"); + const nonStandardOptions = () => { + try { + return JSON.parse(nonStandardOptionsElement); + } catch (e) { + console.error( + "Error whilst parsing JSON for options attribute in l-tile-layer-wms", + e, + ); + return null; + } + }; + + this.layer = tileLayer.wms(urlTemplate, { + ...standardOptions, + ...nonStandardOptions(), + }); const event = new CustomEvent(layerConnected, { detail: { name, layer: this.layer }, bubbles: true, }); this.dispatchEvent(event); } - } export default LTileLayerWMS; diff --git a/src/l-tile-layer-wms.test.js b/src/l-tile-layer-wms.test.js index 48f6b3f..001946b 100644 --- a/src/l-tile-layer-wms.test.js +++ b/src/l-tile-layer-wms.test.js @@ -28,24 +28,26 @@ it("should create an l-tile-layer-wms with the correct options", async () => { it.each([ ["http://example.com/wms", "styles", "default"], ["http://example.com/wms", "format", "image/png"], - ["http://example.com/wms", "transparent", "true"] + ["http://example.com/wms", "transparent", "true"], ])("should handle WMS options %s %s", (urlTemplate, key, value) => { const el = document.createElement("l-tile-layer-wms"); el.setAttribute("url-template", urlTemplate); - el.setAttribute("layers", "defaultLayer"); + el.setAttribute("layers", "example layer ere"); el.setAttribute(key, value); document.body.appendChild(el); const actual = el.layer; - const expected = tileLayer.wms(urlTemplate, { layers: "defaultLayer", [key]: value }); + const expected = tileLayer.wms(urlTemplate, { + layers: "example layer ere", + [key]: value, + }); expect(actual).toEqual(expected); }); - it("should support attribution", () => { const urlTemplate = "http://example.com/wms"; const attribution = "© OpenStreetMap contributors"; - const layers="example-wms-layer" + const layers = "example-wms-layer"; const el = document.createElement("l-tile-layer-wms"); el.setAttribute("url-template", urlTemplate); el.setAttribute("attribution", attribution); @@ -56,3 +58,36 @@ it("should support attribution", () => { const expected = tileLayer.wms(urlTemplate, { attribution, layers }); expect(actual).toEqual(expected); }); + +it("should parse valid JSON in the options attribute", () => { + const urlTemplate = "http://example.com/wms"; + const options = '{"height": 101, "bbox": "coords ere"}'; + const el = document.createElement("l-tile-layer-wms"); + el.setAttribute("url-template", urlTemplate); + el.setAttribute("layers", "example layer ere"); + el.setAttribute("options", options); + document.body.appendChild(el); + + const actual = el.layer; + const expected = tileLayer.wms(urlTemplate, { + layers: "example layer ere", + height: 101, + bbox: "coords ere", + }); + expect(actual).toEqual(expected); +}); + +it("should handle invalid JSON in the options attribute gracefully", () => { + const urlTemplate = "http://example.com/wms"; + const invalidJson = '{"height": 10, "bbox": "coords ere"'; // <- missing closing brace + const el = document.createElement("l-tile-layer-wms"); + el.setAttribute("url-template", urlTemplate); + el.setAttribute("layers", "example layer ere"); + el.setAttribute("options", invalidJson); + document.body.appendChild(el); + + // Expect layer creation to succeed but without additional options + const actual = el.layer; + const expected = tileLayer.wms(urlTemplate, { layers: "example layer ere" }); + expect(actual).toEqual(expected); +}); From 208d0454ed5594597b36048f555cf5321abf65ed Mon Sep 17 00:00:00 2001 From: mo-martinwilson Date: Wed, 30 Oct 2024 12:58:05 +0000 Subject: [PATCH 6/7] add quick if statement to handle when options is not a defined element --- src/l-tile-layer-wms.js | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/l-tile-layer-wms.js b/src/l-tile-layer-wms.js index 6569e83..1798bb2 100644 --- a/src/l-tile-layer-wms.js +++ b/src/l-tile-layer-wms.js @@ -31,14 +31,18 @@ class LTileLayerWMS extends LLayer { const standardOptions = parse(schema, this); const nonStandardOptionsElement = this.getAttribute("options"); const nonStandardOptions = () => { - try { - return JSON.parse(nonStandardOptionsElement); - } catch (e) { - console.error( - "Error whilst parsing JSON for options attribute in l-tile-layer-wms", - e, - ); - return null; + if (nonStandardOptionsElement) { + try { + return JSON.parse(nonStandardOptionsElement); + } catch (e) { + console.error( + "Error whilst parsing JSON for options attribute in l-tile-layer-wms", + e, + ); + return ""; + } + } else { + return ""; } }; From 92118606bb0ddbb894eb3cf4557db98b88a3216d Mon Sep 17 00:00:00 2001 From: mo-martinwilson Date: Wed, 30 Oct 2024 12:59:01 +0000 Subject: [PATCH 7/7] rp empty string with empty obj --- src/l-tile-layer-wms.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/l-tile-layer-wms.js b/src/l-tile-layer-wms.js index 1798bb2..8977821 100644 --- a/src/l-tile-layer-wms.js +++ b/src/l-tile-layer-wms.js @@ -39,10 +39,10 @@ class LTileLayerWMS extends LLayer { "Error whilst parsing JSON for options attribute in l-tile-layer-wms", e, ); - return ""; + return {}; } } else { - return ""; + return {}; } };