From 5a539831bc29f7186bb785eee519570be68edf4e Mon Sep 17 00:00:00 2001 From: andrewgryan Date: Thu, 2 Jan 2025 15:24:15 +0000 Subject: [PATCH 1/4] create a helper module for grid layer settings common between TileLayer and WMS --- src/grid-layer.js | 23 +++++++++++++++++++++++ src/l-tile-layer-wms.js | 5 +++++ src/l-tile-layer-wms.test.js | 19 ++++++++++++++++++- src/l-tile-layer.js | 10 ++++++++-- src/l-tile-layer.test.js | 25 +++++++++++++++++++++++++ 5 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 src/grid-layer.js diff --git a/src/grid-layer.js b/src/grid-layer.js new file mode 100644 index 0000000..47aea46 --- /dev/null +++ b/src/grid-layer.js @@ -0,0 +1,23 @@ +// Helpers to support GridLayer inherited Leaflet functions +import { point } from "leaflet"; + + +/** + * @param {Element} el + * @returns {import("leaflet").GridLayerOptions} + */ +export const gridLayerOptions = (el) => { + const options = {} + if (el.hasAttribute("tile-size")) { + let tileSize = null; + const text = el.getAttribute("tile-size"); + const number = parseInt(text); + if (isNaN(number)) { + tileSize = point(JSON.parse(text)); + } else { + tileSize = number; + } + options["tileSize"] = tileSize; + } + return options; +} diff --git a/src/l-tile-layer-wms.js b/src/l-tile-layer-wms.js index 5cbd327..ebc7e6e 100644 --- a/src/l-tile-layer-wms.js +++ b/src/l-tile-layer-wms.js @@ -3,6 +3,7 @@ import { tileLayer } from "leaflet"; import LLayer from "./l-layer.js"; import { layerConnected } from "./events.js"; import { htmlAttribute, optional, parse, partial } from "./parse.js"; +import { gridLayerOptions } from "./grid-layer.js"; class LTileLayerWMS extends LLayer { static get observedAttributes() { @@ -69,10 +70,14 @@ class LTileLayerWMS extends LLayer { paneOptions["pane"] = this.parentElement.getAttribute("name"); } + // GridLayer options + const gridOptions = gridLayerOptions(this); + this.layer = tileLayer.wms(urlTemplate, { ...standardOptions, ...nonStandardOptions(), ...paneOptions, + ...gridOptions, }); const event = new CustomEvent(layerConnected, { detail: { name, layer: this.layer }, diff --git a/src/l-tile-layer-wms.test.js b/src/l-tile-layer-wms.test.js index 8663534..51a6928 100644 --- a/src/l-tile-layer-wms.test.js +++ b/src/l-tile-layer-wms.test.js @@ -1,5 +1,5 @@ // @vitest-environment happy-dom -import { tileLayer } from "leaflet"; +import { point, tileLayer } from "leaflet"; import { it, expect } from "vitest"; import { layerConnected } from "./events"; import "./index"; @@ -167,3 +167,20 @@ it("should not reload the layer when non-options attributes are changed", async expect(detail.layer.options.bbox).toBe("coords ere"); expect(layerConnectedEventEmittedCount).toBe(1); // initial layer creation only }); + +it.each([ + ["512", 512], + ["[256, 512]", point({x: 256, y: 512})], + ['{"x": 256, "y": 512}', point({x: 256, y: 512})] +])("should support tile-size attribute", (text, tileSize) => { + const baseUrl = "/" + const layers = "layer-1" + const el = document.createElement("l-tile-layer-wms"); + el.setAttribute("url-template", baseUrl); + el.setAttribute("layers", layers) + el.setAttribute("tile-size", text) + document.body.appendChild(el); + const actual = el.layer; + const expected = tileLayer.wms(baseUrl, {layers, tileSize}); + expect(actual).toEqual(expected); +}) diff --git a/src/l-tile-layer.js b/src/l-tile-layer.js index 8185645..6bde124 100644 --- a/src/l-tile-layer.js +++ b/src/l-tile-layer.js @@ -3,6 +3,8 @@ import { tileLayer } from "leaflet"; import { layerConnected } from "./events.js"; import LLayer from "./l-layer.js"; import { htmlAttribute, optional, parse, partial } from "./parse.js"; +import { gridLayerOptions } from "./grid-layer.js"; + class LTileLayer extends LLayer { constructor() { @@ -34,10 +36,14 @@ class LTileLayer extends LLayer { const name = this.getAttribute("name"); const schema = partial({ attribution: optional(htmlAttribute("attribution")), - errorTileUrl: optional(htmlAttribute("error-tile-url")) + errorTileUrl: optional(htmlAttribute("error-tile-url")), }) const options = parse(schema, this) - this.layer = tileLayer(urlTemplate, { ...templateOptions, ...paneOptions, ...options }); + + // GridLayer options + const gridOptions = gridLayerOptions(this); + + this.layer = tileLayer(urlTemplate, { ...templateOptions, ...paneOptions, ...options, ...gridOptions }); const event = new CustomEvent(layerConnected, { detail: { name, layer: this.layer }, bubbles: true, diff --git a/src/l-tile-layer.test.js b/src/l-tile-layer.test.js index cf1940b..c586058 100644 --- a/src/l-tile-layer.test.js +++ b/src/l-tile-layer.test.js @@ -51,3 +51,28 @@ it("should support error-tile-url", () => { const expected = tileLayer(urlTemplate, { errorTileUrl }) expect(actual).toEqual(expected) }) + +it.each([ + ["512", 512], + ["[256, 512]", {x: 256, y: 512}], + ['{"x": 256, "y": 512}', {x: 256, y: 512}] +])("should support tile-size attribute", (text, value) => { + const urlTemplate = "/" + const el = document.createElement("l-tile-layer"); + el.setAttribute("url-template", urlTemplate); + el.setAttribute("tile-size", text) + document.body.appendChild(el); + const actual = el.layer; + const expected = tileLayer(urlTemplate, { tileSize: value }) + expect(actual).toEqual(expected); +}) + +it("should support tile-size attribute default value", () => { + const urlTemplate = "/" + const el = document.createElement("l-tile-layer"); + el.setAttribute("url-template", urlTemplate); + document.body.appendChild(el); + const actual = el.layer; + const expected = tileLayer(urlTemplate, {}) + expect(actual).toEqual(expected); +}) From f2d4e09d1fa57932dbc92b146bef1d92662d87f8 Mon Sep 17 00:00:00 2001 From: andrewgryan Date: Thu, 2 Jan 2025 15:40:16 +0000 Subject: [PATCH 2/4] prettier --- src/grid-layer.js | 5 ++-- src/l-tile-layer-wms.test.js | 16 ++++++------- src/l-tile-layer.js | 36 ++++++++++++++++------------- src/l-tile-layer.test.js | 44 ++++++++++++++++++------------------ 4 files changed, 52 insertions(+), 49 deletions(-) diff --git a/src/grid-layer.js b/src/grid-layer.js index 47aea46..7432b5b 100644 --- a/src/grid-layer.js +++ b/src/grid-layer.js @@ -1,13 +1,12 @@ // Helpers to support GridLayer inherited Leaflet functions import { point } from "leaflet"; - /** * @param {Element} el * @returns {import("leaflet").GridLayerOptions} */ export const gridLayerOptions = (el) => { - const options = {} + const options = {}; if (el.hasAttribute("tile-size")) { let tileSize = null; const text = el.getAttribute("tile-size"); @@ -20,4 +19,4 @@ export const gridLayerOptions = (el) => { options["tileSize"] = tileSize; } return options; -} +}; diff --git a/src/l-tile-layer-wms.test.js b/src/l-tile-layer-wms.test.js index 51a6928..8bece42 100644 --- a/src/l-tile-layer-wms.test.js +++ b/src/l-tile-layer-wms.test.js @@ -170,17 +170,17 @@ it("should not reload the layer when non-options attributes are changed", async it.each([ ["512", 512], - ["[256, 512]", point({x: 256, y: 512})], - ['{"x": 256, "y": 512}', point({x: 256, y: 512})] + ["[256, 512]", point({ x: 256, y: 512 })], + ['{"x": 256, "y": 512}', point({ x: 256, y: 512 })], ])("should support tile-size attribute", (text, tileSize) => { - const baseUrl = "/" - const layers = "layer-1" + const baseUrl = "/"; + const layers = "layer-1"; const el = document.createElement("l-tile-layer-wms"); el.setAttribute("url-template", baseUrl); - el.setAttribute("layers", layers) - el.setAttribute("tile-size", text) + el.setAttribute("layers", layers); + el.setAttribute("tile-size", text); document.body.appendChild(el); const actual = el.layer; - const expected = tileLayer.wms(baseUrl, {layers, tileSize}); + const expected = tileLayer.wms(baseUrl, { layers, tileSize }); expect(actual).toEqual(expected); -}) +}); diff --git a/src/l-tile-layer.js b/src/l-tile-layer.js index 6bde124..b8de34a 100644 --- a/src/l-tile-layer.js +++ b/src/l-tile-layer.js @@ -5,7 +5,6 @@ import LLayer from "./l-layer.js"; import { htmlAttribute, optional, parse, partial } from "./parse.js"; import { gridLayerOptions } from "./grid-layer.js"; - class LTileLayer extends LLayer { constructor() { super(); @@ -13,37 +12,42 @@ class LTileLayer extends LLayer { } connectedCallback() { - const urlTemplate = parse(htmlAttribute("url-template"), this) - + const urlTemplate = parse(htmlAttribute("url-template"), this); + // Template attributes - const urlAttributes = LTileLayer.parseTemplateAttributes(urlTemplate) - const templateOptions = {} + const urlAttributes = LTileLayer.parseTemplateAttributes(urlTemplate); + const templateOptions = {}; for (const attribute of urlAttributes) { - const value = this.getAttribute(attribute) + const value = this.getAttribute(attribute); if (value !== null) { - templateOptions[attribute] = value + templateOptions[attribute] = value; } } // Pane options - const paneOptions = {} + const paneOptions = {}; // Support parent element if (this.parentElement.tagName.toLowerCase() === "l-pane") { - paneOptions["pane"] = this.parentElement.getAttribute("name") + paneOptions["pane"] = this.parentElement.getAttribute("name"); } - + // Options const name = this.getAttribute("name"); const schema = partial({ attribution: optional(htmlAttribute("attribution")), errorTileUrl: optional(htmlAttribute("error-tile-url")), - }) - const options = parse(schema, this) + }); + const options = parse(schema, this); // GridLayer options const gridOptions = gridLayerOptions(this); - - this.layer = tileLayer(urlTemplate, { ...templateOptions, ...paneOptions, ...options, ...gridOptions }); + + this.layer = tileLayer(urlTemplate, { + ...templateOptions, + ...paneOptions, + ...options, + ...gridOptions, + }); const event = new CustomEvent(layerConnected, { detail: { name, layer: this.layer }, bubbles: true, @@ -56,8 +60,8 @@ class LTileLayer extends LLayer { * @returns {string[]} */ static parseTemplateAttributes(urlTemplate) { - const regex = /{(.*?)}/g - return [...urlTemplate.matchAll(regex)].map(match => match[1]) + const regex = /{(.*?)}/g; + return [...urlTemplate.matchAll(regex)].map((match) => match[1]); } } diff --git a/src/l-tile-layer.test.js b/src/l-tile-layer.test.js index c586058..fa00595 100644 --- a/src/l-tile-layer.test.js +++ b/src/l-tile-layer.test.js @@ -27,52 +27,52 @@ it("should cover l-tile-layer", async () => { it.each([ ["/tile/{z}/{x}/{y}.png?key={key}", "key", "value"], ["/tile/{z}/{x}/{y}.png?key={camelCase}", "camelCase", "value"], - ["/tile/{z}/{x}/{y}.png?key={kebab-case}", "kebab-case", "value"] + ["/tile/{z}/{x}/{y}.png?key={kebab-case}", "kebab-case", "value"], ])("should perform arbitrary templating %s %s", (urlTemplate, key, value) => { const el = document.createElement("l-tile-layer"); el.setAttribute("url-template", urlTemplate); - el.setAttribute(key, value) + el.setAttribute(key, value); document.body.appendChild(el); - const actual = el.layer - const expected = tileLayer(urlTemplate, { [key]: value }) - expect(actual).toEqual(expected) -}) + const actual = el.layer; + const expected = tileLayer(urlTemplate, { [key]: value }); + expect(actual).toEqual(expected); +}); it("should support error-tile-url", () => { - const urlTemplate = "/{z}/{x}/{y}.png" - const errorTileUrl = "/error.png" + const urlTemplate = "/{z}/{x}/{y}.png"; + const errorTileUrl = "/error.png"; const el = document.createElement("l-tile-layer"); el.setAttribute("url-template", urlTemplate); - el.setAttribute("error-tile-url", errorTileUrl) + el.setAttribute("error-tile-url", errorTileUrl); document.body.appendChild(el); - const actual = el.layer - const expected = tileLayer(urlTemplate, { errorTileUrl }) - expect(actual).toEqual(expected) -}) + const actual = el.layer; + const expected = tileLayer(urlTemplate, { errorTileUrl }); + expect(actual).toEqual(expected); +}); it.each([ ["512", 512], - ["[256, 512]", {x: 256, y: 512}], - ['{"x": 256, "y": 512}', {x: 256, y: 512}] + ["[256, 512]", { x: 256, y: 512 }], + ['{"x": 256, "y": 512}', { x: 256, y: 512 }], ])("should support tile-size attribute", (text, value) => { - const urlTemplate = "/" + const urlTemplate = "/"; const el = document.createElement("l-tile-layer"); el.setAttribute("url-template", urlTemplate); - el.setAttribute("tile-size", text) + el.setAttribute("tile-size", text); document.body.appendChild(el); const actual = el.layer; - const expected = tileLayer(urlTemplate, { tileSize: value }) + const expected = tileLayer(urlTemplate, { tileSize: value }); expect(actual).toEqual(expected); -}) +}); it("should support tile-size attribute default value", () => { - const urlTemplate = "/" + const urlTemplate = "/"; const el = document.createElement("l-tile-layer"); el.setAttribute("url-template", urlTemplate); document.body.appendChild(el); const actual = el.layer; - const expected = tileLayer(urlTemplate, {}) + const expected = tileLayer(urlTemplate, {}); expect(actual).toEqual(expected); -}) +}); From 18438327d54f08495bf20822ec5321214f3fb9df Mon Sep 17 00:00:00 2001 From: andrewgryan Date: Thu, 2 Jan 2025 15:43:54 +0000 Subject: [PATCH 3/4] convert to ternary and use falsy behaviour of getAttribute --- src/grid-layer.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/grid-layer.js b/src/grid-layer.js index 7432b5b..aac74a5 100644 --- a/src/grid-layer.js +++ b/src/grid-layer.js @@ -7,16 +7,10 @@ import { point } from "leaflet"; */ export const gridLayerOptions = (el) => { const options = {}; - if (el.hasAttribute("tile-size")) { - let tileSize = null; - const text = el.getAttribute("tile-size"); + const text = el.getAttribute("tile-size"); + if (text) { const number = parseInt(text); - if (isNaN(number)) { - tileSize = point(JSON.parse(text)); - } else { - tileSize = number; - } - options["tileSize"] = tileSize; + options["tileSize"] = isNaN(number) ? point(JSON.parse(text)) : number; } return options; }; From e754bdbf075690c94b3f35d1c62929bc4fcf2799 Mon Sep 17 00:00:00 2001 From: andrewgryan Date: Thu, 2 Jan 2025 15:50:28 +0000 Subject: [PATCH 4/4] Release 0.13.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 51a5f6b..af379f8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "leaflet-html", "type": "module", - "version": "0.13.5", + "version": "0.13.6", "description": "Leaflet maps expressed in HTML suitable for HTMX", "keywords": [ "leaflet",