From 2a92306a465b2f5783a4592c18ae92dede6db87e Mon Sep 17 00:00:00 2001 From: Naty Clementi Date: Wed, 31 Jan 2024 11:07:40 -0500 Subject: [PATCH] docs(geospatial): add examples for duckdb supported methods (#8128) ## Description of changes - Add docstring examples for duckdb geospatial functions ## Issues closed Closes #7959 --- docs/_quarto.yml | 2 + ibis/expr/types/geospatial.py | 858 +++++++++++++++++++++++++++++++++- ibis/expr/types/numeric.py | 24 + 3 files changed, 881 insertions(+), 3 deletions(-) diff --git a/docs/_quarto.yml b/docs/_quarto.yml index 5c85612c30f7..acc400f29482 100644 --- a/docs/_quarto.yml +++ b/docs/_quarto.yml @@ -425,6 +425,8 @@ quartodoc: contents: - GeoSpatialValue - GeoSpatialColumn + - name: NumericValue.point + package: ibis.expr.types.numeric - kind: page summary: diff --git a/ibis/expr/types/geospatial.py b/ibis/expr/types/geospatial.py index 557e009e20f9..32ce1de93996 100644 --- a/ibis/expr/types/geospatial.py +++ b/ibis/expr/types/geospatial.py @@ -20,6 +20,30 @@ def area(self) -> ir.FloatingValue: ------- FloatingValue The area of `self` + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> t = ibis.examples.zones.fetch() + >>> t.geom.area() + ┏━━━━━━━━━━━━━━━┓ + ┃ GeoArea(geom) ┃ + ┡━━━━━━━━━━━━━━━┩ + │ float64 │ + ├───────────────┤ + │ 7.903953e+07 │ + │ 1.439095e+08 │ + │ 3.168508e+07 │ + │ 8.023733e+06 │ + │ 5.041488e+07 │ + │ 4.093479e+07 │ + │ 3.934104e+07 │ + │ 2.682802e+06 │ + │ 3.416422e+07 │ + │ 4.404143e+07 │ + │ … │ + └───────────────┘ """ return ops.GeoArea(self).to_expr() @@ -50,6 +74,30 @@ def as_text(self) -> ir.StringValue: ------- StringValue String value + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> t = ibis.examples.zones.fetch() + >>> t.geom.as_text() + ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + ┃ GeoAsText(geom) ┃ + ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩ + │ string │ + ├──────────────────────────────────────────────────────────────────────────────┤ + │ POLYGON ((933100.9183527103 192536.08569720192, 933091.0114800561 192572.17… │ + │ MULTIPOLYGON (((1033269.2435912937 172126.0078125, 1033439.6426391453 17088… │ + │ POLYGON ((1026308.7695066631 256767.6975403726, 1026495.5934945047 256638.6… │ + │ POLYGON ((992073.4667968601 203714.07598876953, 992068.6669922024 203711.50… │ + │ POLYGON ((935843.3104932606 144283.33585065603, 936046.5648079664 144173.41… │ + │ POLYGON ((966568.7466657609 158679.85468779504, 966615.255504474 158662.292… │ + │ POLYGON ((1010804.2179628164 218919.64069513977, 1011049.1648243815 218914.… │ + │ POLYGON ((1005482.2763733566 221686.46616631746, 1005304.8982993066 221499.… │ + │ POLYGON ((1043803.993348822 216615.9250395149, 1043849.7083857208 216473.16… │ + │ POLYGON ((1044355.0717166215 190734.32089698315, 1044612.1216432452 190156.… │ + │ … │ + └──────────────────────────────────────────────────────────────────────────────┘ """ return ops.GeoAsText(self).to_expr() @@ -75,6 +123,33 @@ def contains(self, right: GeoSpatialValue) -> ir.BooleanValue: ------- BooleanValue Whether `self` contains `right` + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> import shapely + >>> t = ibis.examples.zones.fetch() + >>> p = shapely.Point(935996.821, 191376.75) # centroid for zone 1 + >>> plit = ibis.literal(p, "geometry") + >>> t.geom.contains(plit).name("contains") + ┏━━━━━━━━━━┓ + ┃ contains ┃ + ┡━━━━━━━━━━┩ + │ boolean │ + ├──────────┤ + │ True │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ … │ + └──────────┘ """ return ops.GeoContains(self, right).to_expr() @@ -107,6 +182,36 @@ def covers(self, right: GeoSpatialValue) -> ir.BooleanValue: ------- BooleanValue Whether `self` covers `right` + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> import shapely + >>> t = ibis.examples.zones.fetch() + + Polygon area center in zone 1 + + >>> z1_ctr_buff = shapely.Point(935996.821, 191376.75).buffer(10) + >>> z1_ctr_buff_lit = ibis.literal(z1_ctr_buff, "geometry") + >>> t.geom.covers(z1_ctr_buff_lit).name("covers") + ┏━━━━━━━━━┓ + ┃ covers ┃ + ┡━━━━━━━━━┩ + │ boolean │ + ├─────────┤ + │ True │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ … │ + └─────────┘ """ return ops.GeoCovers(self, right).to_expr() @@ -122,11 +227,62 @@ def covered_by(self, right: GeoSpatialValue) -> ir.BooleanValue: ------- BooleanValue Whether `self` is covered by `right` + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> import shapely + >>> t = ibis.examples.zones.fetch() + + Polygon area center in zone 1 + + >>> pol_big = shapely.Point(935996.821,191376.75).buffer(10000) + >>> pol_big_lit = ibis.literal(pol_big, "geometry") + >>> t.geom.covered_by(pol_big_lit).name("covered_by") + ┏━━━━━━━━━━━━┓ + ┃ covered_by ┃ + ┡━━━━━━━━━━━━┩ + │ boolean │ + ├────────────┤ + │ True │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ … │ + └────────────┘ + >>> pol_small = shapely.Point(935996.821,191376.75).buffer(100) + >>> pol_small_lit = ibis.literal(pol_small, "geometry") + >>> t.geom.covered_by(pol_small_lit).name("covered_by") + ┏━━━━━━━━━━━━┓ + ┃ covered_by ┃ + ┡━━━━━━━━━━━━┩ + │ boolean │ + ├────────────┤ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ … │ + └────────────┘ """ return ops.GeoCoveredBy(self, right).to_expr() def crosses(self, right: GeoSpatialValue) -> ir.BooleanValue: - """Check if the geometries have at least one interior point in common. + """Check if the geometries have at least one, but not all, interior + points in common. Parameters ---------- @@ -137,6 +293,53 @@ def crosses(self, right: GeoSpatialValue) -> ir.BooleanValue: ------- BooleanValue Whether `self` and `right` have at least one common interior point. + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> import shapely + >>> t = ibis.examples.zones.fetch() + + Line from center of zone 1 to center of zone 2 + + >>> line = shapely.LineString([[935996.821, 191376.75], [1031085.719, 164018.754]]) + >>> line_lit = ibis.literal(line, "geometry") + >>> t.geom.crosses(line_lit).name("crosses") + ┏━━━━━━━━━┓ + ┃ crosses ┃ + ┡━━━━━━━━━┩ + │ boolean │ + ├─────────┤ + │ True │ + │ True │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ … │ + └─────────┘ + >>> t.filter(t.geom.crosses(line_lit))[["zone", "LocationID"]] + ┏━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓ + ┃ zone ┃ LocationID ┃ + ┡━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩ + │ string │ int32 │ + ├────────────────────────┼────────────┤ + │ Newark Airport │ 1 │ + │ Jamaica Bay │ 2 │ + │ Canarsie │ 39 │ + │ East Flatbush/Farragut │ 71 │ + │ Erasmus │ 85 │ + │ Flatbush/Ditmas Park │ 89 │ + │ Flatlands │ 91 │ + │ Green-Wood Cemetery │ 111 │ + │ Sunset Park West │ 228 │ + │ Windsor Terrace │ 257 │ + └────────────────────────┴────────────┘ """ return ops.GeoCrosses(self, right).to_expr() @@ -173,6 +376,33 @@ def disjoint(self, right: GeoSpatialValue) -> ir.BooleanValue: ------- BooleanValue Whether `self` and `right` are disjoint + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> import shapely + >>> t = ibis.examples.zones.fetch() + >>> p = shapely.Point(935996.821, 191376.75) # zone 1 centroid + >>> plit = ibis.literal(p, "geometry") + >>> t.geom.disjoint(plit).name("disjoint") + ┏━━━━━━━━━━┓ + ┃ disjoint ┃ + ┡━━━━━━━━━━┩ + │ boolean │ + ├──────────┤ + │ False │ + │ True │ + │ True │ + │ True │ + │ True │ + │ True │ + │ True │ + │ True │ + │ True │ + │ True │ + │ … │ + └──────────┘ """ return ops.GeoDisjoint(self, right).to_expr() @@ -194,6 +424,46 @@ def d_within( ------- BooleanValue Whether `self` is partially within `distance` from `right`. + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> import shapely + >>> t = ibis.examples.zones.fetch() + >>> penn_station = shapely.Point(986345.399 , 211974.446) + >>> penn_lit = ibis.literal(penn_station, "geometry") + + Check zones within 1000ft of Penn Station centroid + + >>> t.geom.d_within(penn_lit, 1000).name("d_within_1000") + ┏━━━━━━━━━━━━━━━┓ + ┃ d_within_1000 ┃ + ┡━━━━━━━━━━━━━━━┩ + │ boolean │ + ├───────────────┤ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ … │ + └───────────────┘ + >>> t.filter(t.geom.d_within(penn_lit, 1000))[["zone"]] + ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + ┃ zone ┃ + ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩ + │ string │ + ├──────────────────────────────┤ + │ East Chelsea │ + │ Midtown South │ + │ Penn Station/Madison Sq West │ + └──────────────────────────────┘ """ return ops.GeoDWithin(self, right, distance).to_expr() @@ -209,6 +479,30 @@ def geo_equals(self, right: GeoSpatialValue) -> ir.BooleanValue: ------- BooleanValue Whether `self` equals `right` + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> t = ibis.examples.zones.fetch() + >>> t.geom.geo_equals(t.geom) + ┏━━━━━━━━━━━━━━━━━━━━━━━┓ + ┃ GeoEquals(geom, geom) ┃ + ┡━━━━━━━━━━━━━━━━━━━━━━━┩ + │ boolean │ + ├───────────────────────┤ + │ True │ + │ True │ + │ True │ + │ True │ + │ True │ + │ True │ + │ True │ + │ True │ + │ True │ + │ True │ + │ … │ + └───────────────────────┘ """ return ops.GeoEquals(self, right).to_expr() @@ -234,6 +528,30 @@ def geometry_type(self) -> ir.StringValue: ------- StringValue String representing the type of `self`. + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> t = ibis.examples.zones.fetch() + >>> t.geom.geometry_type() + ┏━━━━━━━━━━━━━━━━━━━━━━━┓ + ┃ GeoGeometryType(geom) ┃ + ┡━━━━━━━━━━━━━━━━━━━━━━━┩ + │ string │ + ├───────────────────────┤ + │ POLYGON │ + │ MULTIPOLYGON │ + │ POLYGON │ + │ POLYGON │ + │ POLYGON │ + │ POLYGON │ + │ POLYGON │ + │ POLYGON │ + │ POLYGON │ + │ POLYGON │ + │ … │ + └───────────────────────┘ """ return ops.GeoGeometryType(self).to_expr() @@ -249,6 +567,33 @@ def intersects(self, right: GeoSpatialValue) -> ir.BooleanValue: ------- BooleanValue Whether `self` intersects `right` + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> import shapely + >>> t = ibis.examples.zones.fetch() + >>> p = shapely.Point(935996.821, 191376.75) # zone 1 centroid + >>> plit = ibis.literal(p, "geometry") + >>> t.geom.intersects(plit).name("intersects") + ┏━━━━━━━━━━━━┓ + ┃ intersects ┃ + ┡━━━━━━━━━━━━┩ + │ boolean │ + ├────────────┤ + │ True │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ … │ + └────────────┘ """ return ops.GeoIntersects(self, right).to_expr() @@ -259,6 +604,30 @@ def is_valid(self) -> ir.BooleanValue: ------- BooleanValue Whether `self` is valid + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> t = ibis.examples.zones.fetch() + >>> t.geom.is_valid() + ┏━━━━━━━━━━━━━━━━━━┓ + ┃ GeoIsValid(geom) ┃ + ┡━━━━━━━━━━━━━━━━━━┩ + │ boolean │ + ├──────────────────┤ + │ True │ + │ True │ + │ True │ + │ True │ + │ True │ + │ True │ + │ True │ + │ True │ + │ True │ + │ True │ + │ … │ + └──────────────────┘ """ return ops.GeoIsValid(self).to_expr() @@ -292,6 +661,36 @@ def overlaps(self, right: GeoSpatialValue) -> ir.BooleanValue: ------- BooleanValue Overlaps indicator + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> import shapely + >>> t = ibis.examples.zones.fetch() + + Polygon center in an edge point of zone 1 + + >>> p_edge_buffer = shapely.Point(933100.918, 192536.086).buffer(100) + >>> buff_lit = ibis.literal(p_edge_buffer, "geometry") + >>> t.geom.overlaps(buff_lit).name("overlaps") + ┏━━━━━━━━━━┓ + ┃ overlaps ┃ + ┡━━━━━━━━━━┩ + │ boolean │ + ├──────────┤ + │ True │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ … │ + └──────────┘ """ return ops.GeoOverlaps(self, right).to_expr() @@ -307,6 +706,36 @@ def touches(self, right: GeoSpatialValue) -> ir.BooleanValue: ------- BooleanValue Whether self and right are touching + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> import shapely + >>> t = ibis.examples.zones.fetch() + + Edge point of zone 1 + + >>> p_edge = shapely.Point(933100.9183527103, 192536.08569720192) + >>> p_edge_lit = ibis.literal(p_edge, "geometry") + >>> t.geom.touches(p_edge_lit).name("touches") + ┏━━━━━━━━━┓ + ┃ touches ┃ + ┡━━━━━━━━━┩ + │ boolean │ + ├─────────┤ + │ True │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ False │ + │ … │ + └─────────┘ """ return ops.GeoTouches(self, right).to_expr() @@ -322,16 +751,77 @@ def distance(self, right: GeoSpatialValue) -> ir.FloatingValue: ------- FloatingValue Distance between `self` and `right` + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> import shapely + >>> t = ibis.examples.zones.fetch() + + Penn station zone centroid + + >>> penn_station = shapely.Point(986345.399, 211974.446) + >>> penn_lit = ibis.literal(penn_station, "geometry") + >>> t.geom.distance(penn_lit).name("distance_penn") + ┏━━━━━━━━━━━━━━━┓ + ┃ distance_penn ┃ + ┡━━━━━━━━━━━━━━━┩ + │ float64 │ + ├───────────────┤ + │ 47224.139856 │ + │ 55992.665470 │ + │ 54850.880098 │ + │ 8011.846870 │ + │ 84371.995209 │ + │ 54196.904809 │ + │ 15965.509896 │ + │ 20566.442476 │ + │ 54070.543584 │ + │ 56994.826531 │ + │ … │ + └───────────────┘ """ return ops.GeoDistance(self, right).to_expr() def length(self) -> ir.FloatingValue: """Compute the length of a geospatial expression. + Returns zero for polygons. + Returns ------- FloatingValue Length of `self` + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> import shapely + >>> line = shapely.LineString([[0, 0], [1, 0], [1, 1]]) + >>> line_lit = ibis.literal(line, type="geometry") + >>> line_lit.length() + 2.0 + >>> t = ibis.examples.zones.fetch() + >>> t.geom.length() + ┏━━━━━━━━━━━━━━━━━┓ + ┃ GeoLength(geom) ┃ + ┡━━━━━━━━━━━━━━━━━┩ + │ float64 │ + ├─────────────────┤ + │ 0.0 │ + │ 0.0 │ + │ 0.0 │ + │ 0.0 │ + │ 0.0 │ + │ 0.0 │ + │ 0.0 │ + │ 0.0 │ + │ 0.0 │ + │ 0.0 │ + │ … │ + └─────────────────┘ """ return ops.GeoLength(self).to_expr() @@ -368,7 +858,6 @@ def union(self, right: GeoSpatialValue) -> GeoSpatialValue: """Merge two geometries into a union geometry. Returns the pointwise union of the two geometries. - This corresponds to the non-aggregate version the PostGIS ST_Union. Parameters ---------- @@ -379,6 +868,36 @@ def union(self, right: GeoSpatialValue) -> GeoSpatialValue: ------- GeoSpatialValue Union of geometries + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> import shapely + >>> t = ibis.examples.zones.fetch() + + Penn station zone centroid + + >>> penn_station = shapely.Point(986345.399, 211974.446) + >>> penn_lit = ibis.literal(penn_station, "geometry") + >>> t.geom.centroid().union(penn_lit).name("union_centroid_penn") + ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + ┃ union_centroid_penn ┃ + ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩ + │ geospatial:geometry │ + ├──────────────────────────────────────────────────────────────┤ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ … │ + └──────────────────────────────────────────────────────────────┘ """ return ops.GeoUnion(self, right).to_expr() @@ -391,6 +910,30 @@ def x(self) -> ir.FloatingValue: ------- FloatingValue X coordinate of `self` + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> t = ibis.examples.zones.fetch() + >>> t.geom.centroid().x() + ┏━━━━━━━━━━━━━━━━━━━━━━━━━┓ + ┃ GeoX(GeoCentroid(geom)) ┃ + ┡━━━━━━━━━━━━━━━━━━━━━━━━━┩ + │ float64 │ + ├─────────────────────────┤ + │ 9.359968e+05 │ + │ 1.031086e+06 │ + │ 1.026453e+06 │ + │ 9.906340e+05 │ + │ 9.318714e+05 │ + │ 9.643197e+05 │ + │ 1.006497e+06 │ + │ 1.005552e+06 │ + │ 1.043003e+06 │ + │ 1.042224e+06 │ + │ … │ + └─────────────────────────┘ """ return ops.GeoX(self).to_expr() @@ -403,6 +946,30 @@ def y(self) -> ir.FloatingValue: ------- FloatingValue Y coordinate of `self` + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> t = ibis.examples.zones.fetch() + >>> t.geom.centroid().y() + ┏━━━━━━━━━━━━━━━━━━━━━━━━━┓ + ┃ GeoY(GeoCentroid(geom)) ┃ + ┡━━━━━━━━━━━━━━━━━━━━━━━━━┩ + │ float64 │ + ├─────────────────────────┤ + │ 191376.749531 │ + │ 164018.754403 │ + │ 254265.478659 │ + │ 202959.782391 │ + │ 140681.351376 │ + │ 157998.935612 │ + │ 216719.218169 │ + │ 222936.087552 │ + │ 212969.849014 │ + │ 186706.496469 │ + │ … │ + └─────────────────────────┘ """ return ops.GeoY(self).to_expr() @@ -455,6 +1022,16 @@ def start_point(self) -> PointValue: ------- PointValue Start point + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> import shapely + >>> line = shapely.LineString([[0, 0], [1, 0], [1, 1]]) + >>> line_lit = ibis.literal(line, type="geometry") + >>> line_lit.start_point() + """ return ops.GeoStartPoint(self).to_expr() @@ -467,6 +1044,16 @@ def end_point(self) -> PointValue: ------- PointValue End point + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> import shapely + >>> line = shapely.LineString([[0, 0], [1, 0], [1, 1]]) + >>> line_lit = ibis.literal(line, type="geometry") + >>> line_lit.end_point() + """ return ops.GeoEndPoint(self).to_expr() @@ -496,6 +1083,30 @@ def n_points(self) -> ir.IntegerValue: ------- IntegerValue Number of points + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> t = ibis.examples.zones.fetch() + >>> t.geom.n_points() + ┏━━━━━━━━━━━━━━━━━━┓ + ┃ GeoNPoints(geom) ┃ + ┡━━━━━━━━━━━━━━━━━━┩ + │ int64 │ + ├──────────────────┤ + │ 232 │ + │ 2954 │ + │ 121 │ + │ 88 │ + │ 170 │ + │ 277 │ + │ 182 │ + │ 40 │ + │ 189 │ + │ 157 │ + │ … │ + └──────────────────┘ """ return ops.GeoNPoints(self).to_expr() @@ -550,6 +1161,41 @@ def buffer(self, radius: float | ir.FloatingValue) -> GeoSpatialValue: ------- GeoSpatialValue Geometry expression + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> t = ibis.examples.zones.fetch() + >>> p = t.x_cent.point(t.y_cent) + >>> p.buffer(10) # note buff.area.mean() ~ pi * r^2 + ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + ┃ GeoBuffer(GeoPoint(x_cent, y_cent), 10.0) ┃ + ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩ + │ geospatial:geometry │ + ├──────────────────────────────────────────────────────────────────────────────┤ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ … │ + └──────────────────────────────────────────────────────────────────────────────┘ """ return ops.GeoBuffer(self, radius=radius).to_expr() @@ -560,6 +1206,30 @@ def centroid(self) -> PointValue: ------- PointValue The centroid + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> t = ibis.examples.zones.fetch() + >>> t.geom.centroid() + ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + ┃ GeoCentroid(geom) ┃ + ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩ + │ point │ + ├──────────────────────────────────┤ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ … │ + └──────────────────────────────────┘ """ return ops.GeoCentroid(self).to_expr() @@ -570,6 +1240,40 @@ def envelope(self) -> ir.PolygonValue: ------- PolygonValue A polygon + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> t = ibis.examples.zones.fetch() + >>> t.geom.envelope() + ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + ┃ GeoEnvelope(geom) ┃ + ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩ + │ polygon │ + ├──────────────────────────────────────────────────────────────────────────────┤ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ … │ + └──────────────────────────────────────────────────────────────────────────────┘ """ return ops.GeoEnvelope(self).to_expr() @@ -585,6 +1289,27 @@ def within(self, right: GeoSpatialValue) -> ir.BooleanValue: ------- BooleanValue Whether `self` is in `right`. + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> import shapely + >>> t = ibis.examples.zones.fetch() + >>> penn_station_buff = shapely.Point(986345.399, 211974.446).buffer(5000) + >>> penn_lit = ibis.literal(penn_station_buff, "geometry") + >>> t.filter(t.geom.within(penn_lit))["zone"] + ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + ┃ zone ┃ + ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩ + │ string │ + ├──────────────────────────────┤ + │ East Chelsea │ + │ Flatiron │ + │ Garment District │ + │ Midtown South │ + │ Penn Station/Madison Sq West │ + └──────────────────────────────┘ """ return ops.GeoWithin(self, right).to_expr() @@ -617,6 +1342,30 @@ def intersection(self, right: GeoSpatialValue) -> GeoSpatialValue: ------- GeoSpatialValue Intersection of `self` and `right` + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> t = ibis.examples.zones.fetch() + >>> t.geom.intersection(t.geom.centroid()) + ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + ┃ GeoIntersection(geom, GeoCentroid(geom)) ┃ + ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩ + │ geospatial:geometry │ + ├──────────────────────────────────────────┤ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ … │ + └──────────────────────────────────────────┘ """ return ops.GeoIntersection(self, right).to_expr() @@ -632,6 +1381,40 @@ def difference(self, right: GeoSpatialValue) -> GeoSpatialValue: ------- GeoSpatialValue Difference of `self` and `right` + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> t = ibis.examples.zones.fetch() + >>> t.geom.difference(t.geom.centroid()) + ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + ┃ GeoDifference(geom, GeoCentroid(geom)) ┃ + ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩ + │ geospatial:geometry │ + ├──────────────────────────────────────────────────────────────────────────────┤ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ … │ + └──────────────────────────────────────────────────────────────────────────────┘ """ return ops.GeoDifference(self, right).to_expr() @@ -693,6 +1476,42 @@ def convert( -------- [`flip_coordinates`](#ibis.expr.types.geospatial.GeoSpatialValue.flip_coordinates) + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> t = ibis.examples.zones.fetch() + + Data is originally in epsg:2263 + + >>> t.geom.convert("EPSG:2263", "EPSG:4326") + ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + ┃ GeoConvert(geom) ┃ + ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩ + │ geospatial:geometry │ + ├──────────────────────────────────────────────────────────────────────────────┤ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ … │ + └──────────────────────────────────────────────────────────────────────────────┘ """ return ops.GeoConvert(self, source, target).to_expr() @@ -761,6 +1580,31 @@ def flip_coordinates(self) -> GeoSpatialValue: ------- GeoSpatialValue New geometry with flipped coordinates + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> t = ibis.examples.zones.fetch() + >>> t.geom.centroid().flip_coordinates() + ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + ┃ GeoFlipCoordinates(GeoCentroid(geom)) ┃ + ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩ + │ geospatial:geometry │ + ├───────────────────────────────────────┤ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ … │ + └───────────────────────────────────────┘ + """ return ops.GeoFlipCoordinates(self).to_expr() @@ -775,7 +1619,7 @@ class GeoSpatialColumn(NumericColumn, GeoSpatialValue): def unary_union(self) -> ir.GeoSpatialScalar: """Aggregate a set of geometries into a union. - This corresponds to the aggregate version of the PostGIS ST_Union. + This corresponds to the aggregate version of the union. We give it a different name (following the corresponding method in GeoPandas) to avoid name conflicts with the non-aggregate version. @@ -783,6 +1627,14 @@ def unary_union(self) -> ir.GeoSpatialScalar: ------- GeoSpatialScalar Union of geometries + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> t = ibis.examples.zones.fetch() + >>> t.geom.unary_union() + """ return ops.GeoUnaryUnion(self).to_expr().name("union") diff --git a/ibis/expr/types/numeric.py b/ibis/expr/types/numeric.py index 0cd7764e99e4..08d507546c59 100644 --- a/ibis/expr/types/numeric.py +++ b/ibis/expr/types/numeric.py @@ -744,6 +744,30 @@ def point(self, right: int | float | NumericValue) -> ir.PointValue: ------- PointValue Points + + Examples + -------- + >>> import ibis + >>> ibis.options.interactive = True + >>> t = ibis.examples.zones.fetch() + >>> t.x_cent.point(t.y_cent) + ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + ┃ GeoPoint(x_cent, y_cent) ┃ + ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩ + │ point │ + ├──────────────────────────────────┤ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ │ + │ … │ + └──────────────────────────────────┘ """ return ops.GeoPoint(self, right).to_expr()