Skip to content

GeoPandas: leading-null GeoSeries input loses Z dimensions #3369

Description

@jiayuasu

Expected behavior

A leading null should not change the dimensions of the other geometries in a GeoSeries. Empty XYZ geometries and points whose Z is NaN should retain their dimensional metadata.

Actual behavior

The constructor path added in #3349 for #3333 converts local geometries through WKB and ST_GeomFromWKB. Adding a leading null causes these geometries to become 2D:

Input geometry GeoSeries([geometry, None]) GeoSeries([None, geometry])
POINT Z EMPTY 3D 2D
LINESTRING Z EMPTY 3D 2D
POLYGON Z EMPTY 3D 2D
POINT Z (1 2 NaN) 3D 2D
POINT Z (1 2 3) 3D 3D
POINT EMPTY 2D 2D

Steps to reproduce

With a SedonaContext initialized using the current-master JVM jar:

import shapely
from sedona.spark.geopandas import GeoSeries

g = shapely.from_wkt("POINT Z EMPTY")
ordinary = GeoSeries([g, None]).to_geopandas().iloc[0]
leading_null = GeoSeries([None, g]).to_geopandas().iloc[1]

print(ordinary.wkt, shapely.get_coordinate_dimension(ordinary))
# POINT Z EMPTY 3
print(leading_null.wkt, shapely.get_coordinate_dimension(leading_null))
# POINT EMPTY 2

Sedona version

2.0.0-SNAPSHOT, master commit 15e415a.

The Spark 4.1 JVM jar was freshly built from this commit with mvn -B -pl spark-shaded -am -Dspark=4.1 -Dscala=2.13 -DskipTests package. Both Python serializer modes reproduced the same results: the pure-Python fallback and a native extension freshly compiled from the same source. The six inputs above were compared through both constructor paths in each mode.

Environment

  • API: Python / GeoPandas compatibility API
  • Spark: 4.1.1; Scala: 2.13.17
  • Java: 17.0.13; Python: 3.11.13
  • Shapely: 2.1.2; GEOS: 3.13.1
  • GeoPandas: 1.1.1; pandas: 2.2.3
  • Local Spark, macOS arm64

Root cause and suggested fix

Direct JVM checks show that the WKB parser retains the 3D coordinate sequence. GeometrySerializer then chooses XY when the sequence has no coordinates or all Z values are NaN. JVM result rows are already 2D before Python decoding.

Replace the leading-null constructor's WKB conversion with construction using an explicit Spark GeometryType schema and Sedona's Python geometry serializer. Preserve the pandas index, row order, name, CRS, embedded SRIDs, and normalization of all pandas missing-value variants. Add dimensional regression cases alongside the existing constructor tests and verify Spark 3.5 and 4.1.

A local explicit-schema prototype preserved the dimensional cases, names, duplicate MultiIndex order, missing values, and CRS on Spark 4.1.1. One additional requirement is to preserve embedded SRIDs in pure-Python serialization: the fallback currently writes zero SRID bytes, so switching to it directly would regress the existing leading-null embedded-SRID case. The native serializer already preserves these SRIDs. A full replacement also needs M/ZM compatibility coverage, since the fallback Python serializer currently rejects measured geometries.

Changing the JVM serializer to treat every three-dimensional JTS sequence as XYZ would also promote ordinary XY data, because JTS can represent XY with a NaN Z slot.

The reproduction measures returned geometry dimensions directly. It does not rely on ST_NDims, ST_CoordDim, or ST_Zmflag, whose empty-geometry results were intentionally changed in #3360.

Existing issues

  • I searched the existing issues and did not find a duplicate.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions