Skip to content

Commit

Permalink
feat(duckdb-geospatial): add support for flipping coordinates
Browse files Browse the repository at this point in the history
- Extend api to support ST_FlipCoordinates
- Clarify convert (equivalent of st_transform) docs to mention it
that coordinates are expected as XY (longitude/latitude)
  • Loading branch information
ncclementi authored and gforsyth committed Jan 9, 2024
1 parent 7b79ec8 commit d47088b
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 1 deletion.
6 changes: 6 additions & 0 deletions ibis/backends/duckdb/registry.py
Expand Up @@ -61,6 +61,11 @@ def _centroid(t, op):
return sa.func.st_centroid(arg, type_=Geometry_WKB)


def _geo_flip_coordinates(t, op):
arg = t.translate(op.arg)
return sa.func.st_flipcoordinates(arg, type_=Geometry_WKB)


def _geo_end_point(t, op):
arg = t.translate(op.arg)
return sa.func.st_endpoint(arg, type_=Geometry_WKB)
Expand Down Expand Up @@ -568,6 +573,7 @@ def _array_remove(t, op):
ops.GeoX: unary(sa.func.ST_X),
ops.GeoY: unary(sa.func.ST_Y),
ops.GeoConvert: _geo_convert,
ops.GeoFlipCoordinates: _geo_flip_coordinates,
# other ops
ops.TimestampRange: fixed_arity(sa.func.range, 3),
ops.RegexSplit: fixed_arity(sa.func.str_split_regex, 2),
Expand Down
20 changes: 20 additions & 0 deletions ibis/backends/duckdb/tests/test_geospatial.py
Expand Up @@ -214,6 +214,26 @@ def test_geospatial_convert(geotable, gdf):
)


def test_geospatial_flip_coordinates(geotable):
flipped = geotable.geom.flip_coordinates()

# flipped coords
point = shapely.geometry.Point(40, -100)
line_string = shapely.geometry.LineString([[0, 0], [1, 1], [1, 2], [2, 2]])
polygon = shapely.geometry.Polygon(((0, 0), (0, 1), (1, 1), (1, 0), (0, 0)))

d = {
"name": ["Point", "LineString", "Polygon"],
"geometry": [point, line_string, polygon],
}

flipped_gdf = gpd.GeoDataFrame(d)

gtm.assert_geoseries_equal(
flipped.to_pandas(), flipped_gdf.geometry, check_crs=False
)


def test_create_table_geospatial_types(geotable, con):
name = ibis.util.gen_name("geotable")

Expand Down
7 changes: 7 additions & 0 deletions ibis/expr/operations/geospatial.py
Expand Up @@ -487,3 +487,10 @@ class GeoAsText(GeoSpatialUnOp):
"""Return the Well-Known Text (WKT) representation of the input, without SRID metadata."""

dtype = dt.string


@public
class GeoFlipCoordinates(GeoSpatialUnOp):
"""Returns a new geometry with the coordinates of the input geometry "flipped" so that x = y and y = x."""

dtype = dt.geometry
18 changes: 17 additions & 1 deletion ibis/expr/types/geospatial.py
Expand Up @@ -674,7 +674,8 @@ def transform(self, srid: ir.IntegerValue) -> GeoSpatialValue:
def convert(
self, source: ir.StringValue, target: ir.StringValue | ir.IntegerValue
) -> GeoSpatialValue:
"""Transform a geometry into a new SRID (CRS).
"""Transform a geometry into a new SRID (CRS). The coordinates are assumed
to always be XY (Longitude-Latitude).
Parameters
----------
Expand All @@ -687,6 +688,11 @@ def convert(
-------
GeoSpatialValue
Transformed geometry
See Also
--------
[`flip_coordinates`](#ibis.expr.types.geospatial.GeoSpatialValue.flip_coordinates)
"""
return ops.GeoConvert(self, source, target).to_expr()

Expand Down Expand Up @@ -748,6 +754,16 @@ def line_merge(self) -> ir.LineStringValue:
"""
return ops.GeoLineMerge(self).to_expr()

def flip_coordinates(self) -> GeoSpatialValue:
"""Flip coordinates of a geometry so that x = y and y = x.
Returns
-------
GeoSpatialValue
New geometry with flipped coordinates
"""
return ops.GeoFlipCoordinates(self).to_expr()


@public
class GeoSpatialScalar(NumericScalar, GeoSpatialValue):
Expand Down

0 comments on commit d47088b

Please sign in to comment.