Skip to content

[GH-3150] Preserve GeoParquet CRS metadata - #3151

Merged
jiayuasu merged 2 commits into
apache:masterfrom
jiayuasu:fix/geoparquet-crs-preservation
Jul 23, 2026
Merged

[GH-3150] Preserve GeoParquet CRS metadata#3151
jiayuasu merged 2 commits into
apache:masterfrom
jiayuasu:fix/geoparquet-crs-preservation

Conversation

@jiayuasu

@jiayuasu jiayuasu commented Jul 23, 2026

Copy link
Copy Markdown
Member

Did you read the Contributor Guide?

Is this PR related to a ticket?

  • Yes, and the PR name follows the format [SEDONA-XXX] my subject.
  • Yes, and the PR name follows the format [GH-XXX] my subject. Closes GeoParquet CRS parsing mutates PROJJSON keys #3150
  • No:
    • this is a documentation update. The PR name follows the format [DOCS] my subject
    • this is a CI update. The PR name follows the format [CI] my subject

What changes were proposed in this PR?

Preserve GeoParquet CRS metadata exactly as it appears in the nested PROJJSON object when parsing or serializing the geo metadata. The outer GeoParquet structure still uses the case conversion required by its case classes, but the raw crs subtree is restored afterward so standard keys and user-provided extensions are not changed in either direction.

Extract a geometry SRID directly from declared top-level PROJJSON identifiers instead of constructing a projection solely to call toAuthority(). Both the singular id and plural ids forms are supported. Positive numeric and numeric-string EPSG codes are accepted, OGC:CRS84 maps to SRID 4326 case-insensitively, and invalid or unsupported identifiers map to SRID 0.

The root cause was recursive key conversion combined with requiring a complete executable CRS definition for identifier extraction. This could make GeoParquet files with non-default CRS metadata fail to round trip with the current proj4sedona parser, expose mutated CRS JSON through metadata queries, or lose their SRID.

This also improves the read path. extractSridFromCrs runs from GeoParquetSchemaConverter for each geometry column in each file footer; direct JSON field reads avoid repeatedly parsing PROJJSON, resolving registries and datums, and constructing full projection objects during multi-file scans.

The fix is user-visible through the geoparquet.metadata data source in all four Spark shims. They share parseKeyValueMetaData, so rendered columnMetadata.crs values now retain the original PROJJSON keys instead of exposing camelized JSON.

When PROJJSON contains neither a recognized top-level id nor a recognized entry in ids, this change deliberately returns SRID 0 rather than attempting parameter-based authority inference through proj4sedona. Sedona-written GeoParquet includes an identifier, while preserving the raw CRS object still lets callers inspect identifier-free third-party metadata.

Regression tests cover exact PROJJSON preservation on write and read, incomplete ID-bearing CRS objects, plural identifiers, case-insensitive OGC identifiers, numeric-string EPSG codes, and invalid codes.

How was this patch tested?

  • geoparquetIOTests with the released proj4sedona 0.1.2 dependency: 52 tests passed.
  • geoparquetIOTests with the current proj4sedona main branch installed as a local snapshot: 52 tests passed.
  • CRSTransformProj4Test, geoparquetIOTests, and netcdfMetadataTest together with that snapshot: 157 tests passed.
  • The repository pre-commit hooks passed.

Did this PR include necessary documentation updates?

  • Yes, I am adding a new API. I am using the current SNAPSHOT version number in vX.Y.Z format.
  • Yes, I have updated the documentation.
  • No, this PR does not affect any public API so no need to change the documentation.

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

This PR fixes GeoParquet CRS handling so that PROJJSON CRS metadata round-trips without mutating nested PROJJSON keys, and SRID extraction can succeed based solely on the declared top-level PROJJSON id (without requiring a fully-instantiable CRS/projection).

Changes:

  • Preserve CRS PROJJSON exactly as stored in GeoParquet metadata by restoring the raw crs JValue after camelizing the outer GeoParquet structures.
  • Rework SRID extraction to read id.authority + id.code directly (supporting positive numeric and numeric-string EPSG codes; mapping OGC:CRS84 to 4326; invalid/unsupported identifiers to 0).
  • Add regression tests covering PROJJSON preservation and SRID extraction for incomplete CRS objects and invalid/string EPSG codes.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
spark/common/src/main/scala/org/apache/spark/sql/execution/datasources/geoparquet/GeoParquetMetaData.scala Restores raw crs after camelization and simplifies SRID extraction to read PROJJSON id directly.
spark/common/src/test/scala/org/apache/sedona/sql/geoparquetIOTests.scala Adds regression tests for PROJJSON key preservation and expanded SRID extraction scenarios.

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

@jiayuasu
jiayuasu marked this pull request as ready for review July 23, 2026 08:16
@jiayuasu
jiayuasu merged commit d18c733 into apache:master Jul 23, 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.

GeoParquet CRS parsing mutates PROJJSON keys

2 participants