@india-boundary-corrector/maplibre-protocol 0.0.1 → 0.0.3

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
@@ -1,7 +1,11 @@
1
1
  # @india-boundary-corrector/maplibre-protocol
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/@india-boundary-corrector/maplibre-protocol)](https://www.npmjs.com/package/@india-boundary-corrector/maplibre-protocol)
4
+
3
5
  MapLibre GL custom protocol for India boundary corrections.
4
6
 
7
+ [Try it on JSFiddle](https://jsfiddle.net/2ym91L65/3/)
8
+
5
9
  ## Installation
6
10
 
7
11
  ```bash
@@ -34,15 +38,16 @@ No bundler required! Just include the script and use the global `IndiaBoundaryCo
34
38
  osm: {
35
39
  type: 'raster',
36
40
  tiles: ['ibc://https://tile.openstreetmap.org/{z}/{x}/{y}.png'],
37
- tileSize: 256
41
+ tileSize: 256,
42
+ attribution: '© OpenStreetMap contributors'
38
43
  }
39
44
  },
40
45
  layers: [
41
46
  { id: 'osm', type: 'raster', source: 'osm' }
42
47
  ]
43
48
  },
44
- center: [78.9629, 20.5937],
45
- zoom: 5
49
+ center: [75.3412, 33.2778],
50
+ zoom: 4
46
51
  });
47
52
  </script>
48
53
  ```
@@ -65,15 +70,16 @@ const map = new maplibregl.Map({
65
70
  osm: {
66
71
  type: 'raster',
67
72
  tiles: ['ibc://https://tile.openstreetmap.org/{z}/{x}/{y}.png'],
68
- tileSize: 256
73
+ tileSize: 256,
74
+ attribution: '© OpenStreetMap contributors'
69
75
  }
70
76
  },
71
77
  layers: [
72
78
  { id: 'osm', type: 'raster', source: 'osm' }
73
79
  ]
74
80
  },
75
- center: [78.9629, 20.5937],
76
- zoom: 5
81
+ center: [75.3412, 33.2778],
82
+ zoom: 4
77
83
  });
78
84
  ```
79
85
 
@@ -124,7 +130,9 @@ const map = new maplibregl.Map({
124
130
  layers: [
125
131
  { id: 'osmde', type: 'raster', source: 'osmde' }
126
132
  ]
127
- }
133
+ },
134
+ center: [75.3412, 33.2778],
135
+ zoom: 4
128
136
  });
129
137
  ```
130
138
 
package/dist/index.cjs CHANGED
@@ -35,30 +35,50 @@ var import_tilefixer = require("@india-boundary-corrector/tilefixer");
35
35
  var import_layer_configs2 = require("@india-boundary-corrector/layer-configs");
36
36
  var import_data2 = require("@india-boundary-corrector/data");
37
37
  var PROTOCOL_PREFIX = "ibc";
38
- function parseCorrectionsUrl(url) {
38
+ function extractTileCoordsFromUrl(url) {
39
+ try {
40
+ const urlObj = new URL(url);
41
+ const pathParts = urlObj.pathname.split("/").filter((p) => p.length > 0);
42
+ for (let i = pathParts.length - 1; i >= 2; i--) {
43
+ const yPart = pathParts[i].replace(/(@\d+x)?\.[^.]+$/, "");
44
+ const xPart = pathParts[i - 1];
45
+ const zPart = pathParts[i - 2];
46
+ if (/^\d+$/.test(zPart) && /^\d+$/.test(xPart) && /^\d+$/.test(yPart)) {
47
+ return {
48
+ z: parseInt(zPart, 10),
49
+ x: parseInt(xPart, 10),
50
+ y: parseInt(yPart, 10)
51
+ };
52
+ }
53
+ }
54
+ } catch {
55
+ }
56
+ return null;
57
+ }
58
+ function parseCorrectionsUrl(url, registry) {
39
59
  const withoutProtocol = url.replace(`${PROTOCOL_PREFIX}://`, "");
40
60
  let configId = null;
41
61
  let tileUrl = withoutProtocol;
42
62
  const atIndex = withoutProtocol.indexOf("@");
43
- if (atIndex > 0 && atIndex < withoutProtocol.indexOf("/")) {
63
+ const slashIndex = withoutProtocol.indexOf("/");
64
+ if (atIndex > 0 && (slashIndex === -1 || atIndex < slashIndex)) {
44
65
  configId = withoutProtocol.substring(0, atIndex);
45
66
  tileUrl = withoutProtocol.substring(atIndex + 1);
46
67
  }
47
- const urlObj = new URL(tileUrl);
48
- const pathParts = urlObj.pathname.split("/").filter((p) => p.length > 0);
49
- let z, x, y;
50
- for (let i = pathParts.length - 1; i >= 2; i--) {
51
- const yPart = pathParts[i].replace(/\.[^.]+$/, "");
52
- const xPart = pathParts[i - 1];
53
- const zPart = pathParts[i - 2];
54
- if (/^\d+$/.test(zPart) && /^\d+$/.test(xPart) && /^\d+$/.test(yPart)) {
55
- z = parseInt(zPart, 10);
56
- x = parseInt(xPart, 10);
57
- y = parseInt(yPart, 10);
58
- break;
59
- }
68
+ let coords = null;
69
+ if (configId) {
70
+ coords = extractTileCoordsFromUrl(tileUrl);
71
+ } else {
72
+ const parsed = registry.parseTileUrl(tileUrl);
73
+ coords = parsed?.coords ?? null;
60
74
  }
61
- return { configId, tileUrl, z, x, y };
75
+ return {
76
+ configId,
77
+ tileUrl,
78
+ z: coords?.z,
79
+ x: coords?.x,
80
+ y: coords?.y
81
+ };
62
82
  }
63
83
  async function fetchAndFixTile(tileUrl, z, x, y, tileFixer, layerConfig, tileSize, options = {}) {
64
84
  const { data, correctionsFailed, correctionsError } = await tileFixer.fetchAndFixTile(
@@ -166,7 +186,7 @@ var CorrectionProtocol = class {
166
186
  _createLoadFunction() {
167
187
  const self = this;
168
188
  return async (params, abortController) => {
169
- const { configId, tileUrl, z, x, y } = parseCorrectionsUrl(params.url);
189
+ const { configId, tileUrl, z, x, y } = parseCorrectionsUrl(params.url, self._registry);
170
190
  if (z === void 0 || x === void 0 || y === void 0) {
171
191
  console.warn(`[CorrectionProtocol] Could not parse tile coordinates from URL: ${params.url}, falling back to original`);
172
192
  const response = await fetch(tileUrl, { signal: abortController?.signal });
@@ -189,16 +209,16 @@ var CorrectionProtocol = class {
189
209
  self._tileSize,
190
210
  { signal: abortController?.signal }
191
211
  );
192
- if (result.correctionsFailed) {
212
+ if (result.correctionsFailed && result.correctionsError?.name !== "AbortError") {
193
213
  console.warn("[CorrectionProtocol] Corrections fetch failed:", result.correctionsError);
194
214
  self._emit("correctionerror", { error: result.correctionsError, coords: { z, x, y }, tileUrl });
195
215
  }
196
216
  return { data: result.data };
197
217
  } catch (err) {
198
- console.warn("[CorrectionProtocol] Error applying corrections, falling back to original:", err);
199
- self._emit("correctionerror", { error: err, coords: { z, x, y }, tileUrl });
200
- const response = await fetch(tileUrl, { signal: abortController?.signal });
201
- return { data: await response.arrayBuffer() };
218
+ if (err.name === "AbortError") {
219
+ throw err;
220
+ }
221
+ throw err;
202
222
  }
203
223
  };
204
224
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.js"],"sourcesContent":["import { getPmtilesUrl } from '@india-boundary-corrector/data';\nimport { layerConfigs } from '@india-boundary-corrector/layer-configs';\nimport { BoundaryCorrector as TileFixer } from '@india-boundary-corrector/tilefixer';\n\n// Re-export for convenience\nexport { layerConfigs, LayerConfig } from '@india-boundary-corrector/layer-configs';\nexport { getPmtilesUrl } from '@india-boundary-corrector/data';\n\nconst PROTOCOL_PREFIX = 'ibc';\n\n/**\n * Parse an ibc:// URL.\n * Format: ibc://[configId@]originalUrl\n * Examples:\n * ibc://https://tile.openstreetmap.org/{z}/{x}/{y}.png\n * ibc://osm-carto@https://tile.openstreetmap.org/{z}/{x}/{y}.png\n * \n * @param {string} url - The full URL with ibc:// prefix\n * @returns {{ configId: string|null, tileUrl: string, z: number, x: number, y: number }}\n */\nfunction parseCorrectionsUrl(url) {\n // Remove protocol prefix\n const withoutProtocol = url.replace(`${PROTOCOL_PREFIX}://`, '');\n \n // Check for configId@url format\n let configId = null;\n let tileUrl = withoutProtocol;\n \n const atIndex = withoutProtocol.indexOf('@');\n if (atIndex > 0 && atIndex < withoutProtocol.indexOf('/')) {\n // Has configId prefix\n configId = withoutProtocol.substring(0, atIndex);\n tileUrl = withoutProtocol.substring(atIndex + 1);\n }\n \n // Extract z, x, y from the URL (assuming standard {z}/{x}/{y} pattern in the path)\n // The URL has already been templated by MapLibre, so we need to parse actual numbers\n const urlObj = new URL(tileUrl);\n const pathParts = urlObj.pathname.split('/').filter(p => p.length > 0);\n \n // Find z/x/y pattern - typically last 3 numeric segments\n let z, x, y;\n for (let i = pathParts.length - 1; i >= 2; i--) {\n const yPart = pathParts[i].replace(/\\.[^.]+$/, ''); // Remove extension\n const xPart = pathParts[i - 1];\n const zPart = pathParts[i - 2];\n \n if (/^\\d+$/.test(zPart) && /^\\d+$/.test(xPart) && /^\\d+$/.test(yPart)) {\n z = parseInt(zPart, 10);\n x = parseInt(xPart, 10);\n y = parseInt(yPart, 10);\n break;\n }\n }\n \n return { configId, tileUrl, z, x, y };\n}\n\n/**\n * Fetch and fix a tile for MapLibre protocol.\n * Extracted for testability.\n * @param {string} tileUrl - URL of the raster tile\n * @param {number} z - Zoom level\n * @param {number} x - Tile X coordinate\n * @param {number} y - Tile Y coordinate\n * @param {TileFixer} tileFixer - TileFixer instance\n * @param {Object} layerConfig - Layer configuration (can be null)\n * @param {number} tileSize - Tile size in pixels\n * @param {Object} [options] - Fetch options\n * @param {AbortSignal} [options.signal] - Abort signal\n * @returns {Promise<{data: ArrayBuffer, correctionsFailed: boolean, correctionsError: Error|null}>}\n */\nasync function fetchAndFixTile(tileUrl, z, x, y, tileFixer, layerConfig, tileSize, options = {}) {\n const { data, correctionsFailed, correctionsError } = await tileFixer.fetchAndFixTile(\n tileUrl, z, x, y, layerConfig, { tileSize, signal: options.signal }\n );\n return { data, correctionsFailed, correctionsError };\n}\n\n/**\n * India boundary corrections protocol for MapLibre GL.\n * \n * Usage:\n * const protocol = new CorrectionProtocol();\n * protocol.register(maplibregl);\n * \n * // In your style:\n * tiles: ['ibc://https://tile.openstreetmap.org/{z}/{x}/{y}.png']\n * // Or with explicit config:\n * tiles: ['ibc://osm-carto@https://tile.openstreetmap.org/{z}/{x}/{y}.png']\n */\nexport class CorrectionProtocol {\n /**\n * @param {Object} [options]\n * @param {string} [options.pmtilesUrl] - URL to PMTiles file (defaults to CDN)\n * @param {number} [options.tileSize=256] - Tile size in pixels\n */\n constructor(options = {}) {\n this._pmtilesUrl = options.pmtilesUrl ?? getPmtilesUrl();\n this._tileSize = options.tileSize ?? 256;\n this._tileFixer = new TileFixer(this._pmtilesUrl);\n this._registry = layerConfigs.createMergedRegistry();\n /** @type {Map<string, Set<Function>>} */\n this._listeners = new Map();\n \n this._loadFn = this._createLoadFunction();\n }\n\n /**\n * Add a listener for an event.\n * @param {'correctionerror'} event - Event name\n * @param {Function} listener - Callback function receiving event data\n * @returns {this}\n */\n on(event, listener) {\n if (!this._listeners.has(event)) {\n this._listeners.set(event, new Set());\n }\n this._listeners.get(event).add(listener);\n return this;\n }\n\n /**\n * Remove an event listener.\n * @param {'correctionerror'} event - Event name\n * @param {Function} listener - Callback to remove\n * @returns {this}\n */\n off(event, listener) {\n this._listeners.get(event)?.delete(listener);\n return this;\n }\n\n /**\n * Emit an event to all listeners.\n * @param {string} event - Event name\n * @param {Object} data - Event data\n * @private\n */\n _emit(event, data) {\n this._listeners.get(event)?.forEach(fn => fn(data));\n }\n\n /**\n * Add a custom layer config to the registry.\n * @param {Object} layerConfig - LayerConfig to add\n * @returns {this}\n */\n addLayerConfig(layerConfig) {\n this._registry.register(layerConfig);\n return this;\n }\n\n /**\n * Get the registry.\n * @returns {LayerConfigRegistry}\n */\n getRegistry() {\n return this._registry;\n }\n\n /**\n * Get the TileFixer instance.\n * @returns {TileFixer}\n */\n getTileFixer() {\n return this._tileFixer;\n }\n\n /**\n * Register the protocol with MapLibre GL.\n * @param {typeof import('maplibre-gl')} maplibregl - MapLibre GL namespace\n * @returns {this}\n */\n register(maplibregl) {\n maplibregl.addProtocol(PROTOCOL_PREFIX, this._loadFn);\n return this;\n }\n\n /**\n * Unregister the protocol from MapLibre GL.\n * @param {typeof import('maplibre-gl')} maplibregl - MapLibre GL namespace\n * @returns {this}\n */\n unregister(maplibregl) {\n maplibregl.removeProtocol(PROTOCOL_PREFIX);\n return this;\n }\n\n /**\n * Create the protocol load function.\n * @returns {Function}\n * @private\n */\n _createLoadFunction() {\n const self = this;\n \n return async (params, abortController) => {\n const { configId, tileUrl, z, x, y } = parseCorrectionsUrl(params.url);\n \n // Validate parsed coordinates\n if (z === undefined || x === undefined || y === undefined) {\n console.warn(`[CorrectionProtocol] Could not parse tile coordinates from URL: ${params.url}, falling back to original`);\n const response = await fetch(tileUrl, { signal: abortController?.signal });\n return { data: await response.arrayBuffer() };\n }\n \n // Resolve layer config\n let layerConfig;\n if (configId) {\n layerConfig = self._registry.get(configId);\n } else {\n layerConfig = self._registry.detectFromTileUrls([tileUrl]);\n }\n \n try {\n const result = await fetchAndFixTile(\n tileUrl,\n z, \n x,\n y,\n self._tileFixer,\n layerConfig,\n self._tileSize,\n { signal: abortController?.signal }\n );\n \n if (result.correctionsFailed) {\n console.warn('[CorrectionProtocol] Corrections fetch failed:', result.correctionsError);\n self._emit('correctionerror', { error: result.correctionsError, coords: { z, x, y }, tileUrl });\n }\n \n return { data: result.data };\n } catch (err) {\n console.warn('[CorrectionProtocol] Error applying corrections, falling back to original:', err);\n self._emit('correctionerror', { error: err, coords: { z, x, y }, tileUrl });\n const response = await fetch(tileUrl, { signal: abortController?.signal });\n return { data: await response.arrayBuffer() };\n }\n };\n }\n}\n\n/**\n * Create and register a correction protocol with MapLibre GL.\n * \n * @param {typeof import('maplibre-gl')} maplibregl - MapLibre GL namespace\n * @param {Object} [options] - Protocol options\n * @param {string} [options.pmtilesUrl] - URL to PMTiles file\n * @param {number} [options.tileSize=256] - Tile size in pixels\n * @returns {CorrectionProtocol}\n * \n * @example\n * import maplibregl from 'maplibre-gl';\n * import { registerCorrectionProtocol } from '@india-boundary-corrector/maplibre-protocol';\n * \n * const protocol = registerCorrectionProtocol(maplibregl);\n * \n * // Use in style:\n * const map = new maplibregl.Map({\n * container: 'map',\n * style: {\n * sources: {\n * osm: {\n * type: 'raster',\n * tiles: ['ibc://https://tile.openstreetmap.org/{z}/{x}/{y}.png'],\n * tileSize: 256\n * }\n * },\n * layers: [{ id: 'osm', type: 'raster', source: 'osm' }]\n * }\n * });\n */\nexport function registerCorrectionProtocol(maplibregl, options = {}) {\n const protocol = new CorrectionProtocol(options);\n return protocol.register(maplibregl);\n}\n\n// Export for testing\nexport { parseCorrectionsUrl, fetchAndFixTile };\n"],"mappings":"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","names":["import_layer_configs","import_data","TileFixer"]}
1
+ {"version":3,"sources":["../src/index.js"],"sourcesContent":["import { getPmtilesUrl } from '@india-boundary-corrector/data';\nimport { layerConfigs } from '@india-boundary-corrector/layer-configs';\nimport { BoundaryCorrector as TileFixer } from '@india-boundary-corrector/tilefixer';\n\n// Re-export for convenience\nexport { layerConfigs, LayerConfig } from '@india-boundary-corrector/layer-configs';\nexport { getPmtilesUrl } from '@india-boundary-corrector/data';\n\nconst PROTOCOL_PREFIX = 'ibc';\n\n/**\n * Extract tile coordinates from a URL using generic z/x/y pattern matching.\n * Handles standard tile URL patterns including retina suffixes (@2x, @3x, etc.).\n * \n * @param {string} url - The tile URL to parse\n * @returns {{ z: number, x: number, y: number } | null} Parsed coordinates or null if not found\n */\nfunction extractTileCoordsFromUrl(url) {\n try {\n const urlObj = new URL(url);\n const pathParts = urlObj.pathname.split('/').filter(p => p.length > 0);\n \n // Find z/x/y pattern - typically last 3 numeric segments\n for (let i = pathParts.length - 1; i >= 2; i--) {\n // Remove extension and retina suffix (e.g., \"5@2x.png\" -> \"5\")\n const yPart = pathParts[i].replace(/(@\\d+x)?\\.[^.]+$/, '');\n const xPart = pathParts[i - 1];\n const zPart = pathParts[i - 2];\n \n if (/^\\d+$/.test(zPart) && /^\\d+$/.test(xPart) && /^\\d+$/.test(yPart)) {\n return {\n z: parseInt(zPart, 10),\n x: parseInt(xPart, 10),\n y: parseInt(yPart, 10)\n };\n }\n }\n } catch {\n // Invalid URL\n }\n return null;\n}\n\n/**\n * Parse an ibc:// URL.\n * Format: ibc://[configId@]originalUrl\n * Examples:\n * ibc://https://tile.openstreetmap.org/{z}/{x}/{y}.png\n * ibc://osm-carto@https://tile.openstreetmap.org/{z}/{x}/{y}.png\n * \n * @param {string} url - The full URL with ibc:// prefix\n * @param {import('@india-boundary-corrector/layer-configs').LayerConfigRegistry} registry - Registry to use for parsing\n * @returns {{ configId: string|null, tileUrl: string, z: number|undefined, x: number|undefined, y: number|undefined }}\n */\nfunction parseCorrectionsUrl(url, registry) {\n // Remove protocol prefix\n const withoutProtocol = url.replace(`${PROTOCOL_PREFIX}://`, '');\n \n // Check for configId@url format\n let configId = null;\n let tileUrl = withoutProtocol;\n \n const atIndex = withoutProtocol.indexOf('@');\n const slashIndex = withoutProtocol.indexOf('/');\n // Config ID exists if @ comes before first / (or if there's no /)\n if (atIndex > 0 && (slashIndex === -1 || atIndex < slashIndex)) {\n // Has configId prefix\n configId = withoutProtocol.substring(0, atIndex);\n tileUrl = withoutProtocol.substring(atIndex + 1);\n }\n \n // If configId is explicit, use generic parsing (URL may not match config's patterns)\n // Otherwise, use registry detection only (unregistered URLs won't get corrections)\n let coords = null;\n if (configId) {\n coords = extractTileCoordsFromUrl(tileUrl);\n } else {\n const parsed = registry.parseTileUrl(tileUrl);\n coords = parsed?.coords ?? null;\n }\n \n return { \n configId, \n tileUrl, \n z: coords?.z, \n x: coords?.x, \n y: coords?.y \n };\n}\n\n/**\n * Fetch and fix a tile for MapLibre protocol.\n * Extracted for testability.\n * @param {string} tileUrl - URL of the raster tile\n * @param {number} z - Zoom level\n * @param {number} x - Tile X coordinate\n * @param {number} y - Tile Y coordinate\n * @param {TileFixer} tileFixer - TileFixer instance\n * @param {Object} layerConfig - Layer configuration (can be null)\n * @param {number} tileSize - Tile size in pixels\n * @param {Object} [options] - Fetch options\n * @param {AbortSignal} [options.signal] - Abort signal\n * @returns {Promise<{data: ArrayBuffer, correctionsFailed: boolean, correctionsError: Error|null}>}\n */\nasync function fetchAndFixTile(tileUrl, z, x, y, tileFixer, layerConfig, tileSize, options = {}) {\n const { data, correctionsFailed, correctionsError } = await tileFixer.fetchAndFixTile(\n tileUrl, z, x, y, layerConfig, { tileSize, signal: options.signal }\n );\n return { data, correctionsFailed, correctionsError };\n}\n\n/**\n * India boundary corrections protocol for MapLibre GL.\n * \n * Usage:\n * const protocol = new CorrectionProtocol();\n * protocol.register(maplibregl);\n * \n * // In your style:\n * tiles: ['ibc://https://tile.openstreetmap.org/{z}/{x}/{y}.png']\n * // Or with explicit config:\n * tiles: ['ibc://osm-carto@https://tile.openstreetmap.org/{z}/{x}/{y}.png']\n */\nexport class CorrectionProtocol {\n /**\n * @param {Object} [options]\n * @param {string} [options.pmtilesUrl] - URL to PMTiles file (defaults to CDN)\n * @param {number} [options.tileSize=256] - Tile size in pixels\n */\n constructor(options = {}) {\n this._pmtilesUrl = options.pmtilesUrl ?? getPmtilesUrl();\n this._tileSize = options.tileSize ?? 256;\n this._tileFixer = new TileFixer(this._pmtilesUrl);\n this._registry = layerConfigs.createMergedRegistry();\n /** @type {Map<string, Set<Function>>} */\n this._listeners = new Map();\n \n this._loadFn = this._createLoadFunction();\n }\n\n /**\n * Add a listener for an event.\n * @param {'correctionerror'} event - Event name\n * @param {Function} listener - Callback function receiving event data\n * @returns {this}\n */\n on(event, listener) {\n if (!this._listeners.has(event)) {\n this._listeners.set(event, new Set());\n }\n this._listeners.get(event).add(listener);\n return this;\n }\n\n /**\n * Remove an event listener.\n * @param {'correctionerror'} event - Event name\n * @param {Function} listener - Callback to remove\n * @returns {this}\n */\n off(event, listener) {\n this._listeners.get(event)?.delete(listener);\n return this;\n }\n\n /**\n * Emit an event to all listeners.\n * @param {string} event - Event name\n * @param {Object} data - Event data\n * @private\n */\n _emit(event, data) {\n this._listeners.get(event)?.forEach(fn => fn(data));\n }\n\n /**\n * Add a custom layer config to the registry.\n * @param {Object} layerConfig - LayerConfig to add\n * @returns {this}\n */\n addLayerConfig(layerConfig) {\n this._registry.register(layerConfig);\n return this;\n }\n\n /**\n * Get the registry.\n * @returns {LayerConfigRegistry}\n */\n getRegistry() {\n return this._registry;\n }\n\n /**\n * Get the TileFixer instance.\n * @returns {TileFixer}\n */\n getTileFixer() {\n return this._tileFixer;\n }\n\n /**\n * Register the protocol with MapLibre GL.\n * @param {typeof import('maplibre-gl')} maplibregl - MapLibre GL namespace\n * @returns {this}\n */\n register(maplibregl) {\n maplibregl.addProtocol(PROTOCOL_PREFIX, this._loadFn);\n return this;\n }\n\n /**\n * Unregister the protocol from MapLibre GL.\n * @param {typeof import('maplibre-gl')} maplibregl - MapLibre GL namespace\n * @returns {this}\n */\n unregister(maplibregl) {\n maplibregl.removeProtocol(PROTOCOL_PREFIX);\n return this;\n }\n\n /**\n * Create the protocol load function.\n * @returns {Function}\n * @private\n */\n _createLoadFunction() {\n const self = this;\n \n return async (params, abortController) => {\n const { configId, tileUrl, z, x, y } = parseCorrectionsUrl(params.url, self._registry);\n \n // Validate parsed coordinates\n if (z === undefined || x === undefined || y === undefined) {\n console.warn(`[CorrectionProtocol] Could not parse tile coordinates from URL: ${params.url}, falling back to original`);\n const response = await fetch(tileUrl, { signal: abortController?.signal });\n return { data: await response.arrayBuffer() };\n }\n \n // Resolve layer config\n let layerConfig;\n if (configId) {\n layerConfig = self._registry.get(configId);\n } else {\n layerConfig = self._registry.detectFromTileUrls([tileUrl]);\n }\n \n try {\n const result = await fetchAndFixTile(\n tileUrl,\n z, \n x,\n y,\n self._tileFixer,\n layerConfig,\n self._tileSize,\n { signal: abortController?.signal }\n );\n \n if (result.correctionsFailed && result.correctionsError?.name !== 'AbortError') {\n console.warn('[CorrectionProtocol] Corrections fetch failed:', result.correctionsError);\n self._emit('correctionerror', { error: result.correctionsError, coords: { z, x, y }, tileUrl });\n }\n \n return { data: result.data };\n } catch (err) {\n // Don't catch AbortError - let it propagate\n if (err.name === 'AbortError') {\n throw err;\n }\n // Re-throw other errors (tile fetch failures, processing errors)\n // correctionerror is only for PMTiles/correction failures (handled via correctionsFailed)\n throw err;\n }\n };\n }\n}\n\n/**\n * Create and register a correction protocol with MapLibre GL.\n * \n * @param {typeof import('maplibre-gl')} maplibregl - MapLibre GL namespace\n * @param {Object} [options] - Protocol options\n * @param {string} [options.pmtilesUrl] - URL to PMTiles file\n * @param {number} [options.tileSize=256] - Tile size in pixels\n * @returns {CorrectionProtocol}\n * \n * @example\n * import maplibregl from 'maplibre-gl';\n * import { registerCorrectionProtocol } from '@india-boundary-corrector/maplibre-protocol';\n * \n * const protocol = registerCorrectionProtocol(maplibregl);\n * \n * // Use in style:\n * const map = new maplibregl.Map({\n * container: 'map',\n * style: {\n * sources: {\n * osm: {\n * type: 'raster',\n * tiles: ['ibc://https://tile.openstreetmap.org/{z}/{x}/{y}.png'],\n * tileSize: 256\n * }\n * },\n * layers: [{ id: 'osm', type: 'raster', source: 'osm' }]\n * }\n * });\n */\nexport function registerCorrectionProtocol(maplibregl, options = {}) {\n const protocol = new CorrectionProtocol(options);\n return protocol.register(maplibregl);\n}\n\n// Export for testing\nexport { parseCorrectionsUrl, fetchAndFixTile };\n"],"mappings":"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","names":["import_layer_configs","import_data","TileFixer"]}
@@ -31,17 +31,23 @@ var IndiaBoundaryCorrector = (() => {
31
31
  });
32
32
 
33
33
  // ../data/version.js
34
- var packageVersion = "0.0.1";
34
+ var packageVersion = "0.0.3";
35
35
 
36
36
  // ../data/index.js
37
37
  var import_meta = {};
38
38
  var PACKAGE_NAME = "@india-boundary-corrector/data";
39
39
  var PMTILES_FILENAME = "india_boundary_corrections.pmtiles";
40
40
  var DEFAULT_CDN_URL = `https://unpkg.com/${PACKAGE_NAME}@${packageVersion}/${PMTILES_FILENAME}`;
41
+ function shouldUseCdnFallback(hostname) {
42
+ return hostname === "esm.sh";
43
+ }
41
44
  function detectPmtilesUrl() {
42
45
  try {
43
46
  if (typeof import_meta !== "undefined" && import_meta.url) {
44
47
  const moduleUrl = new URL(".", import_meta.url);
48
+ if (shouldUseCdnFallback(moduleUrl.hostname)) {
49
+ return DEFAULT_CDN_URL;
50
+ }
45
51
  return new URL(PMTILES_FILENAME, moduleUrl).href;
46
52
  }
47
53
  } catch {
@@ -167,10 +173,10 @@ var IndiaBoundaryCorrector = (() => {
167
173
  let pattern = template.replace(/[.*+?^${}()|[\]\\]/g, (char) => {
168
174
  if (char === "{" || char === "}") return char;
169
175
  return "\\" + char;
170
- }).replace(/^https:\/\//, "https?://").replace(/^http:\/\//, "https?://").replace(/\{([a-z0-9])-([a-z0-9])\}/gi, (_2, start, end) => `(\\{${start}-${end}\\}|[a-z0-9]+)`).replace(/\{(z|x|y|s|r)\}/gi, (_2, name) => {
176
+ }).replace(/^https:\/\//, "https?://").replace(/^http:\/\//, "https?://").replace(/\{([a-z0-9])-([a-z0-9])\}/gi, (_2, start, end) => `(\\{${start}-${end}\\}|\\{s\\}|[a-z0-9]+)`).replace(/\{(z|x|y|s|r)\}/gi, (_2, name) => {
171
177
  const lowerName = name.toLowerCase();
172
178
  if (lowerName === "s") {
173
- return "(\\{s\\}|[a-z0-9]+)";
179
+ return "(\\{s\\}|\\{[a-z0-9]-[a-z0-9]\\}|[a-z0-9]+)";
174
180
  }
175
181
  if (lowerName === "r") {
176
182
  return "(\\{r\\}|@\\d+x)?";
@@ -204,6 +210,9 @@ var IndiaBoundaryCorrector = (() => {
204
210
  if (!id || typeof id !== "string") {
205
211
  throw new Error("LayerConfig requires a non-empty string id");
206
212
  }
213
+ if (id.includes("/")) {
214
+ throw new Error(`LayerConfig id cannot contain slashes: "${id}"`);
215
+ }
207
216
  this.id = id;
208
217
  this.startZoom = startZoom;
209
218
  this.zoomThreshold = zoomThreshold;
@@ -2705,7 +2714,7 @@ var IndiaBoundaryCorrector = (() => {
2705
2714
  if (!maskCanvas || maskCanvas.width !== tileSize || maskCanvas.height !== tileSize) {
2706
2715
  maskCanvas = new OffscreenCanvas(tileSize, tileSize);
2707
2716
  }
2708
- const maskCtx = maskCanvas.getContext("2d");
2717
+ const maskCtx = maskCanvas.getContext("2d", { willReadFrequently: true });
2709
2718
  maskCtx.fillStyle = "black";
2710
2719
  maskCtx.fillRect(0, 0, tileSize, tileSize);
2711
2720
  maskCtx.strokeStyle = "white";
@@ -2907,8 +2916,11 @@ var IndiaBoundaryCorrector = (() => {
2907
2916
  const useOsm = zoom >= zoomThreshold;
2908
2917
  const addLayerName = useOsm ? "to-add-osm" : "to-add-ne";
2909
2918
  const delLayerName = useOsm ? "to-del-osm" : "to-del-ne";
2910
- const canvas = new OffscreenCanvas(tileSize, tileSize);
2911
- const ctx = canvas.getContext("2d");
2919
+ if (!this._canvas || this._canvas.width !== tileSize) {
2920
+ this._canvas = new OffscreenCanvas(tileSize, tileSize);
2921
+ }
2922
+ const canvas = this._canvas;
2923
+ const ctx = canvas.getContext("2d", { willReadFrequently: true });
2912
2924
  const blob = new Blob([rasterTile]);
2913
2925
  const imageBitmap = await createImageBitmap(blob);
2914
2926
  ctx.drawImage(imageBitmap, 0, 0, tileSize, tileSize);
@@ -2990,30 +3002,50 @@ var IndiaBoundaryCorrector = (() => {
2990
3002
 
2991
3003
  // src/index.js
2992
3004
  var PROTOCOL_PREFIX = "ibc";
2993
- function parseCorrectionsUrl(url) {
3005
+ function extractTileCoordsFromUrl(url) {
3006
+ try {
3007
+ const urlObj = new URL(url);
3008
+ const pathParts = urlObj.pathname.split("/").filter((p) => p.length > 0);
3009
+ for (let i2 = pathParts.length - 1; i2 >= 2; i2--) {
3010
+ const yPart = pathParts[i2].replace(/(@\d+x)?\.[^.]+$/, "");
3011
+ const xPart = pathParts[i2 - 1];
3012
+ const zPart = pathParts[i2 - 2];
3013
+ if (/^\d+$/.test(zPart) && /^\d+$/.test(xPart) && /^\d+$/.test(yPart)) {
3014
+ return {
3015
+ z: parseInt(zPart, 10),
3016
+ x: parseInt(xPart, 10),
3017
+ y: parseInt(yPart, 10)
3018
+ };
3019
+ }
3020
+ }
3021
+ } catch {
3022
+ }
3023
+ return null;
3024
+ }
3025
+ function parseCorrectionsUrl(url, registry) {
2994
3026
  const withoutProtocol = url.replace(`${PROTOCOL_PREFIX}://`, "");
2995
3027
  let configId = null;
2996
3028
  let tileUrl = withoutProtocol;
2997
3029
  const atIndex = withoutProtocol.indexOf("@");
2998
- if (atIndex > 0 && atIndex < withoutProtocol.indexOf("/")) {
3030
+ const slashIndex = withoutProtocol.indexOf("/");
3031
+ if (atIndex > 0 && (slashIndex === -1 || atIndex < slashIndex)) {
2999
3032
  configId = withoutProtocol.substring(0, atIndex);
3000
3033
  tileUrl = withoutProtocol.substring(atIndex + 1);
3001
3034
  }
3002
- const urlObj = new URL(tileUrl);
3003
- const pathParts = urlObj.pathname.split("/").filter((p) => p.length > 0);
3004
- let z2, x3, y;
3005
- for (let i2 = pathParts.length - 1; i2 >= 2; i2--) {
3006
- const yPart = pathParts[i2].replace(/\.[^.]+$/, "");
3007
- const xPart = pathParts[i2 - 1];
3008
- const zPart = pathParts[i2 - 2];
3009
- if (/^\d+$/.test(zPart) && /^\d+$/.test(xPart) && /^\d+$/.test(yPart)) {
3010
- z2 = parseInt(zPart, 10);
3011
- x3 = parseInt(xPart, 10);
3012
- y = parseInt(yPart, 10);
3013
- break;
3014
- }
3035
+ let coords = null;
3036
+ if (configId) {
3037
+ coords = extractTileCoordsFromUrl(tileUrl);
3038
+ } else {
3039
+ const parsed = registry.parseTileUrl(tileUrl);
3040
+ coords = parsed?.coords ?? null;
3015
3041
  }
3016
- return { configId, tileUrl, z: z2, x: x3, y };
3042
+ return {
3043
+ configId,
3044
+ tileUrl,
3045
+ z: coords?.z,
3046
+ x: coords?.x,
3047
+ y: coords?.y
3048
+ };
3017
3049
  }
3018
3050
  async function fetchAndFixTile(tileUrl, z2, x3, y, tileFixer, layerConfig, tileSize, options = {}) {
3019
3051
  const { data, correctionsFailed, correctionsError } = await tileFixer.fetchAndFixTile(
@@ -3121,7 +3153,7 @@ var IndiaBoundaryCorrector = (() => {
3121
3153
  _createLoadFunction() {
3122
3154
  const self = this;
3123
3155
  return async (params, abortController) => {
3124
- const { configId, tileUrl, z: z2, x: x3, y } = parseCorrectionsUrl(params.url);
3156
+ const { configId, tileUrl, z: z2, x: x3, y } = parseCorrectionsUrl(params.url, self._registry);
3125
3157
  if (z2 === void 0 || x3 === void 0 || y === void 0) {
3126
3158
  console.warn(`[CorrectionProtocol] Could not parse tile coordinates from URL: ${params.url}, falling back to original`);
3127
3159
  const response = await fetch(tileUrl, { signal: abortController?.signal });
@@ -3144,16 +3176,16 @@ var IndiaBoundaryCorrector = (() => {
3144
3176
  self._tileSize,
3145
3177
  { signal: abortController?.signal }
3146
3178
  );
3147
- if (result.correctionsFailed) {
3179
+ if (result.correctionsFailed && result.correctionsError?.name !== "AbortError") {
3148
3180
  console.warn("[CorrectionProtocol] Corrections fetch failed:", result.correctionsError);
3149
3181
  self._emit("correctionerror", { error: result.correctionsError, coords: { z: z2, x: x3, y }, tileUrl });
3150
3182
  }
3151
3183
  return { data: result.data };
3152
3184
  } catch (err2) {
3153
- console.warn("[CorrectionProtocol] Error applying corrections, falling back to original:", err2);
3154
- self._emit("correctionerror", { error: err2, coords: { z: z2, x: x3, y }, tileUrl });
3155
- const response = await fetch(tileUrl, { signal: abortController?.signal });
3156
- return { data: await response.arrayBuffer() };
3185
+ if (err2.name === "AbortError") {
3186
+ throw err2;
3187
+ }
3188
+ throw err2;
3157
3189
  }
3158
3190
  };
3159
3191
  }