Skip to content

Commit d86c6af

Browse files
arwas11shobsi
authored andcommitted
feat: add GeoSeries.intersection() and bigframes.bigquery.st_intersection() (#1529)
* feat: add GeoSeries.intersection() and bigframes.bigquery.st_intersection() * fix docstrings format * fix docstring output error * fix docstrings code examples * fix st_intersection
1 parent 927324e commit d86c6af

File tree

10 files changed

+596
-81
lines changed

10 files changed

+596
-81
lines changed

bigframes/bigquery/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
unix_millis,
2828
unix_seconds,
2929
)
30-
from bigframes.bigquery._operations.geo import st_area, st_difference
30+
from bigframes.bigquery._operations.geo import st_area, st_difference, st_intersection
3131
from bigframes.bigquery._operations.json import (
3232
json_extract,
3333
json_extract_array,
@@ -49,6 +49,7 @@
4949
# geo ops
5050
"st_area",
5151
"st_difference",
52+
"st_intersection",
5253
# json ops
5354
"json_set",
5455
"json_extract",

bigframes/bigquery/_operations/geo.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,3 +211,95 @@ def st_difference(
211211
in other.
212212
"""
213213
return series._apply_binary_op(other, ops.geo_st_difference_op)
214+
215+
216+
def st_intersection(
217+
series: bigframes.series.Series, other: bigframes.series.Series
218+
) -> bigframes.series.Series:
219+
"""
220+
Returns a `GEOGRAPHY` that represents the point set intersection of the two
221+
input `GEOGRAPHYs`. Thus, every point in the intersection appears in both
222+
`geography_1` and `geography_2`.
223+
224+
.. note::
225+
BigQuery's Geography functions, like `st_intersection`, interpret the geometry
226+
data type as a point set on the Earth's surface. A point set is a set
227+
of points, lines, and polygons on the WGS84 reference spheroid, with
228+
geodesic edges. See: https://cloud.google.com/bigquery/docs/geospatial-data
229+
230+
**Examples:**
231+
232+
>>> import bigframes as bpd
233+
>>> import bigframes.bigquery as bbq
234+
>>> import bigframes.geopandas
235+
>>> from shapely.geometry import Polygon, LineString, Point
236+
>>> bpd.options.display.progress_bar = None
237+
238+
We can check two GeoSeries against each other, row by row.
239+
240+
>>> s1 = bigframes.geopandas.GeoSeries(
241+
... [
242+
... Polygon([(0, 0), (2, 2), (0, 2)]),
243+
... Polygon([(0, 0), (2, 2), (0, 2)]),
244+
... LineString([(0, 0), (2, 2)]),
245+
... LineString([(2, 0), (0, 2)]),
246+
... Point(0, 1),
247+
... ],
248+
... )
249+
>>> s2 = bigframes.geopandas.GeoSeries(
250+
... [
251+
... Polygon([(0, 0), (1, 1), (0, 1)]),
252+
... LineString([(1, 0), (1, 3)]),
253+
... LineString([(2, 0), (0, 2)]),
254+
... Point(1, 1),
255+
... Point(0, 1),
256+
... ],
257+
... index=range(1, 6),
258+
... )
259+
260+
>>> s1
261+
0 POLYGON ((0 0, 2 2, 0 2, 0 0))
262+
1 POLYGON ((0 0, 2 2, 0 2, 0 0))
263+
2 LINESTRING (0 0, 2 2)
264+
3 LINESTRING (2 0, 0 2)
265+
4 POINT (0 1)
266+
dtype: geometry
267+
268+
>>> s2
269+
1 POLYGON ((0 0, 1 1, 0 1, 0 0))
270+
2 LINESTRING (1 0, 1 3)
271+
3 LINESTRING (2 0, 0 2)
272+
4 POINT (1 1)
273+
5 POINT (0 1)
274+
dtype: geometry
275+
276+
>>> bbq.st_intersection(s1, s2)
277+
0 None
278+
1 POLYGON ((0 0, 0.99954 1, 0 1, 0 0))
279+
2 POINT (1 1.00046)
280+
3 LINESTRING (2 0, 0 2)
281+
4 GEOMETRYCOLLECTION EMPTY
282+
5 None
283+
dtype: geometry
284+
285+
We can also do intersection of each geometry and a single shapely geometry:
286+
287+
>>> bbq.st_intersection(s1, bigframes.geopandas.GeoSeries([Polygon([(0, 0), (1, 1), (0, 1)])]))
288+
0 POLYGON ((0 0, 0.99954 1, 0 1, 0 0))
289+
1 None
290+
2 None
291+
3 None
292+
4 None
293+
dtype: geometry
294+
295+
Args:
296+
other (GeoSeries or geometric object):
297+
The Geoseries (elementwise) or geometric object to find the
298+
intersection with.
299+
300+
Returns:
301+
bigframes.geopandas.GeoSeries:
302+
The Geoseries (elementwise) of the intersection of points in
303+
each aligned geometry with other.
304+
"""
305+
return series._apply_binary_op(other, ops.geo_st_intersection_op)

bigframes/core/compile/scalar_op_compiler.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,6 +1037,13 @@ def geo_st_geogpoint_op_impl(x: ibis_types.Value, y: ibis_types.Value):
10371037
)
10381038

10391039

1040+
@scalar_op_compiler.register_binary_op(ops.geo_st_intersection_op, pass_op=False)
1041+
def geo_st_intersection_op_impl(x: ibis_types.Value, y: ibis_types.Value):
1042+
return typing.cast(ibis_types.GeoSpatialValue, x).intersection(
1043+
typing.cast(ibis_types.GeoSpatialValue, y)
1044+
)
1045+
1046+
10401047
@scalar_op_compiler.register_unary_op(ops.geo_x_op)
10411048
def geo_x_op_impl(x: ibis_types.Value):
10421049
return typing.cast(ibis_types.GeoSpatialValue, x).x()

bigframes/geopandas/geoseries.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,6 @@ def to_wkt(self: GeoSeries) -> bigframes.series.Series:
9696

9797
def difference(self: GeoSeries, other: GeoSeries) -> bigframes.series.Series: # type: ignore
9898
return self._apply_binary_op(other, ops.geo_st_difference_op)
99+
100+
def intersection(self: GeoSeries, other: GeoSeries) -> bigframes.series.Series: # type: ignore
101+
return self._apply_binary_op(other, ops.geo_st_intersection_op)

bigframes/operations/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
geo_st_difference_op,
9494
geo_st_geogfromtext_op,
9595
geo_st_geogpoint_op,
96+
geo_st_intersection_op,
9697
geo_x_op,
9798
geo_y_op,
9899
)
@@ -371,6 +372,7 @@
371372
"geo_st_astext_op",
372373
"geo_st_geogfromtext_op",
373374
"geo_st_geogpoint_op",
375+
"geo_st_intersection_op",
374376
"geo_x_op",
375377
"geo_y_op",
376378
# Numpy ops mapping

bigframes/operations/geo_ops.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,7 @@
6565
dtypes.is_geo_like, dtypes.FLOAT_DTYPE, description="geo-like"
6666
),
6767
)
68+
69+
geo_st_intersection_op = base_ops.create_binary_op(
70+
name="geo_st_intersection", type_signature=op_typing.BinaryGeo()
71+
)

0 commit comments

Comments
 (0)