Skip to content

Commit b128f31

Browse files
authored
function accessors (#100)
* Support for function accessors * updated point example * updated Readme
1 parent c84555e commit b128f31

File tree

15 files changed

+224
-46
lines changed

15 files changed

+224
-46
lines changed

README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,53 @@ Standalone examples exist in the [`examples/`](examples/) directory. Create an i
2121

2222
More hosted examples on Observable are planned.
2323

24+
## Providing accessors
25+
26+
All deck.gl layers have two types of properties: ["Render Options"](https://deck.gl/docs/api-reference/layers/scatterplot-layer#render-options) — constant properties across a layer — and "Data Accessors" — properties that can vary across rows. An accessor is any property prefixed with `get`, like `GeoArrowScatterplotLayer`'s `getFillColor`.
27+
28+
With `@geoarrow/deck.gl-layers` specifically, there are two ways to pass these data accessors, either as pre-computed columns or with function callbacks on Arrow data.
29+
30+
### Pre-computed Arrow columns
31+
32+
If you have an Arrow column ([`Vector`](https://arrow.apache.org/docs/js/classes/Arrow_dom.Vector.html) in Arrow JS terminology), you can pass that directly into a layer:
33+
34+
```ts
35+
import { Table } from "apache-arrow";
36+
import { GeoArrowScatterplotLayer } from "@geoarrow/deck.gl-layers";
37+
38+
const table = new Table(...);
39+
const deckLayer = new GeoArrowScatterplotLayer({
40+
id: "scatterplot",
41+
data: table,
42+
/// Geometry column
43+
getPosition: table.getChild("geometry")!,
44+
/// Column of type FixedSizeList[3] or FixedSizeList[4], with child type Uint8
45+
getFillColor: table.getChild("colors")!,
46+
});
47+
```
48+
49+
For example, [lonboard](https://github.com/developmentseed/lonboard) computes Arrow columns on the Python side for all attributes so that end users have available the full capabilities Python. Then those columns are serialized to Python and the resulting `arrow.Vector` is passed into the relevant layer.
50+
51+
### Function accessors
52+
53+
GeoArrow layers accept a callback that takes an object with `index` and `data`. `data` is an `arrow.RecordBatch` object (a vertical section of the input `Table`), and `index` is the positional index of the current row of that batch. In TypeScript, you should see accurate type checking.
54+
55+
```ts
56+
const deckLayer = new GeoArrowPathLayer({
57+
id: "geoarrow-path",
58+
data: table,
59+
getColor: ({ index, data, target }) => {
60+
const recordBatch = data.data;
61+
const row = recordBatch.get(index)!;
62+
return COLORS_LOOKUP[row["scalerank"]];
63+
},
64+
}),
65+
```
66+
67+
The full example is in [`examples/multilinestring/app.tsx`](examples/multilinestring/app.tsx).
68+
69+
You can also use assign to the `target` prop to reduce garbage collector overhead, as described in the [deck.gl performance guide](https://deck.gl/docs/developer-guide/performance#supply-binary-blobs-to-the-data-prop).
70+
2471
## Data Loading
2572

2673
To create deck.gl layers using this library, you need to first get GeoArrow-formatted data into the browser, discussed below.
@@ -38,6 +85,7 @@ import { GeoArrowScatterplotLayer } from "@geoarrow/deck.gl-layers";
3885
const resp = await fetch("url/to/file.arrow");
3986
const jsTable = await tableFromIPC(resp);
4087
const deckLayer = new GeoArrowScatterplotLayer({
88+
id: "scatterplot",
4189
data: jsTable,
4290
/// Replace with the correct geometry column name
4391
getPosition: jsTable.getChild("geometry")!,
@@ -60,6 +108,7 @@ const arrayBuffer = await resp.arrayBuffer();
60108
const wasmTable = readParquet(new Uint8Array(arrayBuffer));
61109
const jsTable = tableFromIPC(wasmTable.intoIPCStream());
62110
const deckLayer = new GeoArrowScatterplotLayer({
111+
id: "scatterplot",
63112
data: jsTable,
64113
/// Replace with the correct geometry column name
65114
getPosition: jsTable.getChild("geometry")!,
@@ -82,6 +131,7 @@ const arrayBuffer = await resp.arrayBuffer();
82131
const wasmTable = readGeoParquet(new Uint8Array(arrayBuffer));
83132
const jsTable = tableFromIPC(wasmTable.intoTable().intoIPCStream());
84133
const deckLayer = new GeoArrowScatterplotLayer({
134+
id: "scatterplot",
85135
data: jsTable,
86136
/// Replace with the correct geometry column name
87137
getPosition: jsTable.getChild("geometry")!,
@@ -104,6 +154,7 @@ const arrayBuffer = await resp.arrayBuffer();
104154
const wasmTable = readFlatGeobuf(new Uint8Array(arrayBuffer));
105155
const jsTable = tableFromIPC(wasmTable.intoTable().intoIPCStream());
106156
const deckLayer = new GeoArrowScatterplotLayer({
157+
id: "scatterplot",
107158
data: jsTable,
108159
/// Replace with the correct geometry column name
109160
getPosition: jsTable.getChild("geometry")!,

examples/multilinestring/app.tsx

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ const GEOARROW_MULTILINESTRING_DATA =
99
"http://localhost:8080/ne_10m_roads_north_america.feather";
1010

1111
const INITIAL_VIEW_STATE = {
12-
latitude: 20,
13-
longitude: 0,
14-
zoom: 2,
12+
latitude: 40,
13+
longitude: -90,
14+
zoom: 4,
1515
bearing: 0,
1616
pitch: 0,
1717
};
@@ -24,16 +24,20 @@ const NAV_CONTROL_STYLE = {
2424
left: 10,
2525
};
2626

27-
function Root() {
28-
const onClick = (info) => {
29-
if (info.object) {
30-
// eslint-disable-next-line
31-
alert(
32-
`${info.object.properties.name} (${info.object.properties.abbrev})`
33-
);
34-
}
35-
};
27+
// https://colorbrewer2.org/#type=sequential&scheme=PuBuGn&n=9
28+
const COLORS_LOOKUP = {
29+
"3": [255, 247, 251],
30+
"4": [236, 226, 240],
31+
"5": [208, 209, 230],
32+
"6": [166, 189, 219],
33+
"7": [103, 169, 207],
34+
"8": [54, 144, 192],
35+
"9": [2, 129, 138],
36+
"10": [1, 108, 89],
37+
"11": [1, 70, 54],
38+
};
3639

40+
function Root() {
3741
const [table, setTable] = useState<arrow.Table | null>(null);
3842

3943
useEffect(() => {
@@ -58,17 +62,14 @@ function Root() {
5862
new GeoArrowPathLayer({
5963
id: "geoarrow-path",
6064
data: table,
61-
// getPath: table.getC
62-
// getPosition: table.getChild("geometry")!,
63-
getColor:[255, 0, 0],
64-
widthMinPixels: 0.2,
65-
// getFillColor: [255, 0, 0],
66-
// getFillColor: table.getChild("colors")!,
67-
// getLineColor: table.getChild("colors")!,
68-
radiusMinPixels: 4,
69-
getPointRadius: 10,
70-
pointRadiusMinPixels: 0.8,
71-
})
65+
getColor: ({ index, data }) => {
66+
const recordBatch = data.data;
67+
const row = recordBatch.get(index)!;
68+
return COLORS_LOOKUP[row["scalerank"]];
69+
},
70+
widthMinPixels: 0.8,
71+
pickable: true,
72+
}),
7273
);
7374

7475
return (

examples/point/app.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,17 @@ function Root() {
5454
new GeoArrowScatterplotLayer({
5555
id: "geoarrow-points",
5656
data: table,
57+
// Pre-computed colors in the original table
5758
getFillColor: table.getChild("colors")!,
58-
radiusMinPixels: 1.5,
59-
getPointRadius: 10,
60-
pointRadiusMinPixels: 0.8,
59+
opacity: 0.01,
60+
getRadius: ({ index, data }) => {
61+
const recordBatch = data.data;
62+
const row = recordBatch.get(index)!;
63+
return row["avg_d_kbps"] / 10;
64+
},
65+
radiusMinPixels: 0.1,
6166
pickable: true,
62-
})
67+
}),
6368
);
6469

6570
return (

src/arc-layer.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,12 +164,14 @@ export class GeoArrowArcLayer<
164164
...ourDefaultProps,
165165
...otherProps,
166166

167-
// @ts-expect-error used for picking purposes
167+
// used for picking purposes
168168
recordBatchIdx,
169169
tableOffsets,
170170

171171
id: `${this.props.id}-geoarrow-arc-${recordBatchIdx}`,
172172
data: {
173+
// @ts-expect-error passed through to enable use by function accessors
174+
data: table.batches[recordBatchIdx],
173175
length: sourceData.length,
174176
attributes: {
175177
getSourcePosition: {

src/column-layer.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,12 +168,14 @@ export class GeoArrowColumnLayer<
168168
...ourDefaultProps,
169169
...otherProps,
170170

171-
// @ts-expect-error used for picking purposes
171+
// used for picking purposes
172172
recordBatchIdx,
173173
tableOffsets,
174174

175175
id: `${this.props.id}-geoarrow-column-${recordBatchIdx}`,
176176
data: {
177+
// @ts-expect-error passed through to enable use by function accessors
178+
data: table.batches[recordBatchIdx],
177179
length: geometryData.length,
178180
attributes: {
179181
getPosition: {

src/h3-hexagon-layer.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,15 @@ export class GeoArrowH3HexagonLayer<
105105
...ourDefaultProps,
106106
...otherProps,
107107

108-
// @ts-expect-error used for picking purposes
108+
// used for picking purposes
109109
recordBatchIdx,
110110
tableOffsets,
111111

112112
id: `${this.props.id}-geoarrow-arc-${recordBatchIdx}`,
113113

114114
data: {
115+
// @ts-expect-error passed through to enable use by function accessors
116+
data: table.batches[recordBatchIdx],
115117
length: hexData.length,
116118
attributes: {
117119
getHexagon: {

src/heatmap-layer.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,14 @@ export class GeoArrowHeatmapLayer<
128128
...ourDefaultProps,
129129
...otherProps,
130130

131-
// @ts-expect-error used for picking purposes
131+
// used for picking purposes
132132
recordBatchIdx,
133133
tableOffsets,
134134

135135
id: `${this.props.id}-geoarrow-heatmap-${recordBatchIdx}`,
136136
data: {
137+
// @ts-expect-error passed through to enable use by function accessors
138+
data: table.batches[recordBatchIdx],
137139
length: geometryData.length,
138140
attributes: {
139141
getPosition: {

src/path-layer.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,9 @@ export class GeoArrowPathLayer<
182182

183183
id: `${this.props.id}-geoarrow-path-${recordBatchIdx}`,
184184
data: {
185+
// @ts-expect-error passed through to enable use by function accessors
186+
data: table.batches[recordBatchIdx],
185187
length: lineStringData.length,
186-
// @ts-expect-error
187188
startIndices: geomOffsets,
188189
attributes: {
189190
getPath: { value: flatCoordinateArray, size: nDim },
@@ -255,10 +256,14 @@ export class GeoArrowPathLayer<
255256
// used for picking purposes
256257
recordBatchIdx,
257258
tableOffsets,
258-
invertedGeomOffsets: invertOffsets(geomOffsets),
259259

260260
id: `${this.props.id}-geoarrow-path-${recordBatchIdx}`,
261261
data: {
262+
// @ts-expect-error passed through to enable use by function accessors
263+
data: table.batches[recordBatchIdx],
264+
// Map from expanded multi-geometry index to original index
265+
// Used both in picking and for function callbacks
266+
invertedGeomOffsets: invertOffsets(geomOffsets),
262267
// Note: this needs to be the length one level down.
263268
length: lineStringData.length,
264269
// Offsets into coordinateArray where each single-line string starts

src/picking.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import { GeoArrowPickingInfo } from "./types";
55
export interface GeoArrowExtraPickingProps {
66
recordBatchIdx: number;
77
tableOffsets: Uint32Array;
8-
invertedGeomOffsets?: Uint8Array | Uint16Array | Uint32Array;
8+
data: {
9+
invertedGeomOffsets?: Uint8Array | Uint16Array | Uint32Array;
10+
};
911
}
1012

1113
export function getPickingInfo(
@@ -22,8 +24,8 @@ export function getPickingInfo(
2224

2325
// if a Multi- geometry dataset, map from the rendered index back to the
2426
// feature index
25-
if (sourceLayer.props.invertedGeomOffsets) {
26-
index = sourceLayer.props.invertedGeomOffsets[index];
27+
if (sourceLayer.props.data.invertedGeomOffsets) {
28+
index = sourceLayer.props.data.invertedGeomOffsets[index];
2729
}
2830

2931
const recordBatchIdx = sourceLayer.props.recordBatchIdx;

src/scatterplot-layer.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,12 +169,14 @@ export class GeoArrowScatterplotLayer<
169169
...ourDefaultProps,
170170
...otherProps,
171171

172-
// @ts-expect-error used for picking purposes
172+
// used for picking purposes
173173
recordBatchIdx,
174174
tableOffsets,
175175

176176
id: `${this.props.id}-geoarrow-scatterplot-${recordBatchIdx}`,
177177
data: {
178+
// @ts-expect-error passed through to enable use by function accessors
179+
data: table.batches[recordBatchIdx],
178180
length: geometryData.length,
179181
attributes: {
180182
getPosition: {
@@ -237,13 +239,17 @@ export class GeoArrowScatterplotLayer<
237239
...ourDefaultProps,
238240
...otherProps,
239241

240-
// @ts-expect-error used for picking purposes
242+
// used for picking purposes
241243
recordBatchIdx,
242244
tableOffsets,
243-
invertedGeomOffsets: invertOffsets(geomOffsets),
244245

245246
id: `${this.props.id}-geoarrow-scatterplot-${recordBatchIdx}`,
246247
data: {
248+
// @ts-expect-error passed through to enable use by function accessors
249+
data: table.batches[recordBatchIdx],
250+
// Map from expanded multi-geometry index to original index
251+
// Used both in picking and for function callbacks
252+
invertedGeomOffsets: invertOffsets(geomOffsets),
247253
// Note: this needs to be the length one level down.
248254
length: pointData.length,
249255
attributes: {

0 commit comments

Comments
 (0)