pmtiles-swarm 0.98.0 → 0.98.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/CHANGELOG.md +20 -0
- package/docs/tile-stacks.md +5 -3
- package/package.json +1 -1
- package/src/api.js +1 -1
- package/src/archive-summary.js +183 -0
- package/src/feed.js +1 -1
- package/src/mbtiles.js +2 -13
- package/src/pmtiles-probe.js +6 -153
- package/src/tiles.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,26 @@
|
|
|
7
7
|
### 🐞 Bug fixes
|
|
8
8
|
- _...Add new stuff here..._
|
|
9
9
|
|
|
10
|
+
## 0.98.1
|
|
11
|
+
### ✨ Features and improvements
|
|
12
|
+
- **The two archive readers no longer import each other.** Summarising an MBTiles archive reached
|
|
13
|
+
into `pmtiles-probe.js` for `summarize`, which put one format's reader inside the other's module
|
|
14
|
+
and dragged the `pmtiles` package in behind it. The half neither format owns — the summary shape,
|
|
15
|
+
its version, the metadata readers and the tile-type table — is now `archive-summary.js`, which
|
|
16
|
+
imports nothing and opens nothing: it takes a header and a metadata document and returns a
|
|
17
|
+
summary. `pmtiles-probe.js` keeps the PMTiles half and `mbtiles.js` the SQLite half, and neither
|
|
18
|
+
reaches the other.
|
|
19
|
+
|
|
20
|
+
Nothing about what the node serves changes; this is where the code lives.
|
|
21
|
+
|
|
22
|
+
### 🐞 Bug fixes
|
|
23
|
+
- **An MBTiles archive holding MLT tiles was summarised as an unknown format.** The table mapping a
|
|
24
|
+
format name onto a tile type had been written twice, in opposite directions, and the copy beside
|
|
25
|
+
the MBTiles reader was written by hand and had no entry for `mlt` — so an archive declaring it
|
|
26
|
+
came back as `unknown`, which is what the catalog then reported and what a stack over it read.
|
|
27
|
+
There is one table now, with the reverse lookup derived from it, so the two halves cannot drift
|
|
28
|
+
apart again.
|
|
29
|
+
|
|
10
30
|
## 0.98.0
|
|
11
31
|
### ✨ Features and improvements
|
|
12
32
|
- **A finished MBTiles archive works as a stack layer, and now says what it holds.** It always read:
|
package/docs/tile-stacks.md
CHANGED
|
@@ -469,9 +469,11 @@ This is the one thing that stops a stack being a small feature, and it is
|
|
|
469
469
|
narrower than "the node cannot handle tiles". It already does, in most of the
|
|
470
470
|
ways a stack needs:
|
|
471
471
|
|
|
472
|
-
- `identify.js` reads an archive's magic bytes;
|
|
473
|
-
headers, directories and metadata; `mbtiles.js` queries tile rows
|
|
474
|
-
SQLite.
|
|
472
|
+
- `identify.js` reads an archive's magic bytes; `pmtiles-probe.js` reads PMTiles
|
|
473
|
+
headers, directories and metadata; `mbtiles.js` queries tile rows and the
|
|
474
|
+
metadata table out of SQLite. Neither format's reader imports the other's:
|
|
475
|
+
what they share is `archive-summary.js`, which turns a header and a metadata
|
|
476
|
+
document into the summary the catalog keeps and opens nothing itself.
|
|
475
477
|
- `TileStore.getTile` resolves an archive, reads a tile through the local file
|
|
476
478
|
or the swarm, and knows its format from the header rather than by guessing.
|
|
477
479
|
- The tile route already gzips vector tiles through `node:zlib`, abandons a
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmtiles-swarm",
|
|
3
|
-
"version": "0.98.
|
|
3
|
+
"version": "0.98.1",
|
|
4
4
|
"description": "BitTorrent distribution for PMTiles map archives: create torrents, watch folders, publish and subscribe to RSS feeds, and seed through qBittorrent or an embedded client",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
package/src/api.js
CHANGED
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
} from './sources.js';
|
|
39
39
|
import { limitFor, remaining } from './seeding.js';
|
|
40
40
|
import { buildTileJson, extensionMatches, tileExtension } from './tilejson.js';
|
|
41
|
-
import { SUMMARY_VERSION } from './
|
|
41
|
+
import { SUMMARY_VERSION } from './archive-summary.js';
|
|
42
42
|
import { TileReadError } from './tiles.js';
|
|
43
43
|
import { loadCodec } from './codec.js';
|
|
44
44
|
import { answerStackTile, outputFormat, outputSize } from './stack-tile.js';
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The summary this project keeps about a map archive, and the vocabulary for
|
|
3
|
+
* building one.
|
|
4
|
+
*
|
|
5
|
+
* What a subscriber needs in order to decide whether they want an archive:
|
|
6
|
+
* coverage, zoom range, tile type, size. This is what makes a map RSS feed more
|
|
7
|
+
* useful than a generic torrent feed — an item can say "raster webp, z0-14,
|
|
8
|
+
* covering Switzerland, 36 GiB" instead of just a filename, so a consumer can
|
|
9
|
+
* filter before committing to a download.
|
|
10
|
+
*
|
|
11
|
+
* Here rather than beside either reader because both formats produce one, and
|
|
12
|
+
* neither should have to import the other to do it. Nothing in this file opens
|
|
13
|
+
* a file or a socket: it takes a header and a metadata document and returns the
|
|
14
|
+
* summary, which is why it can be shared without dragging one format's
|
|
15
|
+
* dependencies into the other's module.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Maps tile type numbers onto names and content types.
|
|
20
|
+
*
|
|
21
|
+
* The numbering is PMTiles', because a number was needed and that one is
|
|
22
|
+
* written down. An MBTiles archive names its format in a metadata row instead,
|
|
23
|
+
* and `tileTypeFor` is how it says the same thing in the same terms.
|
|
24
|
+
*/
|
|
25
|
+
const TILE_TYPES = {
|
|
26
|
+
0: { format: 'unknown', contentType: 'application/octet-stream' },
|
|
27
|
+
1: { format: 'pbf', contentType: 'application/x-protobuf' },
|
|
28
|
+
2: { format: 'png', contentType: 'image/png' },
|
|
29
|
+
3: { format: 'jpeg', contentType: 'image/jpeg' },
|
|
30
|
+
4: { format: 'webp', contentType: 'image/webp' },
|
|
31
|
+
5: { format: 'avif', contentType: 'image/avif' },
|
|
32
|
+
// No MIME is registered for MLT, and nothing selects a tile by content type.
|
|
33
|
+
6: { format: 'mlt', contentType: 'application/octet-stream' },
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The tile type number for a format an archive names in words.
|
|
38
|
+
*
|
|
39
|
+
* The inverse of the table above, kept beside it so the two halves cannot
|
|
40
|
+
* drift. `mvt` and `jpg` are spellings the same formats appear under in the
|
|
41
|
+
* wild; anything unrecognised is 0, which reads back as "unknown" rather than
|
|
42
|
+
* as a format nothing can serve.
|
|
43
|
+
* @param {string} [format] - What the archive calls its tiles.
|
|
44
|
+
* @returns {number} - The tile type number.
|
|
45
|
+
*/
|
|
46
|
+
export function tileTypeFor(format) {
|
|
47
|
+
const named = String(format ?? '').toLowerCase();
|
|
48
|
+
const found = Object.entries(TILE_TYPES).find(
|
|
49
|
+
([, type]) => type.format === named,
|
|
50
|
+
);
|
|
51
|
+
if (found) return Number(found[0]);
|
|
52
|
+
return { mvt: 1, jpg: 3 }[named] ?? 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Summary of an archive, as stored in the catalog and published in the feed.
|
|
57
|
+
* @typedef {object} ArchiveSummary
|
|
58
|
+
* @property {number} specVersion - PMTiles spec version.
|
|
59
|
+
* @property {string} format - Tile format: pbf, png, jpeg, webp, avif.
|
|
60
|
+
* @property {string} contentType - Matching content type.
|
|
61
|
+
* @property {number} minZoom - Lowest zoom present.
|
|
62
|
+
* @property {number} maxZoom - Highest zoom present.
|
|
63
|
+
* @property {number[]} bounds - [minLon, minLat, maxLon, maxLat].
|
|
64
|
+
* @property {number[]} center - [lon, lat, zoom].
|
|
65
|
+
* @property {number} tileCount - Addressed tile count.
|
|
66
|
+
* @property {boolean} clustered - Whether tiles are stored in Hilbert order.
|
|
67
|
+
* @property {string} [name] - Name from the archive metadata.
|
|
68
|
+
* @property {string} [description] - Description from the archive metadata.
|
|
69
|
+
* @property {string} [attribution] - Attribution from the archive metadata.
|
|
70
|
+
* @property {object[]} [vectorLayers] - Vector layer definitions, for pbf archives.
|
|
71
|
+
* @property {boolean} [sparse] - What the archive says about missing tiles, if it says anything.
|
|
72
|
+
*/
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Reads a flag out of archive metadata, which is not reliably typed.
|
|
76
|
+
*
|
|
77
|
+
* A PMTiles JSON blob carries a real boolean, but the same metadata routinely
|
|
78
|
+
* arrives having been round-tripped through MBTiles, where every value is TEXT
|
|
79
|
+
* — so the honest reading of `"false"` is false, not "a non-empty string".
|
|
80
|
+
* @param {unknown} value - Whatever the metadata held.
|
|
81
|
+
* @returns {boolean | undefined} - The flag, or undefined if it said nothing.
|
|
82
|
+
*/
|
|
83
|
+
export function metadataFlag(value) {
|
|
84
|
+
if (value === undefined || value === null || value === '') return undefined;
|
|
85
|
+
if (typeof value === 'boolean') return value;
|
|
86
|
+
if (typeof value === 'number') return value !== 0;
|
|
87
|
+
const text = String(value).trim().toLowerCase();
|
|
88
|
+
if (['true', '1', 'yes'].includes(text)) return true;
|
|
89
|
+
if (['false', '0', 'no'].includes(text)) return false;
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The tile encoding an archive declares, if it is one MapLibre understands.
|
|
95
|
+
*
|
|
96
|
+
* See docs/tilejson.md — `encoding`.
|
|
97
|
+
* @param {unknown} value - Whatever the metadata held.
|
|
98
|
+
* @returns {string | undefined} - A known encoding, or undefined.
|
|
99
|
+
*/
|
|
100
|
+
export function metadataEncoding(value) {
|
|
101
|
+
if (typeof value !== 'string') return undefined;
|
|
102
|
+
const text = value.trim().toLowerCase();
|
|
103
|
+
// An unrecognised value is dropped: it would cost a client its own default.
|
|
104
|
+
return ['terrarium', 'mapbox', 'custom', 'mlt'].includes(text)
|
|
105
|
+
? text
|
|
106
|
+
: undefined;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* The four factors a `custom` encoding is unreadable without. All or nothing.
|
|
111
|
+
* @param {object} metadata - The archive's metadata.
|
|
112
|
+
* @returns {object | undefined} - The four factors, or undefined.
|
|
113
|
+
*/
|
|
114
|
+
export function customEncodingFactors(metadata) {
|
|
115
|
+
const named = ['redFactor', 'greenFactor', 'blueFactor', 'baseShift'];
|
|
116
|
+
const out = {};
|
|
117
|
+
for (const name of named) {
|
|
118
|
+
const value = Number(metadata[name]);
|
|
119
|
+
if (!Number.isFinite(value)) return undefined;
|
|
120
|
+
out[name] = value;
|
|
121
|
+
}
|
|
122
|
+
return out;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* What this prober reads. Raise it whenever a field is added to the summary,
|
|
127
|
+
* and archives probed by an older build are re-read once. See
|
|
128
|
+
* docs/internals.md — "Re-reading a summary an older prober wrote".
|
|
129
|
+
*/
|
|
130
|
+
export const SUMMARY_VERSION = 3;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* What the catalog keeps about an archive, from its header and metadata.
|
|
134
|
+
* @param {object} header - A parsed PMTiles v3 header.
|
|
135
|
+
* @param {object} [metadata] - The archive's own metadata document.
|
|
136
|
+
* @returns {object} - The summary, at `SUMMARY_VERSION`.
|
|
137
|
+
*/
|
|
138
|
+
export function summarize(header, metadata = {}) {
|
|
139
|
+
const type = TILE_TYPES[header.tileType] ?? TILE_TYPES[0];
|
|
140
|
+
|
|
141
|
+
// An archive with no bounds set reports all zeroes; treat that as global
|
|
142
|
+
// rather than as a point at null island.
|
|
143
|
+
const hasBounds = !(
|
|
144
|
+
header.minLon === 0 &&
|
|
145
|
+
header.minLat === 0 &&
|
|
146
|
+
header.maxLon === 0 &&
|
|
147
|
+
header.maxLat === 0
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
summaryVersion: SUMMARY_VERSION,
|
|
152
|
+
specVersion: header.specVersion,
|
|
153
|
+
format: type.format,
|
|
154
|
+
contentType: type.contentType,
|
|
155
|
+
minZoom: header.minZoom,
|
|
156
|
+
maxZoom: header.maxZoom,
|
|
157
|
+
bounds: hasBounds
|
|
158
|
+
? [header.minLon, header.minLat, header.maxLon, header.maxLat]
|
|
159
|
+
: [-180, -85.051129, 180, 85.051129],
|
|
160
|
+
center: [
|
|
161
|
+
header.centerLon,
|
|
162
|
+
header.centerLat,
|
|
163
|
+
header.centerZoom || Math.round(header.maxZoom / 2),
|
|
164
|
+
],
|
|
165
|
+
tileCount: header.numAddressedTiles,
|
|
166
|
+
clustered: header.clustered,
|
|
167
|
+
name: metadata.name,
|
|
168
|
+
description: metadata.description,
|
|
169
|
+
attribution: metadata.attribution,
|
|
170
|
+
vectorLayers: metadata.vector_layers,
|
|
171
|
+
// What the archive says about its own missing tiles. tileserver-gl reads
|
|
172
|
+
// the same key, so an archive built to be served there carries the answer
|
|
173
|
+
// with it and does not have to be configured again here.
|
|
174
|
+
sparse: metadataFlag(metadata.sparse),
|
|
175
|
+
// The header settles MLT; only the metadata can settle elevation packing.
|
|
176
|
+
encoding:
|
|
177
|
+
type.format === 'mlt' ? 'mlt' : metadataEncoding(metadata.encoding),
|
|
178
|
+
encodingFactors:
|
|
179
|
+
metadataEncoding(metadata.encoding) === 'custom'
|
|
180
|
+
? customEncodingFactors(metadata)
|
|
181
|
+
: undefined,
|
|
182
|
+
};
|
|
183
|
+
}
|
package/src/feed.js
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import { mutableMagnet } from './mutable.js';
|
|
21
|
-
import { metadataEncoding } from './
|
|
21
|
+
import { metadataEncoding } from './archive-summary.js';
|
|
22
22
|
|
|
23
23
|
const PMTILES_NS = 'https://github.com/TechIdiots-LLC/pmtiles-swarm/ns/1.0';
|
|
24
24
|
|
package/src/mbtiles.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import { summarize } from './
|
|
2
|
+
import { summarize, tileTypeFor } from './archive-summary.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Reading tiles out of a completed MBTiles archive.
|
|
@@ -21,17 +21,6 @@ import { summarize } from './pmtiles-probe.js';
|
|
|
21
21
|
* MBTiles 1.3: https://github.com/mapbox/mbtiles-spec/blob/master/1.3/spec.md
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
/** Maps an MBTiles `format` onto the PMTiles tile-type number summarize expects. */
|
|
25
|
-
const TILE_TYPES = {
|
|
26
|
-
pbf: 1,
|
|
27
|
-
mvt: 1,
|
|
28
|
-
png: 2,
|
|
29
|
-
jpg: 3,
|
|
30
|
-
jpeg: 3,
|
|
31
|
-
webp: 4,
|
|
32
|
-
avif: 5,
|
|
33
|
-
};
|
|
34
|
-
|
|
35
24
|
/** The whole world, for an archive that declares no bounds. */
|
|
36
25
|
const WHOLE_WORLD = [-180, -85.051129, 180, 85.051129];
|
|
37
26
|
|
|
@@ -166,7 +155,7 @@ export class MbtilesArchive {
|
|
|
166
155
|
return {
|
|
167
156
|
// MBTiles has no equivalent, and nothing reads this except to report it.
|
|
168
157
|
specVersion: 3,
|
|
169
|
-
tileType:
|
|
158
|
+
tileType: tileTypeFor(format),
|
|
170
159
|
minZoom,
|
|
171
160
|
maxZoom,
|
|
172
161
|
minLon: bounds[0],
|
package/src/pmtiles-probe.js
CHANGED
|
@@ -1,97 +1,15 @@
|
|
|
1
1
|
import { FetchSource, PMTiles } from 'pmtiles';
|
|
2
|
+
import { summarize } from './archive-summary.js';
|
|
2
3
|
import { NodeFileSource } from './file-source.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
|
-
*
|
|
6
|
-
* whether they want it: coverage, zoom range, tile type, size.
|
|
6
|
+
* Reading a PMTiles archive's summary, from a local path or over HTTP.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* The summary itself is built in archive-summary.js, which knows nothing about
|
|
9
|
+
* either format. What is here is the half that is PMTiles: opening one, and
|
|
10
|
+
* where its header and metadata are kept.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
/** Maps PMTiles tile type numbers onto names and content types. */
|
|
14
|
-
const TILE_TYPES = {
|
|
15
|
-
0: { format: 'unknown', contentType: 'application/octet-stream' },
|
|
16
|
-
1: { format: 'pbf', contentType: 'application/x-protobuf' },
|
|
17
|
-
2: { format: 'png', contentType: 'image/png' },
|
|
18
|
-
3: { format: 'jpeg', contentType: 'image/jpeg' },
|
|
19
|
-
4: { format: 'webp', contentType: 'image/webp' },
|
|
20
|
-
5: { format: 'avif', contentType: 'image/avif' },
|
|
21
|
-
// No MIME is registered for MLT, and nothing selects a tile by content type.
|
|
22
|
-
6: { format: 'mlt', contentType: 'application/octet-stream' },
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Summary of an archive, as stored in the catalog and published in the feed.
|
|
27
|
-
* @typedef {object} PMTilesSummary
|
|
28
|
-
* @property {number} specVersion - PMTiles spec version.
|
|
29
|
-
* @property {string} format - Tile format: pbf, png, jpeg, webp, avif.
|
|
30
|
-
* @property {string} contentType - Matching content type.
|
|
31
|
-
* @property {number} minZoom - Lowest zoom present.
|
|
32
|
-
* @property {number} maxZoom - Highest zoom present.
|
|
33
|
-
* @property {number[]} bounds - [minLon, minLat, maxLon, maxLat].
|
|
34
|
-
* @property {number[]} center - [lon, lat, zoom].
|
|
35
|
-
* @property {number} tileCount - Addressed tile count.
|
|
36
|
-
* @property {boolean} clustered - Whether tiles are stored in Hilbert order.
|
|
37
|
-
* @property {string} [name] - Name from the archive metadata.
|
|
38
|
-
* @property {string} [description] - Description from the archive metadata.
|
|
39
|
-
* @property {string} [attribution] - Attribution from the archive metadata.
|
|
40
|
-
* @property {object[]} [vectorLayers] - Vector layer definitions, for pbf archives.
|
|
41
|
-
* @property {boolean} [sparse] - What the archive says about missing tiles, if it says anything.
|
|
42
|
-
*/
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Reads a flag out of archive metadata, which is not reliably typed.
|
|
46
|
-
*
|
|
47
|
-
* A PMTiles JSON blob carries a real boolean, but the same metadata routinely
|
|
48
|
-
* arrives having been round-tripped through MBTiles, where every value is TEXT
|
|
49
|
-
* — so the honest reading of `"false"` is false, not "a non-empty string".
|
|
50
|
-
* @param {unknown} value - Whatever the metadata held.
|
|
51
|
-
* @returns {boolean | undefined} - The flag, or undefined if it said nothing.
|
|
52
|
-
*/
|
|
53
|
-
export function metadataFlag(value) {
|
|
54
|
-
if (value === undefined || value === null || value === '') return undefined;
|
|
55
|
-
if (typeof value === 'boolean') return value;
|
|
56
|
-
if (typeof value === 'number') return value !== 0;
|
|
57
|
-
const text = String(value).trim().toLowerCase();
|
|
58
|
-
if (['true', '1', 'yes'].includes(text)) return true;
|
|
59
|
-
if (['false', '0', 'no'].includes(text)) return false;
|
|
60
|
-
return undefined;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* The tile encoding an archive declares, if it is one MapLibre understands.
|
|
65
|
-
*
|
|
66
|
-
* See docs/tilejson.md — `encoding`.
|
|
67
|
-
* @param {unknown} value - Whatever the metadata held.
|
|
68
|
-
* @returns {string | undefined} - A known encoding, or undefined.
|
|
69
|
-
*/
|
|
70
|
-
export function metadataEncoding(value) {
|
|
71
|
-
if (typeof value !== 'string') return undefined;
|
|
72
|
-
const text = value.trim().toLowerCase();
|
|
73
|
-
// An unrecognised value is dropped: it would cost a client its own default.
|
|
74
|
-
return ['terrarium', 'mapbox', 'custom', 'mlt'].includes(text)
|
|
75
|
-
? text
|
|
76
|
-
: undefined;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* The four factors a `custom` encoding is unreadable without. All or nothing.
|
|
81
|
-
* @param {object} metadata - The archive's metadata.
|
|
82
|
-
* @returns {object | undefined} - The four factors, or undefined.
|
|
83
|
-
*/
|
|
84
|
-
export function customEncodingFactors(metadata) {
|
|
85
|
-
const named = ['redFactor', 'greenFactor', 'blueFactor', 'baseShift'];
|
|
86
|
-
const out = {};
|
|
87
|
-
for (const name of named) {
|
|
88
|
-
const value = Number(metadata[name]);
|
|
89
|
-
if (!Number.isFinite(value)) return undefined;
|
|
90
|
-
out[name] = value;
|
|
91
|
-
}
|
|
92
|
-
return out;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
13
|
/**
|
|
96
14
|
* Reads an archive's header and metadata.
|
|
97
15
|
*
|
|
@@ -99,72 +17,7 @@ export function customEncodingFactors(metadata) {
|
|
|
99
17
|
* even for a multi-terabyte archive — and it works against an HTTP URL without
|
|
100
18
|
* downloading it.
|
|
101
19
|
* @param {string} location - Local path or http(s) URL.
|
|
102
|
-
* @returns {Promise<
|
|
103
|
-
*/
|
|
104
|
-
/**
|
|
105
|
-
* What this prober reads. Raise it whenever a field is added to the summary,
|
|
106
|
-
* and archives probed by an older build are re-read once. See
|
|
107
|
-
* docs/internals.md — "Re-reading a summary an older prober wrote".
|
|
108
|
-
*/
|
|
109
|
-
export const SUMMARY_VERSION = 3;
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* What the catalog keeps about an archive, from its header and metadata.
|
|
113
|
-
* @param {object} header - A parsed PMTiles v3 header.
|
|
114
|
-
* @param {object} [metadata] - The archive's own metadata document.
|
|
115
|
-
* @returns {object} - The summary, at `SUMMARY_VERSION`.
|
|
116
|
-
*/
|
|
117
|
-
export function summarize(header, metadata = {}) {
|
|
118
|
-
const type = TILE_TYPES[header.tileType] ?? TILE_TYPES[0];
|
|
119
|
-
|
|
120
|
-
// An archive with no bounds set reports all zeroes; treat that as global
|
|
121
|
-
// rather than as a point at null island.
|
|
122
|
-
const hasBounds = !(
|
|
123
|
-
header.minLon === 0 &&
|
|
124
|
-
header.minLat === 0 &&
|
|
125
|
-
header.maxLon === 0 &&
|
|
126
|
-
header.maxLat === 0
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
return {
|
|
130
|
-
summaryVersion: SUMMARY_VERSION,
|
|
131
|
-
specVersion: header.specVersion,
|
|
132
|
-
format: type.format,
|
|
133
|
-
contentType: type.contentType,
|
|
134
|
-
minZoom: header.minZoom,
|
|
135
|
-
maxZoom: header.maxZoom,
|
|
136
|
-
bounds: hasBounds
|
|
137
|
-
? [header.minLon, header.minLat, header.maxLon, header.maxLat]
|
|
138
|
-
: [-180, -85.051129, 180, 85.051129],
|
|
139
|
-
center: [
|
|
140
|
-
header.centerLon,
|
|
141
|
-
header.centerLat,
|
|
142
|
-
header.centerZoom || Math.round(header.maxZoom / 2),
|
|
143
|
-
],
|
|
144
|
-
tileCount: header.numAddressedTiles,
|
|
145
|
-
clustered: header.clustered,
|
|
146
|
-
name: metadata.name,
|
|
147
|
-
description: metadata.description,
|
|
148
|
-
attribution: metadata.attribution,
|
|
149
|
-
vectorLayers: metadata.vector_layers,
|
|
150
|
-
// What the archive says about its own missing tiles. tileserver-gl reads
|
|
151
|
-
// the same key, so an archive built to be served there carries the answer
|
|
152
|
-
// with it and does not have to be configured again here.
|
|
153
|
-
sparse: metadataFlag(metadata.sparse),
|
|
154
|
-
// The header settles MLT; only the metadata can settle elevation packing.
|
|
155
|
-
encoding:
|
|
156
|
-
type.format === 'mlt' ? 'mlt' : metadataEncoding(metadata.encoding),
|
|
157
|
-
encodingFactors:
|
|
158
|
-
metadataEncoding(metadata.encoding) === 'custom'
|
|
159
|
-
? customEncodingFactors(metadata)
|
|
160
|
-
: undefined,
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Reads an archive's header and metadata.
|
|
166
|
-
* @param {string} location - Local path or http(s) URL.
|
|
167
|
-
* @returns {Promise<PMTilesSummary>} - The summary.
|
|
20
|
+
* @returns {Promise<ArchiveSummary>} - The summary.
|
|
168
21
|
*/
|
|
169
22
|
export async function probePMTiles(location) {
|
|
170
23
|
const isHttp = /^https?:\/\//i.test(location);
|
package/src/tiles.js
CHANGED
|
@@ -7,7 +7,7 @@ import { TorrentSource } from 'pmtiles-torrent';
|
|
|
7
7
|
import { NodeFileSource } from './file-source.js';
|
|
8
8
|
import { onDiskPath } from './incomplete.js';
|
|
9
9
|
import { MbtilesArchive, isMbtiles } from './mbtiles.js';
|
|
10
|
-
import { summarize } from './
|
|
10
|
+
import { summarize } from './archive-summary.js';
|
|
11
11
|
import { LibtorrentReadEngine } from './read-engine.js';
|
|
12
12
|
|
|
13
13
|
/**
|