Skip to content

Commit 81e0f58

Browse files
authored
Fixed multi polygon attribute rendering (#49)
1 parent c85b576 commit 81e0f58

File tree

5 files changed

+60
-25
lines changed

5 files changed

+60
-25
lines changed

examples/multipolygon/app.tsx

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

1111
const INITIAL_VIEW_STATE = {
12-
latitude: 0,
12+
latitude: 25,
1313
longitude: 0,
14-
zoom: 2,
14+
zoom: 1.5,
1515
bearing: 0,
1616
pitch: 0,
1717
};
@@ -58,7 +58,7 @@ function Root() {
5858
wireframe: true,
5959
extruded: true,
6060
getPolygon: table.getChild("geometry")!,
61-
getFillColor: [255, 0, 0],
61+
getFillColor: table.getChild("pop_colors")!,
6262
// pickable: true,
6363
// autoHighlight: true,
6464
})

examples/multipolygon/generate_data.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,27 @@
11
import geopandas as gpd
2+
import numpy as np
3+
import pyarrow as pa
24
import pyarrow.feather as feather
5+
from lonboard.colormap import apply_continuous_cmap
36
from lonboard.geoarrow.geopandas_interop import geopandas_to_geoarrow
7+
from palettable.colorbrewer.diverging import PRGn_11
48

59

610
def main():
711
gdf = gpd.read_file("ne_10m_admin_0_countries.zip", engine="pyogrio")
812
table = geopandas_to_geoarrow(gdf)
13+
14+
log_pop_est = np.where(gdf["POP_EST"] == 0, 0, np.log10(gdf["POP_EST"]))
15+
16+
min_pop = np.min(log_pop_est)
17+
max_pop = np.max(log_pop_est)
18+
normalized = (log_pop_est - min_pop) / (max_pop - min_pop)
19+
colors = apply_continuous_cmap(normalized, PRGn_11)
20+
21+
table = table.append_column(
22+
"pop_colors", pa.FixedSizeListArray.from_arrays(colors.flatten("C"), 3)
23+
)
24+
925
feather.write_feather(
1026
table, "ne_10m_admin_0_countries.feather", compression="uncompressed"
1127
)

src/path-layer.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ export class GeoArrowPathLayer<
286286

287287
const nDim = pointData.type.listSize;
288288
const flatCoordinateArray = coordData.values;
289-
const resolvedRingOffsets =
289+
const multiLineStringToCoordOffsets =
290290
getMultiLineStringResolvedOffsets(multiLineStringData);
291291

292292
const props: PathLayerProps = {
@@ -303,6 +303,8 @@ export class GeoArrowPathLayer<
303303
data: {
304304
// Note: this needs to be the length one level down.
305305
length: lineStringData.length,
306+
// Offsets into coordinateArray where each single-line string starts
307+
//
306308
// Note: this is ringOffsets, not geomOffsets because we're rendering
307309
// the individual paths on the map.
308310
// @ts-ignore
@@ -313,19 +315,21 @@ export class GeoArrowPathLayer<
313315
},
314316
};
315317

318+
// Note: here we use multiLineStringToCoordOffsets, not ringOffsets,
319+
// because we want the mapping from the _feature_ to the vertex
316320
assignAccessor({
317321
props,
318322
propName: "getColor",
319323
propInput: this.props.getColor,
320324
chunkIdx: recordBatchIdx,
321-
geomCoordOffsets: resolvedRingOffsets,
325+
geomCoordOffsets: multiLineStringToCoordOffsets,
322326
});
323327
assignAccessor({
324328
props,
325329
propName: "getWidth",
326330
propInput: this.props.getWidth,
327331
chunkIdx: recordBatchIdx,
328-
geomCoordOffsets: resolvedRingOffsets,
332+
geomCoordOffsets: multiLineStringToCoordOffsets,
329333
});
330334

331335
const layer = new PathLayer(this.getSubLayerProps(props));

src/solid-polygon-layer.ts

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -308,11 +308,22 @@ export class GeoArrowSolidPolygonLayer<
308308
const nDim = pointData.type.listSize;
309309

310310
// const geomOffsets = multiPolygonData.valueOffsets;
311-
const polygonOffsets = polygonData.valueOffsets;
311+
// const polygonOffsets = polygonData.valueOffsets;
312312
// const ringOffsets = ringData.valueOffsets;
313313
const flatCoordinateArray = coordData.values;
314314

315-
const resolvedRingOffsets =
315+
// NOTE: we have two different uses of offsets. One is for _rendering_
316+
// each polygon. The other is for mapping _accessor attributes_ from one
317+
// value per feature to one value per vertex. And for that we need to use
318+
// these offsets in two different ways.
319+
//
320+
// TODO: Don't construct the offsets twice from scratch? I.e. from the
321+
// polygon-to-coord offsets you should be able to infer the
322+
// multi-polygon-to-coord offsets? Or something like that
323+
const resolvedPolygonToCoordOffsets =
324+
getPolygonResolvedOffsets(polygonData);
325+
326+
const resolvedMultiPolygonToCoordOffsets =
316327
getMultiPolygonResolvedOffsets(multiPolygonData);
317328

318329
const props: SolidPolygonLayerProps = {
@@ -330,11 +341,12 @@ export class GeoArrowSolidPolygonLayer<
330341
// Note: this needs to be the length one level down, because we're
331342
// rendering the polygons, not the multipolygons
332343
length: polygonData.length,
333-
// Offsets into coordinateArray where each polygon starts
334-
// Note: this is polygonOffsets, not geomOffsets because we're
335-
// rendering the individual polygons on the map.
336-
// @ts-ignore
337-
startIndices: resolvedRingOffsets,
344+
// Offsets into coordinateArray where each single-polygon starts
345+
//
346+
// Note that this is polygonToCoordOffsets and not geomToCoordOffsets
347+
// because we're rendering each part of the MultiPolygon individually
348+
// @ts-expect-error
349+
startIndices: resolvedPolygonToCoordOffsets,
338350
attributes: {
339351
getPolygon: { value: flatCoordinateArray, size: nDim },
340352
},
@@ -346,21 +358,21 @@ export class GeoArrowSolidPolygonLayer<
346358
propName: "getElevation",
347359
propInput: this.props.getElevation,
348360
chunkIdx: recordBatchIdx,
349-
geomCoordOffsets: resolvedRingOffsets,
361+
geomCoordOffsets: resolvedMultiPolygonToCoordOffsets,
350362
});
351363
assignAccessor({
352364
props,
353365
propName: "getFillColor",
354366
propInput: this.props.getFillColor,
355367
chunkIdx: recordBatchIdx,
356-
geomCoordOffsets: resolvedRingOffsets,
368+
geomCoordOffsets: resolvedMultiPolygonToCoordOffsets,
357369
});
358370
assignAccessor({
359371
props,
360372
propName: "getLineColor",
361373
propInput: this.props.getLineColor,
362374
chunkIdx: recordBatchIdx,
363-
geomCoordOffsets: resolvedRingOffsets,
375+
geomCoordOffsets: resolvedMultiPolygonToCoordOffsets,
364376
});
365377

366378
const layer = new SolidPolygonLayer(this.getSubLayerProps(props));

src/utils.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -524,17 +524,20 @@ export function getPolygonResolvedOffsets(data: PolygonData): Int32Array {
524524
return resolvedRingOffsets;
525525
}
526526

527-
/**
528-
* Get offsets pointing from each exploded polygon from the MultiPolygonArray to
529-
* their coordinates.
530-
*
531-
* Despite the fact that this is a MultiPolygonArray, we want the offsets from
532-
* the _PolygonArray_ instead, because each MultiPolygon is rendered as separate
533-
* Polygons.
534-
*/
535527
export function getMultiPolygonResolvedOffsets(
536528
data: MultiPolygonData
537529
): Int32Array {
538530
const polygonData = getMultiPolygonChild(data);
539-
return getPolygonResolvedOffsets(polygonData);
531+
const ringData = getPolygonChild(polygonData);
532+
533+
const geomOffsets = data.valueOffsets;
534+
const polygonOffsets = polygonData.valueOffsets;
535+
const ringOffsets = ringData.valueOffsets;
536+
537+
const resolvedRingOffsets = new Int32Array(geomOffsets.length);
538+
for (let i = 0; i < resolvedRingOffsets.length; ++i) {
539+
resolvedRingOffsets[i] = ringOffsets[polygonOffsets[geomOffsets[i]]];
540+
}
541+
542+
return resolvedRingOffsets;
540543
}

0 commit comments

Comments
 (0)