Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wip: handle picking stroke of polygon layer #114

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions examples/polygon/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"]);
}
};

Expand Down Expand Up @@ -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/[email protected]/dist/earcut-worker.min.js",
),
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
5 changes: 4 additions & 1 deletion src/path-layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,11 @@
params: GetPickingInfoParams & {
sourceLayer: { props: GeoArrowExtraPickingProps };
},
): GeoArrowPickingInfo {

Check failure on line 101 in src/path-layer.ts

View workflow job for this annotation

GitHub Actions / lint-and-test

A function whose declared type is neither 'undefined', 'void', nor 'any' must return a value.
return getPickingInfo(params, this.props.data);
// Notes for handling

console.log(params);
const info = getPickingInfo(params, this.props.data);
}

renderLayers(): Layer<{}> | LayersList | null {
Expand Down
6 changes: 6 additions & 0 deletions src/picking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
82 changes: 65 additions & 17 deletions src/polygon-layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@
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";
Expand Down Expand Up @@ -39,6 +43,8 @@
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;
}

Expand All @@ -53,21 +59,42 @@
* 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;
Expand All @@ -81,14 +108,20 @@
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 */
Expand Down Expand Up @@ -185,7 +218,14 @@
sourceLayer: { props: GeoArrowExtraPickingProps };
},
): GeoArrowPickingInfo {
return getPickingInfo(params, this.props.data);
console.log(params.info);
if (params.info.sourceLayer?.props?.invertedOffsets?.length > 0) {

Check failure on line 222 in src/polygon-layer.ts

View workflow job for this annotation

GitHub Actions / lint-and-test

Property 'invertedOffsets' does not exist on type 'StatefulComponentProps<Required<LayerProps>>'.
console.log("has inverted offsets");
}
params.info.layer?.id;

// Propagate the picked info from the SolidPolygonLayer
return params.info;
}

renderLayers(): Layer<{}> | LayersList | null {
Expand Down Expand Up @@ -225,10 +265,16 @@
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);
}
Expand Down Expand Up @@ -305,6 +351,7 @@
data,
positionFormat,
getPolygon,
pickable: false,
},
);

Expand Down Expand Up @@ -348,6 +395,7 @@
data: table,
positionFormat,
getPath,
invertedOffsets,
},
);

Expand Down
35 changes: 35 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, any>,
Expand Down
Loading