Skip to content

Commit

Permalink
Feat: reverse the layer order for automatically split geometry (#516)
Browse files Browse the repository at this point in the history
## What I am changing
<!-- What were the high-level goals of the change? -->
- Layer order for objects with mixed geometry types

## How I did it
Changed list appending order for `split_mixed_gdf` and `parse_wkb_table`
function from [`points`, `linestrings`, `polygons`] to [`polygons`,
`linestrings`, `points`]

## How you can test it
<!-- How might a reviewer test your changes to verify that they work as
expected? -->
- Points and paths should be now plotted on top of polygon for better
visibility.

Old:

![Untitled](https://github.com/developmentseed/lonboard/assets/17250607/f0d4a4f5-d69d-4e27-bbc7-3d159d1d2e6b)

New:

![image](https://github.com/developmentseed/lonboard/assets/17250607/8e154ff7-996a-4556-9406-daa355d4184a)

## Related Issues
#513

---------

Co-authored-by: Kyle Barron <[email protected]>
  • Loading branch information
RaczeQ and kylebarron authored May 14, 2024
1 parent 68cec33 commit 68cc745
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 41 deletions.
53 changes: 23 additions & 30 deletions lonboard/_geoarrow/parse_wkb.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ def parse_wkb_table(table: pa.Table) -> List[pa.Table]:
return [table]

# Handle WKB input
parsed_tables = []
crs_str = get_field_crs(field)
shapely_arr = shapely.from_wkb(column)

Expand All @@ -67,35 +66,29 @@ def parse_wkb_table(table: pa.Table) -> List[pa.Table]:
(type_ids == GeometryType.POLYGON) | (type_ids == GeometryType.MULTIPOLYGON)
)[0]

if len(point_indices) > 0:
point_field, point_arr = construct_geometry_array(
shapely_arr[point_indices],
crs_str=crs_str,
)
point_table = table.take(point_indices).set_column(
field_idx, point_field, point_arr
)
parsed_tables.append(point_table)

if len(linestring_indices) > 0:
linestring_field, linestring_arr = construct_geometry_array(
shapely_arr[linestring_indices],
crs_str=crs_str,
)
linestring_table = table.take(linestring_indices).set_column(
field_idx, linestring_field, linestring_arr
)
parsed_tables.append(linestring_table)

if len(polygon_indices) > 0:
polygon_field, polygon_arr = construct_geometry_array(
shapely_arr[polygon_indices],
crs_str=crs_str,
)
polygon_table = table.take(polygon_indices).set_column(
field_idx, polygon_field, polygon_arr
)
parsed_tables.append(polygon_table)
# Here we intentionally check geometries in a specific order.
# Starting from polygons, then linestrings, then points,
# so that the order of generated layers is polygon, then path then scatterplot.
# This ensures that points are rendered on top and polygons on the bottom.
parsed_tables = []
for single_type_geometry_indices in (
polygon_indices,
linestring_indices,
point_indices,
):
if len(single_type_geometry_indices) > 0:
single_type_geometry_field, single_type_geometry_arr = (
construct_geometry_array(
shapely_arr[single_type_geometry_indices],
crs_str=crs_str,
)
)
single_type_geometry_table = table.take(
single_type_geometry_indices
).set_column(
field_idx, single_type_geometry_field, single_type_geometry_arr
)
parsed_tables.append(single_type_geometry_table)

return parsed_tables

Expand Down
20 changes: 13 additions & 7 deletions lonboard/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,24 +140,30 @@ def split_mixed_gdf(gdf: gpd.GeoDataFrame) -> List[gpd.GeoDataFrame]:
if unique_type_ids == {GeometryType.POLYGON, GeometryType.MULTIPOLYGON}:
return [gdf]

gdfs = []
point_indices = np.where(
(type_ids == GeometryType.POINT) | (type_ids == GeometryType.MULTIPOINT)
)[0]
if len(point_indices) > 0:
gdfs.append(gdf.iloc[point_indices])

linestring_indices = np.where(
(type_ids == GeometryType.LINESTRING)
| (type_ids == GeometryType.MULTILINESTRING)
)[0]
if len(linestring_indices) > 0:
gdfs.append(gdf.iloc[linestring_indices])

polygon_indices = np.where(
(type_ids == GeometryType.POLYGON) | (type_ids == GeometryType.MULTIPOLYGON)
)[0]
if len(polygon_indices) > 0:
gdfs.append(gdf.iloc[polygon_indices])

# Here we intentionally check geometries in a specific order.
# Starting from polygons, then linestrings, then points,
# so that the order of generated layers is polygon, then path then scatterplot.
# This ensures that points are rendered on top and polygons on the bottom.
gdfs = []
for single_type_geometry_indices in (
polygon_indices,
linestring_indices,
point_indices,
):
if len(single_type_geometry_indices) > 0:
gdfs.append(gdf.iloc[single_type_geometry_indices])

return gdfs
8 changes: 4 additions & 4 deletions tests/test_viz.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ def test_viz_wkb_pyarrow():
def test_viz_wkb_mixed_pyarrow():
table = pq.read_table(fixtures_dir / "monaco_nofilter_noclip_compact.parquet")
map_ = viz(table)
assert isinstance(map_.layers[0], ScatterplotLayer)
assert isinstance(map_.layers[0], PolygonLayer)
assert isinstance(map_.layers[1], PathLayer)
assert isinstance(map_.layers[2], PolygonLayer)
assert isinstance(map_.layers[2], ScatterplotLayer)


def test_viz_reproject():
Expand Down Expand Up @@ -84,9 +84,9 @@ def test_viz_geo_interface_mixed_feature_collection():
geo_interface_obj = GeoInterfaceHolder(gdf)
map_ = viz(geo_interface_obj)

assert isinstance(map_.layers[0], ScatterplotLayer)
assert isinstance(map_.layers[0], PolygonLayer)
assert isinstance(map_.layers[1], PathLayer)
assert isinstance(map_.layers[2], PolygonLayer)
assert isinstance(map_.layers[2], ScatterplotLayer)


def test_viz_geopandas_geodataframe():
Expand Down

0 comments on commit 68cc745

Please sign in to comment.