From 3a3c6fef324c9740e17d3a268f19dd50f9c0769c Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Fri, 22 Mar 2024 16:52:50 -0400 Subject: [PATCH 1/4] Fix picking in the polygon layer --- examples/polygon/app.tsx | 6 +++--- package-lock.json | 4 ++-- src/polygon-layer.ts | 5 ++++- 3 files changed, 9 insertions(+), 6 deletions(-) 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/src/polygon-layer.ts b/src/polygon-layer.ts index 87c1832..4485811 100644 --- a/src/polygon-layer.ts +++ b/src/polygon-layer.ts @@ -185,7 +185,8 @@ export class GeoArrowPolygonLayer< sourceLayer: { props: GeoArrowExtraPickingProps }; }, ): GeoArrowPickingInfo { - return getPickingInfo(params, this.props.data); + // Propagate the picked info from the SolidPolygonLayer + return params.info; } renderLayers(): Layer<{}> | LayersList | null { @@ -348,6 +349,8 @@ export class GeoArrowPolygonLayer< data: table, positionFormat, getPath, + // We only pick solid polygon layers, not the path layers + pickable: false, }, ); From 90ca322a20781294dad9afc29786a62e79e7b8b0 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Fri, 22 Mar 2024 16:53:08 -0400 Subject: [PATCH 2/4] Fix picking in the polygon layer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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", From 91a81526df4c01ec5ea896b059d85422b94aadb8 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Fri, 22 Mar 2024 17:25:16 -0400 Subject: [PATCH 3/4] wip: handle picking stroke of polygon layer --- src/path-layer.ts | 5 ++- src/picking.ts | 6 ++++ src/polygon-layer.ts | 73 +++++++++++++++++++++++++++++++++----------- 3 files changed, 65 insertions(+), 19 deletions(-) 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 4485811..04a3e85 100644 --- a/src/polygon-layer.ts +++ b/src/polygon-layer.ts @@ -11,7 +11,7 @@ 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 } from "./utils.js"; import { GeoArrowExtraPickingProps, getPickingInfo } from "./picking.js"; import { ColorAccessor, FloatAccessor, GeoArrowPickingInfo } from "./types.js"; import { EXTENSION_NAME } from "./constants.js"; @@ -39,6 +39,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 +55,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 +104,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,6 +214,12 @@ export class GeoArrowPolygonLayer< sourceLayer: { props: GeoArrowExtraPickingProps }; }, ): GeoArrowPickingInfo { + 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; } @@ -226,10 +261,12 @@ 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); } else { assert(false); } @@ -306,6 +343,7 @@ export class GeoArrowPolygonLayer< data, positionFormat, getPolygon, + pickable: false, }, ); @@ -349,8 +387,7 @@ export class GeoArrowPolygonLayer< data: table, positionFormat, getPath, - // We only pick solid polygon layers, not the path layers - pickable: false, + invertedOffsets, }, ); From 2428817ffc0bcfc023539e42a17c5260fa679f9f Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Fri, 22 Mar 2024 17:41:45 -0400 Subject: [PATCH 4/4] Invert sequence of offsets --- src/polygon-layer.ts | 10 +++++++++- src/utils.ts | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/polygon-layer.ts b/src/polygon-layer.ts index 04a3e85..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, invertOffsets } 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"; @@ -267,6 +271,10 @@ export class GeoArrowPolygonLayer< getPath = getPolygonExterior(geometryColumn); } else if (ga.vector.isMultiPolygonVector(geometryColumn)) { [invertedOffsets, getPath] = getMultiPolygonExterior(geometryColumn); + const test = invertOffsetsSequence( + getPath.data.map((data) => data.valueOffsets), + ); + console.log(test); } else { assert(false); } 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,