UNPKG

@deck.gl/carto

Version:

CARTO official integration with Deck.gl. Build geospatial applications using CARTO and Deck.gl.

4 lines 261 kB
{ "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/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/h3-tileset-2d.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/parse-map.ts", "../src/api/layer-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} from './api/index';\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\nimport {\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} from '@carto/api-client';\n\nexport const 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};\n\nexport {\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 GeojsonResult,\n JsonResult,\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} from '@deck.gl/core';\n\nimport {\n aggregateTile,\n ClusteredFeaturePropertiesT,\n clustersToBinary,\n computeAggregationStats,\n extractAggregationProperties,\n ParsedQuadbinCell,\n ParsedQuadbinTile\n} from './cluster-utils';\nimport {DEFAULT_TILE_SIZE} from '../constants';\nimport QuadbinTileset2D from './quadbin-tileset-2d';\nimport {getQuadbinPolygon} from './quadbin-utils';\nimport CartoSpatialTileLoader from './schema/carto-spatial-tile-loader';\nimport {TilejsonPropType, mergeLoadOptions} from './utils';\nimport type {TilejsonResult} from '@carto/api-client';\n\nregisterLoaders([CartoSpatialTileLoader]);\n\nconst defaultProps: DefaultProps<ClusterTileLayerProps> = {\n data: TilejsonPropType,\n clusterLevel: {type: 'number', value: 5, min: 1},\n getPosition: {\n type: 'accessor',\n value: ({id}) => getQuadbinPolygon(id, 0.5).slice(2, 4) as [number, number]\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>,\n PickingInfo<Feature<Geometry, FeaturePropertiesT>>\n>;\n\n/** All properties supported by ClusterTileLayer. */\nexport type ClusterTileLayerProps<FeaturePropertiesT = unknown> =\n _ClusterTileLayerProps<FeaturePropertiesT> &\n Omit<TileLayerProps<ParsedQuadbinTile<FeaturePropertiesT>>, 'data'>;\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 is used.\n *\n * @default cell center\n */\n getPosition?: Accessor<ParsedQuadbinCell<FeaturePropertiesT>, [number, number]>;\n\n /**\n * The weight of each cell used for clustering.\n *\n * @default 1\n */\n getWeight?: Accessor<ParsedQuadbinCell<FeaturePropertiesT>, number>;\n};\n\nclass ClusterGeoJsonLayer<\n FeaturePropertiesT extends {} = {},\n ExtraProps extends {} = {}\n> extends TileLayer<\n ParsedQuadbinTile<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[];\n hoveredFeatureId: bigint | number | null;\n highlightColor: number[];\n aggregationCache: WeakMap<any, Map<number, ClusteredFeaturePropertiesT<FeaturePropertiesT>[]>>;\n };\n\n initializeState() {\n super.initializeState();\n this.state.aggregationCache = new WeakMap();\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>>[];\n if (!visibleTiles?.length) {\n return null;\n }\n visibleTiles.sort((a, b) => b.zoom - a.zoom);\n\n const {zoom} = this.context.viewport;\n const {clusterLevel, getPosition, getWeight} = this.props;\n const {aggregationCache} = this.state;\n\n const properties = extractAggregationProperties(visibleTiles[0]);\n const data = [] as ClusteredFeaturePropertiesT<FeaturePropertiesT>[];\n let needsUpdate = false;\n for (const tile of visibleTiles) {\n // Calculate aggregation based on viewport zoom\n const overZoom = Math.round(zoom - tile.zoom);\n const aggregationLevels = Math.round(clusterLevel) - overZoom;\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 );\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<ParsedQuadbinTile<FeaturePropertiesT>>;\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\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 return mergeLoadOptions(super.getLoadOptions(), {\n fetch: {headers: {Authorization: `Bearer ${tileJSON.accessToken}`}},\n cartoSpatialTile: {scheme: 'quadbin'}\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 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: QuadbinTileset2D 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 {_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;\n count: number;\n position: [number, number];\n};\nexport type ParsedQuadbinCell<FeaturePropertiesT> = {id: bigint; properties: FeaturePropertiesT};\nexport type ParsedQuadbinTile<FeaturePropertiesT> = ParsedQuadbinCell<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>>,\n tileAggregationCache: Map<number, ClusteredFeaturePropertiesT<FeaturePropertiesT>[]>,\n aggregationLevels: number,\n properties: AggregationProperties<FeaturePropertiesT> = [],\n getPosition: Accessor<ParsedQuadbinCell<FeaturePropertiesT>, [number, number]>,\n getWeight: Accessor<ParsedQuadbinCell<FeaturePropertiesT>, number>\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<number, 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 id = cellToParent(id);\n }\n\n // Unfortunately TS doesn't support Record<bigint, any>\n // https://github.com/microsoft/TypeScript/issues/46395\n const parentId = Number(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>>\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 {\n BinaryAttribute,\n BinaryFeature,\n BinaryFeatureCollection,\n BinaryPointFeature\n} 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\nexport const DEFAULT_AGGREGATION_EXP_ALIAS = '__aggregationValue';\nexport const DEFAULT_AGGREGATION_EXP = `1 AS ${DEFAULT_AGGREGATION_EXP_ALIAS}`;\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 {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';\ntype TypedArray = Float32Array | Float64Array;\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): SpatialJson | null {\n if (!arrayBuffer) return null;\n const tile: Tile = parsePbf(arrayBuffer, TileReader);\n\n const {cells} = tile;\n const scheme = options?.cartoSpatialTile?.scheme;\n const data = {cells, scheme};\n\n return binaryToSpatialjson(data);\n}\n\nexport default CartoSpatialTileLoader;\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {log} from '@deck.gl/core';\nimport {Tile as PropertiesTile} from './schema/carto-properties-tile';\nimport {Tile as VectorTile} from './schema/carto-tile';\nimport {_deepEqual as deepEqual} from '@deck.gl/core';\nimport type {TilejsonResult} from '@carto/api-client';\n\n/**\n * Merges load options with additional options, creating a new object without mutating the input.\n * Handles nested objects through recursive deep merge with protection against circular references.\n */\nexport function mergeLoadOptions(loadOptions: any, additionalOptions: any, depth = 0): any {\n if (!loadOptions) {\n return additionalOptions;\n }\n if (!additionalOptions) {\n return loadOptions;\n }\n\n // Safety check against deep recursion\n if (depth > 10) {\n return additionalOptions;\n }\n\n const result = {...loadOptions};\n\n for (const key in additionalOptions) {\n const value = additionalOptions[key];\n // Skip circular references\n if (value === loadOptions || value === additionalOptions) {\n continue;\n }\n if (typeof value === 'object' && value !== null) {\n result[key] = mergeLoadOptions(loadOptions[key], value, depth + 1);\n } else {\n result[key] = value;\n }\n }\n\n return result;\n}\n\n// eslint-disable-next-line max-statements\nexport function mergeBoundaryData(geometry: VectorTile, properties: PropertiesTile): VectorTile {\n const mapping = {};\n for (const {geoid, ...rest} of properties.properties) {\n if (geoid in mapping) {\n log.warn('Duplicate geoid key in boundary mapping, using first occurance')();\n } else {\n mapping[geoid] = rest;\n }\n }\n\n for (const type of ['points', 'lines', 'polygons']) {\n const geom = geometry[type];\n if (geom.positions.value.length === 0) {\n continue;\n }\n\n geom.properties = geom.properties.map(({geoid}) => mapping[geoid]);\n\n // numericProps need to be filled to match length of positions buffer\n const {positions, globalFeatureIds} = geom;\n let indices: Uint16Array | Uint32Array | null = null;\n if (type === 'lines') indices = geom.pathIndices.value;\n if (type === 'polygons') indices = geom.polygonIndices.value;\n const length = positions.value.length / positions.size;\n for (const key in properties.numericProps) {\n const sourceProp = properties.numericProps[key].value;\n const TypedArray = sourceProp.constructor as\n | Float32ArrayConstructor\n | Float64ArrayConstructor;\n const destProp = new TypedArray(length);\n geom.numericProps[key] = {value: destProp, size: 1};\n\n if (!indices) {\n for (let i = 0; i < length; i++) {\n // points\n const featureId = globalFeatureIds.value[i];\n destProp[i] = sourceProp[featureId];\n }\n } else {\n // lines|polygons\n for (let i = 0; i < indices.length - 1; i++) {\n const startIndex = indices[i];\n const endIndex = indices[i + 1];\n const featureId = globalFeatureIds.value[startIndex];\n destProp.fill(sourceProp[featureId], startIndex, endIndex);\n }\n }\n }\n }\n\n return geometry;\n}\n\nexport const TilejsonPropType = {\n type: 'object' as const,\n value: null as null | TilejsonResult,\n validate: (value: TilejsonResult, propType) =>\n (propType.optional && value === null) ||\n (typeof value === 'object' &&\n Array.isArray(value.tiles) &&\n value.tiles.every(url => typeof url === 'string')),\n equal: (value1, value2) => {\n return deepEqual(value1, value2, 2);\n },\n async: true\n};\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {CompositeLayer, CompositeLayerProps, DefaultProps} from '@deck.gl/core';\nimport {H3HexagonLayer, H3HexagonLayerProps} from '@deck.gl/geo-layers';\nimport H3Tileset2D, {getHexagonResolution} from './h3-tileset-2d';\nimport SpatialIndexTileLayer, {SpatialIndexTileLayerProps} from './spatial-index-tile-layer';\nimport type {TilejsonResult} from '@carto/api-client';\nimport {TilejsonPropType, mergeLoadOptions} from './utils';\nimport {DEFAULT_TILE_SIZE} from '../constants';\n\nexport const renderSubLayers = props => {\n const {data} = props;\n const {index} = props.tile;\n if (!data || !data.length) return null;\n\n return new H3HexagonLayer(props, {\n getHexagon: d => d.id,\n centerHexagon: index,\n highPrecision: true\n });\n};\n\nconst defaultProps: DefaultProps<H3TileLayerProps> = {\n data: TilejsonPropType,\n tileSize: DEFAULT_TILE_SIZE\n};\n\n/** All properties supported by H3TileLayer. */\nexport type H3TileLayerProps<DataT = unknown> = _H3TileLayerProps<DataT> & CompositeLayerProps;\n\n/** Properties added by H3TileLayer. */\ntype _H3TileLayerProps<DataT> = Omit<H3HexagonLayerProps<DataT>, 'data'> &\n Omit<SpatialIndexTileLayerProps<DataT>, 'data'> & {\n data: null | TilejsonResult | Promise<TilejsonResult>;\n };\n\nexport default class H3TileLayer<DataT = any, ExtraPropsT extends {} = {}> extends CompositeLayer<\n ExtraPropsT & Required<_H3TileLayerProps<DataT>>\n> {\n static layerName = 'H3TileLayer';\n static defaultProps = defaultProps;\n\n initializeState(): void {\n H3HexagonLayer._checkH3Lib();\n }\n\n getLoadOptions(): any {\n const tileJSON = this.props.data as TilejsonResult;\n return mergeLoadOptions(super.getLoadOptions(), {\n fetch: {headers: {Authorization: `Bearer ${tileJSON.accessToken}`}},\n cartoSpatialTile: {scheme: 'h3'}\n });\n }\n\n renderLayers(): SpatialIndexTileLayer | null {\n const tileJSON = this.props.data as TilejsonResult;\n if (!tileJSON) return null;\n\n const {tiles: data} = tileJSON;\n let {minresolution, maxresolution} = tileJSON;\n // Convert Mercator zooms provided in props into H3 res levels\n // and clip into valid range provided from the tilejson\n if (this.props.minZoom) {\n minresolution = Math.max(\n minresolution,\n getHexagonResolution({zoom: this.props.minZoom, latitude: 0}, this.props.tileSize)\n );\n }\n if (this.props.maxZoom) {\n maxresolution = Math.min(\n maxresolution,\n getHexagonResolution({zoom: this.props.maxZoom, latitude: 0}, this.props.tileSize)\n );\n }\n\n const SubLayerClass = this.getSubLayerClass('spatial-index-tile', SpatialIndexTileLayer);\n // The naming is unfortunate, but minZoom & maxZoom in the context\n // of a Tileset2D refer to the resolution levels, not the Mercator zooms\n return new SubLayerClass(this.props, {\n id: `h3-tile-layer-${this.props.id}`,\n data,\n // TODO: Tileset2D should be generic over TileIndex type\n TilesetClass: H3Tileset2D as any,\n renderSubLayers,\n // minZoom and maxZoom are H3 resolutions, however we must use this naming as that is what the Tileset2D class expects\n minZoom: minresolution,\n maxZoom: maxresolution,\n loadOptions: this.getLoadOptions()\n });\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(...originToDirectedEdge