maplibre-tilepackage-protocol 1.1.0 → 1.2.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/package.json CHANGED
@@ -1,23 +1,43 @@
1
- {
2
- "name": "maplibre-tilepackage-protocol",
3
- "version": "1.1.0",
4
- "description": "Maplibre-gl-js protocol for reading VTPK and TPKX files",
5
- "homepage": "https://github.com/spatialillusions/maplibre-tilepackage-protocol#readme",
6
- "bugs": {
7
- "url": "https://github.com/spatialillusions/maplibre-tilepackage-protocol/issues"
8
- },
9
- "repository": {
10
- "type": "git",
11
- "url": "git+https://github.com/spatialillusions/maplibre-tilepackage-protocol.git"
12
- },
13
- "license": "MIT",
14
- "author": "Måns Beckman",
15
- "type": "module",
16
- "main": "src/index.js",
17
- "scripts": {
18
- "test": "echo \"Error: no test specified\" && exit 1"
19
- },
20
- "dependencies": {
21
- "maplibre-local-glyphs": "^1.2.0"
22
- }
1
+ {
2
+ "name": "maplibre-tilepackage-protocol",
3
+ "version": "1.2.1",
4
+ "description": "Maplibre-gl-js protocol for reading VTPK and TPKX files",
5
+ "homepage": "https://github.com/spatialillusions/maplibre-tilepackage-protocol#readme",
6
+ "bugs": {
7
+ "url": "https://github.com/spatialillusions/maplibre-tilepackage-protocol/issues"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/spatialillusions/maplibre-tilepackage-protocol.git"
12
+ },
13
+ "license": "MIT",
14
+ "author": "Måns Beckman",
15
+ "type": "module",
16
+ "main": "src/index.js",
17
+ "scripts": {
18
+ "test": "echo \"Error: no test specified\" && exit 1",
19
+ "build": "rollup -c",
20
+ "build:esm": "rollup -c --configPlugin @rollup/plugin-node-resolve",
21
+ "demo": "npm run build"
22
+ },
23
+ "dependencies": {
24
+ "@mapbox/point-geometry": "^0.1.0",
25
+ "@mapbox/vector-tile": "^1.3.1",
26
+ "@maplibre/vt-pbf": "^4.1.0",
27
+ "fflate": "^0.8.2",
28
+ "maplibre-local-glyphs": "^1.2.0",
29
+ "pbf": "^3.2.1"
30
+ },
31
+ "devDependencies": {
32
+ "@rollup/plugin-commonjs": "^26.0.3",
33
+ "@rollup/plugin-node-resolve": "^16.0.3",
34
+ "rollup": "^4.53.3"
35
+ },
36
+ "module": "dist/index.esm.js",
37
+ "exports": {
38
+ ".": {
39
+ "import": "./dist/index.esm.js",
40
+ "require": "./src/index.js"
41
+ }
42
+ }
23
43
  }
@@ -0,0 +1,45 @@
1
+ // rollup.config.mjs
2
+ // node resolves makes npm packages be included in the bundle
3
+ import { nodeResolve } from "@rollup/plugin-node-resolve";
4
+ import commonjs from "@rollup/plugin-commonjs";
5
+
6
+ const externals = [
7
+ "@mapbox/vector-tile",
8
+ "pbf",
9
+ "@mapbox/point-geometry",
10
+ "@maplibre/vt-pbf",
11
+ "maplibre-local-glyphs",
12
+ ];
13
+
14
+ function onwarn(warning) {
15
+ if (warning.code === "CIRCULAR_DEPENDENCY") return;
16
+ console.warn(warning.message);
17
+ }
18
+
19
+ export default [
20
+ // ESM library build (dependencies external)
21
+ {
22
+ input: "src/index.js",
23
+ output: {
24
+ file: "dist/index.esm.js",
25
+ format: "esm",
26
+ sourcemap: true,
27
+ },
28
+ // Keep selected externals; bundle fflate (omit from externals)
29
+ external: externals,
30
+ plugins: [nodeResolve({ browser: true }), commonjs()],
31
+ onwarn,
32
+ },
33
+ // IIFE demo build (fully bundled)
34
+ {
35
+ input: "src/index.js",
36
+ output: {
37
+ file: "dist/bundle.js",
38
+ format: "iife",
39
+ name: "TilePackageProtocol",
40
+ sourcemap: true,
41
+ },
42
+ plugins: [nodeResolve({ browser: true }), commonjs()],
43
+ onwarn,
44
+ },
45
+ ];
@@ -12,16 +12,23 @@ export default async function defaultDecompress(buf, compression) {
12
12
  return buf;
13
13
  }
14
14
  if (compression === "gzip") {
15
- if (typeof globalThis.DecompressionStream === "undefined") {
15
+ // Safe global detection for DecompressionStream feature test
16
+ const g =
17
+ typeof globalThis !== "undefined"
18
+ ? globalThis
19
+ : typeof window !== "undefined"
20
+ ? window
21
+ : typeof self !== "undefined"
22
+ ? self
23
+ : {};
24
+ if (typeof g.DecompressionStream === "undefined") {
16
25
  return fflate.decompressSync(new Uint8Array(buf));
17
26
  }
18
27
  const stream = new Response(buf).body;
19
28
  if (!stream) {
20
29
  throw new Error("Failed to read response stream");
21
30
  }
22
- const result = stream.pipeThrough(
23
- new globalThis.DecompressionStream("gzip"),
24
- );
31
+ const result = stream.pipeThrough(new g.DecompressionStream("gzip"));
25
32
  return new Response(result).arrayBuffer();
26
33
  }
27
34
  throw new Error("Compression method not supported");
@@ -59,6 +59,7 @@ export function jsonToHeader(json, files, etag) {
59
59
  maxLon: Number(json.extent.xmax),
60
60
  maxLat: Number(json.extent.ymax),
61
61
  files: files,
62
+ coverageMap: json.coverageMap,
62
63
  etag: etag,
63
64
  };
64
65
  }
@@ -239,11 +240,49 @@ export default async function getHeaderAndFileList(source) {
239
240
  root.metadataOffset = metadataOffset;
240
241
  root.metadataLength = tilePackageFiles["p12/metadata.json"].size;
241
242
  }
243
+
244
+ async function calculateCoverage(z, x, y, tilemap, coverageMap) {
245
+ z = z + 1;
246
+ x = x * 2;
247
+ y = y * 2;
248
+ if (coverageMap[z] == undefined) coverageMap[z] = {};
249
+ if (coverageMap[z][x] == undefined) coverageMap[z][x] = {};
250
+ if (coverageMap[z][x + 1] == undefined) coverageMap[z][x + 1] = {};
251
+ coverageMap[z][x][y] = tilemap[0];
252
+ if (isNaN(tilemap[0])) {
253
+ calculateCoverage(z, x, y, tilemap[0], coverageMap);
254
+ }
255
+ coverageMap[z][x + 1][y] = tilemap[1];
256
+ if (isNaN(tilemap[1])) {
257
+ calculateCoverage(z, x + 1, y, tilemap[1], coverageMap);
258
+ }
259
+ coverageMap[z][x][y + 1] = tilemap[2];
260
+ if (isNaN(tilemap[2])) {
261
+ calculateCoverage(z, x, y + 1, tilemap[2], coverageMap);
262
+ }
263
+ coverageMap[z][x + 1][y + 1] = tilemap[3];
264
+ if (isNaN(tilemap[3])) {
265
+ calculateCoverage(z, x + 1, y + 1, tilemap[3], coverageMap);
266
+ }
267
+ }
268
+ // Get and calculate the tilemap
269
+ if (source.coverageCheck > 0 && tilePackageFiles["p12/tilemap/root.json"]) {
270
+ const tilemap = await getJsonFromFile(
271
+ "p12/tilemap/root.json",
272
+ tilePackageFiles,
273
+ source,
274
+ );
275
+ const coverageMap = {};
276
+ coverageMap[0] = {};
277
+ coverageMap[0][0] = {};
278
+ coverageMap[0][0][0] = "blob";
279
+ calculateCoverage(0, 0, 0, tilemap.index, coverageMap);
280
+ root.coverageMap = coverageMap;
281
+ } else {
282
+ // Coverage map unavailable (either disabled or missing tilemap/root.json)
283
+ }
242
284
  }
243
- //TODO read iteminfo.xml....
244
285
 
245
286
  const header = jsonToHeader(root, tilePackageFiles, resp.etag);
246
-
247
- //return [header, [dirKey, rootDir.length, rootDir]];
248
287
  return [header, ""];
249
288
  }
package/src/index.js CHANGED
@@ -1,22 +1,28 @@
1
1
  export * from "./maplibre-gl-js-protocol.js";
2
2
  export * from "./source.js";
3
3
  import { FetchSource } from "./source.js";
4
+ import subdivideVectorTile from "./tilecutter/subdivide.js";
4
5
  import defaultDecompress from "./default-decompress.js";
5
6
  import getJsonFromFile from "./get-json-from-file.js";
6
7
  import SharedPromiseCache from "./shared-promise-cache.js";
7
8
 
9
+ /**
10
+ * Escape literal newlines in JSON text.
11
+ * Replaces actual newline characters with the JSON escape sequence.
12
+ */
13
+ function escapeJsonNewlines(text) {
14
+ // Replace literal newlines with JSON escape sequences
15
+ // This handles \r\n, \n, and \r
16
+ return text.replace(/\r\n/g, "\\n").replace(/[\r\n]/g, "\\n");
17
+ }
18
+
8
19
  function calculateFilename(z, x, y, header) {
9
20
  const zoom = z.toString().padStart(2, "0");
10
21
  const baseRow = Math.floor(y / 128) * 128;
11
22
  const baseCol = Math.floor(x / 128) * 128;
12
23
  const rowHex = baseRow.toString(16).padStart(4, "0");
13
24
  const colHex = baseCol.toString(16).padStart(4, "0");
14
- let basePath;
15
- if (header.type === "tpkx") {
16
- basePath = `tile`;
17
- } else {
18
- basePath = `p12/tile`;
19
- }
25
+ const basePath = header.type === "tpkx" ? "tile" : "p12/tile";
20
26
  return `${basePath}/L${zoom}/R${rowHex}C${colHex}.bundle`;
21
27
  }
22
28
 
@@ -26,10 +32,6 @@ function getTileInfoFromTileIndex(tileIndex, z, x, y) {
26
32
  return tileIndex[row][col];
27
33
  }
28
34
 
29
- /**
30
- * Error thrown when a response for TilePackage over HTTP does not match previous, cached parts of the archive.
31
- * The default TilePackage implementation will catch this error once internally and retry a request.
32
- */
33
35
  class EtagMismatch extends Error {
34
36
  constructor(message) {
35
37
  super(message);
@@ -38,24 +40,29 @@ class EtagMismatch extends Error {
38
40
  }
39
41
 
40
42
  export class TilePackage {
41
- constructor(source, cache, decompress) {
43
+ constructor(source, options) {
44
+ // Default coverageCheck enabled; disable with coverageCheck:false
45
+ this.coverageCheck = 1;
46
+ if (options && options.coverageCheck === false) this.coverageCheck = 0;
47
+ // Max dz guard (skip subdivision beyond this): default 8
48
+ this.maxDz =
49
+ options && typeof options.maxDz === "number" ? options.maxDz : 8;
42
50
  if (typeof source === "string") {
43
- this.source = new FetchSource(source);
51
+ const opts = this.coverageCheck
52
+ ? { coverageCheck: this.coverageCheck }
53
+ : undefined;
54
+ this.source = new FetchSource(source, opts);
44
55
  } else {
45
56
  this.source = source;
57
+ // Propagate default coverageCheck to existing Source instances if undefined
58
+ if (this.source && this.source.coverageCheck === undefined) {
59
+ this.source.coverageCheck = this.coverageCheck;
60
+ }
46
61
  }
47
-
48
- if (decompress) {
49
- this.decompress = decompress;
50
- } else {
51
- this.decompress = defaultDecompress;
52
- }
53
-
54
- if (cache) {
55
- this.cache = cache;
56
- } else {
57
- this.cache = new SharedPromiseCache();
58
- }
62
+ this.decompress =
63
+ options && options.decompress ? options.decompress : defaultDecompress;
64
+ this.cache =
65
+ options && options.cache ? options.cache : new SharedPromiseCache();
59
66
  }
60
67
 
61
68
  async getHeader() {
@@ -63,42 +70,99 @@ export class TilePackage {
63
70
  }
64
71
 
65
72
  async getZxyAttempt(z, x, y, signal) {
66
- //y = (1 << z) - 1 - y; //TMS vs XYZ
67
73
  const header = await this.cache.getHeader(this.source);
68
- if (z < header.minZoom || z > header.maxZoom) {
69
- return undefined;
70
- }
74
+ if (z < header.minZoom || z > header.maxZoom) return undefined;
71
75
  const file = calculateFilename(z, x, y, header);
72
- if (!header.files[file]) {
73
- return undefined;
74
- }
75
- const tileIndex = await this.cache.getTileIndex(
76
- this.source,
77
- file,
78
- header,
79
- signal,
80
- );
81
- const tileInfo = getTileInfoFromTileIndex(tileIndex, z, x, y);
82
- if (tileInfo && tileInfo.tileSize > 0) {
83
- const tileOffset =
84
- header.files[file].absoluteOffset + tileInfo.tileOffset;
85
- const resp = await this.source.getBytes(
86
- tileOffset,
87
- tileInfo.tileSize,
76
+ let tileInfo = null;
77
+ if (header.files[file]) {
78
+ const tileIndex = await this.cache.getTileIndex(
79
+ this.source,
80
+ file,
81
+ header,
88
82
  signal,
89
- header.etag,
90
83
  );
91
- const data = await this.decompress(resp.data, header.tileCompression);
92
- return {
93
- data: data,
94
- cacheControl: resp.cacheControl,
95
- expires: resp.expires,
96
- };
97
- } else {
98
- //return await this.getZxyAttempt(z - 1, x / 2, y / 2, signal);
99
- // Return undefined if the tile is not found
100
- return undefined;
84
+ tileInfo = getTileInfoFromTileIndex(tileIndex, z, x, y);
85
+ if (tileInfo && tileInfo.tileSize > 0) {
86
+ const tileOffset =
87
+ header.files[file].absoluteOffset + tileInfo.tileOffset;
88
+ const resp = await this.source.getBytes(
89
+ tileOffset,
90
+ tileInfo.tileSize,
91
+ signal,
92
+ header.etag,
93
+ );
94
+ const data = await this.decompress(resp.data, header.tileCompression);
95
+ return { data, cacheControl: resp.cacheControl, expires: resp.expires };
96
+ }
101
97
  }
98
+ if (header.packageType === "vtpk" && header.coverageMap) {
99
+ // Ascend to find parent with value 1
100
+ let pz = z,
101
+ px = x,
102
+ py = y,
103
+ foundParent = null;
104
+ while (pz > header.minZoom) {
105
+ pz -= 1;
106
+ px = Math.floor(px / 2);
107
+ py = Math.floor(py / 2);
108
+ const level = header.coverageMap[pz];
109
+ const val = level && level[px] ? level[px][py] : undefined;
110
+ if (val === 1) {
111
+ foundParent = { pz, px, py };
112
+ break;
113
+ }
114
+ }
115
+ if (foundParent) {
116
+ const parent = await this.getZxyAttempt(
117
+ foundParent.pz,
118
+ foundParent.px,
119
+ foundParent.py,
120
+ signal,
121
+ );
122
+ if (parent && parent.data) {
123
+ const cached = this.cache.getSubdivided(this.source, z, x, y);
124
+ if (cached) {
125
+ return {
126
+ data: cached,
127
+ cacheControl: parent.cacheControl,
128
+ expires: parent.expires,
129
+ };
130
+ }
131
+ const dz = z - foundParent.pz;
132
+ if (dz > this.maxDz) {
133
+ return undefined; // exceed hard cap
134
+ }
135
+ try {
136
+ const { data: overzoomed } = subdivideVectorTile(
137
+ parent.data,
138
+ foundParent.pz,
139
+ foundParent.px,
140
+ foundParent.py,
141
+ z,
142
+ x,
143
+ y,
144
+ { buffer: 128, maxDzWarn: this.maxDz - 1 },
145
+ );
146
+ this.cache.setSubdivided(this.source, z, x, y, overzoomed);
147
+ return {
148
+ data: overzoomed,
149
+ cacheControl: parent.cacheControl,
150
+ expires: parent.expires,
151
+ };
152
+ } catch (e) {
153
+ console.warn(
154
+ `[tilepackage subdivide] failed for z=${z} x=${x} y=${y}:`,
155
+ e,
156
+ );
157
+ }
158
+ } else {
159
+ console.warn(
160
+ `[tilepackage subdivide] parent tile missing at z=${foundParent.pz} x=${foundParent.px} y=${foundParent.py}`,
161
+ );
162
+ }
163
+ }
164
+ }
165
+ return undefined;
102
166
  }
103
167
 
104
168
  async getZxy(z, x, y, signal) {
@@ -115,21 +179,21 @@ export class TilePackage {
115
179
 
116
180
  async getMetadataAttempt() {
117
181
  const header = await this.cache.getHeader(this.source);
118
-
119
182
  let metadata = {};
120
183
  if (header.packageType === "vtpk") {
121
184
  const resp = await this.source.getBytes(
122
- header.jsonMetadataOffset,
123
- header.jsonMetadataLength,
185
+ header.metadataOffset,
186
+ header.metadataLength,
124
187
  undefined,
125
188
  header.etag,
126
189
  );
127
-
128
190
  const decoder = new TextDecoder("utf-8");
129
- metadata = JSON.parse(decoder.decode(resp.data));
191
+ const jsonText = decoder.decode(resp.data);
192
+ console.log("test", jsonText);
193
+ console.log("escaped text", escapeJsonNewlines(jsonText));
194
+ metadata = JSON.parse(escapeJsonNewlines(jsonText));
130
195
  }
131
196
  metadata.name = header.name;
132
-
133
197
  return metadata;
134
198
  }
135
199
 
@@ -147,10 +211,7 @@ export class TilePackage {
147
211
 
148
212
  async getResourceAttempt(file, signal) {
149
213
  const header = await this.cache.getHeader(this.source);
150
- if (!header.files[file]) {
151
- return undefined;
152
- }
153
-
214
+ if (!header.files[file]) return undefined;
154
215
  const resource = await this.cache.getResource(
155
216
  this.source,
156
217
  file,
@@ -180,37 +241,59 @@ export class TilePackage {
180
241
  const header = await this.cache.getHeader(this.source);
181
242
  const sourceKey = this.source.getKey();
182
243
  if (header.packageType === "vtpk") {
244
+ const metadata = await getJsonFromFile(
245
+ "p12/root.json",
246
+ header.files,
247
+ this.source,
248
+ );
183
249
  const style = await getJsonFromFile(
184
250
  "p12/resources/styles/root.json",
185
251
  header.files,
186
252
  this.source,
187
253
  );
188
- style.sources.esri.url = `"tilepackage://${sourceKey}`;
189
- style.sources.esri.maxzoom = header.maxZoom || 22;
254
+ if (style.sources && style.sources.esri) {
255
+ delete style.sources.esri.url;
256
+ style.sources.esri.tiles = [`tilepackage://${sourceKey}/{z}/{x}/{y}`];
257
+ style.sources.esri.type = style.sources.esri.type || "vector";
258
+ style.sources.esri.minzoom = header.minZoom || 0;
259
+ style.sources.esri.maxzoom = header.maxZoom || 22;
260
+ }
261
+ for (const k in style.sources) {
262
+ const src = style.sources[k];
263
+ if (
264
+ src &&
265
+ typeof src.url === "string" &&
266
+ src.url.startsWith("tilepackage://")
267
+ ) {
268
+ delete src.url;
269
+ if (!src.tiles)
270
+ src.tiles = [`tilepackage://${sourceKey}/{z}/{x}/{y}`];
271
+ }
272
+ }
273
+ if (metadata.copyrightText)
274
+ style.sources.esri.attribution = metadata.copyrightText;
190
275
  style.glyphs = `tilepackage://${sourceKey}/{fontstack}/{range}`;
191
276
  style.sprite = `tilepackage://${sourceKey}/sprite`;
192
-
277
+ if (this.debug)
278
+ console.debug(
279
+ "[tilepackage style] rewritten esri source",
280
+ style.sources.esri,
281
+ );
193
282
  return style;
194
- } else {
195
- return {
196
- version: 8,
197
- sources: {
198
- esri: {
199
- type: "raster",
200
- tileSize: header.tileSize || 256,
201
- url: `"tilepackage://${sourceKey}`,
202
- maxzoom: header.maxZoom || 22,
203
- },
204
- },
205
- layers: [
206
- {
207
- id: "tilepackageraster",
208
- type: "raster",
209
- source: "esri",
210
- },
211
- ],
212
- };
213
283
  }
284
+ return {
285
+ version: 8,
286
+ sources: {
287
+ esri: {
288
+ type: "raster",
289
+ tileSize: header.tileSize || 256,
290
+ tiles: [`tilepackage://${sourceKey}/{z}/{x}/{y}`],
291
+ maxzoom: header.maxZoom || 22,
292
+ minzoom: header.minZoom || 0,
293
+ },
294
+ },
295
+ layers: [{ id: "tilepackageraster", type: "raster", source: "esri" }],
296
+ };
214
297
  }
215
298
 
216
299
  async getStyle() {
@@ -229,7 +312,6 @@ export class TilePackage {
229
312
  const header = await this.getHeader();
230
313
  const metadata = await this.getMetadata();
231
314
  const ext = header.tileType;
232
-
233
315
  const tileJson = {
234
316
  tilejson: "3.0.0",
235
317
  scheme: "xyz",
@@ -238,21 +320,11 @@ export class TilePackage {
238
320
  version: header.version,
239
321
  bounds: [header.minLon, header.minLat, header.maxLon, header.maxLat],
240
322
  };
241
- if (metadata.vector_layers) {
242
- tileJson.vector_layers = metadata.vector_layers;
243
- }
244
- if (metadata.attribution) {
245
- tileJson.attribution = metadata.attribution;
246
- }
247
- if (metadata.description) {
248
- tileJson.description = metadata.description;
249
- }
250
- if (header.minZoom) {
251
- tileJson.minzoom = header.minZoom;
252
- }
253
- if (header.maxZoom) {
254
- tileJson.maxzoom = header.maxZoom;
255
- }
323
+ if (metadata.vector_layers) tileJson.vector_layers = metadata.vector_layers;
324
+ if (metadata.attribution) tileJson.attribution = metadata.attribution;
325
+ if (metadata.description) tileJson.description = metadata.description;
326
+ if (header.minZoom) tileJson.minzoom = header.minZoom;
327
+ if (header.maxZoom) tileJson.maxzoom = header.maxZoom;
256
328
  return tileJson;
257
329
  }
258
330
  }
@@ -52,6 +52,7 @@ export class Protocol {
52
52
  this.errorOnMissingTile = options
53
53
  ? options.errorOnMissingTile || false
54
54
  : false;
55
+ this.debug = options ? options.debug || false : false;
55
56
  this.getData = async (params, abortController) => {
56
57
  if (params.type === "json") {
57
58
  let tilePackageUrl = params.url.substr(14); // TODO fix this to be more robust
@@ -91,9 +92,10 @@ export class Protocol {
91
92
  }
92
93
  // This is a request for a tile json
93
94
  if (this.metadata) {
94
- return {
95
- data: await instance.getTileJson(params.url),
96
- };
95
+ const tj = await instance.getTileJson(params.url);
96
+ if (this.debug)
97
+ console.debug("[tilepackage] tilejson (metadata)", tj);
98
+ return { data: tj };
97
99
  }
98
100
  const h = await instance.getHeader();
99
101
  if (h.minLon >= h.maxLon || h.minLat >= h.maxLat) {
@@ -102,14 +104,15 @@ export class Protocol {
102
104
  );
103
105
  }
104
106
 
105
- return {
106
- data: {
107
- tiles: [`${params.url}/{z}/{x}/{y}`],
108
- minzoom: h.minZoom,
109
- maxzoom: h.maxZoom,
110
- bounds: [h.minLon, h.minLat, h.maxLon, h.maxLat],
111
- },
107
+ const synthesized = {
108
+ tiles: [`${params.url}/{z}/{x}/{y}`],
109
+ minzoom: h.minZoom,
110
+ maxzoom: h.maxZoom,
111
+ bounds: [h.minLon, h.minLat, h.maxLon, h.maxLat],
112
112
  };
113
+ if (this.debug)
114
+ console.debug("[tilepackage] tilejson (synthesized)", synthesized);
115
+ return { data: synthesized };
113
116
  //}
114
117
  }
115
118
  // TODO handle other paths ???
@@ -197,6 +200,13 @@ export class Protocol {
197
200
  const y = result[4];
198
201
  const header = await instance.getHeader();
199
202
  const resp = await instance.getZxy(+z, +x, +y, abortController.signal);
203
+ if (this.debug)
204
+ console.debug("[tilepackage] tile fetch", {
205
+ z: +z,
206
+ x: +x,
207
+ y: +y,
208
+ found: !!resp,
209
+ });
200
210
  if (resp) {
201
211
  return {
202
212
  data: new Uint8Array(resp.data),
@@ -206,10 +216,13 @@ export class Protocol {
206
216
  }
207
217
  if (header.tileType === "pbf") {
208
218
  if (this.errorOnMissingTile) {
219
+ //*
209
220
  const e = new Error(
210
- "Tile not found in Tile Package, this is normal for VTPK files with variable tile depth.",
211
- ); // e.name is 'Error'
221
+ `Tile [${z},${x},${y}] not found in Tile Package, normal for VTPK with variable depth.`,
222
+ );
212
223
  e.name = "TileError";
224
+ if (this.debug)
225
+ console.debug("[tilepackage] missing tile", { z, x, y });
213
226
  throw e;
214
227
  }
215
228
  return { data: new Uint8Array() };
@@ -229,12 +242,15 @@ export class Protocol {
229
242
  */
230
243
  add(p) {
231
244
  this.tiles.set(p.source.getKey(), p);
245
+ if (this.debug)
246
+ console.debug("[tilepackage] add instance", p.source.getKey());
232
247
  }
233
248
 
234
249
  /**
235
250
  * Fetch a {@link TilePackage} instance by URL, for remote TilePackage instances.
236
251
  */
237
252
  get(url) {
253
+ if (this.debug) console.debug("[tilepackage] get instance", url);
238
254
  return this.tiles.get(url);
239
255
  }
240
256
  }