Skip to content

[GH-3189] GeoPandas: Preserve CRS metadata without geometry SRIDs - #3190

Merged
jiayuasu merged 2 commits into
apache:masterfrom
jiayuasu:feature/geopandas-crs-metadata
Jul 28, 2026
Merged

[GH-3189] GeoPandas: Preserve CRS metadata without geometry SRIDs#3190
jiayuasu merged 2 commits into
apache:masterfrom
jiayuasu:feature/geopandas-crs-metadata

Conversation

@jiayuasu

@jiayuasu jiayuasu commented Jul 27, 2026

Copy link
Copy Markdown
Member

Did you read the Contributor Guide?

Is this PR related to a ticket?

What changes were proposed in this PR?

This PR preserves CRS metadata when a distributed GeoSeries or
GeoDataFrame has no geometry row from which Sedona can recover an SRID.

CRS WKT is stored in each geometry column's pandas-on-Spark InternalField
metadata. This makes the metadata survive column extraction, projection,
filtering to zero rows, copies, geometry expressions, active-geometry
switches, renames, assignments, and pandas-on-Spark round trips without
collecting geometry rows.

set_crs and constructors now retain the full CRS even when it has no EPSG
code, while the physical Sedona SRID remains 0 in that case. Construction
with duplicate indexes updates SRID directly in the Spark column instead of
performing index alignment.

The patch also fixes GeoSeries.copy(deep=True), which previously raised an
assertion for every input, and keeps copied metadata independent from later
changes to the source object.

This is the shared CRS foundation needed by the distributed clip,
dissolve, and collection APIs merged from #3183 and #3185.

How was this patch tested?

  • TestGeoSeries::test_set_crs
  • TestGeoSeries::test_crs_metadata_propagation
  • TestGeoDataFrame::test_construct_from_geopandas
  • TestGeoDataFrame::test_set_crs
  • TestGeoDataFrame::test_crs_metadata_survives_frame_selection
  • TestGeoDataFrame::test_set_geometry_crs
  • CRS preservation regressions for clip and dissolve
  • Repository pre-commit hooks

The tests cover empty and all-null geometries, zero-row filters, explicit CRS
replacement and removal, non-EPSG CRS values, deep-copy independence,
GeoPandas conversion, duplicate-index construction, Spark-pandas round trips,
and both in-place and copied active-geometry switches.

Did this PR include necessary documentation updates?

  • No, this PR does not affect any public API so no need to change the documentation.

@jiayuasu
jiayuasu requested a review from Copilot July 27, 2026 18:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Preserves CRS metadata for distributed GeoSeries/GeoDataFrame outputs that have no usable geometry SRID (empty, all-null, or SRID 0), ensuring CRS can still round-trip through common operations and conversions.

Changes:

  • Added explicit CRS fallback metadata on GeoSeries and updated CRS resolution precedence (SRID > explicit metadata > lineage metadata).
  • Updated GeoDataFrame construction, geometry switching, copying, and GeoPandas conversion to retain/clear CRS fallback appropriately (including duplicate-index construction behavior).
  • Expanded Python test coverage for empty/all-null geometries, duplicate-index construction, geometry switching, copies, and GeoPandas round-trips.

Reviewed changes

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

File Description
python/sedona/spark/geopandas/geoseries.py Adds explicit CRS fallback metadata and updates CRS lookup / copy behavior.
python/sedona/spark/geopandas/geodataframe.py Preserves/clears CRS fallback across construction, geometry access/switching, copying, and GeoPandas conversion; avoids duplicate-index row multiplication on construction.
python/tests/geopandas/test_geoseries.py Adds tests for CRS preservation/removal for all-null and empty explode results.
python/tests/geopandas/test_geodataframe.py Adds tests for duplicate-index construction and CRS preservation across set_geometry/copy/reconstruction and GeoPandas conversion.

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

Comment on lines +343 to +348
self._col_label: Label
self._sindex: SpatialIndex = None
self._empty_crs_source: typing.Optional["GeoSeries"] = None
# Explicit CRS metadata wins over the lineage fallback below when
# geometry rows are empty or carry SRID 0.
self._empty_crs_value = None
Comment on lines +440 to +444
geometry = self[self._geometry_column_name]
empty_crs_source = getattr(self, "_empty_crs_source", None)
if empty_crs_source is not None:
geometry._empty_crs_source = empty_crs_source
return geometry
Comment on lines +492 to +501
if srid != 0:
return CRS.from_user_input(srid)
# These fallbacks are metadata rather than a fresh read from geometry
# coordinates. Explicit metadata takes precedence over inherited
# lineage metadata, including for non-empty geometries with SRID 0.
if self._empty_crs_value is not None:
return self._empty_crs_value
if self._empty_crs_source is not None:
return self._empty_crs_source.crs
return None
@jiayuasu jiayuasu added this to the sedona-1.9.1 milestone Jul 28, 2026
@jiayuasu
jiayuasu merged commit bf61c25 into apache:master Jul 28, 2026
34 checks passed
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.

GeoPandas: Preserve CRS metadata for empty geometry results

2 participants