diff --git a/examples/polygon/app.tsx b/examples/polygon/app.tsx index 0b784cc..c73c2fe 100644 --- a/examples/polygon/app.tsx +++ b/examples/polygon/app.tsx @@ -29,7 +29,7 @@ const NAV_CONTROL_STYLE = { function Root() { const onClick = (info: PickingInfo) => { if (info.object) { - console.log(JSON.stringify(info.object.toJSON())); + console.log(info.object["BoroName"]); } }; @@ -66,10 +66,10 @@ function Root() { extruded: false, wireframe: true, // getElevation: 0, - pickable: false, + pickable: true, positionFormat: "XY", _normalize: false, - autoHighlight: true, + autoHighlight: false, earcutWorkerUrl: new URL( "https://cdn.jsdelivr.net/npm/@geoarrow/geoarrow-js@0.3.0-beta.1/dist/earcut-worker.min.js", ), diff --git a/package-lock.json b/package-lock.json index e26ef35..aa2d664 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@geoarrow/deck.gl-layers", - "version": "0.3.0-beta.11", + "version": "0.3.0-beta.14", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@geoarrow/deck.gl-layers", - "version": "0.3.0-beta.11", + "version": "0.3.0-beta.14", "license": "MIT", "workspaces": [ ".", diff --git a/package.json b/package.json index 04c1606..3e07ad1 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "examples/*" ], "name": "@geoarrow/deck.gl-layers", - "version": "0.3.0-beta.14", + "version": "0.3.0-beta.15", "type": "module", "description": "", "source": "src/index.ts", diff --git a/src/path-layer.ts b/src/path-layer.ts index 33ab5fe..827df65 100644 --- a/src/path-layer.ts +++ b/src/path-layer.ts @@ -99,7 +99,10 @@ export class GeoArrowPathLayer< sourceLayer: { props: GeoArrowExtraPickingProps }; }, ): GeoArrowPickingInfo { - return getPickingInfo(params, this.props.data); + // Notes for handling + + console.log(params); + const info = getPickingInfo(params, this.props.data); } renderLayers(): Layer<{}> | LayersList | null { diff --git a/src/picking.ts b/src/picking.ts index 0d66152..bfb9b4e 100644 --- a/src/picking.ts +++ b/src/picking.ts @@ -41,6 +41,12 @@ export function getPickingInfo( // Update index to be _global_ index, not within the specific record batch index += currentBatchOffset; + + // if (sourceLayer.props?.invertedOffsets) { + // sourceLayer.props?.invertedOffsets[recordBatchIdx][index] + // } + // // const recordBatchIdx = ; + return { ...info, index, diff --git a/src/polygon-layer.ts b/src/polygon-layer.ts index 87c1832..ba44206 100644 --- a/src/polygon-layer.ts +++ b/src/polygon-layer.ts @@ -11,7 +11,11 @@ import { PolygonLayer } from "@deck.gl/layers/typed"; import type { PolygonLayerProps } from "@deck.gl/layers/typed"; import * as arrow from "apache-arrow"; import * as ga from "@geoarrow/geoarrow-js"; -import { getGeometryVector } from "./utils.js"; +import { + getGeometryVector, + invertOffsets, + invertOffsetsSequence, +} from "./utils.js"; import { GeoArrowExtraPickingProps, getPickingInfo } from "./picking.js"; import { ColorAccessor, FloatAccessor, GeoArrowPickingInfo } from "./types.js"; import { EXTENSION_NAME } from "./constants.js"; @@ -39,6 +43,8 @@ export function getPolygonExterior( return new arrow.Vector(input.data.map((data) => getPolygonExterior(data))); } + // Polygon and MultiLineString arrays have the same representation, so this is + // a "physical no-op" which just changes the type on the array. return input; } @@ -53,21 +59,42 @@ export function getPolygonExterior( * This means that we need to condense both two offset buffers from the * MultiPolygonVector/Data (geomOffsets and polygonOffsets) into a single * `geomOffsets` for the new MultiLineStringVector/Data. + * + * Note that this returns a second result for "invertedPathOffsets". When + * _rendering_ the path layer that's the stroke of the MultiPolygon, we render + * more individual geometries than we have original MultiPolygons. Therefore, + * for picking, we need to go "back" from the index of the path to the index of + * the original multi polygon. We create inverted offsets to help make that + * happen. */ export function getMultiPolygonExterior( input: ga.vector.MultiPolygonVector, -): ga.vector.MultiLineStringVector; +): [ + (Uint32Array | Uint8Array | Uint16Array)[], + ga.vector.MultiLineStringVector, +]; export function getMultiPolygonExterior( input: ga.data.MultiPolygonData, -): ga.data.MultiLineStringData; +): [Uint32Array | Uint8Array | Uint16Array, ga.data.MultiLineStringData]; export function getMultiPolygonExterior( input: ga.vector.MultiPolygonVector | ga.data.MultiPolygonData, -): ga.vector.MultiLineStringVector | ga.data.MultiLineStringData { +): + | [ + (Uint32Array | Uint8Array | Uint16Array)[], + ga.vector.MultiLineStringVector, + ] + | [Uint32Array | Uint8Array | Uint16Array, ga.data.MultiLineStringData] { if ("data" in input) { - return new arrow.Vector( - input.data.map((data) => getMultiPolygonExterior(data)), - ); + const outputChunks: ga.data.MultiLineStringData[] = []; + const invertedOffsets: (Uint32Array | Uint8Array | Uint16Array)[] = []; + for (const dataChunk of input.data) { + const [invertedOffset, outputChunk] = getMultiPolygonExterior(dataChunk); + invertedOffsets.push(invertedOffset); + outputChunks.push(outputChunk); + } + + return [invertedOffsets, new arrow.Vector(outputChunks)]; } const geomOffsets: Int32Array = input.valueOffsets; @@ -81,14 +108,20 @@ export function getMultiPolygonExterior( resolvedOffsets[i] = polygonOffsets[geomOffsets[i]]; } - return arrow.makeData({ - type: new arrow.List(polygonData.type.children[0]), - length: input.length, - nullCount: input.nullCount, - nullBitmap: input.nullBitmap, - child: lineStringData, - valueOffsets: resolvedOffsets, - }); + // Pass in global row start into invertOffsets + const invertedOffsets = invertOffsets(resolvedOffsets); + + return [ + invertedOffsets, + arrow.makeData({ + type: new arrow.List(polygonData.type.children[0]), + length: input.length, + nullCount: input.nullCount, + nullBitmap: input.nullBitmap, + child: lineStringData, + valueOffsets: resolvedOffsets, + }), + ]; } /** All properties supported by GeoArrowPolygonLayer */ @@ -185,7 +218,14 @@ export class GeoArrowPolygonLayer< sourceLayer: { props: GeoArrowExtraPickingProps }; }, ): GeoArrowPickingInfo { - return getPickingInfo(params, this.props.data); + console.log(params.info); + if (params.info.sourceLayer?.props?.invertedOffsets?.length > 0) { + console.log("has inverted offsets"); + } + params.info.layer?.id; + + // Propagate the picked info from the SolidPolygonLayer + return params.info; } renderLayers(): Layer<{}> | LayersList | null { @@ -225,10 +265,16 @@ export class GeoArrowPolygonLayer< const { data: table } = this.props; let getPath: ga.vector.MultiLineStringVector; + let invertedOffsets: null | (Uint32Array | Uint8Array | Uint16Array)[] = + null; if (ga.vector.isPolygonVector(geometryColumn)) { getPath = getPolygonExterior(geometryColumn); } else if (ga.vector.isMultiPolygonVector(geometryColumn)) { - getPath = getMultiPolygonExterior(geometryColumn); + [invertedOffsets, getPath] = getMultiPolygonExterior(geometryColumn); + const test = invertOffsetsSequence( + getPath.data.map((data) => data.valueOffsets), + ); + console.log(test); } else { assert(false); } @@ -305,6 +351,7 @@ export class GeoArrowPolygonLayer< data, positionFormat, getPolygon, + pickable: false, }, ); @@ -348,6 +395,7 @@ export class GeoArrowPolygonLayer< data: table, positionFormat, getPath, + invertedOffsets, }, ); diff --git a/src/utils.ts b/src/utils.ts index 172f22b..a9e9691 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -356,6 +356,41 @@ export function invertOffsets( return invertedOffsets; } +export function invertOffsetsSequence( + offsets: Int32Array[], +): Uint8Array | Uint16Array | Uint32Array { + let largestOffset = offsets + .map((offsetBuffer) => offsetBuffer[offsetBuffer.length - 1]) + .reduce((a, b) => a + b, 0); + let numOffsets = offsets.reduce((a, b) => a + b.length, 0); + + const arrayConstructor = + numOffsets < Math.pow(2, 8) + ? Uint8Array + : numOffsets < Math.pow(2, 16) + ? Uint16Array + : Uint32Array; + + const invertedOffsets = new arrayConstructor(largestOffset); + let chunkStart = 0; + for (const offsetBuffer of offsets) { + for (let arrayIdx = 0; arrayIdx < offsetBuffer.length - 1; arrayIdx++) { + const thisOffset = offsetBuffer[arrayIdx]; + const nextOffset = offsetBuffer[arrayIdx + 1]; + for ( + let offset = chunkStart + thisOffset; + offset < chunkStart + nextOffset; + offset++ + ) { + invertedOffsets[offset] = chunkStart + arrayIdx; + } + } + chunkStart += offsetBuffer[offsetBuffer.length - 1]; + } + + return invertedOffsets; +} + // TODO: better typing export function extractAccessorsFromProps( props: Record,