This is an automated email from the ASF dual-hosted git repository.

jiayu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sedona.git


The following commit(s) were added to refs/heads/master by this push:
     new e0381aba1a [GH-2224] Register S2Geography UDT as Geography UDT into 
Apache Sedona (#2223)
e0381aba1a is described below

commit e0381aba1ae103eec4139db32c072a077e38445a
Author: Zhuocheng Shang <[email protected]>
AuthorDate: Thu Aug 7 15:49:30 2025 -0700

    [GH-2224] Register S2Geography UDT as Geography UDT into Apache Sedona 
(#2223)
    
    * add Singlepoint, Singlepolyline for correctlty round trip WKT write
    
    * Register S2Geography UDT and format
    
    comment out existing Geography type, can bring back later
    
    * change S2Geography to static
    
    * comment out existing Geography functions
    
    * comment out ST_GeogFromWKT
    
    * Geography return with Geography type as extend of S2Geography
    
    * change back to original Geography type
    
    * fix existing Geography function
    
    * wrapper with Geography on WTK, WKB read write
    
    * GeoConstructor from WKB using ST_S2GeogFromWKB
    
    * temporary  comment out all ST_GeogFromWKT test
    
    * comment out ST_GeogFromWKT
    
    * return new wrapper of Geography type
    
    * fix pre-commit run  black-jupyter
    
    * import TestBase
    
    * Merge upstream and finish geogWKB changes
    
    * format files
    
    * fix test data file path
    
    * Refactor the PR
    
    * add default toString() return WKT with precision model fixed
    
    * add ST_GeogCollFromText, ST_GeogFromText, ST_GeogFromWKB
    
    * remove old S2Geography reference
    
    * Drop all ST_S2 constructors for now
    
    ---------
    
    Co-authored-by: Jia Yu <[email protected]>
---
 .../org/apache/sedona/common/Constructors.java     |  5 --
 .../java/org/apache/sedona/common/Functions.java   |  6 +-
 .../sedona/common/S2Geography/Accessors.java       | 38 ++++-----
 .../sedona/common/S2Geography/EncodeTag.java       |  2 +-
 .../S2Geography/EncodedShapeIndexGeography.java    |  6 +-
 .../{S2Geography.java => Geography.java}           | 52 +++++++++----
 .../common/S2Geography/GeographyCollection.java    | 16 ++--
 .../sedona/common/S2Geography/GeographyIndex.java  |  2 +-
 .../GeographySerializer.java}                      | 25 +++---
 .../common/S2Geography/MultiPolygonGeography.java  |  2 +-
 .../sedona/common/S2Geography/PointGeography.java  |  6 +-
 .../common/S2Geography/PolygonGeography.java       |  3 +-
 .../common/S2Geography/PolylineGeography.java      |  2 +-
 .../common/S2Geography/ShapeIndexGeography.java    |  6 +-
 .../sedona/common/S2Geography/WKBReader.java       | 28 ++++---
 .../sedona/common/S2Geography/WKBWriter.java       | 20 +++--
 .../sedona/common/S2Geography/WKTReader.java       | 24 ++++--
 .../sedona/common/S2Geography/WKTWriter.java       | 91 ++++++++++++----------
 .../sedona/common/geography/Constructors.java      | 50 ++++++++++++
 .../sedona/common/geometrySerde/GeometrySerde.java | 30 ++++++-
 .../sedona/common/S2Geography/DistanceTest.java    |  8 +-
 .../S2Geography/GeographyCollectionTest.java       |  8 +-
 .../common/S2Geography/PointGeographyTest.java     |  4 +-
 .../S2Geography/ShapeIndexGeographyTest.java       |  4 +-
 .../sedona/common/S2Geography/TestHelper.java      | 25 ++++--
 .../sedona/common/S2Geography/WKBReaderTest.java   | 12 +--
 .../sedona/common/S2Geography/WKBWriterTest.java   | 25 +++---
 .../common/S2Geography/WKTReadWriterTest.java      | 10 +--
 python/tests/sql/test_dataframe_api.py             |  8 +-
 python/tests/sql/test_geography.py                 | 36 ++++-----
 .../scala/org/apache/sedona/sql/UDF/Catalog.scala  |  4 +
 .../spark/sql/sedona_sql/UDT/GeographyUDT.scala    |  8 +-
 .../sql/sedona_sql/UDT/UdtRegistratorWrapper.scala |  2 +-
 .../sql/sedona_sql/expressions/Constructors.scala  | 14 ----
 .../sql/sedona_sql/expressions/Functions.scala     | 13 +---
 .../expressions/InferredExpression.scala           | 25 +++---
 .../expressions/geography/Constructors.scala       | 81 +++++++++++++++++++
 .../sql/sedona_sql/expressions/implicits.scala     | 48 +++++++++++-
 .../sedona_sql/expressions/st_constructors.scala   | 22 +++++-
 .../apache/sedona/sql/constructorTestScala.scala   |  9 ---
 .../apache/sedona/sql/dataFrameAPITestScala.scala  | 16 ++--
 .../org/apache/sedona/sql/functionTestScala.scala  | 40 +++++-----
 .../sedona/sql/geography/ConstructorsTest.scala    | 76 ++++++++++++++++++
 43 files changed, 617 insertions(+), 295 deletions(-)

diff --git a/common/src/main/java/org/apache/sedona/common/Constructors.java 
b/common/src/main/java/org/apache/sedona/common/Constructors.java
index b53e8162dd..3cd4729243 100644
--- a/common/src/main/java/org/apache/sedona/common/Constructors.java
+++ b/common/src/main/java/org/apache/sedona/common/Constructors.java
@@ -22,7 +22,6 @@ import java.io.IOException;
 import javax.xml.parsers.ParserConfigurationException;
 import org.apache.sedona.common.enums.FileDataSplitter;
 import org.apache.sedona.common.enums.GeometryType;
-import org.apache.sedona.common.geometryObjects.Geography;
 import org.apache.sedona.common.utils.FormatUtils;
 import org.apache.sedona.common.utils.GeoHashDecoder;
 import org.locationtech.jts.geom.*;
@@ -45,10 +44,6 @@ public class Constructors {
     return new WKTReader(geometryFactory).read(wkt);
   }
 
-  public static Geography geogFromWKT(String wkt, int srid) throws 
ParseException {
-    return new Geography(geomFromWKT(wkt, srid));
-  }
-
   public static Geometry geomFromEWKT(String ewkt) throws ParseException {
     if (ewkt == null) {
       return null;
diff --git a/common/src/main/java/org/apache/sedona/common/Functions.java 
b/common/src/main/java/org/apache/sedona/common/Functions.java
index 0bab24344b..83522080d3 100644
--- a/common/src/main/java/org/apache/sedona/common/Functions.java
+++ b/common/src/main/java/org/apache/sedona/common/Functions.java
@@ -28,8 +28,8 @@ import java.util.*;
 import java.util.List;
 import java.util.stream.Collectors;
 import org.apache.commons.lang3.tuple.Pair;
+import org.apache.sedona.common.S2Geography.Geography;
 import org.apache.sedona.common.geometryObjects.Circle;
-import org.apache.sedona.common.geometryObjects.Geography;
 import org.apache.sedona.common.sphere.Spheroid;
 import org.apache.sedona.common.subDivide.GeometrySubDivider;
 import org.apache.sedona.common.utils.*;
@@ -785,7 +785,7 @@ public class Functions {
   }
 
   public static String asEWKT(Geography geography) {
-    return asEWKT(geography.getGeometry());
+    return asEWKT(geography);
   }
 
   public static String asWKT(Geometry geometry) {
@@ -797,7 +797,7 @@ public class Functions {
   }
 
   public static byte[] asEWKB(Geography geography) {
-    return asEWKB(geography.getGeometry());
+    return asEWKB(geography);
   }
 
   public static String asHexEWKB(Geometry geom, String endian) {
diff --git 
a/common/src/main/java/org/apache/sedona/common/S2Geography/Accessors.java 
b/common/src/main/java/org/apache/sedona/common/S2Geography/Accessors.java
index c3a6f1b5a0..1c26f58ec3 100644
--- a/common/src/main/java/org/apache/sedona/common/S2Geography/Accessors.java
+++ b/common/src/main/java/org/apache/sedona/common/S2Geography/Accessors.java
@@ -24,9 +24,9 @@ public class Accessors {
 
   public Accessors() {}
 
-  public static boolean S2_isEmpty(S2Geography s2Geography) {
-    for (int i = 0; i < s2Geography.numShapes(); i++) {
-      S2Shape shape = s2Geography.shape(i);
+  public static boolean S2_isEmpty(Geography geography) {
+    for (int i = 0; i < geography.numShapes(); i++) {
+      S2Shape shape = geography.shape(i);
       if (!shape.isEmpty()) return false;
     }
     return true;
@@ -47,7 +47,7 @@ public class Accessors {
    * Returns true if `geog` is a “collection” (i.e. multi‐point, 
multi‐linestring, or multi‐polygon)
    * rather than a single simple feature.
    */
-  public boolean S2_isCollection(S2Geography geog) {
+  public boolean S2_isCollection(Geography geog) {
     int dim = S2_dimension(geog);
     if (dim == -1) {
       return false;
@@ -79,21 +79,21 @@ public class Accessors {
     }
   }
 
-  public static int S2_dimension(S2Geography s2Geography) {
-    int dimension = s2Geography.dimension();
+  public static int S2_dimension(Geography geography) {
+    int dimension = geography.dimension();
     if (dimension != -1) return dimension;
 
-    for (int i = 0; i < s2Geography.numShapes(); i++) {
-      S2Shape shape = s2Geography.shape(i);
+    for (int i = 0; i < geography.numShapes(); i++) {
+      S2Shape shape = geography.shape(i);
       if (shape.dimension() > dimension) dimension = shape.dimension();
     }
     return dimension;
   }
 
-  public static int S2_numPoints(S2Geography s2Geography) {
+  public static int S2_numPoints(Geography geography) {
     int numPoints = 0;
-    for (int i = 0; i < s2Geography.numShapes(); i++) {
-      S2Shape shape = s2Geography.shape(i);
+    for (int i = 0; i < geography.numShapes(); i++) {
+      S2Shape shape = geography.shape(i);
       switch (shape.dimension()) {
         case 0:
         case 2:
@@ -107,7 +107,7 @@ public class Accessors {
     return numPoints;
   }
 
-  double S2_area(S2Geography geog) {
+  double S2_area(Geography geog) {
     if (S2_dimension(geog) != 2) return 0;
     switch (geog.kind) {
       case POLYGON:
@@ -121,7 +121,7 @@ public class Accessors {
 
   public double S2_area(GeographyCollection geographyCollection) {
     double area = 0;
-    for (S2Geography geography : geographyCollection.features) {
+    for (Geography geography : geographyCollection.features) {
       area += S2_area(geography);
     }
     return area;
@@ -131,7 +131,7 @@ public class Accessors {
     return polygonGeography.polygon.getArea();
   }
 
-  public double s2_length(S2Geography geog) {
+  public double s2_length(Geography geog) {
     double length = 0.0;
     if (S2_dimension(geog) == 1) {
       for (int i = 0, n = geog.numShapes(); i < n; ++i) {
@@ -148,7 +148,7 @@ public class Accessors {
     return length;
   }
 
-  public double s2_perimeter(S2Geography geog) {
+  public double s2_perimeter(Geography geog) {
     double perimeter = 0.0;
     if (S2_dimension(geog) == 2) {
       for (int i = 0, n = geog.numShapes(); i < n; ++i) {
@@ -165,7 +165,7 @@ public class Accessors {
     return perimeter;
   }
 
-  public static double s2_X(S2Geography geog) {
+  public static double s2_X(Geography geog) {
     double out = Double.NaN;
     for (int i = 0, n = geog.numShapes(); i < n; ++i) {
       S2Shape shape = geog.shape(i);
@@ -187,7 +187,7 @@ public class Accessors {
    * Extract the Y coordinate (latitude in degrees) if this is exactly one 
point; otherwise returns
    * NaN.
    */
-  public static double s2_Y(S2Geography geog) {
+  public static double s2_Y(Geography geog) {
     double out = Double.NaN;
     for (int i = 0, n = geog.numShapes(); i < n; ++i) {
       S2Shape shape = geog.shape(i);
@@ -224,7 +224,7 @@ public class Accessors {
 
   /** Runs S2 validation on each member of a GeographyCollection. */
   public static boolean s2FindValidationError(GeographyCollection geog, 
S2Error error) {
-    for (S2Geography feature : geog.getFeatures()) {
+    for (Geography feature : geog.getFeatures()) {
       if (s2FindValidationError(feature, error)) {
         return true;
       }
@@ -237,7 +237,7 @@ public class Accessors {
    * C++ logic: 0-dim → always OK 1-dim → polyline path 2-dim → polygon path 
else → treat as
    * collection of polygons
    */
-  public static boolean s2FindValidationError(S2Geography geog, S2Error error) 
{
+  public static boolean s2FindValidationError(Geography geog, S2Error error) {
     int dim = geog.dimension();
     switch (dim) {
       case 0:
diff --git 
a/common/src/main/java/org/apache/sedona/common/S2Geography/EncodeTag.java 
b/common/src/main/java/org/apache/sedona/common/S2Geography/EncodeTag.java
index a139965372..dd7ada9735 100644
--- a/common/src/main/java/org/apache/sedona/common/S2Geography/EncodeTag.java
+++ b/common/src/main/java/org/apache/sedona/common/S2Geography/EncodeTag.java
@@ -24,7 +24,7 @@ import com.esotericsoftware.kryo.io.UnsafeInput;
 import com.google.common.geometry.S2CellId;
 import java.io.*;
 import java.util.List;
-import org.apache.sedona.common.S2Geography.S2Geography.GeographyKind;
+import org.apache.sedona.common.S2Geography.Geography.GeographyKind;
 
 /**
  * A 4 byte prefix for encoded geographies. Builds a 5-byte header (EncodeTag) 
containing 1 byte:
diff --git 
a/common/src/main/java/org/apache/sedona/common/S2Geography/EncodedShapeIndexGeography.java
 
b/common/src/main/java/org/apache/sedona/common/S2Geography/EncodedShapeIndexGeography.java
index 53a0b67f5b..03d355dcc4 100644
--- 
a/common/src/main/java/org/apache/sedona/common/S2Geography/EncodedShapeIndexGeography.java
+++ 
b/common/src/main/java/org/apache/sedona/common/S2Geography/EncodedShapeIndexGeography.java
@@ -27,7 +27,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.logging.Logger;
 
-public class EncodedShapeIndexGeography extends S2Geography {
+public class EncodedShapeIndexGeography extends Geography {
   private static final Logger logger = 
Logger.getLogger(EncodedShapeIndexGeography.class.getName());
 
   public S2ShapeIndex shapeIndex;
@@ -63,7 +63,7 @@ public class EncodedShapeIndexGeography extends S2Geography {
    *
    * @return the last shapeId assigned.
    */
-  public int addIndex(S2Geography geog) {
+  public int addIndex(Geography geog) {
     int lastId = -1;
     for (int i = 0, n = geog.numShapes(); i < n; i++) {
       shapeIndex.add(geog.shape(i));
@@ -80,7 +80,7 @@ public class EncodedShapeIndexGeography extends S2Geography {
   }
 
   @Override
-  protected void encode(UnsafeOutput os, EncodeOptions opts) throws 
IOException {
+  public void encode(UnsafeOutput os, EncodeOptions opts) throws IOException {
     throw new IOException("Encode() not implemented for 
EncodedShapeIndexGeography()");
   }
   // decode
diff --git 
a/common/src/main/java/org/apache/sedona/common/S2Geography/S2Geography.java 
b/common/src/main/java/org/apache/sedona/common/S2Geography/Geography.java
similarity index 84%
rename from 
common/src/main/java/org/apache/sedona/common/S2Geography/S2Geography.java
rename to 
common/src/main/java/org/apache/sedona/common/S2Geography/Geography.java
index b352e6c3f2..37920aa90a 100644
--- a/common/src/main/java/org/apache/sedona/common/S2Geography/S2Geography.java
+++ b/common/src/main/java/org/apache/sedona/common/S2Geography/Geography.java
@@ -32,21 +32,28 @@ import org.slf4j.LoggerFactory;
  * An abstract class represent S2Geography. Has 6 subtypes of geography: 
POINT, POLYLINE, POLYGON,
  * GEOGRAPHY_COLLECTION, SHAPE_INDEX, ENCODED_SHAPE_INDEX.
  */
-public abstract class S2Geography {
-  private static final Logger logger = 
LoggerFactory.getLogger(S2Geography.class.getName());
+public abstract class Geography {
+  private static final Logger logger = 
LoggerFactory.getLogger(Geography.class.getName());
 
   private static final int BUFFER_SIZE = 4 * 1024;
 
   protected final GeographyKind kind;
 
-  protected S2Geography(GeographyKind kind) {
+  private int srid = 0;
+
+  protected Geography(GeographyKind kind) {
     this.kind = kind;
   }
 
-  public void setSRID(int srid) {}
+  public void setSRID(int srid) {
+    if (srid < 0) {
+      throw new IllegalArgumentException("SRID must be non-negative, got: " + 
srid);
+    }
+    this.srid = srid;
+  }
 
   public int getSRID() {
-    return -1;
+    return srid;
   }
 
   public enum GeographyKind {
@@ -83,6 +90,11 @@ public abstract class S2Geography {
       throw new IllegalArgumentException("Unknown GeographyKind: " + kind);
     }
   }
+
+  public int getKind() {
+    return this.kind.getKind();
+  }
+
   /**
    * @return 0, 1, or 2 if all Shape()s that are returned will have the same 
dimension (i.e., they
    *     are all points, all lines, or all polygons).
@@ -145,7 +157,7 @@ public abstract class S2Geography {
 
   @Override
   public String toString() {
-    return this.toText(new PrecisionModel());
+    return this.toText(new PrecisionModel(PrecisionModel.FIXED));
   }
 
   public String toString(PrecisionModel precisionModel) {
@@ -179,6 +191,7 @@ public abstract class S2Geography {
       tag.setFlags((byte) (tag.getFlags() | EncodeTag.FLAG_EMPTY));
       tag.setCoveringSize((byte) 0);
       tag.encode(out);
+      out.writeInt(getSRID()); // write the SRID
       out.flush();
       return;
     }
@@ -204,37 +217,46 @@ public abstract class S2Geography {
 
     // 3) Write the geography
     this.encode(out, opts);
+    out.writeInt(getSRID()); // write the SRID
     out.flush();
   }
 
-  public static S2Geography decodeTagged(InputStream is) throws IOException {
+  public static Geography decodeTagged(InputStream is) throws IOException {
     // wrap ONCE
     UnsafeInput kryoIn = new UnsafeInput(is, BUFFER_SIZE);
     EncodeTag topTag = EncodeTag.decode(kryoIn);
     // 1) decode the tag
-    return S2Geography.decode(kryoIn, topTag);
+    return Geography.decode(kryoIn, topTag);
   }
 
-  public static S2Geography decode(UnsafeInput in, EncodeTag tag) throws 
IOException {
+  public static Geography decode(UnsafeInput in, EncodeTag tag) throws 
IOException {
     // 2) dispatch to subclass's decode method according to tag.kind
+    Geography geo;
     switch (tag.getKind()) {
       case CELL_CENTER:
       case POINT:
       case SINGLEPOINT:
-        return PointGeography.decode(in, tag);
+        geo = PointGeography.decode(in, tag);
+        break;
       case POLYLINE:
       case SINGLEPOLYLINE:
-        return PolylineGeography.decode(in, tag);
+        geo = PolylineGeography.decode(in, tag);
+        break;
       case POLYGON:
-        return PolygonGeography.decode(in, tag);
+        geo = PolygonGeography.decode(in, tag);
+        break;
       case GEOGRAPHY_COLLECTION:
-        return GeographyCollection.decode(in, tag);
+        geo = GeographyCollection.decode(in, tag);
+        break;
       case SHAPE_INDEX:
-        return EncodedShapeIndexGeography.decode(in, tag);
+        geo = EncodedShapeIndexGeography.decode(in, tag);
+        break;
       default:
         throw new IOException("Unsupported GeographyKind for decoding: " + 
tag.getKind());
     }
+    geo.setSRID(in.readInt()); // read the SRID
+    return geo;
   }
 
-  protected abstract void encode(UnsafeOutput os, EncodeOptions opts) throws 
IOException;
+  public abstract void encode(UnsafeOutput os, EncodeOptions opts) throws 
IOException;
 }
diff --git 
a/common/src/main/java/org/apache/sedona/common/S2Geography/GeographyCollection.java
 
b/common/src/main/java/org/apache/sedona/common/S2Geography/GeographyCollection.java
index e18e42651d..db0aa398e2 100644
--- 
a/common/src/main/java/org/apache/sedona/common/S2Geography/GeographyCollection.java
+++ 
b/common/src/main/java/org/apache/sedona/common/S2Geography/GeographyCollection.java
@@ -29,10 +29,10 @@ import java.util.List;
 import java.util.logging.Logger;
 
 /** A Geography wrapping zero or more Geography objects, representing a 
GEOMETRYCOLLECTION. */
-public class GeographyCollection extends S2Geography {
+public class GeographyCollection extends Geography {
   private static final Logger logger = 
Logger.getLogger(GeographyCollection.class.getName());
 
-  public final List<S2Geography> features;
+  public final List<Geography> features;
   public final List<Integer> numShapesList;
   public int totalShapes;
 
@@ -45,7 +45,7 @@ public class GeographyCollection extends S2Geography {
   }
 
   /** Wraps existing Geography features. */
-  public GeographyCollection(List<S2Geography> features) {
+  public GeographyCollection(List<Geography> features) {
     super(GeographyKind.GEOGRAPHY_COLLECTION);
     this.features = new ArrayList<>(features);
     this.numShapesList = new ArrayList<>();
@@ -81,14 +81,14 @@ public class GeographyCollection extends S2Geography {
   @Override
   public S2Region region() {
     Collection<S2Region> regs = new ArrayList<>();
-    for (S2Geography geo : features) {
+    for (Geography geo : features) {
       regs.add(geo.region());
     }
     return new S2RegionUnion(regs);
   }
 
   /** Returns an immutable copy of the features list. */
-  public List<S2Geography> getFeatures() {
+  public List<Geography> getFeatures() {
     return ImmutableList.copyOf(features);
   }
 
@@ -99,7 +99,7 @@ public class GeographyCollection extends S2Geography {
     EncodeOptions childOptions = new EncodeOptions(opts);
     childOptions.setIncludeCovering(false);
     out.writeInt(features.size());
-    for (S2Geography feature : features) {
+    for (Geography feature : features) {
       feature.encodeTagged(out, opts);
     }
     out.flush();
@@ -128,7 +128,7 @@ public class GeographyCollection extends S2Geography {
     for (int i = 0; i < n; i++) {
       tag = EncodeTag.decode(in);
       // avoid creating new stream, directly call S2Geography.decode
-      S2Geography feature = S2Geography.decode(in, tag);
+      Geography feature = Geography.decode(in, tag);
       geo.features.add(feature);
     }
     geo.countShapes();
@@ -138,7 +138,7 @@ public class GeographyCollection extends S2Geography {
   private void countShapes() {
     numShapesList.clear();
     totalShapes = 0;
-    for (S2Geography geo : features) {
+    for (Geography geo : features) {
       int n = geo.numShapes();
       numShapesList.add(n);
       totalShapes += n;
diff --git 
a/common/src/main/java/org/apache/sedona/common/S2Geography/GeographyIndex.java 
b/common/src/main/java/org/apache/sedona/common/S2Geography/GeographyIndex.java
index 53f50cbfa2..0e022aea89 100644
--- 
a/common/src/main/java/org/apache/sedona/common/S2Geography/GeographyIndex.java
+++ 
b/common/src/main/java/org/apache/sedona/common/S2Geography/GeographyIndex.java
@@ -36,7 +36,7 @@ public class GeographyIndex {
     this.values = new ArrayList<>(Collections.singletonList(-1)); // list of 
shape id
   }
 
-  public void add(S2Geography geog, int value) {
+  public void add(Geography geog, int value) {
     for (int i = 0; i < geog.numShapes(); i++) {
       index.add(geog.shape(i));
       int shapeId = index.getShapes().size();
diff --git 
a/common/src/main/java/org/apache/sedona/common/geometryObjects/Geography.java 
b/common/src/main/java/org/apache/sedona/common/S2Geography/GeographySerializer.java
similarity index 56%
rename from 
common/src/main/java/org/apache/sedona/common/geometryObjects/Geography.java
rename to 
common/src/main/java/org/apache/sedona/common/S2Geography/GeographySerializer.java
index f1eba5deca..bc83c75496 100644
--- 
a/common/src/main/java/org/apache/sedona/common/geometryObjects/Geography.java
+++ 
b/common/src/main/java/org/apache/sedona/common/S2Geography/GeographySerializer.java
@@ -16,22 +16,21 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sedona.common.geometryObjects;
+package org.apache.sedona.common.S2Geography;
 
-import org.locationtech.jts.geom.Geometry;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 
-public class Geography {
-  private final Geometry geometry;
-
-  public Geography(Geometry geometry) {
-    this.geometry = geometry;
-  }
-
-  public Geometry getGeometry() {
-    return this.geometry;
+public class GeographySerializer {
+  public static byte[] serialize(Geography geography) throws IOException {
+    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    geography.encodeTagged(outputStream, new EncodeOptions());
+    return outputStream.toByteArray();
   }
 
-  public String toString() {
-    return this.geometry.toText();
+  public static Geography deserialize(byte[] buffer) throws IOException {
+    ByteArrayInputStream inputStream = new ByteArrayInputStream(buffer);
+    return Geography.decodeTagged(inputStream);
   }
 }
diff --git 
a/common/src/main/java/org/apache/sedona/common/S2Geography/MultiPolygonGeography.java
 
b/common/src/main/java/org/apache/sedona/common/S2Geography/MultiPolygonGeography.java
index 99b5a5ebd0..5be5193680 100644
--- 
a/common/src/main/java/org/apache/sedona/common/S2Geography/MultiPolygonGeography.java
+++ 
b/common/src/main/java/org/apache/sedona/common/S2Geography/MultiPolygonGeography.java
@@ -42,7 +42,7 @@ public class MultiPolygonGeography extends 
GeographyCollection {
     super(Collections.emptyList());
   }
 
-  public List<S2Geography> getFeatures() {
+  public List<Geography> getFeatures() {
     return features;
   }
 
diff --git 
a/common/src/main/java/org/apache/sedona/common/S2Geography/PointGeography.java 
b/common/src/main/java/org/apache/sedona/common/S2Geography/PointGeography.java
index ab35a00223..056812fa54 100644
--- 
a/common/src/main/java/org/apache/sedona/common/S2Geography/PointGeography.java
+++ 
b/common/src/main/java/org/apache/sedona/common/S2Geography/PointGeography.java
@@ -33,7 +33,7 @@ import org.locationtech.jts.geom.impl.CoordinateArraySequence;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class PointGeography extends S2Geography {
+public class PointGeography extends Geography {
   private static final Logger logger = 
LoggerFactory.getLogger(PointGeography.class.getName());
 
   private static final int BUFFER_SIZE = 4 * 1024;
@@ -139,6 +139,7 @@ public class PointGeography extends S2Geography {
         tag.setCoveringSize((byte) 1);
         tag.encode(out);
         out.writeLong(cid.id());
+        out.writeInt(getSRID()); // write the SRID
         out.flush();
         return;
       }
@@ -148,7 +149,7 @@ public class PointGeography extends S2Geography {
   }
 
   @Override
-  protected void encode(UnsafeOutput out, EncodeOptions opts) throws 
IOException {
+  public void encode(UnsafeOutput out, EncodeOptions opts) throws IOException {
     // now the *payload* must go into its own buffer:
     ByteArrayOutputStream baos = new ByteArrayOutputStream();
     Output tmpOut = new Output(baos);
@@ -171,7 +172,6 @@ public class PointGeography extends S2Geography {
     // use writeInt(len, false) so it's exactly 4 bytes
     out.writeInt(payload.length, /* optimizePositive= */ false);
     out.writeBytes(payload);
-
     out.flush();
   }
 
diff --git 
a/common/src/main/java/org/apache/sedona/common/S2Geography/PolygonGeography.java
 
b/common/src/main/java/org/apache/sedona/common/S2Geography/PolygonGeography.java
index 66ad219aea..7ce23b6d64 100644
--- 
a/common/src/main/java/org/apache/sedona/common/S2Geography/PolygonGeography.java
+++ 
b/common/src/main/java/org/apache/sedona/common/S2Geography/PolygonGeography.java
@@ -33,7 +33,7 @@ import org.locationtech.jts.geom.CoordinateSequence;
 import org.locationtech.jts.geom.LinearRing;
 import org.locationtech.jts.geom.impl.CoordinateArraySequence;
 
-public class PolygonGeography extends S2Geography {
+public class PolygonGeography extends Geography {
   private static final Logger logger = 
Logger.getLogger(PolygonGeography.class.getName());
 
   public final S2Polygon polygon;
@@ -103,7 +103,6 @@ public class PolygonGeography extends S2Geography {
 
     S2Polygon poly = S2Polygon.decode(in);
     geo = new PolygonGeography(poly);
-
     return geo;
   }
 
diff --git 
a/common/src/main/java/org/apache/sedona/common/S2Geography/PolylineGeography.java
 
b/common/src/main/java/org/apache/sedona/common/S2Geography/PolylineGeography.java
index b57b7df664..a8bb1638f8 100644
--- 
a/common/src/main/java/org/apache/sedona/common/S2Geography/PolylineGeography.java
+++ 
b/common/src/main/java/org/apache/sedona/common/S2Geography/PolylineGeography.java
@@ -33,7 +33,7 @@ import org.locationtech.jts.geom.CoordinateSequence;
 import org.locationtech.jts.geom.impl.CoordinateArraySequence;
 
 /** A Geography representing zero or more polylines using S2Polyline. */
-public class PolylineGeography extends S2Geography {
+public class PolylineGeography extends Geography {
   private static final Logger logger = 
Logger.getLogger(PolylineGeography.class.getName());
 
   public final List<S2Polyline> polylines;
diff --git 
a/common/src/main/java/org/apache/sedona/common/S2Geography/ShapeIndexGeography.java
 
b/common/src/main/java/org/apache/sedona/common/S2Geography/ShapeIndexGeography.java
index 297b277a65..b341ccd480 100644
--- 
a/common/src/main/java/org/apache/sedona/common/S2Geography/ShapeIndexGeography.java
+++ 
b/common/src/main/java/org/apache/sedona/common/S2Geography/ShapeIndexGeography.java
@@ -25,7 +25,7 @@ import com.google.common.geometry.*;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 
-public class ShapeIndexGeography extends S2Geography {
+public class ShapeIndexGeography extends Geography {
   public S2ShapeIndex shapeIndex;
 
   /** Build an empty ShapeIndexGeography. */
@@ -35,7 +35,7 @@ public class ShapeIndexGeography extends S2Geography {
   }
 
   /** Build and immediately add one Geography. */
-  public ShapeIndexGeography(S2Geography geog) {
+  public ShapeIndexGeography(Geography geog) {
     super(GeographyKind.SHAPE_INDEX);
     this.shapeIndex = new S2ShapeIndex();
     addIndex(geog);
@@ -70,7 +70,7 @@ public class ShapeIndexGeography extends S2Geography {
     return new S2ShapeIndexRegion(shapeIndex);
   }
   /** Index every S2Shape from the given Geography. */
-  public void addIndex(S2Geography geog) {
+  public void addIndex(Geography geog) {
     for (int i = 0, n = geog.numShapes(); i < n; i++) {
       shapeIndex.add(geog.shape(i));
     }
diff --git 
a/common/src/main/java/org/apache/sedona/common/S2Geography/WKBReader.java 
b/common/src/main/java/org/apache/sedona/common/S2Geography/WKBReader.java
index 08a18028aa..01e76538c8 100644
--- a/common/src/main/java/org/apache/sedona/common/S2Geography/WKBReader.java
+++ b/common/src/main/java/org/apache/sedona/common/S2Geography/WKBReader.java
@@ -104,13 +104,14 @@ public class WKBReader {
   }
 
   /**
-   * Reads a single {@link S2Geography} in WKB format from a byte array.
+   * Reads a single {@link org.apache.sedona.common.S2Geography.Geography} in 
WKB format from a byte
+   * array.
    *
    * @param bytes the byte array to read from
    * @return the geometry read
    * @throws ParseException if the WKB is ill-formed
    */
-  public S2Geography read(byte[] bytes) throws ParseException {
+  public Geography read(byte[] bytes) throws ParseException {
     try {
       // Use a very high limit to avoid malformed input OOM
       return read(new ByteArrayInStream(bytes), Integer.MAX_VALUE);
@@ -120,19 +121,20 @@ public class WKBReader {
   }
 
   /**
-   * Reads a {@link S2Geography} in binary WKB format from an {@link InStream}.
+   * Reads a {@link org.apache.sedona.common.S2Geography.Geography} in binary 
WKB format from an
+   * {@link InStream}.
    *
    * @param is the stream to read from
    * @return the Geometry read
    * @throws IOException if the underlying stream creates an error
    * @throws ParseException if the WKB is ill-formed
    */
-  public S2Geography read(InStream is) throws IOException, ParseException {
+  public Geography read(InStream is) throws IOException, ParseException {
     // can't tell size of InStream, but MAX_VALUE should be safe
     return read(is, Integer.MAX_VALUE);
   }
 
-  private S2Geography read(InStream is, int maxCoordNum) throws IOException, 
ParseException {
+  private Geography read(InStream is, int maxCoordNum) throws IOException, 
ParseException {
     /**
      * This puts an upper bound on the allowed value in coordNum fields. It 
avoids OOM exceptions
      * due to malformed input.
@@ -151,7 +153,7 @@ public class WKBReader {
     return num;
   }
 
-  private S2Geography readGeometry(int SRID) throws IOException, 
ParseException {
+  private Geography readGeometry(int SRID) throws IOException, ParseException {
 
     // determine byte order
     byte byteOrderWKB = dis.readByte();
@@ -211,7 +213,7 @@ public class WKBReader {
     if (ordValues == null || ordValues.length < inputDimension)
       ordValues = new double[inputDimension];
 
-    S2Geography geog = null;
+    org.apache.sedona.common.S2Geography.Geography geog = null;
     switch (geometryType) {
       case WKBConstants.wkbPoint:
         geog = readPoint(ordinateFlags);
@@ -247,7 +249,8 @@ public class WKBReader {
    * @param g the geometry to update
    * @return the geometry with an updated SRID value, if required
    */
-  private S2Geography setSRID(S2Geography g, int SRID) {
+  private org.apache.sedona.common.S2Geography.Geography setSRID(
+      org.apache.sedona.common.S2Geography.Geography g, int SRID) {
     if (SRID != 0) g.setSRID(SRID);
     return g;
   }
@@ -361,7 +364,7 @@ public class WKBReader {
     int numGeom = readNumField(FIELD_NUMELEMS);
     List<S2Point> pts = new ArrayList<>(numGeom);
     for (int i = 0; i < numGeom; i++) {
-      S2Geography point = readGeometry(SRID);
+      org.apache.sedona.common.S2Geography.Geography point = 
readGeometry(SRID);
       if (!(point instanceof PointGeography)) {
         throw new ParseException(INVALID_GEOM_TYPE_MSG + "MultiPoint");
       }
@@ -374,7 +377,7 @@ public class WKBReader {
     int numGeom = readNumField(FIELD_NUMELEMS);
     List<S2Polyline> polylines = new ArrayList<>(numGeom);
     for (int i = 0; i < numGeom; i++) {
-      S2Geography polyline = readGeometry(SRID);
+      org.apache.sedona.common.S2Geography.Geography polyline = 
readGeometry(SRID);
       if (!(polyline instanceof PolylineGeography)) {
         throw new ParseException(INVALID_GEOM_TYPE_MSG + "MultiPolyline");
       }
@@ -387,7 +390,7 @@ public class WKBReader {
     int numGeom = readNumField(FIELD_NUMELEMS);
     List<S2Polygon> polygons = new ArrayList<>(numGeom);
     for (int i = 0; i < numGeom; i++) {
-      S2Geography geom = readGeometry(SRID);
+      org.apache.sedona.common.S2Geography.Geography geom = readGeometry(SRID);
       polygons.add(((PolygonGeography) geom).polygon);
     }
     return new MultiPolygonGeography(polygons);
@@ -395,7 +398,8 @@ public class WKBReader {
 
   private GeographyCollection readGeographyCollection(int SRID) throws 
IOException, ParseException {
     int numGeom = readNumField(FIELD_NUMELEMS);
-    S2Geography[] geoms = new S2Geography[numGeom];
+    org.apache.sedona.common.S2Geography.Geography[] geoms =
+        new org.apache.sedona.common.S2Geography.Geography[numGeom];
     for (int i = 0; i < numGeom; i++) {
       geoms[i] = readGeometry(SRID);
     }
diff --git 
a/common/src/main/java/org/apache/sedona/common/S2Geography/WKBWriter.java 
b/common/src/main/java/org/apache/sedona/common/S2Geography/WKBWriter.java
index bc9853e795..1de6732e46 100644
--- a/common/src/main/java/org/apache/sedona/common/S2Geography/WKBWriter.java
+++ b/common/src/main/java/org/apache/sedona/common/S2Geography/WKBWriter.java
@@ -151,12 +151,12 @@ public class WKBWriter {
   }
 
   /**
-   * Writes a {@link S2Geography} into a byte array.
+   * Writes a {@link org.apache.sedona.common.S2Geography.Geography} into a 
byte array.
    *
    * @param geog the geometry to write
    * @return the byte array containing the WKB
    */
-  public byte[] write(S2Geography geog) {
+  public byte[] write(Geography geog) {
     try {
       byteArrayOS.reset();
       write(geog, byteArrayOutStream);
@@ -167,13 +167,14 @@ public class WKBWriter {
   }
 
   /**
-   * Writes a {@link S2Geography} to an {@link OutStream}.
+   * Writes a {@link Geography} to an {@link OutStream}.
    *
-   * @param geog the geometry to write
+   * @param geogIn the geography to write
    * @param os the out stream to write to
    * @throws IOException if an I/O error occurs
    */
-  public void write(S2Geography geog, OutStream os) throws IOException {
+  public void write(Geography geogIn, OutStream os) throws IOException {
+    org.apache.sedona.common.S2Geography.Geography geog = geogIn;
     if (geog instanceof SinglePointGeography) {
       writePoint(WKBConstants.wkbPoint, (SinglePointGeography) geog, os);
     } else if (geog instanceof PointGeography) {
@@ -338,7 +339,8 @@ public class WKBWriter {
     writeGeometryType(WKBConstants.wkbMultiPolygon, multiPoly, os);
 
     // 2) Number of polygons
-    List<S2Geography> polys = multiPoly.getFeatures(); // however you expose 
each sub-polygon
+    List<org.apache.sedona.common.S2Geography.Geography> polys =
+        multiPoly.getFeatures(); // however you expose each sub-polygon
     writeInt(polys.size(), os);
 
     // 3) Disable SRID on nested shapes if you include SRID in the outer header
@@ -346,7 +348,7 @@ public class WKBWriter {
     this.includeSRID = false;
 
     // 4) For each polygon, write a full Polygon WKB
-    for (S2Geography pg : polys) {
+    for (org.apache.sedona.common.S2Geography.Geography pg : polys) {
       // 4a) Nested Polygon header
       writeByteOrder(os);
       writeGeometryType(WKBConstants.wkbPolygon, pg, os);
@@ -397,7 +399,9 @@ public class WKBWriter {
     os.write(buf, 1);
   }
 
-  private void writeGeometryType(int geometryType, S2Geography g, OutStream 
os) throws IOException {
+  private void writeGeometryType(
+      int geometryType, org.apache.sedona.common.S2Geography.Geography g, 
OutStream os)
+      throws IOException {
     int ordinals = 0;
     if (outputOrdinates.contains(Ordinate.Z)) {
       ordinals = ordinals | 0x80000000;
diff --git 
a/common/src/main/java/org/apache/sedona/common/S2Geography/WKTReader.java 
b/common/src/main/java/org/apache/sedona/common/S2Geography/WKTReader.java
index dc16092d44..63c8f03fde 100644
--- a/common/src/main/java/org/apache/sedona/common/S2Geography/WKTReader.java
+++ b/common/src/main/java/org/apache/sedona/common/S2Geography/WKTReader.java
@@ -64,6 +64,12 @@ public class WKTReader {
     this.precisionModel = geometryFactory.getPrecisionModel();
   }
 
+  public WKTReader(GeometryFactory geometryFactory) {
+    this.geometryFactory = geometryFactory;
+    this.csFactory = geometryFactory.getCoordinateSequenceFactory();
+    this.precisionModel = geometryFactory.getPrecisionModel();
+  }
+
   /**
    * Sets a flag indicating, that coordinates may have 3 ordinate values even 
though no Z or M
    * ordinate indicator is present. The default value is {@link 
#ALLOW_OLD_JTS_COORDINATE_SYNTAX}.
@@ -104,7 +110,7 @@ public class WKTReader {
    * @return a <code>Geometry</code> specified by <code>wellKnownText</code>
    * @throws ParseException if a parsing problem occurs
    */
-  public S2Geography read(String wellKnownText) throws ParseException {
+  public Geography read(String wellKnownText) throws ParseException {
     StringReader reader = new StringReader(wellKnownText);
     try {
       return read(reader);
@@ -114,14 +120,15 @@ public class WKTReader {
   }
 
   /**
-   * Reads a Well-Known Text representation of a {@link S2Geography} from a 
{@link Reader}.
+   * Reads a Well-Known Text representation of a {@link
+   * org.apache.sedona.common.S2Geography.Geography} from a {@link Reader}.
    *
    * @param reader a Reader which will return a &lt;Geometry Tagged Text&gt; 
string (see the OpenGIS
    *     Simple Features Specification)
    * @return a <code>Geometry</code> read from <code>reader</code>
    * @throws ParseException if a parsing problem occurs
    */
-  public S2Geography read(Reader reader) throws ParseException {
+  public Geography read(Reader reader) throws ParseException {
     StreamTokenizer tokenizer = createTokenizer(reader);
     try {
       return readGeometryTaggedText(tokenizer);
@@ -553,8 +560,8 @@ public class WKTReader {
    * @throws IOException if an I/O error occurs
    * @param tokenizer tokenizer over a stream of text in Well-known Text
    */
-  private S2Geography readGeometryTaggedText(StreamTokenizer tokenizer)
-      throws IOException, ParseException {
+  private org.apache.sedona.common.S2Geography.Geography 
readGeometryTaggedText(
+      StreamTokenizer tokenizer) throws IOException, ParseException {
     String type;
 
     EnumSet<Ordinate> ordinateFlags = EnumSet.of(Ordinate.X, Ordinate.Y);
@@ -570,7 +577,7 @@ public class WKTReader {
     return readGeometryTaggedText(tokenizer, type, ordinateFlags);
   }
 
-  private S2Geography readGeometryTaggedText(
+  private org.apache.sedona.common.S2Geography.Geography 
readGeometryTaggedText(
       StreamTokenizer tokenizer, String type, EnumSet<Ordinate> ordinateFlags)
       throws IOException, ParseException {
 
@@ -918,9 +925,10 @@ public class WKTReader {
     if (nextToken.equals(WKTConstants.EMPTY)) {
       return new GeographyCollection();
     }
-    List<S2Geography> geometries = new ArrayList<S2Geography>();
+    List<org.apache.sedona.common.S2Geography.Geography> geometries =
+        new ArrayList<org.apache.sedona.common.S2Geography.Geography>();
     do {
-      S2Geography geometry = readGeometryTaggedText(tokenizer);
+      org.apache.sedona.common.S2Geography.Geography geometry = 
readGeometryTaggedText(tokenizer);
       geometries.add(geometry);
       nextToken = getNextCloserOrComma(tokenizer);
     } while (nextToken.equals(COMMA));
diff --git 
a/common/src/main/java/org/apache/sedona/common/S2Geography/WKTWriter.java 
b/common/src/main/java/org/apache/sedona/common/S2Geography/WKTWriter.java
index dd5661097a..56266cd7a7 100644
--- a/common/src/main/java/org/apache/sedona/common/S2Geography/WKTWriter.java
+++ b/common/src/main/java/org/apache/sedona/common/S2Geography/WKTWriter.java
@@ -92,7 +92,7 @@ public class WKTWriter {
   }
 
   /**
-   * Creates a writer that writes {@link Geometry}s with the given output 
dimension (2 to 4). The
+   * Creates a writer that writes {@link Geography}s with the given output 
dimension (2 to 4). The
    * output follows the following rules:
    *
    * <ul>
@@ -214,14 +214,14 @@ public class WKTWriter {
   /**
    * Converts a <code>Geometry</code> to its Well-known Text representation.
    *
-   * @param geometry a <code>Geometry</code> to process
+   * @param geography a <code>Geometry</code> to process
    * @return a &lt;Geometry Tagged Text&gt; string (see the OpenGIS Simple 
Features Specification)
    */
-  public String write(S2Geography geometry) {
+  public String write(Geography geography) {
     Writer sw = new StringWriter();
 
     try {
-      writeFormatted(geometry, false, sw);
+      writeFormatted(geography, false, sw);
     } catch (IOException ex) {
       Assert.shouldNeverReachHere();
     }
@@ -229,59 +229,65 @@ public class WKTWriter {
   }
 
   /**
-   * Converts a <code>Geometry</code> to its Well-known Text representation.
+   * Converts a <code>geography</code> to its Well-known Text representation.
    *
-   * @param geometry a <code>Geometry</code> to process
+   * @param geography a <code>geography</code> to process
    */
-  public void write(S2Geography geometry, Writer writer) throws IOException {
+  public void write(Geography geography, Writer writer) throws IOException {
     // write the geometry
-    writeFormatted(geometry, isFormatted, writer);
+    writeFormatted(geography, isFormatted, writer);
   }
 
   /**
-   * Converts a <code>Geometry</code> to its Well-known Text representation.
+   * Converts a <code>geography</code> to its Well-known Text representation.
    *
-   * @param geometry a <code>Geometry</code> to process
+   * @param geography a <code>geography</code> to process
    */
-  private void writeFormatted(S2Geography geometry, boolean useFormatting, 
Writer writer)
+  private void writeFormatted(Geography geography, boolean useFormatting, 
Writer writer)
       throws IOException {
-    OrdinateFormat formatter = getFormatter(geometry);
+    OrdinateFormat formatter = getFormatter(geography);
     // append the WKT
-    appendGeometryTaggedText(geometry, useFormatting, writer, formatter);
+    appendGeometryTaggedText(geography, useFormatting, writer, formatter);
   }
 
-  private OrdinateFormat getFormatter(S2Geography geometry) {
-    // if present use the cached formatter
-    if (ordinateFormat != null) return ordinateFormat;
+  private OrdinateFormat getFormatter(Geography geography) {
+    // 1) If we’ve already created an OrdinateFormat, reuse it
+    if (this.ordinateFormat != null) {
+      return this.ordinateFormat;
+    }
 
-    // no precision model was specified, so use the geometry's
-    PrecisionModel pm = new PrecisionModel(); // geometry.getPrecisionModel();
-    OrdinateFormat formatter = createFormatter(pm);
-    return formatter;
+    PrecisionModel pm;
+    if (this.precisionModel != null) {
+      pm = this.precisionModel;
+    } else {
+      pm = new PrecisionModel();
+    }
+    this.ordinateFormat = createFormatter(pm);
+    return this.ordinateFormat;
   }
 
   /**
    * Converts a <code>Geometry</code> to &lt;Geometry Tagged Text&gt; format, 
then appends it to the
    * writer.
    *
-   * @param geometry the <code>Geometry</code> to process
+   * @param geography the <code>Geometry</code> to process
    * @param useFormatting flag indicating that the output should be formatted
    * @param writer the output writer to append to
    * @param formatter the <code>DecimalFormatter</code> to use to convert from 
a precise coordinate
    *     to an external coordinate
    */
   private void appendGeometryTaggedText(
-      S2Geography geometry, boolean useFormatting, Writer writer, 
OrdinateFormat formatter)
+      Geography geography, boolean useFormatting, Writer writer, 
OrdinateFormat formatter)
       throws IOException {
     EnumSet<Ordinate> seq = getOutputOrdinates();
     // Append the WKT
-    appendGeometryTaggedText(geometry, seq, useFormatting, 0, writer, 
formatter);
+    appendGeometryTaggedText(geography, seq, useFormatting, 0, writer, 
formatter);
   }
   /**
    * Converts a <code>Geometry</code> to &lt;Geometry Tagged Text&gt; format, 
then appends it to the
    * writer.
    *
-   * @param geometry the <code>Geometry</code> to process
+   * @param geography the <code>Geometry</code> to process
    * @param useFormatting flag indicating that the output should be formatted
    * @param level the indentation level
    * @param writer the output writer to append to
@@ -289,7 +295,7 @@ public class WKTWriter {
    *     to an external coordinate
    */
   private void appendGeometryTaggedText(
-      S2Geography geometry,
+      Geography geography,
       EnumSet<Ordinate> outputOrdinates,
       boolean useFormatting,
       int level,
@@ -300,9 +306,9 @@ public class WKTWriter {
     indent(useFormatting, level, writer);
 
     // ——— Handle Points ———
-    if (geometry instanceof SinglePointGeography) {
+    if (geography instanceof SinglePointGeography) {
       appendPointTaggedText(
-          (SinglePointGeography) geometry,
+          (SinglePointGeography) geography,
           outputOrdinates,
           useFormatting,
           level,
@@ -311,16 +317,16 @@ public class WKTWriter {
       return;
     }
 
-    if (geometry instanceof PointGeography) {
+    if (geography instanceof PointGeography) {
       appendMultiPointTaggedText(
-          (PointGeography) geometry, outputOrdinates, useFormatting, level, 
writer, formatter);
+          (PointGeography) geography, outputOrdinates, useFormatting, level, 
writer, formatter);
       return;
     }
 
     // ——— Handle LineStrings ———
-    if (geometry instanceof SinglePolylineGeography) {
+    if (geography instanceof SinglePolylineGeography) {
       appendPolylineTaggedText(
-          (SinglePolylineGeography) geometry,
+          (SinglePolylineGeography) geography,
           outputOrdinates,
           useFormatting,
           level,
@@ -329,22 +335,22 @@ public class WKTWriter {
       return;
     }
 
-    if (geometry instanceof PolylineGeography) {
+    if (geography instanceof PolylineGeography) {
       appendMultiLineStringTaggedText(
-          (PolylineGeography) geometry, outputOrdinates, useFormatting, level, 
writer, formatter);
+          (PolylineGeography) geography, outputOrdinates, useFormatting, 
level, writer, formatter);
       return;
     }
 
     // ——— Handle Polygons ———
-    if (geometry instanceof PolygonGeography) {
+    if (geography instanceof PolygonGeography) {
       appendPolygonTaggedText(
-          (PolygonGeography) geometry, outputOrdinates, useFormatting, level, 
writer, formatter);
+          (PolygonGeography) geography, outputOrdinates, useFormatting, level, 
writer, formatter);
       return;
     }
 
-    if (geometry instanceof MultiPolygonGeography) {
+    if (geography instanceof MultiPolygonGeography) {
       appendMultiPolygonTaggedText(
-          (MultiPolygonGeography) geometry,
+          (MultiPolygonGeography) geography,
           outputOrdinates,
           useFormatting,
           level,
@@ -354,13 +360,18 @@ public class WKTWriter {
     }
 
     // ——— Handle Collections ———
-    if (geometry instanceof GeographyCollection) {
+    if (geography instanceof GeographyCollection) {
       appendGeometryCollectionTaggedText(
-          (GeographyCollection) geometry, outputOrdinates, useFormatting, 
level, writer, formatter);
+          (GeographyCollection) geography,
+          outputOrdinates,
+          useFormatting,
+          level,
+          writer,
+          formatter);
       return;
     }
 
-    Assert.shouldNeverReachHere("Unsupported Geometry implementation: " + 
geometry.getClass());
+    Assert.shouldNeverReachHere("Unsupported Geometry implementation: " + 
geography.getClass());
   }
 
   /**
diff --git 
a/common/src/main/java/org/apache/sedona/common/geography/Constructors.java 
b/common/src/main/java/org/apache/sedona/common/geography/Constructors.java
new file mode 100644
index 0000000000..d5e56d4683
--- /dev/null
+++ b/common/src/main/java/org/apache/sedona/common/geography/Constructors.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sedona.common.geography;
+
+import org.apache.sedona.common.S2Geography.Geography;
+import org.apache.sedona.common.S2Geography.WKBReader;
+import org.apache.sedona.common.S2Geography.WKTReader;
+import org.locationtech.jts.io.ParseException;
+
+public class Constructors {
+
+  public static Geography geogFromWKB(byte[] wkb) throws ParseException {
+    return new WKBReader().read(wkb);
+  }
+
+  public static Geography geogFromWKB(byte[] wkb, int SRID) throws 
ParseException {
+    Geography geog = geogFromWKB(wkb);
+    geog.setSRID(SRID);
+    return geog;
+  }
+
+  public static Geography geogFromWKT(String wkt, int srid) throws 
ParseException {
+    Geography geog = new WKTReader().read(wkt);
+    geog.setSRID(srid);
+    return geog;
+  }
+
+  public static Geography geogCollFromText(String wkt, int srid) throws 
ParseException {
+    if (wkt == null || !wkt.startsWith("GEOMETRYCOLLECTION")) {
+      return null;
+    }
+    return geogFromWKT(wkt, srid);
+  }
+}
diff --git 
a/common/src/main/java/org/apache/sedona/common/geometrySerde/GeometrySerde.java
 
b/common/src/main/java/org/apache/sedona/common/geometrySerde/GeometrySerde.java
index 5475d37c7b..489efc2b4a 100644
--- 
a/common/src/main/java/org/apache/sedona/common/geometrySerde/GeometrySerde.java
+++ 
b/common/src/main/java/org/apache/sedona/common/geometrySerde/GeometrySerde.java
@@ -23,9 +23,11 @@ import com.esotericsoftware.kryo.Registration;
 import com.esotericsoftware.kryo.Serializer;
 import com.esotericsoftware.kryo.io.Input;
 import com.esotericsoftware.kryo.io.Output;
+import java.io.IOException;
 import java.io.Serializable;
+import org.apache.sedona.common.S2Geography.Geography;
+import org.apache.sedona.common.S2Geography.GeographySerializer;
 import org.apache.sedona.common.geometryObjects.Circle;
-import org.apache.sedona.common.geometryObjects.Geography;
 import org.locationtech.jts.geom.Envelope;
 import org.locationtech.jts.geom.Geometry;
 import org.locationtech.jts.geom.GeometryCollection;
@@ -66,7 +68,11 @@ public class GeometrySerde extends Serializer implements 
Serializable {
       out.writeDouble(envelope.getMaxY());
     } else if (object instanceof Geography) {
       writeType(out, Type.GEOGRAPHY);
-      writeGeometry(kryo, out, ((Geography) object).getGeometry());
+      try {
+        writeGeography(out, (Geography) object);
+      } catch (IOException e) {
+        throw new RuntimeException(e);
+      }
     } else {
       throw new UnsupportedOperationException(
           "Cannot serialize object of type " + object.getClass().getName());
@@ -84,6 +90,12 @@ public class GeometrySerde extends Serializer implements 
Serializable {
     writeUserData(kryo, out, geometry);
   }
 
+  private void writeGeography(Output out, Geography geography) throws 
IOException {
+    byte[] data = GeographySerializer.serialize(geography);
+    out.writeInt(data.length);
+    out.write(data, 0, data.length);
+  }
+
   private void writeUserData(Kryo kryo, Output out, Geometry geometry) {
     out.writeBoolean(geometry.getUserData() != null);
     if (geometry.getUserData() != null) {
@@ -124,7 +136,11 @@ public class GeometrySerde extends Serializer implements 
Serializable {
         }
       case GEOGRAPHY:
         {
-          return new Geography(readGeometry(kryo, input));
+          try {
+            return readGeography(input);
+          } catch (IOException e) {
+            throw new RuntimeException(e);
+          }
         }
       default:
         throw new UnsupportedOperationException(
@@ -150,6 +166,14 @@ public class GeometrySerde extends Serializer implements 
Serializable {
     return geometry;
   }
 
+  private Geography readGeography(Input input) throws IOException {
+    int length = input.readInt();
+    byte[] bytes = new byte[length];
+    input.readBytes(bytes);
+    Geography geography = GeographySerializer.deserialize(bytes);
+    return geography;
+  }
+
   private enum Type {
     SHAPE(0),
     CIRCLE(1),
diff --git 
a/common/src/test/java/org/apache/sedona/common/S2Geography/DistanceTest.java 
b/common/src/test/java/org/apache/sedona/common/S2Geography/DistanceTest.java
index c8bff4e798..25e1bd37fd 100644
--- 
a/common/src/test/java/org/apache/sedona/common/S2Geography/DistanceTest.java
+++ 
b/common/src/test/java/org/apache/sedona/common/S2Geography/DistanceTest.java
@@ -32,8 +32,8 @@ public class DistanceTest {
   @Test
   public void pointDistance() throws Exception {
     // 1) Build two points
-    S2Geography pt1 = new PointGeography(S2LatLng.fromDegrees(0, 0).toPoint());
-    S2Geography pt2 = new PointGeography(S2LatLng.fromDegrees(90, 
0).toPoint());
+    Geography pt1 = new PointGeography(S2LatLng.fromDegrees(0, 0).toPoint());
+    Geography pt2 = new PointGeography(S2LatLng.fromDegrees(90, 0).toPoint());
     ShapeIndexGeography geo1 = new ShapeIndexGeography(pt1);
     ShapeIndexGeography geo2 = new ShapeIndexGeography(pt2);
 
@@ -45,8 +45,8 @@ public class DistanceTest {
   @Test
   public void furtestDistance() throws Exception {
     // 1) Build two points
-    S2Geography pt1 = new PointGeography(S2LatLng.fromDegrees(0, 0).toPoint());
-    S2Geography pt2 = new PointGeography(S2LatLng.fromDegrees(90, 
0).toPoint());
+    Geography pt1 = new PointGeography(S2LatLng.fromDegrees(0, 0).toPoint());
+    Geography pt2 = new PointGeography(S2LatLng.fromDegrees(90, 0).toPoint());
     ShapeIndexGeography geo1 = new ShapeIndexGeography(pt1);
     ShapeIndexGeography geo2 = new ShapeIndexGeography(pt2);
 
diff --git 
a/common/src/test/java/org/apache/sedona/common/S2Geography/GeographyCollectionTest.java
 
b/common/src/test/java/org/apache/sedona/common/S2Geography/GeographyCollectionTest.java
index 927d5ee110..98e106f0a3 100644
--- 
a/common/src/test/java/org/apache/sedona/common/S2Geography/GeographyCollectionTest.java
+++ 
b/common/src/test/java/org/apache/sedona/common/S2Geography/GeographyCollectionTest.java
@@ -45,7 +45,7 @@ public class GeographyCollectionTest {
     // Single point geography
     S2Point p = S2LatLng.fromDegrees(10, 20).toPoint();
     PointGeography pointGeo = new PointGeography(Arrays.asList(p));
-    List<S2Geography> features = Arrays.<S2Geography>asList(pointGeo);
+    List<Geography> features = Arrays.<Geography>asList(pointGeo);
     GeographyCollection coll = new GeographyCollection(features);
 
     assertEquals("Collection numShapes", 1, coll.numShapes());
@@ -74,7 +74,7 @@ public class GeographyCollectionTest {
     S2Polyline polyline = new S2Polyline(linePts);
     PolylineGeography polyGeo = new PolylineGeography(polyline);
 
-    List<S2Geography> features = Arrays.<S2Geography>asList(pg1, pg2, polyGeo);
+    List<Geography> features = Arrays.<Geography>asList(pg1, pg2, polyGeo);
     GeographyCollection coll = new GeographyCollection(features);
 
     assertEquals("Collection numShapes", 3, coll.numShapes());
@@ -90,7 +90,7 @@ public class GeographyCollectionTest {
     S2Point p2 = S2LatLng.fromDegrees(30, 40).toPoint();
     PointGeography pg1 = new PointGeography(Arrays.asList(p1));
     PointGeography pg2 = new PointGeography(Arrays.asList(p2));
-    GeographyCollection original = new 
GeographyCollection(List.<S2Geography>of(pg1, pg2));
+    GeographyCollection original = new 
GeographyCollection(List.<Geography>of(pg1, pg2));
 
     // encode/decode round trip via TestHelper
     EncodeOptions opts = new EncodeOptions();
@@ -104,7 +104,7 @@ public class GeographyCollectionTest {
     S2Point b = S2LatLng.fromDegrees(10, 10).toPoint();
     PointGeography pg1 = new PointGeography(List.of(a));
     PointGeography pg2 = new PointGeography(List.of(b));
-    GeographyCollection coll = new 
GeographyCollection(Arrays.<S2Geography>asList(pg1, pg2));
+    GeographyCollection coll = new 
GeographyCollection(Arrays.<Geography>asList(pg1, pg2));
 
     List<S2CellId> cover = new ArrayList<>();
     coll.getCellUnionBound(cover);
diff --git 
a/common/src/test/java/org/apache/sedona/common/S2Geography/PointGeographyTest.java
 
b/common/src/test/java/org/apache/sedona/common/S2Geography/PointGeographyTest.java
index 49173417e8..1d01248d41 100644
--- 
a/common/src/test/java/org/apache/sedona/common/S2Geography/PointGeographyTest.java
+++ 
b/common/src/test/java/org/apache/sedona/common/S2Geography/PointGeographyTest.java
@@ -32,7 +32,7 @@ public class PointGeographyTest {
   public void testEncodeTag() throws IOException {
     // 1) Create an empty geography
     PointGeography geog = new PointGeography();
-    assertEquals(S2Geography.GeographyKind.POINT, geog.kind);
+    assertEquals(Geography.GeographyKind.POINT, geog.kind);
     assertEquals(0, geog.numShapes());
     // Java returns -1 for no shapes; if yours returns 0, adjust accordingly
     assertEquals(-1, geog.dimension());
@@ -42,7 +42,7 @@ public class PointGeographyTest {
     ByteArrayOutputStream baos = new ByteArrayOutputStream();
     geog.encodeTagged(baos, new EncodeOptions());
     byte[] data = baos.toByteArray();
-    assertEquals(4, data.length);
+    assertEquals(8, data.length);
 
     // 2) Create a single-point geography at lat=45°, lng=-64°
     S2Point pt = S2LatLng.fromDegrees(45, -64).toPoint();
diff --git 
a/common/src/test/java/org/apache/sedona/common/S2Geography/ShapeIndexGeographyTest.java
 
b/common/src/test/java/org/apache/sedona/common/S2Geography/ShapeIndexGeographyTest.java
index e5409ea454..82a4536662 100644
--- 
a/common/src/test/java/org/apache/sedona/common/S2Geography/ShapeIndexGeographyTest.java
+++ 
b/common/src/test/java/org/apache/sedona/common/S2Geography/ShapeIndexGeographyTest.java
@@ -55,10 +55,10 @@ public class ShapeIndexGeographyTest {
 
     byte[] data = baos.toByteArray();
     ByteArrayInputStream in = new ByteArrayInputStream(data);
-    S2Geography roundtrip = geog.decodeTagged(in);
+    Geography roundtrip = geog.decodeTagged(in);
 
     // 6) Verify the kind and count
-    assertEquals(S2Geography.GeographyKind.ENCODED_SHAPE_INDEX, 
roundtrip.kind);
+    assertEquals(Geography.GeographyKind.ENCODED_SHAPE_INDEX, roundtrip.kind);
     assertEquals(3, roundtrip.numShapes());
 
     // 6) Cast back to shape-index geography
diff --git 
a/common/src/test/java/org/apache/sedona/common/S2Geography/TestHelper.java 
b/common/src/test/java/org/apache/sedona/common/S2Geography/TestHelper.java
index eb8eae88fc..bc75e8a27a 100644
--- a/common/src/test/java/org/apache/sedona/common/S2Geography/TestHelper.java
+++ b/common/src/test/java/org/apache/sedona/common/S2Geography/TestHelper.java
@@ -34,7 +34,13 @@ public class TestHelper {
 
   private static final double EPS = 1e-6;
 
-  public static void assertRoundTrip(S2Geography original, EncodeOptions opts) 
throws IOException {
+  public static void assertRoundTrip(Geography original, EncodeOptions opts) 
throws IOException {
+    int srid = original.getSRID();
+    assertTrue("SRID must be non-negative", srid >= 0);
+    if (srid == 0) {
+      // If SRID is not set, we set it to a default value for testing purposes
+      original.setSRID(4326);
+    }
     // 1) Encode to bytes
     ByteArrayOutputStream baos = new ByteArrayOutputStream();
     original.encodeTagged(baos, opts);
@@ -42,7 +48,10 @@ public class TestHelper {
 
     // 2) Decode back
     ByteArrayInputStream in = new ByteArrayInputStream(data);
-    S2Geography decoded = original.decodeTagged(in);
+    Geography decoded = Geography.decodeTagged(in);
+
+    assertEquals(original.getSRID(), decoded.getSRID()); // Ensure SRID matches
+    original.setSRID(srid); // Restore original SRID
 
     // 3) Compare kind, shapes, dimension
     assertEquals("Kind should round-trip", original.kind, decoded.kind);
@@ -104,7 +113,7 @@ public class TestHelper {
    * Asserts that the EncodeTag for the given geography honors the 
includeCovering option; if
    * includeCovering==true, coveringSize should be >0, otherwise it must be 
zero.
    */
-  public static void assertCovering(S2Geography original, EncodeOptions opts) 
throws IOException {
+  public static void assertCovering(Geography original, EncodeOptions opts) 
throws IOException {
     // encode and read only the tag
     ByteArrayOutputStream baos = new ByteArrayOutputStream();
     original.encodeTagged(baos, opts);
@@ -156,10 +165,10 @@ public class TestHelper {
   public static void checkWKBGeography(String wkbHex, String expectedWKT) 
throws ParseException {
     WKBReader wkbReader = new WKBReader();
     byte[] wkb = WKBReader.hexToBytes(wkbHex);
-    S2Geography geoWKB = wkbReader.read(wkb);
+    Geography geoWKB = wkbReader.read(wkb);
 
     WKTReader wktReader = new WKTReader();
-    S2Geography geoWKT = wktReader.read(expectedWKT);
+    Geography geoWKT = wktReader.read(expectedWKT);
 
     boolean isEqual = compareTo(geoWKT, geoWKT) == 0;
     if (!isEqual) {
@@ -169,7 +178,7 @@ public class TestHelper {
     assertTrue(isEqual);
   }
 
-  public static int compareTo(S2Geography geo1, S2Geography geo2) {
+  public static int compareTo(Geography geo1, Geography geo2) {
     int compare = geo1.kind.getKind() - geo2.kind.getKind();
     if (compare != 0) {
       return compare;
@@ -207,8 +216,8 @@ public class TestHelper {
       if (S2_isEmpty(geo1) && S2_isEmpty(geo2)) return 0;
       assertEquals(geo1.numShapes(), geo2.numShapes());
       for (int i = 0; i < geo1.numShapes(); i++) {
-        S2Geography g1 = (S2Geography) ((GeographyCollection) 
geo1).features.get(i);
-        S2Geography g2 = (S2Geography) ((GeographyCollection) 
geo2).features.get(i);
+        Geography g1 = (Geography) ((GeographyCollection) 
geo1).features.get(i);
+        Geography g2 = (Geography) ((GeographyCollection) 
geo2).features.get(i);
         compareTo(g1, g2);
       }
     }
diff --git 
a/common/src/test/java/org/apache/sedona/common/S2Geography/WKBReaderTest.java 
b/common/src/test/java/org/apache/sedona/common/S2Geography/WKBReaderTest.java
index 85313bf825..400e835f36 100644
--- 
a/common/src/test/java/org/apache/sedona/common/S2Geography/WKBReaderTest.java
+++ 
b/common/src/test/java/org/apache/sedona/common/S2Geography/WKBReaderTest.java
@@ -56,10 +56,10 @@ public class WKBReaderTest {
         };
 
     WKBReader reader = new WKBReader();
-    S2Geography geo = reader.read(wkb);
+    Geography geo = reader.read(wkb);
 
     // Kind should be POINT
-    Assert.assertEquals(S2Geography.GeographyKind.SINGLEPOINT, geo.kind);
+    Assert.assertEquals(Geography.GeographyKind.SINGLEPOINT, geo.kind);
 
     // Extract the S2Point
     S2Point p = geo.shape(0).chain(0).get(0);
@@ -123,10 +123,10 @@ public class WKBReaderTest {
         };
 
     WKBReader reader = new WKBReader();
-    S2Geography geo = reader.read(wkb);
+    Geography geo = reader.read(wkb);
 
     // Expect a POLYLINE geometry
-    Assert.assertEquals(S2Geography.GeographyKind.SINGLEPOLYLINE, geo.kind);
+    Assert.assertEquals(Geography.GeographyKind.SINGLEPOLYLINE, geo.kind);
 
     // Extract the two S2Points
     List<S2Point> pts = geo.shape(0).chain(0);
@@ -321,10 +321,10 @@ public class WKBReaderTest {
         };
 
     WKBReader reader = new WKBReader();
-    S2Geography geo = reader.read(wkb);
+    Geography geo = reader.read(wkb);
 
     // Expect a POLYGON geometry
-    Assert.assertEquals(S2Geography.GeographyKind.POLYGON, geo.kind);
+    Assert.assertEquals(Geography.GeographyKind.POLYGON, geo.kind);
 
     // Outer ring: 4 points
     List<S2Point> outer = geo.shape(0).chain(0);
diff --git 
a/common/src/test/java/org/apache/sedona/common/S2Geography/WKBWriterTest.java 
b/common/src/test/java/org/apache/sedona/common/S2Geography/WKBWriterTest.java
index 48b43d8a74..2f6cb85024 100644
--- 
a/common/src/test/java/org/apache/sedona/common/S2Geography/WKBWriterTest.java
+++ 
b/common/src/test/java/org/apache/sedona/common/S2Geography/WKBWriterTest.java
@@ -36,7 +36,7 @@ public class WKBWriterTest {
   public void PointGeographyToHexTest() throws IOException, ParseException {
     // 1) create an S2Point at (lat=10, lon=30)
     S2Point s2Pt = S2LatLng.fromDegrees(10.0, 30.0).toPoint();
-    SinglePointGeography inputGeo = new SinglePointGeography(s2Pt);
+    Geography inputGeo = new SinglePointGeography(s2Pt);
 
     // 2) write it to 2D little‐endian WKB
     WKBWriter writer = new WKBWriter(2, ByteOrderValues.LITTLE_ENDIAN);
@@ -60,7 +60,7 @@ public class WKBWriterTest {
     List<S2Point> pts =
         List.of(
             S2LatLng.fromDegrees(10.0, 30.0).toPoint(), 
S2LatLng.fromDegrees(42.0, 12.0).toPoint());
-    SinglePolylineGeography inputLine = new SinglePolylineGeography(new 
S2Polyline(pts));
+    Geography inputLine = new SinglePolylineGeography(new S2Polyline(pts));
 
     // 2) write it to 2D little‐endian WKB
     WKBWriter writer = new WKBWriter(2, ByteOrderValues.LITTLE_ENDIAN);
@@ -89,12 +89,12 @@ public class WKBWriterTest {
   public void MultiPointTest() throws ParseException, IOException {
     String wkt = "MULTIPOINT ((10 40), (40 30))";
     WKTReader reader = new WKTReader();
-    S2Geography geo = reader.read(wkt);
+    Geography geo = reader.read(wkt);
 
     WKBWriter writer = new WKBWriter(2, ByteOrderValues.LITTLE_ENDIAN);
     byte[] wkb = writer.write(geo);
     WKBReader readerWKB = new WKBReader();
-    S2Geography geoWKB = readerWKB.read(wkb);
+    Geography geoWKB = readerWKB.read(wkb);
     assertEquals(0, TestHelper.compareTo(geo, geoWKB));
   }
 
@@ -102,12 +102,12 @@ public class WKBWriterTest {
   public void MultiPolylineTest() throws ParseException, IOException {
     String wkt = "MULTILINESTRING((0 1,2 3),(4 5,6 7))";
     WKTReader reader = new WKTReader();
-    S2Geography geo = reader.read(wkt);
+    Geography geo = reader.read(wkt);
 
     WKBWriter writer = new WKBWriter(2, ByteOrderValues.LITTLE_ENDIAN);
     byte[] wkb = writer.write(geo);
     WKBReader readerWKB = new WKBReader();
-    S2Geography geoWKB = readerWKB.read(wkb);
+    Geography geoWKB = readerWKB.read(wkb);
     assertEquals(0, TestHelper.compareTo(geo, geoWKB));
   }
 
@@ -116,12 +116,12 @@ public class WKBWriterTest {
     String wkt =
         "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0),(1 1,1 9,9 9,9 1,1 1)),((-9 
0,-9 10,-1 10,-1 0,-9 0)))";
     WKTReader reader = new WKTReader();
-    S2Geography geo = reader.read(wkt);
+    Geography geo = reader.read(wkt);
 
     WKBWriter writer = new WKBWriter(2, ByteOrderValues.LITTLE_ENDIAN);
     byte[] wkb = writer.write(geo);
     WKBReader readerWKB = new WKBReader();
-    S2Geography geoWKB = readerWKB.read(wkb);
+    Geography geoWKB = readerWKB.read(wkb);
     assertEquals(0, TestHelper.compareTo(geo, geoWKB));
   }
 
@@ -131,12 +131,11 @@ public class WKBWriterTest {
         "GEOMETRYCOLLECTION(POINT(0 1),POINT(0 1),POINT(2 3),LINESTRING(2 3,4 
5),LINESTRING(0 1,2 3),LINESTRING(4 5,6 7),POLYGON((0 0,0 10,10 10,10 0,0 0),(1 
1,1 9,9 9,9 1,1 1)),POLYGON((0 0,0 10,10 10,10 0,0 0),(1 1,1 9,9 9,9 1,1 
1)),POLYGON((-9 0,-9 10,-1 10,-1 0,-9 0)))";
 
     WKTReader reader = new WKTReader();
-    S2Geography geo = reader.read(wkt);
-
+    Geography geo = reader.read(wkt);
     WKBWriter writer = new WKBWriter(2, ByteOrderValues.LITTLE_ENDIAN);
     byte[] wkb = writer.write(geo);
     WKBReader readerWKB = new WKBReader();
-    S2Geography geoWKB = readerWKB.read(wkb);
+    Geography geoWKB = readerWKB.read(wkb);
     assertEquals(0, TestHelper.compareTo(geo, geoWKB));
   }
 
@@ -145,12 +144,12 @@ public class WKBWriterTest {
     String wkt = "POINT EMPTY";
 
     WKTReader reader = new WKTReader();
-    S2Geography geo = reader.read(wkt);
+    Geography geo = reader.read(wkt);
 
     WKBWriter writer = new WKBWriter(2, ByteOrderValues.LITTLE_ENDIAN);
     byte[] wkb = writer.write(geo);
     WKBReader readerWKB = new WKBReader();
-    S2Geography geoWKB = readerWKB.read(wkb);
+    Geography geoWKB = readerWKB.read(wkb);
     assertEquals(0, TestHelper.compareTo(geo, geoWKB));
   }
 }
diff --git 
a/common/src/test/java/org/apache/sedona/common/S2Geography/WKTReadWriterTest.java
 
b/common/src/test/java/org/apache/sedona/common/S2Geography/WKTReadWriterTest.java
index 33291036d5..ab11afc5e0 100644
--- 
a/common/src/test/java/org/apache/sedona/common/S2Geography/WKTReadWriterTest.java
+++ 
b/common/src/test/java/org/apache/sedona/common/S2Geography/WKTReadWriterTest.java
@@ -28,7 +28,7 @@ public class WKTReadWriterTest {
 
   private final WKTReader reader = new WKTReader() {};
 
-  private String writeWithPrecision(S2Geography geom, PrecisionModel pm) {
+  private String writeWithPrecision(Geography geom, PrecisionModel pm) {
     WKTWriter w = new WKTWriter();
     w.setPrecisionModel(pm);
     return w.write(geom);
@@ -36,7 +36,7 @@ public class WKTReadWriterTest {
 
   @Test
   public void significantDigits_defaultAndSingle() throws ParseException {
-    S2Geography geog = reader.read("POINT (0 3.333333333333334)");
+    Geography geog = reader.read("POINT (0 3.333333333333334)");
     String wkt = new WKTWriter().write(geog);
     // default ≈16 digits
     assertEquals("POINT (0 3.3333333333333344)", wkt);
@@ -50,14 +50,14 @@ public class WKTReadWriterTest {
   @Test
   public void lineString_roundTrip() throws ParseException {
     String wkt = "LINESTRING (30 10, 10 30, 40 40)";
-    S2Geography g = reader.read(wkt);
+    Geography g = reader.read(wkt);
     assertEquals(wkt, writeWithPrecision(g, new 
PrecisionModel(PrecisionModel.FIXED)));
   }
 
   @Test
   public void multilineString_roundTrip() throws ParseException {
     String wkt = "MULTILINESTRING ((30 10, 10 30, 40 40), (30 10, 20 30, 40 
40))";
-    S2Geography g = reader.read(wkt);
+    Geography g = reader.read(wkt);
     assertEquals(wkt, writeWithPrecision(g, new 
PrecisionModel(PrecisionModel.FIXED)));
   }
 
@@ -71,7 +71,7 @@ public class WKTReadWriterTest {
         "POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10), " + "(20 30, 35 35, 30 
20, 20 30))";
     String expected =
         "POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10), " + "(30 20, 35 35, 20 
30, 30 20))";
-    PolygonGeography sp = (PolygonGeography) reader.read(withHole);
+    Geography sp = reader.read(withHole);
     assertEquals(
         expected,
         writeWithPrecision(reader.read(withHole), new 
PrecisionModel(PrecisionModel.FIXED)));
diff --git a/python/tests/sql/test_dataframe_api.py 
b/python/tests/sql/test_dataframe_api.py
index 4c37b5a23e..9751803d7e 100644
--- a/python/tests/sql/test_dataframe_api.py
+++ b/python/tests/sql/test_dataframe_api.py
@@ -83,8 +83,8 @@ test_configurations = [
     (stc.ST_GeomFromWKT, ("wkt",), "linestring_wkt", "", "LINESTRING (1 2, 3 
4)"),
     (stc.ST_GeomFromWKT, ("wkt", 4326), "linestring_wkt", "", "LINESTRING (1 
2, 3 4)"),
     (stc.ST_GeomFromEWKT, ("ewkt",), "linestring_ewkt", "", "LINESTRING (1 2, 
3 4)"),
-    (stc.ST_GeogFromWKT, ("wkt",), "linestring_wkt", "", "LINESTRING (1 2, 3 
4)"),
-    (stc.ST_GeogFromWKT, ("wkt", 4326), "linestring_wkt", "", "LINESTRING (1 
2, 3 4)"),
+    # (stc.ST_GeogFromWKT, ("wkt",), "linestring_wkt", "", "LINESTRING (1 2, 3 
4)"),
+    # (stc.ST_GeogFromWKT, ("wkt", 4326), "linestring_wkt", "", "LINESTRING (1 
2, 3 4)"),
     (stc.ST_LineFromText, ("wkt",), "linestring_wkt", "", "LINESTRING (1 2, 3 
4)"),
     (
         stc.ST_LineFromWKB,
@@ -1238,7 +1238,7 @@ wrong_type_configurations = [
     (stc.ST_LinestringFromWKB, (None,)),
     (stc.ST_GeomFromEWKB, (None,)),
     (stc.ST_GeomFromWKT, (None,)),
-    (stc.ST_GeogFromWKT, (None,)),
+    # (stc.ST_GeogFromWKT, (None,)),
     (stc.ST_GeometryFromText, (None,)),
     (stc.ST_LineFromText, (None,)),
     (stc.ST_LineStringFromText, (None, "")),
@@ -1722,7 +1722,7 @@ class TestDataFrameAPI(TestBase):
             self.assert_geometry_almost_equal(expected_result, actual_result)
             return
         elif isinstance(actual_result, Geography):
-            self.assert_geometry_almost_equal(expected_result, 
actual_result.geometry)
+            # self.assert_geometry_almost_equal(expected_result, 
actual_result.geometry)
             return
         elif isinstance(actual_result, bytearray):
             actual_result = actual_result.hex()
diff --git a/python/tests/sql/test_geography.py 
b/python/tests/sql/test_geography.py
index 4bdd4c3916..0901c98d50 100644
--- a/python/tests/sql/test_geography.py
+++ b/python/tests/sql/test_geography.py
@@ -15,11 +15,11 @@
 # specific language governing permissions and limitations
 # under the License.
 
-from pyspark.sql.functions import expr
-from pyspark.sql.types import StructType
-from shapely.wkt import loads as wkt_loads
-from sedona.spark.core.geom.geography import Geography
-from sedona.spark.sql.types import GeographyType
+# from pyspark.sql.functions import expr
+# from pyspark.sql.types import StructType
+# from shapely.wkt import loads as wkt_loads
+# from sedona.spark.core.geom.geography import Geography
+# from sedona.spark.sql.types import GeographyType
 from tests.test_base import TestBase
 
 
@@ -27,19 +27,19 @@ class TestGeography(TestBase):
 
     def test_deserialize_geography(self):
         """Test serialization and deserialization of Geography objects"""
-        geog_df = self.spark.range(0, 10).withColumn(
-            "geog", expr("ST_GeogFromWKT(CONCAT('POINT (', id, ' ', id + 1, 
')'))")
-        )
-        rows = geog_df.collect()
-        assert len(rows) == 10
-        for row in rows:
-            id = row["id"]
-            geog = row["geog"]
-            assert geog.geometry.wkt == f"POINT ({id} {id + 1})"
+        # geog_df = self.spark.range(0, 10).withColumn(
+        #     "geog", expr("ST_GeogFromWKT(CONCAT('POINT (', id, ' ', id + 1, 
')'))")
+        # )
+        # rows = geog_df.collect()
+        # assert len(rows) == 10
+        # for row in rows:
+        #     id = row["id"]
+        #     geog = row["geog"]
+        #     assert geog.geometry.wkt == f"POINT ({id} {id + 1})"
 
     def test_serialize_geography(self):
         wkt = "MULTIPOLYGON (((10 10, 20 20, 20 10, 10 10)), ((-10 -10, -20 
-20, -20 -10, -10 -10)))"
-        geog = Geography(wkt_loads(wkt))
-        schema = StructType().add("geog", GeographyType())
-        returned_geog = self.spark.createDataFrame([(geog,)], 
schema).take(1)[0][0]
-        assert geog.geometry.equals(returned_geog.geometry)
+        # geog = Geography(wkt_loads(wkt))
+        # schema = StructType().add("geog", GeographyType())
+        # returned_geog = self.spark.createDataFrame([(geog,)], 
schema).take(1)[0][0]
+        # assert geog.geometry.equals(returned_geog.geometry)
diff --git 
a/spark/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala 
b/spark/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
index cf65addcba..fea04a6a04 100644
--- a/spark/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
+++ b/spark/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
@@ -22,6 +22,7 @@ import org.apache.spark.sql.expressions.Aggregator
 import org.apache.spark.sql.sedona_sql.expressions.collect.ST_Collect
 import org.apache.spark.sql.sedona_sql.expressions.raster._
 import org.apache.spark.sql.sedona_sql.expressions._
+import 
org.apache.spark.sql.sedona_sql.expressions.geography.{ST_GeogCollFromText, 
ST_GeogFromText, ST_GeogFromWKB, ST_GeogFromWKT}
 import org.locationtech.jts.geom.Geometry
 import org.locationtech.jts.operation.buffer.BufferParameters
 
@@ -43,6 +44,8 @@ object Catalog extends AbstractCatalog {
     function[ST_GeometryFromText](0),
     function[ST_LineFromText](),
     function[ST_GeogFromWKT](0),
+    function[ST_GeogFromText](0),
+    function[ST_GeogFromWKB](0),
     function[ST_GeomFromWKT](0),
     function[ST_GeomFromEWKT](),
     function[ST_GeomFromWKB](),
@@ -208,6 +211,7 @@ object Catalog extends AbstractCatalog {
     function[ST_MPolyFromText](0),
     function[ST_MLineFromText](0),
     function[ST_GeomCollFromText](0),
+    function[ST_GeogCollFromText](0),
     function[ST_Split](),
     function[ST_S2CellIDs](),
     function[ST_S2ToGeom](),
diff --git 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/UDT/GeographyUDT.scala
 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/UDT/GeographyUDT.scala
index f9eea7f98c..d4d4fb8898 100644
--- 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/UDT/GeographyUDT.scala
+++ 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/UDT/GeographyUDT.scala
@@ -18,12 +18,10 @@
  */
 package org.apache.spark.sql.sedona_sql.UDT
 
-import org.apache.sedona.common.geometrySerde.GeometrySerializer;
-import org.apache.spark.sql.catalyst.util.{ArrayData, GenericArrayData}
 import org.apache.spark.sql.types._
 import org.json4s.JsonDSL._
 import org.json4s.JsonAST.JValue
-import org.apache.sedona.common.geometryObjects.Geography;
+import org.apache.sedona.common.S2Geography.{GeographySerializer, Geography}
 
 class GeographyUDT extends UserDefinedType[Geography] {
   override def sqlType: DataType = BinaryType
@@ -33,11 +31,11 @@ class GeographyUDT extends UserDefinedType[Geography] {
   override def userClass: Class[Geography] = classOf[Geography]
 
   override def serialize(obj: Geography): Array[Byte] =
-    GeometrySerializer.serialize(obj.getGeometry)
+    GeographySerializer.serialize(obj)
 
   override def deserialize(datum: Any): Geography = {
     datum match {
-      case value: Array[Byte] => new 
Geography(GeometrySerializer.deserialize(value))
+      case value: Array[Byte] => GeographySerializer.deserialize(value)
     }
   }
 
diff --git 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/UDT/UdtRegistratorWrapper.scala
 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/UDT/UdtRegistratorWrapper.scala
index e581625556..2a9b0b397c 100644
--- 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/UDT/UdtRegistratorWrapper.scala
+++ 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/UDT/UdtRegistratorWrapper.scala
@@ -18,9 +18,9 @@
  */
 package org.apache.spark.sql.sedona_sql.UDT
 
+import org.apache.sedona.common.S2Geography.Geography
 import org.apache.spark.sql.types.UDTRegistration
 import org.locationtech.jts.geom.Geometry
-import org.apache.sedona.common.geometryObjects.Geography;
 import org.locationtech.jts.index.SpatialIndex
 
 object UdtRegistratorWrapper {
diff --git 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Constructors.scala
 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Constructors.scala
index cd8ece23a6..98e1636b34 100644
--- 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Constructors.scala
+++ 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Constructors.scala
@@ -98,20 +98,6 @@ private[apache] case class ST_GeomFromWKT(inputExpressions: 
Seq[Expression])
   }
 }
 
-/**
- * Return a Geography from a WKT string
- *
- * @param inputExpressions
- *   This function takes a geometry string and a srid. The string format must 
be WKT.
- */
-private[apache] case class ST_GeogFromWKT(inputExpressions: Seq[Expression])
-    extends InferredExpression(Constructors.geogFromWKT _) {
-
-  protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = 
{
-    copy(inputExpressions = newChildren)
-  }
-}
-
 /**
  * Return a Geometry from a OGC Extended WKT string
  *
diff --git 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
index 5af10573b8..d41140d6cc 100644
--- 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
+++ 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
@@ -18,7 +18,6 @@
  */
 package org.apache.spark.sql.sedona_sql.expressions
 
-import org.apache.sedona.common.geometryObjects.Geography
 import org.apache.sedona.common.{Functions, FunctionsGeoTools}
 import org.apache.sedona.common.sphere.{Haversine, Spheroid}
 import org.apache.sedona.common.utils.{InscribedCircle, ValidDetail}
@@ -511,10 +510,8 @@ private[apache] case class ST_AsBinary(inputExpressions: 
Seq[Expression])
 }
 
 private[apache] case class ST_AsEWKB(inputExpressions: Seq[Expression])
-    extends InferredExpression(
-      (geom: Geometry) => Functions.asEWKB(geom),
-      (geog: Geography) => Functions.asEWKB(geog)) {
-
+    extends InferredExpression((geom: Geometry) => Functions.asEWKB(geom)) {
+  // (geog: Geography) => Functions.asEWKB(geog)
   protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = 
{
     copy(inputExpressions = newChildren)
   }
@@ -1278,10 +1275,8 @@ private[apache] case class ST_Force2D(inputExpressions: 
Seq[Expression])
  * @param inputExpressions
  */
 private[apache] case class ST_AsEWKT(inputExpressions: Seq[Expression])
-    extends InferredExpression(
-      (geom: Geometry) => Functions.asEWKT(geom),
-      (geog: Geography) => Functions.asEWKT(geog)) {
-
+    extends InferredExpression((geom: Geometry) => Functions.asEWKT(geom)) {
+  // (geog: Geography) => Functions.asEWKT(geog)
   protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = 
{
     copy(inputExpressions = newChildren)
   }
diff --git 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/InferredExpression.scala
 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/InferredExpression.scala
index 68290b6983..606dcbb931 100644
--- 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/InferredExpression.scala
+++ 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/InferredExpression.scala
@@ -19,22 +19,20 @@
 package org.apache.spark.sql.sedona_sql.expressions
 
 import org.apache.commons.lang3.StringUtils
+import org.apache.sedona.common.S2Geography.Geography
 import org.apache.spark.sql.catalyst.InternalRow
-import org.apache.spark.sql.catalyst.expressions.{Expression, 
ImplicitCastInputTypes}
 import org.apache.spark.sql.catalyst.expressions.codegen.CodegenFallback
+import org.apache.spark.sql.catalyst.expressions.{Expression, 
ImplicitCastInputTypes}
 import org.apache.spark.sql.catalyst.util.ArrayData
-import org.apache.spark.sql.sedona_sql.UDT.{GeometryUDT, GeographyUDT}
-import org.apache.spark.sql.types.{AbstractDataType, BinaryType, BooleanType, 
DataType, DataTypes, DoubleType, IntegerType, LongType, StringType}
+import org.apache.spark.sql.sedona_sql.UDT.{GeographyUDT, GeometryUDT}
+import org.apache.spark.sql.sedona_sql.expressions.implicits._
+import org.apache.spark.sql.types._
 import org.apache.spark.unsafe.types.UTF8String
 import org.locationtech.jts.geom.Geometry
-import org.apache.spark.sql.sedona_sql.expressions.implicits._
 
 import scala.collection.convert.ImplicitConversions.`collection 
AsScalaIterable`
 import scala.collection.mutable.ArrayBuffer
-import scala.reflect.runtime.universe.TypeTag
-import scala.reflect.runtime.universe.Type
-import scala.reflect.runtime.universe.typeOf
-import org.apache.sedona.common.geometryObjects.Geography
+import scala.reflect.runtime.universe.{Type, TypeTag, typeOf}
 
 /**
  * Custom exception to include the input row and the original exception 
message.
@@ -200,16 +198,20 @@ object InferrableType {
     new InferrableType[java.util.List[java.lang.Double]] {}
   implicit val javaGeomListInstance: InferrableType[java.util.List[Geometry]] =
     new InferrableType[java.util.List[Geometry]] {}
+  implicit val javaGeogListInstance: InferrableType[java.util.List[Geography]] 
=
+    new InferrableType[java.util.List[Geography]] {}
 }
 
 object InferredTypes {
   def buildArgumentExtractor(t: Type): Expression => InternalRow => Any = {
     if (t =:= typeOf[Geometry]) { expr => input =>
       expr.toGeometry(input)
-    } else if (t =:= typeOf[Geography]) { expr => input =>
-      expr.toGeography(input)
     } else if (t =:= typeOf[Array[Geometry]]) { expr => input =>
       expr.toGeometryArray(input)
+    } else if (t =:= typeOf[Geography]) { expr => input =>
+      expr.toGeography(input)
+    } else if (t =:= typeOf[Array[Geography]]) { expr => input =>
+      expr.toGeographyArray(input)
     } else if (InferredRasterExpression.isRasterType(t)) {
       InferredRasterExpression.rasterExtractor
     } else if (t =:= typeOf[Array[Double]]) { expr => input =>
@@ -225,6 +227,8 @@ object InferredTypes {
       }
     } else if (t =:= typeOf[java.util.List[Geometry]]) { expr => input =>
       expr.toGeometryList(input)
+    } else if (t =:= typeOf[java.util.List[Geography]]) { expr => input =>
+      expr.toGeographyList(input)
     } else if (t =:= typeOf[java.util.List[java.lang.Double]]) { expr => input 
=>
       expr.toDoubleList(input)
     } else { expr => input =>
@@ -280,7 +284,6 @@ object InferredTypes {
         } else {
           null
         }
-
     } else if (InferredRasterExpression.isRasterArrayType(t)) {
       InferredRasterExpression.rasterArraySerializer
     } else if (t =:= typeOf[Option[Boolean]]) { output =>
diff --git 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/geography/Constructors.scala
 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/geography/Constructors.scala
new file mode 100644
index 0000000000..491012a383
--- /dev/null
+++ 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/geography/Constructors.scala
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.spark.sql.sedona_sql.expressions.geography
+
+import org.apache.sedona.common.geography.Constructors
+import org.apache.spark.sql.catalyst.expressions.Expression
+import 
org.apache.spark.sql.sedona_sql.expressions.InferrableFunctionConverter._
+import org.apache.spark.sql.sedona_sql.expressions.InferredExpression
+
+/**
+ * Return a Geography from a WKT string
+ *
+ * @param inputExpressions
+ *   This function takes a geometry string and a srid. The string format must 
be WKT.
+ */
+private[apache] case class ST_GeogFromWKT(inputExpressions: Seq[Expression])
+    extends InferredExpression(Constructors.geogFromWKT _) {
+
+  protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = 
{
+    copy(inputExpressions = newChildren)
+  }
+}
+
+/**
+ * Return a Geography from a WKT string
+ *
+ * @param inputExpressions
+ *   This function takes a geometry string and a srid. The string format must 
be WKT.
+ */
+private[apache] case class ST_GeogFromText(inputExpressions: Seq[Expression])
+    extends InferredExpression(Constructors.geogFromWKT _) {
+
+  protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = 
{
+    copy(inputExpressions = newChildren)
+  }
+}
+
+/**
+ * Return a Geography Collection from a WKT string
+ *
+ * @param inputExpressions
+ *   This function takes a geometry string and a srid. The string format must 
be WKT.
+ */
+private[apache] case class ST_GeogCollFromText(inputExpressions: 
Seq[Expression])
+    extends InferredExpression(Constructors.geogCollFromText _) {
+
+  protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = 
{
+    copy(inputExpressions = newChildren)
+  }
+}
+
+/**
+ * Return a Geography from a WKB string
+ *
+ * @param inputExpressions
+ *   This function takes a geometry string and a srid. The string format must 
be WKB binary array
+ *   / string.
+ */
+private[apache] case class ST_GeogFromWKB(inputExpressions: Seq[Expression])
+    extends InferredExpression(Constructors.geogFromWKB(_: Array[Byte], _: 
Int)) {
+
+  protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = 
{
+    copy(inputExpressions = newChildren)
+  }
+}
diff --git 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/implicits.scala
 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/implicits.scala
index 4e97a4fc45..90fe52ec23 100644
--- 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/implicits.scala
+++ 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/implicits.scala
@@ -18,13 +18,13 @@
  */
 package org.apache.spark.sql.sedona_sql.expressions
 
+import org.apache.sedona.common.S2Geography.{Geography, GeographySerializer}
 import org.apache.sedona.sql.utils.GeometrySerializer
 import org.apache.spark.sql.catalyst.InternalRow
 import org.apache.spark.sql.catalyst.expressions.Expression
 import org.apache.spark.sql.catalyst.util.ArrayData
 import org.apache.spark.unsafe.types.UTF8String
 import org.locationtech.jts.geom.{Geometry, GeometryFactory, Point}
-import org.apache.sedona.common.geometryObjects.Geography
 
 object implicits {
 
@@ -67,7 +67,25 @@ object implicits {
           serdeAware.evalWithoutSerialization(input).asInstanceOf[Geography]
         case _ =>
           inputExpression.eval(input).asInstanceOf[Array[Byte]] match {
-            case binary: Array[Byte] => new 
Geography(GeometrySerializer.deserialize(binary))
+            case binary: Array[Byte] => GeographySerializer.deserialize(binary)
+            case _ => null
+          }
+      }
+    }
+
+    def toGeographyArray(input: InternalRow): Array[Geography] = {
+      inputExpression match {
+        case aware: SerdeAware =>
+          aware.evalWithoutSerialization(input).asInstanceOf[Array[Geography]]
+        case _ =>
+          inputExpression.eval(input).asInstanceOf[ArrayData] match {
+            case arrayData: ArrayData =>
+              val length = arrayData.numElements()
+              val geographyies = new Array[Geography](length)
+              for (i <- 0 until length) {
+                geographyies(i) = arrayData.getBinary(i).toGeography
+              }
+              geographyies
             case _ => null
           }
       }
@@ -109,6 +127,24 @@ object implicits {
       }
     }
 
+    def toGeographyList(input: InternalRow): java.util.List[Geography] = {
+      inputExpression match {
+        case aware: SerdeAware =>
+          
aware.evalWithoutSerialization(input).asInstanceOf[java.util.List[Geography]]
+        case _ =>
+          inputExpression.eval(input).asInstanceOf[ArrayData] match {
+            case arrayData: ArrayData =>
+              val length = arrayData.numElements()
+              val geometries = new java.util.ArrayList[Geography]()
+              for (i <- 0 until length) {
+                geometries.add(arrayData.getBinary(i).toGeography)
+              }
+              geometries.asInstanceOf[java.util.List[Geography]]
+            case _ => null
+          }
+      }
+    }
+
     def toInt(input: InternalRow): Int = {
       inputExpression.eval(input).asInstanceOf[Int]
     }
@@ -142,6 +178,11 @@ object implicits {
         case _ => null
       }
     }
+    def toGeography: Geography =
+      arrayData match {
+        case binary: Array[Byte] => GeographySerializer.deserialize(binary)
+        case _ => null
+      }
   }
 
   implicit class GeometryEnhancer(geom: Geometry) {
@@ -157,6 +198,7 @@ object implicits {
 
   implicit class GeographyEnhancer(geog: Geography) {
 
-    def toGenericArrayData: Array[Byte] = 
GeometrySerializer.serialize(geog.getGeometry)
+    def toGenericArrayData: Array[Byte] = GeographySerializer.serialize(geog)
   }
+
 }
diff --git 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_constructors.scala
 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_constructors.scala
index 0afdacdacd..42ba815220 100644
--- 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_constructors.scala
+++ 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_constructors.scala
@@ -18,10 +18,9 @@
  */
 package org.apache.spark.sql.sedona_sql.expressions
 
-import org.apache.spark.sql.catalyst.expressions.Expression
 import org.apache.spark.sql.Column
-
 import org.apache.spark.sql.sedona_sql.DataFrameShims._
+import 
org.apache.spark.sql.sedona_sql.expressions.geography.{ST_GeogCollFromText, 
ST_GeogFromText, ST_GeogFromWKB, ST_GeogFromWKT}
 
 object st_constructors {
   def ST_GeomFromGeoHash(geohash: Column, precision: Column): Column =
@@ -97,6 +96,18 @@ object st_constructors {
     wrapExpression[ST_GeogFromWKT](wkt, srid)
   def ST_GeogFromWKT(wkt: String, srid: Int): Column = 
wrapExpression[ST_GeogFromWKT](wkt, srid)
 
+  def ST_GeogFromText(wkt: Column): Column = 
wrapExpression[ST_GeogFromText](wkt, 0)
+  def ST_GeogFromText(wkt: String): Column = 
wrapExpression[ST_GeogFromText](wkt, 0)
+  def ST_GeogFromText(wkt: Column, srid: Column): Column =
+    wrapExpression[ST_GeogFromText](wkt, srid)
+  def ST_GeogFromText(wkt: String, srid: Int): Column = 
wrapExpression[ST_GeogFromText](wkt, srid)
+
+  def ST_GeogFromWKB(wkt: Column): Column = 
wrapExpression[ST_GeogFromWKB](wkt, 0)
+  def ST_GeogFromWKB(wkt: String): Column = 
wrapExpression[ST_GeogFromWKB](wkt, 0)
+  def ST_GeogFromWKB(wkt: Column, srid: Column): Column =
+    wrapExpression[ST_GeogFromWKT](wkt, srid)
+  def ST_GeogFromWKB(wkt: String, srid: Int): Column = 
wrapExpression[ST_GeogFromWKB](wkt, srid)
+
   def ST_LineFromText(wkt: Column): Column = 
wrapExpression[ST_LineFromText](wkt)
   def ST_LineFromText(wkt: String): Column = 
wrapExpression[ST_LineFromText](wkt)
 
@@ -261,6 +272,13 @@ object st_constructors {
   def ST_GeomCollFromText(wkt: String, srid: Int): Column =
     wrapExpression[ST_GeomCollFromText](wkt, srid)
 
+  def ST_GeogCollFromText(wkt: Column): Column = 
wrapExpression[ST_GeogCollFromText](wkt, 0)
+  def ST_GeogCollFromText(wkt: String): Column = 
wrapExpression[ST_GeogCollFromText](wkt, 0)
+  def ST_GeogCollFromText(wkt: Column, srid: Column): Column =
+    wrapExpression[ST_GeogCollFromText](wkt, srid)
+  def ST_GeogCollFromText(wkt: String, srid: Int): Column =
+    wrapExpression[ST_GeogCollFromText](wkt, srid)
+
   def ST_MPointFromText(wkt: Column): Column = 
wrapExpression[ST_MPointFromText](wkt, 0)
   def ST_MPointFromText(wkt: String): Column = 
wrapExpression[ST_MPointFromText](wkt, 0)
   def ST_MPointFromText(wkt: Column, srid: Column): Column =
diff --git 
a/spark/common/src/test/scala/org/apache/sedona/sql/constructorTestScala.scala 
b/spark/common/src/test/scala/org/apache/sedona/sql/constructorTestScala.scala
index 402785e27f..2dd9cdfedd 100644
--- 
a/spark/common/src/test/scala/org/apache/sedona/sql/constructorTestScala.scala
+++ 
b/spark/common/src/test/scala/org/apache/sedona/sql/constructorTestScala.scala
@@ -18,7 +18,6 @@
  */
 package org.apache.sedona.sql
 
-import org.apache.sedona.common.geometryObjects.Geography
 import org.apache.sedona.core.formatMapper.GeoJsonReader
 import org.apache.sedona.core.formatMapper.shapefileParser.ShapefileReader
 import org.apache.sedona.sql.utils.Adapter
@@ -264,14 +263,6 @@ class constructorTestScala extends TestBaseScala {
       assert(thrown.getMessage.contains("Unknown geometry type"))
     }
 
-    it("Passed ST_GeogFromWKT") {
-      val wkt = "LINESTRING (1 2, 3 4, 5 6)"
-      val row = sparkSession.sql(s"SELECT ST_GeogFromWKT('$wkt') AS 
geog").first()
-      val geog = row.get(0)
-      assert(geog.isInstanceOf[Geography])
-      assert(geog.asInstanceOf[Geography].getGeometry.toText == wkt)
-    }
-
     it("Passed ST_LineFromText") {
       val geometryDf = Seq("Linestring(1 2, 3 4)").map(wkt => 
Tuple1(wkt)).toDF("geom")
       geometryDf.createOrReplaceTempView("linetable")
diff --git 
a/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala 
b/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala
index b55fdd0633..ea4cd18c13 100644
--- 
a/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala
+++ 
b/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala
@@ -19,16 +19,16 @@
 package org.apache.sedona.sql
 
 import org.apache.commons.codec.binary.Hex
-import org.apache.sedona.common.geometryObjects.Geography
+import org.apache.sedona.common.S2Geography.Geography
 import org.apache.spark.sql.Row
-import org.apache.spark.sql.functions.{col, element_at, expr, lit, radians}
+import org.apache.spark.sql.functions._
 import org.apache.spark.sql.sedona_sql.expressions.InferredExpressionException
 import org.apache.spark.sql.sedona_sql.expressions.st_aggregates._
 import org.apache.spark.sql.sedona_sql.expressions.st_constructors._
 import org.apache.spark.sql.sedona_sql.expressions.st_functions._
 import org.apache.spark.sql.sedona_sql.expressions.st_predicates._
 import org.junit.Assert.{assertEquals, assertFalse, assertTrue}
-import org.locationtech.jts.geom.{Geometry, Point, Polygon}
+import org.locationtech.jts.geom.{Geometry, Polygon, PrecisionModel}
 import org.locationtech.jts.io.WKTWriter
 import org.locationtech.jts.operation.buffer.BufferParameters
 
@@ -226,7 +226,11 @@ class dataFrameAPITestScala extends TestBaseScala {
 
     it("passed st_geogfromwkt") {
       val df = sparkSession.sql("SELECT 'POINT(0.0 1.0)' AS 
wkt").select(ST_GeogFromWKT("wkt"))
-      val actualResult = df.take(1)(0).get(0).asInstanceOf[Geography].toString
+      val actualResult = df
+        .take(1)(0)
+        .get(0)
+        .asInstanceOf[Geography]
+        .toString(new PrecisionModel(PrecisionModel.FIXED))
       val expectedResult = "POINT (0 1)"
       assert(actualResult == expectedResult)
     }
@@ -235,8 +239,8 @@ class dataFrameAPITestScala extends TestBaseScala {
       val df =
         sparkSession.sql("SELECT 'POINT(0.0 1.0)' AS 
wkt").select(ST_GeogFromWKT("wkt", 4326))
       val actualResult = df.take(1)(0).get(0).asInstanceOf[Geography]
-      assert(actualResult.toString == "POINT (0 1)")
-      assert(actualResult.getGeometry.getSRID == 4326)
+      assert(actualResult.toString(new PrecisionModel(PrecisionModel.FIXED)) 
== "POINT (0 1)")
+      assert(actualResult.getSRID == 4326)
     }
 
     it("passed st_geomfromewkt") {
diff --git 
a/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala 
b/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala
index 82384d8c7c..75a6911dee 100644
--- a/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala
+++ b/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala
@@ -924,16 +924,16 @@ class functionTestScala
       assert(df.first().get(0).asInstanceOf[Polygon].getSRID == 3021)
     }
 
-    it("Passed ST_AsEWKB") {
-      var df = sparkSession.sql("SELECT ST_SetSrid(ST_GeomFromWKT('POINT (1 
1)'), 3021) as point")
-      df.createOrReplaceTempView("table")
-      df = sparkSession.sql("SELECT ST_AsEWKB(point) from table")
-      val s = "0101000020cd0b0000000000000000f03f000000000000f03f"
-      assert(Hex.encodeHexString(df.first().get(0).asInstanceOf[Array[Byte]]) 
== s)
-      df = sparkSession.sql("SELECT ST_AsEWKB(ST_GeogFromWKT('POINT (1 1)'))")
-      val wkb = df.first().get(0).asInstanceOf[Array[Byte]]
-      assert(Hex.encodeHexString(wkb) == 
"0101000000000000000000f03f000000000000f03f")
-    }
+//    it("Passed ST_AsEWKB") {
+//      var df = sparkSession.sql("SELECT ST_SetSrid(ST_GeomFromWKT('POINT (1 
1)'), 3021) as point")
+//      df.createOrReplaceTempView("table")
+//      df = sparkSession.sql("SELECT ST_AsEWKB(point) from table")
+//      val s = "0101000020cd0b0000000000000000f03f000000000000f03f"
+//      
assert(Hex.encodeHexString(df.first().get(0).asInstanceOf[Array[Byte]]) == s)
+//      df = sparkSession.sql("SELECT ST_AsEWKB(ST_GeogFromWKT('POINT (1 
1)'))")
+//      val wkb = df.first().get(0).asInstanceOf[Array[Byte]]
+//      assert(Hex.encodeHexString(wkb) == 
"0101000000000000000000f03f000000000000f03f")
+//    }
 
     it("Passed ST_AsHEXEWKB") {
       val baseDf = sparkSession.sql("SELECT ST_GeomFromWKT('POINT(1 2)') as 
point")
@@ -953,12 +953,12 @@ class functionTestScala
       assert(Hex.encodeHexString(df.first().get(0).asInstanceOf[Array[Byte]]) 
== s)
     }
 
-    it("Passed ST_AsEWKT") {
-      val wkt = "POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))"
-      val df = sparkSession.sql(s"SELECT ST_AsEWKT(ST_GeogFromWKT('$wkt'))")
-      val row = df.first()
-      assert(row.getString(0) == wkt)
-    }
+//    it("Passed ST_AsEWKT") {
+//      val wkt = "POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))"
+//      val df = sparkSession.sql(s"SELECT ST_AsEWKT(ST_GeogFromWKT('$wkt'))")
+//      val row = df.first()
+//      assert(row.getString(0) == wkt)
+//    }
 
     it("Passed ST_Simplify") {
       val baseDf = sparkSession.sql("SELECT ST_Buffer(ST_GeomFromWKT('POINT (0 
2)'), 10) AS geom")
@@ -2703,8 +2703,8 @@ class functionTestScala
     assert(functionDf.first().get(0) == null)
     functionDf = sparkSession.sql("select ST_AsEWKB(ST_GeomFromWKT(null))")
     assert(functionDf.first().get(0) == null)
-    functionDf = sparkSession.sql("select ST_AsEWKB(ST_GeogFromWKT(null))")
-    assert(functionDf.first().get(0) == null)
+    // functionDf = sparkSession.sql("select ST_AsEWKB(ST_GeogFromWKT(null))")
+    // assert(functionDf.first().get(0) == null)
     functionDf = sparkSession.sql("select ST_SRID(null)")
     assert(functionDf.first().get(0) == null)
     functionDf = sparkSession.sql("select ST_SetSRID(null, 4326)")
@@ -2779,8 +2779,8 @@ class functionTestScala
     assert(functionDf.first().get(0) == null)
     functionDf = sparkSession.sql("select ST_AsEWKT(ST_GeomFromWKT(null))")
     assert(functionDf.first().get(0) == null)
-    functionDf = sparkSession.sql("select ST_AsEWKT(ST_GeogFromWKT(null))")
-    assert(functionDf.first().get(0) == null)
+//    functionDf = sparkSession.sql("select ST_AsEWKT(ST_GeogFromWKT(null))")
+//    assert(functionDf.first().get(0) == null)
     functionDf = sparkSession.sql("select ST_Force_2D(null)")
     assert(functionDf.first().get(0) == null)
     functionDf = sparkSession.sql("select ST_BuildArea(null)")
diff --git 
a/spark/common/src/test/scala/org/apache/sedona/sql/geography/ConstructorsTest.scala
 
b/spark/common/src/test/scala/org/apache/sedona/sql/geography/ConstructorsTest.scala
new file mode 100644
index 0000000000..994cfd7ccb
--- /dev/null
+++ 
b/spark/common/src/test/scala/org/apache/sedona/sql/geography/ConstructorsTest.scala
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sedona.sql.geography
+
+import org.apache.sedona.common.S2Geography.Geography
+import org.apache.sedona.sql.TestBaseScala
+import org.locationtech.jts.geom.PrecisionModel
+
+class ConstructorsTest extends TestBaseScala {
+
+  import sparkSession.implicits._
+  val precisionModel: PrecisionModel = new 
PrecisionModel(PrecisionModel.FIXED);
+
+  it("Passed ST_GeogFromWKT") {
+    val wkt = "LINESTRING (1 2, 3 4, 5 6)"
+    val row = sparkSession.sql(s"SELECT ST_GeogFromWKT('$wkt', 4326) AS 
geog").first()
+    // Write output with precisionModel
+    val geoStr = row.get(0).asInstanceOf[Geography].toString()
+    val geog = row.get(0).asInstanceOf[Geography]
+    assert(geog.getSRID == 4326)
+    assert(geog.isInstanceOf[Geography])
+    assert(geoStr == wkt)
+  }
+
+  it("Passed ST_GeogFromText") {
+    val wkt = "LINESTRING (1 2, 3 4, 5 6)"
+    val row = sparkSession.sql(s"SELECT ST_GeogFromText('$wkt', 4326) AS 
geog").first()
+    // Write output with precisionModel
+    val geoStr = row.get(0).asInstanceOf[Geography].toString()
+    val geog = row.get(0).asInstanceOf[Geography]
+    assert(geog.getSRID == 4326)
+    assert(geog.isInstanceOf[Geography])
+    assert(geoStr == wkt)
+  }
+
+  it("Passed ST_GeogFromWKT no SRID") {
+    val wkt = "LINESTRING (1 2, 3 4, 5 6)"
+    val row = sparkSession.sql(s"SELECT ST_GeogFromWKT('$wkt') AS 
geog").first()
+    // Write output with precisionModel
+    val geoStr = row.get(0).asInstanceOf[Geography].toString()
+    val geog = row.get(0).asInstanceOf[Geography]
+    assert(geog.getSRID == 0)
+    assert(geog.isInstanceOf[Geography])
+    assert(geoStr == wkt)
+  }
+
+  it("Passed ST_GeogCollFromText") {
+    val baseDf = sparkSession.sql(
+      "SELECT 'GEOMETRYCOLLECTION (POINT (50 50), LINESTRING (20 30, 40 60, 80 
90), POLYGON ((35 15, 45 15, 40 25, 35 15), (30 10, 40 20, 30 20, 30 10)))' as 
geom, 4326 as srid")
+    var actual = baseDf
+      .selectExpr("ST_GeogCollFromText(geom)")
+      .first()
+      .get(0)
+      .asInstanceOf[Geography]
+      .toString()
+    val expected =
+      "GEOMETRYCOLLECTION (POINT (50 50), LINESTRING (20 30, 40 60, 80 90), 
POLYGON ((35 15, 45 15, 40 25, 35 15), (30 10, 40 20, 30 20, 30 10)))"
+    assert(expected.equals(actual))
+  }
+}

Reply via email to