Skip to content

Commit

Permalink
feat(duckdb): allow casting to geometry from text
Browse files Browse the repository at this point in the history
  • Loading branch information
cpcloud committed Sep 25, 2024
1 parent d3496fd commit 4182393
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 11 deletions.
9 changes: 9 additions & 0 deletions ibis/backends/duckdb/tests/test_geospatial.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,3 +385,12 @@ def test_load_spatial_casting(ext_dir):

assert geo_expr.type().is_geospatial()
assert isinstance(con.execute(geo_expr), gpd.GeoSeries)


def test_geom_from_string(con):
value = ibis.literal("POINT (1 2)")
assert value.type().is_string()

Check warning on line 392 in ibis/backends/duckdb/tests/test_geospatial.py

View check run for this annotation

Codecov / codecov/patch

ibis/backends/duckdb/tests/test_geospatial.py#L390-L392

Added lines #L390 - L392 were not covered by tests

expr = value.cast("geometry")
result = con.execute(expr)
assert result == shapely.from_wkt("POINT (1 2)")

Check warning on line 396 in ibis/backends/duckdb/tests/test_geospatial.py

View check run for this annotation

Codecov / codecov/patch

ibis/backends/duckdb/tests/test_geospatial.py#L394-L396

Added lines #L394 - L396 were not covered by tests
10 changes: 7 additions & 3 deletions ibis/backends/sql/compilers/duckdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -410,13 +410,17 @@ def visit_TimestampFromYMDHMS(
return self.f[func](*args)

def visit_Cast(self, op, *, arg, to):
dtype = op.arg.dtype
if to.is_interval():
func = self.f[f"to_{_INTERVAL_SUFFIXES[to.unit.short]}"]
return func(sg.cast(arg, to=self.type_mapper.from_ibis(dt.int32)))
elif to.is_timestamp() and op.arg.dtype.is_integer():
elif to.is_timestamp() and dtype.is_integer():
return self.f.to_timestamp(arg)
elif to.is_geospatial() and op.arg.dtype.is_binary():
return self.f.st_geomfromwkb(arg)
elif to.is_geospatial():
if dtype.is_binary():
return self.f.st_geomfromwkb(arg)

Check warning on line 421 in ibis/backends/sql/compilers/duckdb.py

View check run for this annotation

Codecov / codecov/patch

ibis/backends/sql/compilers/duckdb.py#L421

Added line #L421 was not covered by tests
elif dtype.is_string():
return self.f.st_geomfromtext(arg)

Check warning on line 423 in ibis/backends/sql/compilers/duckdb.py

View check run for this annotation

Codecov / codecov/patch

ibis/backends/sql/compilers/duckdb.py#L423

Added line #L423 was not covered by tests

return self.cast(arg, to)

Expand Down
17 changes: 9 additions & 8 deletions ibis/expr/types/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,16 +203,17 @@ def cast(self, target_type: Any) -> Value:
"""
op = ops.Cast(self, to=target_type)

if op.to == self.type():
# noop case if passed type is the same
to = op.to
dtype = self.type()

if to == dtype or (
to.is_geospatial()
and dtype.is_geospatial()
and (dtype.geotype or "geometry") == to.geotype
):
# no-op case if passed type is the same
return self

if op.to.is_geospatial() and not self.type().is_binary():
from_geotype = self.type().geotype or "geometry"
to_geotype = op.to.geotype
if from_geotype == to_geotype:
return self

return op.to_expr()

def try_cast(self, target_type: Any) -> Value:
Expand Down

0 comments on commit 4182393

Please sign in to comment.