@india-boundary-corrector/layer-configs 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <https://unlicense.org>
package/README.md ADDED
@@ -0,0 +1,124 @@
1
+ # @india-boundary-corrector/layer-configs
2
+
3
+ Pre-built layer configurations for India boundary corrector packages.
4
+
5
+ ## Available Configs
6
+
7
+ - `cartodb-dark` - CartoDB dark tiles
8
+ - `cartodb-light` - CartoDB light/voyager tiles
9
+ - `open-topo` - OpenTopoMap tiles
10
+ - `osm-carto` - OpenStreetMap standard tiles
11
+ - `osm-hot` - Humanitarian OpenStreetMap tiles
12
+
13
+ ## Usage
14
+
15
+ ```javascript
16
+ import {
17
+ layerConfigs,
18
+ LayerConfig
19
+ } from '@india-boundary-corrector/layer-configs';
20
+
21
+ // Get a config by id
22
+ const darkConfig = layerConfigs.get('cartodb-dark');
23
+
24
+ // Or detect from tile URL templates using the registry
25
+ const tileUrlTemplates = [
26
+ 'https://a.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png',
27
+ 'https://b.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png'
28
+ ];
29
+ const config = layerConfigs.detectFromTemplates(tileUrlTemplates);
30
+
31
+ // Or detect from actual tile URLs (with numeric coordinates)
32
+ const actualTileUrl = 'https://a.basemaps.cartocdn.com/dark_all/5/10/15.png';
33
+ const config2 = layerConfigs.detectFromTileUrls([actualTileUrl]);
34
+
35
+ // List available config ids
36
+ console.log(layerConfigs.getAvailableIds());
37
+ // ['cartodb-dark', 'cartodb-light', 'open-topo', 'osm-carto', 'osm-hot']
38
+
39
+ // Create and register a custom config
40
+ const myConfig = new LayerConfig({
41
+ id: 'my-custom-style',
42
+ startZoom: 0,
43
+ zoomThreshold: 5,
44
+ tileUrlTemplates: ['https://mytiles.com/{z}/{x}/{y}.png'],
45
+ // Zoom-to-width interpolation map
46
+ lineWidthStops: { 1: 0.6, 10: 3 },
47
+ // Line styles - drawn in order
48
+ lineStyles: [
49
+ { color: '#262626' }, // solid line
50
+ { color: '#262626', widthFraction: 0.5, dashArray: [30, 2, 8, 2] }, // dashed overlay
51
+ ],
52
+ });
53
+ layerConfigs.register(myConfig);
54
+
55
+ // Remove a config
56
+ layerConfigs.remove('my-custom-style');
57
+ ```
58
+
59
+ ## LayerConfig Options
60
+
61
+ | Option | Type | Default | Description |
62
+ |--------|------|---------|-------------|
63
+ | `id` | string | required | Unique identifier for the config |
64
+ | `startZoom` | number | 0 | Minimum zoom level to start rendering corrections |
65
+ | `zoomThreshold` | number | 5 | Zoom level to switch between NE and OSM data |
66
+ | `tileUrlTemplates` | string \| string[] | [] | URL templates for matching tiles (e.g., `https://{s}.tile.example.com/{z}/{x}/{y}.png`) |
67
+ | `lineWidthStops` | object | { 1: 0.5, 10: 2.5 } | Zoom-to-width interpolation map |
68
+ | `lineStyles` | array | [{ color: 'green' }] | Array of line styles to draw |
69
+ | `delWidthFactor` | number | 1.5 | Multiplier for deletion line width |
70
+ | `lineExtensionFactor` | number | 0.5 | Factor to extend add lines by (multiplied by deletion line width). Helps cover gaps where deleted lines meet the new boundary. Set to 0 to disable. |
71
+
72
+ ### URL Template Placeholders
73
+
74
+ | Placeholder | Description |
75
+ |-------------|-------------|
76
+ | `{z}` | Zoom level |
77
+ | `{x}` | Tile X coordinate |
78
+ | `{y}` | Tile Y coordinate |
79
+ | `{s}` | Subdomain - Leaflet style (optional, matches a, b, c, etc.) |
80
+ | `{a-c}` | Subdomain - OpenLayers style (matches a, b, c) |
81
+ | `{1-4}` | Subdomain - OpenLayers numeric style (matches 1, 2, 3, 4) |
82
+ | `{r}` | Retina suffix (optional, matches @2x, etc.) |
83
+
84
+ ### LineStyle Object
85
+
86
+ | Property | Type | Default | Description |
87
+ |----------|------|---------|-------------|
88
+ | `color` | string | required | Line color (CSS color string) |
89
+ | `widthFraction` | number | 1.0 | Width as fraction of base line width |
90
+ | `dashArray` | number[] | - | Dash pattern array (omit for solid line) |
91
+ | `alpha` | number | 1.0 | Opacity/alpha value from 0 (transparent) to 1 (opaque) |
92
+ | `startZoom` | number | layerConfig.startZoom | Minimum zoom level for this style |
93
+ | `endZoom` | number | Infinity | Maximum zoom level for this style |
94
+
95
+ ### Zoom-Specific Line Styles
96
+
97
+ Line styles can be active only at certain zoom levels:
98
+
99
+ ```javascript
100
+ const config = new LayerConfig({
101
+ id: 'zoom-specific',
102
+ startZoom: 1,
103
+ lineStyles: [
104
+ { color: 'red' }, // Active at z1+ (all zooms)
105
+ { color: 'blue', startZoom: 5 }, // Active at z5+
106
+ { color: 'green', endZoom: 4 }, // Active at z1-4
107
+ { color: 'yellow', startZoom: 3, endZoom: 6 }, // Active at z3-6 only
108
+ ],
109
+ });
110
+
111
+ // Get active styles for a specific zoom
112
+ config.getLineStylesForZoom(3); // Returns styles: red, green, yellow
113
+ config.getLineStylesForZoom(7); // Returns styles: red, blue
114
+ ```
115
+
116
+ ### Line Width Calculation
117
+
118
+ Line widths are interpolated/extrapolated from the `lineWidthStops` map:
119
+ - **Addition lines**: `baseLineWidth * widthFraction` where baseLineWidth is interpolated from `lineWidthStops`
120
+ - **Deletion lines**: `baseLineWidth * maxWidthFraction * delWidthFactor` where maxWidthFraction is the largest widthFraction among active styles
121
+
122
+ ## License
123
+
124
+ Unlicense
package/dist/index.cjs ADDED
@@ -0,0 +1,407 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.js
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ LayerConfig: () => LayerConfig,
24
+ LayerConfigRegistry: () => LayerConfigRegistry,
25
+ layerConfigs: () => layerConfigs
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+
29
+ // src/configs.json
30
+ var configs_default = [
31
+ {
32
+ id: "cartodb-dark",
33
+ zoomThreshold: 5,
34
+ tileUrlTemplates: [
35
+ "https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png",
36
+ "https://basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png",
37
+ "https://cartodb-basemaps-{s}.global.ssl.fastly.net/dark_all/{z}/{x}/{y}{r}.png",
38
+ "https://{s}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}{r}.png",
39
+ "https://basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}{r}.png",
40
+ "https://cartodb-basemaps-{s}.global.ssl.fastly.net/dark_nolabels/{z}/{x}/{y}{r}.png"
41
+ ],
42
+ lineWidthStops: { "1": 0.5, "10": 2.5 },
43
+ lineStyles: [
44
+ { color: "rgb(40, 40, 40)" }
45
+ ]
46
+ },
47
+ {
48
+ id: "cartodb-light",
49
+ startZoom: 0,
50
+ zoomThreshold: 5,
51
+ tileUrlTemplates: [
52
+ "https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png",
53
+ "https://basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png",
54
+ "https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}{r}.png",
55
+ "https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png",
56
+ "https://basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png",
57
+ "https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_nolabels/{z}/{x}/{y}{r}.png",
58
+ "https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png",
59
+ "https://basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png",
60
+ "https://{s}.basemaps.cartocdn.com/rastertiles/voyager_nolabels/{z}/{x}/{y}{r}.png",
61
+ "https://basemaps.cartocdn.com/rastertiles/voyager_nolabels/{z}/{x}/{y}{r}.png",
62
+ "https://{s}.basemaps.cartocdn.com/rastertiles/voyager_labels_under/{z}/{x}/{y}{r}.png",
63
+ "https://basemaps.cartocdn.com/rastertiles/voyager_labels_under/{z}/{x}/{y}{r}.png"
64
+ ],
65
+ lineWidthStops: { "1": 0.25, "2": 0.25, "3": 0.5, "4": 0.75, "5": 1 },
66
+ lineStyles: [
67
+ { color: "rgb(235, 214, 214)", alpha: 0.2, startZoom: 6, widthFraction: 5 },
68
+ { color: "rgb(235, 214, 214)" }
69
+ ]
70
+ },
71
+ {
72
+ id: "open-topo",
73
+ startZoom: 4,
74
+ zoomThreshold: 4,
75
+ tileUrlTemplates: [
76
+ "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png",
77
+ "https://tile.opentopomap.org/{z}/{x}/{y}.png"
78
+ ],
79
+ lineWidthStops: { "4": 0.75, "5": 1, "6": 1.25, "7": 1.5, "8": 1.75, "9": 1.25, "10": 1.25, "13": 1.5 },
80
+ lineStyles: [
81
+ { color: "rgb(83, 83, 83)", startZoom: 7, endZoom: 8, alpha: 0.4, widthFraction: 4 },
82
+ { color: "rgb(83, 83, 83)", endZoom: 8 },
83
+ { color: "rgb(140, 20, 180)", startZoom: 9, widthFraction: 9, alpha: 0.2 },
84
+ { color: "rgb(140, 20, 180)", startZoom: 9 }
85
+ ]
86
+ },
87
+ {
88
+ id: "osm-carto",
89
+ startZoom: 1,
90
+ zoomThreshold: 1,
91
+ tileUrlTemplates: [
92
+ "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
93
+ "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
94
+ ],
95
+ lineWidthStops: { "1": 0.5, "2": 0.6, "3": 0.7, "4": 1, "10": 3.75 },
96
+ lineStyles: [
97
+ { color: "rgb(200, 180, 200)" },
98
+ { color: "rgb(160, 120, 160)", widthFraction: 0.333, dashArray: [30, 2, 8, 2] }
99
+ ]
100
+ },
101
+ {
102
+ id: "osm-hot",
103
+ startZoom: 2,
104
+ zoomThreshold: 2,
105
+ tileUrlTemplates: [
106
+ "https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png"
107
+ ],
108
+ lineWidthStops: { "2": 3, "3": 3, "7": 3, "8": 3.5, "9": 3.5 },
109
+ lineStyles: [
110
+ { color: "rgb(149, 175, 180)" },
111
+ { color: "rgb(89, 117, 123)", widthFraction: 0.33 }
112
+ ]
113
+ }
114
+ ];
115
+
116
+ // src/layerconfig.js
117
+ function templateToRegex(template) {
118
+ const groups = [];
119
+ let pattern = template.replace(/[.*+?^${}()|[\]\\]/g, (char) => {
120
+ if (char === "{" || char === "}") return char;
121
+ return "\\" + char;
122
+ }).replace(/^https:\/\//, "https?://").replace(/^http:\/\//, "https?://").replace(/\{[a-z0-9]-[a-z0-9]\}/gi, () => {
123
+ groups.push("s");
124
+ return "([a-z0-9]+)";
125
+ }).replace(/\{(z|x|y|s|r)\}/gi, (_, name) => {
126
+ const lowerName = name.toLowerCase();
127
+ groups.push(lowerName);
128
+ if (lowerName === "s") {
129
+ return "([a-z0-9]+)";
130
+ }
131
+ if (lowerName === "r") {
132
+ return "(@\\d+x)?";
133
+ }
134
+ return "(\\d+)";
135
+ });
136
+ return { pattern: new RegExp("^" + pattern + "(\\?.*)?$", "i"), groups };
137
+ }
138
+ function templateToTemplateRegex(template) {
139
+ let pattern = template.replace(/[.*+?^${}()|[\]\\]/g, (char) => {
140
+ if (char === "{" || char === "}") return char;
141
+ return "\\" + char;
142
+ }).replace(/^https:\/\//, "https?://").replace(/^http:\/\//, "https?://").replace(/\{([a-z0-9])-([a-z0-9])\}/gi, (_, start, end) => `(\\{${start}-${end}\\}|[a-z0-9]+)`).replace(/\{(z|x|y|s|r)\}/gi, (_, name) => {
143
+ const lowerName = name.toLowerCase();
144
+ if (lowerName === "s") {
145
+ return "(\\{s\\}|[a-z0-9]+)";
146
+ }
147
+ if (lowerName === "r") {
148
+ return "(\\{r\\}|@\\d+x)?";
149
+ }
150
+ return `\\{${lowerName}\\}`;
151
+ });
152
+ return new RegExp("^" + pattern + "(\\?.*)?$", "i");
153
+ }
154
+ var LayerConfig = class _LayerConfig {
155
+ constructor({
156
+ id,
157
+ startZoom = 0,
158
+ zoomThreshold = 5,
159
+ // Tile URL templates for matching (e.g., "https://{s}.tile.example.com/{z}/{x}/{y}.png")
160
+ tileUrlTemplates = [],
161
+ // Line width stops: map of zoom level to line width (at least 2 entries)
162
+ lineWidthStops = { 1: 0.5, 10: 2.5 },
163
+ // Line styles array - each element describes a line to draw
164
+ // { color: string, widthFraction?: number, dashArray?: number[], startZoom?: number, endZoom?: number }
165
+ // Lines are drawn in array order. startZoom defaults to layerConfig startZoom, endZoom defaults to Infinity
166
+ lineStyles = [{ color: "green", widthFraction: 1 }],
167
+ // Factor to multiply line width for deletion blur (default 1.5)
168
+ // Higher values leave gaps where wiped lines meet existing lines
169
+ // Lower values mean wiped lines show through
170
+ delWidthFactor = 1.5,
171
+ // Factor to extend add lines by (multiplied by deletion line width)
172
+ // Helps cover gaps where deleted lines meet the new boundary
173
+ // Set to 0 to disable extension
174
+ lineExtensionFactor = 0.5
175
+ }) {
176
+ if (!id || typeof id !== "string") {
177
+ throw new Error("LayerConfig requires a non-empty string id");
178
+ }
179
+ this.id = id;
180
+ this.startZoom = startZoom;
181
+ this.zoomThreshold = zoomThreshold;
182
+ if (startZoom > zoomThreshold) {
183
+ throw new Error(`LayerConfig "${id}": startZoom (${startZoom}) must be <= zoomThreshold (${zoomThreshold})`);
184
+ }
185
+ const templates = Array.isArray(tileUrlTemplates) ? tileUrlTemplates : tileUrlTemplates ? [tileUrlTemplates] : [];
186
+ this.tileUrlTemplates = templates;
187
+ this._compiledPatterns = templates.map((t) => templateToRegex(t));
188
+ this._templatePatterns = templates.map((t) => templateToTemplateRegex(t));
189
+ if (!lineWidthStops || typeof lineWidthStops !== "object" || Array.isArray(lineWidthStops)) {
190
+ throw new Error(`LayerConfig "${id}": lineWidthStops must be an object`);
191
+ }
192
+ const stopKeys = Object.keys(lineWidthStops);
193
+ if (stopKeys.length < 2) {
194
+ throw new Error(`LayerConfig "${id}": lineWidthStops must have at least 2 entries`);
195
+ }
196
+ for (const key of stopKeys) {
197
+ const zoom = Number(key);
198
+ if (!Number.isInteger(zoom) || zoom < 0) {
199
+ throw new Error(`LayerConfig "${id}": lineWidthStops keys must be non-negative integers, got "${key}"`);
200
+ }
201
+ if (typeof lineWidthStops[key] !== "number" || lineWidthStops[key] <= 0) {
202
+ throw new Error(`LayerConfig "${id}": lineWidthStops values must be positive numbers`);
203
+ }
204
+ }
205
+ this.lineWidthStops = lineWidthStops;
206
+ if (!Array.isArray(lineStyles) || lineStyles.length === 0) {
207
+ throw new Error(`LayerConfig "${id}": lineStyles must be a non-empty array`);
208
+ }
209
+ for (let i = 0; i < lineStyles.length; i++) {
210
+ const style = lineStyles[i];
211
+ if (!style || typeof style !== "object") {
212
+ throw new Error(`LayerConfig "${id}": lineStyles[${i}] must be an object`);
213
+ }
214
+ if (!style.color || typeof style.color !== "string") {
215
+ throw new Error(`LayerConfig "${id}": lineStyles[${i}].color must be a non-empty string`);
216
+ }
217
+ }
218
+ this.lineStyles = lineStyles.map((style) => ({
219
+ ...style,
220
+ startZoom: style.startZoom ?? startZoom,
221
+ endZoom: style.endZoom ?? Infinity
222
+ }));
223
+ this.delWidthFactor = delWidthFactor;
224
+ this.lineExtensionFactor = lineExtensionFactor;
225
+ }
226
+ /**
227
+ * Get line styles active at a given zoom level
228
+ * @param {number} z - Zoom level
229
+ * @returns {Array<{color: string, widthFraction?: number, dashArray?: number[]}>}
230
+ */
231
+ getLineStylesForZoom(z) {
232
+ return this.lineStyles.filter((style) => z >= style.startZoom && z <= style.endZoom);
233
+ }
234
+ /**
235
+ * Check if this config matches the given template URLs (with {z}/{x}/{y} placeholders)
236
+ * @param {string | string[]} templates - Single template URL or array of template URLs
237
+ * @returns {boolean}
238
+ */
239
+ matchTemplate(templates) {
240
+ if (this._templatePatterns.length === 0) return false;
241
+ const urls = Array.isArray(templates) ? templates : [templates];
242
+ if (urls.length === 0) return false;
243
+ return urls.some(
244
+ (url) => this._templatePatterns.some((pattern) => pattern.test(url))
245
+ );
246
+ }
247
+ /**
248
+ * Check if this config matches the given tile URLs (with actual coordinates)
249
+ * @param {string | string[]} tiles - Single tile URL or array of tile URLs
250
+ * @returns {boolean}
251
+ */
252
+ matchTileUrl(tiles) {
253
+ if (this._compiledPatterns.length === 0) return false;
254
+ const urls = Array.isArray(tiles) ? tiles : [tiles];
255
+ if (urls.length === 0) return false;
256
+ return urls.some(
257
+ (url) => this._compiledPatterns.some(({ pattern }) => pattern.test(url))
258
+ );
259
+ }
260
+ /**
261
+ * Extract tile coordinates (z, x, y) from a URL using this config's templates
262
+ * @param {string} url - Tile URL to extract coordinates from
263
+ * @returns {{ z: number, x: number, y: number } | null}
264
+ */
265
+ extractCoords(url) {
266
+ for (const { pattern, groups } of this._compiledPatterns) {
267
+ const match = url.match(pattern);
268
+ if (match) {
269
+ const result = {};
270
+ for (let i = 0; i < groups.length; i++) {
271
+ const name = groups[i];
272
+ const value = match[i + 1];
273
+ if (name === "z" || name === "x" || name === "y") {
274
+ result[name] = parseInt(value, 10);
275
+ }
276
+ }
277
+ if ("z" in result && "x" in result && "y" in result) {
278
+ return { z: result.z, x: result.x, y: result.y };
279
+ }
280
+ }
281
+ }
282
+ return null;
283
+ }
284
+ /**
285
+ * Serialize the config to a plain object for postMessage
286
+ * @returns {Object}
287
+ */
288
+ toJSON() {
289
+ return {
290
+ id: this.id,
291
+ startZoom: this.startZoom,
292
+ zoomThreshold: this.zoomThreshold,
293
+ tileUrlTemplates: this.tileUrlTemplates,
294
+ lineWidthStops: this.lineWidthStops,
295
+ lineStyles: this.lineStyles,
296
+ delWidthFactor: this.delWidthFactor,
297
+ lineExtensionFactor: this.lineExtensionFactor
298
+ };
299
+ }
300
+ /**
301
+ * Create a LayerConfig from a plain object (e.g., from postMessage)
302
+ * @param {Object} obj
303
+ * @returns {LayerConfig}
304
+ */
305
+ static fromJSON(obj) {
306
+ return new _LayerConfig(obj);
307
+ }
308
+ };
309
+
310
+ // src/index.js
311
+ var LayerConfigRegistry = class _LayerConfigRegistry {
312
+ constructor() {
313
+ this.registry = {};
314
+ }
315
+ /**
316
+ * Get a layer config by id
317
+ */
318
+ get(id) {
319
+ return this.registry[id];
320
+ }
321
+ /**
322
+ * Register a new layer config
323
+ */
324
+ register(config) {
325
+ this.registry[config.id] = config;
326
+ }
327
+ /**
328
+ * Remove a layer config by id
329
+ */
330
+ remove(id) {
331
+ if (!this.registry[id]) return false;
332
+ delete this.registry[id];
333
+ return true;
334
+ }
335
+ /**
336
+ * Detect layer config from tile URL templates (with {z}/{x}/{y} placeholders)
337
+ * @param {string | string[]} templates - Single template URL or array of template URLs
338
+ */
339
+ detectFromTemplates(templates) {
340
+ if (!templates || Array.isArray(templates) && templates.length === 0) return void 0;
341
+ for (const config of Object.values(this.registry)) {
342
+ if (config.matchTemplate(templates)) {
343
+ return config;
344
+ }
345
+ }
346
+ return void 0;
347
+ }
348
+ /**
349
+ * Detect layer config from actual tile URLs (with numeric coordinates)
350
+ * @param {string | string[]} urls - Single tile URL or array of tile URLs
351
+ */
352
+ detectFromTileUrls(urls) {
353
+ if (!urls || Array.isArray(urls) && urls.length === 0) return void 0;
354
+ for (const config of Object.values(this.registry)) {
355
+ if (config.matchTileUrl(urls)) {
356
+ return config;
357
+ }
358
+ }
359
+ return void 0;
360
+ }
361
+ /**
362
+ * Get all available layer config ids
363
+ */
364
+ getAvailableIds() {
365
+ return Object.keys(this.registry);
366
+ }
367
+ /**
368
+ * Create a new registry with all configs from this registry plus extra configs.
369
+ * @param {LayerConfig[]} extraLayerConfigs - Additional configs to add
370
+ * @returns {LayerConfigRegistry} A new registry with merged configs
371
+ */
372
+ createMergedRegistry(extraLayerConfigs) {
373
+ const registry = new _LayerConfigRegistry();
374
+ for (const id of this.getAvailableIds()) {
375
+ registry.register(this.get(id));
376
+ }
377
+ if (extraLayerConfigs && extraLayerConfigs.length > 0) {
378
+ for (const config of extraLayerConfigs) {
379
+ registry.register(config);
380
+ }
381
+ }
382
+ return registry;
383
+ }
384
+ /**
385
+ * Parse a tile URL into its components: layer config and coordinates
386
+ * @param {string} url - Tile URL to parse
387
+ * @returns {{ layerConfig: LayerConfig, coords: { z: number, x: number, y: number } } | null}
388
+ */
389
+ parseTileUrl(url) {
390
+ const layerConfig = this.detectFromTileUrls([url]);
391
+ if (!layerConfig) return null;
392
+ const coords = layerConfig.extractCoords(url);
393
+ if (!coords) return null;
394
+ return { layerConfig, coords };
395
+ }
396
+ };
397
+ var layerConfigs = new LayerConfigRegistry();
398
+ for (const configData of configs_default) {
399
+ layerConfigs.register(new LayerConfig(configData));
400
+ }
401
+ // Annotate the CommonJS export names for ESM import in node:
402
+ 0 && (module.exports = {
403
+ LayerConfig,
404
+ LayerConfigRegistry,
405
+ layerConfigs
406
+ });
407
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.js","../src/configs.json","../src/layerconfig.js"],"sourcesContent":["import configsJson from './configs.json' with { type: 'json' };\nimport { LayerConfig } from './layerconfig.js';\n\nexport { LayerConfig } from './layerconfig.js';\n\n/**\n * Layer configuration registry\n */\nexport class LayerConfigRegistry {\n constructor() {\n this.registry = {};\n }\n\n /**\n * Get a layer config by id\n */\n get(id) {\n return this.registry[id];\n }\n\n /**\n * Register a new layer config\n */\n register(config) {\n this.registry[config.id] = config;\n }\n\n /**\n * Remove a layer config by id\n */\n remove(id) {\n if (!this.registry[id]) return false;\n delete this.registry[id];\n return true;\n }\n\n /**\n * Detect layer config from tile URL templates (with {z}/{x}/{y} placeholders)\n * @param {string | string[]} templates - Single template URL or array of template URLs\n */\n detectFromTemplates(templates) {\n if (!templates || (Array.isArray(templates) && templates.length === 0)) return undefined;\n \n for (const config of Object.values(this.registry)) {\n if (config.matchTemplate(templates)) {\n return config;\n }\n }\n \n return undefined;\n }\n\n /**\n * Detect layer config from actual tile URLs (with numeric coordinates)\n * @param {string | string[]} urls - Single tile URL or array of tile URLs\n */\n detectFromTileUrls(urls) {\n if (!urls || (Array.isArray(urls) && urls.length === 0)) return undefined;\n \n for (const config of Object.values(this.registry)) {\n if (config.matchTileUrl(urls)) {\n return config;\n }\n }\n \n return undefined;\n }\n\n /**\n * Get all available layer config ids\n */\n getAvailableIds() {\n return Object.keys(this.registry);\n }\n\n /**\n * Create a new registry with all configs from this registry plus extra configs.\n * @param {LayerConfig[]} extraLayerConfigs - Additional configs to add\n * @returns {LayerConfigRegistry} A new registry with merged configs\n */\n createMergedRegistry(extraLayerConfigs) {\n const registry = new LayerConfigRegistry();\n \n for (const id of this.getAvailableIds()) {\n registry.register(this.get(id));\n }\n \n if (extraLayerConfigs && extraLayerConfigs.length > 0) {\n for (const config of extraLayerConfigs) {\n registry.register(config);\n }\n }\n \n return registry;\n }\n\n /**\n * Parse a tile URL into its components: layer config and coordinates\n * @param {string} url - Tile URL to parse\n * @returns {{ layerConfig: LayerConfig, coords: { z: number, x: number, y: number } } | null}\n */\n parseTileUrl(url) {\n // Check if URL matches any layer config\n const layerConfig = this.detectFromTileUrls([url]);\n if (!layerConfig) return null;\n \n // Extract tile coordinates using the matched config\n const coords = layerConfig.extractCoords(url);\n if (!coords) return null;\n \n return { layerConfig, coords };\n }\n}\n\n// Default registry with built-in configs loaded from JSON\nexport const layerConfigs = new LayerConfigRegistry();\nfor (const configData of configsJson) {\n layerConfigs.register(new LayerConfig(configData));\n}\n\n","[\n {\n \"id\": \"cartodb-dark\",\n \"zoomThreshold\": 5,\n \"tileUrlTemplates\": [\n \"https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png\",\n \"https://basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png\",\n \"https://cartodb-basemaps-{s}.global.ssl.fastly.net/dark_all/{z}/{x}/{y}{r}.png\",\n \"https://{s}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}{r}.png\",\n \"https://basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}{r}.png\",\n \"https://cartodb-basemaps-{s}.global.ssl.fastly.net/dark_nolabels/{z}/{x}/{y}{r}.png\"\n ],\n \"lineWidthStops\": { \"1\": 0.5, \"10\": 2.5 },\n \"lineStyles\": [\n { \"color\": \"rgb(40, 40, 40)\" }\n ]\n },\n {\n \"id\": \"cartodb-light\",\n \"startZoom\": 0,\n \"zoomThreshold\": 5,\n \"tileUrlTemplates\": [\n \"https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png\",\n \"https://basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png\",\n \"https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}{r}.png\",\n \"https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png\",\n \"https://basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png\",\n \"https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_nolabels/{z}/{x}/{y}{r}.png\",\n \"https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png\",\n \"https://basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png\",\n \"https://{s}.basemaps.cartocdn.com/rastertiles/voyager_nolabels/{z}/{x}/{y}{r}.png\",\n \"https://basemaps.cartocdn.com/rastertiles/voyager_nolabels/{z}/{x}/{y}{r}.png\",\n \"https://{s}.basemaps.cartocdn.com/rastertiles/voyager_labels_under/{z}/{x}/{y}{r}.png\",\n \"https://basemaps.cartocdn.com/rastertiles/voyager_labels_under/{z}/{x}/{y}{r}.png\"\n ],\n \"lineWidthStops\": { \"1\": 0.25, \"2\": 0.25, \"3\": 0.5, \"4\": 0.75, \"5\": 1.0 },\n \"lineStyles\": [\n { \"color\": \"rgb(235, 214, 214)\", \"alpha\": 0.2, \"startZoom\": 6, \"widthFraction\": 5 },\n { \"color\": \"rgb(235, 214, 214)\" }\n ]\n },\n {\n \"id\": \"open-topo\",\n \"startZoom\": 4,\n \"zoomThreshold\": 4,\n \"tileUrlTemplates\": [\n \"https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png\",\n \"https://tile.opentopomap.org/{z}/{x}/{y}.png\"\n ],\n \"lineWidthStops\": { \"4\": 0.75, \"5\": 1.0, \"6\": 1.25, \"7\": 1.5, \"8\": 1.75, \"9\": 1.25, \"10\": 1.25, \"13\": 1.5 },\n \"lineStyles\": [\n { \"color\": \"rgb(83, 83, 83)\", \"startZoom\": 7, \"endZoom\": 8, \"alpha\": 0.4, \"widthFraction\": 4 },\n { \"color\": \"rgb(83, 83, 83)\", \"endZoom\": 8 },\n { \"color\": \"rgb(140, 20, 180)\", \"startZoom\": 9, \"widthFraction\": 9, \"alpha\": 0.2 },\n { \"color\": \"rgb(140, 20, 180)\", \"startZoom\": 9 }\n ]\n },\n {\n \"id\": \"osm-carto\",\n \"startZoom\": 1,\n \"zoomThreshold\": 1,\n \"tileUrlTemplates\": [\n \"https://tile.openstreetmap.org/{z}/{x}/{y}.png\",\n \"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\"\n ],\n \"lineWidthStops\": { \"1\": 0.5, \"2\": 0.6, \"3\": 0.7, \"4\": 1.0, \"10\": 3.75 },\n \"lineStyles\": [\n { \"color\": \"rgb(200, 180, 200)\" },\n { \"color\": \"rgb(160, 120, 160)\", \"widthFraction\": 0.333, \"dashArray\": [30, 2, 8, 2] }\n ]\n },\n {\n \"id\": \"osm-hot\",\n \"startZoom\": 2,\n \"zoomThreshold\": 2,\n \"tileUrlTemplates\": [\n \"https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png\"\n ],\n \"lineWidthStops\": { \"2\": 3.0, \"3\": 3.0, \"7\": 3.0, \"8\": 3.5, \"9\": 3.5 },\n \"lineStyles\": [\n { \"color\": \"rgb(149, 175, 180)\" },\n { \"color\": \"rgb(89, 117, 123)\", \"widthFraction\": 0.33 }\n ]\n }\n]\n","/**\n * Convert a tile URL template to a regex pattern and capture group names.\n * Supports {z}, {x}, {y}, {s} (Leaflet subdomain), {a-c}/{1-4} (OpenLayers subdomain), and {r} (retina) placeholders.\n * @param {string} template - URL template like \"https://{s}.tile.example.com/{z}/{x}/{y}.png\"\n * @returns {{ pattern: RegExp, groups: string[] }}\n */\nfunction templateToRegex(template) {\n const groups = [];\n // Escape regex special chars, then replace placeholders\n let pattern = template\n .replace(/[.*+?^${}()|[\\]\\\\]/g, (char) => {\n // Don't escape our placeholders\n if (char === '{' || char === '}') return char;\n return '\\\\' + char;\n })\n // Make protocol flexible (http/https)\n .replace(/^https:\\/\\//, 'https?://')\n .replace(/^http:\\/\\//, 'https?://')\n // Handle {a-c} or {1-4} etc (OpenLayers style subdomain)\n .replace(/\\{[a-z0-9]-[a-z0-9]\\}/gi, () => {\n groups.push('s');\n return '([a-z0-9]+)';\n })\n .replace(/\\{(z|x|y|s|r)\\}/gi, (_, name) => {\n const lowerName = name.toLowerCase();\n groups.push(lowerName);\n if (lowerName === 's') {\n // Subdomain: single letter or short string\n return '([a-z0-9]+)';\n }\n if (lowerName === 'r') {\n // Retina suffix: optional @2x or similar\n return '(@\\\\d+x)?';\n }\n // z, x, y: numeric\n return '(\\\\d+)';\n });\n \n // Allow optional query string at end\n return { pattern: new RegExp('^' + pattern + '(\\\\?.*)?$', 'i'), groups };\n}\n\n/**\n * Convert a tile URL template to a regex that matches the template itself.\n * @param {string} template - URL template like \"https://{s}.tile.example.com/{z}/{x}/{y}.png\"\n * @returns {RegExp}\n */\nfunction templateToTemplateRegex(template) {\n // Escape regex special chars, then replace placeholders with literal match\n let pattern = template\n .replace(/[.*+?^${}()|[\\]\\\\]/g, (char) => {\n if (char === '{' || char === '}') return char;\n return '\\\\' + char;\n })\n // Make protocol flexible (http/https)\n .replace(/^https:\\/\\//, 'https?://')\n .replace(/^http:\\/\\//, 'https?://')\n // Handle {a-c} or {1-4} (OpenLayers style subdomain)\n .replace(/\\{([a-z0-9])-([a-z0-9])\\}/gi, (_, start, end) => `(\\\\{${start}-${end}\\\\}|[a-z0-9]+)`)\n .replace(/\\{(z|x|y|s|r)\\}/gi, (_, name) => {\n const lowerName = name.toLowerCase();\n if (lowerName === 's') {\n // Match {s} placeholder or actual subdomain\n return '(\\\\{s\\\\}|[a-z0-9]+)';\n }\n if (lowerName === 'r') {\n // Match {r} placeholder or actual retina or empty\n return '(\\\\{r\\\\}|@\\\\d+x)?';\n }\n // Match {z}, {x}, {y} placeholders\n return `\\\\{${lowerName}\\\\}`;\n });\n \n // Allow optional query string at end\n return new RegExp('^' + pattern + '(\\\\?.*)?$', 'i');\n}\n\n/**\n * Base class for layer configurations\n * \n * Supports separate styling for NE (Natural Earth) data at low zoom levels\n * and OSM data at higher zoom levels, split by zoomThreshold.\n */\nexport class LayerConfig {\n constructor({\n id,\n startZoom = 0,\n zoomThreshold = 5,\n // Tile URL templates for matching (e.g., \"https://{s}.tile.example.com/{z}/{x}/{y}.png\")\n tileUrlTemplates = [],\n // Line width stops: map of zoom level to line width (at least 2 entries)\n lineWidthStops = { 1: 0.5, 10: 2.5 },\n // Line styles array - each element describes a line to draw\n // { color: string, widthFraction?: number, dashArray?: number[], startZoom?: number, endZoom?: number }\n // Lines are drawn in array order. startZoom defaults to layerConfig startZoom, endZoom defaults to Infinity\n lineStyles = [{ color: 'green', widthFraction: 1.0 }],\n // Factor to multiply line width for deletion blur (default 1.5)\n // Higher values leave gaps where wiped lines meet existing lines\n // Lower values mean wiped lines show through\n delWidthFactor = 1.5,\n // Factor to extend add lines by (multiplied by deletion line width)\n // Helps cover gaps where deleted lines meet the new boundary\n // Set to 0 to disable extension\n lineExtensionFactor = 0.5,\n }) {\n if (!id || typeof id !== 'string') {\n throw new Error('LayerConfig requires a non-empty string id');\n }\n\n this.id = id;\n this.startZoom = startZoom;\n this.zoomThreshold = zoomThreshold;\n\n if (startZoom > zoomThreshold) {\n throw new Error(`LayerConfig \"${id}\": startZoom (${startZoom}) must be <= zoomThreshold (${zoomThreshold})`);\n }\n\n // Normalize to array\n const templates = Array.isArray(tileUrlTemplates) ? tileUrlTemplates : \n (tileUrlTemplates ? [tileUrlTemplates] : []);\n this.tileUrlTemplates = templates;\n \n // Pre-compile regex patterns for matching tile URLs (with actual coords)\n this._compiledPatterns = templates.map(t => templateToRegex(t));\n \n // Pre-compile regex patterns for matching template URLs (with {z}/{x}/{y} placeholders)\n this._templatePatterns = templates.map(t => templateToTemplateRegex(t));\n\n // Validate lineWidthStops\n if (!lineWidthStops || typeof lineWidthStops !== 'object' || Array.isArray(lineWidthStops)) {\n throw new Error(`LayerConfig \"${id}\": lineWidthStops must be an object`);\n }\n const stopKeys = Object.keys(lineWidthStops);\n if (stopKeys.length < 2) {\n throw new Error(`LayerConfig \"${id}\": lineWidthStops must have at least 2 entries`);\n }\n for (const key of stopKeys) {\n const zoom = Number(key);\n if (!Number.isInteger(zoom) || zoom < 0) {\n throw new Error(`LayerConfig \"${id}\": lineWidthStops keys must be non-negative integers, got \"${key}\"`);\n }\n if (typeof lineWidthStops[key] !== 'number' || lineWidthStops[key] <= 0) {\n throw new Error(`LayerConfig \"${id}\": lineWidthStops values must be positive numbers`);\n }\n }\n this.lineWidthStops = lineWidthStops;\n\n // Validate lineStyles\n if (!Array.isArray(lineStyles) || lineStyles.length === 0) {\n throw new Error(`LayerConfig \"${id}\": lineStyles must be a non-empty array`);\n }\n for (let i = 0; i < lineStyles.length; i++) {\n const style = lineStyles[i];\n if (!style || typeof style !== 'object') {\n throw new Error(`LayerConfig \"${id}\": lineStyles[${i}] must be an object`);\n }\n if (!style.color || typeof style.color !== 'string') {\n throw new Error(`LayerConfig \"${id}\": lineStyles[${i}].color must be a non-empty string`);\n }\n }\n \n // Line styles - normalize startZoom/endZoom defaults\n this.lineStyles = lineStyles.map(style => ({\n ...style,\n startZoom: style.startZoom ?? startZoom,\n endZoom: style.endZoom ?? Infinity,\n }));\n \n // Deletion width factor\n this.delWidthFactor = delWidthFactor;\n \n // Line extension factor\n this.lineExtensionFactor = lineExtensionFactor;\n }\n\n /**\n * Get line styles active at a given zoom level\n * @param {number} z - Zoom level\n * @returns {Array<{color: string, widthFraction?: number, dashArray?: number[]}>}\n */\n getLineStylesForZoom(z) {\n return this.lineStyles.filter(style => z >= style.startZoom && z <= style.endZoom);\n }\n\n /**\n * Check if this config matches the given template URLs (with {z}/{x}/{y} placeholders)\n * @param {string | string[]} templates - Single template URL or array of template URLs\n * @returns {boolean}\n */\n matchTemplate(templates) {\n if (this._templatePatterns.length === 0) return false;\n \n const urls = Array.isArray(templates) ? templates : [templates];\n if (urls.length === 0) return false;\n \n return urls.some(url => \n this._templatePatterns.some(pattern => pattern.test(url))\n );\n }\n\n /**\n * Check if this config matches the given tile URLs (with actual coordinates)\n * @param {string | string[]} tiles - Single tile URL or array of tile URLs\n * @returns {boolean}\n */\n matchTileUrl(tiles) {\n if (this._compiledPatterns.length === 0) return false;\n \n const urls = Array.isArray(tiles) ? tiles : [tiles];\n if (urls.length === 0) return false;\n \n return urls.some(url => \n this._compiledPatterns.some(({ pattern }) => pattern.test(url))\n );\n }\n\n /**\n * Extract tile coordinates (z, x, y) from a URL using this config's templates\n * @param {string} url - Tile URL to extract coordinates from\n * @returns {{ z: number, x: number, y: number } | null}\n */\n extractCoords(url) {\n for (const { pattern, groups } of this._compiledPatterns) {\n const match = url.match(pattern);\n if (match) {\n const result = {};\n for (let i = 0; i < groups.length; i++) {\n const name = groups[i];\n const value = match[i + 1];\n if (name === 'z' || name === 'x' || name === 'y') {\n result[name] = parseInt(value, 10);\n }\n }\n if ('z' in result && 'x' in result && 'y' in result) {\n return { z: result.z, x: result.x, y: result.y };\n }\n }\n }\n return null;\n }\n\n /**\n * Serialize the config to a plain object for postMessage\n * @returns {Object}\n */\n toJSON() {\n return {\n id: this.id,\n startZoom: this.startZoom,\n zoomThreshold: this.zoomThreshold,\n tileUrlTemplates: this.tileUrlTemplates,\n lineWidthStops: this.lineWidthStops,\n lineStyles: this.lineStyles,\n delWidthFactor: this.delWidthFactor,\n lineExtensionFactor: this.lineExtensionFactor,\n };\n }\n\n /**\n * Create a LayerConfig from a plain object (e.g., from postMessage)\n * @param {Object} obj\n * @returns {LayerConfig}\n */\n static fromJSON(obj) {\n return new LayerConfig(obj);\n }\n}\n\nexport default LayerConfig;\n"],"mappings":"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","names":[]}