@india-boundary-corrector/maplibre-protocol 0.2.0 → 0.2.2

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/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  MapLibre GL custom protocol for India boundary corrections.
6
6
 
7
- [Try it on JSFiddle](https://jsfiddle.net/2ym91L65/3/)
7
+ [Try it on JSFiddle](https://jsfiddle.net/txboc1rh/)
8
8
 
9
9
  ## Installation
10
10
 
@@ -30,7 +30,7 @@ var IndiaBoundaryCorrector = (() => {
30
30
  });
31
31
 
32
32
  // ../data/version.js
33
- var packageVersion = "0.2.0";
33
+ var packageVersion = "0.2.2";
34
34
 
35
35
  // ../data/index.js
36
36
  var import_meta = {};
@@ -128,12 +128,11 @@ var IndiaBoundaryCorrector = (() => {
128
128
  "https://{s}.basemaps.cartocdn.com/rastertiles/voyager_labels_under/{z}/{x}/{y}.png",
129
129
  "https://basemaps.cartocdn.com/rastertiles/voyager_labels_under/{z}/{x}/{y}.png"
130
130
  ],
131
- lineWidthStops: { "1": 0.5, "2": 0.75, "3": 0.75, "4": 1, "5": 1, "7": 1.5, "16": 2.5 },
131
+ lineWidthStops: { "1": 0.5, "2": 0.75, "3": 0.75, "4": 1, "5": 1, "7": 1.75, "16": 2.5 },
132
132
  lineStyles: [
133
133
  { color: "rgb(235, 214, 214)", layerSuffix: "ne", endZoom: 4, lineExtensionFactor: 0.1, delWidthFactor: 2.5 },
134
134
  { color: "rgb(235, 214, 214)", layerSuffix: "ne-disp", endZoom: 4, delWidthFactor: 0 },
135
- { color: "rgb(235, 214, 214)", layerSuffix: "osm", alpha: 0.2, startZoom: 6, endZoom: 11, widthFraction: 5, lineExtensionFactor: 0.1 },
136
- { color: "rgb(235, 214, 214)", layerSuffix: "osm", alpha: 0.2, startZoom: 12, widthFraction: 4, lineExtensionFactor: 0.1 },
135
+ { color: "rgb(235, 214, 214)", layerSuffix: "osm", alpha: 0.1, startZoom: 6, lineWidthStops: { "6": 9, "10": 8, "14": 8 }, lineExtensionFactor: 0.1 },
137
136
  { color: "rgb(235, 214, 214)", layerSuffix: "osm", startZoom: 5, lineExtensionFactor: 0.1 },
138
137
  { color: "rgb(235, 214, 214)", layerSuffix: "osm-disp", startZoom: 5, delWidthFactor: 0 },
139
138
  { color: "rgb(235, 214, 214)", layerSuffix: "osm-internal", startZoom: 5, widthFraction: 0.5, dashArray: [2, 2], delWidthFactor: 2 },
@@ -156,12 +155,11 @@ var IndiaBoundaryCorrector = (() => {
156
155
  "https://{s}.basemaps.cartocdn.com/rastertiles/voyager_labels_under/{z}/{x}/{y}{r}.png",
157
156
  "https://basemaps.cartocdn.com/rastertiles/voyager_labels_under/{z}/{x}/{y}{r}.png"
158
157
  ],
159
- lineWidthStops: { "1": 0.75, "2": 0.75, "3": 0.75, "4": 1, "5": 1, "7": 1.5, "11": 2 },
158
+ lineWidthStops: { "1": 1, "3": 1, "4": 2, "5": 2, "6": 2.5, "7": 3, "8": 3, "10": 4, "11": 4, "12": 4 },
160
159
  lineStyles: [
161
160
  { color: "rgb(235, 214, 214)", layerSuffix: "ne", endZoom: 4, lineExtensionFactor: 0.1, delWidthFactor: 2.5 },
162
161
  { color: "rgb(235, 214, 214)", layerSuffix: "ne-disp", endZoom: 4, delWidthFactor: 0 },
163
- { color: "rgb(235, 214, 214)", layerSuffix: "osm", alpha: 0.1, startZoom: 6, endZoom: 11, widthFraction: 5, lineExtensionFactor: 0.1 },
164
- { color: "rgb(235, 214, 214)", layerSuffix: "osm", alpha: 0.1, startZoom: 12, widthFraction: 4, lineExtensionFactor: 0.1 },
162
+ { color: "rgb(235, 214, 214)", layerSuffix: "osm", alpha: 0.1, startZoom: 6, lineWidthStops: { "6": 20, "8": 19, "10": 18, "12": 17 }, lineExtensionFactor: 0.1 },
165
163
  { color: "rgb(235, 214, 214)", layerSuffix: "osm", startZoom: 5, lineExtensionFactor: 0.1 },
166
164
  { color: "rgb(235, 214, 214)", layerSuffix: "osm-disp", startZoom: 5, delWidthFactor: 0 },
167
165
  { color: "rgb(235, 214, 214)", layerSuffix: "osm-internal", startZoom: 5, widthFraction: 0.5, dashArray: [4, 4], delWidthFactor: 2 },
@@ -172,15 +170,20 @@ var IndiaBoundaryCorrector = (() => {
172
170
  id: "open-topo",
173
171
  tileUrlTemplates: [
174
172
  "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png",
175
- "https://tile.opentopomap.org/{z}/{x}/{y}.png"
173
+ "https://tile.opentopomap.org/{z}/{x}/{y}.png",
174
+ "https://{s}.tile.top-o-map.de/{z}/{x}/{y}.png",
175
+ "https://tile.top-o-map.de/{z}/{x}/{y}.png"
176
176
  ],
177
177
  lineWidthStops: { "4": 0.75, "5": 1, "6": 1.25, "7": 1.5, "8": 1.75, "9": 1.25, "10": 1.25, "13": 1.5 },
178
178
  lineStyles: [
179
179
  { color: "rgb(83, 83, 83)", layerSuffix: "ne", startZoom: 4, endZoom: 6 },
180
180
  { color: "rgb(173, 173, 173)", layerSuffix: "osm", startZoom: 7, endZoom: 8, alpha: 0.5, widthFraction: 4 },
181
181
  { color: "rgb(83, 83, 83)", layerSuffix: "osm", startZoom: 7, endZoom: 8 },
182
+ { color: "rgb(83, 83, 83)", layerSuffix: "osm-internal", widthFraction: 0.75, startZoom: 7, endZoom: 8 },
182
183
  { color: "rgb(199, 158, 204)", layerSuffix: "osm", startZoom: 9, widthFraction: 7, alpha: 0.6, lineExtensionFactor: 0.2 },
183
- { color: "rgb(175, 41, 203)", layerSuffix: "osm", startZoom: 9, lineExtensionFactor: 0.2 }
184
+ { color: "rgb(175, 41, 203)", layerSuffix: "osm", startZoom: 9, lineExtensionFactor: 0.2 },
185
+ { color: "rgb(199, 158, 204)", layerSuffix: "osm-internal", startZoom: 9, widthFraction: 2.25, alpha: 0.6, lineExtensionFactor: 0.2 },
186
+ { color: "rgb(175, 41, 203)", layerSuffix: "osm-internal", startZoom: 9, widthFactor: 0.75, lineExtensionFactor: 0.2 }
184
187
  ]
185
188
  },
186
189
  {
@@ -189,9 +192,8 @@ var IndiaBoundaryCorrector = (() => {
189
192
  "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
190
193
  "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
191
194
  ],
192
- lineWidthStops: { "1": 0.5, "2": 0.6, "3": 0.7, "4": 1, "10": 3.75 },
195
+ lineWidthStops: { "3": 0.7, "4": 1, "10": 3.75 },
193
196
  lineStyles: [
194
- { color: "rgb(200, 180, 200)", layerSuffix: "osm", startZoom: 1, endZoom: 3, delWidthFactor: 2.5, widthFraction: 1.5 },
195
197
  { color: "rgb(200, 180, 200)", layerSuffix: "osm", startZoom: 4, delWidthFactor: 1.5 },
196
198
  { color: "rgb(160, 120, 160)", layerSuffix: "osm", startZoom: 4, widthFraction: 0.333, dashArray: [30, 2, 8, 2], delWidthFactor: 0 },
197
199
  { color: "rgb(200, 180, 200)", layerSuffix: "osm-internal", startZoom: 4, widthFraction: 0.45 },
@@ -215,6 +217,39 @@ var IndiaBoundaryCorrector = (() => {
215
217
  var INFINITY = -1;
216
218
  var MIN_LINE_WIDTH = 0.1;
217
219
  var DEFAULT_LINE_WIDTH = 1;
220
+ function interpolateLineWidth(zoom, lineWidthStops) {
221
+ const zooms = Object.keys(lineWidthStops).map(Number).sort((a, b2) => a - b2);
222
+ if (lineWidthStops[zoom] !== void 0) {
223
+ return lineWidthStops[zoom];
224
+ }
225
+ if (zoom < zooms[0]) {
226
+ const z1 = zooms[0];
227
+ const z2 = zooms[1];
228
+ const w1 = lineWidthStops[z1];
229
+ const w2 = lineWidthStops[z2];
230
+ const slope = (w2 - w1) / (z2 - z1);
231
+ return Math.max(MIN_LINE_WIDTH, w1 + slope * (zoom - z1));
232
+ }
233
+ if (zoom > zooms[zooms.length - 1]) {
234
+ const z1 = zooms[zooms.length - 2];
235
+ const z2 = zooms[zooms.length - 1];
236
+ const w1 = lineWidthStops[z1];
237
+ const w2 = lineWidthStops[z2];
238
+ const slope = (w2 - w1) / (z2 - z1);
239
+ return Math.max(MIN_LINE_WIDTH, w2 + slope * (zoom - z2));
240
+ }
241
+ for (let i2 = 0; i2 < zooms.length - 1; i2++) {
242
+ if (zoom > zooms[i2] && zoom < zooms[i2 + 1]) {
243
+ const z1 = zooms[i2];
244
+ const z2 = zooms[i2 + 1];
245
+ const w1 = lineWidthStops[z1];
246
+ const w2 = lineWidthStops[z2];
247
+ const t = (zoom - z1) / (z2 - z1);
248
+ return w1 + t * (w2 - w1);
249
+ }
250
+ }
251
+ return DEFAULT_LINE_WIDTH;
252
+ }
218
253
  function templateToRegex(template) {
219
254
  const groups = [];
220
255
  const hasRetina = template.includes("{r}");
@@ -281,14 +316,33 @@ var IndiaBoundaryCorrector = (() => {
281
316
  if (/^[a-z]+$/.test(trimmed)) return true;
282
317
  return false;
283
318
  }
319
+ function validateLineWidthStops(lineWidthStops, prefix) {
320
+ if (!lineWidthStops || typeof lineWidthStops !== "object" || Array.isArray(lineWidthStops)) {
321
+ throw new Error(`${prefix}: lineWidthStops must be an object`);
322
+ }
323
+ const stopKeys = Object.keys(lineWidthStops);
324
+ if (stopKeys.length < 2) {
325
+ throw new Error(`${prefix}: lineWidthStops must have at least 2 entries`);
326
+ }
327
+ for (const key of stopKeys) {
328
+ const zoom = Number(key);
329
+ if (!Number.isInteger(zoom) || zoom < 0) {
330
+ throw new Error(`${prefix}: lineWidthStops keys must be non-negative integers, got "${key}"`);
331
+ }
332
+ if (typeof lineWidthStops[key] !== "number" || lineWidthStops[key] <= 0) {
333
+ throw new Error(`${prefix}: lineWidthStops values must be positive numbers`);
334
+ }
335
+ }
336
+ }
284
337
  var LineStyle = class _LineStyle {
285
338
  /**
286
339
  * Validate a LineStyle configuration object.
287
340
  * @param {Object} obj - The object to validate
288
341
  * @param {number} [index] - Optional index for error messages (when validating in an array)
342
+ * @param {boolean} [requireLineWidthStops=false] - Whether lineWidthStops is required
289
343
  * @throws {Error} If validation fails
290
344
  */
291
- static validateJSON(obj, index) {
345
+ static validateJSON(obj, index, requireLineWidthStops = false) {
292
346
  const prefix = index !== void 0 ? `lineStyles[${index}]` : "LineStyle";
293
347
  if (!obj || typeof obj !== "object") {
294
348
  throw new Error(`${prefix}: must be an object`);
@@ -323,22 +377,30 @@ var IndiaBoundaryCorrector = (() => {
323
377
  if (obj.delWidthFactor !== void 0 && (typeof obj.delWidthFactor !== "number" || obj.delWidthFactor < 0)) {
324
378
  throw new Error(`${prefix}: delWidthFactor must be a non-negative number`);
325
379
  }
380
+ if (requireLineWidthStops && obj.lineWidthStops === void 0) {
381
+ throw new Error(`${prefix}: lineWidthStops is required`);
382
+ }
383
+ if (obj.lineWidthStops !== void 0) {
384
+ validateLineWidthStops(obj.lineWidthStops, prefix);
385
+ }
326
386
  }
327
387
  /**
328
388
  * @param {Object} options
329
389
  * @param {string} options.color - CSS color string
330
390
  * @param {string} options.layerSuffix - Layer suffix (e.g., 'osm', 'ne', 'osm-disp')
391
+ * @param {Object<number, number>} options.lineWidthStops - Line width stops for this style
331
392
  * @param {number} [options.widthFraction=1.0] - Multiplier for base line width
332
393
  * @param {number[]} [options.dashArray] - Dash pattern for dashed lines
333
394
  * @param {number} [options.alpha=1.0] - Opacity (0-1)
334
395
  * @param {number} [options.startZoom=0] - Minimum zoom level for this style
335
396
  * @param {number} [options.endZoom=INFINITY] - Maximum zoom level for this style (INFINITY means no limit)
336
- * @param {number} [options.lineExtensionFactor=0.5] - Factor to extend lines by (multiplied by deletion line width)
397
+ * @param {number} [options.lineExtensionFactor=0.0] - Factor to extend lines by (multiplied by deletion line width)
337
398
  * @param {number} [options.delWidthFactor=1.5] - Factor to multiply line width for deletion blur
338
399
  */
339
- constructor({ color, layerSuffix, widthFraction = 1, dashArray, alpha = 1, startZoom = 0, endZoom = INFINITY, lineExtensionFactor = 0, delWidthFactor = 1.5 }) {
400
+ constructor({ color, layerSuffix, lineWidthStops, widthFraction = 1, dashArray, alpha = 1, startZoom = 0, endZoom = INFINITY, lineExtensionFactor = 0, delWidthFactor = 1.5 }) {
340
401
  this.color = color;
341
402
  this.layerSuffix = layerSuffix;
403
+ this.lineWidthStops = lineWidthStops;
342
404
  this.widthFraction = widthFraction;
343
405
  this.dashArray = dashArray;
344
406
  this.alpha = alpha;
@@ -347,6 +409,14 @@ var IndiaBoundaryCorrector = (() => {
347
409
  this.lineExtensionFactor = lineExtensionFactor;
348
410
  this.delWidthFactor = delWidthFactor;
349
411
  }
412
+ /**
413
+ * Get base line width for this style at a given zoom level.
414
+ * @param {number} zoom - Zoom level
415
+ * @returns {number}
416
+ */
417
+ getLineWidth(zoom) {
418
+ return interpolateLineWidth(zoom, this.lineWidthStops);
419
+ }
350
420
  /**
351
421
  * Check if this style is active at the given zoom level.
352
422
  * @param {number} z - Zoom level
@@ -363,6 +433,7 @@ var IndiaBoundaryCorrector = (() => {
363
433
  return {
364
434
  color: this.color,
365
435
  layerSuffix: this.layerSuffix,
436
+ lineWidthStops: this.lineWidthStops,
366
437
  widthFraction: this.widthFraction,
367
438
  dashArray: this.dashArray,
368
439
  alpha: this.alpha,
@@ -379,7 +450,7 @@ var IndiaBoundaryCorrector = (() => {
379
450
  * @returns {LineStyle}
380
451
  */
381
452
  static fromJSON(obj, index) {
382
- _LineStyle.validateJSON(obj, index);
453
+ _LineStyle.validateJSON(obj, index, true);
383
454
  return new _LineStyle(obj);
384
455
  }
385
456
  };
@@ -445,9 +516,13 @@ var IndiaBoundaryCorrector = (() => {
445
516
  this._compiledPatterns = templates.map((t) => templateToRegex(t));
446
517
  this._templatePatterns = templates.map((t) => templateToTemplateRegex(t));
447
518
  this.lineWidthStops = lineWidthStops;
448
- this.lineStyles = lineStyles.map(
449
- (style) => style instanceof LineStyle ? style : new LineStyle(style)
450
- );
519
+ this.lineStyles = lineStyles.map((style) => {
520
+ if (style instanceof LineStyle) {
521
+ return style;
522
+ }
523
+ const styleWithStops = style.lineWidthStops ? style : { ...style, lineWidthStops };
524
+ return new LineStyle(styleWithStops);
525
+ });
451
526
  }
452
527
  /**
453
528
  * Get line styles active at a given zoom level
@@ -466,45 +541,6 @@ var IndiaBoundaryCorrector = (() => {
466
541
  const activeStyles = this.getLineStylesForZoom(z2);
467
542
  return [...new Set(activeStyles.map((s) => s.layerSuffix))];
468
543
  }
469
- /**
470
- * Interpolate or extrapolate line width for a given zoom level.
471
- * Uses the lineWidthStops map to calculate the appropriate width.
472
- * @param {number} zoom - Zoom level
473
- * @returns {number}
474
- */
475
- getLineWidth(zoom) {
476
- const zooms = Object.keys(this.lineWidthStops).map(Number).sort((a, b2) => a - b2);
477
- if (this.lineWidthStops[zoom] !== void 0) {
478
- return this.lineWidthStops[zoom];
479
- }
480
- if (zoom < zooms[0]) {
481
- const z1 = zooms[0];
482
- const z2 = zooms[1];
483
- const w1 = this.lineWidthStops[z1];
484
- const w2 = this.lineWidthStops[z2];
485
- const slope = (w2 - w1) / (z2 - z1);
486
- return Math.max(MIN_LINE_WIDTH, w1 + slope * (zoom - z1));
487
- }
488
- if (zoom > zooms[zooms.length - 1]) {
489
- const z1 = zooms[zooms.length - 2];
490
- const z2 = zooms[zooms.length - 1];
491
- const w1 = this.lineWidthStops[z1];
492
- const w2 = this.lineWidthStops[z2];
493
- const slope = (w2 - w1) / (z2 - z1);
494
- return Math.max(MIN_LINE_WIDTH, w2 + slope * (zoom - z2));
495
- }
496
- for (let i2 = 0; i2 < zooms.length - 1; i2++) {
497
- if (zoom > zooms[i2] && zoom < zooms[i2 + 1]) {
498
- const z1 = zooms[i2];
499
- const z2 = zooms[i2 + 1];
500
- const w1 = this.lineWidthStops[z1];
501
- const w2 = this.lineWidthStops[z2];
502
- const t = (zoom - z1) / (z2 - z1);
503
- return w1 + t * (w2 - w1);
504
- }
505
- }
506
- return DEFAULT_LINE_WIDTH;
507
- }
508
544
  /**
509
545
  * Check if this config matches the given template URLs (with {z}/{x}/{y} placeholders)
510
546
  * @param {string | string[]} templates - Single template URL or array of template URLs
@@ -2958,12 +2994,11 @@ var IndiaBoundaryCorrector = (() => {
2958
2994
  "https://{s}.basemaps.cartocdn.com/rastertiles/voyager_labels_under/{z}/{x}/{y}.png",
2959
2995
  "https://basemaps.cartocdn.com/rastertiles/voyager_labels_under/{z}/{x}/{y}.png"
2960
2996
  ],
2961
- lineWidthStops: { "1": 0.5, "2": 0.75, "3": 0.75, "4": 1, "5": 1, "7": 1.5, "16": 2.5 },
2997
+ lineWidthStops: { "1": 0.5, "2": 0.75, "3": 0.75, "4": 1, "5": 1, "7": 1.75, "16": 2.5 },
2962
2998
  lineStyles: [
2963
2999
  { color: "rgb(235, 214, 214)", layerSuffix: "ne", endZoom: 4, lineExtensionFactor: 0.1, delWidthFactor: 2.5 },
2964
3000
  { color: "rgb(235, 214, 214)", layerSuffix: "ne-disp", endZoom: 4, delWidthFactor: 0 },
2965
- { color: "rgb(235, 214, 214)", layerSuffix: "osm", alpha: 0.2, startZoom: 6, endZoom: 11, widthFraction: 5, lineExtensionFactor: 0.1 },
2966
- { color: "rgb(235, 214, 214)", layerSuffix: "osm", alpha: 0.2, startZoom: 12, widthFraction: 4, lineExtensionFactor: 0.1 },
3001
+ { color: "rgb(235, 214, 214)", layerSuffix: "osm", alpha: 0.1, startZoom: 6, lineWidthStops: { "6": 9, "10": 8, "14": 8 }, lineExtensionFactor: 0.1 },
2967
3002
  { color: "rgb(235, 214, 214)", layerSuffix: "osm", startZoom: 5, lineExtensionFactor: 0.1 },
2968
3003
  { color: "rgb(235, 214, 214)", layerSuffix: "osm-disp", startZoom: 5, delWidthFactor: 0 },
2969
3004
  { color: "rgb(235, 214, 214)", layerSuffix: "osm-internal", startZoom: 5, widthFraction: 0.5, dashArray: [2, 2], delWidthFactor: 2 },
@@ -2986,12 +3021,11 @@ var IndiaBoundaryCorrector = (() => {
2986
3021
  "https://{s}.basemaps.cartocdn.com/rastertiles/voyager_labels_under/{z}/{x}/{y}{r}.png",
2987
3022
  "https://basemaps.cartocdn.com/rastertiles/voyager_labels_under/{z}/{x}/{y}{r}.png"
2988
3023
  ],
2989
- lineWidthStops: { "1": 0.75, "2": 0.75, "3": 0.75, "4": 1, "5": 1, "7": 1.5, "11": 2 },
3024
+ lineWidthStops: { "1": 1, "3": 1, "4": 2, "5": 2, "6": 2.5, "7": 3, "8": 3, "10": 4, "11": 4, "12": 4 },
2990
3025
  lineStyles: [
2991
3026
  { color: "rgb(235, 214, 214)", layerSuffix: "ne", endZoom: 4, lineExtensionFactor: 0.1, delWidthFactor: 2.5 },
2992
3027
  { color: "rgb(235, 214, 214)", layerSuffix: "ne-disp", endZoom: 4, delWidthFactor: 0 },
2993
- { color: "rgb(235, 214, 214)", layerSuffix: "osm", alpha: 0.1, startZoom: 6, endZoom: 11, widthFraction: 5, lineExtensionFactor: 0.1 },
2994
- { color: "rgb(235, 214, 214)", layerSuffix: "osm", alpha: 0.1, startZoom: 12, widthFraction: 4, lineExtensionFactor: 0.1 },
3028
+ { color: "rgb(235, 214, 214)", layerSuffix: "osm", alpha: 0.1, startZoom: 6, lineWidthStops: { "6": 20, "8": 19, "10": 18, "12": 17 }, lineExtensionFactor: 0.1 },
2995
3029
  { color: "rgb(235, 214, 214)", layerSuffix: "osm", startZoom: 5, lineExtensionFactor: 0.1 },
2996
3030
  { color: "rgb(235, 214, 214)", layerSuffix: "osm-disp", startZoom: 5, delWidthFactor: 0 },
2997
3031
  { color: "rgb(235, 214, 214)", layerSuffix: "osm-internal", startZoom: 5, widthFraction: 0.5, dashArray: [4, 4], delWidthFactor: 2 },
@@ -3002,15 +3036,20 @@ var IndiaBoundaryCorrector = (() => {
3002
3036
  id: "open-topo",
3003
3037
  tileUrlTemplates: [
3004
3038
  "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png",
3005
- "https://tile.opentopomap.org/{z}/{x}/{y}.png"
3039
+ "https://tile.opentopomap.org/{z}/{x}/{y}.png",
3040
+ "https://{s}.tile.top-o-map.de/{z}/{x}/{y}.png",
3041
+ "https://tile.top-o-map.de/{z}/{x}/{y}.png"
3006
3042
  ],
3007
3043
  lineWidthStops: { "4": 0.75, "5": 1, "6": 1.25, "7": 1.5, "8": 1.75, "9": 1.25, "10": 1.25, "13": 1.5 },
3008
3044
  lineStyles: [
3009
3045
  { color: "rgb(83, 83, 83)", layerSuffix: "ne", startZoom: 4, endZoom: 6 },
3010
3046
  { color: "rgb(173, 173, 173)", layerSuffix: "osm", startZoom: 7, endZoom: 8, alpha: 0.5, widthFraction: 4 },
3011
3047
  { color: "rgb(83, 83, 83)", layerSuffix: "osm", startZoom: 7, endZoom: 8 },
3048
+ { color: "rgb(83, 83, 83)", layerSuffix: "osm-internal", widthFraction: 0.75, startZoom: 7, endZoom: 8 },
3012
3049
  { color: "rgb(199, 158, 204)", layerSuffix: "osm", startZoom: 9, widthFraction: 7, alpha: 0.6, lineExtensionFactor: 0.2 },
3013
- { color: "rgb(175, 41, 203)", layerSuffix: "osm", startZoom: 9, lineExtensionFactor: 0.2 }
3050
+ { color: "rgb(175, 41, 203)", layerSuffix: "osm", startZoom: 9, lineExtensionFactor: 0.2 },
3051
+ { color: "rgb(199, 158, 204)", layerSuffix: "osm-internal", startZoom: 9, widthFraction: 2.25, alpha: 0.6, lineExtensionFactor: 0.2 },
3052
+ { color: "rgb(175, 41, 203)", layerSuffix: "osm-internal", startZoom: 9, widthFactor: 0.75, lineExtensionFactor: 0.2 }
3014
3053
  ]
3015
3054
  },
3016
3055
  {
@@ -3019,9 +3058,8 @@ var IndiaBoundaryCorrector = (() => {
3019
3058
  "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
3020
3059
  "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
3021
3060
  ],
3022
- lineWidthStops: { "1": 0.5, "2": 0.6, "3": 0.7, "4": 1, "10": 3.75 },
3061
+ lineWidthStops: { "3": 0.7, "4": 1, "10": 3.75 },
3023
3062
  lineStyles: [
3024
- { color: "rgb(200, 180, 200)", layerSuffix: "osm", startZoom: 1, endZoom: 3, delWidthFactor: 2.5, widthFraction: 1.5 },
3025
3063
  { color: "rgb(200, 180, 200)", layerSuffix: "osm", startZoom: 4, delWidthFactor: 1.5 },
3026
3064
  { color: "rgb(160, 120, 160)", layerSuffix: "osm", startZoom: 4, widthFraction: 0.333, dashArray: [30, 2, 8, 2], delWidthFactor: 0 },
3027
3065
  { color: "rgb(200, 180, 200)", layerSuffix: "osm-internal", startZoom: 4, widthFraction: 0.45 },
@@ -3045,6 +3083,39 @@ var IndiaBoundaryCorrector = (() => {
3045
3083
  var INFINITY2 = -1;
3046
3084
  var MIN_LINE_WIDTH2 = 0.1;
3047
3085
  var DEFAULT_LINE_WIDTH2 = 1;
3086
+ function interpolateLineWidth2(zoom, lineWidthStops) {
3087
+ const zooms = Object.keys(lineWidthStops).map(Number).sort((a, b2) => a - b2);
3088
+ if (lineWidthStops[zoom] !== void 0) {
3089
+ return lineWidthStops[zoom];
3090
+ }
3091
+ if (zoom < zooms[0]) {
3092
+ const z1 = zooms[0];
3093
+ const z2 = zooms[1];
3094
+ const w1 = lineWidthStops[z1];
3095
+ const w2 = lineWidthStops[z2];
3096
+ const slope = (w2 - w1) / (z2 - z1);
3097
+ return Math.max(MIN_LINE_WIDTH2, w1 + slope * (zoom - z1));
3098
+ }
3099
+ if (zoom > zooms[zooms.length - 1]) {
3100
+ const z1 = zooms[zooms.length - 2];
3101
+ const z2 = zooms[zooms.length - 1];
3102
+ const w1 = lineWidthStops[z1];
3103
+ const w2 = lineWidthStops[z2];
3104
+ const slope = (w2 - w1) / (z2 - z1);
3105
+ return Math.max(MIN_LINE_WIDTH2, w2 + slope * (zoom - z2));
3106
+ }
3107
+ for (let i2 = 0; i2 < zooms.length - 1; i2++) {
3108
+ if (zoom > zooms[i2] && zoom < zooms[i2 + 1]) {
3109
+ const z1 = zooms[i2];
3110
+ const z2 = zooms[i2 + 1];
3111
+ const w1 = lineWidthStops[z1];
3112
+ const w2 = lineWidthStops[z2];
3113
+ const t = (zoom - z1) / (z2 - z1);
3114
+ return w1 + t * (w2 - w1);
3115
+ }
3116
+ }
3117
+ return DEFAULT_LINE_WIDTH2;
3118
+ }
3048
3119
  function templateToRegex2(template) {
3049
3120
  const groups = [];
3050
3121
  const hasRetina = template.includes("{r}");
@@ -3111,14 +3182,33 @@ var IndiaBoundaryCorrector = (() => {
3111
3182
  if (/^[a-z]+$/.test(trimmed)) return true;
3112
3183
  return false;
3113
3184
  }
3185
+ function validateLineWidthStops2(lineWidthStops, prefix) {
3186
+ if (!lineWidthStops || typeof lineWidthStops !== "object" || Array.isArray(lineWidthStops)) {
3187
+ throw new Error(`${prefix}: lineWidthStops must be an object`);
3188
+ }
3189
+ const stopKeys = Object.keys(lineWidthStops);
3190
+ if (stopKeys.length < 2) {
3191
+ throw new Error(`${prefix}: lineWidthStops must have at least 2 entries`);
3192
+ }
3193
+ for (const key of stopKeys) {
3194
+ const zoom = Number(key);
3195
+ if (!Number.isInteger(zoom) || zoom < 0) {
3196
+ throw new Error(`${prefix}: lineWidthStops keys must be non-negative integers, got "${key}"`);
3197
+ }
3198
+ if (typeof lineWidthStops[key] !== "number" || lineWidthStops[key] <= 0) {
3199
+ throw new Error(`${prefix}: lineWidthStops values must be positive numbers`);
3200
+ }
3201
+ }
3202
+ }
3114
3203
  var LineStyle2 = class _LineStyle2 {
3115
3204
  /**
3116
3205
  * Validate a LineStyle configuration object.
3117
3206
  * @param {Object} obj - The object to validate
3118
3207
  * @param {number} [index] - Optional index for error messages (when validating in an array)
3208
+ * @param {boolean} [requireLineWidthStops=false] - Whether lineWidthStops is required
3119
3209
  * @throws {Error} If validation fails
3120
3210
  */
3121
- static validateJSON(obj, index) {
3211
+ static validateJSON(obj, index, requireLineWidthStops = false) {
3122
3212
  const prefix = index !== void 0 ? `lineStyles[${index}]` : "LineStyle";
3123
3213
  if (!obj || typeof obj !== "object") {
3124
3214
  throw new Error(`${prefix}: must be an object`);
@@ -3153,22 +3243,30 @@ var IndiaBoundaryCorrector = (() => {
3153
3243
  if (obj.delWidthFactor !== void 0 && (typeof obj.delWidthFactor !== "number" || obj.delWidthFactor < 0)) {
3154
3244
  throw new Error(`${prefix}: delWidthFactor must be a non-negative number`);
3155
3245
  }
3246
+ if (requireLineWidthStops && obj.lineWidthStops === void 0) {
3247
+ throw new Error(`${prefix}: lineWidthStops is required`);
3248
+ }
3249
+ if (obj.lineWidthStops !== void 0) {
3250
+ validateLineWidthStops2(obj.lineWidthStops, prefix);
3251
+ }
3156
3252
  }
3157
3253
  /**
3158
3254
  * @param {Object} options
3159
3255
  * @param {string} options.color - CSS color string
3160
3256
  * @param {string} options.layerSuffix - Layer suffix (e.g., 'osm', 'ne', 'osm-disp')
3257
+ * @param {Object<number, number>} options.lineWidthStops - Line width stops for this style
3161
3258
  * @param {number} [options.widthFraction=1.0] - Multiplier for base line width
3162
3259
  * @param {number[]} [options.dashArray] - Dash pattern for dashed lines
3163
3260
  * @param {number} [options.alpha=1.0] - Opacity (0-1)
3164
3261
  * @param {number} [options.startZoom=0] - Minimum zoom level for this style
3165
3262
  * @param {number} [options.endZoom=INFINITY] - Maximum zoom level for this style (INFINITY means no limit)
3166
- * @param {number} [options.lineExtensionFactor=0.5] - Factor to extend lines by (multiplied by deletion line width)
3263
+ * @param {number} [options.lineExtensionFactor=0.0] - Factor to extend lines by (multiplied by deletion line width)
3167
3264
  * @param {number} [options.delWidthFactor=1.5] - Factor to multiply line width for deletion blur
3168
3265
  */
3169
- constructor({ color, layerSuffix, widthFraction = 1, dashArray, alpha = 1, startZoom = 0, endZoom = INFINITY2, lineExtensionFactor = 0, delWidthFactor = 1.5 }) {
3266
+ constructor({ color, layerSuffix, lineWidthStops, widthFraction = 1, dashArray, alpha = 1, startZoom = 0, endZoom = INFINITY2, lineExtensionFactor = 0, delWidthFactor = 1.5 }) {
3170
3267
  this.color = color;
3171
3268
  this.layerSuffix = layerSuffix;
3269
+ this.lineWidthStops = lineWidthStops;
3172
3270
  this.widthFraction = widthFraction;
3173
3271
  this.dashArray = dashArray;
3174
3272
  this.alpha = alpha;
@@ -3177,6 +3275,14 @@ var IndiaBoundaryCorrector = (() => {
3177
3275
  this.lineExtensionFactor = lineExtensionFactor;
3178
3276
  this.delWidthFactor = delWidthFactor;
3179
3277
  }
3278
+ /**
3279
+ * Get base line width for this style at a given zoom level.
3280
+ * @param {number} zoom - Zoom level
3281
+ * @returns {number}
3282
+ */
3283
+ getLineWidth(zoom) {
3284
+ return interpolateLineWidth2(zoom, this.lineWidthStops);
3285
+ }
3180
3286
  /**
3181
3287
  * Check if this style is active at the given zoom level.
3182
3288
  * @param {number} z - Zoom level
@@ -3193,6 +3299,7 @@ var IndiaBoundaryCorrector = (() => {
3193
3299
  return {
3194
3300
  color: this.color,
3195
3301
  layerSuffix: this.layerSuffix,
3302
+ lineWidthStops: this.lineWidthStops,
3196
3303
  widthFraction: this.widthFraction,
3197
3304
  dashArray: this.dashArray,
3198
3305
  alpha: this.alpha,
@@ -3209,7 +3316,7 @@ var IndiaBoundaryCorrector = (() => {
3209
3316
  * @returns {LineStyle}
3210
3317
  */
3211
3318
  static fromJSON(obj, index) {
3212
- _LineStyle2.validateJSON(obj, index);
3319
+ _LineStyle2.validateJSON(obj, index, true);
3213
3320
  return new _LineStyle2(obj);
3214
3321
  }
3215
3322
  };
@@ -3275,9 +3382,13 @@ var IndiaBoundaryCorrector = (() => {
3275
3382
  this._compiledPatterns = templates.map((t) => templateToRegex2(t));
3276
3383
  this._templatePatterns = templates.map((t) => templateToTemplateRegex2(t));
3277
3384
  this.lineWidthStops = lineWidthStops;
3278
- this.lineStyles = lineStyles.map(
3279
- (style) => style instanceof LineStyle2 ? style : new LineStyle2(style)
3280
- );
3385
+ this.lineStyles = lineStyles.map((style) => {
3386
+ if (style instanceof LineStyle2) {
3387
+ return style;
3388
+ }
3389
+ const styleWithStops = style.lineWidthStops ? style : { ...style, lineWidthStops };
3390
+ return new LineStyle2(styleWithStops);
3391
+ });
3281
3392
  }
3282
3393
  /**
3283
3394
  * Get line styles active at a given zoom level
@@ -3296,45 +3407,6 @@ var IndiaBoundaryCorrector = (() => {
3296
3407
  const activeStyles = this.getLineStylesForZoom(z2);
3297
3408
  return [...new Set(activeStyles.map((s) => s.layerSuffix))];
3298
3409
  }
3299
- /**
3300
- * Interpolate or extrapolate line width for a given zoom level.
3301
- * Uses the lineWidthStops map to calculate the appropriate width.
3302
- * @param {number} zoom - Zoom level
3303
- * @returns {number}
3304
- */
3305
- getLineWidth(zoom) {
3306
- const zooms = Object.keys(this.lineWidthStops).map(Number).sort((a, b2) => a - b2);
3307
- if (this.lineWidthStops[zoom] !== void 0) {
3308
- return this.lineWidthStops[zoom];
3309
- }
3310
- if (zoom < zooms[0]) {
3311
- const z1 = zooms[0];
3312
- const z2 = zooms[1];
3313
- const w1 = this.lineWidthStops[z1];
3314
- const w2 = this.lineWidthStops[z2];
3315
- const slope = (w2 - w1) / (z2 - z1);
3316
- return Math.max(MIN_LINE_WIDTH2, w1 + slope * (zoom - z1));
3317
- }
3318
- if (zoom > zooms[zooms.length - 1]) {
3319
- const z1 = zooms[zooms.length - 2];
3320
- const z2 = zooms[zooms.length - 1];
3321
- const w1 = this.lineWidthStops[z1];
3322
- const w2 = this.lineWidthStops[z2];
3323
- const slope = (w2 - w1) / (z2 - z1);
3324
- return Math.max(MIN_LINE_WIDTH2, w2 + slope * (zoom - z2));
3325
- }
3326
- for (let i2 = 0; i2 < zooms.length - 1; i2++) {
3327
- if (zoom > zooms[i2] && zoom < zooms[i2 + 1]) {
3328
- const z1 = zooms[i2];
3329
- const z2 = zooms[i2 + 1];
3330
- const w1 = this.lineWidthStops[z1];
3331
- const w2 = this.lineWidthStops[z2];
3332
- const t = (zoom - z1) / (z2 - z1);
3333
- return w1 + t * (w2 - w1);
3334
- }
3335
- }
3336
- return DEFAULT_LINE_WIDTH2;
3337
- }
3338
3410
  /**
3339
3411
  * Check if this config matches the given template URLs (with {z}/{x}/{y} placeholders)
3340
3412
  * @param {string | string[]} templates - Single template URL or array of template URLs
@@ -3810,12 +3882,13 @@ var IndiaBoundaryCorrector = (() => {
3810
3882
  const canvas = this._canvas;
3811
3883
  const ctx = canvas.getContext("2d", { willReadFrequently: true });
3812
3884
  ctx.drawImage(imageBitmap, 0, 0, tileSize, tileSize);
3813
- const baseLineWidth = layerConfig.getLineWidth(zoom);
3814
3885
  const delLineWidthBySuffix = {};
3815
3886
  for (const suffix of layerSuffixes) {
3816
3887
  const stylesForSuffix = activeLineStyles.filter((s) => s.layerSuffix === suffix);
3817
- const maxProduct = Math.max(...stylesForSuffix.map((s) => s.widthFraction * s.delWidthFactor));
3818
- delLineWidthBySuffix[suffix] = baseLineWidth * maxProduct;
3888
+ const maxDelWidth = Math.max(...stylesForSuffix.map(
3889
+ (s) => s.getLineWidth(zoom) * s.widthFraction * s.delWidthFactor
3890
+ ));
3891
+ delLineWidthBySuffix[suffix] = maxDelWidth;
3819
3892
  }
3820
3893
  for (const suffix of layerSuffixes) {
3821
3894
  const delLineWidth = delLineWidthBySuffix[suffix];
@@ -3830,7 +3903,7 @@ var IndiaBoundaryCorrector = (() => {
3830
3903
  }
3831
3904
  }
3832
3905
  for (const style of activeLineStyles) {
3833
- const { color, layerSuffix, widthFraction, dashArray, alpha, lineExtensionFactor } = style;
3906
+ const { color, widthFraction, dashArray, alpha, lineExtensionFactor, layerSuffix } = style;
3834
3907
  const addLayerName = `to-add-${layerSuffix}`;
3835
3908
  let addFeatures = corrections[addLayerName] || [];
3836
3909
  if (addFeatures.length > 0) {
@@ -3838,7 +3911,7 @@ var IndiaBoundaryCorrector = (() => {
3838
3911
  if (lineExtensionFactor > 0) {
3839
3912
  addFeatures = extendFeaturesByFactor(addFeatures, lineExtensionFactor, delLineWidth, tileSize);
3840
3913
  }
3841
- const lineWidth = baseLineWidth * widthFraction;
3914
+ const lineWidth = style.getLineWidth(zoom) * widthFraction;
3842
3915
  drawFeatures(ctx, addFeatures, color, lineWidth, tileSize, dashArray, alpha);
3843
3916
  }
3844
3917
  }