@loaders.gl/mvt
Version:
Loader for Mapbox Vector Tiles
1,648 lines (1,626 loc) • 77.5 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
// dist/index.js
var dist_exports = {};
__export(dist_exports, {
MVTLoader: () => MVTLoader,
MVTSource: () => MVTSource,
MVTWorkerLoader: () => MVTWorkerLoader,
MVTWriter: () => MVTWriter,
TableTileSource: () => TableTileSource,
TileJSONLoader: () => TileJSONLoader
});
module.exports = __toCommonJS(dist_exports);
// dist/lib/get-schemas-from-tilejson.js
function getSchemaFromTileJSONLayer(layer) {
const fields = [];
if (layer.fields) {
for (const field of layer.fields) {
fields.push({
name: field.name,
type: getDataTypeFromTileJSONField(field),
metadata: getMetadataFromTileJSONField(field)
});
}
}
return {
metadata: getMetadataFromTileJSONLayer(layer),
fields
};
}
function getMetadataFromTileJSONLayer(layer) {
const metadata = {};
for (const [key, value] of Object.entries(layer)) {
if (key !== "fields" && value) {
metadata[key] = JSON.stringify(value);
}
}
return metadata;
}
function getDataTypeFromTileJSONField(field) {
switch (field.type.toLowerCase()) {
case "float32":
return "float32";
case "number":
case "float64":
return "float64";
case "string":
case "utf8":
return "utf8";
case "boolean":
return "bool";
default:
return "null";
}
}
function getMetadataFromTileJSONField(field) {
const metadata = {};
for (const [key, value] of Object.entries(field)) {
if (key !== "name" && value) {
metadata[key] = JSON.stringify(value);
}
}
return metadata;
}
// dist/lib/parse-tilejson.js
var isObject = (x) => x !== null && typeof x === "object";
function parseTileJSON(jsonMetadata, options) {
var _a;
if (!jsonMetadata || !isObject(jsonMetadata)) {
return null;
}
let tileJSON = {
name: jsonMetadata.name || "",
description: jsonMetadata.description || ""
};
if (typeof jsonMetadata.generator === "string") {
tileJSON.generator = jsonMetadata.generator;
}
if (typeof jsonMetadata.generator_options === "string") {
tileJSON.generatorOptions = jsonMetadata.generator_options;
}
tileJSON.boundingBox = parseBounds(jsonMetadata.bounds) || parseBounds(jsonMetadata.antimeridian_adjusted_bounds);
tileJSON.center = parseCenter(jsonMetadata.center);
tileJSON.maxZoom = safeParseFloat(jsonMetadata.maxzoom);
tileJSON.minZoom = safeParseFloat(jsonMetadata.minzoom);
if (typeof (jsonMetadata == null ? void 0 : jsonMetadata.json) === "string") {
try {
tileJSON.metaJson = JSON.parse(jsonMetadata.json);
} catch (error) {
console.warn("Failed to parse tilejson.json field", error);
}
}
const tilestats = jsonMetadata.tilestats || ((_a = tileJSON.metaJson) == null ? void 0 : _a.tilestats);
const tileStatsLayers = parseTilestatsLayers(tilestats, options);
const tileJSONlayers = parseTileJSONLayers(jsonMetadata.vector_layers);
const layers = mergeLayers(tileJSONlayers, tileStatsLayers);
tileJSON = {
...tileJSON,
layers
};
if (tileJSON.maxZoom === null && layers.length > 0) {
tileJSON.maxZoom = layers[0].maxZoom || null;
}
if (tileJSON.minZoom === null && layers.length > 0) {
tileJSON.minZoom = layers[0].minZoom || null;
}
return tileJSON;
}
function parseTileJSONLayers(layers) {
if (!Array.isArray(layers)) {
return [];
}
return layers.map((layer) => parseTileJSONLayer(layer));
}
function parseTileJSONLayer(layer) {
const fields = Object.entries(layer.fields || []).map(([key, datatype]) => ({
name: key,
...attributeTypeToFieldType(String(datatype))
}));
const layer2 = { ...layer };
delete layer2.fields;
return {
name: layer.id || "",
...layer2,
fields
};
}
function parseTilestatsLayers(tilestats, options) {
if (isObject(tilestats) && Array.isArray(tilestats.layers)) {
return tilestats.layers.map((layer) => parseTilestatsForLayer(layer, options));
}
return [];
}
function parseTilestatsForLayer(layer, options) {
const fields = [];
const indexedAttributes = {};
const attributes = layer.attributes || [];
for (const attribute of attributes) {
const name = attribute.attribute;
if (typeof name === "string") {
if (name.split("|").length > 1) {
const fname = name.split("|")[0];
indexedAttributes[fname] = indexedAttributes[fname] || [];
indexedAttributes[fname].push(attribute);
console.warn("ignoring tilestats indexed field", fname);
} else if (!fields[name]) {
fields.push(attributeToField(attribute, options));
} else {
}
}
}
return {
name: layer.layer || "",
dominantGeometry: layer.geometry,
fields
};
}
function mergeLayers(layers, tilestatsLayers) {
return layers.map((layer) => {
const tilestatsLayer = tilestatsLayers.find((tsLayer) => tsLayer.name === layer.name);
const fields = (tilestatsLayer == null ? void 0 : tilestatsLayer.fields) || layer.fields || [];
const mergedLayer = {
...layer,
...tilestatsLayer,
fields
};
mergedLayer.schema = getSchemaFromTileJSONLayer(mergedLayer);
return mergedLayer;
});
}
function parseBounds(bounds) {
const result = fromArrayOrString(bounds);
if (Array.isArray(result) && result.length === 4 && [result[0], result[2]].every(isLng) && [result[1], result[3]].every(isLat)) {
return [
[result[0], result[1]],
[result[2], result[3]]
];
}
return void 0;
}
function parseCenter(center) {
const result = fromArrayOrString(center);
if (Array.isArray(result) && result.length === 3 && isLng(result[0]) && isLat(result[1]) && isZoom(result[2])) {
return result;
}
return null;
}
function safeParseFloat(input) {
const result = typeof input === "string" ? parseFloat(input) : typeof input === "number" ? input : null;
return result === null || isNaN(result) ? null : result;
}
function isLat(num) {
return Number.isFinite(num) && num <= 90 && num >= -90;
}
function isLng(num) {
return Number.isFinite(num) && num <= 180 && num >= -180;
}
function isZoom(num) {
return Number.isFinite(num) && num >= 0 && num <= 22;
}
function fromArrayOrString(data) {
if (typeof data === "string") {
return data.split(",").map(parseFloat);
} else if (Array.isArray(data)) {
return data;
}
return null;
}
var attrTypeMap = {
number: {
type: "float32"
},
numeric: {
type: "float32"
},
string: {
type: "utf8"
},
vachar: {
type: "utf8"
},
float: {
type: "float32"
},
int: {
type: "int32"
},
int4: {
type: "int32"
},
boolean: {
type: "boolean"
},
bool: {
type: "boolean"
}
};
function attributeToField(attribute = {}, options) {
var _a;
const fieldTypes = attributeTypeToFieldType(attribute.type);
const field = {
name: attribute.attribute,
// what happens if attribute type is string...
// filterProps: getFilterProps(fieldTypes.type, attribute),
...fieldTypes
};
if (typeof attribute.min === "number") {
field.min = attribute.min;
}
if (typeof attribute.max === "number") {
field.max = attribute.max;
}
if (typeof attribute.count === "number") {
field.uniqueValueCount = attribute.count;
}
if (attribute.values) {
field.values = attribute.values;
}
if (field.values && typeof options.maxValues === "number") {
field.values = (_a = field.values) == null ? void 0 : _a.slice(0, options.maxValues);
}
return field;
}
function attributeTypeToFieldType(aType) {
const type = aType.toLowerCase();
if (!type || !attrTypeMap[type]) {
}
return attrTypeMap[type] || { type: "string" };
}
// dist/tilejson-loader.js
var VERSION = true ? "4.4.2" : "latest";
var TileJSONLoader = {
dataType: null,
batchType: null,
name: "TileJSON",
id: "tilejson",
module: "pmtiles",
version: VERSION,
worker: true,
extensions: ["json"],
mimeTypes: ["application/json"],
text: true,
options: {
tilejson: {
maxValues: void 0
}
},
parse: async (arrayBuffer, options) => {
const jsonString = new TextDecoder().decode(arrayBuffer);
const json = JSON.parse(jsonString);
const tilejsonOptions = { ...TileJSONLoader.options.tilejson, ...options == null ? void 0 : options.tilejson };
return parseTileJSON(json, tilejsonOptions);
},
parseTextSync: (text, options) => {
const json = JSON.parse(text);
const tilejsonOptions = { ...TileJSONLoader.options.tilejson, ...options == null ? void 0 : options.tilejson };
return parseTileJSON(json, tilejsonOptions);
}
};
// dist/lib/parse-mvt.js
var import_gis = require("@loaders.gl/gis");
var import_loader_utils = require("@loaders.gl/loader-utils");
var import_pbf = __toESM(require("pbf"), 1);
// dist/lib/utils/geometry-utils.js
var import_polygon = require("@math.gl/polygon");
function signedArea(ring) {
let sum = 0;
for (let i = 0, j = ring.length - 1, p1, p2; i < ring.length; j = i++) {
p1 = ring[i];
p2 = ring[j];
sum += (p2[0] - p1[0]) * (p1[1] + p2[1]);
}
return sum;
}
function convertToLocalCoordinates(coordinates, extent) {
if (Array.isArray(coordinates[0])) {
for (const subcoords of coordinates) {
convertToLocalCoordinates(subcoords, extent);
}
return;
}
const p = coordinates;
p[0] /= extent;
p[1] /= extent;
}
function convertToLocalCoordinatesFlat(data, extent) {
for (let i = 0; i < data.length; ++i) {
data[i] /= extent;
}
}
function projectToLngLat(line, tileIndex, extent) {
if (typeof line[0][0] !== "number") {
for (const point of line) {
projectToLngLat(point, tileIndex, extent);
}
return;
}
const size = extent * Math.pow(2, tileIndex.z);
const x0 = extent * tileIndex.x;
const y0 = extent * tileIndex.y;
for (let j = 0; j < line.length; j++) {
const p = line[j];
p[0] = (p[0] + x0) * 360 / size - 180;
const y2 = 180 - (p[1] + y0) * 360 / size;
p[1] = 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90;
}
}
function projectToLngLatFlat(data, tileIndex, extent) {
const { x, y, z } = tileIndex;
const size = extent * Math.pow(2, z);
const x0 = extent * x;
const y0 = extent * y;
for (let j = 0, jl = data.length; j < jl; j += 2) {
data[j] = (data[j] + x0) * 360 / size - 180;
const y2 = 180 - (data[j + 1] + y0) * 360 / size;
data[j + 1] = 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90;
}
}
function classifyRings(rings) {
const len = rings.length;
if (len <= 1)
return [rings];
const polygons = [];
let polygon;
let ccw;
for (let i = 0; i < len; i++) {
const area = signedArea(rings[i]);
if (area === 0)
continue;
if (ccw === void 0)
ccw = area < 0;
if (ccw === area < 0) {
if (polygon)
polygons.push(polygon);
polygon = [rings[i]];
} else if (polygon)
polygon.push(rings[i]);
}
if (polygon)
polygons.push(polygon);
return polygons;
}
function classifyRingsFlat(geom) {
const len = geom.indices.length;
const type = "Polygon";
if (len <= 1) {
return {
type,
data: geom.data,
areas: [[(0, import_polygon.getPolygonSignedArea)(geom.data)]],
indices: [geom.indices]
};
}
const areas = [];
const polygons = [];
let ringAreas = [];
let polygon = [];
let ccw;
let offset = 0;
for (let endIndex, i = 0, startIndex; i < len; i++) {
startIndex = geom.indices[i] - offset;
endIndex = geom.indices[i + 1] - offset || geom.data.length;
const shape = geom.data.slice(startIndex, endIndex);
const area = (0, import_polygon.getPolygonSignedArea)(shape);
if (area === 0) {
const before = geom.data.slice(0, startIndex);
const after = geom.data.slice(endIndex);
geom.data = before.concat(after);
offset += endIndex - startIndex;
continue;
}
if (ccw === void 0)
ccw = area < 0;
if (ccw === area < 0) {
if (polygon.length) {
areas.push(ringAreas);
polygons.push(polygon);
}
polygon = [startIndex];
ringAreas = [area];
} else {
ringAreas.push(area);
polygon.push(startIndex);
}
}
if (ringAreas)
areas.push(ringAreas);
if (polygon.length)
polygons.push(polygon);
return { type, areas, indices: polygons, data: geom.data };
}
// dist/lib/vector-tile/vector-tile-feature.js
var VectorTileFeature = class {
properties;
extent;
type;
id;
_pbf;
_geometry;
_keys;
_values;
_geometryInfo;
// eslint-disable-next-line max-params
constructor(pbf, end, extent, keys, values, geometryInfo) {
this.properties = {};
this.extent = extent;
this.type = 0;
this.id = null;
this._pbf = pbf;
this._geometry = -1;
this._keys = keys;
this._values = values;
this._geometryInfo = geometryInfo;
pbf.readFields(readFeature, this, end);
}
toGeoJSONFeature(coordinates, tileIndex) {
const coords = this.loadGeometry();
switch (coordinates) {
case "wgs84":
return _toGeoJSONFeature(this, coords, (line) => projectToLngLat(line, tileIndex, this.extent));
default:
return _toGeoJSONFeature(this, coords, convertToLocalCoordinates);
}
}
/**
*
* @param options
* @returns
*/
toBinaryFeature(coordinates, tileIndex) {
const geom = this.loadFlatGeometry();
switch (coordinates) {
case "wgs84":
return this._toBinaryCoordinates(geom, (coords) => projectToLngLatFlat(coords, tileIndex, this.extent));
default:
return this._toBinaryCoordinates(geom, convertToLocalCoordinatesFlat);
}
}
/** Read a bounding box from the feature */
// eslint-disable-next-line max-statements
bbox() {
const pbf = this._pbf;
pbf.pos = this._geometry;
const end = pbf.readVarint() + pbf.pos;
let cmd = 1;
let length = 0;
let x = 0;
let y = 0;
let x1 = Infinity;
let x2 = -Infinity;
let y1 = Infinity;
let y2 = -Infinity;
while (pbf.pos < end) {
if (length <= 0) {
const cmdLen = pbf.readVarint();
cmd = cmdLen & 7;
length = cmdLen >> 3;
}
length--;
if (cmd === 1 || cmd === 2) {
x += pbf.readSVarint();
y += pbf.readSVarint();
if (x < x1)
x1 = x;
if (x > x2)
x2 = x;
if (y < y1)
y1 = y;
if (y > y2)
y2 = y;
} else if (cmd !== 7) {
throw new Error(`unknown command ${cmd}`);
}
}
return [x1, y1, x2, y2];
}
// BINARY HELPERS
/**
*
* @param transform
* @returns result
*/
_toBinaryCoordinates(geom, transform) {
let geometry;
transform(geom.data, this.extent);
const coordLength = 2;
switch (this.type) {
case 1:
this._geometryInfo.pointFeaturesCount++;
this._geometryInfo.pointPositionsCount += geom.indices.length;
geometry = { type: "Point", ...geom };
break;
case 2:
this._geometryInfo.lineFeaturesCount++;
this._geometryInfo.linePathsCount += geom.indices.length;
this._geometryInfo.linePositionsCount += geom.data.length / coordLength;
geometry = { type: "LineString", ...geom };
break;
case 3:
geometry = classifyRingsFlat(geom);
this._geometryInfo.polygonFeaturesCount++;
this._geometryInfo.polygonObjectsCount += geometry.indices.length;
for (const indices of geometry.indices) {
this._geometryInfo.polygonRingsCount += indices.length;
}
this._geometryInfo.polygonPositionsCount += geometry.data.length / coordLength;
break;
default:
throw new Error(`Invalid geometry type: ${this.type}`);
}
const result = { type: "Feature", geometry, properties: this.properties };
if (this.id !== null) {
result.id = this.id;
}
return result;
}
// GEOJSON HELPER
// eslint-disable-next-line complexity, max-statements
loadGeometry() {
const pbf = this._pbf;
pbf.pos = this._geometry;
const end = pbf.readVarint() + pbf.pos;
let cmd = 1;
let length = 0;
let x = 0;
let y = 0;
const lines = [];
let line;
while (pbf.pos < end) {
if (length <= 0) {
const cmdLen = pbf.readVarint();
cmd = cmdLen & 7;
length = cmdLen >> 3;
}
length--;
switch (cmd) {
case 1:
case 2:
x += pbf.readSVarint();
y += pbf.readSVarint();
if (cmd === 1) {
if (line)
lines.push(line);
line = [];
}
if (line)
line.push([x, y]);
break;
case 7:
if (line) {
line.push(line[0].slice());
}
break;
default:
throw new Error(`unknown command ${cmd}`);
}
}
if (line)
lines.push(line);
return lines;
}
/**
* Expands the protobuf data to an intermediate Flat GeoJSON
* data format, which maps closely to the binary data buffers.
* It is similar to GeoJSON, but rather than storing the coordinates
* in multidimensional arrays, we have a 1D `data` with all the
* coordinates, and then index into this using the `indices`
* parameter, e.g.
*
* geometry: {
* type: 'Point', data: [1,2], indices: [0]
* }
* geometry: {
* type: 'LineString', data: [1,2,3,4,...], indices: [0]
* }
* geometry: {
* type: 'Polygon', data: [1,2,3,4,...], indices: [[0, 2]]
* }
* Thus the indices member lets us look up the relevant range
* from the data array.
* The Multi* versions of the above types share the same data
* structure, just with multiple elements in the indices array
*/
// eslint-disable-next-line complexity, max-statements
loadFlatGeometry() {
const pbf = this._pbf;
pbf.pos = this._geometry;
const endPos = pbf.readVarint() + pbf.pos;
let cmd = 1;
let cmdLen;
let length = 0;
let x = 0;
let y = 0;
let i = 0;
const indices = [];
const data = [];
while (pbf.pos < endPos) {
if (length <= 0) {
cmdLen = pbf.readVarint();
cmd = cmdLen & 7;
length = cmdLen >> 3;
}
length--;
if (cmd === 1 || cmd === 2) {
x += pbf.readSVarint();
y += pbf.readSVarint();
if (cmd === 1) {
indices.push(i);
}
data.push(x, y);
i += 2;
} else if (cmd === 7) {
if (i > 0) {
const start = indices[indices.length - 1];
data.push(data[start], data[start + 1]);
i += 2;
}
} else {
throw new Error(`unknown command ${cmd}`);
}
}
return { data, indices };
}
};
__publicField(VectorTileFeature, "types", ["Unknown", "Point", "LineString", "Polygon"]);
function _toGeoJSONFeature(vtFeature, coords, transform) {
let type = VectorTileFeature.types[vtFeature.type];
let i;
let j;
let coordinates;
switch (vtFeature.type) {
case 1:
const points = [];
for (i = 0; i < coords.length; i++) {
points[i] = coords[i][0];
}
coordinates = points;
transform(coordinates, vtFeature.extent);
break;
case 2:
coordinates = coords;
for (i = 0; i < coordinates.length; i++) {
transform(coordinates[i], vtFeature.extent);
}
break;
case 3:
coordinates = classifyRings(coords);
for (i = 0; i < coordinates.length; i++) {
for (j = 0; j < coordinates[i].length; j++) {
transform(coordinates[i][j], vtFeature.extent);
}
}
break;
default:
throw new Error("illegal vector tile type");
}
if (coordinates.length === 1) {
coordinates = coordinates[0];
} else {
type = `Multi${type}`;
}
const result = {
type: "Feature",
geometry: {
type,
coordinates
},
properties: vtFeature.properties
};
if (vtFeature.id !== null) {
result.properties ||= {};
result.properties.id = vtFeature.id;
}
return result;
}
function readFeature(tag, feature, pbf) {
if (feature && pbf) {
if (tag === 1)
feature.id = pbf.readVarint();
else if (tag === 2)
readTag(pbf, feature);
else if (tag === 3)
feature.type = pbf.readVarint();
else if (tag === 4)
feature._geometry = pbf.pos;
}
}
function readTag(pbf, feature) {
const end = pbf.readVarint() + pbf.pos;
while (pbf.pos < end) {
const key = feature._keys[pbf.readVarint()];
const value = feature._values[pbf.readVarint()];
feature.properties[key] = value;
}
}
// dist/lib/vector-tile/vector-tile-layer.js
var VectorTileLayer = class {
version;
name;
extent;
length;
_pbf;
_keys;
_values;
_features;
constructor(pbf, end) {
this.version = 1;
this.name = "";
this.extent = 4096;
this.length = 0;
this._pbf = pbf;
this._keys = [];
this._values = [];
this._features = [];
pbf.readFields(readLayer, this, end);
this.length = this._features.length;
}
/**
* return feature `i` from this layer as a `VectorTileFeature`
* @param index
* @returns feature
*/
getGeoJSONFeature(i) {
if (i < 0 || i >= this._features.length) {
throw new Error("feature index out of bounds");
}
this._pbf.pos = this._features[i];
const end = this._pbf.readVarint() + this._pbf.pos;
return new VectorTileFeature(this._pbf, end, this.extent, this._keys, this._values);
}
/**
* return binary feature `i` from this layer as a `VectorTileFeature`
*
* @param index
* @param geometryInfo
* @returns binary feature
*/
getBinaryFeature(i, geometryInfo) {
if (i < 0 || i >= this._features.length) {
throw new Error("feature index out of bounds");
}
this._pbf.pos = this._features[i];
const end = this._pbf.readVarint() + this._pbf.pos;
return new VectorTileFeature(this._pbf, end, this.extent, this._keys, this._values, geometryInfo);
}
};
function readLayer(tag, layer, pbf) {
if (layer && pbf) {
if (tag === 15)
layer.version = pbf.readVarint();
else if (tag === 1)
layer.name = pbf.readString();
else if (tag === 5)
layer.extent = pbf.readVarint();
else if (tag === 2)
layer._features.push(pbf.pos);
else if (tag === 3)
layer._keys.push(pbf.readString());
else if (tag === 4)
layer._values.push(readValueMessage(pbf));
}
}
function readValueMessage(pbf) {
let value = null;
const end = pbf.readVarint() + pbf.pos;
while (pbf.pos < end) {
const tag = pbf.readVarint() >> 3;
value = tag === 1 ? pbf.readString() : tag === 2 ? pbf.readFloat() : tag === 3 ? pbf.readDouble() : tag === 4 ? pbf.readVarint64() : tag === 5 ? pbf.readVarint() : tag === 6 ? pbf.readSVarint() : tag === 7 ? pbf.readBoolean() : null;
}
return value;
}
// dist/lib/vector-tile/vector-tile.js
var VectorTile = class {
layers;
constructor(pbf, end) {
this.layers = pbf.readFields(readTile, {}, end);
}
};
function readTile(tag, layers, pbf) {
if (tag === 3) {
if (pbf) {
const layer = new VectorTileLayer(pbf, pbf.readVarint() + pbf.pos);
if (layer.length && layers) {
layers[layer.name] = layer;
}
}
}
}
// dist/lib/parse-mvt.js
function parseMVT(arrayBuffer, options) {
var _a, _b;
const mvtOptions = checkOptions(options);
const shape = ((_a = options == null ? void 0 : options.gis) == null ? void 0 : _a.format) || ((_b = options == null ? void 0 : options.mvt) == null ? void 0 : _b.shape) || (options == null ? void 0 : options.shape);
switch (shape) {
case "columnar-table":
return { shape: "columnar-table", data: parseToBinary(arrayBuffer, mvtOptions) };
case "geojson-table": {
const table = {
shape: "geojson-table",
type: "FeatureCollection",
features: parseToGeojsonFeatures(arrayBuffer, mvtOptions)
};
return table;
}
case "geojson":
return parseToGeojsonFeatures(arrayBuffer, mvtOptions);
case "binary-geometry":
return parseToBinary(arrayBuffer, mvtOptions);
case "binary":
return parseToBinary(arrayBuffer, mvtOptions);
default:
throw new Error(shape || "undefined shape");
}
}
function parseToBinary(arrayBuffer, options) {
const [flatGeoJsonFeatures, geometryInfo] = parseToFlatGeoJson(arrayBuffer, options);
const binaryData = (0, import_gis.flatGeojsonToBinary)(flatGeoJsonFeatures, geometryInfo);
binaryData.byteLength = arrayBuffer.byteLength;
return binaryData;
}
function parseToFlatGeoJson(arrayBuffer, options) {
const features2 = [];
const geometryInfo = {
coordLength: 2,
pointPositionsCount: 0,
pointFeaturesCount: 0,
linePositionsCount: 0,
linePathsCount: 0,
lineFeaturesCount: 0,
polygonPositionsCount: 0,
polygonObjectsCount: 0,
polygonRingsCount: 0,
polygonFeaturesCount: 0
};
if (arrayBuffer.byteLength <= 0) {
return [features2, geometryInfo];
}
const tile = new VectorTile(new import_pbf.default(arrayBuffer));
const selectedLayers = options && Array.isArray(options.layers) ? options.layers : Object.keys(tile.layers);
selectedLayers.forEach((layerName) => {
const vectorTileLayer = tile.layers[layerName];
if (!vectorTileLayer) {
return;
}
for (let i = 0; i < vectorTileLayer.length; i++) {
const vectorTileFeature = vectorTileLayer.getBinaryFeature(i, geometryInfo);
const decodedFeature = getDecodedFeatureBinary(vectorTileFeature, options, layerName);
features2.push(decodedFeature);
}
});
return [features2, geometryInfo];
}
function parseToGeojsonFeatures(arrayBuffer, options) {
if (arrayBuffer.byteLength <= 0) {
return [];
}
const features2 = [];
const tile = new VectorTile(new import_pbf.default(arrayBuffer));
const selectedLayers = Array.isArray(options.layers) ? options.layers : Object.keys(tile.layers);
selectedLayers.forEach((layerName) => {
const vectorTileLayer = tile.layers[layerName];
if (!vectorTileLayer) {
return;
}
for (let i = 0; i < vectorTileLayer.length; i++) {
const vectorTileFeature = vectorTileLayer.getGeoJSONFeature(i);
const decodedFeature = getDecodedFeature(vectorTileFeature, options, layerName);
features2.push(decodedFeature);
}
});
return features2;
}
function checkOptions(options) {
var _a;
if (!(options == null ? void 0 : options.mvt)) {
throw new Error("mvt options required");
}
if (((_a = options.mvt) == null ? void 0 : _a.coordinates) === "wgs84" && !options.mvt.tileIndex) {
throw new Error("MVT Loader: WGS84 coordinates need tileIndex property");
}
if (options.gis) {
import_loader_utils.log.warn('MVTLoader: "options.gis" is deprecated, use "options.mvt.shape" instead')();
}
return options.mvt;
}
function getDecodedFeature(feature, options, layerName) {
const decodedFeature = feature.toGeoJSONFeature(options.coordinates || "local", options.tileIndex);
if (options.layerProperty) {
decodedFeature.properties ||= {};
decodedFeature.properties[options.layerProperty] = layerName;
}
return decodedFeature;
}
function getDecodedFeatureBinary(feature, options, layerName) {
const decodedFeature = feature.toBinaryFeature(options.coordinates || "local", options.tileIndex);
if (options.layerProperty && decodedFeature.properties) {
decodedFeature.properties[options.layerProperty] = layerName;
}
return decodedFeature;
}
// dist/mvt-format.js
var MVTFormat = {
name: "Mapbox Vector Tile",
id: "mvt",
module: "mvt",
// Note: ArcGIS uses '.pbf' extension and 'application/octet-stream'
extensions: ["mvt", "pbf"],
mimeTypes: [
// https://www.iana.org/assignments/media-types/application/vnd.mapbox-vector-tile
"application/vnd.mapbox-vector-tile",
"application/x-protobuf"
// 'application/octet-stream'
],
category: "geometry"
};
// dist/mvt-loader.js
var VERSION2 = true ? "4.4.2" : "latest";
var MVTWorkerLoader = {
...MVTFormat,
dataType: null,
batchType: null,
version: VERSION2,
worker: true,
options: {
mvt: {
shape: "geojson",
coordinates: "local",
layerProperty: "layerName",
layers: void 0,
tileIndex: void 0
}
}
};
var MVTLoader = {
...MVTWorkerLoader,
parse: async (arrayBuffer, options) => parseMVT(arrayBuffer, options),
parseSync: parseMVT,
binary: true
};
// dist/lib/mapbox-vt-pbf/to-vector-tile.js
var import_pbf2 = __toESM(require("pbf"), 1);
// dist/lib/mvt-pbf/mvt-constants.js
var TileInfo;
(function(TileInfo2) {
TileInfo2[TileInfo2["layers"] = 3] = "layers";
})(TileInfo || (TileInfo = {}));
var LayerInfo;
(function(LayerInfo2) {
LayerInfo2[LayerInfo2["version"] = 15] = "version";
LayerInfo2[LayerInfo2["name"] = 1] = "name";
LayerInfo2[LayerInfo2["features"] = 2] = "features";
LayerInfo2[LayerInfo2["keys"] = 3] = "keys";
LayerInfo2[LayerInfo2["values"] = 4] = "values";
LayerInfo2[LayerInfo2["extent"] = 5] = "extent";
})(LayerInfo || (LayerInfo = {}));
var FeatureInfo;
(function(FeatureInfo2) {
FeatureInfo2[FeatureInfo2["id"] = 1] = "id";
FeatureInfo2[FeatureInfo2["tags"] = 2] = "tags";
FeatureInfo2[FeatureInfo2["type"] = 3] = "type";
FeatureInfo2[FeatureInfo2["geometry"] = 4] = "geometry";
})(FeatureInfo || (FeatureInfo = {}));
var GeometryType;
(function(GeometryType2) {
GeometryType2[GeometryType2["UNKNOWN"] = 0] = "UNKNOWN";
GeometryType2[GeometryType2["POINT"] = 1] = "POINT";
GeometryType2[GeometryType2["LINESTRING"] = 2] = "LINESTRING";
GeometryType2[GeometryType2["POLYGON"] = 3] = "POLYGON";
})(GeometryType || (GeometryType = {}));
var PropertyType;
(function(PropertyType2) {
PropertyType2[PropertyType2["string_value"] = 1] = "string_value";
PropertyType2[PropertyType2["float_value"] = 2] = "float_value";
PropertyType2[PropertyType2["double_value"] = 3] = "double_value";
PropertyType2[PropertyType2["int_value"] = 4] = "int_value";
PropertyType2[PropertyType2["uint_value"] = 5] = "uint_value";
PropertyType2[PropertyType2["sint_value"] = 6] = "sint_value";
PropertyType2[PropertyType2["bool_value"] = 7] = "bool_value";
})(PropertyType || (PropertyType = {}));
var Command;
(function(Command2) {
Command2[Command2["MoveTo"] = 1] = "MoveTo";
Command2[Command2["LineTo"] = 2] = "LineTo";
Command2[Command2["ClosePath"] = 7] = "ClosePath";
})(Command || (Command = {}));
// dist/lib/mvt-pbf/write-mvt-to-pbf.js
function writeMVT(tile, pbf) {
for (const key in tile.layers) {
const context = {
layer: tile.layers[key],
keys: [],
values: [],
keycache: {},
valuecache: {}
};
pbf.writeMessage(TileInfo.layers, writeLayer, context);
}
}
function writeLayer(context, pbf) {
const { layer } = context;
pbf.writeVarintField(LayerInfo.version, layer.version || 1);
pbf.writeStringField(LayerInfo.name, layer.name || "");
pbf.writeVarintField(LayerInfo.extent, layer.extent || 4096);
for (let i = 0; i < layer.length; i++) {
context.feature = layer.feature(i);
pbf.writeMessage(LayerInfo.features, writeFeature, context);
}
const keys = context.keys;
for (let i = 0; i < keys.length; i++) {
pbf.writeStringField(LayerInfo.keys, keys[i]);
}
const values = context.values;
for (let i = 0; i < values.length; i++) {
pbf.writeMessage(LayerInfo.values, writeValue, values[i]);
}
}
function writeFeature(context, pbf) {
const feature = context.feature;
if (feature.id !== void 0) {
pbf.writeVarintField(FeatureInfo.id, feature.id);
}
pbf.writeMessage(FeatureInfo.tags, writeProperties, context);
pbf.writeVarintField(FeatureInfo.type, feature.type);
pbf.writeMessage(FeatureInfo.geometry, writeGeometry, feature);
}
function writeProperties(context, pbf) {
const feature = context.feature;
const { keys, values, keycache, valuecache } = context;
for (const key in feature.properties) {
let keyIndex = keycache[key];
if (typeof keyIndex === "undefined") {
keys.push(key);
keyIndex = keys.length - 1;
keycache[key] = keyIndex;
}
pbf.writeVarint(keyIndex);
let value = feature.properties[key];
const type = typeof value;
if (type !== "string" && type !== "boolean" && type !== "number") {
value = JSON.stringify(value);
}
const valueKey = `${type}:${value}`;
let valueIndex = valuecache[valueKey];
if (typeof valueIndex === "undefined") {
values.push(value);
valueIndex = values.length - 1;
valuecache[valueKey] = valueIndex;
}
pbf.writeVarint(valueIndex);
}
}
function command(cmd, length) {
return (length << 3) + (cmd & 7);
}
function zigzag(num) {
return num << 1 ^ num >> 31;
}
function writeGeometry(feature, pbf) {
const geometry = feature.loadGeometry();
const type = feature.type;
let x = 0;
let y = 0;
const rings = geometry.length;
for (let r = 0; r < rings; r++) {
const ring = geometry[r];
let count = 1;
if (type === 1) {
count = ring.length;
}
pbf.writeVarint(command(1, count));
const lineCount = type === 3 ? ring.length - 1 : ring.length;
for (let i = 0; i < lineCount; i++) {
if (i === 1 && type !== 1) {
pbf.writeVarint(command(2, lineCount - 1));
}
const dx = ring[i].x - x;
const dy = ring[i].y - y;
pbf.writeVarint(zigzag(dx));
pbf.writeVarint(zigzag(dy));
x += dx;
y += dy;
}
if (type === 3) {
pbf.writeVarint(command(7, 1));
}
}
}
function writeValue(value, pbf) {
switch (typeof value) {
case "string":
pbf.writeStringField(1, value);
break;
case "boolean":
pbf.writeBooleanField(7, value);
break;
case "number":
if (value % 1 !== 0) {
pbf.writeDoubleField(3, value);
} else if (value < 0) {
pbf.writeSVarintField(6, value);
} else {
pbf.writeVarintField(5, value);
}
break;
default:
}
}
// dist/lib/mapbox-vt-pbf/geojson-wrapper.js
var Point = class {
x;
y;
constructor(x, y) {
this.x = x;
this.y = y;
}
};
var GeoJSONWrapper = class {
options;
features;
length;
constructor(features2, options = {}) {
this.options = options;
this.features = features2;
this.length = features2.length;
}
feature(index) {
return new FeatureWrapper(this.features[index], this.options.extent);
}
};
var FeatureWrapper = class {
id;
type;
rawGeometry;
properties;
extent;
geometry = [];
constructor(feature, extent) {
this.id = typeof feature.id === "number" ? feature.id : void 0;
this.type = feature.type;
this.rawGeometry = feature.type === 1 ? [feature.geometry] : feature.geometry;
this.properties = feature.tags;
this.extent = extent || 4096;
}
loadGeometry() {
const rings = this.rawGeometry;
this.geometry = [];
for (const ring of rings) {
const newRing = [];
for (const coord of ring) {
newRing.push(new Point(coord[0], coord[1]));
}
this.geometry.push(newRing);
}
return this.geometry;
}
bbox() {
if (!this.geometry) {
this.loadGeometry();
}
const rings = this.geometry;
let x1 = Infinity;
let x2 = -Infinity;
let y1 = Infinity;
let y2 = -Infinity;
for (const ring of rings) {
for (const coord of ring) {
x1 = Math.min(x1, coord.x);
x2 = Math.max(x2, coord.x);
y1 = Math.min(y1, coord.y);
y2 = Math.max(y2, coord.y);
}
}
return [x1, y1, x2, y2];
}
};
// dist/lib/mapbox-vt-pbf/to-vector-tile.js
var import_loader_utils2 = require("@loaders.gl/loader-utils");
function fromGeojson(geojson, options) {
options = options || {};
geojson = normalizeGeojson(geojson);
const extent = options.extent || 4096;
const features2 = convertFeaturesToVectorTileFeatures(geojson.features, extent, options.tileIndex);
const layer = new GeoJSONWrapper(features2, { ...options, extent });
layer.name = options.layerName || "geojsonLayer";
layer.version = options.version || 1;
layer.extent = options.extent || 4096;
return fromVectorTileJs({ layers: { [layer.name]: layer } });
}
function fromVectorTileJs(tile) {
const pbf = new import_pbf2.default();
writeMVT(tile, pbf);
const uint8Array = pbf.finish();
return (0, import_loader_utils2.copyToArrayBuffer)(uint8Array.buffer, uint8Array.byteOffset, uint8Array.byteOffset + uint8Array.byteLength);
}
function normalizeGeojson(geojson) {
if (Array.isArray(geojson)) {
return {
type: "FeatureCollection",
features: geojson
};
}
if (geojson.type === "Feature") {
return {
type: "FeatureCollection",
features: [geojson]
};
}
throw new Error("Invalid GeoJSON object");
}
function convertFeaturesToVectorTileFeatures(features2, extent, tileIndex) {
if (features2.every(isVectorTileFeature)) {
return features2;
}
return features2.map((feature) => convertFeatureToVectorTile(feature, extent, tileIndex));
}
function convertFeatureToVectorTile(feature, extent, tileIndex) {
const geometry = feature.geometry;
const type = getVectorTileType(geometry.type);
return {
id: typeof feature.id === "number" ? feature.id : void 0,
type,
geometry: projectGeometryToTileSpace(geometry, extent, tileIndex),
tags: feature.properties || {}
};
}
function projectGeometryToTileSpace(geometry, extent, tileIndex) {
switch (geometry.type) {
case "Point":
return [projectPointToTile(geometry.coordinates, extent, tileIndex)];
case "MultiPoint":
return geometry.coordinates.map((coord) => projectPointToTile(coord, extent, tileIndex));
case "LineString":
return [
geometry.coordinates.map((coord) => projectPointToTile(coord, extent, tileIndex))
];
case "MultiLineString":
return geometry.coordinates.map((line) => line.map((coord) => projectPointToTile(coord, extent, tileIndex)));
case "Polygon":
return geometry.coordinates.map((ring) => ring.map((coord) => projectPointToTile(coord, extent, tileIndex)));
case "MultiPolygon":
return geometry.coordinates.flatMap((polygon) => polygon.map((ring) => ring.map((coord) => projectPointToTile(coord, extent, tileIndex))));
default:
throw new Error(`Unsupported geometry type: ${geometry.type}`);
}
}
function projectPointToTile(point, extent, tileIndex) {
if (isNormalizedPoint(point)) {
return [Math.round(point[0] * extent), Math.round(point[1] * extent)];
}
if (tileIndex && isLngLatPoint(point)) {
return projectLngLatToTile(point, tileIndex, extent);
}
return [Math.round(point[0]), Math.round(point[1])];
}
function isNormalizedPoint(point) {
return Math.abs(point[0]) <= 1 && Math.abs(point[1]) <= 1;
}
function isLngLatPoint(point) {
return Math.abs(point[0]) <= 180 && Math.abs(point[1]) <= 90;
}
function projectLngLatToTile(point, tileIndex, extent) {
const [lng, lat] = point;
const { x, y, z } = tileIndex;
const size = extent * Math.pow(2, z);
const x0 = extent * x;
const y0 = extent * y;
const worldX = (lng + 180) / 360 * size;
const worldY = (180 - 180 / Math.PI * Math.log(Math.tan(Math.PI / 4 + lat * Math.PI / 180 / 2))) * size / 360;
return [Math.round(worldX - x0), Math.round(worldY - y0)];
}
function isVectorTileFeature(feature) {
return typeof (feature == null ? void 0 : feature.type) === "number" && Array.isArray(feature.geometry);
}
function getVectorTileType(type) {
switch (type) {
case "Point":
case "MultiPoint":
return 1;
case "LineString":
case "MultiLineString":
return 2;
case "Polygon":
case "MultiPolygon":
return 3;
default:
throw new Error(`Unknown geometry type: ${type}`);
}
}
// dist/lib/encode-mvt.js
function encodeMVT(data, options) {
const { mvt } = options || {};
const encodeOptions = {
layerName: (mvt == null ? void 0 : mvt.layerName) || "geojsonLayer",
version: (mvt == null ? void 0 : mvt.version) || 1,
extent: (mvt == null ? void 0 : mvt.extent) || 4096,
tileIndex: mvt == null ? void 0 : mvt.tileIndex
};
return fromGeojson(data, encodeOptions);
}
// dist/mvt-writer.js
var VERSION3 = true ? "4.4.2" : "latest";
var MVTWriter = {
...MVTFormat,
version: VERSION3,
binary: true,
options: {
mvt: {
layerName: "geojsonLayer",
version: 1,
extent: 4096
}
},
async encode(data, options) {
return encodeMVT(data, options);
},
encodeSync(data, options) {
return encodeMVT(data, options);
}
};
// dist/mvt-source.js
var import_loader_utils3 = require("@loaders.gl/loader-utils");
var import_images = require("@loaders.gl/images");
var import_mvt = require("@loaders.gl/mvt");
var MVTSource = {
...MVTFormat,
version: "0.0.0",
type: "mvt",
fromUrl: true,
fromBlob: false,
defaultOptions: {
mvt: {
// TODO - add options here
}
},
testURL: (url) => true,
createDataSource(url, options) {
return new MVTTileSource(url, options);
}
};
var MVTTileSource = class extends import_loader_utils3.DataSource {
metadataUrl = null;
schema = "tms";
metadata;
extension;
mimeType = null;
constructor(url, options) {
var _a, _b;
super(url, options, MVTSource.defaultOptions);
this.metadataUrl = ((_a = options.mvt) == null ? void 0 : _a.metadataUrl) || `${this.url}/tilejson.json`;
this.extension = ((_b = options.mvt) == null ? void 0 : _b.extension) || ".png";
this.getTileData = this.getTileData.bind(this);
this.metadata = this.getMetadata();
if (isURLTemplate(this.url)) {
this.schema = "template";
}
}
// @ts-ignore - Metadata type misalignment
async getMetadata() {
var _a, _b;
if (!this.metadataUrl) {
return null;
}
let response;
try {
response = await this.fetch(this.metadataUrl);
} catch (error) {
console.error(error.message);
return null;
}
if (!response.ok) {
console.error(response.statusText);
return null;
}
const tileJSON = await response.text();
const metadata = ((_b = (_a = import_mvt.TileJSONLoader).parseTextSync) == null ? void 0 : _b.call(_a, tileJSON)) || null;
return metadata;
}
getTileMIMEType() {
return this.mimeType;
}
async getTile(parameters) {
const { x, y, z } = parameters;
const tileUrl = this.getTileURL(x, y, z);
const response = await this.fetch(tileUrl);
if (!response.ok) {
return null;
}
const arrayBuffer = await response.arrayBuffer();
return arrayBuffer;
}
// Tile Source interface implementation: deck.gl compatible API
// TODO - currently only handles image tiles, not vector tiles
async getTileData(parameters) {
const { x, y, z } = parameters.index;
const arrayBuffer = await this.getTile({ x, y, z, layers: [] });
if (arrayBuffer === null) {
return null;
}
const imageMetadata = (0, import_images.getBinaryImageMetadata)(arrayBuffer);
this.mimeType = this.mimeType || (imageMetadata == null ? void 0 : imageMetadata.mimeType) || "application/vnd.mapbox-vector-tile";
switch (this.mimeType) {
case "application/vnd.mapbox-vector-tile":
return await this._parseVectorTile(arrayBuffer, { x, y, z, layers: [] });
default:
return await this._parseImageTile(arrayBuffer);
}
}
// ImageTileSource interface implementation
async getImageTile(tileParams) {
const arrayBuffer = await this.getTile(tileParams);
return arrayBuffer ? this._parseImageTile(arrayBuffer) : null;
}
async _parseImageTile(arrayBuffer) {
return await import_images.ImageLoader.parse(arrayBuffer, this.loadOptions);
}
// VectorTileSource interface implementation
async getVectorTile(tileParams) {
const arrayBuffer = await this.getTile(tileParams);
return arrayBuffer ? this._parseVectorTile(arrayBuffer, tileParams) : null;
}
async _parseVectorTile(arrayBuffer, tileParams) {
var _a;
const loadOptions = {
mvt: {
shape: "geojson-table",
coordinates: "wgs84",
tileIndex: { x: tileParams.x, y: tileParams.y, z: tileParams.z },
...(_a = this.loadOptions) == null ? void 0 : _a.mvt
},
...this.loadOptions
};
return await import_mvt.MVTLoader.parse(arrayBuffer, loadOptions);
}
getMetadataUrl() {
return this.metadataUrl;
}
getTileURL(x, y, z) {
switch (this.schema) {
case "xyz":
return `${this.url}/${x}/${y}/${z}${this.extension}`;
case "tms":
return `${this.url}/${z}/${x}/${y}${this.extension}`;
case "template":
return getURLFromTemplate(this.url, x, y, z, "0");
default:
throw new Error(this.schema);
}
}
};
function isURLTemplate(s) {
return /(?=.*{z})(?=.*{x})(?=.*({y}|{-y}))|(?=.*{x})(?=.*({y}|{-y})(?=.*{z}))/.test(s);
}
var xRegex = new RegExp("{x}", "g");
var yRegex = new RegExp("{y}", "g");
var zRegex = new RegExp("{z}", "g");
function getURLFromTemplate(template, x, y, z, id = "0") {
if (Array.isArray(template)) {
const i = stringHash(id) % template.length;
template = template[i];
}
let url = template;
url = url.replace(xRegex, String(x));
url = url.replace(yRegex, String(y));
url = url.replace(zRegex, String(z));
if (Number.isInteger(y) && Number.isInteger(z)) {
url = url.replace(/\{-y\}/g, String(Math.pow(2, z) - y - 1));
}
return url;
}
function stringHash(s) {
return Math.abs(s.split("").reduce((a, b) => (a << 5) - a + b.charCodeAt(0) | 0, 0));
}
// dist/table-tile-source.js
var import_loader_utils4 = require("@loaders.gl/loader-utils");
var import_schema_utils = require("@loaders.gl/schema-utils");
var import_stats = require("@probe.gl/stats");
// dist/lib/vector-tiler/proto-tile.js
function createProtoTile(features2, z, tx, ty, options) {
const tolerance = z === options.maxZoom ? 0 : options.tolerance / ((1 << z) * options.extent);
const tile = {
protoFeatures: [],
sourceFeatures: null,
numPoints: 0,
numSimplified: 0,
numFeatures: features2.length,
x: tx,
y: ty,
z,
transformed: false,
minX: 2,
minY: 1,
maxX: -1,
maxY: 0
};
for (const feature of features2) {
addProtoFeature(tile, feature, tolerance, options);
}
return tile;
}
function addProtoFeature(tile, feature, tolerance, options) {
const geometry = feature.geometry;
const type = feature.type;
const simplifiedGeometry = [];
tile.minX = Math.min(tile.minX, feature.minX);
tile.minY = Math.min(tile.minY, feature.minY);
tile.maxX = Math.max(tile.maxX, feature.maxX);
tile.maxY = Math.max(tile.maxY, feature.maxY);
let simplifiedType;
switch (type) {
case "Point":
case "MultiPoint":
simplifiedType = 1;
for (let i = 0; i < geometry.length; i += 3) {
simplifiedGeometry.push(geometry[i], geometry[i + 1]);
tile.numPoints++;
tile.numSimplified++;
}
break;
case "LineString":
simplifiedType = 2;
addProtoLine(simplifiedGeometry, geometry, tile, tolerance, false, false);
break;
case "MultiLineString":
simplifiedType = 2;
for (let i = 0; i < geometry.length; i++) {
addProtoLine(simplifiedGeometry, geometry[i], tile, tolerance, false, i === 0);
}
break;
case "Polygon":
simplifiedType = 3;
for (let i = 0; i < geometry.length; i++) {
addProtoLine(simplifiedGeometry, geometry[i], tile, tolerance, true, i === 0);
}
break;
case "MultiPolygon":
simplifiedType = 3;
for (let k = 0; k < geometry.length; k++) {
const polygon = geometry[k];
for (let i = 0; i < polygon.length; i++) {
addProtoLine(simplifiedGeometry, polygon[i], tile, tolerance, true, i === 0);
}
}
break;
default:
throw new Error(`Unknown geometry type: ${type}`);
}
if (simplifiedGeometry.length) {
let tags = feature.tags || null;
if (type === "LineString" && options.lineMetrics) {
tags = {};
for (const key in feature.tags) {
tags[key] = feature.tags[key];
}
tags.mapbox_clip_start = geometry.start / geometry.size;
tags.mapbox_clip