Skip to content

Commit 5ec047f

Browse files
SNOW-2335536: Add support for Geospatial functions - part 2 (#3786)
1 parent d7e9c10 commit 5ec047f

3 files changed

Lines changed: 276 additions & 22 deletions

File tree

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,18 @@
1313
- `get_username_password`
1414
- `get_cloud_provider_token`
1515

16+
- Added support for the following scalar functions in `functions.py`:
17+
- `h3_cell_to_boundary`
18+
- `h3_cell_to_parent`
19+
- `h3_cell_to_point`
20+
- `h3_compact_cells`
21+
- `h3_compact_cells_strings`
22+
- `h3_coverage`
23+
- `h3_coverage_strings`
24+
- `h3_get_resolution`
25+
- `h3_grid_disk`
26+
- `h3_grid_distance`
27+
1628
### Snowpark pandas API Updates
1729

1830
#### New Features

docs/source/snowpark/functions.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,16 @@ Functions
229229
hex
230230
hex_encode
231231
hour
232+
h3_cell_to_boundary
233+
h3_cell_to_parent
234+
h3_cell_to_point
235+
h3_compact_cells
236+
h3_compact_cells_strings
237+
h3_coverage
238+
h3_coverage_strings
239+
h3_get_resolution
240+
h3_grid_disk
241+
h3_grid_distance
232242
iff
233243
ifnull
234244
in_

src/snowflake/snowpark/_functions/scalar_functions.py

Lines changed: 254 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
)
1414
from snowflake.snowpark._functions.general_functions import (
1515
builtin,
16+
lit,
1617
)
1718

1819

@@ -321,9 +322,6 @@ def getdate(_emit_ast: bool = True) -> Column:
321322
"""
322323
Returns the current timestamp for the system in the local time zone.
323324
324-
Args:
325-
_emit_ast (bool, optional): Whether to emit the abstract syntax tree (AST). Defaults to True.
326-
327325
Returns:
328326
A :class:`~snowflake.snowpark.Column` with the current date and time.
329327
@@ -342,9 +340,6 @@ def localtime(_emit_ast: bool = True) -> Column:
342340
"""
343341
Returns the current time for the system.
344342
345-
Args:
346-
_emit_ast (bool, optional): Whether to emit the abstract syntax tree (AST). Defaults to True.
347-
348343
Returns:
349344
A :class:`~snowflake.snowpark.Column` with the current local time.
350345
@@ -362,9 +357,6 @@ def systimestamp(_emit_ast: bool = True) -> Column:
362357
"""
363358
Returns the current timestamp for the system.
364359
365-
Args:
366-
_emit_ast (bool, optional): Whether to emit the abstract syntax tree (AST). Defaults to True.
367-
368360
Returns:
369361
A :class:`~snowflake.snowpark.Column` with the current system timestamp.
370362
@@ -383,9 +375,6 @@ def invoker_role(_emit_ast: bool = True) -> Column:
383375
"""
384376
Returns the name of the role that was active when the current stored procedure or user-defined function was called.
385377
386-
Args:
387-
_emit_ast (bool, optional): Whether to emit the abstract syntax tree (AST). Defaults to True.
388-
389378
Returns:
390379
Column: A Snowflake `Column` object representing the name of the active role.
391380
@@ -406,10 +395,6 @@ def invoker_share(_emit_ast: bool = True) -> Column:
406395
Returns the name of the share that directly accessed the table or view where the INVOKER_SHARE
407396
function is invoked, otherwise the function returns None.
408397
409-
Args:
410-
_emit_ast (bool, optional): A flag indicating whether to emit the abstract
411-
syntax tree (AST). Defaults to True.
412-
413398
Returns:
414399
Column: A Snowflake `Column` object representing the name of the active share.
415400
@@ -428,7 +413,6 @@ def is_application_role_in_session(role_name: str, _emit_ast: bool = True) -> Co
428413
429414
Args:
430415
role_name (str): The name of the application role to check.
431-
_emit_ast (bool, optional): Whether to emit the abstract syntax tree (AST). Defaults to True.
432416
433417
Returns:
434418
A :class:`~snowflake.snowpark.Column` indicating whether the specified application role is active in the current session.
@@ -452,7 +436,6 @@ def is_database_role_in_session(
452436
453437
Args:
454438
role_name (ColumnOrName): The name of the database role to check. Can be a string or a Column.
455-
_emit_ast (bool, optional): Whether to emit the abstract syntax tree (AST). Defaults to True.
456439
457440
Returns:
458441
Column: A Snowflake `Column` object representing the result of the check.
@@ -476,7 +459,6 @@ def is_granted_to_invoker_role(role_name: str, _emit_ast: bool = True) -> Column
476459
477460
Args:
478461
role_name (str): The name of the role to check.
479-
_emit_ast (bool, optional): Whether to emit the abstract syntax tree (AST). Defaults to True.
480462
481463
Returns:
482464
Column: A Snowflake `Column` object representing the result of the check.
@@ -498,7 +480,6 @@ def is_role_in_session(role: ColumnOrName, _emit_ast: bool = True) -> Column:
498480
499481
Args:
500482
role (ColumnOrName): A Column or column name containing the role name to check.
501-
_emit_ast (bool, optional): Whether to emit the abstract syntax tree (AST). Defaults to True.
502483
503484
Returns:
504485
Column: A Snowflake `Column` object representing the result of the check.
@@ -520,8 +501,6 @@ def getvariable(name: str, _emit_ast: bool = True) -> Column:
520501
521502
Args:
522503
name (str): The name of the session variable to retrieve.
523-
_emit_ast (bool, optional): A flag indicating whether to emit the abstract syntax tree (AST).
524-
Defaults to True.
525504
526505
Returns:
527506
Column: A Snowflake `Column` object representing the value of the specified session variable.
@@ -531,3 +510,256 @@ def getvariable(name: str, _emit_ast: bool = True) -> Column:
531510
>>> assert result[0]["RESULT"] is None
532511
"""
533512
return builtin("getvariable", _emit_ast=_emit_ast)(name)
513+
514+
515+
@publicapi
516+
def h3_cell_to_boundary(cell_id: ColumnOrName, _emit_ast: bool = True) -> Column:
517+
"""Returns the boundary of an H3 cell as a GeoJSON polygon.
518+
519+
Args:
520+
cell_id (ColumnOrName): The H3 cell IDs.
521+
522+
Returns:
523+
Column: The boundary of the H3 cell as a GeoJSON polygon string.
524+
525+
Example::
526+
>>> df = session.create_dataframe([613036919424548863, 577023702256844799], schema=["cell_id"])
527+
>>> result = df.select(h3_cell_to_boundary(df["cell_id"]).alias("boundary")).collect()
528+
>>> len(result) == 2
529+
True
530+
"""
531+
c = _to_col_if_str(cell_id, "h3_cell_to_boundary")
532+
return builtin("h3_cell_to_boundary", _emit_ast=_emit_ast)(c)
533+
534+
535+
@publicapi
536+
def h3_cell_to_parent(
537+
cell_id: ColumnOrName, target_resolution: ColumnOrName, _emit_ast: bool = True
538+
) -> Column:
539+
"""Returns the parent H3 cell at the specified target resolution.
540+
541+
Args:
542+
cell_id (ColumnOrName): The H3 cell IDs.
543+
target_resolution (ColumnOrName) : The target resolution levels.
544+
545+
Returns:
546+
Column: The parent H3 cell at the target resolution.
547+
548+
Example::
549+
>>> from snowflake.snowpark.functions import col
550+
>>> df = session.create_dataframe([[613036919424548863, 7], [608533319805566975, 6]], schema=["cell_id", "target_resolution"])
551+
>>> df.select(h3_cell_to_parent(col("cell_id"), col("target_resolution")).alias("parent_cell")).collect()
552+
[Row(PARENT_CELL=608533319805566975), Row(PARENT_CELL=604029720295636991)]
553+
"""
554+
cell_id_c = _to_col_if_str(cell_id, "h3_cell_to_parent")
555+
target_resolution_c = _to_col_if_str(target_resolution, "h3_cell_to_parent")
556+
return builtin("h3_cell_to_parent", _emit_ast=_emit_ast)(
557+
cell_id_c, target_resolution_c
558+
)
559+
560+
561+
@publicapi
562+
def h3_cell_to_point(cell_id: ColumnOrName, _emit_ast: bool = True) -> Column:
563+
"""
564+
Returns the center point of an H3 cell as a GeoJSON Point object.
565+
566+
Args:
567+
cell_id (ColumnOrName): The H3 cell IDs.
568+
569+
Returns:
570+
Column: GeoJSON Point objects representing the center points of the H3 cells.
571+
572+
Example::
573+
>>> import json
574+
>>> df = session.create_dataframe([613036919424548863], schema=["cell_id"])
575+
>>> result = df.select(h3_cell_to_point(df["cell_id"]).alias("POINT")).collect()
576+
>>> assert len(result) == 1
577+
>>> point_json = json.loads(result[0]["POINT"])
578+
>>> assert point_json["type"] == "Point"
579+
>>> assert "coordinates" in point_json
580+
>>> assert len(point_json["coordinates"]) == 2
581+
"""
582+
c = _to_col_if_str(cell_id, "h3_cell_to_point")
583+
return builtin("h3_cell_to_point", _emit_ast=_emit_ast)(c)
584+
585+
586+
@publicapi
587+
def h3_compact_cells(array_of_cell_ids: ColumnOrName, _emit_ast: bool = True) -> Column:
588+
"""
589+
Returns a compacted array of H3 cell IDs by merging cells at the same resolution into their parent cells when possible.
590+
591+
Args:
592+
array_of_cell_ids (ColumnOrName): An array of H3 cell IDs to be compacted.
593+
594+
Returns:
595+
Column: An array of compacted H3 cell IDs.
596+
597+
Example::
598+
>>> df = session.create_dataframe([
599+
... [[622236750562230271, 622236750562263039, 622236750562295807, 622236750562328575, 622236750562361343, 622236750562394111, 622236750562426879, 622236750558396415]]
600+
... ], schema=["cell_ids"])
601+
>>> df.select(h3_compact_cells(df["cell_ids"]).alias("compacted")).collect()
602+
[Row(COMPACTED='[\\n 622236750558396415,\\n 617733150935089151\\n]')]
603+
"""
604+
c = _to_col_if_str(array_of_cell_ids, "h3_compact_cells")
605+
return builtin("h3_compact_cells", _emit_ast=_emit_ast)(c)
606+
607+
608+
@publicapi
609+
def h3_compact_cells_strings(
610+
array_of_cell_ids: ColumnOrName, _emit_ast: bool = True
611+
) -> Column:
612+
"""
613+
Returns a compacted array of H3 cell IDs by removing redundant cells that are covered by their parent cells at coarser resolutions.
614+
615+
Args:
616+
array_of_cell_ids (ColumnOrName): An array of H3 cell ID strings to be compacted.
617+
618+
Returns:
619+
Column: The compacted array of H3 cell ID strings.
620+
621+
Example::
622+
623+
>>> df = session.create_dataframe([[
624+
... ['8a2a10705507fff', '8a2a1070550ffff', '8a2a10705517fff', '8a2a1070551ffff',
625+
... '8a2a10705527fff', '8a2a1070552ffff', '8a2a10705537fff', '8a2a10705cdffff']
626+
... ]], schema=["cell_ids"])
627+
>>> df.select(h3_compact_cells_strings("cell_ids").alias("compacted")).collect()
628+
[Row(COMPACTED='[\\n "8a2a10705cdffff",\\n "892a1070553ffff"\\n]')]
629+
"""
630+
c = _to_col_if_str(array_of_cell_ids, "h3_compact_cells_strings")
631+
return builtin("h3_compact_cells_strings", _emit_ast=_emit_ast)(c)
632+
633+
634+
@publicapi
635+
def h3_coverage(
636+
geography_expression: ColumnOrName,
637+
target_resolution: ColumnOrName,
638+
_emit_ast: bool = True,
639+
) -> Column:
640+
"""
641+
Returns an array of H3 cell IDs that cover the given geography at the specified resolution.
642+
643+
Args:
644+
geography_expression (ColumnOrName) : A GEOGRAPHY object.
645+
target_resolution (ColumnOrName) : The target H3 resolution (0-15).
646+
647+
Returns:
648+
Column: An array of H3 cell IDs as strings.
649+
650+
Example::
651+
>>> from snowflake.snowpark.functions import to_geography, lit
652+
>>> df = session.create_dataframe([
653+
... ["POLYGON((-122.481889 37.826683,-122.479487 37.808548,-122.474150 37.808904,-122.476510 37.826935,-122.481889 37.826683))"]
654+
... ], schema=["polygon_wkt"])
655+
>>> result = df.select(h3_coverage(to_geography(df["polygon_wkt"]), lit(8)).alias("h3_cells")).collect()
656+
>>> result
657+
[Row(H3_CELLS='[\\n 613196571539931135,\\n 613196571542028287,\\n 613196571548319743,\\n 613196571550416895,\\n 613196571560902655,\\n 613196571598651391\\n]')]
658+
"""
659+
geography_col = _to_col_if_str(geography_expression, "h3_coverage")
660+
resolution_col = _to_col_if_str(target_resolution, "h3_coverage")
661+
return builtin("h3_coverage", _emit_ast=_emit_ast)(geography_col, resolution_col)
662+
663+
664+
@publicapi
665+
def h3_coverage_strings(
666+
geography_expression: ColumnOrName,
667+
target_resolution: Union[ColumnOrName, int],
668+
_emit_ast: bool = True,
669+
) -> Column:
670+
"""
671+
Returns an array of H3 cell identifiers as strings that cover the given geography at the specified resolution.
672+
673+
Args:
674+
geography_expression (ColumnOrName): The GEOGRAPHY to cover.
675+
target_resolution (ColumnOrName, int): The H3 resolution level (0-15).
676+
677+
Returns:
678+
Column: An array of H3 cell identifiers as strings.
679+
680+
Example::
681+
682+
>>> from snowflake.snowpark.functions import to_geography
683+
>>> df = session.create_dataframe([
684+
... "POLYGON((-122.481889 37.826683,-122.479487 37.808548,-122.474150 37.808904,-122.476510 37.826935,-122.481889 37.826683))"
685+
... ], schema=["geo_wkt"])
686+
>>> df.select(h3_coverage_strings(to_geography(df["geo_wkt"]), 8).alias("h3_cells")).collect()
687+
[Row(H3_CELLS='[\\n "8828308701fffff",\\n "8828308703fffff",\\n "8828308709fffff",\\n "882830870bfffff",\\n "8828308715fffff",\\n "8828308739fffff"\\n]')]
688+
"""
689+
geo_col = _to_col_if_str(geography_expression, "h3_coverage_strings")
690+
res_col = (
691+
target_resolution
692+
if isinstance(target_resolution, Column)
693+
else lit(target_resolution)
694+
)
695+
return builtin("h3_coverage_strings", _emit_ast=_emit_ast)(geo_col, res_col)
696+
697+
698+
@publicapi
699+
def h3_get_resolution(cell_id: ColumnOrName, _emit_ast: bool = True) -> Column:
700+
"""
701+
Returns the resolution of an H3 cell ID.
702+
703+
Args:
704+
cell_id (ColumnOrName): The H3 cell ID.
705+
706+
Returns:
707+
Column: The resolution of the H3 cell ID.
708+
709+
Example::
710+
711+
>>> df = session.create_dataframe([617540519050084351, 617540519050084352], schema=["cell_id"])
712+
>>> df.select(h3_get_resolution(df["cell_id"]).alias("resolution")).collect()
713+
[Row(RESOLUTION=9), Row(RESOLUTION=9)]
714+
"""
715+
c = _to_col_if_str(cell_id, "h3_get_resolution")
716+
return builtin("h3_get_resolution", _emit_ast=_emit_ast)(c)
717+
718+
719+
@publicapi
720+
def h3_grid_disk(
721+
cell_id: ColumnOrName, k_value: ColumnOrName, _emit_ast: bool = True
722+
) -> Column:
723+
"""
724+
Returns an array of H3 cell IDs within k distance of the origin cell.
725+
726+
Args:
727+
cell_id (ColumnOrName): The H3 cell ID as the center of the disk.
728+
k_value (ColumnOrName): The distance (number of rings) from the center cell.
729+
730+
Returns:
731+
Column: An array of H3 cell IDs within the specified distance.
732+
733+
Example::
734+
735+
>>> df = session.create_dataframe([[617540519050084351, 1], [617540519050084351, 2]], schema=["cell_id", "k_value"])
736+
>>> df.select(h3_grid_disk("cell_id", "k_value").alias("grid_disk")).collect()
737+
[Row(GRID_DISK='[\\n 617540519050084351,\\n 617540519051657215,\\n 617540519050608639,\\n 617540519050870783,\\n 617540519050346495,\\n 617540519051395071,\\n 617540519051132927\\n]'), Row(GRID_DISK='[\\n 617540519050084351,\\n 617540519051657215,\\n 617540519050608639,\\n 617540519050870783,\\n 617540519050346495,\\n 617540519051395071,\\n 617540519051132927,\\n 617540519048249343,\\n 617540519048773631,\\n 617540519089143807,\\n 617540519088095231,\\n 617540519107756031,\\n 617540519108018175,\\n 617540519104086015,\\n 617540519103561727,\\n 617540519046414335,\\n 617540519047462911,\\n 617540519044579327,\\n 617540519044317183\\n]')]
738+
"""
739+
cell_id_col = _to_col_if_str(cell_id, "h3_grid_disk")
740+
k_value_col = _to_col_if_str(k_value, "h3_grid_disk")
741+
return builtin("h3_grid_disk", _emit_ast=_emit_ast)(cell_id_col, k_value_col)
742+
743+
744+
@publicapi
745+
def h3_grid_distance(
746+
cell_id_1: ColumnOrName, cell_id_2: ColumnOrName, _emit_ast: bool = True
747+
) -> Column:
748+
"""
749+
Returns the grid distance between two H3 cell IDs.
750+
751+
Args:
752+
cell_id_1 (ColumnOrName): The first H3 cell ID column or value.
753+
cell_id_2 (ColumnOrName): The second H3 cell ID column or value.
754+
755+
Returns:
756+
Column: The grid distance between the two H3 cells.
757+
758+
Example::
759+
>>> df = session.create_dataframe([[617540519103561727, 617540519052967935]], schema=["cell_id_1", "cell_id_2"])
760+
>>> df.select(h3_grid_distance(df["cell_id_1"], df["cell_id_2"]).alias("distance")).collect()
761+
[Row(DISTANCE=5)]
762+
"""
763+
cell_id_1 = _to_col_if_str(cell_id_1, "h3_grid_distance")
764+
cell_id_2 = _to_col_if_str(cell_id_2, "h3_grid_distance")
765+
return builtin("h3_grid_distance", _emit_ast=_emit_ast)(cell_id_1, cell_id_2)

0 commit comments

Comments
 (0)