Skip to content

feat(c/sedona-proj): Implement item crs support for ST_Transform#531

Merged
paleolimbot merged 13 commits intoapache:mainfrom
paleolimbot:item-crs-st-transform
Jan 22, 2026
Merged

feat(c/sedona-proj): Implement item crs support for ST_Transform#531
paleolimbot merged 13 commits intoapache:mainfrom
paleolimbot:item-crs-st-transform

Conversation

@paleolimbot
Copy link
Member

@paleolimbot paleolimbot commented Jan 20, 2026

This PR integrates Item Crs inputs and outputs into ST_Transform, which now has a rather complicated signature with respect to what can happen:

  • (Geometry or ItemCrs) + (String or Integer constant to) -> Geometery (type-level CRS)
  • (Geometry or ItemCrs) + (String or integer from) + (String or Integer constant to) -> Geometery (type-level CRS)
  • (Geometry or ItemCrs) + (String or Integer array/column to) -> Item CRS
  • (Geometry or ItemCrs) + (String or integer from) + (String or Integer array/column to) -> Item CRS

There's probably no great way to implement all of that cleanly but I did try to reduce repetition as much as possible.

import pandas as pd
import sedona.db

sd = sedona.db.connect()

df = pd.DataFrame({"wkt": ["POINT (0 1)", "POINT (2 3)"], "srid_from": [4326, 3857], "srid_to": [3857, 4326]})
sd.create_data_frame(df).to_view("foofy")

# Array -> Array
sd.sql("SELECT ST_Transform(ST_GeomFromWKT(wkt), srid_from, srid_to) as g FROM foofy").show()
#> ┌───────────────────────────────────────────────────────────────────────────────┐
#> │                                       g                                       │
#> │                                     struct                                    │
#> ╞═══════════════════════════════════════════════════════════════════════════════╡
#> │ {item: POINT(0 111325.1428663851), crs: EPSG:3857}                            │
#> ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
#> │ {item: POINT(0.000017966305682390428 0.00002694945852358465), crs: EPSG:4326} │
#> └───────────────────────────────────────────────────────────────────────────────┘

# Array -> Scalar
sd.sql("SELECT ST_Transform(ST_GeomFromWKT(wkt), srid_from, 4326) as g FROM foofy").show()
#> ┌───────────────────────────────────────────────────────┐
#> │                           g                           │
#> │                        geometry                       │
#> ╞═══════════════════════════════════════════════════════╡
#> │ POINT(0 1)                                            │
#> ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
#> │ POINT(0.000017966305682390428 0.00002694945852358465) │
#> └───────────────────────────────────────────────────────┘

# Scalar -> Array
sd.sql("SELECT ST_Transform(ST_GeomFromWKT(wkt), 4326, srid_to) as g FROM foofy").show()
#> ┌────────────────────────────────────────────────────┐
#> │                          g                         │
#> │                       struct                       │
#> ╞════════════════════════════════════════════════════╡
#> │ {item: POINT(0 111325.1428663851), crs: EPSG:3857} │
#> ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
#> │ {item: POINT(2 3), crs: EPSG:4326}                 │
#> └────────────────────────────────────────────────────┘

# ItemCrs -> Array
sd.sql("SELECT ST_Transform(ST_SetSRID(ST_GeomFromWKT(wkt), srid_from), srid_to) as g FROM foofy").show()
#> ┌───────────────────────────────────────────────────────────────────────────────┐
#> │                                       g                                       │
#> │                                     struct                                    │
#> ╞═══════════════════════════════════════════════════════════════════════════════╡
#> │ {item: POINT(0 111325.1428663851), crs: EPSG:3857}                            │
#> ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
#> │ {item: POINT(0.000017966305682390428 0.00002694945852358465), crs: EPSG:4326} │
#> └───────────────────────────────────────────────────────────────────────────────┘

# ItemCrs -> Scalar
sd.sql("SELECT ST_Transform(ST_SetSRID(ST_GeomFromWKT(wkt), srid_from), 4326) as g FROM foofy").show()
#> ┌───────────────────────────────────────────────────────┐
#> │                           g                           │
#> │                        geometry                       │
#> ╞═══════════════════════════════════════════════════════╡
#> │ POINT(0 1)                                            │
#> ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
#> │ POINT(0.000017966305682390428 0.00002694945852358465) │
#> └───────────────────────────────────────────────────────┘

@paleolimbot paleolimbot force-pushed the item-crs-st-transform branch from 9570f39 to 3c13875 Compare January 20, 2026 22:14
@paleolimbot paleolimbot requested a review from Copilot January 20, 2026 22:58
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements item-level CRS (Coordinate Reference System) support for the ST_Transform function, enabling transformations where the target CRS varies per row rather than being fixed at the type level.

Changes:

  • Adds support for item-level CRS input/output in ST_Transform, allowing per-row CRS specifications
  • Refactors argument handling to support both scalar and array CRS inputs with appropriate return type determination
  • Optimizes transformation by caching when both source and target CRS are constant across all rows

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 7 comments.

File Description
rust/sedona-testing/src/testers.rs Updates invoke method to compute return types with scalar arguments
rust/sedona-functions/src/executor.rs Adds support for iterating over item-crs struct arrays by extracting geometry from the "item" field
c/sedona-proj/src/st_transform.rs Major refactor to support item-crs inputs/outputs with new argument pattern matching and transformation logic
c/sedona-proj/Cargo.toml Adds sedona-common dependency

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

paleolimbot and others added 3 commits January 20, 2026 17:03
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@paleolimbot paleolimbot marked this pull request as ready for review January 20, 2026 23:05
match maybe_wkb {
Some(wkb) => {
invoke_scalar(&wkb, crs_transform.as_ref(), &mut builder)?;
builder.append_value([]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wanted to confirm the builder.append_value([]); is expected to be called after the invoke_scalar call.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that is expected (invoke_scalar writes into the builder directly but the append_value is needed to force a new element from the builder).

executor.execute_wkb_void(|maybe_wkb| {
match (maybe_wkb, crs_to_crs_iter.next().unwrap()) {
(Some(wkb), (Some(from_crs_str), Some(to_crs_str))) => {
let maybe_from_crs = deserialize_crs(from_crs_str)?;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe caching could help here since this is per-row calculation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a thread local cache behind deserialize_crs() for this reason!

...there are probably more ways we could speed up per-row CRS operations once they actually work, though.

@paleolimbot paleolimbot merged commit 233a329 into apache:main Jan 22, 2026
15 checks passed
@paleolimbot paleolimbot deleted the item-crs-st-transform branch January 22, 2026 22:43
@paleolimbot paleolimbot added this to the 0.3.0 milestone Jan 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants