You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sedona.apache.org by ji...@apache.org on 2024/05/29 03:42:29 UTC

(sedona) branch master updated: [SEDONA-569][SEDONA-570][SEDONA-572] Add ST_PointZM, ST_PointM, ST_PointFromWKB (#1440)

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 7fc3270b7 [SEDONA-569][SEDONA-570][SEDONA-572] Add ST_PointZM, ST_PointM, ST_PointFromWKB (#1440)
7fc3270b7 is described below

commit 7fc3270b71a60c758f9a101db7720b82f7c44766
Author: Jia Yu <ji...@apache.org>
AuthorDate: Tue May 28 20:42:23 2024 -0700

    [SEDONA-569][SEDONA-570][SEDONA-572] Add ST_PointZM, ST_PointM, ST_PointFromWKB (#1440)
    
    * [TASK-212] Add ST_PointZM (#143)
    
    * Add ST_PointZM
    
    * fix snowflake test
    
    * fix snowflake test
    
    * Remove snowflake support
    
    * Add flink test
    
    * Add Dataframe api test; remove show()
    
    * fix python test
    
    * Add scala dataframe api tests
    
    * fix test
    
    * Update versions
    
    * [TASK-278] Add ST_PointM (#145)
    
    * init
    
    * Implement ST_PointM
    
    * Update docs
    
    * remove snowflake test
    
    * update snowflake docs
    
    * Remove snowflake registration for ST_PointM
    
    * Add dataframe api tests
    
    * fix test
    
    * fix test
    
    * [TASK-247] Add ST_PointFromWKB (#150)
    
    * init
    
    * init 2
    
    * add flink test
    
    * add scala, python, snowflake tests
    
    * add docs
    
    * Fix scala implementation
    
    * Fix test
    
    * fix test
    
    * fix test
    
    * add tests
    
    * Fix scala implementation; add tests
    
    * Fix docs
    
    * Add test
    
    * Fix test
    
    * Update versions
    
    ---------
    
    Co-authored-by: Pranav Toggi <pr...@gmail.com>
---
 .../org/apache/sedona/common/Constructors.java     |  48 +++++++++-
 .../org/apache/sedona/common/ConstructorsTest.java |  18 ++++
 .../org/apache/sedona/common/FunctionsTest.java    |  19 ++++
 docs/api/flink/Constructor.md                      |  81 ++++++++++++++++
 docs/api/snowflake/vector-data/Constructor.md      |  31 ++++++-
 docs/api/sql/Constructor.md                        |  81 ++++++++++++++++
 .../main/java/org/apache/sedona/flink/Catalog.java |   3 +
 .../sedona/flink/expressions/Constructors.java     |  57 ++++++++++++
 .../org/apache/sedona/flink/ConstructorTest.java   | 102 ++++++++++++++++++++-
 python/sedona/sql/st_constructors.py               |  52 +++++++++++
 python/tests/sql/test_constructor_test.py          |   4 +
 python/tests/sql/test_dataframe_api.py             |  16 +++-
 .../sedona/snowflake/snowsql/TestConstructors.java |  15 +++
 .../org/apache/sedona/snowflake/snowsql/UDFs.java  |  14 +++
 .../scala/org/apache/sedona/sql/UDF/Catalog.scala  |   3 +
 .../sql/sedona_sql/expressions/Constructors.scala  |  66 +++++++++++++
 .../sedona_sql/expressions/st_constructors.scala   |  26 ++++++
 .../apache/sedona/sql/constructorTestScala.scala   |  59 ++++++++++++
 .../apache/sedona/sql/dataFrameAPITestScala.scala  |  31 ++++++-
 19 files changed, 711 insertions(+), 15 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 f348b2017..6dd348c01 100644
--- a/common/src/main/java/org/apache/sedona/common/Constructors.java
+++ b/common/src/main/java/org/apache/sedona/common/Constructors.java
@@ -17,11 +17,7 @@ import org.apache.sedona.common.enums.FileDataSplitter;
 import org.apache.sedona.common.enums.GeometryType;
 import org.apache.sedona.common.utils.FormatUtils;
 import org.apache.sedona.common.utils.GeoHashDecoder;
-import org.locationtech.jts.geom.Coordinate;
-import org.locationtech.jts.geom.CoordinateXYZM;
-import org.locationtech.jts.geom.Geometry;
-import org.locationtech.jts.geom.GeometryFactory;
-import org.locationtech.jts.geom.PrecisionModel;
+import org.locationtech.jts.geom.*;
 import org.locationtech.jts.io.ParseException;
 import org.locationtech.jts.io.WKBReader;
 import org.locationtech.jts.io.WKTReader;
@@ -70,6 +66,19 @@ public class Constructors {
         return new WKBReader().read(wkb);
     }
 
+    public  static Geometry pointFromWKB(byte[] wkb) throws ParseException {
+        return pointFromWKB(wkb, 0);
+    }
+
+    public static Geometry pointFromWKB(byte[] wkb, int srid) throws ParseException {
+        GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(), srid);
+        Geometry geom =  new WKBReader(geometryFactory).read(wkb);
+        if (!(geom instanceof Point)) {
+            return null;
+        }
+        return geom;
+    }
+
     public static Geometry mLineFromText(String wkt, int srid) throws ParseException {
         if (wkt == null || !wkt.startsWith("MULTILINESTRING")) {
             return null;
@@ -136,6 +145,35 @@ public class Constructors {
         return geometryFactory.createPoint(new Coordinate(x, y, z));
     }
 
+    /**
+     * Creates a point from the given coordinate.
+     *
+     * @param x the x value
+     * @param y the y value
+     * @param m the m value
+     * @param srid Set to 0 if unknown
+     * @return The point geometry
+     */
+    public static Geometry pointM(double x, double y, double m, int srid) {
+        GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(), srid);
+        return geometryFactory.createPoint(new CoordinateXYZM(x, y, 0, m));
+    }
+
+    /**
+     * Creates a point from the given coordinate.
+     *
+     * @param x the x value
+     * @param y the y value
+     * @param z the z value
+     * @param m the m value
+     * @param srid Set to 0 if unknown
+     * @return The point geometry
+     */
+    public static Geometry pointZM(double x, double y, double z, double m, int srid) {
+        GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(), srid);
+        return geometryFactory.createPoint(new CoordinateXYZM(x, y, z, m));
+    }
+
     public static Geometry geomFromText(String geomString, String geomFormat, GeometryType geometryType) {
         FileDataSplitter fileDataSplitter = FileDataSplitter.getFileDataSplitter(geomFormat);
         FormatUtils<Geometry> formatMapper = new FormatUtils<>(fileDataSplitter, false, geometryType);
diff --git a/common/src/test/java/org/apache/sedona/common/ConstructorsTest.java b/common/src/test/java/org/apache/sedona/common/ConstructorsTest.java
index d7b92f3a7..7abbcb306 100644
--- a/common/src/test/java/org/apache/sedona/common/ConstructorsTest.java
+++ b/common/src/test/java/org/apache/sedona/common/ConstructorsTest.java
@@ -143,4 +143,22 @@ public class ConstructorsTest {
         assertEquals(4326, point.getSRID());
         assertEquals("POINT Z(0 1 2)", Functions.asWKT(point));
     }
+
+    @Test
+    public void pointM() {
+        Geometry point = Constructors.pointM(0.0d, 1.0d, 2.0d, 4326);
+
+        assertTrue(point instanceof Point);
+        assertEquals(4326, point.getSRID());
+        assertEquals("SRID=4326;POINT ZM(0 1 0 2)", Functions.asEWKT(point));
+    }
+
+    @Test
+    public void pointZM() {
+        Geometry point = Constructors.pointZM(0.0d, 1.0d, 2.0d, 10.0, 4326);
+
+        assertTrue(point instanceof Point);
+        assertEquals(4326, point.getSRID());
+        assertEquals("POINT ZM(0 1 2 10)", Functions.asWKT(point));
+    }
 }
diff --git a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
index 9107670aa..65a63484b 100644
--- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
+++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
@@ -133,6 +133,25 @@ public class FunctionsTest extends TestBase {
         assertEquals(expected, geometry);
     }
 
+    @Test
+    public void pointFromWKB() throws Exception{
+        Geometry geometry = GEOMETRY_FACTORY.createPoint(new Coordinate(1.0, 2.0));
+        byte[] wkbGeom = Functions.asWKB(geometry);
+        Geometry result = Constructors.pointFromWKB(wkbGeom);
+        assertEquals(geometry, result);
+
+        geometry = GEOMETRY_FACTORY.createPoint(new Coordinate(1.0, 2.0, 3.0));
+        wkbGeom = Functions.asWKB(geometry);
+        result = Constructors.pointFromWKB(wkbGeom, 4326);
+        assertEquals(geometry, result);
+        assertEquals(4326, Objects.requireNonNull(result).getSRID());
+
+        geometry = GEOMETRY_FACTORY.createLineString(coordArray(0.0, 0.0, 1.5, 1.5, 2.0, 2.0));
+        wkbGeom = Functions.asWKB(geometry);
+        result = Constructors.pointFromWKB(wkbGeom, 0);
+        assertNull(result);
+    }
+
     @Test
     public void splitLineStringByMultipoint() {
         LineString lineString = GEOMETRY_FACTORY.createLineString(coordArray(0.0, 0.0, 1.5, 1.5, 2.0, 2.0));
diff --git a/docs/api/flink/Constructor.md b/docs/api/flink/Constructor.md
index c63a8f84c..3f26e08b7 100644
--- a/docs/api/flink/Constructor.md
+++ b/docs/api/flink/Constructor.md
@@ -480,6 +480,87 @@ Output:
 POINT Z(1.2345 2.3456 3.4567)
 ```
 
+## ST_PointM
+
+Introduction: Construct a Point from X, Y and M and an optional srid. If srid is not set, it defaults to 0 (unknown).
+Must use ST_AsEWKT function to print the Z and M coordinates.
+
+Format:
+
+`ST_PointM (X: Double, Y: Double, M: Double)`
+
+`ST_PointM (X: Double, Y: Double, M: Double, srid: Integer)`
+
+Since: `v1.6.1`
+
+Example:
+
+```sql
+SELECT ST_AsEWKT(ST_PointM(1.2345, 2.3456, 3.4567))
+```
+
+Output:
+
+```
+POINT ZM(1.2345 2.3456 0 3.4567)
+```
+
+## ST_PointZM
+
+Introduction: Construct a Point from X, Y, Z, M and an optional srid. If srid is not set, it defaults to 0 (unknown).
+Must use ST_AsEWKT function to print the Z and M coordinates.
+
+Format:
+
+`ST_PointZM (X: Double, Y: Double, Z: Double, M: Double)`
+
+`ST_PointZM (X: Double, Y: Double, Z: Double, M: Double, srid: Integer)`
+
+Since: `v1.6.1`
+
+SQL Example
+
+```sql
+SELECT ST_AsEWKT(ST_PointZM(1.2345, 2.3456, 3.4567, 100))
+```
+
+Output:
+
+```
+POINT ZM(1.2345 2.3456 3.4567, 100)
+```
+
+## ST_PointFromWKB
+
+Introduction: Construct a Point geometry from WKB string or Binary and an optional SRID. This function also supports EWKB format.
+
+!!!note
+    Returns null if geometry is not of type Point.
+
+Format:
+
+`ST_PointFromWKB (Wkb: String)`
+
+`ST_PointFromWKB (Wkb: Binary)`
+
+`ST_PointFromWKB (Wkb: String, srid: Integer)`
+
+`ST_PointFromWKB (Wkb: Binary, srid: Integer)`
+
+Since: `v1.6.1`
+
+Example:
+
+```sql
+SELECT ST_PointFromWKB([01 01 00 00 00 00 00 00 00 00 00 24 40 00 00 00 00 00 00 2e 40])
+```
+
+Output:
+
+```
+POINT (10 15)
+```
+
 ## ST_PolygonFromEnvelope
 
 Introduction: Construct a Polygon from MinX, MinY, MaxX, MaxY.
diff --git a/docs/api/snowflake/vector-data/Constructor.md b/docs/api/snowflake/vector-data/Constructor.md
index e7b98f9dc..796f77994 100644
--- a/docs/api/snowflake/vector-data/Constructor.md
+++ b/docs/api/snowflake/vector-data/Constructor.md
@@ -392,7 +392,7 @@ Introduction: Construct a Point from X and Y
 Format: `ST_Point (X:decimal, Y:decimal)`
 
 In `v1.4.0` an optional Z parameter was removed to be more consistent with other spatial SQL implementations.
-If you are upgrading from an older version of Sedona - please use ST_PointZ to create 3D points.
+If you are upgrading from an older version of Sedona - please use ST_PointZ or ST_PointZM to create 3D points.
 
 SQL example:
 
@@ -443,6 +443,35 @@ Output:
 POINT Z(1.2345 2.3456 3.4567)
 ```
 
+## ST_PointFromWKB
+
+Introduction: Construct a Point geometry from WKB string or Binary and an optional SRID. This function also supports EWKB format.
+
+!!!note
+    Returns null if geometry is not of type Point.
+
+Format:
+
+`ST_PointFromWKB (Wkb: String)`
+
+`ST_PointFromWKB (Wkb: Binary)`
+
+`ST_PointFromWKB (Wkb: String, srid: Integer)`
+
+`ST_PointFromWKB (Wkb: Binary, srid: Integer)`
+
+Example:
+
+```sql
+SELECT ST_PointFromWKB([01 01 00 00 00 00 00 00 00 00 00 24 40 00 00 00 00 00 00 2e 40])
+```
+
+Output:
+
+```
+POINT (10 15)
+```
+
 ## ST_PolygonFromEnvelope
 
 Introduction: Construct a Polygon from MinX, MinY, MaxX, MaxY.
diff --git a/docs/api/sql/Constructor.md b/docs/api/sql/Constructor.md
index 9738f7024..0fdd8177d 100644
--- a/docs/api/sql/Constructor.md
+++ b/docs/api/sql/Constructor.md
@@ -542,6 +542,87 @@ Output:
 POINT Z(1.2345 2.3456 3.4567)
 ```
 
+## ST_PointM
+
+Introduction: Construct a Point from X, Y and M and an optional srid. If srid is not set, it defaults to 0 (unknown).
+Must use ST_AsEWKT function to print the Z and M coordinates.
+
+Format:
+
+`ST_PointM (X: Double, Y: Double, M: Double)`
+
+`ST_PointM (X: Double, Y: Double, M: Double, srid: Integer)`
+
+Since: `v1.6.1`
+
+Example:
+
+```sql
+SELECT ST_AsEWKT(ST_PointM(1.2345, 2.3456, 3.4567))
+```
+
+Output:
+
+```
+POINT ZM(1.2345 2.3456 0 3.4567)
+```
+
+## ST_PointZM
+
+Introduction: Construct a Point from X, Y, Z, M and an optional srid. If srid is not set, it defaults to 0 (unknown).
+Must use ST_AsEWKT function to print the Z and M coordinates.
+
+Format:
+
+`ST_PointZM (X: Double, Y: Double, Z: Double, M: Double)`
+
+`ST_PointZM (X: Double, Y: Double, Z: Double, M: Double, srid: Integer)`
+
+Since: `v1.6.1`
+
+SQL Example
+
+```sql
+SELECT ST_AsEWKT(ST_PointZM(1.2345, 2.3456, 3.4567, 100))
+```
+
+Output:
+
+```
+POINT ZM(1.2345 2.3456 3.4567, 100)
+```
+
+## ST_PointFromWKB
+
+Introduction: Construct a Point geometry from WKB string or Binary and an optional SRID. This function also supports EWKB format.
+
+!!!note
+	Returns null if geometry is not of type Point.
+
+Format:
+
+`ST_PointFromWKB (Wkb: String)`
+
+`ST_PointFromWKB (Wkb: Binary)`
+
+`ST_PointFromWKB (Wkb: String, srid: Integer)`
+
+`ST_PointFromWKB (Wkb: Binary, srid: Integer)`
+
+Since: `v1.6.1`
+
+Example:
+
+```sql
+SELECT ST_PointFromWKB([01 01 00 00 00 00 00 00 00 00 00 24 40 00 00 00 00 00 00 2e 40])
+```
+
+Output:
+
+```
+POINT (10 15)
+```
+
 ## ST_PolygonFromEnvelope
 
 Introduction: Construct a Polygon from MinX, MinY, MaxX, MaxY.
diff --git a/flink/src/main/java/org/apache/sedona/flink/Catalog.java b/flink/src/main/java/org/apache/sedona/flink/Catalog.java
index bedf26a5c..0f5293337 100644
--- a/flink/src/main/java/org/apache/sedona/flink/Catalog.java
+++ b/flink/src/main/java/org/apache/sedona/flink/Catalog.java
@@ -24,7 +24,10 @@ public class Catalog {
                 new Aggregators.ST_Union_Aggr(),
                 new Constructors.ST_Point(),
                 new Constructors.ST_PointZ(),
+                new Constructors.ST_PointM(),
+                new Constructors.ST_PointZM(),
                 new Constructors.ST_PointFromText(),
+                new Constructors.ST_PointFromWKB(),
                 new Constructors.ST_MakePoint(),
                 new Constructors.ST_LineStringFromText(),
                 new Constructors.ST_LineFromText(),
diff --git a/flink/src/main/java/org/apache/sedona/flink/expressions/Constructors.java b/flink/src/main/java/org/apache/sedona/flink/expressions/Constructors.java
index 7054e32d5..1e8f87ded 100644
--- a/flink/src/main/java/org/apache/sedona/flink/expressions/Constructors.java
+++ b/flink/src/main/java/org/apache/sedona/flink/expressions/Constructors.java
@@ -22,6 +22,7 @@ import org.apache.sedona.common.utils.GeoHashDecoder;
 import org.locationtech.jts.geom.Coordinate;
 import org.locationtech.jts.geom.Geometry;
 import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.Point;
 import org.locationtech.jts.io.WKBReader;
 import org.locationtech.jts.io.ParseException;
 import org.locationtech.jts.io.gml2.GMLReader;
@@ -54,6 +55,30 @@ public class Constructors {
         }
     }
 
+    public static class ST_PointM extends ScalarFunction {
+        @DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class)
+        public Geometry eval(@DataTypeHint("Double") Double x, @DataTypeHint("Double") Double y, @DataTypeHint("Double") Double m) throws ParseException {
+            return eval(x, y, m, 0);
+        }
+
+        @DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class)
+        public Geometry eval(@DataTypeHint("Double") Double x, @DataTypeHint("Double") Double y, @DataTypeHint("Double") Double m, @DataTypeHint("Integer") Integer srid) throws ParseException {
+            return org.apache.sedona.common.Constructors.pointM(x, y, m, srid);
+        }
+    }
+
+    public static class ST_PointZM extends ScalarFunction {
+        @DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class)
+        public Geometry eval(@DataTypeHint("Double") Double x, @DataTypeHint("Double") Double y, @DataTypeHint("Double") Double z, @DataTypeHint("Double") Double m) throws ParseException {
+            return eval(x, y, z, m,0);
+        }
+
+        @DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class)
+        public Geometry eval(@DataTypeHint("Double") Double x, @DataTypeHint("Double") Double y, @DataTypeHint("Double") Double z, @DataTypeHint("Double") Double m, @DataTypeHint("Integer") Integer srid) throws ParseException {
+            return org.apache.sedona.common.Constructors.pointZM(x, y, z, m, srid);
+        }
+    }
+
     public static class ST_PointFromText extends ScalarFunction {
         @DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class)
         public Geometry eval(@DataTypeHint("String") String s, @DataTypeHint("String") String inputDelimiter) throws ParseException {
@@ -193,6 +218,38 @@ public class Constructors {
 
     }
 
+    public static class ST_PointFromWKB extends ScalarFunction {
+        @DataTypeHint(value = "RAW", bridgedTo = Geometry.class)
+        public Geometry eval(@DataTypeHint("String") String wkbString) throws ParseException {
+            Geometry geometry = getGeometryByFileData(wkbString, FileDataSplitter.WKB);
+            if (geometry instanceof Point) {
+                geometry.setSRID(0);
+                return geometry;
+            }
+            return null;  // Return null if geometry is not a Point
+        }
+
+        @DataTypeHint(value = "RAW", bridgedTo = Geometry.class)
+        public Geometry eval(@DataTypeHint("String") String wkbString, int srid) throws ParseException {
+            Geometry geometry = getGeometryByFileData(wkbString, FileDataSplitter.WKB);
+            if (geometry instanceof Point) {
+                geometry.setSRID(srid);
+                return geometry;
+            }
+            return null;  // Return null if geometry is not a Point
+        }
+
+        @DataTypeHint(value = "RAW", bridgedTo = Geometry.class)
+        public Geometry eval(@DataTypeHint("Bytes") byte[] wkb) throws ParseException {
+            return org.apache.sedona.common.Constructors.pointFromWKB(wkb, 0);
+        }
+
+        @DataTypeHint(value = "RAW", bridgedTo = Geometry.class)
+        public Geometry eval(@DataTypeHint("Bytes") byte[] wkb, int srid) throws ParseException {
+            return org.apache.sedona.common.Constructors.pointFromWKB(wkb, srid);
+        }
+    }
+
     public static class ST_GeomFromGeoJSON extends ScalarFunction {
         @DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class)
         public Geometry eval(@DataTypeHint("String") String geoJson) throws ParseException {
diff --git a/flink/src/test/java/org/apache/sedona/flink/ConstructorTest.java b/flink/src/test/java/org/apache/sedona/flink/ConstructorTest.java
index 5470f96d0..7534dc072 100644
--- a/flink/src/test/java/org/apache/sedona/flink/ConstructorTest.java
+++ b/flink/src/test/java/org/apache/sedona/flink/ConstructorTest.java
@@ -15,6 +15,7 @@ package org.apache.sedona.flink;
 
 import org.apache.flink.table.api.Table;
 import org.apache.flink.types.Row;
+import org.apache.flink.streaming.api.datastream.DataStream;
 import org.apache.sedona.flink.expressions.Constructors;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -31,14 +32,12 @@ import java.util.List;
 
 import static org.apache.flink.table.api.Expressions.$;
 import static org.apache.flink.table.api.Expressions.call;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.*;
 
 import org.apache.flink.api.common.typeinfo.TypeInformation;
 import org.apache.flink.api.common.typeinfo.BasicTypeInfo;
 import org.apache.flink.api.common.typeinfo.PrimitiveArrayTypeInfo;
 import org.apache.flink.api.java.typeutils.RowTypeInfo;
-import org.apache.flink.streaming.api.datastream.DataStream;
 
 public class ConstructorTest extends TestBase{
 
@@ -99,6 +98,58 @@ public class ConstructorTest extends TestBase{
         assertEquals(5.0, result.getCoordinate().getZ(), 1e-6);
     }
 
+    @Test
+    public void testPointM() {
+        List<Row> data = new ArrayList<>();
+        data.add(Row.of(2.0, 2.0, 5.0, "point"));
+        String[] colNames = new String[]{"x", "y", "m", "name_point"};
+
+        TypeInformation<?>[] colTypes = {
+                BasicTypeInfo.DOUBLE_TYPE_INFO,
+                BasicTypeInfo.DOUBLE_TYPE_INFO,
+                BasicTypeInfo.DOUBLE_TYPE_INFO,
+                BasicTypeInfo.STRING_TYPE_INFO};
+        RowTypeInfo typeInfo = new RowTypeInfo(colTypes, colNames);
+        DataStream<Row> ds = env.fromCollection(data).returns(typeInfo);
+        Table pointTable = tableEnv.fromDataStream(ds);
+
+        Table geomTable = pointTable
+                .select(call(Constructors.ST_PointM.class.getSimpleName(), $(colNames[0]), $(colNames[1]), $(colNames[2]))
+                        .as(colNames[3]));
+
+        Point result = first(geomTable)
+                .getFieldAs(colNames[3]);
+
+        assertEquals(5.0, result.getCoordinate().getM(), 1e-6);
+    }
+
+    @Test
+    public void testPointZM() {
+        List<Row> data = new ArrayList<>();
+        data.add(Row.of(2.0, 2.0, 5.0, 100.0, "point"));
+        String[] colNames = new String[]{"x", "y", "z", "m", "name_point"};
+
+        TypeInformation<?>[] colTypes = {
+                BasicTypeInfo.DOUBLE_TYPE_INFO,
+                BasicTypeInfo.DOUBLE_TYPE_INFO,
+                BasicTypeInfo.DOUBLE_TYPE_INFO,
+                BasicTypeInfo.DOUBLE_TYPE_INFO,
+                BasicTypeInfo.STRING_TYPE_INFO};
+        RowTypeInfo typeInfo = new RowTypeInfo(colTypes, colNames);
+        DataStream<Row> ds = env.fromCollection(data).returns(typeInfo);
+        Table pointTable = tableEnv.fromDataStream(ds);
+
+        Table geomTable = pointTable
+                .select(call(Constructors.ST_PointZM.class.getSimpleName(), $(colNames[0]), $(colNames[1]), $(colNames[2]), $(colNames[3]))
+                        .as(colNames[4]));
+
+        Point result = first(geomTable)
+                .getFieldAs(colNames[4]);
+
+        assertEquals(5.0, result.getCoordinate().getZ(), 1e-6);
+        assertEquals(100.0, result.getCoordinate().getM(), 1e-6);
+    }
+
     @Test
     public void testMakePoint() {
         List<Row> data = new ArrayList<>();
@@ -266,7 +317,50 @@ public class ConstructorTest extends TestBase{
 
         assertEquals(expectedGeom, result);
 
-       }
+    }
+
+    @Test
+    public void testPointFromWKB() throws Exception {
+        String hexWkb1 = "010100000000000000000000000000000000000000";
+        byte[] wkbPoint1 = org.apache.commons.codec.binary.Hex.decodeHex(hexWkb1);
+        String hexWkb2 = "010100000000000000000024400000000000002e40";
+        byte[] wkbPoint2 = org.apache.commons.codec.binary.Hex.decodeHex(hexWkb2);
+        String hexWkb3 = "01030000000100000005000000000000000000e0bf000000000000e0bf000000000000e0bf000000000000e03f000000000000e03f000000000000e03f000000000000e03f000000000000e0bf000000000000e0bf000000000000e0bf";
+        byte[] wkbPolygon = org.apache.commons.codec.binary.Hex.decodeHex(hexWkb3);
+
+        List<Row> data1 = new ArrayList<>();
+        data1.add(Row.of(wkbPoint1));
+        data1.add(Row.of(wkbPoint2));
+        data1.add(Row.of(wkbPolygon));
+
+        List<Row> data2 = new ArrayList<>();
+        data2.add(Row.of(hexWkb1));
+        data2.add(Row.of(hexWkb2));
+        data2.add(Row.of(hexWkb3));
+
+        TypeInformation<?>[] colTypes1 = {PrimitiveArrayTypeInfo.BYTE_PRIMITIVE_ARRAY_TYPE_INFO};
+        TypeInformation<?>[] colTypes2 = {BasicTypeInfo.STRING_TYPE_INFO};
+        RowTypeInfo typeInfo1 = new RowTypeInfo(colTypes1, new String[]{"wkb"});
+        RowTypeInfo typeInfo2 = new RowTypeInfo(colTypes2, new String[]{"wkb"});
+
+        DataStream<Row> wkbDS1 = env.fromCollection(data1).returns(typeInfo1);
+        DataStream<Row> wkbDS2 = env.fromCollection(data2).returns(typeInfo2);
+        Table wkbTable1 = tableEnv.fromDataStream(wkbDS1, $("wkb"));
+        Table wkbTable2 = tableEnv.fromDataStream(wkbDS2, $("wkb"));
+
+        Table pointTable1 = wkbTable1.select(call(Constructors.ST_PointFromWKB.class.getSimpleName(), $("wkb")).as("point"));
+        Table pointTable2 = wkbTable2.select(call(Constructors.ST_PointFromWKB.class.getSimpleName(), $("wkb")).as("point"));
+
+        List<Row> results1 = TestBase.take(pointTable1, 3);
+        assertEquals("POINT (0 0)", results1.get(0).getField(0).toString());
+        assertEquals("POINT (10 15)", results1.get(1).getField(0).toString());
+        assertNull(results1.get(2).getField(0));
+
+        List<Row> results2 = TestBase.take(pointTable2, 3);
+        assertEquals("POINT (0 0)", results2.get(0).getField(0).toString());
+        assertEquals("POINT (10 15)", results2.get(1).getField(0).toString());
+        assertNull(results2.get(2).getField(0));
+    }
 
     @Test
     public void testGeomFromEWKB()
diff --git a/python/sedona/sql/st_constructors.py b/python/sedona/sql/st_constructors.py
index 11b561bee..fae53f49b 100644
--- a/python/sedona/sql/st_constructors.py
+++ b/python/sedona/sql/st_constructors.py
@@ -205,6 +205,44 @@ def ST_PointZ(x: ColumnOrNameOrNumber, y: ColumnOrNameOrNumber, z: ColumnOrNameO
     args = (x, y, z) if srid is None else (x, y, z, srid)
     return _call_constructor_function("ST_PointZ", args)
 
+@validate_argument_types
+def ST_PointM(x: ColumnOrNameOrNumber, y: ColumnOrNameOrNumber, m: ColumnOrNameOrNumber, srid: Optional[ColumnOrNameOrNumber] = None) -> Column:
+    """Generates a 3D point geometry column from numeric values.
+
+    :param x: Either a number or numeric column representing the X coordinate of a point.
+    :type x: ColumnOrNameOrNumber
+    :param y: Either a number or numeric column representing the Y coordinate of a point.
+    :type y: ColumnOrNameOrNumber
+    :param z: Either a number or numeric column representing the Z coordinate of a point, if None then a 2D point is generated, defaults to None
+    :type z: ColumnOrNameOrNumber
+    :param srid: The srid of the point. Defaults to 0 (unknown).
+    :type srid: Optional[ColumnOrNameOrNumber], optional
+    :return: Point geometry column generated from the coordinate values.
+    :rtype: Column
+    """
+    args = (x, y, m) if srid is None else (x, y, m, srid)
+    return _call_constructor_function("ST_PointM", args)
+
+@validate_argument_types
+def ST_PointZM(x: ColumnOrNameOrNumber, y: ColumnOrNameOrNumber, z: ColumnOrNameOrNumber, m: ColumnOrNameOrNumber,srid: Optional[ColumnOrNameOrNumber] = None) -> Column:
+    """Generates a 3D point geometry column from numeric values.
+
+    :param x: Either a number or numeric column representing the X coordinate of a point.
+    :type x: ColumnOrNameOrNumber
+    :param y: Either a number or numeric column representing the Y coordinate of a point.
+    :type y: ColumnOrNameOrNumber
+    :param z: Either a number or numeric column representing the Z coordinate of a point, if None then a 2D point is generated, defaults to None
+    :type z: ColumnOrNameOrNumber
+    :param m: Either a number or numeric column representing the M value of a point.
+    :type m: ColumnOrNameOrNumber
+    :param srid: The srid of the point. Defaults to 0 (unknown).
+    :type srid: Optional[ColumnOrNameOrNumber], optional
+    :return: Point geometry column generated from the coordinate values.
+    :rtype: Column
+    """
+    args = (x, y, z, m) if srid is None else (x, y, z, m, srid)
+    return _call_constructor_function("ST_PointZM", args)
+
 @validate_argument_types
 def ST_PointFromText(coords: ColumnOrName, delimiter: ColumnOrName) -> Column:
     """Generate a point geometry column from coordinates separated by a delimiter and stored
@@ -219,6 +257,20 @@ def ST_PointFromText(coords: ColumnOrName, delimiter: ColumnOrName) -> Column:
     """
     return _call_constructor_function("ST_PointFromText", (coords, delimiter))
 
+@validate_argument_types
+def ST_PointFromWKB(wkb: ColumnOrName, srid: Optional[ColumnOrNameOrNumber] = None) -> Column:
+    """Generate a Point geometry column from a Well-Known Binary (WKB) binary column.
+
+    :param wkb: WKB binary column to generate from.
+    :type wkb: ColumnOrName
+    :param srid: SRID to be set for geometry
+    :type srid: ColumnOrNameOrNumber
+    :return: Point Geometry column representing the WKB binary.
+    :rtype: Column
+    """
+    args = (wkb) if srid is None else (wkb, srid)
+    return _call_constructor_function("ST_PointFromWKB", args)
+
 @validate_argument_types
 def ST_MakePoint(x: ColumnOrNameOrNumber, y: ColumnOrNameOrNumber, z: Optional[ColumnOrNameOrNumber] = None, m: Optional[ColumnOrNameOrNumber] = None) -> Column:
     """Generate a 2D, 3D Z or 4D ZM Point geometry. If z is None then a 2D point is generated.
diff --git a/python/tests/sql/test_constructor_test.py b/python/tests/sql/test_constructor_test.py
index 2180972cd..cdcf114da 100644
--- a/python/tests/sql/test_constructor_test.py
+++ b/python/tests/sql/test_constructor_test.py
@@ -37,6 +37,10 @@ class TestConstructors(TestBase):
         point_df = self.spark.sql("SELECT ST_PointZ(1.2345, 2.3456, 3.4567)")
         assert point_df.count() == 1
 
+    def test_st_point_m(self):
+        point_df = self.spark.sql("SELECT ST_PointM(1.2345, 2.3456, 3.4567)")
+        assert point_df.count() == 1
+
     def test_st_makepoint(self):
         point_csv_df = self.spark.read.format("csv").\
             option("delimiter", ",").\
diff --git a/python/tests/sql/test_dataframe_api.py b/python/tests/sql/test_dataframe_api.py
index 977c460ca..be71f77ae 100644
--- a/python/tests/sql/test_dataframe_api.py
+++ b/python/tests/sql/test_dataframe_api.py
@@ -44,8 +44,8 @@ test_configurations = [
     (stc.ST_GeomFromKML, ("kml",), "constructor", "", "LINESTRING (-71.16 42.26, -71.17 42.26)"),
     (stc.ST_GeomFromText, ("wkt",), "linestring_wkt", "", "LINESTRING (1 2, 3 4)"),
     (stc.ST_GeomFromText, ("wkt",4326), "linestring_wkt", "", "LINESTRING (1 2, 3 4)"),
-    (stc.ST_GeomFromWKB, ("wkb",), "constructor", "ST_ReducePrecision(geom, 2)", "LINESTRING (-2.1 -0.35, -1.5 -0.67)"),
-    (stc.ST_GeomFromEWKB, ("wkb",), "constructor", "ST_ReducePrecision(geom, 2)", "LINESTRING (-2.1 -0.35, -1.5 -0.67)"),
+    (stc.ST_GeomFromWKB, ("wkbLine",), "constructor", "ST_ReducePrecision(geom, 2)", "LINESTRING (-2.1 -0.35, -1.5 -0.67)"),
+    (stc.ST_GeomFromEWKB, ("wkbLine",), "constructor", "ST_ReducePrecision(geom, 2)", "LINESTRING (-2.1 -0.35, -1.5 -0.67)"),
     (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)"),
@@ -58,7 +58,10 @@ test_configurations = [
     (stc.ST_MPolyFromText, ("mpoly", 4326), "constructor", "" , "MULTIPOLYGON (((0 0, 20 0, 20 20, 0 20, 0 0), (5 5, 5 7, 7 7, 7 5, 5 5)))"),
     (stc.ST_MLineFromText, ("mline", ), "constructor", "" , "MULTILINESTRING ((1 2, 3 4), (4 5, 6 7))"),
     (stc.ST_MLineFromText, ("mline", 4326), "constructor", "" , "MULTILINESTRING ((1 2, 3 4), (4 5, 6 7))"),
+    (stc.ST_PointM, ("x", "y", "m", "srid"), "x_y_z_m_srid", "ST_AsEWKT(geom)", "SRID=4326;POINT ZM(1 2 0 100.9)"),
+    (stc.ST_PointZM, ("x", "y", "z", "m", "srid"), "x_y_z_m_srid", "", "POINT Z (1 2 3)"),
     (stc.ST_PointFromText, ("single_point", lambda: f.lit(',')), "constructor", "", "POINT (0 1)"),
+    (stc.ST_PointFromWKB, ("wkbPoint",), "constructor", "", "POINT (10 15)"),
     (stc.ST_MakePoint, ("x", "y", "z"), "constructor", "", "POINT Z (0 1 2)"),
     (stc.ST_PolygonFromEnvelope, ("minx", "miny", "maxx", "maxy"), "min_max_x_y", "", "POLYGON ((0 1, 0 3, 2 3, 2 1, 0 1))"),
     (stc.ST_PolygonFromEnvelope, (0.0, 1.0, 2.0, 3.0), "null", "", "POLYGON ((0 1, 0 3, 2 3, 2 1, 0 1))"),
@@ -238,6 +241,7 @@ wrong_type_configurations = [
     (stc.ST_Point, ("", None)),
     (stc.ST_PointFromText, (None, "")),
     (stc.ST_PointFromText, ("", None)),
+    (stc.ST_PointFromWKB, (None,)),
     (stc.ST_PolygonFromEnvelope, (None, "", "", "")),
     (stc.ST_PolygonFromEnvelope, ("", None, "", "")),
     (stc.ST_PolygonFromEnvelope, ("", "", None, "")),
@@ -399,9 +403,10 @@ class TestDataFrameAPI(TestBase):
 
     @pytest.fixture
     def base_df(self, request):
-        wkb = '0102000000020000000000000084d600c00000000080b5d6bf00000060e1eff7bf00000080075de5bf'
         mpoly = 'MULTIPOLYGON(((0 0 ,20 0 ,20 20 ,0 20 ,0 0 ),(5 5 ,5 7 ,7 7 ,7 5 ,5 5)))'
         mline = 'MULTILINESTRING((1 2, 3 4), (4 5, 6 7))'
+        wkbLine = '0102000000020000000000000084d600c00000000080b5d6bf00000060e1eff7bf00000080075de5bf'
+        wkbPoint = '010100000000000000000024400000000000002e40'
         geojson = "{ \"type\": \"Feature\", \"properties\": { \"prop\": \"01\" }, \"geometry\": { \"type\": \"Point\", \"coordinates\": [ 0.0, 1.0 ] }},"
         gml_string = "<gml:LineString srsName=\"EPSG:4269\"><gml:coordinates>-71.16,42.25 -71.17,42.25 -71.18,42.25</gml:coordinates></gml:LineString>"
         kml_string = "<LineString><coordinates>-71.16,42.26 -71.17,42.26</coordinates></LineString>"
@@ -413,9 +418,10 @@ class TestDataFrameAPI(TestBase):
                 "2.0 AS z",
                 "'0.0,1.0' AS single_point",
                 "'0.0,0.0,1.0,0.0,1.0,1.0,0.0,0.0' AS multiple_point",
-                f"X'{wkb}' AS wkb",
                 f"'{mpoly}' AS mpoly",
                 f"'{mline}' AS mline",
+                f"X'{wkbLine}' AS wkbLine",
+                f"X'{wkbPoint}' AS wkbPoint",
                 f"'{geojson}' AS geojson",
                 "'s00twy01mt' AS geohash",
                 f"'{gml_string}' AS gml",
@@ -431,6 +437,8 @@ class TestDataFrameAPI(TestBase):
             return TestDataFrameAPI.spark.sql("SELECT 'SRID=4269;LINESTRING (1 2, 3 4)' AS ewkt")
         elif request.param == "min_max_x_y":
             return TestDataFrameAPI.spark.sql("SELECT 0.0 AS minx, 1.0 AS miny, 2.0 AS maxx, 3.0 AS maxy")
+        elif request.param == "x_y_z_m_srid":
+            return TestDataFrameAPI.spark.sql("SELECT 1.0 AS x, 2.0 AS y, 3.0 AS z, 100.9 AS m, 4326 AS srid")
         elif request.param == "multipoint_geom":
             return TestDataFrameAPI.spark.sql("SELECT ST_GeomFromWKT('MULTIPOINT((10 40), (40 30), (20 20), (30 10))') AS multipoint")
         elif request.param == "null":
diff --git a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestConstructors.java b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestConstructors.java
index 477f5f0c2..dd6e6bc4f 100644
--- a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestConstructors.java
+++ b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestConstructors.java
@@ -89,6 +89,21 @@ public class TestConstructors extends TestBase{
                 "POINT (0 1)"
         );
     }
+
+    @Test
+    public void test_ST_PointFromWKB() {
+        registerUDF("ST_PointFromWKB", byte[].class);
+        registerUDF("ST_AsEWKT", byte[].class);
+        verifySqlSingleRes(
+                "select sedona.ST_AsText(sedona.ST_PointFromWKB(ST_ASWKB(to_geometry('POINT (10.0 15.0)'))))",
+                "POINT (10 15)"
+        );
+        registerUDF("ST_PointFromWKB", byte[].class, int.class);
+        verifySqlSingleRes(
+                "select sedona.ST_AsEWKT(sedona.ST_PointFromWKB(ST_ASWKB(to_geometry('POINT (10.0 15.0)')), 4326))",
+                "SRID=4326;POINT (10 15)"
+        );
+    }
     @Test
     public void test_ST_GeomFromEWKB() {
         registerUDF("ST_GeomFromEWKB", byte[].class);
diff --git a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java
index c5f8b362c..14c00a64f 100644
--- a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java
+++ b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java
@@ -950,6 +950,20 @@ public class UDFs {
         );
     }
 
+    @UDFAnnotations.ParamMeta(argNames = {"wkb"})
+    public static byte[] ST_PointFromWKB(byte[] wkb) throws ParseException {
+        return GeometrySerde.serialize(
+                Constructors.pointFromWKB(wkb, 0)
+        );
+    }
+
+    @UDFAnnotations.ParamMeta(argNames = {"wkb", "srid"})
+    public static byte[] ST_PointFromWKB(byte[] wkb, int srid) throws ParseException {
+        return GeometrySerde.serialize(
+                Constructors.pointFromWKB(wkb, srid)
+        );
+    }
+
     @UDFAnnotations.ParamMeta(argNames = {"geometry", "srid"})
     public static byte[] ST_Polygon(byte[] geometry, int srid) {
         return GeometrySerde.serialize(
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 988a536cb..0556fb641 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
@@ -38,6 +38,7 @@ object Catalog {
     // Expression for vectors
     function[GeometryType](),
     function[ST_PointFromText](),
+    function[ST_PointFromWKB](),
     function[ST_PolygonFromText](),
     function[ST_LineStringFromText](),
     function[ST_GeomFromText](0),
@@ -53,6 +54,8 @@ object Catalog {
     function[ST_Point](),
     function[ST_MakePoint](null, null),
     function[ST_PointZ](0),
+    function[ST_PointM](0),
+    function[ST_PointZM](0),
     function[ST_PolygonFromEnvelope](),
     function[ST_Contains](),
     function[ST_Intersects](),
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 6ed6d7335..5ca0a56bc 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
@@ -188,6 +188,51 @@ case class ST_GeomFromEWKB(inputExpressions: Seq[Expression])
   }
 }
 
+
+case class ST_PointFromWKB(inputExpressions: Seq[Expression])
+  extends Expression with FoldableExpression with ImplicitCastInputTypes with CodegenFallback with UserDataGeneratator {
+
+  // Validate the number of input expressions (1 or 2)
+  assert(inputExpressions.length >= 1 && inputExpressions.length <= 2)
+
+  override def nullable: Boolean = true
+
+  override def eval(inputRow: InternalRow): Any = {
+    val wkb = inputExpressions.head.eval(inputRow)
+    val srid = if (inputExpressions.length > 1) inputExpressions(1).eval(inputRow) else 0
+
+    wkb match {
+      case geomString: UTF8String =>
+        // Parse UTF-8 encoded WKB string
+        val geom = Constructors.pointFromText(geomString.toString, "wkb")
+        if (geom.getGeometryType == "Point") {
+          geom.setSRID(srid.asInstanceOf[Int])
+          geom.toGenericArrayData
+        } else {
+          null
+        }
+
+      case wkbArray: Array[Byte] =>
+        // Convert raw WKB byte array to geometry
+        Constructors.pointFromWKB(wkbArray, srid.asInstanceOf[Int]).toGenericArrayData
+
+      case _ => null
+    }
+  }
+
+  override def dataType: DataType = GeometryUDT
+
+  override def inputTypes: Seq[AbstractDataType] =
+    if (inputExpressions.length == 1) Seq(TypeCollection(StringType, BinaryType))
+    else Seq(TypeCollection(StringType, BinaryType), IntegerType)
+
+  override def children: Seq[Expression] = inputExpressions
+
+  protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]): ST_PointFromWKB = {
+    copy(inputExpressions = newChildren)
+  }
+}
+
 /**
   * Return a Geometry from a GeoJSON string
   *
@@ -246,6 +291,27 @@ case class ST_PointZ(inputExpressions: Seq[Expression])
   }
 }
 
+/**
+ * Return a Point from X, Y, M and srid
+ *
+ * @param inputExpressions This function takes 4 parameter which are point x, y, m and srid (default 0).
+ */
+case class ST_PointM(inputExpressions: Seq[Expression])
+  extends InferredExpression(Constructors.pointM _) {
+
+  protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
+    copy(inputExpressions = newChildren)
+  }
+}
+
+case class ST_PointZM(inputExpressions: Seq[Expression])
+  extends InferredExpression(Constructors.pointZM _) {
+
+  protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
+    copy(inputExpressions = newChildren)
+  }
+}
+
 case class ST_MakePoint(inputExpressions: Seq[Expression])
   extends InferredExpression(nullTolerantInferrableFunction4(Constructors.makePoint)) {
 
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 02d055a1f..7c99844f4 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
@@ -81,6 +81,32 @@ object st_constructors extends DataFrameAPI {
 
   def ST_PointZ(x: Double, y: Double, z: Double, srid: Int): Column = wrapExpression[ST_PointZ](x, y, z, srid)
 
+  def ST_PointM(x: Column, y: Column, m: Column): Column = wrapExpression[ST_PointM](x, y, m, 0)
+  def ST_PointM(x: String, y: String, m: String): Column = wrapExpression[ST_PointM](x, y, m, 0)
+  def ST_PointM(x: Double, y: Double, m: Double): Column = wrapExpression[ST_PointM](x, y, m, 0)
+
+  def ST_PointM(x: Column, y: Column, m: Column, srid: Column): Column = wrapExpression[ST_PointM](x, y, m, srid)
+
+  def ST_PointM(x: String, y: String, m: String, srid: Column): Column = wrapExpression[ST_PointM](x, y, m, srid)
+
+  def ST_PointM(x: Double, y: Double, m: Double, srid: Int): Column = wrapExpression[ST_PointM](x, y, m, srid)
+
+  def ST_PointZM(x: Column, y: Column, z: Column, m: Column): Column = wrapExpression[ST_PointZM](x, y, z, m, 0)
+  def ST_PointZM(x: String, y: String, z: String, m: String): Column = wrapExpression[ST_PointZM](x, y, z, m, 0)
+  def ST_PointZM(x: Double, y: Double, z: Double, m: Double): Column = wrapExpression[ST_PointZM](x, y, z, m, 0)
+
+  def ST_PointZM(x: Column, y: Column, z: Column, m: Column, srid: Column): Column = wrapExpression[ST_PointZM](x, y, z, m, srid)
+
+  def ST_PointZM(x: String, y: String, z: String, m: String, srid: Column): Column = wrapExpression[ST_PointZM](x, y, z, m, srid)
+
+  def ST_PointZM(x: Double, y: Double, z: Double, m: Double, srid: Int): Column = wrapExpression[ST_PointZM](x, y, z, m, srid)
+
+  def ST_PointFromWKB(wkb: Column): Column = wrapExpression[ST_PointFromWKB](wkb, 0)
+  def ST_PointFromWKB(wkb: String): Column = wrapExpression[ST_PointFromWKB](wkb, 0)
+
+  def ST_PointFromWKB(wkb: Column, srid: Column): Column = wrapExpression[ST_PointFromWKB](wkb, srid)
+  def ST_PointFromWKB(wkb: String, srid: Int): Column = wrapExpression[ST_PointFromWKB](wkb, srid)
+
   def ST_MakePoint(x: Column, y: Column): Column = wrapExpression[ST_MakePoint](x, y, null, null)
   def ST_MakePoint(x: String, y: String): Column = wrapExpression[ST_MakePoint](x, y, null, null)
   def ST_MakePoint(x: Double, y: Double): Column = wrapExpression[ST_MakePoint](x, y, null, null)
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 9d6b416bd..024771e05 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
@@ -76,11 +76,26 @@ class constructorTestScala extends TestBaseScala {
       assert(pointDf.count() == 1)
     }
 
+    it("Passed ST_PointZM") {
+      val pointDf = sparkSession.sql("SELECT ST_PointZM(1.2345, 2.3456, 3.4567, 100)")
+      assert(pointDf.count() == 1)
+    }
+
     it("Passed ST_PointZ null safety") {
       val pointDf = sparkSession.sql("SELECT ST_PointZ(null, null, null)")
       assert(pointDf.count() == 1)
     }
 
+    it("Passed ST_PointM") {
+      val pointDf = sparkSession.sql("SELECT ST_PointM(1.2345, 2.3456, 3.4567)")
+      assert(pointDf.count() == 1)
+    }
+
+    it("Passed ST_PointM null safety") {
+      val pointDf = sparkSession.sql("SELECT ST_PointM(null, null, null)")
+      assert(pointDf.count() == 1)
+    }
+
     it("Passed ST_PolygonFromEnvelope") {
       val polygonDF = sparkSession.sql("select ST_PolygonFromEnvelope(double(1.234),double(2.234),double(3.345),double(3.345))")
       assert(polygonDF.count() == 1)
@@ -94,6 +109,50 @@ class constructorTestScala extends TestBaseScala {
       assert(pointDf.count() == 121960)
     }
 
+    it("Passed ST_PointFromWKB") {
+      val geometryDf = Seq(
+        "0101000000000000000000F03F0000000000000040",
+        "0102000000020000000000000084d600c00000000080b5d6bf00000060e1eff7bf00000080075de5bf",
+        "010100000000000000000024400000000000002e40",
+        "0103000000010000000500000000000000000000000000000000000000000000000000f03f000000000000f03f0000000000001440000000000000f03f0000000000001440000000000000000000000000000000000000000000000000"
+      ).map(Tuple1.apply).toDF("wkb")
+
+      geometryDf.createOrReplaceTempView("wkbtable")
+
+      var validPointDf = sparkSession.sql("SELECT ST_PointFromWKB(wkbtable.wkb) FROM wkbtable")
+      validPointDf.show()
+      var rows = validPointDf.collect()
+      assert(rows.length == 4)
+
+      var expectedPoints = Seq("POINT (1 2)", null, "POINT (10 15)", null)
+      for (i <- rows.indices) {
+        if (expectedPoints(i) == null) {
+          assert(rows(i).isNullAt(0))
+        } else {
+          assert(rows(i).getAs[Geometry](0).toString == expectedPoints(i))
+        }
+      }
+
+      validPointDf = sparkSession.sql("SELECT ST_AsEWKT(ST_PointFromWKB(wkbtable.wkb, 4326)) FROM wkbtable")
+      validPointDf.show()
+      rows = validPointDf.collect()
+      assert(rows.length == 4)
+
+      expectedPoints = Seq("SRID=4326;POINT (1 2)", null, "SRID=4326;POINT (10 15)", null)
+      for (i <- rows.indices) {
+        if (expectedPoints(i) == null) {
+          assert(rows(i).isNullAt(0))
+        } else {
+          assert(rows(i).get(0).toString == expectedPoints(i))
+        }
+      }
+
+      intercept[Exception] {
+        sparkSession.sql("SELECT ST_PointFromWKB('invalid')").collect()
+      }
+    }
+
+
     it("Passed ST_GeomFromWKT") {
       var polygonWktDf = sparkSession.read.format("csv").option("delimiter", "\t").option("header", "false").load(mixedWktGeometryInputLocation)
       polygonWktDf.createOrReplaceTempView("polygontable")
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 01129a4fa..5de1f4c73 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
@@ -25,7 +25,7 @@ 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, Polygon}
+import org.locationtech.jts.geom.{Geometry, Point, Polygon}
 import org.locationtech.jts.io.WKTWriter
 import org.locationtech.jts.operation.buffer.BufferParameters
 
@@ -58,6 +58,14 @@ class dataFrameAPITestScala extends TestBaseScala {
       assert(actual == 40.0)
     }
 
+    it("Passed st_pointm") {
+      val pointDf = sparkSession.sql("SELECT ST_PointM(1,2,100) as point1, ST_PointM(1,2,100,4326) as point2")
+      val point1 = pointDf.select(ST_AsEWKT("point1")).take(1)(0).getString(0)
+      val point2 = pointDf.select(ST_AsEWKT("point2")).take(1)(0).getString(0)
+      assertEquals("POINT ZM(1 2 0 100)", point1)
+      assertEquals("SRID=4326;POINT ZM(1 2 0 100)", point2)
+    }
+
     it("passed st_makepoint") {
       val df = sparkSession.sql("SELECT 0.0 AS x, 1.0 AS y, 2.0 AS z").select(ST_AsText(ST_MakePoint("x", "y", "z")))
       val actualResult = df.take(1)(0).get(0).asInstanceOf[String]
@@ -72,6 +80,19 @@ class dataFrameAPITestScala extends TestBaseScala {
       assert(actualResult == expectedResult)
     }
 
+    it("passed st_pointfromwkb") {
+      val wkbSeq = Seq[Array[Byte]](Array[Byte](1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 64, 0, 0, 0, 0, 0, 0, 46, 64))
+      val df = wkbSeq.toDF("wkb").select(ST_PointFromWKB("wkb"))
+      val actualResult = df.take(1)(0).get(0).asInstanceOf[Geometry].toText()
+      val expectedResult = "POINT (10 15)"
+      assert(actualResult == expectedResult)
+
+      val wkbStringSeq = Seq("010100000000000000000024400000000000002E40")
+      val dfWithString = wkbStringSeq.toDF("wkb").select(ST_PointFromWKB("wkb"))
+      val actualStringResult = dfWithString.take(1)(0).get(0).asInstanceOf[Geometry].toText()
+      assert(actualStringResult == expectedResult)
+    }
+
     it("passed st_polygonfromtext") {
       val df = sparkSession.sql("SELECT '0.0,0.0,1.0,0.0,1.0,1.0,0.0,0.0' AS c").select(ST_PolygonFromText($"c", lit(',')))
       val actualResult = df.take(1)(0).get(0).asInstanceOf[Geometry].toText()
@@ -1461,5 +1482,13 @@ class dataFrameAPITestScala extends TestBaseScala {
       val esriValidityReason = esriValidityTable.take(1)(0).getString(0)
       assertEquals("Interior is disconnected at or near point (1.0, 1.0, NaN)", esriValidityReason)
     }
+
+    it("Passed ST_PointZM") {
+      val pointDf = sparkSession.sql("SELECT ST_PointZM(1,2,3,100) as point1, ST_PointZM(1,2,3,100,4326) as point2")
+      val point1 = pointDf.select(ST_AsEWKT("point1")).take(1)(0).getString(0)
+      val point2 = pointDf.select(ST_AsEWKT("point2")).take(1)(0).getString(0)
+      assertEquals("POINT ZM(1 2 3 100)", point1)
+      assertEquals("SRID=4326;POINT ZM(1 2 3 100)", point2)
+    }
   }
 }