@loaders.gl/mvt
Version:
Loader for Mapbox Vector Tiles
4 lines • 138 kB
Source Map (JSON)
{
"version": 3,
"sources": ["index.js", "lib/get-schemas-from-tilejson.js", "lib/parse-tilejson.js", "tilejson-loader.js", "lib/parse-mvt.js", "lib/utils/geometry-utils.js", "lib/vector-tile/vector-tile-feature.js", "lib/vector-tile/vector-tile-layer.js", "lib/vector-tile/vector-tile.js", "mvt-loader.js", "mvt-source.js", "table-tile-source.js", "lib/vector-tiler/proto-tile.js", "lib/vector-tiler/transform-tile.js", "lib/vector-tiler/tile-to-geojson.js", "lib/vector-tiler/features/proto-feature.js", "lib/vector-tiler/features/simplify-path.js", "lib/vector-tiler/features/convert-feature.js", "lib/vector-tiler/features/clip-features.js", "lib/vector-tiler/features/wrap-features.js"],
"sourcesContent": ["// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n// TileJSONLoader\nexport { TileJSONLoader } from \"./tilejson-loader.js\";\n// MVTLoader\nexport { MVTLoader, MVTWorkerLoader } from \"./mvt-loader.js\";\n// MVTSource\nexport { MVTSource } from \"./mvt-source.js\";\n// TableTileSource (dynamically tiles a table)\nexport { TableTileSource } from \"./table-tile-source.js\";\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n// LAYERS\nexport function getSchemaFromTileJSONLayer(layer) {\n const fields = [];\n if (layer.fields) {\n for (const field of layer.fields) {\n fields.push({\n name: field.name,\n type: getDataTypeFromTileJSONField(field),\n metadata: getMetadataFromTileJSONField(field)\n });\n }\n }\n return {\n metadata: getMetadataFromTileJSONLayer(layer),\n fields\n };\n}\nfunction getMetadataFromTileJSONLayer(layer) {\n const metadata = {};\n for (const [key, value] of Object.entries(layer)) {\n if (key !== 'fields' && value) {\n metadata[key] = JSON.stringify(value);\n }\n }\n return metadata;\n}\n// FIELDS\nfunction getDataTypeFromTileJSONField(field) {\n switch (field.type.toLowerCase()) {\n case 'float32':\n return 'float32';\n case 'number':\n case 'float64':\n return 'float64';\n case 'string':\n case 'utf8':\n return 'utf8';\n case 'boolean':\n return 'bool';\n default:\n return 'null';\n }\n}\nfunction getMetadataFromTileJSONField(field) {\n const metadata = {};\n for (const [key, value] of Object.entries(field)) {\n if (key !== 'name' && value) {\n metadata[key] = JSON.stringify(value);\n }\n }\n return metadata;\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\nimport { getSchemaFromTileJSONLayer } from \"./get-schemas-from-tilejson.js\";\nconst isObject = (x) => x !== null && typeof x === 'object';\n/**\n * Parse TileJSON from metadata\n * @param jsonMetadata - metadata object\n * @param options - options\n * @returns - parsed TileJSON\n */\n// eslint-disable-next-line complexity\nexport function parseTileJSON(jsonMetadata, options) {\n if (!jsonMetadata || !isObject(jsonMetadata)) {\n return null;\n }\n let tileJSON = {\n name: jsonMetadata.name || '',\n description: jsonMetadata.description || ''\n };\n // tippecanoe\n if (typeof jsonMetadata.generator === 'string') {\n tileJSON.generator = jsonMetadata.generator;\n }\n if (typeof jsonMetadata.generator_options === 'string') {\n tileJSON.generatorOptions = jsonMetadata.generator_options;\n }\n // Tippecanoe emits `antimeridian_adjusted_bounds` instead of `bounds`\n tileJSON.boundingBox =\n parseBounds(jsonMetadata.bounds) || parseBounds(jsonMetadata.antimeridian_adjusted_bounds);\n // TODO - can be undefined - we could set to center of bounds...\n tileJSON.center = parseCenter(jsonMetadata.center);\n // TODO - can be undefined, we could extract from layers...\n tileJSON.maxZoom = safeParseFloat(jsonMetadata.maxzoom);\n // TODO - can be undefined, we could extract from layers...\n tileJSON.minZoom = safeParseFloat(jsonMetadata.minzoom);\n // Look for nested metadata embedded in .json field\n // TODO - document what source this applies to, when is this needed?\n if (typeof jsonMetadata?.json === 'string') {\n // try to parse json\n try {\n tileJSON.metaJson = JSON.parse(jsonMetadata.json);\n }\n catch (error) {\n // eslint-disable-next-line no-console\n console.warn('Failed to parse tilejson.json field', error);\n // do nothing\n }\n }\n // Look for fields in tilestats\n const tilestats = jsonMetadata.tilestats || tileJSON.metaJson?.tilestats;\n const tileStatsLayers = parseTilestatsLayers(tilestats, options);\n const tileJSONlayers = parseTileJSONLayers(jsonMetadata.vector_layers); // eslint-disable-line camelcase\n // TODO - merge in description from tilejson\n const layers = mergeLayers(tileJSONlayers, tileStatsLayers);\n tileJSON = {\n ...tileJSON,\n layers\n };\n if (tileJSON.maxZoom === null && layers.length > 0) {\n tileJSON.maxZoom = layers[0].maxZoom || null;\n }\n if (tileJSON.minZoom === null && layers.length > 0) {\n tileJSON.minZoom = layers[0].minZoom || null;\n }\n return tileJSON;\n}\nfunction parseTileJSONLayers(layers) {\n // Look for fields in vector_layers\n if (!Array.isArray(layers)) {\n return [];\n }\n return layers.map((layer) => parseTileJSONLayer(layer));\n}\nfunction parseTileJSONLayer(layer) {\n const fields = Object.entries(layer.fields || []).map(([key, datatype]) => ({\n name: key,\n ...attributeTypeToFieldType(String(datatype))\n }));\n const layer2 = { ...layer };\n delete layer2.fields;\n return {\n name: layer.id || '',\n ...layer2,\n fields\n };\n}\n/** parse Layers array from tilestats */\nfunction parseTilestatsLayers(tilestats, options) {\n if (isObject(tilestats) && Array.isArray(tilestats.layers)) {\n // we are in luck!\n return tilestats.layers.map((layer) => parseTilestatsForLayer(layer, options));\n }\n return [];\n}\nfunction parseTilestatsForLayer(layer, options) {\n const fields = [];\n const indexedAttributes = {};\n const attributes = layer.attributes || [];\n for (const attribute of attributes) {\n const name = attribute.attribute;\n if (typeof name === 'string') {\n // TODO - code copied from kepler.gl, need sample tilestats files to test\n if (name.split('|').length > 1) {\n // indexed field\n const fname = name.split('|')[0];\n indexedAttributes[fname] = indexedAttributes[fname] || [];\n indexedAttributes[fname].push(attribute);\n // eslint-disable-next-line no-console\n console.warn('ignoring tilestats indexed field', fname);\n }\n else if (!fields[name]) {\n fields.push(attributeToField(attribute, options));\n }\n else {\n // return (fields[name], attribute);\n }\n }\n }\n return {\n name: layer.layer || '',\n dominantGeometry: layer.geometry,\n fields\n };\n}\nfunction mergeLayers(layers, tilestatsLayers) {\n return layers.map((layer) => {\n const tilestatsLayer = tilestatsLayers.find((tsLayer) => tsLayer.name === layer.name);\n const fields = tilestatsLayer?.fields || layer.fields || [];\n const mergedLayer = {\n ...layer,\n ...tilestatsLayer,\n fields\n };\n mergedLayer.schema = getSchemaFromTileJSONLayer(mergedLayer);\n return mergedLayer;\n });\n}\n/**\n * bounds should be [minLng, minLat, maxLng, maxLat]\n *`[[w, s], [e, n]]`, indicates the limits of the bounding box using the axis units and order of the specified CRS.\n */\nfunction parseBounds(bounds) {\n // supported formats\n // string: \"-96.657715,40.126127,-90.140061,43.516689\",\n // array: [ -180, -85.05112877980659, 180, 85.0511287798066 ]\n const result = fromArrayOrString(bounds);\n // validate bounds\n if (Array.isArray(result) &&\n result.length === 4 &&\n [result[0], result[2]].every(isLng) &&\n [result[1], result[3]].every(isLat)) {\n return [\n [result[0], result[1]],\n [result[2], result[3]]\n ];\n }\n return undefined;\n}\nfunction parseCenter(center) {\n // supported formats\n // string: \"-96.657715,40.126127,-90.140061,43.516689\",\n // array: [-91.505127,41.615442,14]\n const result = fromArrayOrString(center);\n if (Array.isArray(result) &&\n result.length === 3 &&\n isLng(result[0]) &&\n isLat(result[1]) &&\n isZoom(result[2])) {\n return result;\n }\n return null;\n}\nfunction safeParseFloat(input) {\n const result = typeof input === 'string' ? parseFloat(input) : typeof input === 'number' ? input : null;\n return result === null || isNaN(result) ? null : result;\n}\n// https://github.com/mapbox/tilejson-spec/tree/master/2.2.0\nfunction isLat(num) {\n return Number.isFinite(num) && num <= 90 && num >= -90;\n}\nfunction isLng(num) {\n return Number.isFinite(num) && num <= 180 && num >= -180;\n}\nfunction isZoom(num) {\n return Number.isFinite(num) && num >= 0 && num <= 22;\n}\nfunction fromArrayOrString(data) {\n if (typeof data === 'string') {\n return data.split(',').map(parseFloat);\n }\n else if (Array.isArray(data)) {\n return data;\n }\n return null;\n}\n// possible types https://github.com/mapbox/tippecanoe#modifying-feature-attributes\nconst attrTypeMap = {\n number: {\n type: 'float32'\n },\n numeric: {\n type: 'float32'\n },\n string: {\n type: 'utf8'\n },\n vachar: {\n type: 'utf8'\n },\n float: {\n type: 'float32'\n },\n int: {\n type: 'int32'\n },\n int4: {\n type: 'int32'\n },\n boolean: {\n type: 'boolean'\n },\n bool: {\n type: 'boolean'\n }\n};\nfunction attributeToField(attribute = {}, options) {\n const fieldTypes = attributeTypeToFieldType(attribute.type);\n const field = {\n name: attribute.attribute,\n // what happens if attribute type is string...\n // filterProps: getFilterProps(fieldTypes.type, attribute),\n ...fieldTypes\n };\n // attribute: \"_season_peaks_color\"\n // count: 1000\n // max: 0.95\n // min: 0.24375\n // type: \"number\"\n if (typeof attribute.min === 'number') {\n field.min = attribute.min;\n }\n if (typeof attribute.max === 'number') {\n field.max = attribute.max;\n }\n if (typeof attribute.count === 'number') {\n field.uniqueValueCount = attribute.count;\n }\n if (attribute.values) {\n // Too much data? Add option?\n field.values = attribute.values;\n }\n if (field.values && typeof options.maxValues === 'number') {\n // Too much data? Add option?\n field.values = field.values?.slice(0, options.maxValues);\n }\n return field;\n}\nfunction attributeTypeToFieldType(aType) {\n const type = aType.toLowerCase();\n if (!type || !attrTypeMap[type]) {\n // console.warn(\n // `cannot convert attribute type ${type} to loaders.gl data type, use string by default`\n // );\n }\n return attrTypeMap[type] || { type: 'string' };\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\nimport { parseTileJSON } from \"./lib/parse-tilejson.js\";\n// __VERSION__ is injected by babel-plugin-version-inline\n// @ts-ignore TS2304: Cannot find name '__VERSION__'.\nconst VERSION = typeof \"4.3.2\" !== 'undefined' ? \"4.3.2\" : 'latest';\n/**\n * Loader for TileJSON metadata\n */\nexport const TileJSONLoader = {\n dataType: null,\n batchType: null,\n name: 'TileJSON',\n id: 'tilejson',\n module: 'pmtiles',\n version: VERSION,\n worker: true,\n extensions: ['json'],\n mimeTypes: ['application/json'],\n text: true,\n options: {\n tilejson: {\n maxValues: undefined\n }\n },\n parse: async (arrayBuffer, options) => {\n const jsonString = new TextDecoder().decode(arrayBuffer);\n const json = JSON.parse(jsonString);\n const tilejsonOptions = { ...TileJSONLoader.options.tilejson, ...options?.tilejson };\n return parseTileJSON(json, tilejsonOptions);\n },\n parseTextSync: (text, options) => {\n const json = JSON.parse(text);\n const tilejsonOptions = { ...TileJSONLoader.options.tilejson, ...options?.tilejson };\n return parseTileJSON(json, tilejsonOptions);\n }\n};\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright vis.gl contributors\nimport { flatGeojsonToBinary } from '@loaders.gl/gis';\nimport { log } from '@loaders.gl/loader-utils';\nimport Protobuf from 'pbf';\nimport { VectorTile } from \"./vector-tile/vector-tile.js\";\n/**\n * Parse MVT arrayBuffer and return GeoJSON.\n *\n * @param arrayBuffer A MVT arrayBuffer\n * @param options\n * @returns A GeoJSON geometry object or a binary representation\n */\nexport function parseMVT(arrayBuffer, options) {\n const mvtOptions = checkOptions(options);\n const shape = options?.gis?.format || options?.mvt?.shape || options?.shape;\n switch (shape) {\n case 'columnar-table': // binary + some JS arrays\n return { shape: 'columnar-table', data: parseToBinary(arrayBuffer, mvtOptions) };\n case 'geojson-table': {\n const table = {\n shape: 'geojson-table',\n type: 'FeatureCollection',\n features: parseToGeojsonFeatures(arrayBuffer, mvtOptions)\n };\n return table;\n }\n case 'geojson':\n return parseToGeojsonFeatures(arrayBuffer, mvtOptions);\n case 'binary-geometry':\n return parseToBinary(arrayBuffer, mvtOptions);\n case 'binary':\n return parseToBinary(arrayBuffer, mvtOptions);\n default:\n throw new Error(shape || 'undefined shape');\n }\n}\nfunction parseToBinary(arrayBuffer, options) {\n const [flatGeoJsonFeatures, geometryInfo] = parseToFlatGeoJson(arrayBuffer, options);\n const binaryData = flatGeojsonToBinary(flatGeoJsonFeatures, geometryInfo);\n // Add the original byteLength (as a reasonable approximation of the size of the binary data)\n // TODO decide where to store extra fields like byteLength (header etc) and document\n // @ts-ignore\n binaryData.byteLength = arrayBuffer.byteLength;\n return binaryData;\n}\nfunction parseToFlatGeoJson(arrayBuffer, options) {\n const features = [];\n const geometryInfo = {\n coordLength: 2,\n pointPositionsCount: 0,\n pointFeaturesCount: 0,\n linePositionsCount: 0,\n linePathsCount: 0,\n lineFeaturesCount: 0,\n polygonPositionsCount: 0,\n polygonObjectsCount: 0,\n polygonRingsCount: 0,\n polygonFeaturesCount: 0\n };\n if (arrayBuffer.byteLength <= 0) {\n return [features, geometryInfo];\n }\n const tile = new VectorTile(new Protobuf(arrayBuffer));\n const selectedLayers = options && Array.isArray(options.layers) ? options.layers : Object.keys(tile.layers);\n selectedLayers.forEach((layerName) => {\n const vectorTileLayer = tile.layers[layerName];\n if (!vectorTileLayer) {\n return;\n }\n for (let i = 0; i < vectorTileLayer.length; i++) {\n const vectorTileFeature = vectorTileLayer.getBinaryFeature(i, geometryInfo);\n const decodedFeature = getDecodedFeatureBinary(vectorTileFeature, options, layerName);\n features.push(decodedFeature);\n }\n });\n return [features, geometryInfo];\n}\nfunction parseToGeojsonFeatures(arrayBuffer, options) {\n if (arrayBuffer.byteLength <= 0) {\n return [];\n }\n const features = [];\n const tile = new VectorTile(new Protobuf(arrayBuffer));\n const selectedLayers = Array.isArray(options.layers) ? options.layers : Object.keys(tile.layers);\n selectedLayers.forEach((layerName) => {\n const vectorTileLayer = tile.layers[layerName];\n if (!vectorTileLayer) {\n return;\n }\n for (let i = 0; i < vectorTileLayer.length; i++) {\n const vectorTileFeature = vectorTileLayer.getGeoJSONFeature(i);\n const decodedFeature = getDecodedFeature(vectorTileFeature, options, layerName);\n features.push(decodedFeature);\n }\n });\n return features;\n}\n/** Check that options are good */\nfunction checkOptions(options) {\n if (!options?.mvt) {\n throw new Error('mvt options required');\n }\n if (options.mvt?.coordinates === 'wgs84' && !options.mvt.tileIndex) {\n throw new Error('MVT Loader: WGS84 coordinates need tileIndex property');\n }\n if (options.gis) {\n log.warn('MVTLoader: \"options.gis\" is deprecated, use \"options.mvt.shape\" instead')();\n }\n return options.mvt;\n}\n/**\n * @param feature\n * @param options\n * @returns decoded feature\n */\nfunction getDecodedFeature(feature, options, layerName) {\n const decodedFeature = feature.toGeoJSONFeature(options.coordinates || 'local', options.tileIndex);\n // Add layer name to GeoJSON properties\n if (options.layerProperty) {\n decodedFeature.properties ||= {};\n decodedFeature.properties[options.layerProperty] = layerName;\n }\n return decodedFeature;\n}\n/**\n * @param feature\n * @param options\n * @returns decoded binary feature\n */\nfunction getDecodedFeatureBinary(feature, options, layerName) {\n const decodedFeature = feature.toBinaryFeature(options.coordinates || 'local', options.tileIndex);\n // Add layer name to GeoJSON properties\n if (options.layerProperty && decodedFeature.properties) {\n decodedFeature.properties[options.layerProperty] = layerName;\n }\n return decodedFeature;\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright vis.gl contributors\nimport { getPolygonSignedArea } from '@math.gl/polygon';\n/**\n *\n * @param ring\n * @returns sum\n */\nexport function signedArea(ring) {\n let sum = 0;\n for (let i = 0, j = ring.length - 1, p1, p2; i < ring.length; j = i++) {\n p1 = ring[i];\n p2 = ring[j];\n sum += (p2[0] - p1[0]) * (p1[1] + p2[1]);\n }\n return sum;\n}\n/**\n * This function projects local coordinates in a\n * [0 - bufferSize, this.extent + bufferSize] range to a\n * [0 - (bufferSize / this.extent), 1 + (bufferSize / this.extent)] range.\n * The resulting extent would be 1.\n * @param line\n * @param feature\n */\nexport function convertToLocalCoordinates(coordinates, extent) {\n if (Array.isArray(coordinates[0])) {\n for (const subcoords of coordinates) {\n convertToLocalCoordinates(subcoords, extent);\n }\n return;\n }\n // Just a point\n const p = coordinates;\n p[0] /= extent;\n p[1] /= extent;\n}\n/**\n * For the binary code path, the feature data is just\n * one big flat array, so we just divide each value\n * @param data\n * @param feature\n */\nexport function convertToLocalCoordinatesFlat(data, extent) {\n for (let i = 0; i < data.length; ++i) {\n data[i] /= extent;\n }\n}\n/**\n * Projects local tile coordinates to lngLat in place.\n * @param points\n * @param tileIndex\n */\nexport function projectToLngLat(line, tileIndex, extent) {\n if (typeof line[0][0] !== 'number') {\n for (const point of line) {\n // @ts-expect-error\n projectToLngLat(point, tileIndex, extent);\n }\n return;\n }\n const size = extent * Math.pow(2, tileIndex.z);\n const x0 = extent * tileIndex.x;\n const y0 = extent * tileIndex.y;\n for (let j = 0; j < line.length; j++) {\n const p = line[j];\n p[0] = ((p[0] + x0) * 360) / size - 180;\n const y2 = 180 - ((p[1] + y0) * 360) / size;\n p[1] = (360 / Math.PI) * Math.atan(Math.exp((y2 * Math.PI) / 180)) - 90;\n }\n}\n/**\n * Projects local tile coordinates to lngLat in place.\n * @param points\n * @param tileIndex\nexport function projectTileCoordinatesToLngLat(\n points: number[][],\n tileIndex: {x: number; y: number; z: number},\n extent: number\n): void {\n const {x, y, z} = tileIndex;\n const size = extent * Math.pow(2, z);\n const x0 = extent * x;\n const y0 = extent * y;\n\n for (const p of points) {\n p[0] = ((p[0] + x0) * 360) / size - 180;\n const y2 = 180 - ((p[1] + y0) * 360) / size;\n p[1] = (360 / Math.PI) * Math.atan(Math.exp((y2 * Math.PI) / 180)) - 90;\n }\n}\n */\n/**\n *\n * @param data\n * @param x0\n * @param y0\n * @param size\n */\nexport function projectToLngLatFlat(data, tileIndex, extent) {\n const { x, y, z } = tileIndex;\n const size = extent * Math.pow(2, z);\n const x0 = extent * x;\n const y0 = extent * y;\n for (let j = 0, jl = data.length; j < jl; j += 2) {\n data[j] = ((data[j] + x0) * 360) / size - 180;\n const y2 = 180 - ((data[j + 1] + y0) * 360) / size;\n data[j + 1] = (360 / Math.PI) * Math.atan(Math.exp((y2 * Math.PI) / 180)) - 90;\n }\n}\n/**\n * Classifies an array of rings into polygons with outer rings and holes\n * @param rings\n * @returns polygons\n */\nexport function classifyRings(rings) {\n const len = rings.length;\n if (len <= 1)\n return [rings];\n const polygons = [];\n let polygon;\n let ccw;\n for (let i = 0; i < len; i++) {\n const area = signedArea(rings[i]);\n if (area === 0)\n continue; // eslint-disable-line no-continue\n if (ccw === undefined)\n ccw = area < 0;\n if (ccw === area < 0) {\n if (polygon)\n polygons.push(polygon);\n polygon = [rings[i]];\n }\n else if (polygon)\n polygon.push(rings[i]);\n }\n if (polygon)\n polygons.push(polygon);\n return polygons;\n}\n/**\n * Classifies an array of rings into polygons with outer rings and holes\n * The function also detects holes which have zero area and\n * removes them. In doing so it modifies the input\n * `geom.data` array to remove the unneeded data\n *\n * @param geometry\n * @returns object\n */\n// eslint-disable-next-line max-statements\nexport function classifyRingsFlat(geom) {\n const len = geom.indices.length;\n const type = 'Polygon';\n if (len <= 1) {\n return {\n type,\n data: geom.data,\n areas: [[getPolygonSignedArea(geom.data)]],\n indices: [geom.indices]\n };\n }\n const areas = [];\n const polygons = [];\n let ringAreas = [];\n let polygon = [];\n let ccw;\n let offset = 0;\n for (let endIndex, i = 0, startIndex; i < len; i++) {\n startIndex = geom.indices[i] - offset;\n endIndex = geom.indices[i + 1] - offset || geom.data.length;\n const shape = geom.data.slice(startIndex, endIndex);\n const area = getPolygonSignedArea(shape);\n if (area === 0) {\n // This polygon has no area, so remove it from the shape\n // Remove the section from the data array\n const before = geom.data.slice(0, startIndex);\n const after = geom.data.slice(endIndex);\n geom.data = before.concat(after);\n // Need to offset any remaining indices as we have\n // modified the data buffer\n offset += endIndex - startIndex;\n // Do not add this index to the output and process next shape\n continue; // eslint-disable-line no-continue\n }\n if (ccw === undefined)\n ccw = area < 0;\n if (ccw === area < 0) {\n if (polygon.length) {\n areas.push(ringAreas);\n polygons.push(polygon);\n }\n polygon = [startIndex];\n ringAreas = [area];\n }\n else {\n ringAreas.push(area);\n polygon.push(startIndex);\n }\n }\n if (ringAreas)\n areas.push(ringAreas);\n if (polygon.length)\n polygons.push(polygon);\n return { type, areas, indices: polygons, data: geom.data };\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright vis.gl contributors\nimport { classifyRings, classifyRingsFlat, projectToLngLat, projectToLngLatFlat, convertToLocalCoordinates, convertToLocalCoordinatesFlat } from \"../utils/geometry-utils.js\";\nexport class VectorTileFeature {\n properties;\n extent;\n type;\n id;\n _pbf;\n _geometry;\n _keys;\n _values;\n _geometryInfo;\n static types = ['Unknown', 'Point', 'LineString', 'Polygon'];\n // eslint-disable-next-line max-params\n constructor(pbf, end, extent, keys, values, geometryInfo) {\n // Public\n this.properties = {};\n this.extent = extent;\n this.type = 0;\n this.id = null;\n // Private\n this._pbf = pbf;\n this._geometry = -1;\n this._keys = keys;\n this._values = values;\n // Only used by binary tiles\n this._geometryInfo = geometryInfo;\n pbf.readFields(readFeature, this, end);\n }\n toGeoJSONFeature(coordinates, tileIndex) {\n const coords = this.loadGeometry();\n switch (coordinates) {\n case 'wgs84':\n return _toGeoJSONFeature(this, coords, (line) => projectToLngLat(line, tileIndex, this.extent));\n default:\n return _toGeoJSONFeature(this, coords, convertToLocalCoordinates);\n }\n }\n /**\n *\n * @param options\n * @returns\n */\n toBinaryFeature(coordinates, tileIndex) {\n const geom = this.loadFlatGeometry();\n switch (coordinates) {\n case 'wgs84':\n return this._toBinaryCoordinates(geom, (coords) => projectToLngLatFlat(coords, tileIndex, this.extent));\n default:\n return this._toBinaryCoordinates(geom, convertToLocalCoordinatesFlat);\n }\n }\n /** Read a bounding box from the feature */\n // eslint-disable-next-line max-statements\n bbox() {\n const pbf = this._pbf;\n pbf.pos = this._geometry;\n const end = pbf.readVarint() + pbf.pos;\n let cmd = 1;\n let length = 0;\n let x = 0;\n let y = 0;\n let x1 = Infinity;\n let x2 = -Infinity;\n let y1 = Infinity;\n let y2 = -Infinity;\n while (pbf.pos < end) {\n if (length <= 0) {\n const cmdLen = pbf.readVarint();\n cmd = cmdLen & 0x7;\n length = cmdLen >> 3;\n }\n length--;\n if (cmd === 1 || cmd === 2) {\n x += pbf.readSVarint();\n y += pbf.readSVarint();\n if (x < x1)\n x1 = x;\n if (x > x2)\n x2 = x;\n if (y < y1)\n y1 = y;\n if (y > y2)\n y2 = y;\n }\n else if (cmd !== 7) {\n throw new Error(`unknown command ${cmd}`);\n }\n }\n return [x1, y1, x2, y2];\n }\n // BINARY HELPERS\n /**\n *\n * @param transform\n * @returns result\n */\n _toBinaryCoordinates(geom, transform) {\n let geometry;\n // Apply the supplied transformation to data\n transform(geom.data, this.extent);\n const coordLength = 2;\n // eslint-disable-next-line default-case\n switch (this.type) {\n case 1: // Point\n this._geometryInfo.pointFeaturesCount++;\n this._geometryInfo.pointPositionsCount += geom.indices.length;\n geometry = { type: 'Point', ...geom };\n break;\n case 2: // LineString\n this._geometryInfo.lineFeaturesCount++;\n this._geometryInfo.linePathsCount += geom.indices.length;\n this._geometryInfo.linePositionsCount += geom.data.length / coordLength;\n geometry = { type: 'LineString', ...geom };\n break;\n case 3: // Polygon\n geometry = classifyRingsFlat(geom);\n // Unlike Point & LineString geom.indices is a 2D array, thanks\n // to the classifyRings method\n this._geometryInfo.polygonFeaturesCount++;\n this._geometryInfo.polygonObjectsCount += geometry.indices.length;\n for (const indices of geometry.indices) {\n this._geometryInfo.polygonRingsCount += indices.length;\n }\n this._geometryInfo.polygonPositionsCount += geometry.data.length / coordLength;\n break;\n default:\n throw new Error(`Invalid geometry type: ${this.type}`);\n }\n const result = { type: 'Feature', geometry, properties: this.properties };\n if (this.id !== null) {\n result.id = this.id;\n }\n return result;\n }\n // GEOJSON HELPER\n // eslint-disable-next-line complexity, max-statements\n loadGeometry() {\n const pbf = this._pbf;\n pbf.pos = this._geometry;\n const end = pbf.readVarint() + pbf.pos;\n let cmd = 1;\n let length = 0;\n let x = 0;\n let y = 0;\n const lines = [];\n let line;\n while (pbf.pos < end) {\n if (length <= 0) {\n const cmdLen = pbf.readVarint();\n cmd = cmdLen & 0x7;\n length = cmdLen >> 3;\n }\n length--;\n switch (cmd) {\n case 1:\n case 2:\n x += pbf.readSVarint();\n y += pbf.readSVarint();\n if (cmd === 1) {\n // moveTo\n if (line)\n lines.push(line);\n line = [];\n }\n if (line)\n line.push([x, y]);\n break;\n case 7:\n // Workaround for https://github.com/mapbox/mapnik-vector-tile/issues/90\n if (line) {\n line.push(line[0].slice()); // closePolygon\n }\n break;\n default:\n throw new Error(`unknown command ${cmd}`);\n }\n }\n if (line)\n lines.push(line);\n return lines;\n }\n /**\n * Expands the protobuf data to an intermediate Flat GeoJSON\n * data format, which maps closely to the binary data buffers.\n * It is similar to GeoJSON, but rather than storing the coordinates\n * in multidimensional arrays, we have a 1D `data` with all the\n * coordinates, and then index into this using the `indices`\n * parameter, e.g.\n *\n * geometry: {\n * type: 'Point', data: [1,2], indices: [0]\n * }\n * geometry: {\n * type: 'LineString', data: [1,2,3,4,...], indices: [0]\n * }\n * geometry: {\n * type: 'Polygon', data: [1,2,3,4,...], indices: [[0, 2]]\n * }\n * Thus the indices member lets us look up the relevant range\n * from the data array.\n * The Multi* versions of the above types share the same data\n * structure, just with multiple elements in the indices array\n */\n // eslint-disable-next-line complexity, max-statements\n loadFlatGeometry() {\n const pbf = this._pbf;\n pbf.pos = this._geometry;\n const endPos = pbf.readVarint() + pbf.pos;\n let cmd = 1;\n let cmdLen;\n let length = 0;\n let x = 0;\n let y = 0;\n let i = 0;\n // Note: I attempted to replace the `data` array with a\n // Float32Array, but performance was worse, both using\n // `set()` and direct index access. Also, we cannot\n // know how large the buffer should be, so it would\n // increase memory usage\n const indices = []; // Indices where geometries start\n const data = []; // Flat array of coordinate data\n while (pbf.pos < endPos) {\n if (length <= 0) {\n cmdLen = pbf.readVarint();\n cmd = cmdLen & 0x7;\n length = cmdLen >> 3;\n }\n length--;\n if (cmd === 1 || cmd === 2) {\n x += pbf.readSVarint();\n y += pbf.readSVarint();\n if (cmd === 1) {\n // New line\n indices.push(i);\n }\n data.push(x, y);\n i += 2;\n }\n else if (cmd === 7) {\n // Workaround for https://github.com/mapbox/mapnik-vector-tile/issues/90\n if (i > 0) {\n const start = indices[indices.length - 1]; // start index of polygon\n data.push(data[start], data[start + 1]); // closePolygon\n i += 2;\n }\n }\n else {\n throw new Error(`unknown command ${cmd}`);\n }\n }\n return { data, indices };\n }\n}\nfunction _toGeoJSONFeature(vtFeature, coords, transform) {\n let type = VectorTileFeature.types[vtFeature.type];\n let i;\n let j;\n let coordinates;\n switch (vtFeature.type) {\n case 1:\n const points = [];\n for (i = 0; i < coords.length; i++) {\n points[i] = coords[i][0];\n }\n coordinates = points;\n transform(coordinates, vtFeature.extent);\n break;\n case 2:\n coordinates = coords;\n for (i = 0; i < coordinates.length; i++) {\n transform(coordinates[i], vtFeature.extent);\n }\n break;\n case 3:\n coordinates = classifyRings(coords);\n for (i = 0; i < coordinates.length; i++) {\n for (j = 0; j < coordinates[i].length; j++) {\n transform(coordinates[i][j], vtFeature.extent);\n }\n }\n break;\n default:\n throw new Error('illegal vector tile type');\n }\n if (coordinates.length === 1) {\n // @ts-expect-error\n coordinates = coordinates[0];\n }\n else {\n type = `Multi${type}`;\n }\n const result = {\n type: 'Feature',\n geometry: {\n type: type,\n coordinates: coordinates\n },\n properties: vtFeature.properties\n };\n if (vtFeature.id !== null) {\n result.properties ||= {};\n result.properties.id = vtFeature.id;\n }\n return result;\n}\n// PBF READER UTILS\n/**\n *\n * @param tag\n * @param feature\n * @param pbf\n */\nfunction readFeature(tag, feature, pbf) {\n if (feature && pbf) {\n if (tag === 1)\n feature.id = pbf.readVarint();\n else if (tag === 2)\n readTag(pbf, feature);\n else if (tag === 3)\n feature.type = pbf.readVarint();\n else if (tag === 4)\n feature._geometry = pbf.pos;\n }\n}\n/**\n *\n * @param pbf\n * @param feature\n */\nfunction readTag(pbf, feature) {\n const end = pbf.readVarint() + pbf.pos;\n while (pbf.pos < end) {\n const key = feature._keys[pbf.readVarint()];\n const value = feature._values[pbf.readVarint()];\n feature.properties[key] = value;\n }\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright vis.gl contributors\nimport { VectorTileFeature } from \"./vector-tile-feature.js\";\nexport class VectorTileLayer {\n version;\n name;\n extent;\n length;\n _pbf;\n _keys;\n _values;\n _features;\n constructor(pbf, end) {\n // Public\n this.version = 1;\n this.name = '';\n this.extent = 4096;\n this.length = 0;\n // Private\n this._pbf = pbf;\n this._keys = [];\n this._values = [];\n this._features = [];\n pbf.readFields(readLayer, this, end);\n this.length = this._features.length;\n }\n /**\n * return feature `i` from this layer as a `VectorTileFeature`\n * @param index\n * @returns feature\n */\n getGeoJSONFeature(i) {\n if (i < 0 || i >= this._features.length) {\n throw new Error('feature index out of bounds');\n }\n this._pbf.pos = this._features[i];\n const end = this._pbf.readVarint() + this._pbf.pos;\n return new VectorTileFeature(this._pbf, end, this.extent, this._keys, this._values);\n }\n /**\n * return binary feature `i` from this layer as a `VectorTileFeature`\n *\n * @param index\n * @param geometryInfo\n * @returns binary feature\n */\n getBinaryFeature(i, geometryInfo) {\n if (i < 0 || i >= this._features.length) {\n throw new Error('feature index out of bounds');\n }\n this._pbf.pos = this._features[i];\n const end = this._pbf.readVarint() + this._pbf.pos;\n return new VectorTileFeature(this._pbf, end, this.extent, this._keys, this._values, geometryInfo);\n }\n}\n/**\n *\n * @param tag\n * @param layer\n * @param pbf\n */\nfunction readLayer(tag, layer, pbf) {\n if (layer && pbf) {\n if (tag === 15)\n layer.version = pbf.readVarint();\n else if (tag === 1)\n layer.name = pbf.readString();\n else if (tag === 5)\n layer.extent = pbf.readVarint();\n else if (tag === 2)\n layer._features.push(pbf.pos);\n else if (tag === 3)\n layer._keys.push(pbf.readString());\n else if (tag === 4)\n layer._values.push(readValueMessage(pbf));\n }\n}\n/**\n *\n * @param pbf\n * @returns value\n */\nfunction readValueMessage(pbf) {\n let value = null;\n const end = pbf.readVarint() + pbf.pos;\n while (pbf.pos < end) {\n const tag = pbf.readVarint() >> 3;\n value =\n tag === 1\n ? pbf.readString()\n : tag === 2\n ? pbf.readFloat()\n : tag === 3\n ? pbf.readDouble()\n : tag === 4\n ? pbf.readVarint64()\n : tag === 5\n ? pbf.readVarint()\n : tag === 6\n ? pbf.readSVarint()\n : tag === 7\n ? pbf.readBoolean()\n : null;\n }\n return value;\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright vis.gl contributors\n// This code is forked from https://github.com/mapbox/vector-tile-js under BSD 3-clause license.\nimport { VectorTileLayer } from \"./vector-tile-layer.js\";\nexport class VectorTile {\n layers;\n constructor(pbf, end) {\n this.layers = pbf.readFields(readTile, {}, end);\n }\n}\n/**\n *\n * @param tag\n * @param layers\n * @param pbf\n */\nfunction readTile(tag, layers, pbf) {\n if (tag === 3) {\n if (pbf) {\n const layer = new VectorTileLayer(pbf, pbf.readVarint() + pbf.pos);\n if (layer.length && layers) {\n layers[layer.name] = layer;\n }\n }\n }\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright vis.gl contributors\n// import type {MVTOptions} from './lib/types';\nimport { parseMVT } from \"./lib/parse-mvt.js\";\n// __VERSION__ is injected by babel-plugin-version-inline\n// @ts-ignore TS2304: Cannot find name '__VERSION__'.\nconst VERSION = typeof \"4.3.2\" !== 'undefined' ? \"4.3.2\" : 'latest';\n/**\n * Worker loader for the Mapbox Vector Tile format\n */\nexport const MVTWorkerLoader = {\n dataType: null,\n batchType: null,\n name: 'Mapbox Vector Tile',\n id: 'mvt',\n module: 'mvt',\n version: VERSION,\n // Note: ArcGIS uses '.pbf' extension and 'application/octet-stream'\n extensions: ['mvt', 'pbf'],\n mimeTypes: [\n // https://www.iana.org/assignments/media-types/application/vnd.mapbox-vector-tile\n 'application/vnd.mapbox-vector-tile',\n 'application/x-protobuf'\n // 'application/octet-stream'\n ],\n worker: true,\n category: 'geometry',\n options: {\n mvt: {\n shape: 'geojson',\n coordinates: 'local',\n layerProperty: 'layerName',\n layers: undefined,\n tileIndex: undefined\n }\n }\n};\n/**\n * Loader for the Mapbox Vector Tile format\n */\nexport const MVTLoader = {\n ...MVTWorkerLoader,\n parse: async (arrayBuffer, options) => parseMVT(arrayBuffer, options),\n parseSync: parseMVT,\n binary: true\n};\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\nimport { DataSource, resolvePath } from '@loaders.gl/loader-utils';\nimport { ImageLoader, getBinaryImageMetadata } from '@loaders.gl/images';\nimport { MVTLoader, TileJSONLoader } from '@loaders.gl/mvt';\n/** Creates an MVTTileSource */\nexport const MVTSource = {\n name: 'MVT',\n id: 'mvt',\n module: 'mvt',\n version: '0.0.0',\n extensions: ['mvt'],\n mimeTypes: ['application/octet-stream'],\n options: {\n mvt: {\n // TODO - add options here\n }\n },\n type: 'mvt',\n fromUrl: true,\n fromBlob: false,\n testURL: (url) => true,\n createDataSource(url, props) {\n return new MVTTileSource(url, props);\n }\n};\n/**\n * MVT data source for Mapbox Vector Tiles v1.\n */\n/**\n * A PMTiles data source\n * @note Can be either a raster or vector tile source depending on the contents of the PMTiles file.\n */\nexport class MVTTileSource extends DataSource {\n props;\n url;\n metadataUrl = null;\n data;\n schema = 'tms';\n metadata;\n extension;\n mimeType = null;\n constructor(url, props) {\n super(props);\n this.props = props;\n this.url = resolvePath(url);\n this.metadataUrl = props.mvt?.metadataUrl || `${this.url}/tilejson.json`;\n this.extension = props.mvt?.extension || '.png';\n this.data = this.url;\n this.getTileData = this.getTileData.bind(this);\n this.metadata = this.getMetadata();\n if (isURLTemplate(this.url)) {\n this.schema = 'template';\n }\n }\n // @ts-ignore - Metadata type misalignment\n async getMetadata() {\n if (!this.metadataUrl) {\n return null;\n }\n let response;\n try {\n // Annoyingly, on CORS errors, fetch doesn't use the response status/ok mechanism but instead throws\n // CORS errors are common when requesting an unavailable sub resource such as a metadata file or an unavailable tile)\n response = await this.fetch(this.metadataUrl);\n }\n catch (error) {\n // eslint-disable-next-line no-console\n console.error(error.message);\n return null;\n }\n if (!response.ok) {\n // eslint-disable-next-line no-console\n console.error(response.statusText);\n return null;\n }\n const tileJSON = await response.text();\n const metadata = TileJSONLoader.parseTextSync?.(tileJSON) || null;\n // TODO add metadata attributions\n // metadata.attributions = [...this.props.attributions, ...(metadata.attributions || [])];\n // if (metadata?.mimeType) {\n // this.mimeType = metadata?.tileMIMEType;\n // }\n return metadata;\n }\n getTileMIMEType() {\n return this.mimeType;\n }\n async getTile(parameters) {\n const { x, y, z } = parameters;\n const tileUrl = this.getTileURL(x, y, z);\n const response = await this.fetch(tileUrl);\n if (!response.ok) {\n return null;\n }\n const arrayBuffer = await response.arrayBuffer();\n return arrayBuffer;\n }\n // Tile Source interface implementation: deck.gl compatible API\n // TODO - currently only handles image tiles, not vector tiles\n async getTileData(parameters) {\n const { x, y, z } = parameters.index;\n // const metadata = await this.metadata;\n // mimeType = metadata?.tileMIMEType || 'application/vnd.mapbox-vector-tile';\n const arrayBuffer = await this.getTile({ x, y, z, layers: [] });\n if (arrayBuffer === null) {\n return null;\n }\n const imageMetadata = getBinaryImageMetadata(arrayBuffer);\n this.mimeType =\n this.mimeType || imageMetadata?.mimeType || 'application/vnd.mapbox-vector-tile';\n switch (this.mimeType) {\n case 'application/vnd.mapbox-vector-tile':\n return await this._parseVectorTile(arrayBuffer, { x, y, z, layers: [] });\n default:\n return await this._parseImageTile(arrayBuffer);\n }\n }\n // ImageTileSource interface implementation\n async getImageTile(tileParams) {\n const arrayBuffer = await this.getTile(tileParams);\n return arrayBuffer ? this._parseImageTile(arrayBuffer) : null;\n }\n async _parseImageTile(arrayBuffer) {\n return await ImageLoader.parse(arrayBuffer, this.loadOptions);\n }\n // VectorTileSource interface implementation\n async getVectorTile(tileParams) {\n const arrayBuffer = await this.getTile(tileParams);\n return arrayBuffer ? this._parseVectorTile(arrayBuffer, tileParams) : null;\n }\n async _parseVectorTile(arrayBuffer, tileParams) {\n const loadOptions = {\n shape: 'geojson-table',\n mvt: {\n coordinates: 'wgs84',\n tileIndex: { x: tileParams.x, y: tileParams.y, z: tileParams.z },\n ...this.loadOptions?.mvt\n },\n ...this.loadOptions\n };\n return await MVTLoader.parse(arrayBuffer, loadOptions);\n }\n getMetadataUrl() {\n return this.metadataUrl;\n }\n getTileURL(x, y, z) {\n switch (this.schema) {\n case 'xyz':\n return `${this.url}/${x}/${y}/${z}${this.extension}`;\n case 'tms':\n return `${this.url}/${z}/${x}/${y}${this.extension}`;\n case 'template':\n return getURLFromTemplate(this.url, x, y, z, '0');\n default:\n throw new Error(this.schema);\n }\n }\n}\nexport function isURLTemplate(s) {\n return /(?=.*{z})(?=.*{x})(?=.*({y}|{-y}))|(?=.*{x})(?=.*({y}|{-y})(?=.*{z}))/.test(s);\n}\nconst xRegex = new RegExp('{x}', 'g');\nconst yRegex = new RegExp('{y}', 'g');\nconst zRegex = new RegExp('{z}', 'g');\n/**\n * Get a URL from a URL template\n * @note copied from deck.gl/modules/geo-layers/src/tileset-2d/utils.ts\n * @param template - URL template\n * @param x - tile x coordinate\n * @param y - tile y coordinate\n * @param z - tile z coordinate\n * @param id - tile id\n * @returns URL\n */\nexport function getURLFromTemplate(template, x, y, z, id = '0') {\n if (Array.isArray(template)) {\n const i = stringHash(id) % template.length;\n template = template[i];\n }\n let url = template;\n url = url.replace(xRegex, String(x));\n url = url.replace(yRegex, String(y));\n url = url.replace(zRegex, String(z));\n // Back-compatible support for {-y}\n if (Number.isInteger(y) && Number.isInteger(z)) {\n url = url.replace(/\\{-y\\}/g, String(Math.pow(2, z) - y - 1));\n }\n return url;\n}\nfunction stringHash(s) {\n return Math.abs(s.split('').reduce((a, b) => ((a << 5) - a + b.charCodeAt(0)) | 0, 0));\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT AND ISC\n// Copyright (c) vis.gl contributors\n// Based on https://github.com/mapbox/geojson-vt under compatible ISC license\nimport { log } from '@loaders.gl/loader-utils';\nimport { deduceTableSchema } from '@loaders.gl/schema';\nimport { Stats, Stat } from '@probe.gl/stats';\nimport { createProtoTile } from \"./lib/vector-tiler/proto-tile.js\";\nimport { transformTile } from \"./lib/vector-tiler/transform-tile.js\"; // coordinate transformation\nimport { convertTileToGeoJSON } from \"./lib/vector-tiler/tile-to-geojson.js\"; // tile clipping and wrapping\nimport { convertFeaturesToProtoFeature } from \"./lib/vector-tiler/features/convert-feature.js\";\nimport { clipFeatures } from \"./lib/vector-tiler/features/clip-features.js\"; // stripe clipping algorithm\nimport { wrapFeatures } from \"./lib/vector-tiler/features/wrap-features.js\"; // date line processing\n/** Options to configure tiling */\nexport const TableTileSource = {\n name: 'TableTiler',\n id: 'table-tiler',\n version: '0.0.0',\n extensions: ['mvt'],\n mimeTypes: ['application/octet-stream'],\n options: {\n table: {\n coordinates: 'local',\n promoteId: undefined,\n maxZoom: 14,\n