@deck.gl/carto
Version:
CARTO official integration with Deck.gl. Build geospatial applications using CARTO and Deck.gl.
4 lines • 216 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../src/index.ts", "../src/layers/cluster-tile-layer.ts", "../src/layers/cluster-utils.ts", "../src/utils.ts", "../src/constants.ts", "../src/layers/quadbin-tileset-2d.ts", "../src/layers/h3-tileset-2d.ts", "../src/layers/quadbin-utils.ts", "../src/layers/schema/fast-pbf.ts", "../src/layers/schema/carto-tile.ts", "../src/layers/schema/carto-spatial-tile.ts", "../src/layers/schema/tile-loader-utils.ts", "../src/layers/schema/spatialjson-utils.ts", "../src/layers/schema/carto-spatial-tile-loader.ts", "../src/layers/utils.ts", "../src/layers/h3-tile-layer.ts", "../src/layers/spatial-index-tile-layer.ts", "../src/layers/heatmap-tile-layer.ts", "../src/layers/heatmap.ts", "../src/layers/post-process-utils.ts", "../src/layers/quadbin-tile-layer.ts", "../src/layers/quadbin-layer.ts", "../src/layers/point-label-layer.ts", "../src/layers/raster-tile-layer.ts", "../src/layers/raster-layer.ts", "../src/layers/raster-layer-vertex.glsl.ts", "../src/layers/schema/carto-raster-tile.ts", "../src/layers/schema/carto-raster-tile-loader.ts", "../src/layers/vector-tile-layer.ts", "../src/layers/schema/carto-properties-tile.ts", "../src/layers/schema/carto-properties-tile-loader.ts", "../src/layers/schema/carto-vector-tile-loader.ts", "../src/layers/label-utils.ts", "../src/basemap.ts", "../src/style/color-bins-style.ts", "../src/style/palette.ts", "../src/style/utils.ts", "../src/style/color-categories-style.ts", "../src/style/color-continuous-style.ts", "../src/api/fetch-map.ts", "../src/api/basemap.ts"],
"sourcesContent": ["// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\n// CARTO Layers\nimport {default as ClusterTileLayer} from './layers/cluster-tile-layer';\nimport {default as H3TileLayer} from './layers/h3-tile-layer';\nimport {default as HeatmapTileLayer} from './layers/heatmap-tile-layer';\nimport {default as PointLabelLayer} from './layers/point-label-layer';\nimport {default as QuadbinTileLayer} from './layers/quadbin-tile-layer';\nimport {default as RasterTileLayer} from './layers/raster-tile-layer';\nimport {default as VectorTileLayer} from './layers/vector-tile-layer';\n\n// Exports for playground/bindings\nconst CARTO_LAYERS = {\n ClusterTileLayer,\n H3TileLayer,\n HeatmapTileLayer,\n PointLabelLayer,\n QuadbinTileLayer,\n RasterTileLayer,\n VectorTileLayer\n};\nexport {\n CARTO_LAYERS,\n ClusterTileLayer,\n H3TileLayer,\n HeatmapTileLayer,\n PointLabelLayer,\n QuadbinTileLayer,\n RasterTileLayer,\n VectorTileLayer\n};\n\n// Internal Layers\nexport {default as _QuadbinLayer} from './layers/quadbin-layer';\nexport {default as _RasterLayer} from './layers/raster-layer';\nexport {default as _SpatialIndexTileLayer} from './layers/spatial-index-tile-layer';\n\n// Types\nexport type {ClusterTileLayerProps} from './layers/cluster-tile-layer';\nexport type {H3TileLayerProps} from './layers/h3-tile-layer';\nexport type {HeatmapTileLayerProps} from './layers/heatmap-tile-layer';\nexport type {PointLabelLayerProps} from './layers/point-label-layer';\nexport type {QuadbinLayerProps} from './layers/quadbin-layer';\nexport type {QuadbinTileLayerProps} from './layers/quadbin-tile-layer';\nexport type {RasterLayerProps} from './layers/raster-layer';\nexport type {RasterTileLayerProps} from './layers/raster-tile-layer';\nexport type {SpatialIndexTileLayerProps} from './layers/spatial-index-tile-layer';\nexport type {VectorTileLayerProps} from './layers/vector-tile-layer';\n\n// Helpers\nexport {\n default as BASEMAP,\n GOOGLE_BASEMAPS as _GOOGLE_BASEMAPS,\n getStyleUrl as _getStyleUrl,\n fetchStyle as _fetchStyle,\n applyLayerGroupFilters as _applyLayerGroupFilters,\n STYLE_LAYER_GROUPS as _STYLE_LAYER_GROUPS\n} from './basemap';\nexport {default as colorBins} from './style/color-bins-style';\nexport {default as colorCategories} from './style/color-categories-style';\nexport {default as colorContinuous} from './style/color-continuous-style';\nexport {fetchMap, LayerFactory} from './api/fetch-map';\nexport {fetchBasemapProps} from './api/basemap';\nexport type {\n FetchMapOptions,\n FetchMapResult,\n Basemap as _Basemap,\n MapLibreBasemap as _MapLibreBasemap,\n GoogleBasemap as _GoogleBasemap\n} from './api/index';\n\n// TODO(v10): Consider removing re-exports from '@carto/api-client' below.\n\nexport {\n CARTO_SOURCES,\n boundaryQuerySource,\n boundaryTableSource,\n h3QuerySource,\n h3TableSource,\n h3TilesetSource,\n rasterSource,\n quadbinQuerySource,\n quadbinTableSource,\n quadbinTilesetSource,\n vectorQuerySource,\n vectorTableSource,\n vectorTilesetSource,\n query,\n CartoAPIError,\n SOURCE_DEFAULTS\n} from '@carto/api-client';\n\nexport type {\n TilejsonResult,\n SourceOptions,\n QuerySourceOptions,\n TableSourceOptions,\n TilesetSourceOptions,\n BoundaryQuerySourceOptions,\n BoundaryTableSourceOptions,\n H3QuerySourceOptions,\n H3TableSourceOptions,\n H3TilesetSourceOptions,\n RasterSourceOptions,\n QuadbinQuerySourceOptions,\n QuadbinTableSourceOptions,\n QuadbinTilesetSourceOptions,\n VectorQuerySourceOptions,\n VectorTableSourceOptions,\n VectorTilesetSourceOptions,\n QueryParameters\n} from '@carto/api-client';\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\n/* eslint-disable no-shadow */\n\nimport {GeoJsonLayer, GeoJsonLayerProps} from '@deck.gl/layers';\nimport {\n TileLayer,\n _Tile2DHeader as Tile2DHeader,\n TileLayerProps,\n TileLayerPickingInfo\n} from '@deck.gl/geo-layers';\nimport {registerLoaders} from '@loaders.gl/core';\nimport {binaryToGeojson} from '@loaders.gl/gis';\nimport {BinaryFeatureCollection} from '@loaders.gl/schema';\nimport type {Feature, Geometry} from 'geojson';\n\nimport {\n Accessor,\n DefaultProps,\n CompositeLayer,\n _deepEqual as deepEqual,\n GetPickingInfoParams,\n Layer,\n LayersList,\n PickingInfo,\n WebMercatorViewport\n} from '@deck.gl/core';\n\nimport {\n aggregateTile,\n ClusteredFeaturePropertiesT,\n clustersToBinary,\n computeAggregationStats,\n extractAggregationProperties,\n ParsedQuadbinCell,\n ParsedQuadbinTile,\n ParsedH3Cell,\n ParsedH3Tile\n} from './cluster-utils';\nimport {DEFAULT_TILE_SIZE} from '../constants';\nimport QuadbinTileset2D from './quadbin-tileset-2d';\nimport H3Tileset2D, {getHexagonResolution} from './h3-tileset-2d';\nimport {getQuadbinPolygon} from './quadbin-utils';\nimport {getResolution, cellToLatLng} from 'h3-js';\nimport CartoSpatialTileLoader from './schema/carto-spatial-tile-loader';\nimport {TilejsonPropType, mergeLoadOptions} from './utils';\nimport type {TilejsonResult} from '@carto/api-client';\n\nregisterLoaders([CartoSpatialTileLoader]);\n\nfunction getScheme(tilesetClass: typeof H3Tileset2D | typeof QuadbinTileset2D): 'h3' | 'quadbin' {\n if (tilesetClass === H3Tileset2D) return 'h3';\n if (tilesetClass === QuadbinTileset2D) return 'quadbin';\n throw new Error('Invalid tileset class');\n}\n\nconst defaultProps: DefaultProps<ClusterTileLayerProps> = {\n data: TilejsonPropType,\n clusterLevel: {type: 'number', value: 5, min: 1},\n getPosition: {\n type: 'accessor',\n value: ({id}) => {\n // Determine scheme based on ID type: H3 uses string IDs, Quadbin uses bigint IDs\n if (typeof id === 'string') {\n const [lat, lng] = cellToLatLng(id);\n return [lng, lat];\n }\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion\n return getQuadbinPolygon(id as bigint, 0.5).slice(2, 4) as [number, number];\n }\n },\n getWeight: {type: 'accessor', value: 1},\n refinementStrategy: 'no-overlap',\n tileSize: DEFAULT_TILE_SIZE\n};\n\nexport type ClusterTileLayerPickingInfo<FeaturePropertiesT = {}> = TileLayerPickingInfo<\n ParsedQuadbinTile<FeaturePropertiesT> | ParsedH3Tile<FeaturePropertiesT>,\n PickingInfo<Feature<Geometry, FeaturePropertiesT>>\n>;\n\n/** All properties supported by ClusterTileLayer. */\nexport type ClusterTileLayerProps<FeaturePropertiesT = unknown> =\n _ClusterTileLayerProps<FeaturePropertiesT> &\n Omit<\n TileLayerProps<ParsedQuadbinTile<FeaturePropertiesT> | ParsedH3Tile<FeaturePropertiesT>>,\n 'data'\n >;\n\n/** Properties added by ClusterTileLayer. */\ntype _ClusterTileLayerProps<FeaturePropertiesT> = Omit<\n GeoJsonLayerProps<ClusteredFeaturePropertiesT<FeaturePropertiesT>>,\n 'data'\n> & {\n data: null | TilejsonResult | Promise<TilejsonResult>;\n\n /**\n * The number of aggregation levels to cluster cells by. Larger values increase\n * the clustering radius, with an increment of `clusterLevel` doubling the radius.\n *\n * @default 5\n */\n clusterLevel?: number;\n\n /**\n * The (average) position of points in a cell used for clustering.\n * If not supplied the center of the quadbin cell or H3 cell is used.\n *\n * @default cell center\n */\n getPosition?: Accessor<\n ParsedQuadbinCell<FeaturePropertiesT> | ParsedH3Cell<FeaturePropertiesT>,\n [number, number]\n >;\n\n /**\n * The weight of each cell used for clustering.\n *\n * @default 1\n */\n getWeight?: Accessor<\n ParsedQuadbinCell<FeaturePropertiesT> | ParsedH3Cell<FeaturePropertiesT>,\n number\n >;\n};\n\nclass ClusterGeoJsonLayer<\n FeaturePropertiesT extends {} = {},\n ExtraProps extends {} = {}\n> extends TileLayer<\n ParsedQuadbinTile<FeaturePropertiesT> | ParsedH3Tile<FeaturePropertiesT>,\n ExtraProps & Required<_ClusterTileLayerProps<FeaturePropertiesT>>\n> {\n static layerName = 'ClusterGeoJsonLayer';\n static defaultProps = defaultProps;\n state!: TileLayer<FeaturePropertiesT>['state'] & {\n data: BinaryFeatureCollection;\n clusterIds: (bigint | string)[];\n hoveredFeatureId: bigint | string | number | null;\n highlightColor: number[];\n aggregationCache: WeakMap<any, Map<number, ClusteredFeaturePropertiesT<FeaturePropertiesT>[]>>;\n scheme: string | null;\n };\n\n initializeState() {\n super.initializeState();\n this.state.aggregationCache = new WeakMap();\n this.state.scheme = getScheme(this.props.TilesetClass as any);\n }\n\n updateState(opts) {\n const {props} = opts;\n const scheme = getScheme(props.TilesetClass);\n if (this.state.scheme !== scheme) {\n // Clear caches when scheme changes\n this.setState({scheme, tileset: null});\n this.state.aggregationCache = new WeakMap();\n }\n\n super.updateState(opts);\n }\n\n // eslint-disable-next-line max-statements\n renderLayers(): Layer | null | LayersList {\n const visibleTiles = this.state.tileset?.tiles.filter((tile: Tile2DHeader) => {\n return tile.isLoaded && tile.content && this.state.tileset!.isTileVisible(tile);\n }) as Tile2DHeader<ParsedQuadbinTile<FeaturePropertiesT> | ParsedH3Tile<FeaturePropertiesT>>[];\n if (!visibleTiles?.length || !this.state.tileset) {\n return null;\n }\n visibleTiles.sort((a, b) => b.zoom - a.zoom);\n const {getPosition, getWeight} = this.props;\n const {aggregationCache, scheme} = this.state;\n\n const isH3 = scheme === 'h3';\n\n const properties = extractAggregationProperties(visibleTiles[0]);\n const data = [] as ClusteredFeaturePropertiesT<FeaturePropertiesT>[];\n let needsUpdate = false;\n\n const aggregationLevels = this._getAggregationLevels(visibleTiles);\n\n for (const tile of visibleTiles) {\n // Calculate aggregation based on viewport zoom\n let tileAggregationCache = aggregationCache.get(tile.content);\n if (!tileAggregationCache) {\n tileAggregationCache = new Map();\n aggregationCache.set(tile.content, tileAggregationCache);\n }\n const didAggregate = aggregateTile(\n tile,\n tileAggregationCache,\n aggregationLevels,\n properties,\n getPosition,\n getWeight,\n isH3 ? 'h3' : 'quadbin'\n );\n needsUpdate ||= didAggregate;\n data.push(...tileAggregationCache.get(aggregationLevels)!);\n }\n\n data.sort((a, b) => Number(b.count - a.count));\n\n const clusterIds = data?.map((tile: any) => tile.id);\n needsUpdate ||= !deepEqual(clusterIds, this.state.clusterIds, 1);\n this.setState({clusterIds});\n\n if (needsUpdate) {\n const stats = computeAggregationStats(data, properties);\n const binaryData = clustersToBinary(data);\n binaryData.points.attributes = {stats};\n this.setState({data: binaryData});\n }\n\n const props = {\n ...this.props,\n id: 'clusters',\n data: this.state.data,\n dataComparator: (data?: BinaryFeatureCollection, oldData?: BinaryFeatureCollection) => {\n const newIds = data?.points?.properties?.map((tile: any) => tile.id);\n const oldIds = oldData?.points?.properties?.map((tile: any) => tile.id);\n return deepEqual(newIds, oldIds, 1);\n }\n } as GeoJsonLayerProps<ClusteredFeaturePropertiesT<FeaturePropertiesT>>;\n\n return new GeoJsonLayer(this.getSubLayerProps(props));\n }\n\n getPickingInfo(params: GetPickingInfoParams): ClusterTileLayerPickingInfo<FeaturePropertiesT> {\n const info = params.info as TileLayerPickingInfo<\n ParsedQuadbinTile<FeaturePropertiesT> | ParsedH3Tile<FeaturePropertiesT>\n >;\n\n if (info.index !== -1) {\n const {data} = params.sourceLayer!.props;\n info.object = binaryToGeojson(data as BinaryFeatureCollection, {\n globalFeatureId: info.index\n }) as Feature;\n }\n\n return info;\n }\n\n protected _updateAutoHighlight(info: PickingInfo): void {\n for (const layer of this.getSubLayers()) {\n layer.updateAutoHighlight(info);\n }\n }\n\n filterSubLayer() {\n return true;\n }\n\n private _getAggregationLevels(visibleTiles: Tile2DHeader[]): number {\n const isH3 = this.state.scheme === 'h3';\n const firstTile = visibleTiles[0];\n\n // Resolution of data present in tiles\n let tileResolution;\n\n // Resolution of tiles that should be (eventually) visible in the viewport\n let viewportResolution;\n if (isH3) {\n tileResolution = getResolution(firstTile.id);\n viewportResolution = getHexagonResolution(\n this.context.viewport as WebMercatorViewport,\n (this.state.tileset as any).opts.tileSize\n );\n } else {\n tileResolution = firstTile.zoom;\n viewportResolution = this.context.viewport.zoom;\n }\n\n const resolutionDiff = Math.round(viewportResolution - tileResolution);\n const aggregationLevels = Math.round(this.props.clusterLevel) - resolutionDiff;\n return aggregationLevels;\n }\n}\n\n// Adapter layer around ClusterLayer that converts tileJSON into TileLayer API\nexport default class ClusterTileLayer<\n FeaturePropertiesT = any,\n ExtraProps extends {} = {}\n> extends CompositeLayer<ExtraProps & Required<_ClusterTileLayerProps<FeaturePropertiesT>>> {\n static layerName = 'ClusterTileLayer';\n static defaultProps = defaultProps;\n\n getLoadOptions(): any {\n const tileJSON = this.props.data as TilejsonResult;\n const scheme = tileJSON && 'scheme' in tileJSON ? tileJSON.scheme : 'quadbin';\n return mergeLoadOptions(super.getLoadOptions(), {\n fetch: {headers: {Authorization: `Bearer ${tileJSON.accessToken}`}},\n cartoSpatialTile: {scheme}\n });\n }\n\n renderLayers(): Layer | null | LayersList {\n const tileJSON = this.props.data as TilejsonResult;\n if (!tileJSON) return null;\n\n const {tiles: data, maxresolution: maxZoom} = tileJSON;\n const isH3 = tileJSON && 'scheme' in tileJSON && tileJSON.scheme === 'h3';\n const TilesetClass = isH3 ? H3Tileset2D : QuadbinTileset2D;\n\n return [\n // @ts-ignore\n new ClusterGeoJsonLayer(this.props, {\n id: `cluster-geojson-layer-${this.props.id}`,\n data,\n // TODO: Tileset2D should be generic over TileIndex type\n TilesetClass: TilesetClass as any,\n maxZoom,\n loadOptions: this.getLoadOptions()\n })\n ];\n }\n}\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {cellToParent} from 'quadbin';\nimport {cellToParent as h3CellToParent, getResolution as getH3Resolution} from 'h3-js';\nimport {_Tile2DHeader as Tile2DHeader} from '@deck.gl/geo-layers';\nimport {Accessor, log} from '@deck.gl/core';\nimport {BinaryFeatureCollection} from '@loaders.gl/schema';\nimport {createBinaryPointFeature, createEmptyBinary} from '../utils';\n\nexport type Aggregation = 'any' | 'average' | 'count' | 'min' | 'max' | 'sum';\nexport type AggregationProperties<FeaturePropertiesT> = {\n aggregation: Aggregation;\n name: keyof FeaturePropertiesT;\n}[];\nexport type ClusteredFeaturePropertiesT<FeaturePropertiesT> = FeaturePropertiesT & {\n id: bigint | string;\n count: number;\n position: [number, number];\n};\nexport type ParsedQuadbinCell<FeaturePropertiesT> = {id: bigint; properties: FeaturePropertiesT};\nexport type ParsedQuadbinTile<FeaturePropertiesT> = ParsedQuadbinCell<FeaturePropertiesT>[];\nexport type ParsedH3Cell<FeaturePropertiesT> = {id: string; properties: FeaturePropertiesT};\nexport type ParsedH3Tile<FeaturePropertiesT> = ParsedH3Cell<FeaturePropertiesT>[];\n\n/**\n * Aggregates tile by specified properties, caching result in tile.userData\n *\n * @returns true if data was aggregated, false if cache used\n */\nexport function aggregateTile<FeaturePropertiesT>(\n tile: Tile2DHeader<ParsedQuadbinTile<FeaturePropertiesT> | ParsedH3Tile<FeaturePropertiesT>>,\n tileAggregationCache: Map<number, ClusteredFeaturePropertiesT<FeaturePropertiesT>[]>,\n aggregationLevels: number,\n properties: AggregationProperties<FeaturePropertiesT> = [],\n getPosition: Accessor<\n ParsedQuadbinCell<FeaturePropertiesT> | ParsedH3Cell<FeaturePropertiesT>,\n [number, number]\n >,\n getWeight: Accessor<\n ParsedQuadbinCell<FeaturePropertiesT> | ParsedH3Cell<FeaturePropertiesT>,\n number\n >,\n scheme: 'quadbin' | 'h3' = 'quadbin'\n): boolean {\n if (!tile.content) return false;\n\n // Aggregate on demand and cache result\n if (!tile.userData) tile.userData = {};\n const cell0 = tileAggregationCache.get(aggregationLevels)?.[0];\n if (cell0) {\n // Have already aggregated this tile\n if (properties.every(property => property.name in cell0)) {\n // Use cached result\n return false;\n }\n\n // Aggregated properties have changed, re-aggregate\n tileAggregationCache.clear();\n }\n\n const out: Record<string, any> = {};\n for (const cell of tile.content) {\n let id = cell.id;\n const position = typeof getPosition === 'function' ? getPosition(cell, {} as any) : getPosition;\n\n // Aggregate by parent rid\n for (let i = 0; i < aggregationLevels - 1; i++) {\n if (scheme === 'h3') {\n const currentResolution = getH3Resolution(id as string);\n id = h3CellToParent(id as string, Math.max(0, currentResolution - 1));\n } else {\n id = cellToParent(id as bigint);\n }\n }\n\n // Use string key for both H3 and Quadbin to avoid TypeScript Record<bigint, any> issues\n // https://github.com/microsoft/TypeScript/issues/46395\n const parentId = String(id);\n if (!(parentId in out)) {\n out[parentId] = {id, count: 0, position: [0, 0]};\n for (const {name, aggregation} of properties) {\n if (aggregation === 'any') {\n // Just pick first value for ANY\n out[parentId][name] = cell.properties[name];\n } else {\n out[parentId][name] = 0;\n }\n }\n }\n // Layout props\n const prevTotalW = out[parentId].count;\n out[parentId].count += typeof getWeight === 'function' ? getWeight(cell, {} as any) : getWeight;\n\n const totalW = out[parentId].count;\n const W = totalW - prevTotalW;\n out[parentId].position[0] = (prevTotalW * out[parentId].position[0] + W * position[0]) / totalW;\n out[parentId].position[1] = (prevTotalW * out[parentId].position[1] + W * position[1]) / totalW;\n\n // Re-aggregate other properties so clusters can be styled\n for (const {name, aggregation} of properties) {\n const prevValue = out[parentId][name];\n const value = cell.properties[name] as number;\n if (aggregation === 'average') {\n out[parentId][name] = (prevTotalW * prevValue + W * value) / totalW;\n } else if (aggregation === 'count' || aggregation === 'sum') {\n out[parentId][name] = prevValue + value;\n } else if (aggregation === 'max') {\n out[parentId][name] = Math.max(prevValue, value);\n } else if (aggregation === 'min') {\n out[parentId][name] = Math.min(prevValue, value);\n }\n }\n }\n\n tileAggregationCache.set(aggregationLevels, Object.values(out));\n return true;\n}\n\nexport function extractAggregationProperties<FeaturePropertiesT extends {}>(\n tile: Tile2DHeader<ParsedQuadbinTile<FeaturePropertiesT> | ParsedH3Tile<FeaturePropertiesT>>\n): AggregationProperties<FeaturePropertiesT> {\n const properties: AggregationProperties<FeaturePropertiesT> = [];\n const validAggregations: Aggregation[] = ['any', 'average', 'count', 'min', 'max', 'sum'];\n for (const name of Object.keys(tile.content![0].properties)) {\n let aggregation = name.split('_').pop()!.toLowerCase() as Aggregation;\n if (!validAggregations.includes(aggregation)) {\n log.warn(`No valid aggregation present in ${name} property`)();\n aggregation = 'any';\n }\n properties.push({name: name as keyof FeaturePropertiesT, aggregation});\n }\n\n return properties;\n}\n\nexport function computeAggregationStats<FeaturePropertiesT>(\n data: ClusteredFeaturePropertiesT<FeaturePropertiesT>[],\n properties: AggregationProperties<FeaturePropertiesT>\n) {\n const stats = {} as Record<keyof FeaturePropertiesT, {min: number; max: number}>;\n for (const {name, aggregation} of properties) {\n stats[name] = {min: Infinity, max: -Infinity};\n if (aggregation !== 'any') {\n for (const d of data) {\n stats[name].min = Math.min(stats[name].min, d[name] as number);\n stats[name].max = Math.max(stats[name].max, d[name] as number);\n }\n }\n }\n\n return stats;\n}\n\ntype BinaryFeatureCollectionWithStats<FeaturePropertiesT> = Omit<\n BinaryFeatureCollection,\n 'points'\n> & {\n points: BinaryFeatureCollection['points'] & {\n attributes?: {\n stats: Record<keyof FeaturePropertiesT, {min: number; max: number}>;\n };\n };\n};\n\nexport function clustersToBinary<FeaturePropertiesT>(\n data: ClusteredFeaturePropertiesT<FeaturePropertiesT>[]\n): BinaryFeatureCollectionWithStats<FeaturePropertiesT> {\n const positions = new Float32Array(data.length * 2);\n const featureIds = new Uint16Array(data.length);\n for (let i = 0; i < data.length; i++) {\n positions.set(data[i].position, 2 * i);\n featureIds[i] = i;\n }\n\n return {\n ...createEmptyBinary(),\n points: createBinaryPointFeature(positions, featureIds, featureIds, {}, data)\n };\n}\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {BinaryFeatureCollection, BinaryPointFeature} from '@loaders.gl/schema';\nimport {log} from '@deck.gl/core';\nimport type {Properties, NumericProps} from './layers/schema/spatialjson-utils';\n\nexport function assert(condition: unknown, message?: string): asserts condition {\n log.assert(condition, message);\n}\n\n// Returns a Proxy object that allows accessing binary data\n// as if it were JSON properties\nexport function createBinaryProxy(\n data: {numericProps: NumericProps; properties: Properties[]},\n index: number\n) {\n const {properties, numericProps} = data;\n return new Proxy(properties[index] || {}, {\n get(target, property) {\n if (property in numericProps) {\n return numericProps[property as string].value[index];\n }\n return target[property as string];\n },\n\n has(target, property) {\n return property in numericProps || property in target;\n },\n\n ownKeys(target) {\n return [...Object.keys(numericProps), ...Reflect.ownKeys(target)];\n },\n\n getOwnPropertyDescriptor(target, prop) {\n return {enumerable: true, configurable: true};\n }\n });\n}\n\nexport function getWorkerUrl(id: string, version: string) {\n // For local testing `yarn build-workers` and then host `modules/carto/dist/`\n // return `http://localhost:8081/dist/${id}-worker.js`;\n return `https://unpkg.com/@deck.gl/carto@${version}/dist/${id}-worker.js`;\n}\n\nexport function scaleIdentity() {\n let unknown;\n\n function scale(x) {\n return x === null ? unknown : x;\n }\n\n scale.invert = scale;\n\n scale.domain = scale.range = d => d;\n\n scale.unknown = u => {\n if (u) {\n unknown = u;\n }\n\n return unknown;\n };\n\n scale.copy = () => {\n const scaleCopy = scaleIdentity();\n scaleCopy.unknown(unknown);\n return scaleCopy;\n };\n\n return scale;\n}\n\nexport const isObject: (x: unknown) => boolean = x => x !== null && typeof x === 'object';\n\nexport const isPureObject: (x: any) => boolean = x =>\n isObject(x) && x.constructor === {}.constructor;\n\n// Helpers for binary data\nconst EMPTY_UINT16ARRAY = new Uint16Array();\nconst EMPTY_BINARY_PROPS: Omit<BinaryPointFeature, 'type'> = {\n positions: {value: new Float32Array(), size: 2},\n properties: [],\n numericProps: {},\n featureIds: {value: EMPTY_UINT16ARRAY, size: 1},\n globalFeatureIds: {value: EMPTY_UINT16ARRAY, size: 1}\n};\n\nexport function createEmptyBinary(): Required<BinaryFeatureCollection> {\n return {\n shape: 'binary-feature-collection',\n points: {\n type: 'Point',\n ...EMPTY_BINARY_PROPS\n },\n lines: {\n type: 'LineString',\n pathIndices: {value: EMPTY_UINT16ARRAY, size: 1},\n ...EMPTY_BINARY_PROPS\n },\n polygons: {\n type: 'Polygon',\n polygonIndices: {value: EMPTY_UINT16ARRAY, size: 1},\n primitivePolygonIndices: {value: EMPTY_UINT16ARRAY, size: 1},\n ...EMPTY_BINARY_PROPS\n }\n };\n}\n\nexport function createBinaryPointFeature(\n positions: number[] | Float32Array | Float64Array,\n featureIds: number[] | Uint16Array | Uint32Array,\n globalFeatureIds: number[] | Uint16Array | Uint32Array,\n numericProps: NumericProps,\n properties: Properties,\n size: 2 | 3 = 2\n): BinaryPointFeature {\n return {\n type: 'Point',\n positions: {value: new Float32Array(positions), size},\n featureIds: {value: new Uint16Array(featureIds), size: 1},\n globalFeatureIds: {value: new Uint32Array(globalFeatureIds), size: 1},\n numericProps,\n properties\n };\n}\n\nexport function initializeNumericProps(\n numPoints: number,\n sourceProps?: NumericProps\n): NumericProps {\n const numericProps: NumericProps = {};\n if (sourceProps) {\n Object.keys(sourceProps).forEach(prop => {\n numericProps[prop] = {value: new Float32Array(numPoints), size: 1};\n });\n }\n return numericProps;\n}\n\nexport function copyNumericProps(\n sourceProps: NumericProps,\n targetProps: NumericProps,\n sourceIndex: number,\n targetIndex: number\n): void {\n Object.keys(sourceProps).forEach(prop => {\n targetProps[prop].value[targetIndex] = sourceProps[prop].value[sourceIndex];\n });\n}\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nexport const DEFAULT_TILE_SIZE = 512;\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {_Tileset2D as Tileset2D} from '@deck.gl/geo-layers';\nimport {bigIntToHex, cellToParent, cellToTile, getResolution, tileToCell} from 'quadbin';\n\n// For calculations bigint representation is used, but\n// for constructing URL also provide the hexidecimal value\ntype QuadbinTileIndex = {q: bigint; i?: string};\n\nexport default class QuadbinTileset2D extends Tileset2D {\n // @ts-expect-error for spatial indices, TileSet2d should be parametrized by TileIndexT\n getTileIndices(opts): QuadbinTileIndex[] {\n return super\n .getTileIndices(opts)\n .map(tileToCell)\n .map(q => ({q, i: bigIntToHex(q)}));\n }\n\n // @ts-expect-error TileIndex must be generic\n getTileId({q, i}: QuadbinTileIndex): string {\n return i || bigIntToHex(q);\n }\n\n // @ts-expect-error TileIndex must be generic\n getTileMetadata({q}: QuadbinTileIndex) {\n return super.getTileMetadata(cellToTile(q));\n }\n\n // @ts-expect-error TileIndex must be generic\n getTileZoom({q}: QuadbinTileIndex): number {\n return Number(getResolution(q));\n }\n\n // @ts-expect-error TileIndex must be generic\n getParentIndex({q}: QuadbinTileIndex): QuadbinTileIndex {\n return {q: cellToParent(q)};\n }\n}\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {_Tileset2D as Tileset2D, GeoBoundingBox} from '@deck.gl/geo-layers';\nimport {\n polygonToCells,\n latLngToCell,\n getResolution,\n cellToBoundary,\n cellToParent,\n gridDisk,\n edgeLength,\n UNITS,\n originToDirectedEdges\n} from 'h3-js';\n\nexport type H3TileIndex = {i: string};\n\nconst MAX_LATITUDE = 85.051128;\n\n/**\n * `polygonToCells()` fills based on hexagon center, this function will\n * pad the bounds such that all cells that overlap the bounds will be included.\n *\n * @param bbox - The bounding box to pad.\n * @param resolution - The resolution of the hexagons.\n * @param scale - The scale of the buffer. 1 is to pad by the max edge length of the tile cell.\n * @returns The padded bounding box.\n */\nfunction padBoundingBox(\n {west, north, east, south}: GeoBoundingBox,\n resolution: number,\n scale: number = 1.0\n): GeoBoundingBox {\n const corners = [\n [north, east],\n [south, east],\n [south, west],\n [north, west]\n ];\n const cornerCells = corners.map(c => latLngToCell(c[0], c[1], resolution));\n const cornerEdgeLengths = cornerCells.map(\n c => (Math.max(...originToDirectedEdges(c).map(e => edgeLength(e, UNITS.rads))) * 180) / Math.PI\n );\n const bufferLat = Math.max(...cornerEdgeLengths) * scale;\n const bufferLon = Math.min(180, bufferLat / Math.cos((((north + south) / 2) * Math.PI) / 180));\n\n return {\n north: Math.min(north + bufferLat, MAX_LATITUDE),\n east: east + bufferLon,\n south: Math.max(south - bufferLat, -MAX_LATITUDE),\n west: west - bufferLon\n };\n}\n\nfunction getHexagonsInBoundingBox(\n {west, north, east, south}: GeoBoundingBox,\n resolution: number\n): string[] {\n const longitudeSpan = Math.abs(east - west);\n if (longitudeSpan > 180) {\n // This is a known issue in h3-js: polygonToCells does not work correctly\n // when longitude span is larger than 180 degrees.\n const nSegments = Math.ceil(longitudeSpan / 180);\n let h3Indices: string[] = [];\n for (let s = 0; s < nSegments; s++) {\n const segmentWest = west + s * 180;\n const segmentEast = Math.min(segmentWest + 179.9999999, east);\n h3Indices = h3Indices.concat(\n getHexagonsInBoundingBox({west: segmentWest, north, east: segmentEast, south}, resolution)\n );\n }\n return [...new Set(h3Indices)];\n }\n\n const polygon = [\n [north, east],\n [south, east],\n [south, west],\n [north, west],\n [north, east]\n ];\n return polygonToCells(polygon, resolution);\n}\n\nfunction tileToBoundingBox(index: string): GeoBoundingBox {\n const coordinates = cellToBoundary(index);\n const latitudes = coordinates.map(c => c[0]);\n const longitudes = coordinates.map(c => c[1]);\n const west = Math.min(...longitudes);\n const south = Math.min(...latitudes);\n const east = Math.max(...longitudes);\n const north = Math.max(...latitudes);\n const bbox = {west, south, east, north};\n\n // H3 child cells extend beyond their parent's boundary forming a \"snowflake\"\n // fractal pattern. The required buffer is approximately 10% of the\n // edge length of the tile cell, add a bit more to be safe.\n return padBoundingBox(bbox, getResolution(index), 0.12);\n}\n\n// Resolution conversion function. Takes a WebMercatorViewport and returns\n// a H3 resolution such that the screen space size of the hexagons is\n// similar\n// Relative scale factor (0 = no biasing, 2 = a few hexagons cover view)\nconst BIAS = 2;\nexport function getHexagonResolution(\n viewport: {zoom: number; latitude: number},\n tileSize: number\n): number {\n // Difference in given tile size compared to deck's internal 512px tile size,\n // expressed as an offset to the viewport zoom.\n const zoomOffset = Math.log2(tileSize / 512);\n const hexagonScaleFactor = (2 / 3) * (viewport.zoom - zoomOffset);\n const latitudeScaleFactor = Math.log(1 / Math.cos((Math.PI * viewport.latitude) / 180));\n\n // Clip and bias\n return Math.max(0, Math.floor(hexagonScaleFactor + latitudeScaleFactor - BIAS));\n}\n\nexport default class H3Tileset2D extends Tileset2D {\n /**\n * Returns all tile indices in the current viewport. If the current zoom level is smaller\n * than minZoom, return an empty array. If the current zoom level is greater than maxZoom,\n * return tiles that are on maxZoom.\n */\n // @ts-expect-error Tileset2D should be generic over TileIndex\n getTileIndices({viewport, minZoom, maxZoom}): H3TileIndex[] {\n if (viewport.latitude === undefined) return [];\n const [west, south, east, north] = viewport.getBounds();\n const {tileSize} = this.opts;\n\n let z = getHexagonResolution(viewport, tileSize);\n let indices: string[];\n if (typeof minZoom === 'number' && Number.isFinite(minZoom) && z < minZoom) {\n // TODO support `extent` prop\n return [];\n }\n if (typeof maxZoom === 'number' && Number.isFinite(maxZoom) && z > maxZoom) {\n z = maxZoom;\n\n // Once we are at max zoom, getHexagonsInBoundingBox doesn't work, simply\n // get a ring centered on the hexagon in the viewport center\n const center = latLngToCell(viewport.latitude, viewport.longitude, maxZoom);\n indices = gridDisk(center, 1);\n } else {\n const paddedBounds = padBoundingBox({west, north, east, south}, z);\n indices = getHexagonsInBoundingBox(paddedBounds, z);\n }\n\n return indices.map(i => ({i}));\n }\n\n // @ts-expect-error Tileset2D should be generic over TileIndex\n getTileId({i}: H3TileIndex): string {\n return i;\n }\n\n // @ts-expect-error Tileset2D should be generic over TileIndex\n getTileMetadata({i}: H3TileIndex) {\n return {bbox: tileToBoundingBox(i)};\n }\n\n // @ts-expect-error Tileset2D should be generic over TileIndex\n getTileZoom({i}: H3TileIndex): number {\n return getResolution(i);\n }\n\n // @ts-expect-error Tileset2D should be generic over TileIndex\n getParentIndex(index: H3TileIndex): H3TileIndex {\n const resolution = getResolution(index.i);\n const i = cellToParent(index.i, resolution - 1);\n return {i};\n }\n}\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {worldToLngLat} from '@math.gl/web-mercator';\nimport {cellToTile} from 'quadbin';\n\nconst TILE_SIZE = 512;\n\nexport function quadbinToOffset(quadbin: bigint): [number, number, number] {\n const {x, y, z} = cellToTile(quadbin);\n const scale = TILE_SIZE / (1 << z);\n return [x * scale, TILE_SIZE - y * scale, scale];\n}\n\nexport function quadbinToWorldBounds(quadbin: bigint, coverage: number): [number[], number[]] {\n const [xOffset, yOffset, scale] = quadbinToOffset(quadbin);\n return [\n [xOffset, yOffset],\n [xOffset + coverage * scale, yOffset - coverage * scale]\n ];\n}\n\nexport function getQuadbinPolygon(quadbin: bigint, coverage = 1): number[] {\n const [topLeft, bottomRight] = quadbinToWorldBounds(quadbin, coverage);\n const [w, n] = worldToLngLat(topLeft);\n const [e, s] = worldToLngLat(bottomRight);\n return [e, n, e, s, w, s, w, n, e, n];\n}\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {GZipCompression} from '@loaders.gl/compression';\n\ntype ReadPackedOptions = {\n compression: null | 'gzip';\n};\n\n// Optimized (100X speed improvement) reading function for binary data\nexport function readPackedTypedArray(TypedArray, pbf, obj, options?: ReadPackedOptions) {\n const end = pbf.type === 2 ? pbf.readVarint() + pbf.pos : pbf.pos + 1;\n const data = pbf.buf.buffer.slice(pbf.pos, end);\n\n if (options?.compression === 'gzip') {\n const compression = new GZipCompression();\n const decompressedData = compression.decompressSync(data);\n obj.value = new TypedArray(decompressedData);\n } else {\n obj.value = new TypedArray(data);\n }\n\n pbf.pos = end;\n return obj.value;\n}\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {readPackedTypedArray} from './fast-pbf';\nimport {TypedArray} from '@loaders.gl/loader-utils';\n\n// KeyValueObject ========================================\ninterface KeyValueObject {\n key: string;\n value: string;\n}\n\nclass KeyValueObjectReader {\n static read(pbf, end?: number): KeyValueObject {\n return pbf.readFields(KeyValueObjectReader._readField, {key: '', value: null}, end);\n }\n static _readField(this: void, tag: number, obj: KeyValueObject, pbf) {\n if (tag === 1) obj.key = pbf.readString();\n else if (tag === 2) obj.value = pbf.readString();\n }\n}\n\n// Properties ========================================\n\nexport class PropertiesReader {\n static read(pbf, end?: number) {\n return pbf.readFields(PropertiesReader._readField, {}, end);\n }\n static _readField(this: void, tag: number, obj: Record<string, string>, pbf) {\n if (tag === 1) {\n const {key, value} = KeyValueObjectReader.read(pbf, pbf.readVarint() + pbf.pos);\n obj[key] = value;\n }\n }\n}\n\n// Doubles ========================================\n\ninterface Doubles {\n value: Float64Array;\n size: number;\n}\n\nclass DoublesReader {\n static read(pbf, end?: number): Doubles {\n const {value, size} = pbf.readFields(DoublesReader._readField, {value: [], size: 0}, end);\n return {value, size};\n }\n static _readField(this: void, tag: number, obj, pbf) {\n if (tag === 1) readPackedTypedArray(Float64Array, pbf, obj);\n else if (tag === 2) obj.size = pbf.readVarint(true);\n }\n}\n\n// Ints ========================================\n\ninterface Ints {\n value: Uint32Array;\n size: number;\n}\n\nclass IntsReader {\n static read(pbf, end?: number): Ints {\n const {value, size} = pbf.readFields(IntsReader._readField, {value: [], size: 0}, end);\n return {value: new Uint32Array(value), size};\n }\n static _readField(this: void, tag: number, obj, pbf) {\n if (tag === 1) pbf.readPackedVarint(obj.value);\n else if (tag === 2) obj.size = pbf.readVarint(true);\n }\n}\n\n// Fields ========================================\n\ninterface Fields {\n id: number;\n}\n\nclass FieldsReader {\n static read(pbf, end?: number): Fields {\n return pbf.readFields(FieldsReader._readField, {id: 0}, end);\n }\n static _readField(this: void, tag: number, obj: Fields, pbf) {\n if (tag === 1) obj.id = pbf.readVarint();\n }\n}\n\n// NumericProp ========================================\n\nexport interface NumericProp {\n value: TypedArray;\n size: number;\n}\n\nclass NumericPropReader {\n static read(pbf, end?: number): NumericProp {\n return pbf.readFields(NumericPropReader._readField, {value: []}, end);\n }\n static _readField(this: void, tag: number, obj: NumericProp, pbf) {\n if (tag === 1) readPackedTypedArray(Float64Array, pbf, obj);\n }\n}\n\n// NumericPropKeyValue ========================================\ninterface NumbericPropKeyValue {\n key: string;\n value: NumericProp;\n}\n\nexport class NumericPropKeyValueReader {\n static read(pbf, end?: number): NumbericPropKeyValue {\n return pbf.readFields(NumericPropKeyValueReader._readField, {key: '', value: null}, end);\n }\n static _readField(this: void, tag: number, obj: NumbericPropKeyValue, pbf) {\n if (tag === 1) obj.key = pbf.readString();\n else if (tag === 2) obj.value = NumericPropReader.read(pbf, pbf.readVarint() + pbf.pos);\n }\n}\n\n// Points ========================================\n\ninterface Points {\n positions: Doubles;\n globalFeatureIds: Ints;\n featureIds: Ints;\n properties: Record<string, string>[];\n numericProps: Record<string, NumericProp>;\n fields: Fields[];\n}\n\nclass PointsReader {\n static read(pbf, end?: number): Points {\n return pbf.readFields(\n PointsReader._readField,\n {\n positions: null,\n globalFeatureIds: null,\n featureIds: null,\n properties: [],\n numericProps: {},\n fields: []\n },\n end\n );\n }\n static _readField(this: void, tag: number, obj: Points, pbf) {\n if (tag === 1) obj.positions = DoublesReader.read(pbf, pbf.readVarint() + pbf.pos);\n else if (tag === 2) obj.globalFeatureIds = IntsReader.read(pbf, pbf.readVarint() + pbf.pos);\n else if (tag === 3) obj.featureIds = IntsReader.read(pbf, pbf.readVarint() + pbf.pos);\n else if (tag === 4) obj.properties.push(PropertiesReader.read(pbf, pbf.readVarint() + pbf.pos));\n else if (tag === 5) {\n const entry = NumericPropKeyValueReader.read(pbf, pbf.readVarint() + pbf.pos);\n obj.numericProps[entry.key] = entry.value;\n } else if (tag === 6) obj.fields.push(FieldsReader.read(pbf, pbf.readVarint() + pbf.pos));\n }\n}\n\ninterface Lines extends Points {\n pathIndices: Ints;\n}\n// Lines ========================================\n\nclass LinesReader {\n static read(pbf, end?: number): Lines {\n return pbf.readFields(\n LinesReader._readField,\n {\n positions: null,\n pathIndices: null,\n globalFeatureIds: null,\n featureIds: null,\n properties: [],\n numericProps: {},\n fields: []\n },\n end\n );\n }\n static _readField(this: void, tag: number, obj: Lines, pbf) {\n if (tag === 1) obj.positions = DoublesReader.read(pbf, pbf.readVarint() + pbf.pos);\n else if (tag === 2) obj.pathIndices = IntsReader.read(pbf, pbf.readVarint() + pbf.pos);\n else if (tag === 3) obj.globalFeatureIds = IntsReader.read(pbf, pbf.readVarint() + pbf.pos);\n else if (tag === 4) obj.featureIds = IntsReader.read(pbf, pbf.readVarint() + pbf.pos);\n else if (tag === 5) obj.properties.push(PropertiesReader.read(pbf, pbf.readVarint() + pbf.pos));\n else if (tag === 6) {\n const entry = NumericPropKeyValueReader.read(pbf, pbf.readVarint() + pbf.pos);\n obj.numericProps[entry.key] = entry.value;\n } else if (tag === 7) obj.fields.push(FieldsReader.read(pbf, pbf.readVarint() + pbf.pos));\n }\n}\n// Polygons ========================================\n\ninterface Polygons extends Points {\n polygonIndices: Ints;\n primitivePolygonIndices: Ints;\n triangles: Ints;\n}\n\nclass PolygonsReader {\n static read(pbf, end?: number): Polygons {\n return pbf.readFields(\n PolygonsReader._readField,\n {\n positions: null,\n polygonIndices: null,\n globalFeatureIds: null,\n featureIds: null,\n primitivePolygonIndices: null,\n triangles: null,\n properties: [],\n numericProps: {},\n fields: []\n },\n end\n );\n }\n static _readField(this: void, tag: number, obj: Polygons, pbf) {\n if (tag === 1) obj.positions = DoublesReader.read(pbf, pbf.readVarint() + pbf.pos);\n else if (tag === 2) obj.polygonIndices = IntsReader.read(pbf, pbf.readVarint() + pbf.pos);\n else if (tag === 3) obj.globalFeatureIds = IntsReader.read(pbf, pbf.readVarint() + pbf.pos);\n else if (tag === 4) obj.featureIds = IntsReader.read(pbf, pbf.readVarint() + pbf.pos);\n else if (tag === 5)\n obj.primitivePolygonIndices = IntsReader.read(pbf, pbf.readVarint() + pbf.pos);\n else if (tag === 6) obj.triangles = IntsReader.read(pbf, pbf.readVarint() + pbf.pos);\n else if (tag === 7) obj.properties.push(PropertiesReader.read(pbf, pbf.readVarint() + pbf.pos));\n else if (tag === 8) {\n const entry = NumericPropKeyValueReader.read(pbf, pbf.readVarint() + pbf.pos);\n obj.numericProps[entry.key] = entry.value;\n } else if (tag === 9) obj.fields.push(FieldsReader.read(pbf, pbf.readVarint() + pbf.pos));\n }\n}\n\n// Tile ========================================\n\nexport interface Tile {\n points: Points;\n lines: Lines;\n polygons: Polygons;\n}\n\nexport class TileReader {\n static read(pbf, end?: number): Tile {\n return pbf.readFields(TileReader._readField, {points: null, lines: null, polygons: null}, end);\n }\n static _readField(this: void, tag: number, obj: Tile, pbf) {\n if (tag === 1) obj.points = PointsReader.read(pbf, pbf.readVarint() + pbf.pos);\n else if (tag === 2) obj.lines = LinesReader.read(pbf, pbf.readVarint() + pbf.pos);\n else if (tag === 3) obj.polygons = PolygonsReader.read(pbf, pbf.readVarint() + pbf.pos);\n }\n}\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {readPackedTypedArray} from './fast-pbf';\nimport {Indices, IndexScheme} from './spatialjson-utils';\nimport {NumericProp, NumericPropKeyValueReader, PropertiesReader} from './carto-tile';\n\n// Indices =====================================\n\nexport class IndicesReader {\n static read(pbf, end?: number): Indices {\n return pbf.readFields(IndicesReader._readField, {value: []}, end);\n }\n static _readField(this: void, tag: number, obj, pbf) {\n if (tag === 1) readPackedTypedArray(BigUint64Array, pbf, obj);\n }\n}\n\n// Cells =========================================\n\ninterface Cells {\n indices: Indices;\n properties: Record<string, string>[];\n numericProps: Record<string, NumericProp>;\n}\n\nclass CellsReader {\n static read(pbf, end?: number): Cells {\n return pbf.readFields(\n CellsReader._readField,\n {indices: null, properties: [], numericProps: {}},\n end\n );\n }\n static _readField(this: void, tag: number, obj: Cells, pbf) {\n if (tag === 1) obj.indices = IndicesReader.read(pbf, pbf.readVarint() + pbf.pos);\n else if (tag === 2) obj.properties.push(PropertiesReader.read(pbf, pbf.readVarint() + pbf.pos));\n else if (tag === 3) {\n const entry = NumericPropKeyValueReader.read(pbf, pbf.readVarint() + pbf.pos);\n obj.numericProps[entry.key] = entry.value;\n }\n }\n}\n\n// Tile ========================================\n\n// TODO this type is very similar to SpatialBinary, should align\nexport interface Tile {\n scheme: IndexScheme;\n cells: Cells;\n}\n\nexport class TileReader {\n static read(pbf, end?: number): Tile {\n return pbf.readFields(TileReader._readField, {scheme: 0, cells: null}, end);\n }\n static _readField(this: void, tag: number, obj: Tile, pbf) {\n if (tag === 1) obj.scheme = pbf.readVarint();\n else if (tag === 2) obj.cells = CellsReader.read(pbf, pbf.readVarint() + pbf.pos);\n }\n}\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport Protobuf from 'pbf';\n\nexport function parsePbf(buffer: ArrayBuffer, TileReader) {\n const pbf = new Protobuf(buffer);\n const tile = TileReader.read(pbf);\n return tile;\n}\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {bigIntToHex} from 'quadbin';\nimport {BinaryPointFeature} from '@loaders.gl/schema';\n\nexport type IndexScheme = 'h3' | 'quadbin';\n\nexport type Indices = {value: BigUint64Array};\nexport type NumericProps = BinaryPointFeature['numericProps'];\nexport type Properties = BinaryPointFeature['properties'];\nexport type Cells = {\n indices: Indices;\n numericProps: NumericProps;\n properties: Properties;\n};\nexport type SpatialBinary = {scheme?: IndexScheme; cells: Cells};\nexport type SpatialJson = {\n id: string | bigint;\n properties: Properties;\n}[];\n\nexport function binaryToSpatialjson(binary: SpatialBinary): SpatialJson {\n const {cells, scheme} = binary;\n const count = cells.indices.value.length;\n const spatial: any[] = [];\n for (let i = 0; i < count; i++) {\n const id = scheme === 'h3' ? bigIntToHex(cells.indices.value[i]) : cells.indices.value[i];\n\n const properties = {...cells.properties[i]};\n for (const key of Object.keys(cells.numericProps)) {\n properties[key] = cells.numericProps[key].value[i];\n }\n spatial.push({id, properties});\n }\n\n return spatial;\n}\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {LoaderOptions, LoaderWithParser} from '@loaders.gl/loader-utils';\n\nimport {Tile, TileReader} from './carto-spatial-tile';\nimport {parsePbf} from './tile-loader-utils';\nimport {getWorkerUrl} from '../../utils';\nimport {IndexScheme, binaryToSpatialjson, SpatialJson} from './spatialjson-utils';\n\nconst VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest';\nconst id = 'cartoSpatialTile';\n\ntype CartoSpatialTileLoaderOptions = LoaderOptions & {\n cartoSpatialTile?: {\n scheme: IndexScheme;\n workerUrl: string;\n };\n};\n\nconst DEFAULT_OPTIONS: CartoSpatialTileLoaderOptions = {\n cartoSpatialTile: {\n scheme: 'quadbin',\n workerUrl: getWorkerUrl(id, VERSION)\n }\n};\n\nconst CartoSpatialTileLoader: LoaderWithParser = {\n name: 'CARTO Spatial Tile',\n version: VERSION,\n id,\n module: 'carto',\n extensions: ['pbf'],\n mimeTypes: ['application/vnd.carto-spatial-tile'],\n category: 'geometry',\n parse: async (arrayBuffer, options?: CartoSpatialTileLoaderOptions) =>\n parseCartoSpatialTile(arrayBuffer, options),\n parseSync: parseCartoSpatialTile,\n worker: true,\n options: DEFAULT_OPTIONS\n};\n\nfunction parseCartoSpatialTile(\n arrayBuffer: ArrayBuffer,\n options?: CartoSpatialTileLoaderOptions\n): Spati