styled-map-package-api 5.0.0-pre.0
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/dist/download.cjs +100 -0
- package/dist/download.d.cts +64 -0
- package/dist/download.d.ts +64 -0
- package/dist/download.js +76 -0
- package/dist/from-mbtiles.cjs +81 -0
- package/dist/from-mbtiles.d.cts +10 -0
- package/dist/from-mbtiles.d.ts +10 -0
- package/dist/from-mbtiles.js +57 -0
- package/dist/index.cjs +46 -0
- package/dist/index.d.cts +25 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +16 -0
- package/dist/reader.cjs +287 -0
- package/dist/reader.d.cts +68 -0
- package/dist/reader.d.ts +68 -0
- package/dist/reader.js +259 -0
- package/dist/server.cjs +73 -0
- package/dist/server.d.cts +46 -0
- package/dist/server.d.ts +46 -0
- package/dist/server.js +49 -0
- package/dist/style-downloader.cjs +314 -0
- package/dist/style-downloader.d.cts +119 -0
- package/dist/style-downloader.d.ts +119 -0
- package/dist/style-downloader.js +290 -0
- package/dist/tile-downloader.cjs +156 -0
- package/dist/tile-downloader.d.cts +83 -0
- package/dist/tile-downloader.d.ts +83 -0
- package/dist/tile-downloader.js +124 -0
- package/dist/types-CJq90eOB.d.cts +184 -0
- package/dist/types-CJq90eOB.d.ts +184 -0
- package/dist/utils/errors.cjs +41 -0
- package/dist/utils/errors.d.cts +18 -0
- package/dist/utils/errors.d.ts +18 -0
- package/dist/utils/errors.js +16 -0
- package/dist/utils/fetch.cjs +97 -0
- package/dist/utils/fetch.d.cts +50 -0
- package/dist/utils/fetch.d.ts +50 -0
- package/dist/utils/fetch.js +63 -0
- package/dist/utils/file-formats.cjs +96 -0
- package/dist/utils/file-formats.d.cts +33 -0
- package/dist/utils/file-formats.d.ts +33 -0
- package/dist/utils/file-formats.js +70 -0
- package/dist/utils/geo.cjs +84 -0
- package/dist/utils/geo.d.cts +46 -0
- package/dist/utils/geo.d.ts +46 -0
- package/dist/utils/geo.js +56 -0
- package/dist/utils/mapbox.cjs +121 -0
- package/dist/utils/mapbox.d.cts +43 -0
- package/dist/utils/mapbox.d.ts +43 -0
- package/dist/utils/mapbox.js +91 -0
- package/dist/utils/misc.cjs +39 -0
- package/dist/utils/misc.d.cts +22 -0
- package/dist/utils/misc.d.ts +22 -0
- package/dist/utils/misc.js +13 -0
- package/dist/utils/streams.cjs +99 -0
- package/dist/utils/streams.d.cts +49 -0
- package/dist/utils/streams.d.ts +49 -0
- package/dist/utils/streams.js +73 -0
- package/dist/utils/style.cjs +126 -0
- package/dist/utils/style.d.cts +67 -0
- package/dist/utils/style.d.ts +67 -0
- package/dist/utils/style.js +98 -0
- package/dist/utils/templates.cjs +124 -0
- package/dist/utils/templates.d.cts +80 -0
- package/dist/utils/templates.d.ts +80 -0
- package/dist/utils/templates.js +85 -0
- package/dist/writer.cjs +465 -0
- package/dist/writer.d.cts +5 -0
- package/dist/writer.d.ts +5 -0
- package/dist/writer.js +452 -0
- package/package.json +161 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import * as type_fest from 'type-fest';
|
|
2
|
+
import { d as GlyphRange, T as TileInfo, c as TileFormat } from '../types-CJq90eOB.js';
|
|
3
|
+
import '@maplibre/maplibre-gl-style-spec';
|
|
4
|
+
import 'geojson';
|
|
5
|
+
import 'events';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @param {string} path
|
|
9
|
+
* @returns
|
|
10
|
+
*/
|
|
11
|
+
declare function getResourceType(path: string): "sprite" | "style" | "tile" | "glyph";
|
|
12
|
+
/**
|
|
13
|
+
* Determine the content type of a file based on its extension.
|
|
14
|
+
*
|
|
15
|
+
* @param {string} path
|
|
16
|
+
*/
|
|
17
|
+
declare function getContentType(path: string): "application/json; charset=utf-8" | "application/x-protobuf" | "image/png" | "image/jpeg" | "image/webp" | "application/vnd.mapbox-vector-tile";
|
|
18
|
+
/**
|
|
19
|
+
* Get the filename for a tile, given the TileInfo
|
|
20
|
+
*
|
|
21
|
+
* @param {import("type-fest").SetRequired<import("../writer.js").TileInfo, 'format'>} tileInfo
|
|
22
|
+
* @returns
|
|
23
|
+
*/
|
|
24
|
+
declare function getTileFilename({ sourceId, z, x, y, format }: type_fest.SetRequired<TileInfo, "format">): string;
|
|
25
|
+
/**
|
|
26
|
+
* Get a filename for a sprite file, given the sprite id, pixel ratio and extension
|
|
27
|
+
*
|
|
28
|
+
* @param {{ id: string, pixelRatio: number, ext: '.json' | '.png'}} spriteInfo
|
|
29
|
+
*/
|
|
30
|
+
declare function getSpriteFilename({ id, pixelRatio, ext }: {
|
|
31
|
+
id: string;
|
|
32
|
+
pixelRatio: number;
|
|
33
|
+
ext: ".json" | ".png";
|
|
34
|
+
}): string;
|
|
35
|
+
/**
|
|
36
|
+
* Get the filename for a glyph file, given the fontstack and range
|
|
37
|
+
*
|
|
38
|
+
* @param {object} options
|
|
39
|
+
* @param {string} options.fontstack
|
|
40
|
+
* @param {import("../writer.js").GlyphRange} options.range
|
|
41
|
+
*/
|
|
42
|
+
declare function getGlyphFilename({ fontstack, range }: {
|
|
43
|
+
fontstack: string;
|
|
44
|
+
range: GlyphRange;
|
|
45
|
+
}): string;
|
|
46
|
+
/**
|
|
47
|
+
* Get the URI template for the sprites in the style
|
|
48
|
+
*/
|
|
49
|
+
declare function getSpriteUri(id?: string): string;
|
|
50
|
+
/**
|
|
51
|
+
* Get the URI template for tiles in the style
|
|
52
|
+
*
|
|
53
|
+
* @param {object} opts
|
|
54
|
+
* @param {string} opts.sourceId
|
|
55
|
+
* @param {import("../writer.js").TileFormat} opts.format
|
|
56
|
+
* @returns
|
|
57
|
+
*/
|
|
58
|
+
declare function getTileUri({ sourceId, format }: {
|
|
59
|
+
sourceId: string;
|
|
60
|
+
format: TileFormat;
|
|
61
|
+
}): string;
|
|
62
|
+
/**
|
|
63
|
+
* Replaces variables in a string with values provided in an object. Variables
|
|
64
|
+
* in the string are denoted by curly braces, e.g., {variableName}.
|
|
65
|
+
*
|
|
66
|
+
* @param {string} template - The string containing variables wrapped in curly braces.
|
|
67
|
+
* @param {Record<string, string | number>} variables - An object where the keys correspond to variable names and values correspond to the replacement values.
|
|
68
|
+
* @returns {string} The string with the variables replaced by their corresponding values.
|
|
69
|
+
*/
|
|
70
|
+
declare function replaceVariables(template: string, variables: Record<string, string | number>): string;
|
|
71
|
+
declare const URI_SCHEME: "smp";
|
|
72
|
+
declare const URI_BASE: string;
|
|
73
|
+
declare const VERSION_FILE: "VERSION";
|
|
74
|
+
declare const FORMAT_VERSION: "1.0";
|
|
75
|
+
declare const STYLE_FILE: "style.json";
|
|
76
|
+
declare const SOURCES_FOLDER: "s";
|
|
77
|
+
declare const FONTS_FOLDER: "fonts";
|
|
78
|
+
declare const GLYPH_URI: string;
|
|
79
|
+
|
|
80
|
+
export { FONTS_FOLDER, FORMAT_VERSION, GLYPH_URI, SOURCES_FOLDER, STYLE_FILE, URI_BASE, URI_SCHEME, VERSION_FILE, getContentType, getGlyphFilename, getResourceType, getSpriteFilename, getSpriteUri, getTileFilename, getTileUri, replaceVariables };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const URI_SCHEME = "smp";
|
|
2
|
+
const URI_BASE = URI_SCHEME + "://maps.v1/";
|
|
3
|
+
const VERSION_FILE = "VERSION";
|
|
4
|
+
const FORMAT_VERSION = "1.0";
|
|
5
|
+
const STYLE_FILE = "style.json";
|
|
6
|
+
const SOURCES_FOLDER = "s";
|
|
7
|
+
const SPRITES_FOLDER = "sprites";
|
|
8
|
+
const FONTS_FOLDER = "fonts";
|
|
9
|
+
const TILE_FILE = SOURCES_FOLDER + "/{sourceId}/{z}/{x}/{y}{ext}";
|
|
10
|
+
const SPRITE_FILE = SPRITES_FOLDER + "/{id}/sprite{pixelRatio}{ext}";
|
|
11
|
+
const GLYPH_FILE = FONTS_FOLDER + "/{fontstack}/{range}.pbf.gz";
|
|
12
|
+
const GLYPH_URI = URI_BASE + GLYPH_FILE;
|
|
13
|
+
const pathToResouceType = (
|
|
14
|
+
/** @type {const} */
|
|
15
|
+
{
|
|
16
|
+
[TILE_FILE.split("/")[0] + "/"]: "tile",
|
|
17
|
+
[SPRITE_FILE.split("/")[0] + "/"]: "sprite",
|
|
18
|
+
[GLYPH_FILE.split("/")[0] + "/"]: "glyph"
|
|
19
|
+
}
|
|
20
|
+
);
|
|
21
|
+
function getResourceType(path) {
|
|
22
|
+
if (path === "style.json") return "style";
|
|
23
|
+
for (const [prefix, type] of Object.entries(pathToResouceType)) {
|
|
24
|
+
if (path.startsWith(prefix)) return type;
|
|
25
|
+
}
|
|
26
|
+
throw new Error(`Unknown resource type for path: ${path}`);
|
|
27
|
+
}
|
|
28
|
+
function getContentType(path) {
|
|
29
|
+
if (path.endsWith(".json")) return "application/json; charset=utf-8";
|
|
30
|
+
if (path.endsWith(".pbf.gz") || path.endsWith(".pbf"))
|
|
31
|
+
return "application/x-protobuf";
|
|
32
|
+
if (path.endsWith(".png")) return "image/png";
|
|
33
|
+
if (path.endsWith(".jpg")) return "image/jpeg";
|
|
34
|
+
if (path.endsWith(".webp")) return "image/webp";
|
|
35
|
+
if (path.endsWith(".mvt.gz") || path.endsWith(".mvt"))
|
|
36
|
+
return "application/vnd.mapbox-vector-tile";
|
|
37
|
+
throw new Error(`Unknown content type for path: ${path}`);
|
|
38
|
+
}
|
|
39
|
+
function getTileFilename({ sourceId, z, x, y, format }) {
|
|
40
|
+
const ext = "." + format + (format === "mvt" ? ".gz" : "");
|
|
41
|
+
return replaceVariables(TILE_FILE, { sourceId, z, x, y, ext });
|
|
42
|
+
}
|
|
43
|
+
function getSpriteFilename({ id, pixelRatio, ext }) {
|
|
44
|
+
return replaceVariables(SPRITE_FILE, {
|
|
45
|
+
id,
|
|
46
|
+
pixelRatio: getPixelRatioString(pixelRatio),
|
|
47
|
+
ext
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function getGlyphFilename({ fontstack, range }) {
|
|
51
|
+
return replaceVariables(GLYPH_FILE, { fontstack, range });
|
|
52
|
+
}
|
|
53
|
+
function getSpriteUri(id = "default") {
|
|
54
|
+
return URI_BASE + replaceVariables(SPRITE_FILE, { id, pixelRatio: "", ext: "" });
|
|
55
|
+
}
|
|
56
|
+
function getTileUri({ sourceId, format }) {
|
|
57
|
+
const ext = "." + format + (format === "mvt" ? ".gz" : "");
|
|
58
|
+
return URI_BASE + TILE_FILE.replace("{sourceId}", sourceId).replace("{ext}", ext);
|
|
59
|
+
}
|
|
60
|
+
function getPixelRatioString(pixelRatio) {
|
|
61
|
+
return pixelRatio === 1 ? "" : `@${pixelRatio}x`;
|
|
62
|
+
}
|
|
63
|
+
function replaceVariables(template, variables) {
|
|
64
|
+
return template.replace(/{(.*?)}/g, (match, varName) => {
|
|
65
|
+
return varName in variables ? String(variables[varName]) : match;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
export {
|
|
69
|
+
FONTS_FOLDER,
|
|
70
|
+
FORMAT_VERSION,
|
|
71
|
+
GLYPH_URI,
|
|
72
|
+
SOURCES_FOLDER,
|
|
73
|
+
STYLE_FILE,
|
|
74
|
+
URI_BASE,
|
|
75
|
+
URI_SCHEME,
|
|
76
|
+
VERSION_FILE,
|
|
77
|
+
getContentType,
|
|
78
|
+
getGlyphFilename,
|
|
79
|
+
getResourceType,
|
|
80
|
+
getSpriteFilename,
|
|
81
|
+
getSpriteUri,
|
|
82
|
+
getTileFilename,
|
|
83
|
+
getTileUri,
|
|
84
|
+
replaceVariables
|
|
85
|
+
};
|
package/dist/writer.cjs
ADDED
|
@@ -0,0 +1,465 @@
|
|
|
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
|
+
var writer_exports = {};
|
|
20
|
+
__export(writer_exports, {
|
|
21
|
+
SUPPORTED_SOURCE_TYPES: () => SUPPORTED_SOURCE_TYPES,
|
|
22
|
+
Writer: () => Writer
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(writer_exports);
|
|
25
|
+
var import_maplibre_gl_style_spec = require("@maplibre/maplibre-gl-style-spec");
|
|
26
|
+
var import_bbox = require("@turf/bbox");
|
|
27
|
+
var import_events = require("events");
|
|
28
|
+
var import_filter_obj = require("filter-obj");
|
|
29
|
+
var import_zip_writer = require("zip-writer");
|
|
30
|
+
var import_file_formats = require('./utils/file-formats.cjs');
|
|
31
|
+
var import_geo = require('./utils/geo.cjs');
|
|
32
|
+
var import_misc = require('./utils/misc.cjs');
|
|
33
|
+
var import_streams = require('./utils/streams.cjs');
|
|
34
|
+
var import_style = require('./utils/style.cjs');
|
|
35
|
+
var import_templates = require('./utils/templates.cjs');
|
|
36
|
+
const SUPPORTED_SOURCE_TYPES = (
|
|
37
|
+
/** @type {const} */
|
|
38
|
+
[
|
|
39
|
+
"raster",
|
|
40
|
+
"vector",
|
|
41
|
+
"geojson"
|
|
42
|
+
]
|
|
43
|
+
);
|
|
44
|
+
class Writer extends import_events.EventEmitter {
|
|
45
|
+
#zipWriter = new import_zip_writer.ZipWriter();
|
|
46
|
+
/** @type {Set<string>} */
|
|
47
|
+
#addedFiles = /* @__PURE__ */ new Set();
|
|
48
|
+
/** @type {Set<string>} */
|
|
49
|
+
#fonts = /* @__PURE__ */ new Set();
|
|
50
|
+
/** @type {Set<string>} */
|
|
51
|
+
#addedSpriteIds = /* @__PURE__ */ new Set();
|
|
52
|
+
/** @type {Map<string, SourceInfo>} */
|
|
53
|
+
#sources = /* @__PURE__ */ new Map();
|
|
54
|
+
/** @type {StyleSpecification} */
|
|
55
|
+
#style;
|
|
56
|
+
/** @type {ReadableStream<Uint8Array>} */
|
|
57
|
+
#outputStream;
|
|
58
|
+
/** @type {ReadableStreamDefaultController<Uint8Array>} */
|
|
59
|
+
#outputController;
|
|
60
|
+
static SUPPORTED_SOURCE_TYPES = SUPPORTED_SOURCE_TYPES;
|
|
61
|
+
/**
|
|
62
|
+
* @param {any} style A v7 or v8 MapLibre style. v7 styles will be migrated to
|
|
63
|
+
* v8. (There are currently no typescript declarations for v7 styles, hence
|
|
64
|
+
* this is typed as `any` and validated internally)
|
|
65
|
+
*/
|
|
66
|
+
constructor(style) {
|
|
67
|
+
super();
|
|
68
|
+
if (!style || !("version" in style)) {
|
|
69
|
+
throw new Error("Invalid style");
|
|
70
|
+
}
|
|
71
|
+
if (style.version !== 7 && style.version !== 8) {
|
|
72
|
+
throw new Error(`Invalid style: Unsupported version v${style.version}`);
|
|
73
|
+
}
|
|
74
|
+
if (!Array.isArray(style.layers)) {
|
|
75
|
+
throw new Error("Invalid style: missing layers property");
|
|
76
|
+
}
|
|
77
|
+
const styleCopy = (0, import_misc.clone)(style);
|
|
78
|
+
(0, import_maplibre_gl_style_spec.migrate)(styleCopy);
|
|
79
|
+
const errors = (0, import_maplibre_gl_style_spec.validateStyleMin)(styleCopy);
|
|
80
|
+
if (errors.length) {
|
|
81
|
+
throw new AggregateError(errors, "Invalid style");
|
|
82
|
+
}
|
|
83
|
+
this.#style = styleCopy;
|
|
84
|
+
for (const [sourceId, source] of Object.entries(this.#style.sources)) {
|
|
85
|
+
if (source.type !== "geojson") continue;
|
|
86
|
+
this.#addSource(sourceId, source);
|
|
87
|
+
}
|
|
88
|
+
const zipReader = this.#zipWriter.readable.getReader();
|
|
89
|
+
let outputController;
|
|
90
|
+
this.#outputStream = new ReadableStream({
|
|
91
|
+
start(controller) {
|
|
92
|
+
outputController = controller;
|
|
93
|
+
},
|
|
94
|
+
async pull(controller) {
|
|
95
|
+
try {
|
|
96
|
+
const { done, value } = await zipReader.read();
|
|
97
|
+
if (done) {
|
|
98
|
+
controller.close();
|
|
99
|
+
} else {
|
|
100
|
+
controller.enqueue(
|
|
101
|
+
/** @type {Uint8Array} */
|
|
102
|
+
value
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
} catch (err) {
|
|
106
|
+
controller.error(err);
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
cancel(reason) {
|
|
110
|
+
zipReader.cancel(reason);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
this.#outputController = outputController;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* @returns {ReadableStream<Uint8Array>} Readable stream of the styled map package
|
|
117
|
+
*/
|
|
118
|
+
get outputStream() {
|
|
119
|
+
return this.#outputStream;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Abort the output stream with an error. Call this if an error occurs during
|
|
123
|
+
* writing to propagate the error to consumers of `outputStream`.
|
|
124
|
+
*
|
|
125
|
+
* @param {Error} reason
|
|
126
|
+
*/
|
|
127
|
+
abort(reason) {
|
|
128
|
+
this.#outputController.error(reason);
|
|
129
|
+
}
|
|
130
|
+
#getBounds() {
|
|
131
|
+
let bounds;
|
|
132
|
+
let maxzoom = 0;
|
|
133
|
+
for (const { source } of this.#sources.values()) {
|
|
134
|
+
if (source.type === "geojson") {
|
|
135
|
+
if (isEmptyFeatureCollection(source.data)) continue;
|
|
136
|
+
const bbox2 = get2DBBox(source.data.bbox);
|
|
137
|
+
bounds = bounds ? (0, import_geo.unionBBox)([bounds, bbox2]) : [...bbox2];
|
|
138
|
+
} else {
|
|
139
|
+
if (source.maxzoom < maxzoom) continue;
|
|
140
|
+
if (source.maxzoom === maxzoom) {
|
|
141
|
+
bounds = bounds ? (0, import_geo.unionBBox)([bounds, source.bounds]) : source.bounds;
|
|
142
|
+
} else {
|
|
143
|
+
bounds = source.bounds;
|
|
144
|
+
maxzoom = source.maxzoom;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return bounds;
|
|
149
|
+
}
|
|
150
|
+
#getMaxZoom() {
|
|
151
|
+
let maxzoom = 0;
|
|
152
|
+
for (const { source } of this.#sources.values()) {
|
|
153
|
+
const sourceMaxzoom = (
|
|
154
|
+
// For GeoJSON sources, the maxzoom is 16 unless otherwise set
|
|
155
|
+
source.type === "geojson" ? source.maxzoom || 16 : source.maxzoom
|
|
156
|
+
);
|
|
157
|
+
maxzoom = Math.max(maxzoom, sourceMaxzoom);
|
|
158
|
+
}
|
|
159
|
+
return maxzoom;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Add a source definition to the styled map package
|
|
163
|
+
*
|
|
164
|
+
* @param {string} sourceId
|
|
165
|
+
* @param {InputSource} source
|
|
166
|
+
* @returns {SourceInfo}
|
|
167
|
+
*/
|
|
168
|
+
#addSource(sourceId, source) {
|
|
169
|
+
const encodedSourceId = encodeSourceId(this.#sources.size);
|
|
170
|
+
const tileSourceOverrides = {
|
|
171
|
+
minzoom: 0,
|
|
172
|
+
maxzoom: 0,
|
|
173
|
+
bounds: (
|
|
174
|
+
/** @type {import('./utils/geo.js').BBox} */
|
|
175
|
+
[...import_geo.MAX_BOUNDS]
|
|
176
|
+
),
|
|
177
|
+
tiles: (
|
|
178
|
+
/** @type {string[]} */
|
|
179
|
+
[]
|
|
180
|
+
)
|
|
181
|
+
};
|
|
182
|
+
let smpSource;
|
|
183
|
+
switch (source.type) {
|
|
184
|
+
case "raster":
|
|
185
|
+
case "vector":
|
|
186
|
+
smpSource = {
|
|
187
|
+
...(0, import_filter_obj.excludeKeys)(source, ["tiles", "url"]),
|
|
188
|
+
...tileSourceOverrides
|
|
189
|
+
};
|
|
190
|
+
break;
|
|
191
|
+
case "geojson":
|
|
192
|
+
smpSource = {
|
|
193
|
+
...source,
|
|
194
|
+
maxzoom: 0,
|
|
195
|
+
data: typeof source.data !== "string" ? (
|
|
196
|
+
// Add a bbox property to the GeoJSON data if it doesn't already have one
|
|
197
|
+
{ ...source.data, bbox: source.data.bbox || (0, import_bbox.bbox)(source.data) }
|
|
198
|
+
) : (
|
|
199
|
+
// If GeoJSON data is referenced by a URL, start with an empty FeatureCollection
|
|
200
|
+
{
|
|
201
|
+
type: "FeatureCollection",
|
|
202
|
+
features: [],
|
|
203
|
+
bbox: [0, 0, 0, 0]
|
|
204
|
+
}
|
|
205
|
+
)
|
|
206
|
+
};
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
const sourceInfo = {
|
|
210
|
+
source: smpSource,
|
|
211
|
+
encodedSourceId
|
|
212
|
+
};
|
|
213
|
+
this.#sources.set(sourceId, sourceInfo);
|
|
214
|
+
return sourceInfo;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Add a tile to the styled map package
|
|
218
|
+
*
|
|
219
|
+
* @param {Source} tileData
|
|
220
|
+
* @param {TileInfo} opts
|
|
221
|
+
*/
|
|
222
|
+
async addTile(tileData, { z, x, y, sourceId, format }) {
|
|
223
|
+
let sourceInfo = this.#sources.get(sourceId);
|
|
224
|
+
if (!sourceInfo) {
|
|
225
|
+
const source2 = this.#style.sources[sourceId];
|
|
226
|
+
if (!source2) {
|
|
227
|
+
throw new Error(`Source not referenced in style.json: ${sourceId}`);
|
|
228
|
+
}
|
|
229
|
+
if (source2.type !== "raster" && source2.type !== "vector") {
|
|
230
|
+
throw new Error(`Unsupported source type: ${source2.type}`);
|
|
231
|
+
}
|
|
232
|
+
sourceInfo = this.#addSource(sourceId, source2);
|
|
233
|
+
}
|
|
234
|
+
const { source, encodedSourceId } = sourceInfo;
|
|
235
|
+
if (source.type !== "raster" && source.type !== "vector") {
|
|
236
|
+
throw new Error(`Unsupported source type: ${source.type}`);
|
|
237
|
+
}
|
|
238
|
+
if (!format) {
|
|
239
|
+
;
|
|
240
|
+
[format, tileData] = await (0, import_file_formats.getTileFormatFromStream)(toWebStream(tileData));
|
|
241
|
+
}
|
|
242
|
+
if (!sourceInfo.format) {
|
|
243
|
+
sourceInfo.format = format;
|
|
244
|
+
} else if (sourceInfo.format !== format) {
|
|
245
|
+
throw new Error(
|
|
246
|
+
`Tile format mismatch for source ${sourceId}: expected ${sourceInfo.format}, got ${format}`
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
const bbox2 = (0, import_geo.tileToBBox)({ z, x, y });
|
|
250
|
+
if (z > source.maxzoom) {
|
|
251
|
+
source.maxzoom = z;
|
|
252
|
+
source.bounds = bbox2;
|
|
253
|
+
} else if (z === source.maxzoom) {
|
|
254
|
+
source.bounds = (0, import_geo.unionBBox)([source.bounds, bbox2]);
|
|
255
|
+
}
|
|
256
|
+
const name = (0, import_templates.getTileFilename)({ sourceId: encodedSourceId, z, x, y, format });
|
|
257
|
+
return this.#append(tileData, { name, store: true });
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Create a write stream for adding tiles to the styled map package
|
|
261
|
+
*
|
|
262
|
+
* @param {object} opts
|
|
263
|
+
* @param {number} [opts.concurrency=16] The number of concurrent writes
|
|
264
|
+
*
|
|
265
|
+
* @returns
|
|
266
|
+
*/
|
|
267
|
+
createTileWriteStream({ concurrency = 16 } = {}) {
|
|
268
|
+
return (0, import_streams.writeStreamFromAsync)(this.addTile.bind(this), { concurrency });
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Add a sprite to the styled map package
|
|
272
|
+
*
|
|
273
|
+
* @param {object} options
|
|
274
|
+
* @param {Source} options.json
|
|
275
|
+
* @param {Source} options.png
|
|
276
|
+
* @param {number} [options.pixelRatio]
|
|
277
|
+
* @param {string} [options.id='default']
|
|
278
|
+
* @returns {Promise<void>}
|
|
279
|
+
*/
|
|
280
|
+
async addSprite({ json, png, pixelRatio = 1, id = "default" }) {
|
|
281
|
+
this.#addedSpriteIds.add(id);
|
|
282
|
+
const jsonName = (0, import_templates.getSpriteFilename)({ id, pixelRatio, ext: ".json" });
|
|
283
|
+
const pngName = (0, import_templates.getSpriteFilename)({ id, pixelRatio, ext: ".png" });
|
|
284
|
+
await Promise.all([
|
|
285
|
+
this.#append(json, { name: jsonName }),
|
|
286
|
+
this.#append(png, { name: pngName })
|
|
287
|
+
]);
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Add glyphs to the styled map package
|
|
291
|
+
*
|
|
292
|
+
* @param {Source} glyphData
|
|
293
|
+
* @param {GlyphInfo} glyphInfo
|
|
294
|
+
* @returns {Promise<void>}
|
|
295
|
+
*/
|
|
296
|
+
addGlyphs(glyphData, { font: fontName, range }) {
|
|
297
|
+
this.#fonts.add(fontName);
|
|
298
|
+
const name = (0, import_templates.getGlyphFilename)({ fontstack: fontName, range });
|
|
299
|
+
return this.#append(glyphData, { name });
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Create a write stream for adding glyphs to the styled map package
|
|
303
|
+
*
|
|
304
|
+
* @param {object} opts
|
|
305
|
+
* @param {number} [opts.concurrency=16] The number of concurrent writes
|
|
306
|
+
* @returns
|
|
307
|
+
*/
|
|
308
|
+
createGlyphWriteStream({ concurrency = 16 } = {}) {
|
|
309
|
+
return (0, import_streams.writeStreamFromAsync)(this.addGlyphs.bind(this), { concurrency });
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Finalize the styled map package and write the style to the archive.
|
|
313
|
+
* This method must be called to complete the archive.
|
|
314
|
+
* You must wait for your destination write stream to 'finish' before using the output.
|
|
315
|
+
*/
|
|
316
|
+
async finish() {
|
|
317
|
+
await this.#append(import_templates.FORMAT_VERSION, { name: import_templates.VERSION_FILE });
|
|
318
|
+
this.#prepareStyle();
|
|
319
|
+
const style = JSON.stringify(this.#style);
|
|
320
|
+
await this.#append(style, { name: import_templates.STYLE_FILE });
|
|
321
|
+
const entries = await this.#zipWriter.entries();
|
|
322
|
+
const sortedEntries = sortEntries(entries);
|
|
323
|
+
await this.#zipWriter.finalize({ entries: sortedEntries });
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Mutates the style object to prepare it for writing to the archive.
|
|
327
|
+
* Deterministic: can be run more than once with the same result.
|
|
328
|
+
*/
|
|
329
|
+
#prepareStyle() {
|
|
330
|
+
if (this.#sources.size === 0) {
|
|
331
|
+
throw new Error("Missing sources: add at least one source");
|
|
332
|
+
}
|
|
333
|
+
if (this.#style.glyphs && this.#fonts.size === 0) {
|
|
334
|
+
throw new Error(
|
|
335
|
+
"Missing fonts: style references glyphs but no fonts added"
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
(0, import_style.replaceFontStacks)(this.#style, [...this.#fonts]);
|
|
339
|
+
if (this.#style.glyphs) {
|
|
340
|
+
this.#style.glyphs = import_templates.GLYPH_URI;
|
|
341
|
+
}
|
|
342
|
+
if (typeof this.#style.sprite === "string") {
|
|
343
|
+
if (!this.#addedSpriteIds.has("default")) {
|
|
344
|
+
throw new Error(
|
|
345
|
+
"Missing sprite: style references sprite but none added"
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
this.#style.sprite = (0, import_templates.getSpriteUri)();
|
|
349
|
+
} else if (Array.isArray(this.#style.sprite)) {
|
|
350
|
+
this.#style.sprite = this.#style.sprite.map(({ id }) => {
|
|
351
|
+
if (!this.#addedSpriteIds.has(id)) {
|
|
352
|
+
throw new Error(
|
|
353
|
+
`Missing sprite: style references sprite ${id} but none added`
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
return { id, url: (0, import_templates.getSpriteUri)(id) };
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
this.#style.sources = {};
|
|
360
|
+
for (const [sourceId, { source, encodedSourceId, format = "mvt" }] of this.#sources) {
|
|
361
|
+
if (source.type === "geojson" && isEmptyFeatureCollection(source.data)) {
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
this.#style.sources[sourceId] = source;
|
|
365
|
+
if (!("tiles" in source)) continue;
|
|
366
|
+
source.tiles = [(0, import_templates.getTileUri)({ sourceId: encodedSourceId, format })];
|
|
367
|
+
}
|
|
368
|
+
this.#style.layers = this.#style.layers.filter(
|
|
369
|
+
(layer) => !("source" in layer) || !!this.#style.sources[layer.source]
|
|
370
|
+
);
|
|
371
|
+
const metadata = this.#style.metadata || (this.#style.metadata = {});
|
|
372
|
+
const bounds = this.#getBounds();
|
|
373
|
+
if (bounds) {
|
|
374
|
+
metadata["smp:bounds"] = bounds;
|
|
375
|
+
const [w, s, e, n] = bounds;
|
|
376
|
+
this.#style.center = [w + (e - w) / 2, s + (n - s) / 2];
|
|
377
|
+
}
|
|
378
|
+
metadata["smp:maxzoom"] = this.#getMaxZoom();
|
|
379
|
+
metadata["smp:sourceFolders"] = {};
|
|
380
|
+
for (const [sourceId, { encodedSourceId }] of this.#sources) {
|
|
381
|
+
metadata["smp:sourceFolders"][sourceId] = encodedSourceId;
|
|
382
|
+
}
|
|
383
|
+
this.#style.zoom = Math.max(0, this.#getMaxZoom() - 2);
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
*
|
|
387
|
+
* @param {Source} source
|
|
388
|
+
* @param {{ name: string, store?: boolean }} options
|
|
389
|
+
* @returns {Promise<void>}
|
|
390
|
+
*/
|
|
391
|
+
async #append(source, { name, store = false }) {
|
|
392
|
+
if (this.#addedFiles.has(name)) {
|
|
393
|
+
throw new Error(`${name} already added`);
|
|
394
|
+
}
|
|
395
|
+
this.#addedFiles.add(name);
|
|
396
|
+
const readable = toWebStream(source);
|
|
397
|
+
await this.#zipWriter.addEntry({ readable, name, store });
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
function encodeSourceId(sourceIndex) {
|
|
401
|
+
return sourceIndex.toString(36);
|
|
402
|
+
}
|
|
403
|
+
function toWebStream(source) {
|
|
404
|
+
if (typeof source === "string") {
|
|
405
|
+
const bytes = new TextEncoder().encode(source);
|
|
406
|
+
return new ReadableStream({
|
|
407
|
+
start(controller) {
|
|
408
|
+
controller.enqueue(bytes);
|
|
409
|
+
controller.close();
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
if (source instanceof Uint8Array) {
|
|
414
|
+
return new ReadableStream({
|
|
415
|
+
start(controller) {
|
|
416
|
+
controller.enqueue(
|
|
417
|
+
new Uint8Array(source.buffer, source.byteOffset, source.byteLength)
|
|
418
|
+
);
|
|
419
|
+
controller.close();
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
return (
|
|
424
|
+
/** @type {ReadableStream<Uint8Array>} */
|
|
425
|
+
source
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
function isEmptyFeatureCollection(data) {
|
|
429
|
+
return data.type === "FeatureCollection" && data.features.length === 0;
|
|
430
|
+
}
|
|
431
|
+
function get2DBBox(bbox2) {
|
|
432
|
+
if (bbox2.length === 4) return bbox2;
|
|
433
|
+
return [bbox2[0], bbox2[1], bbox2[3], bbox2[4]];
|
|
434
|
+
}
|
|
435
|
+
function sortEntries(entries) {
|
|
436
|
+
return [...entries].sort((a, b) => {
|
|
437
|
+
if (a.name === import_templates.VERSION_FILE) return -1;
|
|
438
|
+
if (b.name === import_templates.VERSION_FILE) return 1;
|
|
439
|
+
if (a.name === import_templates.STYLE_FILE) return -1;
|
|
440
|
+
if (b.name === import_templates.STYLE_FILE) return 1;
|
|
441
|
+
const foldersA = a.name.split("/");
|
|
442
|
+
const foldersB = b.name.split("/");
|
|
443
|
+
const aIsFirst = foldersA[0] === import_templates.FONTS_FOLDER && foldersA[2] === "0-255.pbf.gz";
|
|
444
|
+
const bIsFirst = foldersB[0] === import_templates.FONTS_FOLDER && foldersB[2] === "0-255.pbf.gz";
|
|
445
|
+
if (aIsFirst && bIsFirst)
|
|
446
|
+
return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
|
|
447
|
+
if (aIsFirst) return -1;
|
|
448
|
+
if (bIsFirst) return 1;
|
|
449
|
+
if (foldersA[0] === import_templates.SOURCES_FOLDER && foldersB[0] !== import_templates.SOURCES_FOLDER)
|
|
450
|
+
return -1;
|
|
451
|
+
if (foldersB[0] === import_templates.SOURCES_FOLDER && foldersA[0] !== import_templates.SOURCES_FOLDER)
|
|
452
|
+
return 1;
|
|
453
|
+
if (foldersA[0] === import_templates.SOURCES_FOLDER && foldersB[0] === import_templates.SOURCES_FOLDER) {
|
|
454
|
+
const zoomA = +foldersA[2];
|
|
455
|
+
const zoomB = +foldersB[2];
|
|
456
|
+
return zoomA - zoomB;
|
|
457
|
+
}
|
|
458
|
+
return 0;
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
462
|
+
0 && (module.exports = {
|
|
463
|
+
SUPPORTED_SOURCE_TYPES,
|
|
464
|
+
Writer
|
|
465
|
+
});
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { G as GlyphInfo, d as GlyphRange, e as SUPPORTED_SOURCE_TYPES, f as Source, g as SourceInfo, c as TileFormat, T as TileInfo, W as Writer } from './types-CJq90eOB.cjs';
|
|
2
|
+
import 'events';
|
|
3
|
+
import '@maplibre/maplibre-gl-style-spec';
|
|
4
|
+
import 'geojson';
|
|
5
|
+
import 'type-fest';
|
package/dist/writer.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { G as GlyphInfo, d as GlyphRange, e as SUPPORTED_SOURCE_TYPES, f as Source, g as SourceInfo, c as TileFormat, T as TileInfo, W as Writer } from './types-CJq90eOB.js';
|
|
2
|
+
import 'events';
|
|
3
|
+
import '@maplibre/maplibre-gl-style-spec';
|
|
4
|
+
import 'geojson';
|
|
5
|
+
import 'type-fest';
|