@india-boundary-corrector/maplibre-protocol 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/README.md +200 -0
- package/dist/index.cjs +210 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.global.js +3167 -0
- package/dist/index.global.js.map +1 -0
- package/dist/index.js +189 -0
- package/dist/index.js.map +1 -0
- package/package.json +54 -0
- package/src/index.d.ts +64 -0
- package/src/index.js +280 -0
package/README.md
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# @india-boundary-corrector/maplibre-protocol
|
|
2
|
+
|
|
3
|
+
MapLibre GL custom protocol for India boundary corrections.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @india-boundary-corrector/maplibre-protocol maplibre-gl
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### Script Tag (IIFE) - Simplest Setup
|
|
14
|
+
|
|
15
|
+
No bundler required! Just include the script and use the global `IndiaBoundaryCorrector`:
|
|
16
|
+
|
|
17
|
+
```html
|
|
18
|
+
<link rel="stylesheet" href="https://unpkg.com/maplibre-gl@5.0.1/dist/maplibre-gl.css" />
|
|
19
|
+
<script src="https://unpkg.com/maplibre-gl@5.0.1/dist/maplibre-gl.js"></script>
|
|
20
|
+
<script src="https://unpkg.com/@india-boundary-corrector/maplibre-protocol/dist/index.global.js"></script>
|
|
21
|
+
|
|
22
|
+
<div id="map" style="height: 400px;"></div>
|
|
23
|
+
|
|
24
|
+
<script>
|
|
25
|
+
// Register the ibc:// protocol
|
|
26
|
+
IndiaBoundaryCorrector.registerCorrectionProtocol(maplibregl);
|
|
27
|
+
|
|
28
|
+
// Use in map style
|
|
29
|
+
const map = new maplibregl.Map({
|
|
30
|
+
container: 'map',
|
|
31
|
+
style: {
|
|
32
|
+
version: 8,
|
|
33
|
+
sources: {
|
|
34
|
+
osm: {
|
|
35
|
+
type: 'raster',
|
|
36
|
+
tiles: ['ibc://https://tile.openstreetmap.org/{z}/{x}/{y}.png'],
|
|
37
|
+
tileSize: 256
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
layers: [
|
|
41
|
+
{ id: 'osm', type: 'raster', source: 'osm' }
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
center: [78.9629, 20.5937],
|
|
45
|
+
zoom: 5
|
|
46
|
+
});
|
|
47
|
+
</script>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### ES Modules
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
import maplibregl from 'maplibre-gl';
|
|
54
|
+
import { registerCorrectionProtocol } from '@india-boundary-corrector/maplibre-protocol';
|
|
55
|
+
|
|
56
|
+
// Register the ibc:// protocol
|
|
57
|
+
registerCorrectionProtocol(maplibregl);
|
|
58
|
+
|
|
59
|
+
// Use in map style
|
|
60
|
+
const map = new maplibregl.Map({
|
|
61
|
+
container: 'map',
|
|
62
|
+
style: {
|
|
63
|
+
version: 8,
|
|
64
|
+
sources: {
|
|
65
|
+
osm: {
|
|
66
|
+
type: 'raster',
|
|
67
|
+
tiles: ['ibc://https://tile.openstreetmap.org/{z}/{x}/{y}.png'],
|
|
68
|
+
tileSize: 256
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
layers: [
|
|
72
|
+
{ id: 'osm', type: 'raster', source: 'osm' }
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
center: [78.9629, 20.5937],
|
|
76
|
+
zoom: 5
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### With Explicit Config ID
|
|
81
|
+
|
|
82
|
+
Specify the config ID in the URL:
|
|
83
|
+
|
|
84
|
+
```javascript
|
|
85
|
+
tiles: ['ibc://cartodb-dark@https://{a-c}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png']
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### With Custom Layer Config
|
|
89
|
+
|
|
90
|
+
```javascript
|
|
91
|
+
import maplibregl from 'maplibre-gl';
|
|
92
|
+
import { CorrectionProtocol, LayerConfig } from '@india-boundary-corrector/maplibre-protocol';
|
|
93
|
+
|
|
94
|
+
// Create protocol with custom config
|
|
95
|
+
const protocol = new CorrectionProtocol();
|
|
96
|
+
|
|
97
|
+
// Add custom config
|
|
98
|
+
protocol.addLayerConfig(new LayerConfig({
|
|
99
|
+
id: 'osm-de',
|
|
100
|
+
tileUrlTemplates: ['https://tile.openstreetmap.de/{z}/{x}/{y}.png'],
|
|
101
|
+
lineWidthStops: { 1: 0.5, 2: 0.6, 3: 0.7, 4: 1.0, 10: 3.75 },
|
|
102
|
+
lineStyles: [
|
|
103
|
+
{ color: 'rgb(180, 200, 180)' },
|
|
104
|
+
{ color: 'rgb(121, 146, 127)', widthFraction: 1/3, dashArray: [30, 2, 8, 2] },
|
|
105
|
+
],
|
|
106
|
+
}));
|
|
107
|
+
|
|
108
|
+
// Register with MapLibre
|
|
109
|
+
protocol.register(maplibregl);
|
|
110
|
+
|
|
111
|
+
// Use in style (auto-detected or explicit)
|
|
112
|
+
const map = new maplibregl.Map({
|
|
113
|
+
container: 'map',
|
|
114
|
+
style: {
|
|
115
|
+
version: 8,
|
|
116
|
+
sources: {
|
|
117
|
+
osmde: {
|
|
118
|
+
type: 'raster',
|
|
119
|
+
tiles: ['ibc://https://tile.openstreetmap.de/{z}/{x}/{y}.png'],
|
|
120
|
+
// Or explicit: tiles: ['ibc://osm-de@https://tile.openstreetmap.de/{z}/{x}/{y}.png']
|
|
121
|
+
tileSize: 256
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
layers: [
|
|
125
|
+
{ id: 'osmde', type: 'raster', source: 'osmde' }
|
|
126
|
+
]
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## URL Format
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
ibc://[configId@]originalTileUrl
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
- `configId` (optional): Layer config ID to use
|
|
138
|
+
- `originalTileUrl`: The original tile URL template
|
|
139
|
+
|
|
140
|
+
Examples:
|
|
141
|
+
- `ibc://https://tile.openstreetmap.org/{z}/{x}/{y}.png` (auto-detect)
|
|
142
|
+
- `ibc://osm-carto@https://tile.openstreetmap.org/{z}/{x}/{y}.png` (explicit)
|
|
143
|
+
|
|
144
|
+
## API
|
|
145
|
+
|
|
146
|
+
### `registerCorrectionProtocol(maplibregl, options?)`
|
|
147
|
+
|
|
148
|
+
Convenience function to create and register a protocol.
|
|
149
|
+
|
|
150
|
+
| Parameter | Type | Description |
|
|
151
|
+
|-----------|------|-------------|
|
|
152
|
+
| `maplibregl` | object | MapLibre GL namespace |
|
|
153
|
+
| `options.pmtilesUrl` | string | URL to PMTiles file |
|
|
154
|
+
| `options.tileSize` | number | Tile size (default: 256) |
|
|
155
|
+
|
|
156
|
+
Returns: `CorrectionProtocol`
|
|
157
|
+
|
|
158
|
+
### `CorrectionProtocol`
|
|
159
|
+
|
|
160
|
+
#### Constructor
|
|
161
|
+
|
|
162
|
+
```javascript
|
|
163
|
+
new CorrectionProtocol(options?)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
#### Methods
|
|
167
|
+
|
|
168
|
+
| Method | Returns | Description |
|
|
169
|
+
|--------|---------|-------------|
|
|
170
|
+
| `addLayerConfig(config)` | `this` | Add a custom layer config |
|
|
171
|
+
| `register(maplibregl)` | `this` | Register protocol with MapLibre |
|
|
172
|
+
| `unregister(maplibregl)` | `this` | Unregister protocol |
|
|
173
|
+
| `getRegistry()` | `LayerConfigRegistry` | Get the layer config registry |
|
|
174
|
+
| `getTileFixer()` | `TileFixer` | Get the TileFixer instance |
|
|
175
|
+
| `on(event, listener)` | `this` | Add an event listener |
|
|
176
|
+
| `off(event, listener)` | `this` | Remove an event listener |
|
|
177
|
+
|
|
178
|
+
#### Events
|
|
179
|
+
|
|
180
|
+
##### `correctionerror`
|
|
181
|
+
|
|
182
|
+
Fired when the corrections data fails to load (e.g., PMTiles fetch failure). The tile will still display using the original uncorrected image.
|
|
183
|
+
|
|
184
|
+
```javascript
|
|
185
|
+
protocol.on('correctionerror', (e) => {
|
|
186
|
+
console.warn('Corrections unavailable:', e.error);
|
|
187
|
+
console.log('Tile coords:', e.coords); // { z, x, y }
|
|
188
|
+
console.log('Tile URL:', e.tileUrl);
|
|
189
|
+
});
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
| Property | Type | Description |
|
|
193
|
+
|----------|------|-------------|
|
|
194
|
+
| `error` | Error | The error that occurred |
|
|
195
|
+
| `coords` | object | Tile coordinates `{ z, x, y }` |
|
|
196
|
+
| `tileUrl` | string | URL of the tile being loaded |
|
|
197
|
+
|
|
198
|
+
## License
|
|
199
|
+
|
|
200
|
+
Unlicense
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
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
|
+
CorrectionProtocol: () => CorrectionProtocol,
|
|
24
|
+
LayerConfig: () => import_layer_configs2.LayerConfig,
|
|
25
|
+
fetchAndFixTile: () => fetchAndFixTile,
|
|
26
|
+
getPmtilesUrl: () => import_data2.getPmtilesUrl,
|
|
27
|
+
layerConfigs: () => import_layer_configs2.layerConfigs,
|
|
28
|
+
parseCorrectionsUrl: () => parseCorrectionsUrl,
|
|
29
|
+
registerCorrectionProtocol: () => registerCorrectionProtocol
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(index_exports);
|
|
32
|
+
var import_data = require("@india-boundary-corrector/data");
|
|
33
|
+
var import_layer_configs = require("@india-boundary-corrector/layer-configs");
|
|
34
|
+
var import_tilefixer = require("@india-boundary-corrector/tilefixer");
|
|
35
|
+
var import_layer_configs2 = require("@india-boundary-corrector/layer-configs");
|
|
36
|
+
var import_data2 = require("@india-boundary-corrector/data");
|
|
37
|
+
var PROTOCOL_PREFIX = "ibc";
|
|
38
|
+
function parseCorrectionsUrl(url) {
|
|
39
|
+
const withoutProtocol = url.replace(`${PROTOCOL_PREFIX}://`, "");
|
|
40
|
+
let configId = null;
|
|
41
|
+
let tileUrl = withoutProtocol;
|
|
42
|
+
const atIndex = withoutProtocol.indexOf("@");
|
|
43
|
+
if (atIndex > 0 && atIndex < withoutProtocol.indexOf("/")) {
|
|
44
|
+
configId = withoutProtocol.substring(0, atIndex);
|
|
45
|
+
tileUrl = withoutProtocol.substring(atIndex + 1);
|
|
46
|
+
}
|
|
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
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return { configId, tileUrl, z, x, y };
|
|
62
|
+
}
|
|
63
|
+
async function fetchAndFixTile(tileUrl, z, x, y, tileFixer, layerConfig, tileSize, options = {}) {
|
|
64
|
+
const { data, correctionsFailed, correctionsError } = await tileFixer.fetchAndFixTile(
|
|
65
|
+
tileUrl,
|
|
66
|
+
z,
|
|
67
|
+
x,
|
|
68
|
+
y,
|
|
69
|
+
layerConfig,
|
|
70
|
+
{ tileSize, signal: options.signal }
|
|
71
|
+
);
|
|
72
|
+
return { data, correctionsFailed, correctionsError };
|
|
73
|
+
}
|
|
74
|
+
var CorrectionProtocol = class {
|
|
75
|
+
/**
|
|
76
|
+
* @param {Object} [options]
|
|
77
|
+
* @param {string} [options.pmtilesUrl] - URL to PMTiles file (defaults to CDN)
|
|
78
|
+
* @param {number} [options.tileSize=256] - Tile size in pixels
|
|
79
|
+
*/
|
|
80
|
+
constructor(options = {}) {
|
|
81
|
+
this._pmtilesUrl = options.pmtilesUrl ?? (0, import_data.getPmtilesUrl)();
|
|
82
|
+
this._tileSize = options.tileSize ?? 256;
|
|
83
|
+
this._tileFixer = new import_tilefixer.BoundaryCorrector(this._pmtilesUrl);
|
|
84
|
+
this._registry = import_layer_configs.layerConfigs.createMergedRegistry();
|
|
85
|
+
this._listeners = /* @__PURE__ */ new Map();
|
|
86
|
+
this._loadFn = this._createLoadFunction();
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Add a listener for an event.
|
|
90
|
+
* @param {'correctionerror'} event - Event name
|
|
91
|
+
* @param {Function} listener - Callback function receiving event data
|
|
92
|
+
* @returns {this}
|
|
93
|
+
*/
|
|
94
|
+
on(event, listener) {
|
|
95
|
+
if (!this._listeners.has(event)) {
|
|
96
|
+
this._listeners.set(event, /* @__PURE__ */ new Set());
|
|
97
|
+
}
|
|
98
|
+
this._listeners.get(event).add(listener);
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Remove an event listener.
|
|
103
|
+
* @param {'correctionerror'} event - Event name
|
|
104
|
+
* @param {Function} listener - Callback to remove
|
|
105
|
+
* @returns {this}
|
|
106
|
+
*/
|
|
107
|
+
off(event, listener) {
|
|
108
|
+
this._listeners.get(event)?.delete(listener);
|
|
109
|
+
return this;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Emit an event to all listeners.
|
|
113
|
+
* @param {string} event - Event name
|
|
114
|
+
* @param {Object} data - Event data
|
|
115
|
+
* @private
|
|
116
|
+
*/
|
|
117
|
+
_emit(event, data) {
|
|
118
|
+
this._listeners.get(event)?.forEach((fn) => fn(data));
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Add a custom layer config to the registry.
|
|
122
|
+
* @param {Object} layerConfig - LayerConfig to add
|
|
123
|
+
* @returns {this}
|
|
124
|
+
*/
|
|
125
|
+
addLayerConfig(layerConfig) {
|
|
126
|
+
this._registry.register(layerConfig);
|
|
127
|
+
return this;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Get the registry.
|
|
131
|
+
* @returns {LayerConfigRegistry}
|
|
132
|
+
*/
|
|
133
|
+
getRegistry() {
|
|
134
|
+
return this._registry;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Get the TileFixer instance.
|
|
138
|
+
* @returns {TileFixer}
|
|
139
|
+
*/
|
|
140
|
+
getTileFixer() {
|
|
141
|
+
return this._tileFixer;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Register the protocol with MapLibre GL.
|
|
145
|
+
* @param {typeof import('maplibre-gl')} maplibregl - MapLibre GL namespace
|
|
146
|
+
* @returns {this}
|
|
147
|
+
*/
|
|
148
|
+
register(maplibregl) {
|
|
149
|
+
maplibregl.addProtocol(PROTOCOL_PREFIX, this._loadFn);
|
|
150
|
+
return this;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Unregister the protocol from MapLibre GL.
|
|
154
|
+
* @param {typeof import('maplibre-gl')} maplibregl - MapLibre GL namespace
|
|
155
|
+
* @returns {this}
|
|
156
|
+
*/
|
|
157
|
+
unregister(maplibregl) {
|
|
158
|
+
maplibregl.removeProtocol(PROTOCOL_PREFIX);
|
|
159
|
+
return this;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Create the protocol load function.
|
|
163
|
+
* @returns {Function}
|
|
164
|
+
* @private
|
|
165
|
+
*/
|
|
166
|
+
_createLoadFunction() {
|
|
167
|
+
const self = this;
|
|
168
|
+
return async (params, abortController) => {
|
|
169
|
+
const { configId, tileUrl, z, x, y } = parseCorrectionsUrl(params.url);
|
|
170
|
+
if (z === void 0 || x === void 0 || y === void 0) {
|
|
171
|
+
console.warn(`[CorrectionProtocol] Could not parse tile coordinates from URL: ${params.url}, falling back to original`);
|
|
172
|
+
const response = await fetch(tileUrl, { signal: abortController?.signal });
|
|
173
|
+
return { data: await response.arrayBuffer() };
|
|
174
|
+
}
|
|
175
|
+
let layerConfig;
|
|
176
|
+
if (configId) {
|
|
177
|
+
layerConfig = self._registry.get(configId);
|
|
178
|
+
} else {
|
|
179
|
+
layerConfig = self._registry.detectFromTileUrls([tileUrl]);
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
const result = await fetchAndFixTile(
|
|
183
|
+
tileUrl,
|
|
184
|
+
z,
|
|
185
|
+
x,
|
|
186
|
+
y,
|
|
187
|
+
self._tileFixer,
|
|
188
|
+
layerConfig,
|
|
189
|
+
self._tileSize,
|
|
190
|
+
{ signal: abortController?.signal }
|
|
191
|
+
);
|
|
192
|
+
if (result.correctionsFailed) {
|
|
193
|
+
console.warn("[CorrectionProtocol] Corrections fetch failed:", result.correctionsError);
|
|
194
|
+
self._emit("correctionerror", { error: result.correctionsError, coords: { z, x, y }, tileUrl });
|
|
195
|
+
}
|
|
196
|
+
return { data: result.data };
|
|
197
|
+
} 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() };
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
function registerCorrectionProtocol(maplibregl, options = {}) {
|
|
207
|
+
const protocol = new CorrectionProtocol(options);
|
|
208
|
+
return protocol.register(maplibregl);
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAA8B;AAC9B,2BAA6B;AAC7B,uBAA+C;AAG/C,IAAAA,wBAA0C;AAC1C,IAAAC,eAA8B;AAE9B,IAAM,kBAAkB;AAYxB,SAAS,oBAAoB,KAAK;AAEhC,QAAM,kBAAkB,IAAI,QAAQ,GAAG,eAAe,OAAO,EAAE;AAG/D,MAAI,WAAW;AACf,MAAI,UAAU;AAEd,QAAM,UAAU,gBAAgB,QAAQ,GAAG;AAC3C,MAAI,UAAU,KAAK,UAAU,gBAAgB,QAAQ,GAAG,GAAG;AAEzD,eAAW,gBAAgB,UAAU,GAAG,OAAO;AAC/C,cAAU,gBAAgB,UAAU,UAAU,CAAC;AAAA,EACjD;AAIA,QAAM,SAAS,IAAI,IAAI,OAAO;AAC9B,QAAM,YAAY,OAAO,SAAS,MAAM,GAAG,EAAE,OAAO,OAAK,EAAE,SAAS,CAAC;AAGrE,MAAI,GAAG,GAAG;AACV,WAAS,IAAI,UAAU,SAAS,GAAG,KAAK,GAAG,KAAK;AAC9C,UAAM,QAAQ,UAAU,CAAC,EAAE,QAAQ,YAAY,EAAE;AACjD,UAAM,QAAQ,UAAU,IAAI,CAAC;AAC7B,UAAM,QAAQ,UAAU,IAAI,CAAC;AAE7B,QAAI,QAAQ,KAAK,KAAK,KAAK,QAAQ,KAAK,KAAK,KAAK,QAAQ,KAAK,KAAK,GAAG;AACrE,UAAI,SAAS,OAAO,EAAE;AACtB,UAAI,SAAS,OAAO,EAAE;AACtB,UAAI,SAAS,OAAO,EAAE;AACtB;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,UAAU,SAAS,GAAG,GAAG,EAAE;AACtC;AAgBA,eAAe,gBAAgB,SAAS,GAAG,GAAG,GAAG,WAAW,aAAa,UAAU,UAAU,CAAC,GAAG;AAC/F,QAAM,EAAE,MAAM,mBAAmB,iBAAiB,IAAI,MAAM,UAAU;AAAA,IACpE;AAAA,IAAS;AAAA,IAAG;AAAA,IAAG;AAAA,IAAG;AAAA,IAAa,EAAE,UAAU,QAAQ,QAAQ,OAAO;AAAA,EACpE;AACA,SAAO,EAAE,MAAM,mBAAmB,iBAAiB;AACrD;AAcO,IAAM,qBAAN,MAAyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAM9B,YAAY,UAAU,CAAC,GAAG;AACxB,SAAK,cAAc,QAAQ,kBAAc,2BAAc;AACvD,SAAK,YAAY,QAAQ,YAAY;AACrC,SAAK,aAAa,IAAI,iBAAAC,kBAAU,KAAK,WAAW;AAChD,SAAK,YAAY,kCAAa,qBAAqB;AAEnD,SAAK,aAAa,oBAAI,IAAI;AAE1B,SAAK,UAAU,KAAK,oBAAoB;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,GAAG,OAAO,UAAU;AAClB,QAAI,CAAC,KAAK,WAAW,IAAI,KAAK,GAAG;AAC/B,WAAK,WAAW,IAAI,OAAO,oBAAI,IAAI,CAAC;AAAA,IACtC;AACA,SAAK,WAAW,IAAI,KAAK,EAAE,IAAI,QAAQ;AACvC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,IAAI,OAAO,UAAU;AACnB,SAAK,WAAW,IAAI,KAAK,GAAG,OAAO,QAAQ;AAC3C,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,OAAO,MAAM;AACjB,SAAK,WAAW,IAAI,KAAK,GAAG,QAAQ,QAAM,GAAG,IAAI,CAAC;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,eAAe,aAAa;AAC1B,SAAK,UAAU,SAAS,WAAW;AACnC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,cAAc;AACZ,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,eAAe;AACb,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SAAS,YAAY;AACnB,eAAW,YAAY,iBAAiB,KAAK,OAAO;AACpD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,WAAW,YAAY;AACrB,eAAW,eAAe,eAAe;AACzC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,sBAAsB;AACpB,UAAM,OAAO;AAEb,WAAO,OAAO,QAAQ,oBAAoB;AACxC,YAAM,EAAE,UAAU,SAAS,GAAG,GAAG,EAAE,IAAI,oBAAoB,OAAO,GAAG;AAGrE,UAAI,MAAM,UAAa,MAAM,UAAa,MAAM,QAAW;AACzD,gBAAQ,KAAK,mEAAmE,OAAO,GAAG,4BAA4B;AACtH,cAAM,WAAW,MAAM,MAAM,SAAS,EAAE,QAAQ,iBAAiB,OAAO,CAAC;AACzE,eAAO,EAAE,MAAM,MAAM,SAAS,YAAY,EAAE;AAAA,MAC9C;AAGA,UAAI;AACJ,UAAI,UAAU;AACZ,sBAAc,KAAK,UAAU,IAAI,QAAQ;AAAA,MAC3C,OAAO;AACL,sBAAc,KAAK,UAAU,mBAAmB,CAAC,OAAO,CAAC;AAAA,MAC3D;AAEA,UAAI;AACF,cAAM,SAAS,MAAM;AAAA,UACnB;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,KAAK;AAAA,UACL;AAAA,UACA,KAAK;AAAA,UACL,EAAE,QAAQ,iBAAiB,OAAO;AAAA,QACpC;AAEA,YAAI,OAAO,mBAAmB;AAC5B,kBAAQ,KAAK,kDAAkD,OAAO,gBAAgB;AACtF,eAAK,MAAM,mBAAmB,EAAE,OAAO,OAAO,kBAAkB,QAAQ,EAAE,GAAG,GAAG,EAAE,GAAG,QAAQ,CAAC;AAAA,QAChG;AAEA,eAAO,EAAE,MAAM,OAAO,KAAK;AAAA,MAC7B,SAAS,KAAK;AACZ,gBAAQ,KAAK,8EAA8E,GAAG;AAC9F,aAAK,MAAM,mBAAmB,EAAE,OAAO,KAAK,QAAQ,EAAE,GAAG,GAAG,EAAE,GAAG,QAAQ,CAAC;AAC1E,cAAM,WAAW,MAAM,MAAM,SAAS,EAAE,QAAQ,iBAAiB,OAAO,CAAC;AACzE,eAAO,EAAE,MAAM,MAAM,SAAS,YAAY,EAAE;AAAA,MAC9C;AAAA,IACF;AAAA,EACF;AACF;AAgCO,SAAS,2BAA2B,YAAY,UAAU,CAAC,GAAG;AACnE,QAAM,WAAW,IAAI,mBAAmB,OAAO;AAC/C,SAAO,SAAS,SAAS,UAAU;AACrC;","names":["import_layer_configs","import_data","TileFixer"]}
|