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]