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

yiguolei pushed a commit to branch branch-4.0
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-4.0 by this push:
     new 51431716560 branch-4.0: [feature](geo) support 3 spatial functions: 
ST_Distance, ST_GeometryType, ST_Length #60170 (#60748)
51431716560 is described below

commit 5143171656078d0e2bbade905ad4f419227439a1
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Sat Feb 14 20:26:36 2026 +0800

    branch-4.0: [feature](geo) support 3 spatial functions: ST_Distance, 
ST_GeometryType, ST_Length #60170 (#60748)
    
    Cherry-picked from #60170
    
    Co-authored-by: Xichun Zhao <[email protected]>
---
 be/src/geo/geo_types.cpp                           | 371 ++++++++++++++
 be/src/geo/geo_types.h                             |  26 +-
 be/src/vec/functions/functions_geo.cpp             | 212 ++++++++
 be/test/geo/geo_types_test.cpp                     | 534 +++++++++++++++++++++
 .../doris/catalog/BuiltinScalarFunctions.java      |   6 +
 .../expressions/functions/scalar/StDistance.java   |  75 +++
 .../functions/scalar/StGeometryType.java           |  74 +++
 .../expressions/functions/scalar/StLength.java     |  75 +++
 .../expressions/visitor/ScalarFunctionVisitor.java |  15 +
 .../spatial_functions/test_gis_function.out        | 225 +++++++++
 .../spatial_functions/test_gis_function.groovy     | 111 +++++
 11 files changed, 1723 insertions(+), 1 deletion(-)

diff --git a/be/src/geo/geo_types.cpp b/be/src/geo/geo_types.cpp
index 6c6f612ca1b..19fca11c8eb 100644
--- a/be/src/geo/geo_types.cpp
+++ b/be/src/geo/geo_types.cpp
@@ -25,6 +25,7 @@
 #include <s2/s2cap.h>
 #include <s2/s2earth.h>
 #include <s2/s2edge_crosser.h>
+#include <s2/s2edge_distances.h>
 #include <s2/s2latlng.h>
 #include <s2/s2loop.h>
 #include <s2/s2point.h>
@@ -38,6 +39,7 @@
 // IWYU pragma: no_include <bits/std_abs.h>
 #include <cmath>
 #include <iomanip>
+#include <limits>
 #include <sstream>
 #include <utility>
 #include <vector>
@@ -1630,5 +1632,374 @@ std::string GeoShape::as_binary(GeoShape* rhs) {
     return res;
 }
 
+// Helper function to compute distance from a point to a line segment
+double distance_point_to_segment(const S2Point& point, const S2Point& 
line_start,
+                                 const S2Point& line_end) {
+    S1Angle angle = S2::GetDistance(point, line_start, line_end);
+    return S2Earth::ToMeters(angle);
+}
+
+// Helper function to compute distance from a point to a polyline
+double distance_point_to_polyline(const S2Point& point, const S2Polyline* 
polyline) {
+    if (polyline->num_vertices() == 0) {
+        return std::numeric_limits<double>::max();
+    }
+    if (polyline->num_vertices() == 1) {
+        return S2Earth::GetDistanceMeters(point, polyline->vertex(0));
+    }
+
+    S1Angle min_angle = S1Angle::Infinity();
+    for (int i = 0; i < polyline->num_vertices() - 1; ++i) {
+        const S2Point& p1 = polyline->vertex(i);
+        const S2Point& p2 = polyline->vertex(i + 1);
+
+        S1Angle dist = S2::GetDistance(point, p1, p2);
+        if (dist < min_angle) {
+            min_angle = dist;
+        }
+    }
+
+    return S2Earth::ToMeters(min_angle);
+}
+
+// Helper function to compute distance from a point to a polygon
+double distance_point_to_polygon(const S2Point& point, const S2Polygon* 
polygon) {
+    // Check if point is inside polygon
+    if (polygon->Contains(point)) {
+        return 0.0;
+    }
+
+    // Find minimum distance to polygon boundary
+    S1Angle min_angle = S1Angle::Infinity();
+
+    for (int i = 0; i < polygon->num_loops(); ++i) {
+        const S2Loop* loop = polygon->loop(i);
+
+        for (int j = 0; j < loop->num_vertices(); ++j) {
+            const S2Point& p1 = loop->vertex(j);
+            const S2Point& p2 = loop->vertex((j + 1) % loop->num_vertices());
+
+            S1Angle dist = S2::GetDistance(point, p1, p2);
+            if (dist < min_angle) {
+                min_angle = dist;
+            }
+        }
+    }
+
+    return S2Earth::ToMeters(min_angle);
+}
+
+double GeoPoint::Length() const {
+    // Point has no length
+    return 0.0;
+}
+
+double GeoLine::Length() const {
+    // GeoLine is always valid with at least 2 vertices (guaranteed by 
constructor)
+    double total_length = 0.0;
+    for (int i = 0; i < _polyline->num_vertices() - 1; ++i) {
+        const S2Point& p1 = _polyline->vertex(i);
+        const S2Point& p2 = _polyline->vertex(i + 1);
+
+        S2LatLng lat_lng1(p1);
+        S2LatLng lat_lng2(p2);
+
+        // Calculate distance in meters using S2Earth
+        double distance = S2Earth::GetDistanceMeters(lat_lng1, lat_lng2);
+        total_length += distance;
+    }
+
+    return total_length;
+}
+
+double GeoPolygon::Length() const {
+    // GeoPolygon is always valid with at least one loop (guaranteed by 
constructor)
+    double perimeter = 0.0;
+
+    for (int loop_idx = 0; loop_idx < _polygon->num_loops(); ++loop_idx) {
+        const S2Loop* loop = _polygon->loop(loop_idx);
+        for (int i = 0; i < loop->num_vertices(); ++i) {
+            const S2Point& p1 = loop->vertex(i);
+            const S2Point& p2 = loop->vertex((i + 1) % loop->num_vertices());
+
+            S2LatLng lat_lng1(p1);
+            S2LatLng lat_lng2(p2);
+
+            // Calculate distance in meters using S2Earth
+            double distance = S2Earth::GetDistanceMeters(lat_lng1, lat_lng2);
+            perimeter += distance;
+        }
+    }
+
+    return perimeter;
+}
+
+double GeoMultiPolygon::Length() const {
+    double total_length = 0.0;
+
+    // Calculate the perimeter of all polygons
+    for (const auto& polygon : _polygons) {
+        total_length += polygon->Length();
+    }
+
+    return total_length;
+}
+
+double GeoCircle::Length() const {
+    // GeoCircle is always valid (guaranteed by constructor)
+    // Get the radius in meters
+    double radius_meters = S2Earth::ToMeters(_cap->radius());
+
+    // Calculate circumference: 2 * pi * r
+    return 2.0 * M_PI * radius_meters;
+}
+
+double GeoPoint::Distance(const GeoShape* rhs) const {
+    // rhs is guaranteed to be valid by StDistance (functions_geo.cpp)
+    switch (rhs->type()) {
+    case GEO_SHAPE_POINT: {
+        const GeoPoint* point = static_cast<const GeoPoint*>(rhs);
+        S2LatLng this_ll = S2LatLng(*_point);
+        S2LatLng other_ll = S2LatLng(*point->point());
+        return S2Earth::GetDistanceMeters(this_ll, other_ll);
+    }
+    case GEO_SHAPE_LINE_STRING: {
+        const GeoLine* line = static_cast<const GeoLine*>(rhs);
+        return distance_point_to_polyline(*_point, line->polyline());
+    }
+    case GEO_SHAPE_POLYGON: {
+        const GeoPolygon* polygon = static_cast<const GeoPolygon*>(rhs);
+        return distance_point_to_polygon(*_point, polygon->polygon());
+    }
+    case GEO_SHAPE_CIRCLE: {
+        const GeoCircle* circle = static_cast<const GeoCircle*>(rhs);
+        S2LatLng this_ll = S2LatLng(*_point);
+        S2LatLng center_ll = S2LatLng(circle->circle()->center());
+        double dist_to_center = S2Earth::GetDistanceMeters(this_ll, center_ll);
+        double circle_radius = S2Earth::ToMeters(circle->circle()->radius());
+
+        // Distance from point to circle is distance to center minus radius
+        if (dist_to_center <= circle_radius + TOLERANCE) {
+            return 0.0; // Point is inside or on the circle boundary
+        }
+        return dist_to_center - circle_radius;
+    }
+    case GEO_SHAPE_MULTI_POLYGON: {
+        return rhs->Distance(this); // Delegate to MultiPolygon's 
implementation
+    }
+    default:
+        return -1.0;
+    }
+}
+
+double GeoLine::Distance(const GeoShape* rhs) const {
+    // rhs is guaranteed to be valid by StDistance (functions_geo.cpp)
+    switch (rhs->type()) {
+    case GEO_SHAPE_POINT: {
+        return rhs->Distance(this); // Reuse Point's Distance implementation
+    }
+    case GEO_SHAPE_LINE_STRING: {
+        const GeoLine* other_line = static_cast<const GeoLine*>(rhs);
+        if (_polyline->Intersects(*other_line->polyline())) {
+            return 0.0;
+        }
+        double min_distance = std::numeric_limits<double>::max();
+
+        // Check distance from each vertex of this line to other line
+        for (int i = 0; i < _polyline->num_vertices(); ++i) {
+            double dist = distance_point_to_polyline(_polyline->vertex(i), 
other_line->polyline());
+            min_distance = std::min(min_distance, dist);
+        }
+
+        // Check distance from each vertex of other line to this line
+        for (int i = 0; i < other_line->polyline()->num_vertices(); ++i) {
+            double dist =
+                    
distance_point_to_polyline(other_line->polyline()->vertex(i), _polyline.get());
+            min_distance = std::min(min_distance, dist);
+        }
+
+        // Handle touching case: if min_distance is within tolerance, lines 
are touching
+        if (min_distance <= TOLERANCE) {
+            return 0.0;
+        }
+        return min_distance;
+    }
+    case GEO_SHAPE_POLYGON: {
+        return rhs->Distance(this); // Delegate to Polygon's implementation
+    }
+    case GEO_SHAPE_CIRCLE: {
+        return rhs->Distance(this); // Delegate to Circle's implementation
+    }
+    case GEO_SHAPE_MULTI_POLYGON: {
+        return rhs->Distance(this); // Delegate to MultiPolygon's 
implementation
+    }
+    default:
+        return -1.0;
+    }
+}
+
+double GeoPolygon::Distance(const GeoShape* rhs) const {
+    // rhs is guaranteed to be valid by StDistance (functions_geo.cpp)
+    switch (rhs->type()) {
+    case GEO_SHAPE_POINT: {
+        return rhs->Distance(this); // Reuse Point's Distance implementation
+    }
+    case GEO_SHAPE_LINE_STRING: {
+        const GeoLine* line = static_cast<const GeoLine*>(rhs);
+        if (_polygon->Intersects(*line->polyline())) {
+            return 0.0;
+        }
+        double min_distance = std::numeric_limits<double>::max();
+
+        // Check distance from each vertex of line to polygon
+        for (int i = 0; i < line->polyline()->num_vertices(); ++i) {
+            double dist = 
distance_point_to_polygon(line->polyline()->vertex(i), _polygon.get());
+            min_distance = std::min(min_distance, dist);
+        }
+
+        // Check distance from each polygon vertex to line
+        for (int i = 0; i < _polygon->num_loops(); ++i) {
+            const S2Loop* loop = _polygon->loop(i);
+            for (int j = 0; j < loop->num_vertices(); ++j) {
+                double dist = distance_point_to_polyline(loop->vertex(j), 
line->polyline());
+                min_distance = std::min(min_distance, dist);
+            }
+        }
+
+        // Handle touching case: if min_distance is within tolerance, they are 
touching
+        if (min_distance <= TOLERANCE) {
+            return 0.0;
+        }
+        return min_distance;
+    }
+    case GEO_SHAPE_POLYGON: {
+        const GeoPolygon* other = static_cast<const GeoPolygon*>(rhs);
+        if (_polygon->Intersects(*other->polygon())) {
+            return 0.0;
+        }
+        double min_distance = std::numeric_limits<double>::max();
+
+        // Check distance from each vertex of this polygon to other polygon
+        for (int i = 0; i < _polygon->num_loops(); ++i) {
+            const S2Loop* loop = _polygon->loop(i);
+            for (int j = 0; j < loop->num_vertices(); ++j) {
+                double dist = distance_point_to_polygon(loop->vertex(j), 
other->polygon());
+                min_distance = std::min(min_distance, dist);
+            }
+        }
+
+        // Check distance from each vertex of other polygon to this polygon
+        for (int i = 0; i < other->polygon()->num_loops(); ++i) {
+            const S2Loop* loop = other->polygon()->loop(i);
+            for (int j = 0; j < loop->num_vertices(); ++j) {
+                double dist = distance_point_to_polygon(loop->vertex(j), 
_polygon.get());
+                min_distance = std::min(min_distance, dist);
+            }
+        }
+
+        // Handle touching case: if min_distance is within tolerance, polygons 
are touching
+        if (min_distance <= TOLERANCE) {
+            return 0.0;
+        }
+        return min_distance;
+    }
+    case GEO_SHAPE_CIRCLE: {
+        return rhs->Distance(this); // Delegate to Circle's implementation
+    }
+    case GEO_SHAPE_MULTI_POLYGON: {
+        return rhs->Distance(this); // Delegate to MultiPolygon's 
implementation
+    }
+    default:
+        return -1.0;
+    }
+}
+
+double GeoMultiPolygon::Distance(const GeoShape* rhs) const {
+    // rhs is guaranteed to be valid by StDistance (functions_geo.cpp)
+    double min_distance = std::numeric_limits<double>::max();
+
+    // Calculate minimum distance from any polygon to the other shape
+    for (const auto& polygon : _polygons) {
+        double dist = polygon->Distance(rhs);
+        if (dist >= 0) {
+            min_distance = std::min(min_distance, dist);
+        }
+    }
+
+    return (min_distance == std::numeric_limits<double>::max()) ? -1.0 : 
min_distance;
+}
+
+double GeoCircle::Distance(const GeoShape* rhs) const {
+    // Both rhs and self are guaranteed to be valid by StDistance 
(functions_geo.cpp)
+    double circle_radius = S2Earth::ToMeters(_cap->radius());
+
+    switch (rhs->type()) {
+    case GEO_SHAPE_POINT: {
+        return rhs->Distance(this); // Reuse Point's Distance implementation
+    }
+    case GEO_SHAPE_LINE_STRING: {
+        const GeoLine* line = static_cast<const GeoLine*>(rhs);
+        double min_distance = std::numeric_limits<double>::max();
+
+        // Find minimum distance from circle center to line
+        for (int i = 0; i < line->polyline()->num_vertices() - 1; ++i) {
+            double dist = distance_point_to_segment(_cap->center(), 
line->polyline()->vertex(i),
+                                                    line->polyline()->vertex(i 
+ 1));
+            min_distance = std::min(min_distance, dist);
+        }
+
+        if (min_distance <= circle_radius + TOLERANCE) {
+            return 0.0;
+        }
+        return min_distance - circle_radius;
+    }
+    case GEO_SHAPE_POLYGON: {
+        const GeoPolygon* polygon = static_cast<const GeoPolygon*>(rhs);
+
+        // If center is inside polygon, distance is 0
+        if (polygon->polygon()->Contains(_cap->center())) {
+            return 0.0;
+        }
+
+        // Find minimum distance from circle center to polygon boundary
+        double min_distance = std::numeric_limits<double>::max();
+
+        for (int i = 0; i < polygon->polygon()->num_loops(); ++i) {
+            const S2Loop* loop = polygon->polygon()->loop(i);
+            for (int j = 0; j < loop->num_vertices(); ++j) {
+                double dist =
+                        distance_point_to_segment(_cap->center(), 
loop->vertex(j),
+                                                  loop->vertex((j + 1) % 
loop->num_vertices()));
+                min_distance = std::min(min_distance, dist);
+            }
+        }
+
+        if (min_distance <= circle_radius + TOLERANCE) {
+            return 0.0;
+        }
+        return min_distance - circle_radius;
+    }
+    case GEO_SHAPE_CIRCLE: {
+        const GeoCircle* other = static_cast<const GeoCircle*>(rhs);
+        double other_radius = S2Earth::ToMeters(other->circle()->radius());
+        S2LatLng this_center_ll = S2LatLng(_cap->center());
+        S2LatLng other_center_ll = S2LatLng(other->circle()->center());
+        double dist_centers = S2Earth::GetDistanceMeters(this_center_ll, 
other_center_ll);
+
+        // Distance between circles is distance between centers minus sum of 
radii
+        double sum_radii = circle_radius + other_radius;
+        if (dist_centers <= sum_radii + TOLERANCE) {
+            return 0.0; // Circles intersect or touch
+        }
+        return dist_centers - sum_radii;
+    }
+    case GEO_SHAPE_MULTI_POLYGON: {
+        return rhs->Distance(this); // Delegate to MultiPolygon's 
implementation
+    }
+    default:
+        return -1.0;
+    }
+}
+
 #include "common/compile_check_avoid_end.h"
 } // namespace doris
diff --git a/be/src/geo/geo_types.h b/be/src/geo/geo_types.h
index 9b2543b5227..09eeb44c742 100644
--- a/be/src/geo/geo_types.h
+++ b/be/src/geo/geo_types.h
@@ -70,6 +70,12 @@ public:
 
     virtual bool touches(const GeoShape* rhs) const { return false; }
 
+    virtual std::string GeometryType() const = 0;
+
+    virtual double Length() const { return 0.0; }
+
+    virtual double Distance(const GeoShape* rhs) const { return -1.0; }
+
     virtual std::string to_string() const { return ""; }
     static std::string as_binary(GeoShape* rhs);
 
@@ -108,9 +114,14 @@ public:
     static bool ComputeAngle(GeoPoint* p1, GeoPoint* p2, GeoPoint* p3, double* 
angle);
     static bool ComputeAzimuth(GeoPoint* p1, GeoPoint* p2, double* angle);
 
+    std::string GeometryType() const override { return "ST_POINT"; }
     std::string to_string() const override;
     std::string as_wkt() const override;
 
+    double Length() const override;
+
+    double Distance(const GeoShape* rhs) const override;
+
     double x() const;
     double y() const;
 
@@ -140,8 +151,13 @@ public:
     GeoShapeType type() const override { return GEO_SHAPE_LINE_STRING; }
     const S2Polyline* polyline() const { return _polyline.get(); }
 
+    std::string GeometryType() const override { return "ST_LINESTRING"; }
     std::string as_wkt() const override;
 
+    double Length() const override;
+
+    double Distance(const GeoShape* rhs) const override;
+
     int numPoint() const;
     const S2Point* getPoint(int i) const;
 
@@ -174,11 +190,13 @@ public:
 
     bool polygon_touch_point(const S2Polygon* polygon, const S2Point* point) 
const;
     bool polygon_touch_polygon(const S2Polygon* polygon1, const S2Polygon* 
polygon2) const;
-
+    std::string GeometryType() const override { return "ST_POLYGON"; }
     std::string as_wkt() const override;
 
     int numLoops() const;
     double getArea() const;
+    double Length() const override;
+    double Distance(const GeoShape* rhs) const override;
     S2Loop* getLoop(int i) const;
 
 protected:
@@ -207,9 +225,12 @@ public:
     bool disjoint(const GeoShape* rhs) const override;
     bool touches(const GeoShape* rhs) const override;
     bool contains(const GeoShape* rhs) const override;
+    std::string GeometryType() const override { return "ST_MULTIPOLYGON"; }
     std::string as_wkt() const override;
 
     double getArea() const;
+    double Length() const override;
+    double Distance(const GeoShape* rhs) const override;
 
 protected:
     void encode(std::string* buf) override;
@@ -236,9 +257,12 @@ public:
     bool disjoint(const GeoShape* rhs) const override;
     bool touches(const GeoShape* rhs) const override;
     bool contains(const GeoShape* rhs) const override;
+    std::string GeometryType() const override { return "ST_CIRCLE"; }
     std::string as_wkt() const override;
 
     double getArea() const;
+    double Length() const override;
+    double Distance(const GeoShape* rhs) const override;
 
 protected:
     void encode(std::string* buf) override;
diff --git a/be/src/vec/functions/functions_geo.cpp 
b/be/src/vec/functions/functions_geo.cpp
index c57d8800687..4bb4ab2bc08 100644
--- a/be/src/vec/functions/functions_geo.cpp
+++ b/be/src/vec/functions/functions_geo.cpp
@@ -23,6 +23,7 @@
 #include <boost/iterator/iterator_facade.hpp>
 #include <utility>
 
+#include "common/compiler_util.h"
 #include "geo/geo_common.h"
 #include "geo/geo_types.h"
 #include "runtime/define_primitive_type.h"
@@ -708,6 +709,214 @@ struct StAsBinary {
     }
 };
 
+struct StLength {
+    static constexpr auto NAME = "st_length";
+    static const size_t NUM_ARGS = 1;
+    using Type = DataTypeFloat64;
+    static Status execute(Block& block, const ColumnNumbers& arguments, size_t 
result) {
+        DCHECK_EQ(arguments.size(), 1);
+        auto return_type = block.get_data_type(result);
+
+        auto col = 
block.get_by_position(arguments[0]).column->convert_to_full_column_if_const();
+        const auto size = col->size();
+        auto res = ColumnFloat64::create();
+        res->reserve(size);
+        auto null_map = ColumnUInt8::create(size, 0);
+        auto& null_map_data = null_map->get_data();
+
+        std::unique_ptr<GeoShape> shape;
+        for (int row = 0; row < size; ++row) {
+            auto shape_value = col->get_data_at(row);
+            shape = GeoShape::from_encoded(shape_value.data, shape_value.size);
+            if (!shape) {
+                null_map_data[row] = 1;
+                res->insert_default();
+                continue;
+            }
+
+            double length = shape->Length();
+            res->insert_value(length);
+        }
+
+        block.replace_by_position(result,
+                                  ColumnNullable::create(std::move(res), 
std::move(null_map)));
+        return Status::OK();
+    }
+};
+
+struct StGeometryType {
+    static constexpr auto NAME = "st_geometrytype";
+    static const size_t NUM_ARGS = 1;
+    using Type = DataTypeString;
+    static Status execute(Block& block, const ColumnNumbers& arguments, size_t 
result) {
+        DCHECK_EQ(arguments.size(), 1);
+        auto return_type = block.get_data_type(result);
+
+        auto col = 
block.get_by_position(arguments[0]).column->convert_to_full_column_if_const();
+        const auto size = col->size();
+        auto res = ColumnString::create();
+        auto null_map = ColumnUInt8::create(size, 0);
+        auto& null_map_data = null_map->get_data();
+
+        std::unique_ptr<GeoShape> shape;
+        for (int row = 0; row < size; ++row) {
+            auto shape_value = col->get_data_at(row);
+            shape = GeoShape::from_encoded(shape_value.data, shape_value.size);
+            if (!shape) {
+                null_map_data[row] = 1;
+                res->insert_default();
+                continue;
+            }
+
+            auto geo_type = shape->GeometryType();
+            res->insert_data(geo_type.data(), geo_type.size());
+        }
+
+        block.replace_by_position(result,
+                                  ColumnNullable::create(std::move(res), 
std::move(null_map)));
+        return Status::OK();
+    }
+};
+
+struct StDistance {
+    static constexpr auto NAME = "st_distance";
+    static const size_t NUM_ARGS = 2;
+    using Type = DataTypeFloat64;
+
+    static Status execute(Block& block, const ColumnNumbers& arguments, size_t 
result) {
+        DCHECK_EQ(arguments.size(), 2);
+        auto return_type = block.get_data_type(result);
+        const auto& [left_column, left_const] =
+                unpack_if_const(block.get_by_position(arguments[0]).column);
+        const auto& [right_column, right_const] =
+                unpack_if_const(block.get_by_position(arguments[1]).column);
+
+        const auto size = std::max(left_column->size(), right_column->size());
+
+        auto res = ColumnFloat64::create();
+        res->reserve(size);
+        auto null_map = ColumnUInt8::create(size, 0);
+        auto& null_map_data = null_map->get_data();
+
+        if (left_const) {
+            const_vector(left_column, right_column, res, null_map_data, size);
+        } else if (right_const) {
+            vector_const(left_column, right_column, res, null_map_data, size);
+        } else {
+            vector_vector(left_column, right_column, res, null_map_data, size);
+        }
+        block.replace_by_position(result,
+                                  ColumnNullable::create(std::move(res), 
std::move(null_map)));
+        return Status::OK();
+    }
+
+private:
+    static bool decode_shape(const StringRef& value, 
std::unique_ptr<GeoShape>& shape) {
+        shape = GeoShape::from_encoded(value.data, value.size);
+        return static_cast<bool>(shape);
+    }
+
+    static void loop_do(StringRef& lhs_value, StringRef& rhs_value,
+                        std::vector<std::unique_ptr<GeoShape>>& shapes,
+                        ColumnFloat64::MutablePtr& res, NullMap& null_map, int 
row) {
+        StringRef* strs[2] = {&lhs_value, &rhs_value};
+        for (int i = 0; i < 2; ++i) {
+            if (!decode_shape(*strs[i], shapes[i])) {
+                null_map[row] = 1;
+                res->insert_default();
+                return;
+            }
+        }
+        double distance = shapes[0]->Distance(shapes[1].get());
+        if (UNLIKELY(distance < 0)) {
+            null_map[row] = 1;
+            res->insert_default();
+            return;
+        }
+        res->insert_value(distance);
+    }
+
+    static void const_vector(const ColumnPtr& left_column, const ColumnPtr& 
right_column,
+                             ColumnFloat64::MutablePtr& res, NullMap& 
null_map, const size_t size) {
+        const auto* left_string = assert_cast<const 
ColumnString*>(left_column.get());
+        const auto* right_string = assert_cast<const 
ColumnString*>(right_column.get());
+
+        auto lhs_value = left_string->get_data_at(0);
+        std::unique_ptr<GeoShape> lhs_shape;
+        if (!decode_shape(lhs_value, lhs_shape)) {
+            for (int row = 0; row < size; ++row) {
+                null_map[row] = 1;
+                res->insert_default();
+            }
+            return;
+        }
+
+        std::unique_ptr<GeoShape> rhs_shape;
+        for (int row = 0; row < size; ++row) {
+            auto rhs_value = right_string->get_data_at(row);
+            if (!decode_shape(rhs_value, rhs_shape)) {
+                null_map[row] = 1;
+                res->insert_default();
+                continue;
+            }
+            double distance = lhs_shape->Distance(rhs_shape.get());
+            if (UNLIKELY(distance < 0)) {
+                null_map[row] = 1;
+                res->insert_default();
+                continue;
+            }
+            res->insert_value(distance);
+        }
+    }
+
+    static void vector_const(const ColumnPtr& left_column, const ColumnPtr& 
right_column,
+                             ColumnFloat64::MutablePtr& res, NullMap& 
null_map, const size_t size) {
+        const auto* left_string = assert_cast<const 
ColumnString*>(left_column.get());
+        const auto* right_string = assert_cast<const 
ColumnString*>(right_column.get());
+
+        auto rhs_value = right_string->get_data_at(0);
+        std::unique_ptr<GeoShape> rhs_shape;
+        if (!decode_shape(rhs_value, rhs_shape)) {
+            for (int row = 0; row < size; ++row) {
+                null_map[row] = 1;
+                res->insert_default();
+            }
+            return;
+        }
+
+        std::unique_ptr<GeoShape> lhs_shape;
+        for (int row = 0; row < size; ++row) {
+            auto lhs_value = left_string->get_data_at(row);
+            if (!decode_shape(lhs_value, lhs_shape)) {
+                null_map[row] = 1;
+                res->insert_default();
+                continue;
+            }
+            double distance = lhs_shape->Distance(rhs_shape.get());
+            if (UNLIKELY(distance < 0)) {
+                null_map[row] = 1;
+                res->insert_default();
+                continue;
+            }
+            res->insert_value(distance);
+        }
+    }
+
+    static void vector_vector(const ColumnPtr& left_column, const ColumnPtr& 
right_column,
+                              ColumnFloat64::MutablePtr& res, NullMap& 
null_map,
+                              const size_t size) {
+        const auto* left_string = assert_cast<const 
ColumnString*>(left_column.get());
+        const auto* right_string = assert_cast<const 
ColumnString*>(right_column.get());
+
+        std::vector<std::unique_ptr<GeoShape>> shapes(2);
+        for (int row = 0; row < size; ++row) {
+            auto lhs_value = left_string->get_data_at(row);
+            auto rhs_value = right_string->get_data_at(row);
+            loop_do(lhs_value, rhs_value, shapes, res, null_map, row);
+        }
+    }
+};
+
 void register_function_geo(SimpleFunctionFactory& factory) {
     factory.register_function<GeoFunction<StPoint>>();
     factory.register_function<GeoFunction<StAsText<StAsWktName>>>();
@@ -735,6 +944,9 @@ void register_function_geo(SimpleFunctionFactory& factory) {
     factory.register_function<GeoFunction<StGeoFromWkb<StGeometryFromWKB>>>();
     factory.register_function<GeoFunction<StGeoFromWkb<StGeomFromWKB>>>();
     factory.register_function<GeoFunction<StAsBinary>>();
+    factory.register_function<GeoFunction<StLength>>();
+    factory.register_function<GeoFunction<StGeometryType>>();
+    factory.register_function<GeoFunction<StDistance>>();
 }
 
 } // namespace doris::vectorized
diff --git a/be/test/geo/geo_types_test.cpp b/be/test/geo/geo_types_test.cpp
index cebb4f9296a..ef5f9083c6f 100644
--- a/be/test/geo/geo_types_test.cpp
+++ b/be/test/geo/geo_types_test.cpp
@@ -1356,6 +1356,540 @@ TEST_F(GeoTypesTest, circle_touches) {
     }
 }
 
+TEST_F(GeoTypesTest, test_geometry_type) {
+    GeoParseStatus status;
+
+    // Test GeoPoint
+    {
+        GeoPoint point;
+        point.from_coord(116.123, 63.546);
+        EXPECT_STREQ("ST_POINT", point.GeometryType().c_str());
+    }
+
+    // Test GeoLineString
+    {
+        const char* wkt = "LINESTRING (30 10, 10 30, 40 40)";
+        auto line = GeoShape::from_wkt(wkt, strlen(wkt), status);
+        EXPECT_NE(nullptr, line.get());
+        EXPECT_STREQ("ST_LINESTRING", line->GeometryType().c_str());
+    }
+
+    // Test GeoPolygon
+    {
+        const char* wkt = "POLYGON ((10 10, 50 10, 50 50, 10 50, 10 10))";
+        auto polygon = GeoShape::from_wkt(wkt, strlen(wkt), status);
+        EXPECT_NE(nullptr, polygon.get());
+        EXPECT_STREQ("ST_POLYGON", polygon->GeometryType().c_str());
+    }
+
+    // Test GeoMultiPolygon
+    {
+        const char* wkt = "MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0)))";
+        auto multi_polygon = GeoShape::from_wkt(wkt, strlen(wkt), status);
+        EXPECT_NE(nullptr, multi_polygon.get());
+        EXPECT_STREQ("ST_MULTIPOLYGON", multi_polygon->GeometryType().c_str());
+    }
+
+    // Test GeoCircle
+    {
+        GeoCircle circle;
+        auto res = circle.init(110.123, 64, 1000);
+        EXPECT_EQ(GEO_PARSE_OK, res);
+        EXPECT_STREQ("ST_CIRCLE", circle.GeometryType().c_str());
+    }
+}
+
+TEST_F(GeoTypesTest, test_length) {
+    GeoParseStatus status;
+
+    // Test GeoPoint - length should be 0
+    {
+        GeoPoint point;
+        point.from_coord(116.123, 63.546);
+        EXPECT_DOUBLE_EQ(0.0, point.Length());
+    }
+
+    // Test GeoLineString - calculate length
+    {
+        const char* wkt = "LINESTRING (0 0, 1 0, 1 1)";
+        auto line = GeoShape::from_wkt(wkt, strlen(wkt), status);
+        EXPECT_NE(nullptr, line.get());
+        double length = line->Length();
+        EXPECT_GT(length, 100000.0);
+        // Line should have two segments: (0,0)-(1,0) and (1,0)-(1,1)
+        // Expected total distance is approximately 2 degrees in Earth distance
+        EXPECT_LT(length, 300000.0); // Less than 300km
+    }
+
+    // Test GeoPolygon - calculate perimeter
+    {
+        const char* wkt = "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))";
+        auto polygon = GeoShape::from_wkt(wkt, strlen(wkt), status);
+        EXPECT_NE(nullptr, polygon.get());
+        double perimeter = polygon->Length();
+        EXPECT_GT(perimeter, 400000.0);
+        // Polygon is 1x1 degree square, perimeter should be roughly 4 times 
one degree
+        EXPECT_LT(perimeter, 500000.0); // Less than 500km
+    }
+
+    // Test GeoPolygon with a hole - perimeter should include inner loop
+    {
+        const char* outer_wkt = "POLYGON ((0 0, 2 0, 2 2, 0 2, 0 0))";
+        const char* hole_wkt =
+                "POLYGON ((0 0, 2 0, 2 2, 0 2, 0 0), (0.5 0.5, 1.5 0.5, 1.5 
1.5, 0.5 1.5, "
+                "0.5 0.5))";
+        auto outer_polygon = GeoShape::from_wkt(outer_wkt, strlen(outer_wkt), 
status);
+        auto hole_polygon = GeoShape::from_wkt(hole_wkt, strlen(hole_wkt), 
status);
+        EXPECT_NE(nullptr, outer_polygon.get());
+        EXPECT_NE(nullptr, hole_polygon.get());
+
+        double outer_perimeter = outer_polygon->Length();
+        double hole_perimeter = hole_polygon->Length();
+        EXPECT_GT(hole_perimeter, outer_perimeter);
+        EXPECT_LT(hole_perimeter, outer_perimeter * 2.0);
+    }
+
+    // Test GeoMultiPolygon - should have non-zero length
+    {
+        const char* wkt = "MULTIPOLYGON (((0 0, 1 0, 1 1, 0 1, 0 0)))";
+        auto multi_polygon = GeoShape::from_wkt(wkt, strlen(wkt), status);
+        EXPECT_NE(nullptr, multi_polygon.get());
+        double length = multi_polygon->Length();
+        EXPECT_GT(length, 400000.0);
+        EXPECT_LT(length, 500000.0); // Less than 500km
+    }
+
+    // Test GeoCircle - length should be circumference (2 * pi * radius in 
meters)
+    {
+        GeoCircle circle;
+        auto res = circle.init(0, 0, 1000); // 1000 meters radius
+        EXPECT_EQ(GEO_PARSE_OK, res);
+        double circumference = circle.Length();
+        EXPECT_GT(circumference, 0.0);
+        // Expected circumference: 2 * pi * 1000 ≈ 6283 meters
+        EXPECT_GT(circumference, 6200.0);
+        EXPECT_LT(circumference, 6300.0);
+    }
+}
+
+TEST_F(GeoTypesTest, test_distance_point) {
+    GeoParseStatus status;
+
+    GeoPoint point1;
+    point1.from_coord(0, 0);
+
+    // ==========================
+    // GeoPoint vs GeoPoint
+    // ==========================
+    {
+        GeoPoint point2;
+        point2.from_coord(0, 0);
+        double dist = point1.Distance(&point2);
+        EXPECT_DOUBLE_EQ(0.0, dist);
+    }
+    {
+        GeoPoint point2;
+        point2.from_coord(1, 0);
+        double dist = point1.Distance(&point2);
+        EXPECT_GT(dist, 0.0);
+        // Distance should be roughly 111km for 1 degree latitude
+        EXPECT_GT(dist, 100000.0);
+        EXPECT_LT(dist, 120000.0);
+    }
+
+    // ==========================
+    // GeoPoint vs GeoLineString
+    // ==========================
+    {
+        const char* wkt = "LINESTRING (0 0, 1 0, 1 1)";
+        auto line = GeoShape::from_wkt(wkt, strlen(wkt), status);
+        EXPECT_NE(nullptr, line.get());
+        double dist = point1.Distance(line.get());
+        EXPECT_DOUBLE_EQ(0.0, dist); // Point is on the line at (0,0)
+    }
+    {
+        const char* wkt = "LINESTRING (5 5, 6 6)";
+        auto line = GeoShape::from_wkt(wkt, strlen(wkt), status);
+        EXPECT_NE(nullptr, line.get());
+        double dist = point1.Distance(line.get());
+        EXPECT_GT(dist, 0.0);
+        // Distance from (0,0) to a line at (5,5)-(6,6)
+        EXPECT_GT(dist, 700000.0); // More than 700km
+    }
+
+    // ==========================
+    // GeoPoint vs GeoPolygon
+    // ==========================
+    {
+        const char* wkt = "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))";
+        auto polygon = GeoShape::from_wkt(wkt, strlen(wkt), status);
+        EXPECT_NE(nullptr, polygon.get());
+        double dist = point1.Distance(polygon.get());
+        EXPECT_DOUBLE_EQ(0.0, dist); // Point is on the polygon boundary at 
(0,0)
+    }
+    {
+        const char* wkt = "POLYGON ((5 5, 6 5, 6 6, 5 6, 5 5))";
+        auto polygon = GeoShape::from_wkt(wkt, strlen(wkt), status);
+        EXPECT_NE(nullptr, polygon.get());
+        double dist = point1.Distance(polygon.get());
+        EXPECT_GT(dist, 0.0);
+        // Distance from (0,0) to polygon at (5,5)
+        EXPECT_GT(dist, 700000.0); // More than 700km
+    }
+
+    // ==========================
+    // GeoPoint vs GeoMultiPolygon
+    // ==========================
+    {
+        const char* wkt = "MULTIPOLYGON (((0 0, 1 0, 1 1, 0 1, 0 0)))";
+        auto multi_polygon = GeoShape::from_wkt(wkt, strlen(wkt), status);
+        EXPECT_NE(nullptr, multi_polygon.get());
+        double dist = point1.Distance(multi_polygon.get());
+        EXPECT_DOUBLE_EQ(0.0, dist); // Point is on the multipolygon boundary
+    }
+
+    // ==========================
+    // GeoPoint vs GeoCircle
+    // ==========================
+    {
+        GeoCircle circle;
+        circle.init(0, 0, 10000); // 10km radius
+        double dist = point1.Distance(&circle);
+        EXPECT_DOUBLE_EQ(0.0, dist); // Point is at the center of circle
+    }
+    {
+        GeoCircle circle;
+        circle.init(5, 0, 10000); // 10km radius at (5,0)
+        double dist = point1.Distance(&circle);
+        EXPECT_GT(dist, 0.0);
+        // Point (0,0) is outside circle at (5,0)
+        EXPECT_LT(dist, 600000.0); // Should be less than 600km from center 
minus radius
+    }
+}
+
+TEST_F(GeoTypesTest, test_distance_linestring) {
+    GeoParseStatus status;
+
+    const char* base_line = "LINESTRING (0 0, 1 0, 1 1)";
+    auto line1 = GeoShape::from_wkt(base_line, strlen(base_line), status);
+    EXPECT_NE(nullptr, line1.get());
+
+    // ==========================
+    // GeoLineString vs GeoPoint
+    // ==========================
+    {
+        GeoPoint point;
+        point.from_coord(0, 0);
+        double dist = line1->Distance(&point);
+        EXPECT_DOUBLE_EQ(0.0, dist); // Point is on the line
+    }
+
+    // ==========================
+    // GeoLineString vs GeoLineString
+    // ==========================
+    {
+        const char* wkt = "LINESTRING (0 0, 1 0)";
+        auto line2 = GeoShape::from_wkt(wkt, strlen(wkt), status);
+        EXPECT_NE(nullptr, line2.get());
+        double dist = line1->Distance(line2.get());
+        EXPECT_DOUBLE_EQ(0.0, dist); // Lines intersect
+    }
+    {
+        const char* wkt = "LINESTRING (-1 0.5, 2 0.5)";
+        auto line2 = GeoShape::from_wkt(wkt, strlen(wkt), status);
+        EXPECT_NE(nullptr, line2.get());
+        double dist = line1->Distance(line2.get());
+        EXPECT_DOUBLE_EQ(0.0, dist); // Lines cross the vertical segment at x=1
+    }
+    {
+        const char* wkt = "LINESTRING (5 5, 6 5)";
+        auto line2 = GeoShape::from_wkt(wkt, strlen(wkt), status);
+        EXPECT_NE(nullptr, line2.get());
+        double dist = line1->Distance(line2.get());
+        EXPECT_GT(dist, 0.0);
+        // Lines are separate
+        EXPECT_GT(dist, 600000.0);
+    }
+
+    // ==========================
+    // GeoLineString vs GeoPolygon
+    // ==========================
+    {
+        const char* wkt = "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))";
+        auto polygon = GeoShape::from_wkt(wkt, strlen(wkt), status);
+        EXPECT_NE(nullptr, polygon.get());
+        double dist = line1->Distance(polygon.get());
+        EXPECT_DOUBLE_EQ(0.0, dist); // Line is on/inside polygon
+    }
+
+    // ==========================
+    // GeoLineString vs GeoMultiPolygon
+    // ==========================
+    {
+        const char* wkt = "MULTIPOLYGON (((0 0, 1 0, 1 1, 0 1, 0 0)))";
+        auto multi_polygon = GeoShape::from_wkt(wkt, strlen(wkt), status);
+        EXPECT_NE(nullptr, multi_polygon.get());
+        double dist = line1->Distance(multi_polygon.get());
+        EXPECT_DOUBLE_EQ(0.0, dist); // Line is on/inside multipolygon
+    }
+
+    // ==========================
+    // GeoLineString vs GeoCircle
+    // ==========================
+    {
+        GeoCircle circle;
+        circle.init(0.5, 0.5, 100000); // 100km radius
+        double dist = line1->Distance(&circle);
+        EXPECT_DOUBLE_EQ(0.0, dist); // Line is within/touches circle
+    }
+}
+
+TEST_F(GeoTypesTest, test_distance_polygon) {
+    GeoParseStatus status;
+
+    const char* base_polygon = "POLYGON ((0 0, 2 0, 2 2, 0 2, 0 0))";
+    auto polygon1 = GeoShape::from_wkt(base_polygon, strlen(base_polygon), 
status);
+    EXPECT_NE(nullptr, polygon1.get());
+
+    // ==========================
+    // GeoPolygon vs GeoPoint
+    // ==========================
+    {
+        GeoPoint point;
+        point.from_coord(0, 0);
+        double dist = polygon1->Distance(&point);
+        EXPECT_DOUBLE_EQ(0.0, dist); // Point is on polygon boundary
+    }
+    {
+        GeoPoint point;
+        point.from_coord(5, 5);
+        double dist = polygon1->Distance(&point);
+        EXPECT_GT(dist, 0.0);
+        // Point is outside polygon
+        EXPECT_GT(dist, 300000.0);
+    }
+
+    // ==========================
+    // GeoPolygon vs GeoLineString
+    // ==========================
+    {
+        const char* wkt = "LINESTRING (0 0, 1 1)";
+        auto line = GeoShape::from_wkt(wkt, strlen(wkt), status);
+        EXPECT_NE(nullptr, line.get());
+        double dist = polygon1->Distance(line.get());
+        EXPECT_DOUBLE_EQ(0.0, dist); // Line is within polygon
+    }
+
+    // ==========================
+    // GeoPolygon vs GeoPolygon
+    // ==========================
+    {
+        const char* wkt = "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))";
+        auto polygon2 = GeoShape::from_wkt(wkt, strlen(wkt), status);
+        EXPECT_NE(nullptr, polygon2.get());
+        double dist = polygon1->Distance(polygon2.get());
+        EXPECT_DOUBLE_EQ(0.0, dist); // Polygons overlap
+    }
+    {
+        const char* wkt = "POLYGON ((2 0, 4 0, 4 2, 2 2, 2 0))";
+        auto polygon2 = GeoShape::from_wkt(wkt, strlen(wkt), status);
+        EXPECT_NE(nullptr, polygon2.get());
+        double dist = polygon1->Distance(polygon2.get());
+        EXPECT_DOUBLE_EQ(0.0, dist); // Polygons touch at the boundary
+    }
+    {
+        const char* wkt = "POLYGON ((5 5, 6 5, 6 6, 5 6, 5 5))";
+        auto polygon2 = GeoShape::from_wkt(wkt, strlen(wkt), status);
+        EXPECT_NE(nullptr, polygon2.get());
+        double dist = polygon1->Distance(polygon2.get());
+        EXPECT_GT(dist, 0.0);
+        // Polygons are separate
+        EXPECT_GT(dist, 300000.0);
+    }
+
+    // ==========================
+    // GeoPolygon vs GeoMultiPolygon
+    // ==========================
+    {
+        const char* wkt = "MULTIPOLYGON (((0 0, 1 0, 1 1, 0 1, 0 0)))";
+        auto multi_polygon = GeoShape::from_wkt(wkt, strlen(wkt), status);
+        EXPECT_NE(nullptr, multi_polygon.get());
+        double dist = polygon1->Distance(multi_polygon.get());
+        EXPECT_DOUBLE_EQ(0.0, dist); // MultiPolygon overlaps with Polygon
+    }
+
+    // ==========================
+    // GeoPolygon vs GeoCircle
+    // ==========================
+    {
+        GeoCircle circle;
+        circle.init(1, 1, 100000); // 100km radius at (1,1)
+        double dist = polygon1->Distance(&circle);
+        EXPECT_DOUBLE_EQ(0.0, dist); // Circle overlaps with polygon
+    }
+}
+
+TEST_F(GeoTypesTest, test_distance_multipolygon) {
+    GeoParseStatus status;
+
+    const char* base_multipolygon =
+            "MULTIPOLYGON ("
+            "((0 0, 2 0, 2 2, 0 2, 0 0)),"
+            "((5 5, 7 5, 7 7, 5 7, 5 5))"
+            ")";
+    auto multipolygon1 = GeoShape::from_wkt(base_multipolygon, 
strlen(base_multipolygon), status);
+    EXPECT_NE(nullptr, multipolygon1.get());
+
+    // ==========================
+    // GeoMultiPolygon vs GeoPoint
+    // ==========================
+    {
+        GeoPoint point;
+        point.from_coord(0, 0);
+        double dist = multipolygon1->Distance(&point);
+        EXPECT_DOUBLE_EQ(0.0, dist); // Point is on multipolygon boundary
+    }
+    {
+        GeoPoint point;
+        point.from_coord(10, 10);
+        double dist = multipolygon1->Distance(&point);
+        EXPECT_GT(dist, 0.0);
+        // Point is outside all polygons, distance to nearest polygon at 
(5,5)-(7,7) is ~469km
+        EXPECT_GT(dist, 400000.0);
+        EXPECT_LT(dist, 500000.0);
+    }
+
+    // ==========================
+    // GeoMultiPolygon vs GeoLineString
+    // ==========================
+    {
+        const char* wkt = "LINESTRING (0 0, 1 1)";
+        auto line = GeoShape::from_wkt(wkt, strlen(wkt), status);
+        EXPECT_NE(nullptr, line.get());
+        double dist = multipolygon1->Distance(line.get());
+        EXPECT_DOUBLE_EQ(0.0, dist); // Line is within first polygon
+    }
+
+    // ==========================
+    // GeoMultiPolygon vs GeoPolygon
+    // ==========================
+    {
+        const char* wkt = "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))";
+        auto polygon = GeoShape::from_wkt(wkt, strlen(wkt), status);
+        EXPECT_NE(nullptr, polygon.get());
+        double dist = multipolygon1->Distance(polygon.get());
+        EXPECT_DOUBLE_EQ(0.0, dist); // Polygon overlaps with first 
multipolygon
+    }
+    {
+        const char* wkt = "POLYGON ((10 10, 12 10, 12 12, 10 12, 10 10))";
+        auto polygon = GeoShape::from_wkt(wkt, strlen(wkt), status);
+        EXPECT_NE(nullptr, polygon.get());
+        double dist = multipolygon1->Distance(polygon.get());
+        EXPECT_GT(dist, 0.0);
+        // Polygon is separate from multipolygon, distance to nearest polygon 
at (5,5)-(7,7) is ~469km
+        EXPECT_GT(dist, 400000.0);
+        EXPECT_LT(dist, 500000.0);
+    }
+
+    // ==========================
+    // GeoMultiPolygon vs GeoMultiPolygon
+    // ==========================
+    {
+        const char* wkt = "MULTIPOLYGON (((0 0, 1 0, 1 1, 0 1, 0 0)))";
+        auto multipolygon2 = GeoShape::from_wkt(wkt, strlen(wkt), status);
+        EXPECT_NE(nullptr, multipolygon2.get());
+        double dist = multipolygon1->Distance(multipolygon2.get());
+        EXPECT_DOUBLE_EQ(0.0, dist); // Overlapping multipolygons
+    }
+
+    // ==========================
+    // GeoMultiPolygon vs GeoCircle
+    // ==========================
+    {
+        GeoCircle circle;
+        circle.init(1, 1, 100000); // 100km radius
+        double dist = multipolygon1->Distance(&circle);
+        EXPECT_DOUBLE_EQ(0.0, dist); // Circle overlaps with first polygon
+    }
+}
+
+TEST_F(GeoTypesTest, test_distance_circle) {
+    GeoParseStatus status;
+
+    GeoCircle circle1;
+    circle1.init(0, 0, 10000); // 10km radius
+
+    // ==========================
+    // GeoCircle vs GeoPoint
+    // ==========================
+    {
+        GeoPoint point;
+        point.from_coord(0, 0);
+        double dist = circle1.Distance(&point);
+        EXPECT_DOUBLE_EQ(0.0, dist); // Point is at center of circle
+    }
+    {
+        GeoPoint point;
+        point.from_coord(1, 0);
+        double dist = circle1.Distance(&point);
+        EXPECT_GT(dist, 0.0);
+        // Point is outside circle
+        EXPECT_LT(dist, 200000.0); // Less than 200km
+    }
+
+    // ==========================
+    // GeoCircle vs GeoLineString
+    // ==========================
+    {
+        const char* wkt = "LINESTRING (0 0, 0 1)";
+        auto line = GeoShape::from_wkt(wkt, strlen(wkt), status);
+        EXPECT_NE(nullptr, line.get());
+        double dist = circle1.Distance(line.get());
+        EXPECT_DOUBLE_EQ(0.0, dist); // Line is within circle
+    }
+
+    // ==========================
+    // GeoCircle vs GeoPolygon
+    // ==========================
+    {
+        const char* wkt = "POLYGON ((0 0, 0.1 0, 0.1 0.1, 0 0.1, 0 0))";
+        auto polygon = GeoShape::from_wkt(wkt, strlen(wkt), status);
+        EXPECT_NE(nullptr, polygon.get());
+        double dist = circle1.Distance(polygon.get());
+        EXPECT_DOUBLE_EQ(0.0, dist); // Polygon is within circle
+    }
+
+    // ==========================
+    // GeoCircle vs GeoMultiPolygon
+    // ==========================
+    {
+        const char* wkt = "MULTIPOLYGON (((0 0, 0.1 0, 0.1 0.1, 0 0.1, 0 0)))";
+        auto multi_polygon = GeoShape::from_wkt(wkt, strlen(wkt), status);
+        EXPECT_NE(nullptr, multi_polygon.get());
+        double dist = circle1.Distance(multi_polygon.get());
+        EXPECT_DOUBLE_EQ(0.0, dist); // MultiPolygon is within circle
+    }
+
+    // ==========================
+    // GeoCircle vs GeoCircle
+    // ==========================
+    {
+        GeoCircle circle2;
+        circle2.init(0, 0, 5000); // 5km radius at same center
+        double dist = circle1.Distance(&circle2);
+        EXPECT_DOUBLE_EQ(0.0, dist); // Circles overlap
+    }
+    {
+        GeoCircle circle2;
+        circle2.init(10, 0, 5000); // 5km radius at (10,0)
+        double dist = circle1.Distance(&circle2);
+        EXPECT_GT(dist, 0.0);
+        // Circles are separate, distance between centers ~1110km minus radii 
(10+5km) = ~1095km
+        EXPECT_GT(dist, 1000000.0);
+        EXPECT_LT(dist, 1100000.0);
+    }
+}
+
 TEST_F(GeoTypesTest, polygon_contains) {
     GeoParseStatus status;
     const char* wkt = "POLYGON ((10 10, 50 10, 50 10, 50 50, 50 50, 10 50, 10 
10))";
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java
index c852d7c30e8..abe469f61f7 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java
@@ -467,12 +467,15 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StAzimuth;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.StCircle;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.StContains;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.StDisjoint;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.StDistance;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StDistanceSphere;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StGeomFromWKB;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StGeometryFromWKB;
+import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StGeometryType;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StGeometryfromtext;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StGeomfromtext;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StIntersects;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.StLength;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StLinefromtext;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StLinestringfromtext;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.StPoint;
@@ -1033,6 +1036,9 @@ public class BuiltinScalarFunctions implements 
FunctionHelper {
             scalar(StIntersects.class, "st_intersects"),
             scalar(StDisjoint.class, "st_disjoint"),
             scalar(StTouches.class, "st_touches"),
+            scalar(StLength.class, "st_length"),
+            scalar(StGeometryType.class, "st_geometrytype"),
+            scalar(StDistance.class, "st_distance"),
             scalar(StDistanceSphere.class, "st_distance_sphere"),
             scalar(StAngleSphere.class, "st_angle_sphere"),
             scalar(StAngle.class, "st_angle"),
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StDistance.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StDistance.java
new file mode 100644
index 00000000000..dbed7879d6b
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StDistance.java
@@ -0,0 +1,75 @@
+// 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.doris.nereids.trees.expressions.functions.scalar;
+
+import org.apache.doris.catalog.FunctionSignature;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.functions.AlwaysNullable;
+import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
+import 
org.apache.doris.nereids.trees.expressions.functions.PropagateNullLiteral;
+import org.apache.doris.nereids.trees.expressions.shape.BinaryExpression;
+import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
+import org.apache.doris.nereids.types.DoubleType;
+import org.apache.doris.nereids.types.VarcharType;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+/**
+ * ScalarFunction 'st_distance'.
+ */
+public class StDistance extends ScalarFunction
+        implements BinaryExpression, ExplicitlyCastableSignature, 
AlwaysNullable, PropagateNullLiteral {
+
+    public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
+            
FunctionSignature.ret(DoubleType.INSTANCE).args(VarcharType.SYSTEM_DEFAULT, 
VarcharType.SYSTEM_DEFAULT)
+    );
+
+    /**
+     * constructor with 2 arguments.
+     */
+    public StDistance(Expression arg0, Expression arg1) {
+        super("st_distance", arg0, arg1);
+    }
+
+    /** constructor for withChildren and reuse signature */
+    private StDistance(ScalarFunctionParams functionParams) {
+        super(functionParams);
+    }
+
+    /**
+     * withChildren.
+     */
+    @Override
+    public StDistance withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 2);
+        return new StDistance(getFunctionParams(children));
+    }
+
+    @Override
+    public List<FunctionSignature> getSignatures() {
+        return SIGNATURES;
+    }
+
+    @Override
+    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+        return visitor.visitStDistance(this, context);
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StGeometryType.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StGeometryType.java
new file mode 100644
index 00000000000..882c0718232
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StGeometryType.java
@@ -0,0 +1,74 @@
+// 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.doris.nereids.trees.expressions.functions.scalar;
+
+import org.apache.doris.catalog.FunctionSignature;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.functions.AlwaysNullable;
+import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
+import 
org.apache.doris.nereids.trees.expressions.functions.PropagateNullLiteral;
+import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression;
+import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
+import org.apache.doris.nereids.types.VarcharType;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+/**
+ * ScalarFunction 'st_geometrytype'.
+ */
+public class StGeometryType extends ScalarFunction
+        implements UnaryExpression, ExplicitlyCastableSignature, 
AlwaysNullable, PropagateNullLiteral {
+
+    public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
+            
FunctionSignature.ret(VarcharType.SYSTEM_DEFAULT).args(VarcharType.SYSTEM_DEFAULT)
+    );
+
+    /**
+     * constructor with 1 argument.
+     */
+    public StGeometryType(Expression arg0) {
+        super("st_geometrytype", arg0);
+    }
+
+    /** constructor for withChildren and reuse signature */
+    private StGeometryType(ScalarFunctionParams functionParams) {
+        super(functionParams);
+    }
+
+    /**
+     * withChildren.
+     */
+    @Override
+    public StGeometryType withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 1);
+        return new StGeometryType(getFunctionParams(children));
+    }
+
+    @Override
+    public List<FunctionSignature> getSignatures() {
+        return SIGNATURES;
+    }
+
+    @Override
+    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+        return visitor.visitStGeometryType(this, context);
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StLength.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StLength.java
new file mode 100644
index 00000000000..963de26cea7
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StLength.java
@@ -0,0 +1,75 @@
+// 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.doris.nereids.trees.expressions.functions.scalar;
+
+import org.apache.doris.catalog.FunctionSignature;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.functions.AlwaysNullable;
+import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
+import 
org.apache.doris.nereids.trees.expressions.functions.PropagateNullLiteral;
+import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression;
+import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
+import org.apache.doris.nereids.types.DoubleType;
+import org.apache.doris.nereids.types.VarcharType;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+/**
+ * ScalarFunction 'st_length'.
+ */
+public class StLength extends ScalarFunction
+        implements UnaryExpression, ExplicitlyCastableSignature, 
AlwaysNullable, PropagateNullLiteral {
+
+    public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
+            
FunctionSignature.ret(DoubleType.INSTANCE).args(VarcharType.SYSTEM_DEFAULT)
+    );
+
+    /**
+     * constructor with 1 argument.
+     */
+    public StLength(Expression arg0) {
+        super("st_length", arg0);
+    }
+
+    /** constructor for withChildren and reuse signature */
+    private StLength(ScalarFunctionParams functionParams) {
+        super(functionParams);
+    }
+
+    /**
+     * withChildren.
+     */
+    @Override
+    public StLength withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 1);
+        return new StLength(getFunctionParams(children));
+    }
+
+    @Override
+    public List<FunctionSignature> getSignatures() {
+        return SIGNATURES;
+    }
+
+    @Override
+    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+        return visitor.visitStLength(this, context);
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java
index 60e32fbda43..519f53408a9 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java
@@ -488,12 +488,15 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StAzimuth;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.StCircle;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.StContains;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.StDisjoint;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.StDistance;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StDistanceSphere;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StGeomFromWKB;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StGeometryFromWKB;
+import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StGeometryType;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StGeometryfromtext;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StGeomfromtext;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StIntersects;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.StLength;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StLinefromtext;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StLinestringfromtext;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.StPoint;
@@ -2326,6 +2329,18 @@ public interface ScalarFunctionVisitor<R, C> {
         return visitScalarFunction(stTouches, context);
     }
 
+    default R visitStLength(StLength stLength, C context) {
+        return visitScalarFunction(stLength, context);
+    }
+
+    default R visitStGeometryType(StGeometryType stGeometryType, C context) {
+        return visitScalarFunction(stGeometryType, context);
+    }
+
+    default R visitStDistance(StDistance stDistance, C context) {
+        return visitScalarFunction(stDistance, context);
+    }
+
     default R visitStDistanceSphere(StDistanceSphere stDistanceSphere, C 
context) {
         return visitScalarFunction(stDistanceSphere, context);
     }
diff --git 
a/regression-test/data/nereids_p0/sql_functions/spatial_functions/test_gis_function.out
 
b/regression-test/data/nereids_p0/sql_functions/spatial_functions/test_gis_function.out
index f9ca847769b..8dd039d4e82 100644
--- 
a/regression-test/data/nereids_p0/sql_functions/spatial_functions/test_gis_function.out
+++ 
b/regression-test/data/nereids_p0/sql_functions/spatial_functions/test_gis_function.out
@@ -782,6 +782,204 @@ true
 -- !sql --
 45.0
 
+-- ST_Length --
+-- !sql --
+0.0
+
+-- !sql --
+333568.3675501366
+
+-- !sql --
+4430868.978662349
+
+-- !sql --
+6.283185307179586
+
+-- !sql --
+3991709.218883891
+
+-- !sql --
+333585.3035324518
+
+-- !sql --
+0.0
+
+-- !sql --
+555812.8141039772
+
+-- !sql --
+2221785.032905475
+
+-- !sql --
+31.41592653589793
+
+-- !sql --
+4430868.978662349
+
+-- ST_GeometryType --
+-- !sql --
+ST_POINT
+
+-- !sql --
+ST_LINESTRING
+
+-- !sql --
+ST_POLYGON
+
+-- !sql --
+ST_CIRCLE
+
+-- !sql --
+ST_MULTIPOLYGON
+
+-- !sql --
+ST_LINESTRING
+
+-- ST_Distance --
+-- Point-Point
+-- !sql --
+0.0
+
+-- !sql --
+555812.8141039772
+
+-- !sql --
+555812.8141039772
+
+-- Point-LineString
+-- !sql --
+0.0
+
+-- !sql --
+555975.5058874196
+
+-- !sql --
+0.0
+
+-- !sql --
+555975.5058874196
+
+-- Point-Polygon
+-- !sql --
+0.0
+
+-- !sql --
+776861.3769121666
+
+-- !sql --
+0.0
+
+-- !sql --
+776861.3769121666
+
+-- Point-Circle
+-- !sql --
+0.0
+
+-- !sql --
+222389.2023549679
+
+-- !sql --
+0.0
+
+-- !sql --
+222389.2023549679
+
+-- Point-MultiPolygon
+-- !sql --
+0.0
+
+-- !sql --
+311622.5144267479
+
+-- !sql --
+0.0
+
+-- LineString-LineString
+-- !sql --
+0.0
+
+-- !sql --
+555975.5058874197
+
+-- !sql --
+555975.5058874197
+
+-- LineString-Polygon
+-- !sql --
+0.0
+
+-- !sql --
+0.0
+
+-- !sql --
+0.0
+
+-- LineString-Circle
+-- !sql --
+555974.5058874196
+
+-- !sql --
+555974.5058874196
+
+-- !sql --
+0.0
+
+-- LineString-MultiPolygon
+-- !sql --
+220225.0495313765
+
+-- Polygon-Polygon
+-- !sql --
+0.0
+
+-- !sql --
+547523.7460346645
+
+-- Polygon-Circle
+-- !sql --
+0.0
+
+-- !sql --
+0.0
+
+-- !sql --
+1107675.460602543
+
+-- !sql --
+11.15730564761618
+
+-- Polygon-MultiPolygon
+-- !sql --
+0.0
+
+-- !sql --
+776861.3769121666
+
+-- Circle-Circle
+-- !sql --
+0.0
+
+-- !sql --
+555973.5058874197
+
+-- !sql --
+555973.5058874197
+
+-- Circle-MultiPolygon
+-- !sql --
+0.0
+
+-- !sql --
+1544758.985700087
+
+-- MultiPolygon-MultiPolygon
+-- !sql --
+0.0
+
+-- !sql --
+1559539.293243077
+
 -- ST_AsText --
 -- !sql --
 LINESTRING (1 1, 2 2)
@@ -909,6 +1107,33 @@ LINESTRING (1 1, 2 2)
 -- !sql --
 POLYGON ((114.104486 22.547119, 114.093758 22.547753, 114.096504 22.532057, 
114.104229 22.539826, 114.106203 22.54268, 114.104486 22.547119))
 
+-- !sql --
+1      ST_POINT
+2      ST_LINESTRING
+3      ST_LINESTRING
+4      ST_POLYGON
+5      ST_POLYGON
+6      ST_MULTIPOLYGON
+7      \N
+
+-- !sql --
+1      0.0
+2      111195.10117748393
+3      111195.10117748393
+4      444763.46872762055
+5      2667259.843412653
+6      889323.7417861816
+7      \N
+
+-- !sql --
+1      0.0
+2      0.0
+3      0.0
+4      0.0
+5      939785.7843902707
+6      0.0
+7      \N
+
 -- !sql_part_const_dis_sph --
 3335.853035325018
 
diff --git 
a/regression-test/suites/nereids_p0/sql_functions/spatial_functions/test_gis_function.groovy
 
b/regression-test/suites/nereids_p0/sql_functions/spatial_functions/test_gis_function.groovy
index b8dc671f283..1c8b7134136 100644
--- 
a/regression-test/suites/nereids_p0/sql_functions/spatial_functions/test_gis_function.groovy
+++ 
b/regression-test/suites/nereids_p0/sql_functions/spatial_functions/test_gis_function.groovy
@@ -323,6 +323,88 @@ suite("test_gis_function") {
     qt_sql "SELECT ST_ANGLE_SPHERE(116.35620117, 39.939093, 116.4274406433, 
39.9020987219);"
     qt_sql "SELECT ST_ANGLE_SPHERE(0, 0, 45, 0);"
 
+    // ST_Length tests for all geometry types
+    qt_sql "SELECT ST_Length(ST_Point(0, 0));"
+    qt_sql "SELECT ST_Length(ST_LineFromText(\"LINESTRING (0 0, 1 0, 1 1, 0 
1)\"));"
+    qt_sql "SELECT ST_Length(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 
0))\"));"
+    qt_sql "SELECT ST_Length(ST_Circle(0, 0, 1));"
+    qt_sql "SELECT ST_Length(ST_GeometryFromText(\"MULTIPOLYGON(((0 0, 0 5, 5 
5, 5 0, 0 0)), ((6 6, 6 10, 10 10, 10 6, 6 6)))\"));"
+    qt_sql "SELECT ST_Length(ST_GeometryFromText(\"LINESTRING (0 0, 1 0, 2 0, 
3 0)\"));"
+    qt_sql "SELECT ST_Length(ST_Point(24.7, 56.7));"
+    qt_sql "SELECT ST_Length(ST_LineFromText(\"LINESTRING (0 0, 3 4)\"));"
+    qt_sql "SELECT ST_Length(ST_Polygon(\"POLYGON ((0 0, 5 0, 5 5, 0 5, 0 
0))\"));"
+    qt_sql "SELECT ST_Length(ST_Circle(10, 20, 5));"
+    qt_sql "SELECT ST_Length(ST_GeometryFromText(\"MULTIPOLYGON(((0 0, 10 0, 
10 10, 0 10, 0 0)))\"));"
+
+    // ST_GeometryType tests for all geometry types
+    qt_sql "SELECT ST_GeometryType(ST_Point(0, 0));"
+    qt_sql "SELECT ST_GeometryType(ST_LineFromText(\"LINESTRING (0 0, 1 0, 1 
1, 0 1)\"));"
+    qt_sql "SELECT ST_GeometryType(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 
10, 0 0))\"));"
+    qt_sql "SELECT ST_GeometryType(ST_Circle(0, 0, 1));"
+    qt_sql "SELECT ST_GeometryType(ST_GeometryFromText(\"MULTIPOLYGON(((0 0, 0 
5, 5 5, 5 0, 0 0)), ((6 6, 6 10, 10 10, 10 6, 6 6)))\"));"
+    qt_sql "SELECT ST_GeometryType(ST_GeometryFromText(\"LINESTRING (0 0, 1 0, 
2 0, 3 0)\"));"
+
+    // ST_Distance tests for all geometry type combinations
+    qt_sql "SELECT ST_Distance(ST_Point(0, 0), ST_Point(0, 0));"
+    qt_sql "SELECT ST_Distance(ST_Point(0, 0), ST_Point(3, 4));"
+    qt_sql "SELECT ST_Distance(ST_Point(3, 4), ST_Point(0, 0));"
+    
+    qt_sql "SELECT ST_Distance(ST_Point(0, 0), ST_LineFromText(\"LINESTRING (0 
0, 10 0)\"));"
+    qt_sql "SELECT ST_Distance(ST_Point(5, 5), ST_LineFromText(\"LINESTRING (0 
0, 10 0)\"));"
+    qt_sql "SELECT ST_Distance(ST_LineFromText(\"LINESTRING (0 0, 10 0)\"), 
ST_Point(0, 0));"
+    qt_sql "SELECT ST_Distance(ST_LineFromText(\"LINESTRING (0 0, 10 0)\"), 
ST_Point(5, 5));"
+    
+    qt_sql "SELECT ST_Distance(ST_Point(5, 5), ST_Polygon(\"POLYGON ((0 0, 10 
0, 10 10, 0 10, 0 0))\"));"
+    qt_sql "SELECT ST_Distance(ST_Point(15, 15), ST_Polygon(\"POLYGON ((0 0, 
10 0, 10 10, 0 10, 0 0))\"));"
+    qt_sql "SELECT ST_Distance(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 
0 0))\"), ST_Point(5, 5));"
+    qt_sql "SELECT ST_Distance(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 
0 0))\"), ST_Point(15, 15));"
+    
+    qt_sql "SELECT ST_Distance(ST_Point(0, 0), ST_Circle(0, 0, 1));"
+    qt_sql "SELECT ST_Distance(ST_Point(2, 0), ST_Circle(0, 0, 1));"
+    qt_sql "SELECT ST_Distance(ST_Circle(0, 0, 1), ST_Point(0, 0));"
+    qt_sql "SELECT ST_Distance(ST_Circle(0, 0, 1), ST_Point(2, 0));"
+    
+    qt_sql "SELECT ST_Distance(ST_Point(2, 2), 
ST_GeometryFromText(\"MULTIPOLYGON(((0 0, 0 5, 5 5, 5 0, 0 0)), ((6 6, 6 10, 10 
10, 10 6, 6 6)))\"));"
+    qt_sql "SELECT ST_Distance(ST_Point(12, 12), 
ST_GeometryFromText(\"MULTIPOLYGON(((0 0, 0 5, 5 5, 5 0, 0 0)), ((6 6, 6 10, 10 
10, 10 6, 6 6)))\"));"
+    qt_sql "SELECT ST_Distance(ST_GeometryFromText(\"MULTIPOLYGON(((0 0, 0 5, 
5 5, 5 0, 0 0)), ((6 6, 6 10, 10 10, 10 6, 6 6)))\"), ST_Point(2, 2));"
+    
+    qt_sql "SELECT ST_Distance(ST_LineFromText(\"LINESTRING (0 0, 10 0)\"), 
ST_LineFromText(\"LINESTRING (0 0, 10 0)\"));"
+    qt_sql "SELECT ST_Distance(ST_LineFromText(\"LINESTRING (0 0, 5 0)\"), 
ST_LineFromText(\"LINESTRING (10 0, 15 0)\"));"
+    qt_sql "SELECT ST_Distance(ST_LineFromText(\"LINESTRING (10 0, 15 0)\"), 
ST_LineFromText(\"LINESTRING (0 0, 5 0)\"));"
+    
+    qt_sql "SELECT ST_Distance(ST_LineFromText(\"LINESTRING (5 5, 5 15)\"), 
ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"));"
+    qt_sql "SELECT ST_Distance(ST_LineFromText(\"LINESTRING (0 5, 10 5)\"), 
ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"));"
+    qt_sql "SELECT ST_Distance(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 
0 0))\"), ST_LineFromText(\"LINESTRING (5 5, 5 15)\"));"
+    
+    qt_sql "SELECT ST_Distance(ST_LineFromText(\"LINESTRING (0 0, 10 0)\"), 
ST_Circle(5, 5, 1));"
+    qt_sql "SELECT ST_Distance(ST_Circle(5, 5, 1), 
ST_LineFromText(\"LINESTRING (0 0, 10 0)\"));"
+
+    // circle-line tangent cases (distance should be 0)
+    qt_sql "SELECT ST_Distance(ST_Circle(0, 0, 1), 
ST_LineFromText(\"LINESTRING (-1 0.00000899320363724538, 1 
0.00000899320363724538)\"));"
+    
+    qt_sql "SELECT ST_Distance(ST_LineFromText(\"LINESTRING (12 2, 12 8)\"), 
ST_GeometryFromText(\"MULTIPOLYGON(((0 0, 0 5, 5 5, 5 0, 0 0)), ((6 6, 6 10, 10 
10, 10 6, 6 6)))\"));"
+    
+    qt_sql "SELECT ST_Distance(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 
0 0))\"), ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"));"
+    qt_sql "SELECT ST_Distance(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 
0 0))\"), ST_Polygon(\"POLYGON ((15 0, 25 0, 25 10, 15 10, 15 0))\"));"
+    
+    qt_sql "SELECT ST_Distance(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 
0 0))\"), ST_Circle(5, 5, 1));"
+    qt_sql "SELECT ST_Distance(ST_Circle(5, 5, 1), ST_Polygon(\"POLYGON ((0 0, 
10 0, 10 10, 0 10, 0 0))\"));"
+    qt_sql "SELECT ST_Distance(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 
0 0))\"), ST_Circle(20, 5, 1));"
+    qt_sql "SELECT ST_Distance(ST_Circle(0.001, 0, 50), 
ST_GeometryFromText(\"POLYGON ((-0.00045 -0.00045, 0.00045 -0.00045, 0.00045 
0.00045, -0.00045 0.00045, -0.00045 -0.00045))\"));"
+    
+    qt_sql "SELECT ST_Distance(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 
0 0))\"), ST_GeometryFromText(\"MULTIPOLYGON(((0 0, 0 5, 5 5, 5 0, 0 0)), ((6 
6, 6 10, 10 10, 10 6, 6 6)))\"));"
+    qt_sql "SELECT ST_Distance(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 
0 0))\"), ST_GeometryFromText(\"MULTIPOLYGON(((15 15, 15 20, 20 20, 20 15, 15 
15)))\"));"
+    
+    qt_sql "SELECT ST_Distance(ST_Circle(0, 0, 1), ST_Circle(0, 0, 1));"
+    qt_sql "SELECT ST_Distance(ST_Circle(0, 0, 1), ST_Circle(5, 0, 1));"
+    qt_sql "SELECT ST_Distance(ST_Circle(5, 0, 1), ST_Circle(0, 0, 1));"
+    
+    qt_sql "SELECT ST_Distance(ST_Circle(2, 2, 1), 
ST_GeometryFromText(\"MULTIPOLYGON(((0 0, 0 5, 5 5, 5 0, 0 0)), ((6 6, 6 10, 10 
10, 10 6, 6 6)))\"));"
+    qt_sql "SELECT ST_Distance(ST_Circle(20, 20, 1), 
ST_GeometryFromText(\"MULTIPOLYGON(((0 0, 0 5, 5 5, 5 0, 0 0)), ((6 6, 6 10, 10 
10, 10 6, 6 6)))\"));"
+    
+    qt_sql "SELECT ST_Distance(ST_GeometryFromText(\"MULTIPOLYGON(((0 0, 0 5, 
5 5, 5 0, 0 0)), ((6 6, 6 10, 10 10, 10 6, 6 6)))\"), 
ST_GeometryFromText(\"MULTIPOLYGON(((0 0, 0 5, 5 5, 5 0, 0 0)), ((6 6, 6 10, 10 
10, 10 6, 6 6)))\"));"
+    qt_sql "SELECT ST_Distance(ST_GeometryFromText(\"MULTIPOLYGON(((0 0, 0 5, 
5 5, 5 0, 0 0)))\"), ST_GeometryFromText(\"MULTIPOLYGON(((15 15, 15 20, 20 20, 
20 15, 15 15)))\"));"
+
     qt_sql "SELECT ST_AsText(ST_GeometryFromText(\"LINESTRING (1 1, 2 2)\"));"
     qt_sql "SELECT ST_AsText(ST_GeomFromText(\"LINESTRING (1 1, 2 2)\"));"
 
@@ -376,6 +458,35 @@ suite("test_gis_function") {
     qt_sql "SELECT 
ST_AsText(ST_GeomFromWKB(ST_AsBinary(ST_GeometryFromText(\"LINESTRING (1 1, 2 
2)\"))));"
     qt_sql "SELECT ST_AsText(ST_GeomFromWKB(ST_AsBinary(ST_Polygon(\"POLYGON 
((114.104486 22.547119,114.093758 22.547753,114.096504 22.532057,114.104229 
22.539826,114.106203 22.542680,114.104486 22.547119))\"))));"
 
+    // table-driven tests for ST_Length/ST_GeometryType/ST_Distance
+    sql "drop table if exists test_gis_table_cases"
+    sql """
+    CREATE TABLE test_gis_table_cases (
+        `id` int NOT NULL,
+        `wkt_a` varchar(512) NULL,
+        `wkt_b` varchar(512) NULL
+    ) ENGINE=OLAP
+    UNIQUE KEY(`id`)
+    COMMENT 'OLAP'
+    DISTRIBUTED BY HASH(`id`) BUCKETS 4
+    PROPERTIES (
+        "replication_allocation" = "tag.location.default: 1"
+    );
+    """
+    sql """
+    insert into test_gis_table_cases values
+        (1, 'POINT(0 0)', 'POINT(0 0)'),
+        (2, 'LINESTRING(0 0, 0 1)', 'POINT(0 0)'),
+        (3, 'LINESTRING(0 0, 1 0)', 'LINESTRING(0 0, 1 0)'),
+        (4, 'POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))', 'LINESTRING(0.2 0.2, 0.8 
0.8)'),
+        (5, 'POLYGON((0 0, 4 0, 4 4, 0 4, 0 0), (1 1, 1 3, 3 3, 3 1, 1 1))', 
'POINT(10 10)'),
+        (6, 'MULTIPOLYGON(((0 0, 0 1, 1 1, 1 0, 0 0)), ((2 2, 2 3, 3 3, 3 2, 2 
2)))', 'POINT(2.5 2.5)'),
+        (7, null, null);
+    """
+    qt_sql "select id, ST_GeometryType(ST_GeomFromText(wkt_a)) from 
test_gis_table_cases order by id;"
+    qt_sql "select id, ST_Length(ST_GeomFromText(wkt_a)) from 
test_gis_table_cases order by id;"
+    qt_sql "select id, ST_Distance(ST_GeomFromText(wkt_a), 
ST_GeomFromText(wkt_b)) from test_gis_table_cases order by id;"
+
     // test const
     sql "drop table if exists test_gis_const"
     sql """


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to