UNPKG

@loaders.gl/mvt

Version:

Loader for Mapbox Vector Tiles

1,687 lines (1,666 loc) 65.2 kB
"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, 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.3.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-loader.js var VERSION2 = true ? "4.3.2" : "latest"; var MVTWorkerLoader = { dataType: null, batchType: null, name: "Mapbox Vector Tile", id: "mvt", module: "mvt", version: VERSION2, // 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' ], worker: true, category: "geometry", 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/mvt-source.js var import_loader_utils2 = require("@loaders.gl/loader-utils"); var import_images = require("@loaders.gl/images"); var import_mvt = require("@loaders.gl/mvt"); var MVTSource = { name: "MVT", id: "mvt", module: "mvt", version: "0.0.0", extensions: ["mvt"], mimeTypes: ["application/octet-stream"], options: { mvt: { // TODO - add options here } }, type: "mvt", fromUrl: true, fromBlob: false, testURL: (url) => true, createDataSource(url, props) { return new MVTTileSource(url, props); } }; var MVTTileSource = class extends import_loader_utils2.DataSource { props; url; metadataUrl = null; data; schema = "tms"; metadata; extension; mimeType = null; constructor(url, props) { var _a, _b; super(props); this.props = props; this.url = (0, import_loader_utils2.resolvePath)(url); this.metadataUrl = ((_a = props.mvt) == null ? void 0 : _a.metadataUrl) || `${this.url}/tilejson.json`; this.extension = ((_b = props.mvt) == null ? void 0 : _b.extension) || ".png"; this.data = this.url; 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 = { shape: "geojson-table", mvt: { 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_utils3 = require("@loaders.gl/loader-utils"); var import_schema = require("@loaders.gl/schema"); 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_end = geometry.end / geometry.size; } const tileFeature = { geometry: simplifiedGeometry, simplifiedType, // @ts-expect-error tags }; if (feature.id !== null) { tileFeature.id = feature.id; } tile.protoFeatures.push(tileFeature); } } function addProtoLine(result, geometry, tile, tolerance, isPolygon, isOuter) { const sqTolerance = tolerance * tolerance; if (tolerance > 0 && geometry.size < (isPolygon ? sqTolerance : tolerance)) { tile.numPoints += geometry.length / 3; return; } const ring = []; for (let i = 0; i < geometry.length; i += 3) { if (tolerance === 0 || geometry[i + 2] > sqTolerance) { tile.numSimplified++; ring.push(geometry[i], geometry[i + 1]); } tile.numPoints++; } if (isPolygon) rewind(ring, isOuter); result.push(ring); } function rewind(ring, clockwise) { let area = 0; for (let i = 0, j = ring.length - 2; i < ring.length; j = i, i += 2) { area += (ring[i] - ring[j]) * (ring[i + 1] + ring[j + 1]); } if (area > 0 === clockwise) { for (let i = 0, len = ring.length; i < len / 2; i += 2) { const x = ring[i]; const y = ring[i + 1]; ring[i] = ring[len - 2 - i]; ring[i + 1] = ring[len - 1 - i]; ring[len - 2 - i] = x; ring[len - 1 - i] = y; } } } // dist/lib/vector-tiler/transform-tile.js function transformTile(protoTile, extent) { if (protoTile.transformed) { return protoTile; } const z2 = 1 << protoTile.z; const tx = protoTile.x; const ty = protoTile.y; for (const protoFeature of protoTile.protoFeatures) { const geom = protoFeature.geometry; const simplifiedType = protoFeature.simplifiedType; protoFeature.geometry = []; if (simplifiedType === 1) { for (let j = 0; j < geom.length; j += 2) { protoFeature.geometry.push(transformPoint(geom[j], geom[j + 1], extent, z2, tx, ty)); } } else { for (let j = 0; j < geom.length; j++) { const ring = []; for (let k = 0; k < geom[j].length; k += 2) { ring.push(transformPoint(geom[j][k], geom[j][k + 1], extent, z2, tx, ty)); } protoFeature.geometry.push(ring); } } } protoTile.transformed = true; return protoTile; } function transformPoint(x, y, extent, z2, tx, ty) { return [Math.round(extent * (x * z2 - tx)), Math.round(extent * (y * z2 - ty))]; } // dist/lib/vector-tiler/tile-to-geojson.js function convertTileToGeoJSON(protoTile, props) { const features2 = []; for (const rawFeature of protoTile.protoFeatures) { if (!rawFeature || !rawFeature.geometry) { continue; } let type; let coordinates; switch (rawFeature.simplifiedType) { case 1: if (rawFeature.geometry.length === 1) { type = "Point"; coordinates = rawFeature.geometry[0]; } else { type = "MultiPoint"; coordinates = rawFeature.geometry; } break; case 2: if (rawFeature.geometry.length === 1) { type = "LineString"; coordinates = rawFeature.geometry[0]; } else { type = "MultiLineString"; coordinates = rawFeature.geometry; } break; case 3: if (rawFeature.geometry.length > 1) { type = "MultiPolygon"; coordinates = [rawFeature.geometry]; } else { type = "Polygon"; coordinates = rawFeature.geometry; } break; default: throw new Error(`${rawFeature.simplifiedType}is not a valid simplified type`); } switch (props.coordinates) { case "EPSG:4326": case "wgs84": projectToLngLat(coordinates, props.tileIndex, props.extent); break; default: convertToLocalCoordinates(coordinates, props.extent); break; } const feature = { type: "Feature", geometry: { type, coordinates }, properties: rawFeature.tags || {}, id: rawFeature.id }; features2.push(feature); } if (features2.length === 0) { return null; } const table = { shape: "geojson-table", type: "FeatureCollection", features: features2 }; return table; } // dist/lib/vector-tiler/features/proto-feature.js function createProtoFeature(id, type, geometry, tags) { const feature = { // eslint-disable-next-line id: id == null ? null : id, type, simplifiedType: void 0, // TODO geometry, tags, minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity }; switch (type) { case "Point": case "MultiPoint": case "LineString": calcLineBBox(feature, geometry); break; case "MultiLineString": for (const line of geometry) { calcLineBBox(feature, line); } break; case "Polygon": calcLineBBox(feature, geometry[0]); break; case "MultiPolygon": for (const polygon of geometry) { calcLineBBox(feature, polygon[0]); } break; default: throw new Error(String(type)); } return feature; } function calcLineBBox(feature, geometry) { for (let i = 0; i < geometry.length; i += 3) { feature.minX = Math.min(feature.minX, geometry[i]); feature.minY = Math.min(feature.minY, geometry[i + 1]); feature.maxX = Math.max(feature.maxX, geometry[i]); feature.maxY = Math.max(feature.maxY, geometry[i + 1]); } } // dist/lib/vector-tiler/features/simplify-path.js function simplifyPath(coords, first, last, sqTolerance) { let maxSqDist = sqTolerance; const mid = last - first >> 1; let minPosToMid = last - first; let index; const ax = coords[first]; const ay = coords[first + 1]; const bx = coords[last]; const by = coords[last + 1]; for (let i = first + 3; i < last; i += 3) { const d = getSqSegDist(coords[i], coords[i + 1], ax, ay, bx, by); if (d > maxSqDist) { index = i; maxSqDist = d; } else if (d === maxSqDist) { const posToMid = Math.abs(i - mid); if (posToMid < minPosToMid) { index = i; minPosToMid = posToMid; } } } if (maxSqDist > sqTolerance) { if (index - first > 3) simplifyPath(coords, first, index, sqTolerance); coords[index + 2] = maxSqDist; if (last - index > 3) simplifyPath(coords, index, last, sqTolerance); } } function getSqSegDist(px, py, x, y, bx, by) { let dx = bx - x; let dy = by - y; if (dx !== 0 || dy !== 0) { const t = ((px - x) * dx + (py - y) * dy) / (dx * dx + dy * dy); if (t > 1) { x = bx; y = by; } else if (t > 0) { x += dx * t; y += dy * t; } } dx = px - x; dy = py - y; return dx * dx + dy * dy; } // dist/lib/vector-tiler/features/convert-feature.js function convertFeaturesToProtoFeature(data, options) { const protoFeatures = []; switch (data.type) { case "FeatureCollection": let i = 0; for (const feature of data.features) { protoFeatures.push(convertFeature(feature, options, i++)); } break; case "Feature": protoFeatures.push(convertFeature(data, options)); break; default: protoFeatures.push(convertFeature({ geometry: data }, options)); } return protoFeatures; } function convertFeature(geojson, options, index) { if (!geojson.geometry) { return; } const coords = geojson.geometry.coordinates; const type = geojson.geometry.type; const tolerance = Math.pow(options.tolerance / ((1 << options.maxZoom) * options.extent), 2); let geometry = []; let id = geojson.id; if (options.promoteId) { id = geojson.properties[options.promoteId]; } else if (options.generateId) { id = index || 0; } switch (type) { case "Point": convertPoint(coords, geometry); break; case "MultiPoint": for (const p of coords) { convertPoint(p, geometry); } break; case "LineString": convertLine(coords, geometry, tolerance, false); break; case "MultiLineString": if (options.lineMetrics) { for (const line of coords) { geometry = []; convertLine(line, geometry, tolerance, false); features.push(createProtoFeature(id, "LineString", geometry, geojson.properties)); } return; convertLines(coords, geometry, tolerance, false); } break; case "Polygon": convertLines(coords, geometry, tolerance, true); break; case "MultiPolygon": for (const polygon of coords) { const newPolygon = []; convertLines(polygon, newPolygon, tolerance, true); geometry.push(newPolygon); } break; case "GeometryCollection": for (const singleGeometry of geojson.geometry.geometries) { convertFeature(features, { id, geometry: singleGeometry, properties: geojson.properties }, options, index); } break; default: throw new Error("Input data is not a valid GeoJSON object."); } return createProtoFeature(id, type, geometry, geojson.properties); } function convertPoint(coords, out) { out.push(projectX(coords[0]), projectY(coords[1]), 0); } function convertLine(ring, out, tolerance, isPolygon) { let x0, y0; let size = 0; for (let j = 0; j < ring.length; j++) { const x = projectX(ring[j][0]); const y = projectY(ring[j][1]); out.push(x, y, 0); if (j > 0) { if (isPolygon) { size += (x0 * y - x * y0) / 2; } else { size += Math.sqrt(Math.pow(x - x0, 2) + Math.pow(y - y0, 2)); } } x0 = x; y0 = y; } const last = out.length - 3; out[2] = 1; simplifyPath(out, 0, last, tolerance); out[last + 2] = 1; out.size = Math.abs(size); out.start = 0; out.end = out.size; } function convertLines(rings, out, tolerance, isPolygon) { for (let i = 0; i < rings.length; i++) { const geom = []; convertLine(rings[i], geom, tolerance, isPolygon); out.push(geom); } } function projectX(x) { return x / 360 + 0.5; } function projectY(y) { const sin = Math.sin(y * Math.PI / 180); const y2 = 0.5 - 0.25 * Math.log((1 + sin) / (1 - sin)) / Math.PI; return y2 < 0 ? 0 : y2 > 1 ? 1 : y2; } // dist/lib/vector-tiler/features/clip-features.js function clipFeatures(features2, scale, k1, k2, axis, minAll, maxAll, options) { k1 /= scale; k2 /= scale; if (minAll >= k1 && maxAll < k2) { return features2; } else if (maxAll < k1 || minAll >= k2) { return null; } const clipped = []; for (const feature of features2) { const geometry = feature.geometry; let type = feature.type; const min = axis === 0 ? feature.minX : feature.minY; const max = axis === 0 ? feature.maxX : feature.maxY; if (min >= k1 && max < k2) { clipped.push(feature); continue; } else if (max < k1 || min >= k2) { continue; } let newGeometry = []; if (type === "Point" || type === "MultiPoint") { clipPoints(geometry, newGeometry, k1, k2, axis); } else if (type === "LineString") { clipLine(geometry, newGeometry, k1, k2, axis, false, options.lineMetrics); } else if (type === "MultiLineString") { clipLines(geometry, newGeometry, k1, k2, axis, false); } else if (type === "Polygon") { clipLines(geometry, newGeometry, k1, k2, axis, true); } else if (type === "MultiPolygon") { for (const polygon of geometry) { const newPolygon = []; clipLines(polygon, newPolygon, k1, k2, axis, true); if (newPolygon.length) { newGeometry.push(newPolygon); } } } if (newGeometry.length) { if (options.lineMetrics && type === "LineString") { for (const line of newGeometry) { clipped.push(createProtoFeature(feature.id, type, line, feature.tags)); } continue; } if (type === "LineString" || type === "MultiLineString") { if (newGeometry.length === 1) { type = "LineString";