Skip to content

Commit 6a7ab60

Browse files
SNOW-2363827: Add support for scalar Geospatial functions - part 4 (#3807)
1 parent 4c9e7b2 commit 6a7ab60

File tree

3 files changed

+311
-0
lines changed

3 files changed

+311
-0
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,24 @@
4848
- `h3_polygon_to_cells`
4949
- `h3_polygon_to_cells_strings`
5050
- `h3_string_to_int`
51+
- `h3_try_grid_path`
52+
- `h3_try_polygon_to_cells`
5153
- `h3_try_polygon_to_cells_strings`
54+
- `h3_uncompact_cells`
55+
- `h3_uncompact_cells_strings`
56+
- `haversine`
5257
- `hex_decode_binary`
5358
- `last_query_id`
5459
- `last_transaction`
5560
- `least_ignore_nulls`
5661
- `nullif`
5762
- `nvl2`
5863
- `regr_valx`
64+
- `st_area`
65+
- `st_asewkb`
66+
- `st_asewkt`
67+
- `st_asgeojson`
68+
- `st_aswkb`
5969

6070

6171
### Snowpark pandas API Updates

docs/source/snowpark/functions.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ Functions
238238
grouping
239239
grouping_id
240240
hash
241+
haversine
241242
hex
242243
hex_decode_binary
243244
hex_encode
@@ -259,6 +260,10 @@ Functions
259260
h3_cell_to_children
260261
h3_cell_to_children_string
261262
h3_int_to_string
263+
h3_try_grid_path
264+
h3_try_polygon_to_cells
265+
h3_uncompact_cells
266+
h3_uncompact_cells_strings
262267
iff
263268
ifnull
264269
in_
@@ -416,6 +421,11 @@ Functions
416421
strip_null_value
417422
strtok_to_array
418423
struct
424+
st_area
425+
st_asewkb
426+
st_asewkt
427+
st_asgeojson
428+
st_aswkb
419429
substr
420430
substring
421431
sum

src/snowflake/snowpark/_functions/scalar_functions.py

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1495,3 +1495,294 @@ def h3_int_to_string(cell_id: ColumnOrName, _emit_ast: bool = True) -> Column:
14951495
"""
14961496
c = _to_col_if_str(cell_id, "h3_int_to_string")
14971497
return builtin("h3_int_to_string", _emit_ast=_emit_ast)(c)
1498+
1499+
1500+
@publicapi
1501+
def h3_try_grid_path(
1502+
cell_id_1: ColumnOrName, cell_id_2: ColumnOrName, _emit_ast: bool = True
1503+
) -> Column:
1504+
"""
1505+
Returns the grid path between two H3 cell IDs. Returns None if no path exists
1506+
or if the input cell IDs are invalid.
1507+
1508+
Args:
1509+
cell_id_1 (ColumnOrName): The first H3 cell ID.
1510+
cell_id_2 (ColumnOrName): The second H3 cell ID.
1511+
1512+
Returns:
1513+
Column: An array of H3 cell IDs representing the grid path, or None if no path exists or if inputs are invalid.
1514+
1515+
Example::
1516+
1517+
>>> df = session.create_dataframe([['813d7ffffffffff', '81343ffffffffff']], schema=["cell1", "cell2"])
1518+
>>> df.select(h3_try_grid_path(df["cell1"], df["cell2"]).alias("grid_path")).collect()
1519+
[Row(GRID_PATH=None)]
1520+
"""
1521+
c1 = _to_col_if_str(cell_id_1, "h3_try_grid_path")
1522+
c2 = _to_col_if_str(cell_id_2, "h3_try_grid_path")
1523+
return builtin("h3_try_grid_path", _emit_ast=_emit_ast)(c1, c2)
1524+
1525+
1526+
@publicapi
1527+
def h3_try_polygon_to_cells(
1528+
geography_polygon: ColumnOrName,
1529+
target_resolution: ColumnOrName,
1530+
_emit_ast: bool = True,
1531+
) -> Column:
1532+
"""
1533+
Returns an array of H3 cell IDs that cover the given geography polygon at the specified resolution.
1534+
This is a "try" version that returns None instead of raising an error if the conversion fails.
1535+
1536+
Args:
1537+
geography_polygon (ColumnOrName): The GEOGRAPHY polygon object.
1538+
target_resolution (ColumnOrName): The H3 resolution level (0-15).
1539+
1540+
Returns:
1541+
Column: An array of H3 cell IDs as BIGINT values, or None if conversion fails.
1542+
1543+
Example::
1544+
1545+
>>> from snowflake.snowpark.functions import to_geography, lit
1546+
>>> df = session.create_dataframe([
1547+
... ['POLYGON((-122.4 37.8, -122.4 37.7, -122.3 37.7, -122.3 37.8, -122.4 37.8))']
1548+
... ], schema=['polygon_wkt'])
1549+
>>> df.select(
1550+
... h3_try_polygon_to_cells(to_geography(df['polygon_wkt']), lit(5))
1551+
... ).collect()
1552+
[Row(H3_TRY_POLYGON_TO_CELLS(TO_GEOGRAPHY("POLYGON_WKT"), 5)='[\\n 599685771850416127\\n]')]
1553+
"""
1554+
geography_polygon_c = _to_col_if_str(geography_polygon, "h3_try_polygon_to_cells")
1555+
target_resolution_c = _to_col_if_str(target_resolution, "h3_try_polygon_to_cells")
1556+
return builtin("h3_try_polygon_to_cells", _emit_ast=_emit_ast)(
1557+
geography_polygon_c, target_resolution_c
1558+
)
1559+
1560+
1561+
@publicapi
1562+
def h3_uncompact_cells(
1563+
array_of_cell_ids: ColumnOrName,
1564+
target_resolution: ColumnOrName,
1565+
_emit_ast: bool = True,
1566+
) -> Column:
1567+
"""
1568+
Returns an array of H3 cell IDs at the specified target resolution that are contained within the input array of H3 cell IDs.
1569+
This function performs the opposite operation of H3_COMPACT_CELLS by expanding compacted cells to their constituent cells at a finer resolution.
1570+
1571+
Args:
1572+
array_of_cell_ids (ColumnOrName): Column containing an array of H3 cell IDs to uncompact
1573+
target_resolution (ColumnOrName): Column containing the target H3 resolution level for the uncompacted cells
1574+
1575+
Returns:
1576+
Column: An array of H3 cell IDs at the target resolution
1577+
1578+
Examples::
1579+
>>> from snowflake.snowpark.functions import lit
1580+
>>> df = session.create_dataframe([[[622236750558396415, 617733150935089151]]], schema=["cell_ids"])
1581+
>>> df.select(h3_uncompact_cells(df["cell_ids"], lit(10)).alias("uncompacted")).collect()
1582+
[Row(UNCOMPACTED='[\\n 622236750558396415,\\n 622236750562230271,\\n 622236750562263039,\\n 622236750562295807,\\n 622236750562328575,\\n 622236750562361343,\\n 622236750562394111,\\n 622236750562426879\\n]')]
1583+
"""
1584+
array_col = _to_col_if_str(array_of_cell_ids, "h3_uncompact_cells")
1585+
resolution_col = _to_col_if_str(target_resolution, "h3_uncompact_cells")
1586+
return builtin("h3_uncompact_cells", _emit_ast=_emit_ast)(array_col, resolution_col)
1587+
1588+
1589+
@publicapi
1590+
def h3_uncompact_cells_strings(
1591+
array_of_cell_ids: ColumnOrName,
1592+
target_resolution: ColumnOrName,
1593+
_emit_ast: bool = True,
1594+
) -> Column:
1595+
"""
1596+
Returns an array of H3 cell IDs at the specified target resolution that are contained within
1597+
the input array of H3 cell IDs. This function uncompacts H3 cells to a finer resolution.
1598+
1599+
Args:
1600+
array_of_cell_ids (ColumnOrName): An array of H3 cell ID strings.
1601+
target_resolution (ColumnOrName): The target H3 resolution (0-15).
1602+
1603+
Returns:
1604+
Column: An array of H3 cell ID strings at the target resolution.
1605+
1606+
Example::
1607+
1608+
>>> from snowflake.snowpark.functions import array_construct, lit
1609+
>>> df = session.create_dataframe([
1610+
... [['8a2a1072339ffff', '892a1072377ffff']],
1611+
... [['8a2a1072339ffff']]
1612+
... ], schema=["cell_ids"])
1613+
>>> df.select(h3_uncompact_cells_strings(df["cell_ids"], lit(10)).alias("uncompacted")).collect()
1614+
[Row(UNCOMPACTED='[\\n "8a2a1072339ffff",\\n "8a2a10723747fff",\\n "8a2a1072374ffff",\\n "8a2a10723757fff",\\n "8a2a1072375ffff",\\n "8a2a10723767fff",\\n "8a2a1072376ffff",\\n "8a2a10723777fff"\\n]'), Row(UNCOMPACTED='[\\n "8a2a1072339ffff"\\n]')]
1615+
"""
1616+
array_col = _to_col_if_str(array_of_cell_ids, "h3_uncompact_cells_strings")
1617+
resolution_col = _to_col_if_str(target_resolution, "h3_uncompact_cells_strings")
1618+
return builtin("h3_uncompact_cells_strings", _emit_ast=_emit_ast)(
1619+
array_col, resolution_col
1620+
)
1621+
1622+
1623+
@publicapi
1624+
def haversine(
1625+
lat1: ColumnOrName,
1626+
lon1: ColumnOrName,
1627+
lat2: ColumnOrName,
1628+
lon2: ColumnOrName,
1629+
_emit_ast: bool = True,
1630+
) -> Column:
1631+
"""
1632+
Calculates the great circle distance in kilometers between two points on Earth
1633+
using the Haversine formula.
1634+
1635+
Args:
1636+
lat1 (ColumnOrName): The latitude of the first point in degrees.
1637+
lon1 (ColumnOrName): The longitude of the first point in degrees.
1638+
lat2 (ColumnOrName): The latitude of the second point in degrees.
1639+
lon2 (ColumnOrName): The longitude of the second point in degrees.
1640+
1641+
Returns:
1642+
Column: The distance in kilometers between the two points.
1643+
1644+
Example::
1645+
1646+
>>> df = session.create_dataframe([[40.7127, -74.0059, 34.0500, -118.2500]], schema=["lat1", "lon1", "lat2", "lon2"])
1647+
>>> df.select(haversine(df["lat1"], df["lon1"], df["lat2"], df["lon2"]).alias("distance")).collect()
1648+
[Row(DISTANCE=3936.3850963892937)]
1649+
"""
1650+
lat1_col = _to_col_if_str(lat1, "haversine")
1651+
lon1_col = _to_col_if_str(lon1, "haversine")
1652+
lat2_col = _to_col_if_str(lat2, "haversine")
1653+
lon2_col = _to_col_if_str(lon2, "haversine")
1654+
return builtin("haversine", _emit_ast=_emit_ast)(
1655+
lat1_col, lon1_col, lat2_col, lon2_col
1656+
)
1657+
1658+
1659+
@publicapi
1660+
def st_area(
1661+
geography_or_geometry_expression: ColumnOrName, _emit_ast: bool = True
1662+
) -> Column:
1663+
"""
1664+
Returns the area of a GEOGRAPHY or GEOMETRY object.
1665+
1666+
Args:
1667+
geography_or_geometry_expression (ColumnOrName): A GEOGRAPHY or GEOMETRY object
1668+
1669+
Returns:
1670+
Column: The area of the input geography or geometry object
1671+
1672+
Examples:
1673+
>>> from snowflake.snowpark.functions import to_geometry
1674+
>>> df = session.create_dataframe([
1675+
... ['POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))'],
1676+
... ['POINT(1 1)'],
1677+
... ['LINESTRING(0 0, 1 1)']
1678+
... ], schema=["geom"])
1679+
>>> df.select(st_area(to_geometry(df["geom"])).alias("area")).collect()
1680+
[Row(AREA=1.0), Row(AREA=0.0), Row(AREA=0.0)]
1681+
"""
1682+
c = _to_col_if_str(geography_or_geometry_expression, "st_area")
1683+
return builtin("st_area", _emit_ast=_emit_ast)(c)
1684+
1685+
1686+
@publicapi
1687+
def st_asewkb(
1688+
geography_or_geometry_expression: ColumnOrName, _emit_ast: bool = True
1689+
) -> Column:
1690+
"""
1691+
Returns the Extended Well-Known Binary (EWKB) representation of a GEOGRAPHY or GEOMETRY object.
1692+
1693+
Args:
1694+
geography_or_geometry_expression (ColumnOrName): The GEOGRAPHY or GEOMETRY objects to convert to EWKB format
1695+
1696+
Returns:
1697+
Column: The EWKB representation as binary data
1698+
1699+
Examples:
1700+
>>> from snowflake.snowpark.functions import to_geography, col
1701+
>>> from snowflake.snowpark import Row
1702+
>>> df = session.create_dataframe([
1703+
... ['POINT(-122.35 37.55)'],
1704+
... ['LINESTRING(-124.20 42.00, -120.01 41.99)']
1705+
... ], schema=["g"])
1706+
>>> df.select(st_asewkb(to_geography(col("g"))).alias("ewkb")).collect()
1707+
[Row(EWKB=bytearray(b'\\x01\\x01\\x00\\x00 \\xe6\\x10\\x00\\x00fffff\\x96^\\xc0fffff\\xc6B@')), Row(EWKB=bytearray(b'\\x01\\x02\\x00\\x00 \\xe6\\x10\\x00\\x00\\x02\\x00\\x00\\x00\\xcd\\xcc\\xcc\\xcc\\xcc\\x0c_\\xc0\\x00\\x00\\x00\\x00\\x00\\x00E@q=\\n\\xd7\\xa3\\x00^\\xc0\\x1f\\x85\\xebQ\\xb8\\xfeD@'))]
1708+
"""
1709+
c = _to_col_if_str(geography_or_geometry_expression, "st_asewkb")
1710+
return builtin("st_asewkb", _emit_ast=_emit_ast)(c)
1711+
1712+
1713+
@publicapi
1714+
def st_asewkt(
1715+
geography_or_geometry_expression: ColumnOrName, _emit_ast: bool = True
1716+
) -> Column:
1717+
"""
1718+
Returns the Extended Well-Known Text (EWKT) representation of a GEOGRAPHY or GEOMETRY object.
1719+
1720+
Args:
1721+
geography_or_geometry_expression (ColumnOrName): A GEOGRAPHY or GEOMETRY objects to convert to EWKT format
1722+
1723+
Returns:
1724+
Column: The EWKT representation as a string
1725+
1726+
Examples:
1727+
>>> from snowflake.snowpark.functions import to_geography
1728+
>>> from snowflake.snowpark import Row
1729+
>>> df = session.create_dataframe([
1730+
... ['POINT(-122.35 37.55)'],
1731+
... ['LINESTRING(-124.20 42.00, -120.01 41.99)']
1732+
... ], schema=["g"])
1733+
>>> result = df.select(st_asewkt(to_geography(df["g"])).alias("result")).collect()
1734+
>>> assert result == [Row(RESULT='SRID=4326;POINT(-122.35 37.55)'), Row(RESULT='SRID=4326;LINESTRING(-124.2 42,-120.01 41.99)')]
1735+
"""
1736+
c = _to_col_if_str(geography_or_geometry_expression, "st_asewkt")
1737+
return builtin("st_asewkt", _emit_ast=_emit_ast)(c)
1738+
1739+
1740+
@publicapi
1741+
def st_asgeojson(
1742+
geography_or_geometry_expression: ColumnOrName, _emit_ast: bool = True
1743+
) -> Column:
1744+
"""
1745+
Returns the GeoJSON representation of a GEOGRAPHY or GEOMETRY object.
1746+
1747+
Args:
1748+
geography_or_geometry_expression (ColumnOrName): The GEOGRAPHY or GEOMETRY data.
1749+
1750+
Returns:
1751+
Column: The GeoJSON representation as a string.
1752+
1753+
Example::
1754+
1755+
>>> from snowflake.snowpark.functions import col, to_geography
1756+
>>> df = session.create_dataframe(['POINT(-122.35 37.55)', 'LINESTRING(-124.20 42.00, -120.01 41.99)'], schema=['g'])
1757+
>>> df = df.select(to_geography(col("g")).alias("geography"))
1758+
>>> df.select(st_asgeojson(col("geography"))).collect()
1759+
[Row(ST_ASGEOJSON("GEOGRAPHY")='{\\n "coordinates": [\\n -122.35,\\n 37.55\\n ],\\n "type": "Point"\\n}'), Row(ST_ASGEOJSON("GEOGRAPHY")='{\\n "coordinates": [\\n [\\n -124.2,\\n 42\\n ],\\n [\\n -120.01,\\n 41.99\\n ]\\n ],\\n "type": "LineString"\\n}')]
1760+
"""
1761+
c = _to_col_if_str(geography_or_geometry_expression, "st_asgeojson")
1762+
return builtin("st_asgeojson", _emit_ast=_emit_ast)(c)
1763+
1764+
1765+
@publicapi
1766+
def st_aswkb(
1767+
geography_or_geometry_expression: ColumnOrName, _emit_ast: bool = True
1768+
) -> Column:
1769+
"""
1770+
Returns the Well-Known Binary (WKB) representation of a GEOGRAPHY or GEOMETRY object.
1771+
1772+
Args:
1773+
geography_or_geometry_expression (ColumnOrName): The GEOGRAPHY or GEOMETRY object.
1774+
1775+
Returns:
1776+
Column: The WKB representation as binary data.
1777+
1778+
Examples::
1779+
>>> from snowflake.snowpark.functions import col, to_geography
1780+
>>> df = session.create_dataframe([
1781+
... 'POINT(-122.35 37.55)',
1782+
... 'LINESTRING(-124.20 42.00, -120.01 41.99)'
1783+
... ], schema=["g"])
1784+
>>> df.select(st_aswkb(to_geography(col("g")))).collect()
1785+
[Row(ST_ASWKB(TO_GEOGRAPHY("G"))=bytearray(b'\\x01\\x01\\x00\\x00\\x00fffff\\x96^\\xc0fffff\\xc6B@')), Row(ST_ASWKB(TO_GEOGRAPHY("G"))=bytearray(b'\\x01\\x02\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\xcd\\xcc\\xcc\\xcc\\xcc\\x0c_\\xc0\\x00\\x00\\x00\\x00\\x00\\x00E@q=\\n\\xd7\\xa3\\x00^\\xc0\\x1f\\x85\\xebQ\\xb8\\xfeD@'))]
1786+
"""
1787+
c = _to_col_if_str(geography_or_geometry_expression, "st_aswkb")
1788+
return builtin("st_aswkb", _emit_ast=_emit_ast)(c)

0 commit comments

Comments
 (0)