Skip to content

Commit

Permalink
Fix: fix sliced array input for reprojection (#391)
Browse files Browse the repository at this point in the history
When operating on _sliced_ input, the physical offsets are incorrect
because the current array points to a range not at the beginning of the
array. So when creating new arrays that are not sliced, we need to
subtract off the original offset of the first element.

This is the primary bug from
#390
  • Loading branch information
kylebarron authored Feb 27, 2024
1 parent 5a7f08c commit 14a6fdc
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 6 deletions.
25 changes: 19 additions & 6 deletions lonboard/_geoarrow/ops/reproject.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,19 @@ def _reproject_chunk_nest_3(arr: pa.ListArray, transformer: Transformer):
return _map_coords_nest_3(arr, callback)


def _copy_sliced_offsets(offsets: pa.Int32Array) -> pa.Int32Array:
"""
When operating on _sliced_ input, the physical offsets are incorrect because the
current array points to a range not at the beginning of the array. So when creating
new arrays that are not sliced, we need to subtract off the original offset of the
first element.
"""
if offsets[0].as_py() == 0:
return offsets
else:
return pa.array(offsets.to_numpy() - offsets[0].as_py())


def _map_coords_nest_0(
arr: pa.FixedSizeListArray,
callback: Callable[[pa.FixedSizeListArray], pa.FixedSizeListArray],
Expand All @@ -179,7 +192,7 @@ def _map_coords_nest_1(
arr: pa.ListArray,
callback: Callable[[pa.FixedSizeListArray], pa.FixedSizeListArray],
):
geom_offsets = arr.offsets
geom_offsets = _copy_sliced_offsets(arr.offsets)
coords = arr.flatten()
new_coords = callback(coords)
new_geometry_array = pa.ListArray.from_arrays(geom_offsets, new_coords)
Expand All @@ -190,8 +203,8 @@ def _map_coords_nest_2(
arr: pa.ListArray,
callback: Callable[[pa.FixedSizeListArray], pa.FixedSizeListArray],
):
geom_offsets = arr.offsets
ring_offsets = arr.flatten().offsets
geom_offsets = _copy_sliced_offsets(arr.offsets)
ring_offsets = _copy_sliced_offsets(arr.flatten().offsets)
coords = arr.flatten().flatten()
new_coords = callback(coords)
new_ring_array = pa.ListArray.from_arrays(ring_offsets, new_coords)
Expand All @@ -203,9 +216,9 @@ def _map_coords_nest_3(
arr: pa.ListArray,
callback: Callable[[pa.FixedSizeListArray], pa.FixedSizeListArray],
):
geom_offsets = arr.offsets
polygon_offsets = arr.flatten().offsets
ring_offsets = arr.flatten().flatten().offsets
geom_offsets = _copy_sliced_offsets(arr.offsets)
polygon_offsets = _copy_sliced_offsets(arr.flatten().offsets)
ring_offsets = _copy_sliced_offsets(arr.flatten().flatten().offsets)
coords = arr.flatten().flatten().flatten()
new_coords = callback(coords)
new_ring_array = pa.ListArray.from_arrays(ring_offsets, new_coords)
Expand Down
10 changes: 10 additions & 0 deletions tests/test_geoarrow.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from lonboard import SolidPolygonLayer
from lonboard._constants import OGC_84
from lonboard._geoarrow.geopandas_interop import geopandas_to_geoarrow
from lonboard._geoarrow.ops.reproject import reproject_table
from lonboard._utils import get_geometry_column_index


Expand Down Expand Up @@ -51,3 +52,12 @@ def test_geoarrow_table_reprojection():
assert OGC_84 == CRS.from_json(
reprojected_crs_str
), "layer should be reprojected to WGS84"


def test_reproject_sliced_array():
"""See https://github.com/developmentseed/lonboard/issues/390"""
gdf = gpd.read_file(geodatasets.get_path("nybb"))
table = geopandas_to_geoarrow(gdf)
sliced_table = table.slice(2)
# This should work even with a sliced array.
_reprojected = reproject_table(sliced_table, to_crs=OGC_84)

0 comments on commit 14a6fdc

Please sign in to comment.