From 90464b675b53910ec5b565f3bf9e72f73a59a811 Mon Sep 17 00:00:00 2001 From: Mryange <59914473+Mryange@users.noreply.github.com> Date: Mon, 1 Jul 2024 12:53:32 +0800 Subject: [PATCH 1/3] [opt](function)avoid virtual function calls in geo functions (#37003) --- be/src/vec/functions/functions_geo.cpp | 295 ++++++++++++++++--------- be/src/vec/functions/functions_geo.h | 5 +- 2 files changed, 196 insertions(+), 104 deletions(-) diff --git a/be/src/vec/functions/functions_geo.cpp b/be/src/vec/functions/functions_geo.cpp index 172f000928f3dd..b389bc1636e45f 100644 --- a/be/src/vec/functions/functions_geo.cpp +++ b/be/src/vec/functions/functions_geo.cpp @@ -26,12 +26,15 @@ #include "geo/geo_common.h" #include "geo/geo_types.h" #include "vec/columns/column.h" +#include "vec/columns/column_nullable.h" +#include "vec/columns/columns_number.h" #include "vec/common/string_ref.h" #include "vec/core/block.h" #include "vec/core/column_with_type_and_name.h" #include "vec/core/field.h" #include "vec/data_types/data_type_nullable.h" #include "vec/data_types/data_type_number.h" +#include "vec/data_types/data_type_string.h" #include "vec/functions/simple_function_factory.h" namespace doris::vectorized { @@ -40,6 +43,7 @@ struct StPoint { static constexpr auto NEED_CONTEXT = false; static constexpr auto NAME = "st_point"; static const size_t NUM_ARGS = 2; + using Type = DataTypeString; static Status execute(Block& block, const ColumnNumbers& arguments, size_t result) { DCHECK_EQ(arguments.size(), 2); auto return_type = block.get_data_type(result); @@ -51,26 +55,29 @@ struct StPoint { const auto size = std::max(left_column->size(), right_column->size()); - MutableColumnPtr res = return_type->create_column(); - + auto res = ColumnString::create(); + auto null_map = ColumnUInt8::create(size, 0); + auto& null_map_data = null_map->get_data(); GeoPoint point; std::string buf; if (left_const) { - const_vector(left_column, right_column, res, size, point, buf); + const_vector(left_column, right_column, res, null_map_data, size, point, buf); } else if (right_const) { - vector_const(left_column, right_column, res, size, point, buf); + vector_const(left_column, right_column, res, null_map_data, size, point, buf); } else { - vector_vector(left_column, right_column, res, size, point, buf); + vector_vector(left_column, right_column, res, null_map_data, size, point, buf); } - block.replace_by_position(result, std::move(res)); + block.replace_by_position(result, + ColumnNullable::create(std::move(res), std::move(null_map))); return Status::OK(); } - static void loop_do(GeoParseStatus& cur_res, MutableColumnPtr& res, GeoPoint& point, - std::string& buf) { + static void loop_do(GeoParseStatus& cur_res, ColumnString::MutablePtr& res, NullMap& null_map, + int row, GeoPoint& point, std::string& buf) { if (cur_res != GEO_PARSE_OK) { - res->insert_data(nullptr, 0); + null_map[row] = 1; + res->insert_default(); return; } @@ -80,32 +87,32 @@ struct StPoint { } static void const_vector(const ColumnPtr& left_column, const ColumnPtr& right_column, - MutableColumnPtr& res, const size_t size, GeoPoint& point, - std::string& buf) { + ColumnString::MutablePtr& res, NullMap& null_map, const size_t size, + GeoPoint& point, std::string& buf) { double x = left_column->operator[](0).get(); for (int row = 0; row < size; ++row) { auto cur_res = point.from_coord(x, right_column->operator[](row).get()); - loop_do(cur_res, res, point, buf); + loop_do(cur_res, res, null_map, row, point, buf); } } static void vector_const(const ColumnPtr& left_column, const ColumnPtr& right_column, - MutableColumnPtr& res, const size_t size, GeoPoint& point, - std::string& buf) { + ColumnString::MutablePtr& res, NullMap& null_map, const size_t size, + GeoPoint& point, std::string& buf) { double y = right_column->operator[](0).get(); for (int row = 0; row < size; ++row) { auto cur_res = point.from_coord(right_column->operator[](row).get(), y); - loop_do(cur_res, res, point, buf); + loop_do(cur_res, res, null_map, row, point, buf); } } static void vector_vector(const ColumnPtr& left_column, const ColumnPtr& right_column, - MutableColumnPtr& res, const size_t size, GeoPoint& point, - std::string& buf) { + ColumnString::MutablePtr& res, NullMap& null_map, const size_t size, + GeoPoint& point, std::string& buf) { for (int row = 0; row < size; ++row) { auto cur_res = point.from_coord(left_column->operator[](row).get(), right_column->operator[](row).get()); - loop_do(cur_res, res, point, buf); + loop_do(cur_res, res, null_map, row, point, buf); } } }; @@ -122,6 +129,7 @@ struct StAsText { static constexpr auto NEED_CONTEXT = false; static constexpr auto NAME = FunctionName::NAME; 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); @@ -130,20 +138,24 @@ struct StAsText { auto size = input->size(); - MutableColumnPtr res = return_type->create_column(); + auto res = ColumnString::create(); + auto null_map = ColumnUInt8::create(size, 0); + auto& null_map_data = null_map->get_data(); std::unique_ptr shape; for (int row = 0; row < size; ++row) { auto shape_value = input->get_data_at(row); shape = GeoShape::from_encoded(shape_value.data, shape_value.size); if (shape == nullptr) { - res->insert_data(nullptr, 0); + null_map_data[row] = 1; + res->insert_default(); continue; } auto wkt = shape->as_wkt(); res->insert_data(wkt.data(), wkt.size()); } - block.replace_by_position(result, std::move(res)); + block.replace_by_position(result, + ColumnNullable::create(std::move(res), std::move(null_map))); return Status::OK(); } @@ -153,6 +165,7 @@ struct StX { static constexpr auto NEED_CONTEXT = false; static constexpr auto NAME = "st_x"; 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); @@ -161,7 +174,10 @@ struct StX { auto size = input->size(); - MutableColumnPtr res = return_type->create_column(); + auto res = ColumnFloat64::create(); + auto null_map = ColumnUInt8::create(size, 0); + auto& null_map_data = null_map->get_data(); + res->reserve(size); GeoPoint point; for (int row = 0; row < size; ++row) { @@ -169,13 +185,15 @@ struct StX { auto pt = point.decode_from(point_value.data, point_value.size); if (!pt) { + null_map_data[row] = 1; res->insert_default(); continue; } auto x_value = point.x(); - res->insert_data(const_cast((char*)(&x_value)), 0); + res->insert_value(x_value); } - block.replace_by_position(result, std::move(res)); + block.replace_by_position(result, + ColumnNullable::create(std::move(res), std::move(null_map))); return Status::OK(); } @@ -185,6 +203,7 @@ struct StY { static constexpr auto NEED_CONTEXT = false; static constexpr auto NAME = "st_y"; 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); @@ -193,7 +212,10 @@ struct StY { auto size = input->size(); - MutableColumnPtr res = return_type->create_column(); + auto res = ColumnFloat64::create(); + res->reserve(size); + auto null_map = ColumnUInt8::create(size, 0); + auto& null_map_data = null_map->get_data(); GeoPoint point; for (int row = 0; row < size; ++row) { @@ -201,13 +223,15 @@ struct StY { auto pt = point.decode_from(point_value.data, point_value.size); if (!pt) { + null_map_data[row] = 1; res->insert_default(); continue; } auto y_value = point.y(); - res->insert_data(const_cast((char*)(&y_value)), 0); + res->insert_value(y_value); } - block.replace_by_position(result, std::move(res)); + block.replace_by_position(result, + ColumnNullable::create(std::move(res), std::move(null_map))); return Status::OK(); } @@ -217,6 +241,7 @@ struct StDistanceSphere { static constexpr auto NEED_CONTEXT = false; static constexpr auto NAME = "st_distance_sphere"; static const size_t NUM_ARGS = 4; + using Type = DataTypeFloat64; static Status execute(Block& block, const ColumnNumbers& arguments, size_t result) { DCHECK_EQ(arguments.size(), 4); auto return_type = block.get_data_type(result); @@ -227,22 +252,25 @@ struct StDistanceSphere { auto y_lat = block.get_by_position(arguments[3]).column->convert_to_full_column_if_const(); const auto size = x_lng->size(); - - MutableColumnPtr res = return_type->create_column(); - + auto res = ColumnFloat64::create(); + res->reserve(size); + auto null_map = ColumnUInt8::create(size, 0); + auto& null_map_data = null_map->get_data(); for (int row = 0; row < size; ++row) { double distance = 0; if (!GeoPoint::ComputeDistance(x_lng->operator[](row).get(), x_lat->operator[](row).get(), y_lng->operator[](row).get(), y_lat->operator[](row).get(), &distance)) { + null_map_data[row] = 1; res->insert_default(); continue; } - res->insert_data(const_cast((char*)&distance), 0); + res->insert_value(distance); } - block.replace_by_position(result, std::move(res)); + block.replace_by_position(result, + ColumnNullable::create(std::move(res), std::move(null_map))); return Status::OK(); } }; @@ -251,6 +279,7 @@ struct StAngleSphere { static constexpr auto NEED_CONTEXT = false; static constexpr auto NAME = "st_angle_sphere"; static const size_t NUM_ARGS = 4; + using Type = DataTypeFloat64; static Status execute(Block& block, const ColumnNumbers& arguments, size_t result) { DCHECK_EQ(arguments.size(), 4); auto return_type = block.get_data_type(result); @@ -262,7 +291,10 @@ struct StAngleSphere { const auto size = x_lng->size(); - MutableColumnPtr res = return_type->create_column(); + auto res = ColumnFloat64::create(); + res->reserve(size); + auto null_map = ColumnUInt8::create(size, 0); + auto& null_map_data = null_map->get_data(); for (int row = 0; row < size; ++row) { double angle = 0; @@ -270,13 +302,15 @@ struct StAngleSphere { x_lat->operator[](row).get(), y_lng->operator[](row).get(), y_lat->operator[](row).get(), &angle)) { + null_map_data[row] = 1; res->insert_default(); continue; } - res->insert_data(const_cast((char*)&angle), 0); + res->insert_value(angle); } - block.replace_by_position(result, std::move(res)); + block.replace_by_position(result, + ColumnNullable::create(std::move(res), std::move(null_map))); return Status::OK(); } }; @@ -285,15 +319,19 @@ struct StAngle { static constexpr auto NEED_CONTEXT = false; static constexpr auto NAME = "st_angle"; static const size_t NUM_ARGS = 3; + using Type = DataTypeFloat64; static Status execute(Block& block, const ColumnNumbers& arguments, size_t result) { DCHECK_EQ(arguments.size(), 3); auto return_type = block.get_data_type(result); - MutableColumnPtr res = return_type->create_column(); auto p1 = block.get_by_position(arguments[0]).column->convert_to_full_column_if_const(); auto p2 = block.get_by_position(arguments[1]).column->convert_to_full_column_if_const(); auto p3 = block.get_by_position(arguments[2]).column->convert_to_full_column_if_const(); const auto size = p1->size(); + auto res = ColumnFloat64::create(); + res->reserve(size); + auto null_map = ColumnUInt8::create(size, 0); + auto& null_map_data = null_map->get_data(); GeoPoint point1; GeoPoint point2; @@ -303,6 +341,7 @@ struct StAngle { auto shape_value1 = p1->get_data_at(row); auto pt1 = point1.decode_from(shape_value1.data, shape_value1.size); if (!pt1) { + null_map_data[row] = 1; res->insert_default(); continue; } @@ -310,24 +349,28 @@ struct StAngle { auto shape_value2 = p2->get_data_at(row); auto pt2 = point2.decode_from(shape_value2.data, shape_value2.size); if (!pt2) { + null_map_data[row] = 1; res->insert_default(); continue; } auto shape_value3 = p3->get_data_at(row); auto pt3 = point3.decode_from(shape_value3.data, shape_value3.size); if (!pt3) { + null_map_data[row] = 1; res->insert_default(); continue; } double angle = 0; if (!GeoPoint::ComputeAngle(&point1, &point2, &point3, &angle)) { + null_map_data[row] = 1; res->insert_default(); continue; } - res->insert_data(const_cast((char*)&angle), 0); + res->insert_value(angle); } - block.replace_by_position(result, std::move(res)); + block.replace_by_position(result, + ColumnNullable::create(std::move(res), std::move(null_map))); return Status::OK(); } }; @@ -336,10 +379,10 @@ struct StAzimuth { static constexpr auto NEED_CONTEXT = false; static constexpr auto NAME = "st_azimuth"; 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); - MutableColumnPtr res = return_type->create_column(); const auto& [left_column, left_const] = unpack_if_const(block.get_by_position(arguments[0]).column); @@ -347,71 +390,77 @@ struct StAzimuth { 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(); GeoPoint point1; GeoPoint point2; if (left_const) { - const_vector(left_column, right_column, res, size, point1, point2); + const_vector(left_column, right_column, res, null_map_data, size, point1, point2); } else if (right_const) { - vector_const(left_column, right_column, res, size, point1, point2); + vector_const(left_column, right_column, res, null_map_data, size, point1, point2); } else { - vector_vector(left_column, right_column, res, size, point1, point2); + vector_vector(left_column, right_column, res, null_map_data, size, point1, point2); } - block.replace_by_position(result, std::move(res)); + block.replace_by_position(result, + ColumnNullable::create(std::move(res), std::move(null_map))); return Status::OK(); } static void loop_do(bool& pt1, bool& pt2, GeoPoint& point1, GeoPoint& point2, - MutableColumnPtr& res) { + ColumnFloat64::MutablePtr& res, NullMap& null_map, int row) { if (!(pt1 && pt2)) { + null_map[row] = 1; res->insert_default(); return; } double angle = 0; if (!GeoPoint::ComputeAzimuth(&point1, &point2, &angle)) { + null_map[row] = 1; res->insert_default(); return; } - res->insert_data(const_cast((char*)&angle), 0); + res->insert_value(angle); } static void const_vector(const ColumnPtr& left_column, const ColumnPtr& right_column, - MutableColumnPtr& res, size_t size, GeoPoint& point1, - GeoPoint& point2) { + ColumnFloat64::MutablePtr& res, NullMap& null_map, size_t size, + GeoPoint& point1, GeoPoint& point2) { auto shape_value1 = left_column->get_data_at(0); auto pt1 = point1.decode_from(shape_value1.data, shape_value1.size); for (int row = 0; row < size; ++row) { auto shape_value2 = right_column->get_data_at(row); auto pt2 = point2.decode_from(shape_value2.data, shape_value2.size); - loop_do(pt1, pt2, point1, point2, res); + loop_do(pt1, pt2, point1, point2, res, null_map, row); } } static void vector_const(const ColumnPtr& left_column, const ColumnPtr& right_column, - MutableColumnPtr& res, size_t size, GeoPoint& point1, - GeoPoint& point2) { + ColumnFloat64::MutablePtr& res, NullMap& null_map, size_t size, + GeoPoint& point1, GeoPoint& point2) { auto shape_value2 = right_column->get_data_at(0); auto pt2 = point2.decode_from(shape_value2.data, shape_value2.size); for (int row = 0; row < size; ++row) { auto shape_value1 = left_column->get_data_at(row); auto pt1 = point1.decode_from(shape_value1.data, shape_value1.size); - loop_do(pt1, pt2, point1, point2, res); + loop_do(pt1, pt2, point1, point2, res, null_map, row); } } static void vector_vector(const ColumnPtr& left_column, const ColumnPtr& right_column, - MutableColumnPtr& res, size_t size, GeoPoint& point1, - GeoPoint& point2) { + ColumnFloat64::MutablePtr& res, NullMap& null_map, size_t size, + GeoPoint& point1, GeoPoint& point2) { for (int row = 0; row < size; ++row) { auto shape_value1 = left_column->get_data_at(row); auto pt1 = point1.decode_from(shape_value1.data, shape_value1.size); auto shape_value2 = right_column->get_data_at(row); auto pt2 = point2.decode_from(shape_value2.data, shape_value2.size); - loop_do(pt1, pt2, point1, point2, res); + loop_do(pt1, pt2, point1, point2, res, null_map, row); } } }; @@ -420,33 +469,39 @@ struct StAreaSquareMeters { static constexpr auto NEED_CONTEXT = false; static constexpr auto NAME = "st_area_square_meters"; 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); - MutableColumnPtr res = return_type->create_column(); 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 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 area = 0; if (!GeoShape::ComputeArea(shape.get(), &area, "square_meters")) { + null_map_data[row] = 1; res->insert_default(); continue; } - res->insert_data(const_cast((char*)&area), 0); + res->insert_value(area); } - block.replace_by_position(result, std::move(res)); + block.replace_by_position(result, + ColumnNullable::create(std::move(res), std::move(null_map))); return Status::OK(); } }; @@ -455,13 +510,17 @@ struct StAreaSquareKm { static constexpr auto NEED_CONTEXT = false; static constexpr auto NAME = "st_area_square_km"; 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); - MutableColumnPtr res = return_type->create_column(); 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 shape; @@ -469,19 +528,23 @@ struct StAreaSquareKm { 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 area = 0; if (!GeoShape::ComputeArea(shape.get(), &area, "square_km")) { + null_map_data[row] = 1; res->insert_default(); + ; continue; } - res->insert_data(const_cast((char*)&area), 0); + res->insert_value(area); } - block.replace_by_position(result, std::move(res)); + block.replace_by_position(result, + ColumnNullable::create(std::move(res), std::move(null_map))); return Status::OK(); } }; @@ -490,37 +553,46 @@ struct StCircle { static constexpr auto NEED_CONTEXT = true; static constexpr auto NAME = "st_circle"; static const size_t NUM_ARGS = 3; + using Type = DataTypeString; static Status execute(FunctionContext* context, Block& block, const ColumnNumbers& arguments, size_t result) { DCHECK_EQ(arguments.size(), 3); auto return_type = block.get_data_type(result); auto center_lng = block.get_by_position(arguments[0]).column->convert_to_full_column_if_const(); + const auto* center_lng_ptr = assert_cast(center_lng.get()); auto center_lat = block.get_by_position(arguments[1]).column->convert_to_full_column_if_const(); + const auto* center_lat_ptr = assert_cast(center_lat.get()); auto radius = block.get_by_position(arguments[2]).column->convert_to_full_column_if_const(); + const auto* radius_ptr = assert_cast(radius.get()); const auto size = center_lng->size(); - MutableColumnPtr res = return_type->create_column(); + auto res = ColumnString::create(); + + auto null_map = ColumnUInt8::create(size, 0); + auto& null_map_data = null_map->get_data(); GeoCircle circle; std::string buf; for (int row = 0; row < size; ++row) { - auto lng_value = center_lng->get_float64(row); - auto lat_value = center_lat->get_float64(row); - auto radius_value = radius->get_float64(row); + auto lng_value = center_lng_ptr->get_element(row); + auto lat_value = center_lat_ptr->get_element(row); + auto radius_value = radius_ptr->get_element(row); auto value = circle.init(lng_value, lat_value, radius_value); if (value != GEO_PARSE_OK) { - res->insert_data(nullptr, 0); + null_map_data[row] = 1; + res->insert_default(); continue; } buf.clear(); circle.encode_to(&buf); res->insert_data(buf.data(), buf.size()); } - block.replace_by_position(result, std::move(res)); + block.replace_by_position(result, + ColumnNullable::create(std::move(res), std::move(null_map))); return Status::OK(); } @@ -537,6 +609,7 @@ struct StContains { static constexpr auto NEED_CONTEXT = true; static constexpr auto NAME = "st_contains"; static const size_t NUM_ARGS = 2; + using Type = DataTypeUInt8; static Status execute(FunctionContext* context, Block& block, const ColumnNumbers& arguments, size_t result) { DCHECK_EQ(arguments.size(), 2); @@ -548,27 +621,32 @@ struct StContains { const auto size = std::max(left_column->size(), right_column->size()); - MutableColumnPtr res = return_type->create_column(); + auto res = ColumnUInt8::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, size); + const_vector(left_column, right_column, res, null_map_data, size); } else if (right_const) { - vector_const(left_column, right_column, res, size); + vector_const(left_column, right_column, res, null_map_data, size); } else { - vector_vector(left_column, right_column, res, size); + vector_vector(left_column, right_column, res, null_map_data, size); } - block.replace_by_position(result, std::move(res)); + block.replace_by_position(result, + ColumnNullable::create(std::move(res), std::move(null_map))); return Status::OK(); } static void loop_do(StringRef& lhs_value, StringRef& rhs_value, std::vector>& shapes, int& i, - MutableColumnPtr& res) { + ColumnUInt8::MutablePtr& res, NullMap& null_map, int row) { StringRef* strs[2] = {&lhs_value, &rhs_value}; for (i = 0; i < 2; ++i) { shapes[i] = std::shared_ptr(GeoShape::from_encoded(strs[i]->data, strs[i]->size)); if (shapes[i] == nullptr) { + null_map[row] = 1; res->insert_default(); break; } @@ -581,35 +659,35 @@ struct StContains { } static void const_vector(const ColumnPtr& left_column, const ColumnPtr& right_column, - MutableColumnPtr& res, const size_t size) { + ColumnUInt8::MutablePtr& res, NullMap& null_map, const size_t size) { int i; auto lhs_value = left_column->get_data_at(0); std::vector> shapes = {nullptr, nullptr}; for (int row = 0; row < size; ++row) { auto rhs_value = right_column->get_data_at(row); - loop_do(lhs_value, rhs_value, shapes, i, res); + loop_do(lhs_value, rhs_value, shapes, i, res, null_map, row); } } static void vector_const(const ColumnPtr& left_column, const ColumnPtr& right_column, - MutableColumnPtr& res, const size_t size) { + ColumnUInt8::MutablePtr& res, NullMap& null_map, const size_t size) { int i; auto rhs_value = right_column->get_data_at(0); std::vector> shapes = {nullptr, nullptr}; for (int row = 0; row < size; ++row) { auto lhs_value = left_column->get_data_at(row); - loop_do(lhs_value, rhs_value, shapes, i, res); + loop_do(lhs_value, rhs_value, shapes, i, res, null_map, row); } } static void vector_vector(const ColumnPtr& left_column, const ColumnPtr& right_column, - MutableColumnPtr& res, const size_t size) { + ColumnUInt8::MutablePtr& res, NullMap& null_map, const size_t size) { int i; std::vector> shapes = {nullptr, nullptr}; for (int row = 0; row < size; ++row) { auto lhs_value = left_column->get_data_at(row); auto rhs_value = right_column->get_data_at(row); - loop_do(lhs_value, rhs_value, shapes, i, res); + loop_do(lhs_value, rhs_value, shapes, i, res, null_map, row); } } @@ -662,6 +740,7 @@ struct StGeoFromText { static constexpr auto NEED_CONTEXT = true; static constexpr auto NAME = Impl::NAME; static const size_t NUM_ARGS = 1; + using Type = DataTypeString; static Status execute(FunctionContext* context, Block& block, const ColumnNumbers& arguments, size_t result) { DCHECK_EQ(arguments.size(), 1); @@ -669,8 +748,9 @@ struct StGeoFromText { auto& geo = block.get_by_position(arguments[0]).column; const auto size = geo->size(); - MutableColumnPtr res = return_type->create_column(); - + auto res = ColumnString::create(); + auto null_map = ColumnUInt8::create(size, 0); + auto& null_map_data = null_map->get_data(); GeoParseStatus status; std::string buf; for (int row = 0; row < size; ++row) { @@ -678,14 +758,16 @@ struct StGeoFromText { std::unique_ptr shape(GeoShape::from_wkt(value.data, value.size, &status)); if (shape == nullptr || status != GEO_PARSE_OK || (Impl::shape_type != GEO_SHAPE_ANY && shape->type() != Impl::shape_type)) { - res->insert_data(nullptr, 0); + null_map_data[row] = 1; + res->insert_default(); continue; } buf.clear(); shape->encode_to(&buf); res->insert_data(buf.data(), buf.size()); } - block.replace_by_position(result, std::move(res)); + block.replace_by_position(result, + ColumnNullable::create(std::move(res), std::move(null_map))); return Status::OK(); } @@ -713,6 +795,7 @@ struct StGeoFromWkb { static constexpr auto NEED_CONTEXT = true; static constexpr auto NAME = Impl::NAME; static const size_t NUM_ARGS = 1; + using Type = DataTypeString; static Status execute(FunctionContext* context, Block& block, const ColumnNumbers& arguments, size_t result) { DCHECK_EQ(arguments.size(), 1); @@ -720,22 +803,25 @@ struct StGeoFromWkb { auto& geo = block.get_by_position(arguments[0]).column; const auto size = geo->size(); - MutableColumnPtr res = return_type->create_column(); - + auto res = ColumnString::create(); + auto null_map = ColumnUInt8::create(size, 0); + auto& null_map_data = null_map->get_data(); GeoParseStatus status; std::string buf; for (int row = 0; row < size; ++row) { auto value = geo->get_data_at(row); std::unique_ptr shape(GeoShape::from_wkb(value.data, value.size, &status)); if (shape == nullptr || status != GEO_PARSE_OK) { - res->insert_data(nullptr, 0); + null_map_data[row] = 1; + res->insert_default(); continue; } buf.clear(); shape->encode_to(&buf); res->insert_data(buf.data(), buf.size()); } - block.replace_by_position(result, std::move(res)); + block.replace_by_position(result, + ColumnNullable::create(std::move(res), std::move(null_map))); return Status::OK(); } @@ -752,34 +838,39 @@ struct StAsBinary { static constexpr auto NEED_CONTEXT = true; static constexpr auto NAME = "st_asbinary"; static const size_t NUM_ARGS = 1; + using Type = DataTypeString; static Status execute(FunctionContext* context, Block& block, const ColumnNumbers& arguments, size_t result) { DCHECK_EQ(arguments.size(), 1); auto return_type = block.get_data_type(result); - MutableColumnPtr res = return_type->create_column(); + auto res = ColumnString::create(); auto col = block.get_by_position(arguments[0]).column; const auto size = col->size(); - + auto null_map = ColumnUInt8::create(size, 0); + auto& null_map_data = null_map->get_data(); std::unique_ptr 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) { - res->insert_data(nullptr, 0); + null_map_data[row] = 1; + res->insert_default(); continue; } std::string binary = GeoShape::as_binary(shape.get()); if (binary.empty()) { - res->insert_data(nullptr, 0); + null_map_data[row] = 1; + res->insert_default(); continue; } res->insert_data(binary.data(), binary.size()); } - block.replace_by_position(result, std::move(res)); + block.replace_by_position(result, + ColumnNullable::create(std::move(res), std::move(null_map))); return Status::OK(); } @@ -796,13 +887,13 @@ void register_function_geo(SimpleFunctionFactory& factory) { factory.register_function>(); factory.register_function>>(); factory.register_function>>(); - factory.register_function>(); - factory.register_function>(); - factory.register_function>(); - factory.register_function>(); - factory.register_function>(); - factory.register_function>(); - factory.register_function>(); + factory.register_function>(); + factory.register_function>(); + factory.register_function>(); + factory.register_function>(); + factory.register_function>(); + factory.register_function>(); + factory.register_function>(); factory.register_function>(); factory.register_function>>(); factory.register_function>>(); @@ -811,8 +902,8 @@ void register_function_geo(SimpleFunctionFactory& factory) { factory.register_function>>(); factory.register_function>>(); factory.register_function>>(); - factory.register_function>(); - factory.register_function>(); + factory.register_function>(); + factory.register_function>(); factory.register_function>>(); factory.register_function>>(); factory.register_function>(); diff --git a/be/src/vec/functions/functions_geo.h b/be/src/vec/functions/functions_geo.h index 9c4db09e14967f..ac0358d42f59f4 100644 --- a/be/src/vec/functions/functions_geo.h +++ b/be/src/vec/functions/functions_geo.h @@ -56,11 +56,12 @@ struct StContainsState { std::vector> shapes; }; -template +template class GeoFunction : public IFunction { public: static constexpr auto name = Impl::NAME; - static FunctionPtr create() { return std::make_shared>(); } + using ReturnType = typename Impl::Type; + static FunctionPtr create() { return std::make_shared>(); } String get_name() const override { return name; } size_t get_number_of_arguments() const override { return Impl::NUM_ARGS; } bool is_variadic() const override { return false; } From f72a88e2fbd69dd108532e34fb6367332e783d15 Mon Sep 17 00:00:00 2001 From: koi Date: Mon, 7 Apr 2025 12:04:55 +0800 Subject: [PATCH 2/3] [Enhancement] Support some spatial functions (#48695) Support for ST_Intersects, ST_Disjoint, ST_Touches sql functions. --- be/src/geo/geo_types.cpp | 600 ++++++++++++ be/src/geo/geo_types.h | 27 + be/src/vec/functions/functions_geo.cpp | 69 +- be/test/geo/geo_types_test.cpp | 916 ++++++++++++++++++ .../doris/catalog/BuiltinScalarFunctions.java | 6 + .../functions/scalar/StDisjoint.java | 72 ++ .../functions/scalar/StIntersects.java | 72 ++ .../functions/scalar/StTouches.java | 72 ++ .../visitor/ScalarFunctionVisitor.java | 15 + .../spatial_functions/test_gis_function.out | 309 ++++++ .../spatial_functions/test_gis_function.out | 186 ++++ .../test_gis_function.groovy | 133 +++ .../test_gis_function.groovy | 81 ++ 13 files changed, 2533 insertions(+), 25 deletions(-) create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StDisjoint.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StIntersects.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StTouches.java diff --git a/be/src/geo/geo_types.cpp b/be/src/geo/geo_types.cpp index dc27595da3bfc6..04e5b5ef1acd23 100644 --- a/be/src/geo/geo_types.cpp +++ b/be/src/geo/geo_types.cpp @@ -20,8 +20,11 @@ #include #include #include +#include +#include #include #include +#include #include #include #include @@ -30,6 +33,8 @@ #include #include #include + +#include "vec/common/assert_cast.h" // IWYU pragma: no_include #include #include @@ -43,6 +48,8 @@ namespace doris { +constexpr double TOLERANCE = 1e-6; + GeoPoint::GeoPoint() : _point(new S2Point()) {} GeoPoint::~GeoPoint() = default; @@ -75,6 +82,183 @@ static inline GeoParseStatus to_s2point(double lng, double lat, S2Point* point) return GEO_PARSE_OK; } +double project_distance(const S2Point& point, const S2Point& lineStart, const S2Point& lineEnd) { + S2Point lineVector = lineEnd - lineStart; + S2Point pointVector = point - lineStart; + double lineVectorMagnitudeSquared = lineVector.DotProd(lineVector); + double t = pointVector.DotProd(lineVector) / lineVectorMagnitudeSquared; + t = t > 0 ? t : 0; + t = t < 1 ? t : 1; + S2Point nearestPoint = lineStart + t * lineVector; + S2Point distanceVector = point - nearestPoint; + return sqrt(distanceVector.DotProd(distanceVector)); +} + +double compute_distance_to_line(const S2Point& point, const S2Polyline* line) { + const S2Point& line_point1 = line->vertex(0); + const S2Point& line_point2 = line->vertex(1); + S2LatLng lp1 = S2LatLng(line_point1); + S2LatLng lp2 = S2LatLng(line_point2); + + S2LatLng lquery = S2LatLng(point); + double lat1 = lp1.lat().degrees(); + double long1 = lp1.lng().degrees(); + + double lat2 = lp2.lat().degrees(); + double long2 = lp2.lng().degrees(); + + double latq = lquery.lat().degrees(); + double longq = lquery.lng().degrees(); + return project_distance({latq, longq, 0}, {lat1, long1, 0}, {lat2, long2, 0}); +} + +double compute_distance_to_point(const S2Point& point1, const S2Point& point2) { + S2LatLng lp1 = S2LatLng(point1); + S2LatLng lp2 = S2LatLng(point2); + + double lat1 = lp1.lat().degrees(); + double long1 = lp1.lng().degrees(); + + double lat2 = lp2.lat().degrees(); + double long2 = lp2.lng().degrees(); + return sqrt((lat1 - lat2) * (lat1 - lat2) + (long1 - long2) * (long1 - long2)); +} + +double compute_distance_to_line(const S2Point& point, const S2Point& line_point1, + const S2Point& line_point2) { + S2LatLng lp1 = S2LatLng(line_point1); + S2LatLng lp2 = S2LatLng(line_point2); + + S2LatLng lquery = S2LatLng(point); + double lat1 = lp1.lat().degrees(); + double long1 = lp1.lng().degrees(); + + double lat2 = lp2.lat().degrees(); + double long2 = lp2.lng().degrees(); + + double latq = lquery.lat().degrees(); + double longq = lquery.lng().degrees(); + return project_distance({latq, longq, 0}, {lat1, long1, 0}, {lat2, long2, 0}); +} + +double cross_product(const S2Point& a, const S2Point& b, const S2Point& c) { + return (b.x() - a.x()) * (c.y() - a.y()) - (b.y() - a.y()) * (c.x() - a.x()); +} + +bool is_point_on_segment(const S2Point& p, const S2Point& a, const S2Point& b) { + return (p.x() >= std::min(a.x(), b.x()) && p.x() <= std::max(a.x(), b.x()) && + p.y() >= std::min(a.y(), b.y()) && p.y() <= std::max(a.y(), b.y()) && + p.z() >= std::min(a.z(), b.z()) && p.z() <= std::max(a.z(), b.z())); +} + +bool do_segments_intersect(const S2Point& a1, const S2Point& a2, const S2Point& b1, + const S2Point& b2) { + if (std::max(a1.x(), a2.x()) < std::min(b1.x(), b2.x()) || + std::max(a1.y(), a2.y()) < std::min(b1.y(), b2.y()) || + std::max(a1.z(), a2.z()) < std::min(b1.z(), b2.z()) || + std::min(a1.x(), a2.x()) > std::max(b1.x(), b2.x()) || + std::min(a1.y(), a2.y()) > std::max(b1.y(), b2.y()) || + std::min(a1.z(), a2.z()) > std::max(b1.z(), b2.z())) { + return false; + } + + double d1 = cross_product(b1, b2, a1); + double d2 = cross_product(b1, b2, a2); + double d3 = cross_product(a1, a2, b1); + double d4 = cross_product(a1, a2, b2); + + if ((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0)) { + if ((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0)) { + return true; + } + } + + if (d1 == 0 && is_point_on_segment(a1, b1, b2)) { + return true; + } + if (d2 == 0 && is_point_on_segment(a2, b1, b2)) { + return true; + } + if (d3 == 0 && is_point_on_segment(b1, a1, a2)) { + return true; + } + if (d4 == 0 && is_point_on_segment(b2, a1, a2)) { + return true; + } + + return false; +} + +bool is_segments_intersect(const S2Point& point1, const S2Point& point2, const S2Point& line_point1, + const S2Point& line_point2) { + S2LatLng lp1 = S2LatLng(line_point1); + S2LatLng lp2 = S2LatLng(line_point2); + + S2LatLng lquery1 = S2LatLng(point1); + S2LatLng lquery2 = S2LatLng(point2); + double lat1 = lp1.lat().degrees(); + double long1 = lp1.lng().degrees(); + + double lat2 = lp2.lat().degrees(); + double long2 = lp2.lng().degrees(); + + double latq1 = lquery1.lat().degrees(); + double longq1 = lquery1.lng().degrees(); + double latq2 = lquery2.lat().degrees(); + double longq2 = lquery2.lng().degrees(); + return do_segments_intersect({latq1, longq1, 0}, {latq2, longq2, 0}, {lat1, long1, 0}, + {lat2, long2, 0}); +} + +static bool ray_crosses_segment(double px, double py, double ax, double ay, double bx, double by) { + if (ay > by) { + std::swap(ax, bx); + std::swap(ay, by); + } + + if (py <= ay || py > by) return false; + double intersectX; + if (std::abs(ax - bx) < std::numeric_limits::epsilon()) { + intersectX = ax; + } else { + double slope = (bx - ax) / (by - ay); + intersectX = ax + slope * (py - ay); + } + return px < intersectX; +} + +bool is_point_in_polygon(const S2Point& point, const S2Polygon* polygon) { + int crossings = 0; + + for (int j = 0; j < polygon->num_loops(); ++j) { + const S2Loop* loop = polygon->loop(j); + for (int k = 0; k < loop->num_vertices(); ++k) { + const S2Point& p1 = loop->vertex(k); + const S2Point& p2 = loop->vertex((k + 1) % loop->num_vertices()); + + S2LatLng lp1 = S2LatLng(p1); + S2LatLng lp2 = S2LatLng(p2); + + S2LatLng lquery = S2LatLng(point); + double lat1 = lp1.lat().degrees(); + double long1 = lp1.lng().degrees(); + + double lat2 = lp2.lat().degrees(); + double long2 = lp2.lng().degrees(); + + double latq = lquery.lat().degrees(); + double longq = lquery.lng().degrees(); + + bool crossesRay = ray_crosses_segment(latq, longq, lat1, long1, lat2, long2); + + if (crossesRay) { + crossings++; + } + } + } + return (crossings % 2 == 1); +} + static inline GeoParseStatus to_s2point(const GeoCoordinate& coord, S2Point* point) { return to_s2point(coord.x, coord.y, point); } @@ -299,6 +483,57 @@ const std::unique_ptr GeoPolygon::to_coords() const { return coordss; } +bool GeoPoint::intersects(const GeoShape* rhs) const { + switch (rhs->type()) { + case GEO_SHAPE_POINT: { + // points and points are considered to intersect when they are equal + const GeoPoint* point = assert_cast(rhs); + return *_point == *point->point(); + } + case GEO_SHAPE_LINE_STRING: { + const GeoLine* line = assert_cast(rhs); + return line->intersects(this); + } + case GEO_SHAPE_POLYGON: { + const GeoPolygon* polygon = assert_cast(rhs); + return polygon->intersects(this); + } + case GEO_SHAPE_CIRCLE: { + const GeoCircle* circle = assert_cast(rhs); + return circle->intersects(this); + } + default: + return false; + } +} + +bool GeoPoint::disjoint(const GeoShape* rhs) const { + return !intersects(rhs); +} + +bool GeoPoint::touches(const GeoShape* rhs) const { + switch (rhs->type()) { + case GEO_SHAPE_POINT: { + // always returns false because the point has no boundaries + return false; + } + case GEO_SHAPE_LINE_STRING: { + const GeoLine* line = assert_cast(rhs); + return line->touches(this); + } + case GEO_SHAPE_POLYGON: { + const GeoPolygon* polygon = assert_cast(rhs); + return polygon->touches(this); + } + case GEO_SHAPE_CIRCLE: { + const GeoCircle* circle = assert_cast(rhs); + return circle->touches(this); + } + default: + return false; + } +} + std::string GeoPoint::to_string() const { return as_wkt(); } @@ -403,6 +638,86 @@ GeoParseStatus GeoLine::from_coords(const GeoCoordinateList& list) { return to_s2polyline(list, &_polyline); } +bool GeoLine::intersects(const GeoShape* rhs) const { + switch (rhs->type()) { + case GEO_SHAPE_POINT: { + const GeoPoint* point = assert_cast(rhs); + return compute_distance_to_line(*point->point(), _polyline.get()) < TOLERANCE; + } + case GEO_SHAPE_LINE_STRING: { + const GeoLine* line = assert_cast(rhs); + return is_segments_intersect(_polyline->vertex(0), _polyline->vertex(1), + line->polyline()->vertex(0), line->polyline()->vertex(1)); + } + case GEO_SHAPE_POLYGON: { + const GeoPolygon* polygon = assert_cast(rhs); + return polygon->polygon()->Intersects(*_polyline); + } + case GEO_SHAPE_CIRCLE: { + const GeoCircle* circle = assert_cast(rhs); + return circle->intersects(this); + } + default: + return false; + } +} + +bool GeoLine::disjoint(const GeoShape* rhs) const { + return !intersects(rhs); +} + +bool GeoLine::touches(const GeoShape* rhs) const { + switch (rhs->type()) { + case GEO_SHAPE_POINT: { + const GeoPoint* point = assert_cast(rhs); + const S2Point& start = _polyline->vertex(0); + const S2Point& end = _polyline->vertex(_polyline->num_vertices() - 1); + // 1. Points do not have boundaries. when the point is on the start or end of the line return true + if (start == *point->point() || end == *point->point()) { + return true; + } + return false; + } + case GEO_SHAPE_LINE_STRING: { + const GeoLine* other = assert_cast(rhs); + + const S2Point& p1 = _polyline->vertex(0); + const S2Point& p2 = _polyline->vertex(1); + + const S2Point& p3 = other->polyline()->vertex(0); + const S2Point& p4 = other->polyline()->vertex(1); + int count = 0; + if (compute_distance_to_line(p1, p3, p4) < TOLERANCE) { + count++; + } + if (compute_distance_to_line(p2, p3, p4) < TOLERANCE) { + count++; + } + if (compute_distance_to_line(p3, p1, p2) < TOLERANCE) { + count++; + } + if (compute_distance_to_line(p4, p1, p2) < TOLERANCE) { + count++; + } + // Two intersections are allowed when there is only one intersection, or when the intersection is an endpoint + if (count == 1 || (count == 2 && ((p1 == p3 || p1 == p4) + (p2 == p3 || p2 == p4)) == 1)) { + return true; + } + return false; + } + case GEO_SHAPE_POLYGON: { + const GeoPolygon* polygon = assert_cast(rhs); + return polygon->touches(this); + } + case GEO_SHAPE_CIRCLE: { + const GeoCircle* circle = assert_cast(rhs); + return circle->touches(this); + } + default: + return false; + } +} + void GeoLine::encode(std::string* buf) { Encoder encoder; _polyline->Encode(&encoder); @@ -476,6 +791,171 @@ std::string GeoPolygon::as_wkt() const { return ss.str(); } +bool GeoPolygon::intersects(const GeoShape* rhs) const { + switch (rhs->type()) { + case GEO_SHAPE_POINT: { + const GeoPoint* point = assert_cast(rhs); + if (is_point_in_polygon(*point->point(), _polygon.get())) { + return true; + } + return polygon_touch_point(_polygon.get(), point->point()); + } + case GEO_SHAPE_LINE_STRING: { + const GeoLine* line = assert_cast(rhs); + std::vector> intersect_lines = + _polygon->IntersectWithPolyline(*line->polyline()); + if (!intersect_lines.empty()) { + return true; + } + + for (int i = 0; i < line->polyline()->num_vertices(); i++) { + const S2Point& outer_p1 = line->polyline()->vertex(i); + const S2Point& outer_p2 = + line->polyline()->vertex((i + 1) % line->polyline()->num_vertices()); + for (int j = 0; j < _polygon->num_loops(); ++j) { + const S2Loop* loop = _polygon->loop(j); + for (int k = 0; k < loop->num_vertices(); ++k) { + const S2Point& p1 = loop->vertex(k); + const S2Point& p2 = loop->vertex((k + 1) % loop->num_vertices()); + if (is_segments_intersect(outer_p1, outer_p2, p1, p2)) { + return true; + } + } + } + } + return false; + } + case GEO_SHAPE_POLYGON: { + const GeoPolygon* other = assert_cast(rhs); + // When two polygons intersect only at the boundary, s2geometry may not return the correct result. + if (!_polygon->Intersects(*other->polygon())) { + return polygon_touch_polygon(_polygon.get(), other->polygon()); + } + return true; + } + case GEO_SHAPE_CIRCLE: { + const GeoCircle* circle = assert_cast(rhs); + return circle->intersects(this); + } + default: + return false; + } +} + +bool GeoPolygon::disjoint(const GeoShape* rhs) const { + return !intersects(rhs); +} + +bool GeoPolygon::polygon_touch_point(const S2Polygon* polygon, const S2Point* point) const { + for (int k = 0; k < polygon->num_loops(); ++k) { + const S2Loop* innee_loop = polygon->loop(k); + for (int l = 0; l < innee_loop->num_vertices(); ++l) { + const S2Point& p1 = innee_loop->vertex(l); + const S2Point& p2 = innee_loop->vertex((l + 1) % innee_loop->num_vertices()); + double distance = compute_distance_to_line(*point, p1, p2); + if (distance < TOLERANCE) { + return true; + } + } + } + return false; +} + +bool GeoPolygon::polygon_touch_polygon(const S2Polygon* polygon1, const S2Polygon* polygon2) const { + for (int i = 0; i < polygon1->num_loops(); ++i) { + const S2Loop* loop = polygon1->loop(i); + for (int j = 0; j < loop->num_vertices(); ++j) { + const S2Point& p = loop->vertex(j); + for (int k = 0; k < polygon2->num_loops(); ++k) { + const S2Loop* innee_loop = polygon2->loop(k); + for (int l = 0; l < innee_loop->num_vertices(); ++l) { + const S2Point& p1 = innee_loop->vertex(l); + const S2Point& p2 = innee_loop->vertex((l + 1) % loop->num_vertices()); + double distance = compute_distance_to_line(p, p1, p2); + if (distance < TOLERANCE) { + return true; + } + } + } + } + } + return false; +} + +bool GeoPolygon::touches(const GeoShape* rhs) const { + switch (rhs->type()) { + case GEO_SHAPE_POINT: { + const GeoPoint* point = assert_cast(rhs); + return polygon_touch_point(_polygon.get(), point->point()); + } + case GEO_SHAPE_LINE_STRING: { + const GeoLine* line = assert_cast(rhs); + std::vector> intersect_lines = + _polygon->IntersectWithPolyline(*line->polyline()); + + std::set polygon_points; + // 1. collect all points in the 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) { + const S2Point& p = loop->vertex(j); + polygon_points.insert(p); + } + } + // 2. check if the intersect line's points are on the polygon + for (auto& iline : intersect_lines) { + for (int i = 0; i < iline->num_vertices(); ++i) { + const S2Point& p = iline->vertex(i); + if (polygon_points.find(p) == polygon_points.end()) { + return false; + } + } + } + // 3. check if the line is on the boundary of the polygon + if (intersect_lines.empty()) { + for (const S2Point& p : polygon_points) { + double distance = compute_distance_to_line(p, line->polyline()); + if (distance < TOLERANCE) { + return true; + } + } + for (int i = 0; i < line->polyline()->num_vertices(); i++) { + const S2Point& p = line->polyline()->vertex(i); + for (int j = 0; j < _polygon->num_loops(); ++j) { + const S2Loop* loop = _polygon->loop(j); + for (int k = 0; k < loop->num_vertices(); ++k) { + const S2Point& p1 = loop->vertex(k); + const S2Point& p2 = loop->vertex((k + 1) % loop->num_vertices()); + double distance = compute_distance_to_line(p, p1, p2); + if (distance < TOLERANCE) { + return true; + } + } + } + } + return false; + } + return true; + } + case GEO_SHAPE_POLYGON: { + const GeoPolygon* other = assert_cast(rhs); + const S2Polygon* polygon1 = _polygon.get(); + const S2Polygon* polygon2 = other->polygon(); + // when the two polygons do not have overlapping areas, then determine if the touch regulation is met. + if (!polygon1->Intersects(polygon2)) { + return polygon_touch_polygon(polygon1, polygon2); + } + return false; + } + case GEO_SHAPE_CIRCLE: { + const GeoCircle* circle = assert_cast(rhs); + return circle->touches(this); + } + default: + return false; + } +} + bool GeoPolygon::contains(const GeoShape* rhs) const { switch (rhs->type()) { case GEO_SHAPE_POINT: { @@ -521,6 +1001,126 @@ GeoParseStatus GeoCircle::init(double lng, double lat, double radius_meter) { return GEO_PARSE_OK; } +bool GeoCircle::intersects(const GeoShape* rhs) const { + switch (rhs->type()) { + case GEO_SHAPE_POINT: { + const GeoPoint* point = assert_cast(rhs); + const S2Point& center = _cap->center(); + S1ChordAngle radius_angle = _cap->radius(); + // The radius unit of circle is initially in meters, + // which needs to be converted back to meters when comparing + double radius = S2Earth::RadiansToMeters(radius_angle.radians()); + return radius + TOLERANCE >= compute_distance_to_point(center, *point->point()); + } + case GEO_SHAPE_LINE_STRING: { + const GeoLine* line = assert_cast(rhs); + const S2Point& center = _cap->center(); + S1ChordAngle radius_angle = _cap->radius(); + double radius = S2Earth::RadiansToMeters(radius_angle.radians()); + + double distance = compute_distance_to_line(center, line->polyline()); + return radius + TOLERANCE >= distance; + } + case GEO_SHAPE_POLYGON: { + const GeoPolygon* polygon = assert_cast(rhs); + const S2Point& center = _cap->center(); + S1ChordAngle radius_angle = _cap->radius(); + if (is_point_in_polygon(center, polygon->polygon())) { + return true; + } + double radius = S2Earth::RadiansToMeters(radius_angle.radians()); + for (int k = 0; k < polygon->polygon()->num_loops(); ++k) { + const S2Loop* loop = polygon->polygon()->loop(k); + for (int l = 0; l < loop->num_vertices(); ++l) { + const S2Point& p1 = loop->vertex(l); + const S2Point& p2 = loop->vertex((l + 1) % loop->num_vertices()); + double distance = compute_distance_to_line(center, p1, p2); + + if (radius + TOLERANCE >= distance) { + return true; + } + } + } + return false; + } + case GEO_SHAPE_CIRCLE: { + const GeoCircle* circle = assert_cast(rhs); + S1ChordAngle radius_angle = _cap->radius(); + S1ChordAngle other_radius_angle = circle->circle()->radius(); + + double radius1 = S2Earth::RadiansToMeters(radius_angle.radians()); + double radius2 = S2Earth::RadiansToMeters(other_radius_angle.radians()); + double distance = compute_distance_to_point(_cap->center(), circle->circle()->center()); + return radius1 + radius2 + TOLERANCE >= distance; + } + default: + return false; + } +} + +bool GeoCircle::disjoint(const GeoShape* rhs) const { + return !intersects(rhs); +} + +bool GeoCircle::touches(const GeoShape* rhs) const { + switch (rhs->type()) { + case GEO_SHAPE_POINT: { + const GeoPoint* point = assert_cast(rhs); + const S2Point& center = _cap->center(); + S1ChordAngle radius_angle = _cap->radius(); + + double radius = S2Earth::RadiansToMeters(radius_angle.radians()); + return std::abs(radius - compute_distance_to_point(center, *point->point())) < TOLERANCE; + } + case GEO_SHAPE_LINE_STRING: { + const GeoLine* line = assert_cast(rhs); + const S2Point& center = _cap->center(); + S1ChordAngle radius_angle = _cap->radius(); + + double radius = S2Earth::RadiansToMeters(radius_angle.radians()); + double distance = compute_distance_to_line(center, line->polyline()); + return std::abs(radius - distance) < TOLERANCE; + } + case GEO_SHAPE_POLYGON: { + const GeoPolygon* polygon = assert_cast(rhs); + const S2Point& center = _cap->center(); + S1ChordAngle radius_angle = _cap->radius(); + if (is_point_in_polygon(center, polygon->polygon()) || + polygon->polygon_touch_point(polygon->polygon(), ¢er)) { + return false; + } + + double radius = S2Earth::RadiansToMeters(radius_angle.radians()); + for (int k = 0; k < polygon->polygon()->num_loops(); ++k) { + const S2Loop* loop = polygon->polygon()->loop(k); + for (int l = 0; l < loop->num_vertices(); ++l) { + const S2Point& p1 = loop->vertex(l); + const S2Point& p2 = loop->vertex((l + 1) % loop->num_vertices()); + double distance = compute_distance_to_line(center, p1, p2); + + if (std::abs(radius - distance) < TOLERANCE) { + return true; + } + } + } + + return false; + } + case GEO_SHAPE_CIRCLE: { + const GeoCircle* circle = assert_cast(rhs); + S1ChordAngle radius_angle = _cap->radius(); + S1ChordAngle other_radius_angle = circle->circle()->radius(); + + double radius1 = S2Earth::RadiansToMeters(radius_angle.radians()); + double radius2 = S2Earth::RadiansToMeters(other_radius_angle.radians()); + double distance = compute_distance_to_point(_cap->center(), circle->circle()->center()); + return std::abs(radius1 + radius2 - distance) < TOLERANCE; + } + default: + return false; + } +} + bool GeoCircle::contains(const GeoShape* rhs) const { switch (rhs->type()) { case GEO_SHAPE_POINT: { diff --git a/be/src/geo/geo_types.h b/be/src/geo/geo_types.h index aaeff3db58f228..0d08c9a80ef699 100644 --- a/be/src/geo/geo_types.h +++ b/be/src/geo/geo_types.h @@ -60,6 +60,13 @@ class GeoShape { virtual std::string as_wkt() const = 0; virtual bool contains(const GeoShape* rhs) const { return false; } + + virtual bool disjoint(const GeoShape* rhs) const { return false; } + + virtual bool intersects(const GeoShape* rhs) const { return false; } + + virtual bool touches(const GeoShape* rhs) const { return false; } + virtual std::string to_string() const { return ""; } static std::string as_binary(GeoShape* rhs); @@ -82,6 +89,10 @@ class GeoPoint : public GeoShape { GeoCoordinateList to_coords() const; + bool intersects(const GeoShape* rhs) const override; + bool disjoint(const GeoShape* rhs) const override; + bool touches(const GeoShape* rhs) const override; + GeoShapeType type() const override { return GEO_SHAPE_POINT; } const S2Point* point() const { return _point.get(); } @@ -119,6 +130,10 @@ class GeoLine : public GeoShape { GeoCoordinateList to_coords() const; + bool intersects(const GeoShape* rhs) const override; + bool disjoint(const GeoShape* rhs) const override; + bool touches(const GeoShape* rhs) const override; + GeoShapeType type() const override { return GEO_SHAPE_LINE_STRING; } const S2Polyline* polyline() const { return _polyline.get(); } @@ -148,7 +163,14 @@ class GeoPolygon : public GeoShape { GeoShapeType type() const override { return GEO_SHAPE_POLYGON; } const S2Polygon* polygon() const { return _polygon.get(); } + bool intersects(const GeoShape* rhs) const override; + bool disjoint(const GeoShape* rhs) const override; + bool touches(const GeoShape* rhs) const override; bool contains(const GeoShape* rhs) const override; + + bool polygon_touch_point(const S2Polygon* polygon, const S2Point* point) const; + bool polygon_touch_polygon(const S2Polygon* polygon1, const S2Polygon* polygon2) const; + std::string as_wkt() const override; int numLoops() const; @@ -174,6 +196,11 @@ class GeoCircle : public GeoShape { GeoShapeType type() const override { return GEO_SHAPE_CIRCLE; } + const S2Cap* circle() const { return _cap.get(); } + + bool intersects(const GeoShape* rhs) const override; + bool disjoint(const GeoShape* rhs) const override; + bool touches(const GeoShape* rhs) const override; bool contains(const GeoShape* rhs) const override; std::string as_wkt() const override; diff --git a/be/src/vec/functions/functions_geo.cpp b/be/src/vec/functions/functions_geo.cpp index b389bc1636e45f..67ca635d789e30 100644 --- a/be/src/vec/functions/functions_geo.cpp +++ b/be/src/vec/functions/functions_geo.cpp @@ -605,11 +605,13 @@ struct StCircle { } }; -struct StContains { +template +struct StRelationFunction { static constexpr auto NEED_CONTEXT = true; - static constexpr auto NAME = "st_contains"; + static constexpr auto NAME = Func::NAME; static const size_t NUM_ARGS = 2; using Type = DataTypeUInt8; + static Status execute(FunctionContext* context, Block& block, const ColumnNumbers& arguments, size_t result) { DCHECK_EQ(arguments.size(), 2); @@ -621,8 +623,7 @@ struct StContains { const auto size = std::max(left_column->size(), right_column->size()); - auto res = ColumnUInt8::create(); - res->reserve(size); + auto res = ColumnUInt8::create(size, 0); auto null_map = ColumnUInt8::create(size, 0); auto& null_map_data = null_map->get_data(); @@ -639,55 +640,50 @@ struct StContains { } static void loop_do(StringRef& lhs_value, StringRef& rhs_value, - std::vector>& shapes, int& i, + std::vector>& shapes, ColumnUInt8::MutablePtr& res, NullMap& null_map, int row) { StringRef* strs[2] = {&lhs_value, &rhs_value}; - for (i = 0; i < 2; ++i) { - shapes[i] = - std::shared_ptr(GeoShape::from_encoded(strs[i]->data, strs[i]->size)); - if (shapes[i] == nullptr) { + for (int i = 0; i < 2; ++i) { + std::unique_ptr shape(GeoShape::from_encoded(strs[i]->data, strs[i]->size)); + shapes[i] = std::move(shape); + if (!shapes[i]) { null_map[row] = 1; - res->insert_default(); break; } } - - if (i == 2) { - auto contains_value = shapes[0]->contains(shapes[1].get()); - res->insert_data(const_cast((char*)&contains_value), 0); + if (shapes[0] && shapes[1]) { + auto relation_value = Func::evaluate(shapes[0].get(), shapes[1].get()); + res->get_data()[row] = relation_value; } } static void const_vector(const ColumnPtr& left_column, const ColumnPtr& right_column, ColumnUInt8::MutablePtr& res, NullMap& null_map, const size_t size) { - int i; auto lhs_value = left_column->get_data_at(0); - std::vector> shapes = {nullptr, nullptr}; + std::vector> shapes(2); for (int row = 0; row < size; ++row) { auto rhs_value = right_column->get_data_at(row); - loop_do(lhs_value, rhs_value, shapes, i, res, null_map, row); + loop_do(lhs_value, rhs_value, shapes, res, null_map, row); } } static void vector_const(const ColumnPtr& left_column, const ColumnPtr& right_column, ColumnUInt8::MutablePtr& res, NullMap& null_map, const size_t size) { - int i; auto rhs_value = right_column->get_data_at(0); - std::vector> shapes = {nullptr, nullptr}; + std::vector> shapes(2); for (int row = 0; row < size; ++row) { auto lhs_value = left_column->get_data_at(row); - loop_do(lhs_value, rhs_value, shapes, i, res, null_map, row); + loop_do(lhs_value, rhs_value, shapes, res, null_map, row); } } static void vector_vector(const ColumnPtr& left_column, const ColumnPtr& right_column, ColumnUInt8::MutablePtr& res, NullMap& null_map, const size_t size) { - int i; - std::vector> shapes = {nullptr, nullptr}; + std::vector> shapes(2); for (int row = 0; row < size; ++row) { auto lhs_value = left_column->get_data_at(row); auto rhs_value = right_column->get_data_at(row); - loop_do(lhs_value, rhs_value, shapes, i, res, null_map, row); + loop_do(lhs_value, rhs_value, shapes, res, null_map, row); } } @@ -698,7 +694,27 @@ struct StContains { static Status close(FunctionContext* context, FunctionContext::FunctionStateScope scope) { return Status::OK(); } -}; // namespace doris::vectorized +}; + +struct StContainsFunc { + static constexpr auto NAME = "st_contains"; + static bool evaluate(GeoShape* shape1, GeoShape* shape2) { return shape1->contains(shape2); } +}; + +struct StIntersectsFunc { + static constexpr auto NAME = "st_intersects"; + static bool evaluate(GeoShape* shape1, GeoShape* shape2) { return shape1->intersects(shape2); } +}; + +struct StDisjointFunc { + static constexpr auto NAME = "st_disjoint"; + static bool evaluate(GeoShape* shape1, GeoShape* shape2) { return shape1->disjoint(shape2); } +}; + +struct StTouchesFunc { + static constexpr auto NAME = "st_touches"; + static bool evaluate(GeoShape* shape1, GeoShape* shape2) { return shape1->touches(shape2); } +}; struct StGeometryFromText { static constexpr auto NAME = "st_geometryfromtext"; @@ -893,7 +909,10 @@ void register_function_geo(SimpleFunctionFactory& factory) { factory.register_function>(); factory.register_function>(); factory.register_function>(); - factory.register_function>(); + factory.register_function>>(); + factory.register_function>>(); + factory.register_function>>(); + factory.register_function>>(); factory.register_function>(); factory.register_function>>(); factory.register_function>>(); diff --git a/be/test/geo/geo_types_test.cpp b/be/test/geo/geo_types_test.cpp index bf3ada1efb795f..5f8dc105651d24 100644 --- a/be/test/geo/geo_types_test.cpp +++ b/be/test/geo/geo_types_test.cpp @@ -95,6 +95,922 @@ TEST_F(GeoTypesTest, linestring) { } } +TEST_F(GeoTypesTest, point_intersects) { + GeoParseStatus status; + + const char* wkt_linestring = "LINESTRING(-20 0, 20 0)"; + const char* wkt_polygon = "POLYGON((0 0,10 0,10 10,0 10,0 0))"; + + std::unique_ptr line( + GeoShape::from_wkt(wkt_linestring, strlen(wkt_linestring), &status)); + std::unique_ptr polygon( + GeoShape::from_wkt(wkt_polygon, strlen(wkt_polygon), &status)); + ASSERT_NE(nullptr, line.get()); + ASSERT_NE(nullptr, polygon.get()); + + { + // point on the line (center) + GeoPoint point; + point.from_coord(5, 0); + EXPECT_TRUE(point.intersects(line.get())); + } + { + // point at the end of the line + GeoPoint point; + point.from_coord(-20, 0); + EXPECT_TRUE(point.intersects(line.get())); + } + { + // point outside the line + GeoPoint point; + point.from_coord(0, 5); + EXPECT_FALSE(point.intersects(line.get())); + } + + { + // point inside polygons + GeoPoint point; + point.from_coord(5, 5); + EXPECT_TRUE(point.intersects(polygon.get())); + } + { + // point on polygon boundary edges (not vertices) + GeoPoint point; + point.from_coord(5, 0); + EXPECT_TRUE(point.intersects(polygon.get())); + } + { + // point at polygon vertices + GeoPoint point; + point.from_coord(0, 0); + EXPECT_TRUE(point.intersects(polygon.get())); + } + { + // point outside the polygon + GeoPoint point; + point.from_coord(20, 20); + EXPECT_FALSE(point.intersects(polygon.get())); + } + + std::string buf; + polygon->encode_to(&buf); + { + std::unique_ptr shape(GeoShape::from_encoded(buf.data(), buf.size())); + ASSERT_NE(nullptr, shape.get()); + EXPECT_EQ(GEO_SHAPE_POLYGON, shape->type()); + } + { + buf.resize(buf.size() - 1); + std::unique_ptr shape(GeoShape::from_encoded(buf.data(), buf.size())); + EXPECT_EQ(nullptr, shape.get()); + } +} + +TEST_F(GeoTypesTest, linestring_intersects) { + GeoParseStatus status; + + const char* base_line = "LINESTRING(-10 0, 10 0)"; + const char* vertical_line = "LINESTRING(0 -10, 0 10)"; + const char* polygon = "POLYGON((-5 -5,5 -5,5 5,-5 5,-5 -5))"; + + std::unique_ptr base_line_shape( + GeoShape::from_wkt(base_line, strlen(base_line), &status)); + std::unique_ptr vertical_line_shape( + GeoShape::from_wkt(vertical_line, strlen(vertical_line), &status)); + std::unique_ptr polygon_shape(GeoShape::from_wkt(polygon, strlen(polygon), &status)); + ASSERT_NE(nullptr, base_line_shape.get()); + ASSERT_NE(nullptr, vertical_line_shape.get()); + ASSERT_NE(nullptr, polygon_shape.get()); + + // ====================== + // LineString vs Point + // ====================== + { + // point in the middle of the segment + GeoPoint point; + point.from_coord(5, 0); + EXPECT_TRUE(base_line_shape->intersects(&point)); + } + { + // point at the endpoints of the segment + GeoPoint point; + point.from_coord(-10, 0); + EXPECT_TRUE(base_line_shape->intersects(&point)); + } + { + // the point is outside the segment + GeoPoint point; + point.from_coord(0, 5); + EXPECT_FALSE(base_line_shape->intersects(&point)); + } + + // ====================== + // LineString vs LineString + // ====================== + { + // crosswalks + const char* wkt_string = "LINESTRING(-5 5,5 -5)"; + std::unique_ptr cross_line( + GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); + EXPECT_TRUE(base_line_shape->intersects(cross_line.get())); + } + { + // partially overlapping lines + const char* wkt_string = "LINESTRING(-5 0,5 0)"; + std::unique_ptr overlap_line( + GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); + EXPECT_TRUE(base_line_shape->intersects(overlap_line.get())); + } + { + // end contact line + const char* wkt_string = "LINESTRING(10 0,10 10)"; + std::unique_ptr touch_line( + GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); + EXPECT_TRUE(base_line_shape->intersects(touch_line.get())); + } + { + // end contact line + const char* wkt_string = "LINESTRING(9 0,12 0)"; + std::unique_ptr touch_line( + GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); + EXPECT_TRUE(base_line_shape->intersects(touch_line.get())); + } + { + // fully separated lines + const char* wkt_string = "LINESTRING(0 5,10 5)"; + std::unique_ptr separate_line( + GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); + EXPECT_FALSE(base_line_shape->intersects(separate_line.get())); + } + + // ====================== + // LineString vs Polygon + // ====================== + { + // fully internal + const char* wkt_string = "LINESTRING(-2 0,2 0)"; + std::unique_ptr inner_line( + GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); + EXPECT_TRUE(polygon_shape->intersects(inner_line.get())); + } + { + // crossing the border + const char* wkt_string = "LINESTRING(-10 0,10 0)"; + std::unique_ptr cross_line( + GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); + EXPECT_TRUE(polygon_shape->intersects(cross_line.get())); + } + { + // along the borderline + const char* wkt_string = "LINESTRING(-5 -5,5 -5)"; + std::unique_ptr edge_line( + GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); + EXPECT_TRUE(polygon_shape->intersects(edge_line.get())); + } + { + // only one point + const char* wkt_string = "LINESTRING(-5 -5,-5 -10)"; + std::unique_ptr edge_line( + GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); + EXPECT_TRUE(polygon_shape->intersects(edge_line.get())); + } + { + // fully external + const char* wkt_string = "LINESTRING(10 10,20 20)"; + std::unique_ptr outer_line( + GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); + EXPECT_FALSE(polygon_shape->intersects(outer_line.get())); + } + + std::string buf; + base_line_shape->encode_to(&buf); + { + std::unique_ptr decoded(GeoShape::from_encoded(buf.data(), buf.size())); + ASSERT_NE(nullptr, decoded.get()); + EXPECT_EQ(GEO_SHAPE_LINE_STRING, decoded->type()); + } + { + buf.resize(buf.size() - 2); + std::unique_ptr decoded(GeoShape::from_encoded(buf.data(), buf.size())); + EXPECT_EQ(nullptr, decoded.get()); + } +} + +TEST_F(GeoTypesTest, polygon_intersects) { + GeoParseStatus status; + + const char* base_polygon = "POLYGON((0 0,10 0,10 10,0 10,0 0))"; + const char* test_line = "LINESTRING(-5 5,15 5)"; + const char* overlap_polygon = "POLYGON((5 5,15 5,15 15,5 15,5 5))"; + const char* base_polygon2 = "POLYGON((-5 -5,5 -5,5 5,-5 5,-5 -5))"; + + std::unique_ptr polygon( + GeoShape::from_wkt(base_polygon, strlen(base_polygon), &status)); + std::unique_ptr polygon2( + GeoShape::from_wkt(base_polygon2, strlen(base_polygon2), &status)); + std::unique_ptr line(GeoShape::from_wkt(test_line, strlen(test_line), &status)); + std::unique_ptr other_polygon( + GeoShape::from_wkt(overlap_polygon, strlen(overlap_polygon), &status)); + ASSERT_NE(nullptr, polygon.get()); + ASSERT_NE(nullptr, polygon2.get()); + ASSERT_NE(nullptr, line.get()); + ASSERT_NE(nullptr, other_polygon.get()); + + // ====================== + // Polygon vs Point + // ====================== + { + GeoPoint point; + point.from_coord(5, 5); + EXPECT_TRUE(polygon->intersects(&point)); + } + { + GeoPoint point; + point.from_coord(10.1, 10); + EXPECT_FALSE(polygon->intersects(&point)); + } + { + GeoPoint point; + point.from_coord(9.9, 10); + EXPECT_TRUE(polygon->intersects(&point)); + } + { + GeoPoint point; + point.from_coord(0, -4.99); + EXPECT_TRUE(polygon2->intersects(&point)); + } + { + GeoPoint point; + point.from_coord(0, -5.01); + EXPECT_FALSE(polygon2->intersects(&point)); + } + { + GeoPoint point; + point.from_coord(5, 0); + EXPECT_TRUE(polygon->intersects(&point)); + } + { + GeoPoint point; + point.from_coord(0, 0); + EXPECT_TRUE(polygon->intersects(&point)); + } + { + GeoPoint point; + point.from_coord(20, 20); + EXPECT_FALSE(polygon->intersects(&point)); + } + + // ====================== + // Polygon vs LineString + // ====================== + { + const char* wkt = "LINESTRING(2 2,8 8)"; + std::unique_ptr inner_line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(polygon->intersects(inner_line.get())); + } + { + const char* wkt = "LINESTRING(-5 5,15 5)"; + std::unique_ptr cross_line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(polygon->intersects(cross_line.get())); + } + { + const char* wkt = "LINESTRING(0 0,10 0)"; + std::unique_ptr edge_line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(polygon->intersects(edge_line.get())); + } + { + const char* wkt = "LINESTRING(0 0.1,10 0.1)"; + std::unique_ptr edge_line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(polygon->intersects(edge_line.get())); + } + { + const char* wkt = "LINESTRING(0 -0.1,10 -0.1)"; + std::unique_ptr edge_line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(polygon->intersects(edge_line.get())); + } + { + const char* wkt = "LINESTRING(0.1 0,0.1 10)"; + std::unique_ptr edge_line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(polygon->intersects(edge_line.get())); + } + { + const char* wkt = "LINESTRING(-0.1 0,-0.1 10)"; + std::unique_ptr edge_line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(polygon->intersects(edge_line.get())); + } + { + const char* wkt = "LINESTRING(0 10.1,10 10.1)"; + std::unique_ptr edge_line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(polygon->intersects(edge_line.get())); + } + { + const char* wkt = "LINESTRING(0 9.99,10 9.99)"; + std::unique_ptr edge_line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(polygon->intersects(edge_line.get())); + } + { + const char* wkt = "LINESTRING(20 20,30 30)"; + std::unique_ptr outer_line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(polygon->intersects(outer_line.get())); + } + + { + // along the borderline + const char* wkt_string = "LINESTRING(-20 -5.01, 20 -5.01)"; + std::unique_ptr edge_line( + GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); + EXPECT_FALSE(polygon2->intersects(edge_line.get())); + } + { + // along the borderline + const char* wkt_string = "LINESTRING(-20 -4.9, 20 -4.9)"; + std::unique_ptr edge_line( + GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); + EXPECT_TRUE(polygon2->intersects(edge_line.get())); + } + { + // along the borderline + const char* wkt_string = "LINESTRING(-20 -5,20 -5)"; + std::unique_ptr edge_line( + GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); + EXPECT_TRUE(polygon2->intersects(edge_line.get())); + } + + // ====================== + // Polygon vs Polygon + // ====================== + { + const char* wkt = "POLYGON((2 2,8 2,8 8,2 8,2 2))"; + std::unique_ptr small_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(polygon->intersects(small_polygon.get())); + } + { + const char* wkt = "POLYGON((5 5,15 5,15 15,5 15,5 5))"; + std::unique_ptr overlap_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(polygon->intersects(overlap_polygon.get())); + } + { + const char* wkt = "POLYGON((10 0,20 0,20 10,10 10,10 0))"; + std::unique_ptr touch_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(polygon->intersects(touch_polygon.get())); + } + { + const char* wkt = "POLYGON((20 20,30 20,30 30,20 30,20 20))"; + std::unique_ptr separate_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(polygon->intersects(separate_polygon.get())); + } + + std::string buf; + polygon->encode_to(&buf); + { + std::unique_ptr decoded(GeoShape::from_encoded(buf.data(), buf.size())); + ASSERT_NE(nullptr, decoded.get()); + EXPECT_EQ(GEO_SHAPE_POLYGON, decoded->type()); + } + { + buf.resize(buf.size() - 2); + std::unique_ptr decoded(GeoShape::from_encoded(buf.data(), buf.size())); + EXPECT_EQ(nullptr, decoded.get()); + } +} + +TEST_F(GeoTypesTest, circle_intersect) { + GeoParseStatus status; + + GeoCircle circle; + auto res = circle.init(0, 0, 10); + EXPECT_EQ(GEO_PARSE_OK, res); + + // ====================== + // Circle vs Point + // ====================== + { + GeoPoint point; + point.from_coord(0, 10); + EXPECT_TRUE(circle.intersects(&point)); + } + { + GeoPoint point; + point.from_coord(0, 10.1); + EXPECT_FALSE(circle.intersects(&point)); + } + { + GeoPoint point; + point.from_coord(0, 9.9); + EXPECT_TRUE(circle.intersects(&point)); + } + { + GeoPoint point; + point.from_coord(15, 15); + EXPECT_FALSE(circle.intersects(&point)); + } + + // ====================== + // Circle vs LineString + // ====================== + { + const char* wkt = "LINESTRING(-20 0, 20 0)"; + std::unique_ptr line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(circle.intersects(line.get())); + } + { + const char* wkt = "LINESTRING(20 20, 30 30)"; + std::unique_ptr line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(circle.intersects(line.get())); + } + + // ====================== + // Circle vs Polygon + // ====================== + { + const char* wkt = "POLYGON((-5 -5,5 -5,5 5,-5 5,-5 -5))"; + std::unique_ptr poly(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(circle.intersects(poly.get())); + } + { + const char* wkt = "POLYGON((20 20,30 20,30 30,20 30,20 20))"; + std::unique_ptr poly(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(circle.intersects(poly.get())); + } + + // ====================== + // Circle vs Circle + // ====================== + { + GeoCircle other; + other.init(7, 7, 5); + EXPECT_TRUE(circle.intersects(&other)); + } + { + GeoCircle other; + other.init(20, 20, 5); + EXPECT_FALSE(circle.intersects(&other)); + } +} + +TEST_F(GeoTypesTest, point_touches) { + GeoParseStatus status; + + const char* wkt_linestring = "LINESTRING(-20 0, 20 0)"; + const char* wkt_polygon = "POLYGON((0 0,10 0,10 10,0 10,0 0))"; + + std::unique_ptr line( + GeoShape::from_wkt(wkt_linestring, strlen(wkt_linestring), &status)); + std::unique_ptr polygon( + GeoShape::from_wkt(wkt_polygon, strlen(wkt_polygon), &status)); + ASSERT_NE(nullptr, line.get()); + ASSERT_NE(nullptr, polygon.get()); + + { + // point touches the line at the center + GeoPoint point; + point.from_coord(5, 0); + EXPECT_FALSE(point.touches(line.get())); + } + { + // point touches the end of the line + GeoPoint point; + point.from_coord(-20, 0); + EXPECT_TRUE(point.touches(line.get())); + } + { + // point does not touch the line + GeoPoint point; + point.from_coord(0, 5); + EXPECT_FALSE(point.touches(line.get())); + } + + { + // point inside the polygon (does not touch) + GeoPoint point; + point.from_coord(5, 5); + EXPECT_FALSE(point.touches(polygon.get())); + } + { + // point touches the polygon boundary edge (not vertex) + GeoPoint point; + point.from_coord(5, 0); + EXPECT_TRUE(point.touches(polygon.get())); + } + { + // point touches the polygon vertex + GeoPoint point; + point.from_coord(0, 0); + EXPECT_TRUE(point.touches(polygon.get())); + } + { + // point does not touch the polygon + GeoPoint point; + point.from_coord(20, 20); + EXPECT_FALSE(point.touches(polygon.get())); + } + + std::string buf; + polygon->encode_to(&buf); + { + std::unique_ptr shape(GeoShape::from_encoded(buf.data(), buf.size())); + ASSERT_NE(nullptr, shape.get()); + EXPECT_EQ(GEO_SHAPE_POLYGON, shape->type()); + } + { + buf.resize(buf.size() - 1); + std::unique_ptr shape(GeoShape::from_encoded(buf.data(), buf.size())); + EXPECT_EQ(nullptr, shape.get()); + } +} + +TEST_F(GeoTypesTest, linestring_touches) { + GeoParseStatus status; + + const char* base_line = "LINESTRING(-10 0, 10 0)"; + const char* vertical_line = "LINESTRING(0 -10, 0 10)"; + const char* polygon = "POLYGON((-5 -5,5 -5,5 5,-5 5,-5 -5))"; + + std::unique_ptr base_line_shape( + GeoShape::from_wkt(base_line, strlen(base_line), &status)); + std::unique_ptr vertical_line_shape( + GeoShape::from_wkt(vertical_line, strlen(vertical_line), &status)); + std::unique_ptr polygon_shape(GeoShape::from_wkt(polygon, strlen(polygon), &status)); + ASSERT_NE(nullptr, base_line_shape.get()); + ASSERT_NE(nullptr, vertical_line_shape.get()); + ASSERT_NE(nullptr, polygon_shape.get()); + + // ====================== + // LineString vs Point + // ====================== + { + // point in the middle of the segment + GeoPoint point; + point.from_coord(5, 0); + EXPECT_FALSE(base_line_shape->touches(&point)); + } + { + // point at the endpoints of the segment + GeoPoint point; + point.from_coord(-10, 0); + EXPECT_TRUE(base_line_shape->touches(&point)); + } + { + // the point is outside the segment + GeoPoint point; + point.from_coord(0, 5); + EXPECT_FALSE(base_line_shape->touches(&point)); + } + + // ====================== + // LineString vs LineString + // ====================== + { + // crosswalks + const char* wkt_string = "LINESTRING(-5 5, 5 -5)"; + std::unique_ptr cross_line( + GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); + EXPECT_FALSE(base_line_shape->touches(cross_line.get())); + } + { + // partially overlapping lines + const char* wkt_string = "LINESTRING(-5 0, 5 0)"; + std::unique_ptr overlap_line( + GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); + EXPECT_FALSE(base_line_shape->touches(overlap_line.get())); + } + { + // end contact line + const char* wkt_string = "LINESTRING(10 0, 12 0)"; + std::unique_ptr touch_line( + GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); + EXPECT_TRUE(base_line_shape->touches(touch_line.get())); + } + { + // end intersect line + const char* wkt_string = "LINESTRING(9 0, 10 0)"; + std::unique_ptr touch_line( + GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); + EXPECT_FALSE(base_line_shape->touches(touch_line.get())); + } + { + // end intersect line + const char* wkt_string = "LINESTRING(-10 0, 10 0)"; + std::unique_ptr touch_line( + GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); + EXPECT_FALSE(base_line_shape->touches(touch_line.get())); + } + { + // fully separated lines + const char* wkt_string = "LINESTRING(0 5, 10 5)"; + std::unique_ptr separate_line( + GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); + EXPECT_FALSE(base_line_shape->touches(separate_line.get())); + } + + // ====================== + // LineString vs Polygon + // ====================== + { + // fully internal + const char* wkt_string = "LINESTRING(-2 0,2 0)"; + std::unique_ptr inner_line( + GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); + EXPECT_FALSE(polygon_shape->touches(inner_line.get())); + } + { + // crossing the border + const char* wkt_string = "LINESTRING(-10 0, 10 0)"; + std::unique_ptr cross_line( + GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); + EXPECT_FALSE(polygon_shape->touches(cross_line.get())); + } + { + // along the borderline + const char* wkt_string = "LINESTRING(-5 -5, 5 -5)"; + std::unique_ptr edge_line( + GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); + EXPECT_TRUE(polygon_shape->touches(edge_line.get())); + } + { + // along the borderline + const char* wkt_string = "LINESTRING(-20 -5.01, 20 -5.01)"; + std::unique_ptr edge_line( + GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); + EXPECT_FALSE(polygon_shape->touches(edge_line.get())); + } + { + // along the borderline + const char* wkt_string = "LINESTRING(-20 -4.99, 20 -4.99)"; + std::unique_ptr edge_line( + GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); + EXPECT_FALSE(polygon_shape->touches(edge_line.get())); + } + { + // along the borderline + const char* wkt_string = "LINESTRING(-20 -5,20 -5)"; + std::unique_ptr edge_line( + GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); + EXPECT_TRUE(polygon_shape->touches(edge_line.get())); + } + { + // fully external + const char* wkt_string = "LINESTRING(10 10,20 20)"; + std::unique_ptr outer_line( + GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); + EXPECT_FALSE(polygon_shape->touches(outer_line.get())); + } + + std::string buf; + base_line_shape->encode_to(&buf); + { + std::unique_ptr decoded(GeoShape::from_encoded(buf.data(), buf.size())); + ASSERT_NE(nullptr, decoded.get()); + EXPECT_EQ(GEO_SHAPE_LINE_STRING, decoded->type()); + } + { + buf.resize(buf.size() - 2); + std::unique_ptr decoded(GeoShape::from_encoded(buf.data(), buf.size())); + EXPECT_EQ(nullptr, decoded.get()); + } +} + +TEST_F(GeoTypesTest, polygon_touches) { + GeoParseStatus status; + + const char* base_polygon = "POLYGON((0 0,10 0,10 10,0 10,0 0))"; + const char* test_line = "LINESTRING(-5 5,15 5)"; + const char* overlap_polygon = "POLYGON((5 5,15 5,15 15,5 15,5 5))"; + + std::unique_ptr polygon( + GeoShape::from_wkt(base_polygon, strlen(base_polygon), &status)); + std::unique_ptr line(GeoShape::from_wkt(test_line, strlen(test_line), &status)); + std::unique_ptr other_polygon( + GeoShape::from_wkt(overlap_polygon, strlen(overlap_polygon), &status)); + ASSERT_NE(nullptr, polygon.get()); + ASSERT_NE(nullptr, line.get()); + ASSERT_NE(nullptr, other_polygon.get()); + + // ====================== + // Polygon vs Point + // ====================== + { + GeoPoint point; + point.from_coord(5, 5); + EXPECT_FALSE(polygon->touches(&point)); + } + { + GeoPoint point; + point.from_coord(5, 0); + EXPECT_TRUE(polygon->touches(&point)); + } + { + GeoPoint point; + point.from_coord(0, 0); + EXPECT_TRUE(polygon->touches(&point)); + } + { + GeoPoint point; + point.from_coord(20, 20); + EXPECT_FALSE(polygon->touches(&point)); + } + + // ====================== + // Polygon vs LineString + // ====================== + { + const char* wkt = "LINESTRING(2 2,8 8)"; + std::unique_ptr inner_line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(polygon->touches(inner_line.get())); + } + { + const char* wkt = "LINESTRING(-5 5,15 5)"; + std::unique_ptr cross_line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(polygon->touches(cross_line.get())); + } + { + const char* wkt = "LINESTRING(10 5, 15 5)"; + std::unique_ptr cross_line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(polygon->touches(cross_line.get())); + } + { + const char* wkt = "LINESTRING(5 5, 15 15)"; + std::unique_ptr cross_line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(polygon->touches(cross_line.get())); + } + { + const char* wkt = "LINESTRING(10 10, 15 15)"; + std::unique_ptr cross_line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(polygon->touches(cross_line.get())); + } + { + const char* wkt = "LINESTRING(0 0, 5 0)"; + std::unique_ptr edge_line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(polygon->touches(edge_line.get())); + } + { + const char* wkt = "LINESTRING(2 0, 5 0)"; + std::unique_ptr edge_line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(polygon->touches(edge_line.get())); + } + { + const char* wkt = "LINESTRING(0 0,10 0)"; + std::unique_ptr edge_line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(polygon->touches(edge_line.get())); + } + { + const char* wkt = "LINESTRING(20 20,30 30)"; + std::unique_ptr outer_line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(polygon->touches(outer_line.get())); + } + + // ====================== + // Polygon vs Polygon + // ====================== + { + const char* wkt = "POLYGON((2 2,8 2,8 8,2 8,2 2))"; + std::unique_ptr small_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(polygon->touches(small_polygon.get())); + } + { + const char* wkt = "POLYGON((5 5,15 5,15 15,5 15,5 5))"; + std::unique_ptr overlap_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(polygon->touches(overlap_polygon.get())); + } + { + const char* wkt = "POLYGON((10 0,20 0,20 10,10 10,10 0))"; + std::unique_ptr touch_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(polygon->touches(touch_polygon.get())); + } + { + const char* wkt = "POLYGON((10.1 0,20 0,20 10,10.1 10,10.1 0))"; + std::unique_ptr touch_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(polygon->touches(touch_polygon.get())); + } + { + const char* wkt = "POLYGON((9.99 0,20 0,20 10,9.99 10,9.99 0))"; + std::unique_ptr touch_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(polygon->touches(touch_polygon.get())); + } + { + const char* wkt = "POLYGON((20 20,30 20,30 30,20 30,20 20))"; + std::unique_ptr separate_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(polygon->touches(separate_polygon.get())); + } + + std::string buf; + polygon->encode_to(&buf); + { + std::unique_ptr decoded(GeoShape::from_encoded(buf.data(), buf.size())); + ASSERT_NE(nullptr, decoded.get()); + EXPECT_EQ(GEO_SHAPE_POLYGON, decoded->type()); + } + { + buf.resize(buf.size() - 2); + std::unique_ptr decoded(GeoShape::from_encoded(buf.data(), buf.size())); + EXPECT_EQ(nullptr, decoded.get()); + } +} + +TEST_F(GeoTypesTest, circle_touches) { + GeoParseStatus status; + + GeoCircle circle; + auto res = circle.init(0, 0, 10); + EXPECT_EQ(GEO_PARSE_OK, res); + + // ====================== + // Circle vs Point + // ====================== + { + GeoPoint point; + point.from_coord(0, 10); + EXPECT_TRUE(circle.touches(&point)); + } + { + GeoPoint point; + point.from_coord(15, 15); + EXPECT_FALSE(circle.touches(&point)); + } + { + GeoPoint point; + point.from_coord(0, 10); + EXPECT_TRUE(circle.touches(&point)); + } + { + GeoCircle circle2; + auto res = circle2.init(1, 1, 1); + EXPECT_EQ(GEO_PARSE_OK, res); + GeoPoint point; + point.from_coord(2, 1); + EXPECT_TRUE(circle2.touches(&point)); + } + { + GeoPoint point; + point.from_coord(0, 10.1); + EXPECT_FALSE(circle.touches(&point)); + } + + // ====================== + // Circle vs LineString + // ====================== + { + const char* wkt = "LINESTRING(-20 0, 20 0)"; + std::unique_ptr line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(circle.touches(line.get())); + } + { + const char* wkt = "LINESTRING(20 20, 30 30)"; + std::unique_ptr line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(circle.touches(line.get())); + } + + // ====================== + // Circle vs Polygon + // ====================== + { + const char* wkt = "POLYGON((-5 -5,5 -5,5 5,-5 5,-5 -5))"; + std::unique_ptr poly(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(circle.touches(poly.get())); + } + { + const char* wkt = "POLYGON((10 0,20 0,20 10,10 10,10 0))"; + std::unique_ptr poly(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(circle.touches(poly.get())); + } + { + const char* wkt = "POLYGON((10.1 0,20 0,20 10,10.1 10,10.1 0))"; + std::unique_ptr poly(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(circle.touches(poly.get())); + } + { + const char* wkt = "POLYGON((9.99 0,20 0,20 10,9.99 10,9.99 0))"; + std::unique_ptr poly(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(circle.touches(poly.get())); + } + { + const char* wkt = "POLYGON((-10 -10,10 -10,10 -20,-10 -20,-10 -10))"; + std::unique_ptr poly(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(circle.touches(poly.get())); + } + + // ====================== + // Circle vs Circle + // ====================== + { + GeoCircle circle1; + circle1.init(1, 1, 1); + GeoCircle circle2; + circle2.init(3, 1, 1); + EXPECT_TRUE(circle1.touches(&circle2)); + } + { + GeoCircle other; + other.init(7, 7, 5); + EXPECT_FALSE(circle.touches(&other)); + } + { + GeoCircle other; + other.init(20, 0, 10); + EXPECT_TRUE(circle.touches(&other)); + } +} + TEST_F(GeoTypesTest, polygon_contains) { const char* wkt = "POLYGON ((10 10, 50 10, 50 10, 50 50, 50 50, 10 50, 10 10))"; GeoParseStatus status; 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 228170a2fb3c45..74de2465f593c0 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 @@ -386,17 +386,20 @@ 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.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.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.StLinefromtext; import org.apache.doris.nereids.trees.expressions.functions.scalar.StLinestringfromtext; import org.apache.doris.nereids.trees.expressions.functions.scalar.StPoint; import org.apache.doris.nereids.trees.expressions.functions.scalar.StPolyfromtext; import org.apache.doris.nereids.trees.expressions.functions.scalar.StPolygon; import org.apache.doris.nereids.trees.expressions.functions.scalar.StPolygonfromtext; +import org.apache.doris.nereids.trees.expressions.functions.scalar.StTouches; import org.apache.doris.nereids.trees.expressions.functions.scalar.StX; import org.apache.doris.nereids.trees.expressions.functions.scalar.StY; import org.apache.doris.nereids.trees.expressions.functions.scalar.StartsWith; @@ -859,6 +862,9 @@ public class BuiltinScalarFunctions implements FunctionHelper { scalar(StAswkt.class, "st_aswkt"), scalar(StCircle.class, "st_circle"), scalar(StContains.class, "st_contains"), + scalar(StIntersects.class, "st_intersects"), + scalar(StDisjoint.class, "st_disjoint"), + scalar(StTouches.class, "st_touches"), 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/StDisjoint.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StDisjoint.java new file mode 100644 index 00000000000000..e354066e5640ab --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StDisjoint.java @@ -0,0 +1,72 @@ +// 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.BooleanType; +import org.apache.doris.nereids.types.StringType; +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_disjoint'. This class is generated by GenerateFunction. + */ +public class StDisjoint extends ScalarFunction + implements BinaryExpression, ExplicitlyCastableSignature, AlwaysNullable, PropagateNullLiteral { + + public static final List SIGNATURES = ImmutableList.of( + FunctionSignature.ret(BooleanType.INSTANCE).args(VarcharType.SYSTEM_DEFAULT, VarcharType.SYSTEM_DEFAULT), + FunctionSignature.ret(BooleanType.INSTANCE).args(StringType.INSTANCE, StringType.INSTANCE) + ); + + /** + * constructor with 2 arguments. + */ + public StDisjoint(Expression arg0, Expression arg1) { + super("st_disjoint", arg0, arg1); + } + + /** + * withChildren. + */ + @Override + public StDisjoint withChildren(List children) { + Preconditions.checkArgument(children.size() == 2); + return new StDisjoint(children.get(0), children.get(1)); + } + + @Override + public List getSignatures() { + return SIGNATURES; + } + + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitStDisjoint(this, context); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StIntersects.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StIntersects.java new file mode 100644 index 00000000000000..3f041df04688a9 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StIntersects.java @@ -0,0 +1,72 @@ +// 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.BooleanType; +import org.apache.doris.nereids.types.StringType; +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_intersects'. This class is generated by GenerateFunction. + */ +public class StIntersects extends ScalarFunction + implements BinaryExpression, ExplicitlyCastableSignature, AlwaysNullable, PropagateNullLiteral { + + public static final List SIGNATURES = ImmutableList.of( + FunctionSignature.ret(BooleanType.INSTANCE).args(VarcharType.SYSTEM_DEFAULT, VarcharType.SYSTEM_DEFAULT), + FunctionSignature.ret(BooleanType.INSTANCE).args(StringType.INSTANCE, StringType.INSTANCE) + ); + + /** + * constructor with 2 arguments. + */ + public StIntersects(Expression arg0, Expression arg1) { + super("st_intersects", arg0, arg1); + } + + /** + * withChildren. + */ + @Override + public StIntersects withChildren(List children) { + Preconditions.checkArgument(children.size() == 2); + return new StIntersects(children.get(0), children.get(1)); + } + + @Override + public List getSignatures() { + return SIGNATURES; + } + + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitStIntersects(this, context); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StTouches.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StTouches.java new file mode 100644 index 00000000000000..11b114171233fd --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StTouches.java @@ -0,0 +1,72 @@ +// 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.BooleanType; +import org.apache.doris.nereids.types.StringType; +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_touches'. This class is generated by GenerateFunction. + */ +public class StTouches extends ScalarFunction + implements BinaryExpression, ExplicitlyCastableSignature, AlwaysNullable, PropagateNullLiteral { + + public static final List SIGNATURES = ImmutableList.of( + FunctionSignature.ret(BooleanType.INSTANCE).args(VarcharType.SYSTEM_DEFAULT, VarcharType.SYSTEM_DEFAULT), + FunctionSignature.ret(BooleanType.INSTANCE).args(StringType.INSTANCE, StringType.INSTANCE) + ); + + /** + * constructor with 2 arguments. + */ + public StTouches(Expression arg0, Expression arg1) { + super("st_touches", arg0, arg1); + } + + /** + * withChildren. + */ + @Override + public StTouches withChildren(List children) { + Preconditions.checkArgument(children.size() == 2); + return new StTouches(children.get(0), children.get(1)); + } + + @Override + public List getSignatures() { + return SIGNATURES; + } + + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitStTouches(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 cba019f5b6afad..494a9b7c9a3b47 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 @@ -384,17 +384,20 @@ 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.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.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.StLinefromtext; import org.apache.doris.nereids.trees.expressions.functions.scalar.StLinestringfromtext; import org.apache.doris.nereids.trees.expressions.functions.scalar.StPoint; import org.apache.doris.nereids.trees.expressions.functions.scalar.StPolyfromtext; import org.apache.doris.nereids.trees.expressions.functions.scalar.StPolygon; import org.apache.doris.nereids.trees.expressions.functions.scalar.StPolygonfromtext; +import org.apache.doris.nereids.trees.expressions.functions.scalar.StTouches; import org.apache.doris.nereids.trees.expressions.functions.scalar.StX; import org.apache.doris.nereids.trees.expressions.functions.scalar.StY; import org.apache.doris.nereids.trees.expressions.functions.scalar.StartsWith; @@ -1877,6 +1880,18 @@ default R visitStContains(StContains stContains, C context) { return visitScalarFunction(stContains, context); } + default R visitStIntersects(StIntersects stIntersects, C context) { + return visitScalarFunction(stIntersects, context); + } + + default R visitStDisjoint(StDisjoint stDisjoint, C context) { + return visitScalarFunction(stDisjoint, context); + } + + default R visitStTouches(StTouches stTouches, C context) { + return visitScalarFunction(stTouches, 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 db1b1ffcae52d3..bfcd4c28f730b2 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 @@ -14,6 +14,315 @@ true -- !sql -- false +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +true + -- !sql -- 7336.913554999592 diff --git a/regression-test/data/query_p0/sql_functions/spatial_functions/test_gis_function.out b/regression-test/data/query_p0/sql_functions/spatial_functions/test_gis_function.out index 59bc628249f030..8f86e42ddafff7 100644 --- a/regression-test/data/query_p0/sql_functions/spatial_functions/test_gis_function.out +++ b/regression-test/data/query_p0/sql_functions/spatial_functions/test_gis_function.out @@ -14,6 +14,192 @@ true -- !sql -- false +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +false + -- !sql -- 7336.913554999592 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 d50ac65719af03..b68254bd6510df 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 @@ -27,6 +27,139 @@ suite("test_gis_function") { qt_sql "SELECT ST_Contains(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Point(5, 5));" qt_sql "SELECT ST_Contains(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Point(50, 50));" + qt_sql "SELECT ST_Intersects(ST_Point(0, 0), ST_Point(0, 0));" + qt_sql "SELECT ST_Intersects(ST_Point(0, 0), ST_Point(5, 5));" + + qt_sql "SELECT ST_Intersects(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_Point(0, 0));" + qt_sql "SELECT ST_Intersects(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_Point(1.99, 0));" + qt_sql "SELECT ST_Intersects(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_Point(2.01, 0));" + qt_sql "SELECT ST_Intersects(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_Point(2, 0));" + qt_sql "SELECT ST_Intersects(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_Point(0, 1));" + + qt_sql "SELECT ST_Intersects(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_LineFromText(\"LINESTRING (2 0, 3 0)\"));" + qt_sql "SELECT ST_Intersects(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_LineFromText(\"LINESTRING (2.1 0, 3 0)\"));" + qt_sql "SELECT ST_Intersects(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_LineFromText(\"LINESTRING (1.99 0, 3 0)\"));" + qt_sql "SELECT ST_Intersects(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_LineFromText(\"LINESTRING (-2 0.01, 3 0.01)\"));" + qt_sql "SELECT ST_Intersects(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_LineFromText(\"LINESTRING (3 0, 4 0)\"));" + qt_sql "SELECT ST_Intersects(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_LineFromText(\"LINESTRING (1 0, 4 0)\"));" + + qt_sql "SELECT ST_Intersects(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Point(0, 0));" + qt_sql "SELECT ST_Intersects(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Point(5, 0));" + qt_sql "SELECT ST_Intersects(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Point(5, 5));" + + qt_sql "SELECT ST_Intersects(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_LineFromText(\"LINESTRING (20 0, 0 20)\"));" + qt_sql "SELECT ST_Intersects(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_LineFromText(\"LINESTRING (-20 0, 20 0)\"));" + qt_sql "SELECT ST_Intersects(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_LineFromText(\"LINESTRING (3 5, 8 5)\"));" + qt_sql "SELECT ST_Intersects(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_LineFromText(\"LINESTRING (0 0.01, 10 0.01)\"));" + qt_sql "SELECT ST_Intersects(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_LineFromText(\"LINESTRING (0 -0.01, 10 -0.01)\"));" + + qt_sql "SELECT ST_Intersects(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Polygon(\"POLYGON ((5 0, 15 0, 15 10, 5 10, 5 0))\"));" + qt_sql "SELECT ST_Intersects(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Polygon(\"POLYGON ((10 0, 10 10, 20 10, 20 0, 10 0))\"));" + qt_sql "SELECT ST_Intersects(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Polygon(\"POLYGON ((11 0, 11 10, 21 10, 21 0, 11 0))\"));" + qt_sql "SELECT ST_Intersects(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Polygon(\"POLYGON ((10 10, 20 10, 20 20, 10 20, 10 10))\"));" + + qt_sql "SELECT ST_Intersects(ST_Circle(1, 1, 1), ST_Point(2, 1));" + qt_sql "SELECT ST_Intersects(ST_Circle(1, 1, 1), ST_Point(1, 1));" + qt_sql "SELECT ST_Intersects(ST_Circle(1, 1, 1), ST_Point(3, 1));" + + qt_sql "SELECT ST_Intersects(ST_Circle(1, 1, 1), ST_LineFromText(\"LINESTRING (2 0, 2 2)\"));" + qt_sql "SELECT ST_Intersects(ST_Circle(1, 1, 1), ST_LineFromText(\"LINESTRING (1.7 0, 1.7 2)\"));" + qt_sql "SELECT ST_Intersects(ST_Circle(1, 1, 1), ST_LineFromText(\"LINESTRING (1 0.5, 1 1.5)\"));" + + qt_sql "SELECT ST_Intersects(ST_Circle(1, 1, 1), ST_Polygon(\"POLYGON ((2 0, 12 0, 12 10, 2 10, 2 0))\"));" + qt_sql "SELECT ST_Intersects(ST_Circle(5, 5, 1), ST_Polygon(\"POLYGON ((2 0, 12 0, 12 10, 2 10, 2 0))\"));" + qt_sql "SELECT ST_Intersects(ST_Circle(2, 1, 1), ST_Polygon(\"POLYGON ((2 0, 12 0, 12 10, 2 10, 2 0))\"));" + qt_sql "SELECT ST_Intersects(ST_Circle(0, 1, 1), ST_Polygon(\"POLYGON ((2 0, 12 0, 12 10, 2 10, 2 0))\"));" + + qt_sql "SELECT ST_Intersects(ST_Circle(1, 1, 1), ST_Circle(3, 1, 1));" + qt_sql "SELECT ST_Intersects(ST_Circle(1, 1, 1), ST_Circle(2, 1, 1));" + qt_sql "SELECT ST_Intersects(ST_Circle(1, 1, 1), ST_Circle(4, 1, 1));" + + qt_sql "SELECT ST_Touches(ST_Point(0, 0), ST_Point(0, 0));" + qt_sql "SELECT ST_Touches(ST_Point(0, 0), ST_Point(5, 5));" + + qt_sql "SELECT ST_Touches(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_Point(0, 0));" + qt_sql "SELECT ST_Touches(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_Point(2, 0));" + qt_sql "SELECT ST_Touches(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_Point(0, 1));" + + qt_sql "SELECT ST_Touches(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_LineFromText(\"LINESTRING (2 0, 3 0)\"));" + qt_sql "SELECT ST_Touches(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_LineFromText(\"LINESTRING (3 0, 4 0)\"));" + qt_sql "SELECT ST_Touches(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_LineFromText(\"LINESTRING (1 0, 4 0)\"));" + + qt_sql "SELECT ST_Touches(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Point(0, 0));" + qt_sql "SELECT ST_Touches(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Point(5, 0));" + qt_sql "SELECT ST_Touches(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Point(5, 5));" + + qt_sql "SELECT ST_Touches(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_LineFromText(\"LINESTRING (20 0, 0 20)\"));" + qt_sql "SELECT ST_Touches(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_LineFromText(\"LINESTRING (-20 0, 20 0)\"));" + qt_sql "SELECT ST_Touches(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_LineFromText(\"LINESTRING (3 5, 8 5)\"));" + qt_sql "SELECT ST_Touches(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_LineFromText(\"LINESTRING (-3 5, 8 5)\"));" + qt_sql "SELECT ST_Touches(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_LineFromText(\"LINESTRING (-3 5, 15 5)\"));" + + qt_sql "SELECT ST_Touches(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Polygon(\"POLYGON ((5 0, 15 0, 15 10, 5 10, 5 0))\"));" + qt_sql "SELECT ST_Touches(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Polygon(\"POLYGON ((10 0, 10 10, 20 10, 20 0, 10 0))\"));" + qt_sql "SELECT ST_Touches(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Polygon(\"POLYGON ((11 0, 11 10, 21 10, 21 0, 11 0))\"));" + qt_sql "SELECT ST_Touches(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Polygon(\"POLYGON ((10 10, 20 10, 20 20, 10 20, 10 10))\"));" + + qt_sql "SELECT ST_Touches(ST_Circle(1, 1, 1), ST_Point(2, 1));" + qt_sql "SELECT ST_Touches(ST_Circle(1, 1, 1), ST_Point(1, 2));" + qt_sql "SELECT ST_Touches(ST_Circle(1, 1, 1), ST_Point(1, 1));" + qt_sql "SELECT ST_Touches(ST_Circle(1, 1, 1), ST_Point(3, 1));" + + qt_sql "SELECT ST_Touches(ST_Circle(1, 1, 1), ST_LineFromText(\"LINESTRING (2 0, 2 2)\"));" + qt_sql "SELECT ST_Touches(ST_Circle(1, 1, 1), ST_LineFromText(\"LINESTRING (1.7 0, 1.7 2)\"));" + qt_sql "SELECT ST_Touches(ST_Circle(1, 1, 1), ST_LineFromText(\"LINESTRING (1 0.5, 1 1.5)\"));" + + qt_sql "SELECT ST_Touches(ST_Circle(1, 1, 1), ST_Polygon(\"POLYGON ((2 0, 12 0, 12 10, 2 10, 2 0))\"));" + qt_sql "SELECT ST_Touches(ST_Circle(5, 5, 1), ST_Polygon(\"POLYGON ((2 0, 12 0, 12 10, 2 10, 2 0))\"));" + qt_sql "SELECT ST_Touches(ST_Circle(2, 1, 1), ST_Polygon(\"POLYGON ((2 0, 12 0, 12 10, 2 10, 2 0))\"));" + qt_sql "SELECT ST_Touches(ST_Circle(0, 1, 1), ST_Polygon(\"POLYGON ((2 0, 12 0, 12 10, 2 10, 2 0))\"));" + + qt_sql "SELECT ST_Touches(ST_Circle(1, 1, 1), ST_Circle(3, 1, 1));" + qt_sql "SELECT ST_Touches(ST_Circle(1, 1, 1), ST_Circle(2, 1, 1));" + qt_sql "SELECT ST_Touches(ST_Circle(1, 1, 1), ST_Circle(4, 1, 1));" + + qt_sql "SELECT ST_Disjoint(ST_Point(0, 0), ST_Point(0, 0));" + qt_sql "SELECT ST_Disjoint(ST_Point(0, 0), ST_Point(5, 5));" + + qt_sql "SELECT ST_Disjoint(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_Point(0, 0));" + qt_sql "SELECT ST_Disjoint(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_Point(2, 0));" + qt_sql "SELECT ST_Disjoint(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_Point(0, 1));" + + qt_sql "SELECT ST_Disjoint(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_LineFromText(\"LINESTRING (2 0, 3 0)\"));" + qt_sql "SELECT ST_Disjoint(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_LineFromText(\"LINESTRING (3 0, 4 0)\"));" + qt_sql "SELECT ST_Disjoint(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_LineFromText(\"LINESTRING (1 0, 4 0)\"));" + + qt_sql "SELECT ST_Disjoint(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Point(0, 0));" + qt_sql "SELECT ST_Disjoint(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Point(5, 0));" + qt_sql "SELECT ST_Disjoint(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Point(5, 5));" + + qt_sql "SELECT ST_Disjoint(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_LineFromText(\"LINESTRING (20 0, 0 20)\"));" + qt_sql "SELECT ST_Disjoint(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_LineFromText(\"LINESTRING (-20 0, 20 0)\"));" + qt_sql "SELECT ST_Disjoint(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_LineFromText(\"LINESTRING (3 5, 8 5)\"));" + + qt_sql "SELECT ST_Disjoint(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Polygon(\"POLYGON ((5 0, 15 0, 15 10, 5 10, 5 0))\"));" + qt_sql "SELECT ST_Disjoint(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Polygon(\"POLYGON ((10 0, 10 10, 20 10, 20 0, 10 0))\"));" + qt_sql "SELECT ST_Disjoint(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Polygon(\"POLYGON ((11 0, 11 10, 21 10, 21 0, 11 0))\"));" + qt_sql "SELECT ST_Disjoint(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Polygon(\"POLYGON ((10 10, 20 10, 20 20, 10 20, 10 10))\"));" + + qt_sql "SELECT ST_Disjoint(ST_Circle(1, 1, 1), ST_Point(2, 1));" + qt_sql "SELECT ST_Disjoint(ST_Circle(1, 1, 1), ST_Point(1, 1));" + qt_sql "SELECT ST_Disjoint(ST_Circle(1, 1, 1), ST_Point(3, 1));" + + qt_sql "SELECT ST_Disjoint(ST_Circle(1, 1, 1), ST_LineFromText(\"LINESTRING (2 0, 2 2)\"));" + qt_sql "SELECT ST_Disjoint(ST_Circle(1, 1, 1), ST_LineFromText(\"LINESTRING (1.7 0, 1.7 2)\"));" + qt_sql "SELECT ST_Disjoint(ST_Circle(1, 1, 1), ST_LineFromText(\"LINESTRING (1 0.5, 1 1.5)\"));" + + qt_sql "SELECT ST_Disjoint(ST_Circle(1, 1, 1), ST_Polygon(\"POLYGON ((2 0, 12 0, 12 10, 2 10, 2 0))\"));" + qt_sql "SELECT ST_Disjoint(ST_Circle(5, 5, 1), ST_Polygon(\"POLYGON ((2 0, 12 0, 12 10, 2 10, 2 0))\"));" + qt_sql "SELECT ST_Disjoint(ST_Circle(2, 1, 1), ST_Polygon(\"POLYGON ((2 0, 12 0, 12 10, 2 10, 2 0))\"));" + qt_sql "SELECT ST_Disjoint(ST_Circle(0, 1, 1), ST_Polygon(\"POLYGON ((2 0, 12 0, 12 10, 2 10, 2 0))\"));" + + qt_sql "SELECT ST_Disjoint(ST_Circle(1, 1, 1), ST_Circle(3, 1, 1));" + qt_sql "SELECT ST_Disjoint(ST_Circle(1, 1, 1), ST_Circle(2, 1, 1));" + qt_sql "SELECT ST_Disjoint(ST_Circle(1, 1, 1), ST_Circle(4, 1, 1));" + qt_sql "SELECT ST_DISTANCE_SPHERE(116.35620117, 39.939093, 116.4274406433, 39.9020987219);" qt_sql "SELECT ST_ANGLE_SPHERE(116.35620117, 39.939093, 116.4274406433, 39.9020987219);" qt_sql "SELECT ST_ANGLE_SPHERE(0, 0, 45, 0);" diff --git a/regression-test/suites/query_p0/sql_functions/spatial_functions/test_gis_function.groovy b/regression-test/suites/query_p0/sql_functions/spatial_functions/test_gis_function.groovy index 81eadfb0cc039d..f42b40db54e751 100644 --- a/regression-test/suites/query_p0/sql_functions/spatial_functions/test_gis_function.groovy +++ b/regression-test/suites/query_p0/sql_functions/spatial_functions/test_gis_function.groovy @@ -25,6 +25,87 @@ suite("test_gis_function", "arrow_flight_sql") { qt_sql "SELECT ST_Contains(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Point(5, 5));" qt_sql "SELECT ST_Contains(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Point(50, 50));" + qt_sql "SELECT ST_Intersects(ST_Point(0, 0), ST_Point(0, 0));" + qt_sql "SELECT ST_Intersects(ST_Point(0, 0), ST_Point(5, 5));" + + qt_sql "SELECT ST_Intersects(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_Point(0, 0));" + qt_sql "SELECT ST_Intersects(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_Point(2, 0));" + qt_sql "SELECT ST_Intersects(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_Point(0, 1));" + + qt_sql "SELECT ST_Intersects(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_LineFromText(\"LINESTRING (2 0, 3 0)\"));" + qt_sql "SELECT ST_Intersects(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_LineFromText(\"LINESTRING (3 0, 4 0)\"));" + qt_sql "SELECT ST_Intersects(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_LineFromText(\"LINESTRING (1 0, 4 0)\"));" + + qt_sql "SELECT ST_Intersects(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Point(0, 0));" + qt_sql "SELECT ST_Intersects(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Point(5, 0));" + qt_sql "SELECT ST_Intersects(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Point(5, 5));" + + qt_sql "SELECT ST_Intersects(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_LineFromText(\"LINESTRING (20 0, 0 20)\"));" + qt_sql "SELECT ST_Intersects(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_LineFromText(\"LINESTRING (-20 0, 20 0)\"));" + qt_sql "SELECT ST_Intersects(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_LineFromText(\"LINESTRING (3 5, 8 5)\"));" + + qt_sql "SELECT ST_Intersects(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Polygon(\"POLYGON ((5 0, 15 0, 15 10, 5 10, 5 0))\"));" + qt_sql "SELECT ST_Intersects(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Polygon(\"POLYGON ((10 0, 10 10, 20 10, 20 0, 10 0))\"));" + qt_sql "SELECT ST_Intersects(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Polygon(\"POLYGON ((11 0, 11 10, 21 10, 21 0, 11 0))\"));" + qt_sql "SELECT ST_Intersects(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Polygon(\"POLYGON ((10 10, 20 10, 20 20, 10 20, 10 10))\"));" + + qt_sql "SELECT ST_Intersects(ST_Circle(1, 1, 1), ST_Point(2, 1));" + qt_sql "SELECT ST_Intersects(ST_Circle(1, 1, 1), ST_Point(1, 1));" + qt_sql "SELECT ST_Intersects(ST_Circle(1, 1, 1), ST_Point(3, 1));" + + qt_sql "SELECT ST_Intersects(ST_Circle(1, 1, 1), ST_LineFromText(\"LINESTRING (2 0, 2 2)\"));" + qt_sql "SELECT ST_Intersects(ST_Circle(1, 1, 1), ST_LineFromText(\"LINESTRING (1.7 0, 1.7 2)\"));" + qt_sql "SELECT ST_Intersects(ST_Circle(1, 1, 1), ST_LineFromText(\"LINESTRING (1 0.5, 1 1.5)\"));" + + qt_sql "SELECT ST_Intersects(ST_Circle(1, 1, 1), ST_Polygon(\"POLYGON ((2 0, 12 0, 12 10, 2 10, 2 0))\"));" + qt_sql "SELECT ST_Intersects(ST_Circle(5, 5, 1), ST_Polygon(\"POLYGON ((2 0, 12 0, 12 10, 2 10, 2 0))\"));" + qt_sql "SELECT ST_Intersects(ST_Circle(2, 1, 1), ST_Polygon(\"POLYGON ((2 0, 12 0, 12 10, 2 10, 2 0))\"));" + qt_sql "SELECT ST_Intersects(ST_Circle(0, 1, 1), ST_Polygon(\"POLYGON ((2 0, 12 0, 12 10, 2 10, 2 0))\"));" + + qt_sql "SELECT ST_Intersects(ST_Circle(1, 1, 1), ST_Circle(3, 1, 1));" + qt_sql "SELECT ST_Intersects(ST_Circle(1, 1, 1), ST_Circle(2, 1, 1));" + qt_sql "SELECT ST_Intersects(ST_Circle(1, 1, 1), ST_Circle(4, 1, 1));" + + qt_sql "SELECT ST_Touches(ST_Point(0, 0), ST_Point(0, 0));" + qt_sql "SELECT ST_Touches(ST_Point(0, 0), ST_Point(5, 5));" + + qt_sql "SELECT ST_Touches(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_Point(0, 0));" + qt_sql "SELECT ST_Touches(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_Point(2, 0));" + qt_sql "SELECT ST_Touches(ST_LineFromText(\"LINESTRING (-2 0, 2 0)\"), ST_Point(0, 1));" + + qt_sql "SELECT ST_Touches(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Point(0, 0));" + qt_sql "SELECT ST_Touches(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Point(5, 0));" + qt_sql "SELECT ST_Touches(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Point(5, 5));" + + qt_sql "SELECT ST_Touches(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_LineFromText(\"LINESTRING (20 0, 0 20)\"));" + qt_sql "SELECT ST_Touches(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_LineFromText(\"LINESTRING (-20 0, 20 0)\"));" + qt_sql "SELECT ST_Touches(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_LineFromText(\"LINESTRING (3 5, 8 5)\"));" + qt_sql "SELECT ST_Touches(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_LineFromText(\"LINESTRING (-3 5, 8 5)\"));" + qt_sql "SELECT ST_Touches(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_LineFromText(\"LINESTRING (-3 5, 15 5)\"));" + + qt_sql "SELECT ST_Touches(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Polygon(\"POLYGON ((5 0, 15 0, 15 10, 5 10, 5 0))\"));" + qt_sql "SELECT ST_Touches(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Polygon(\"POLYGON ((10 0, 10 10, 20 10, 20 0, 10 0))\"));" + qt_sql "SELECT ST_Touches(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Polygon(\"POLYGON ((11 0, 11 10, 21 10, 21 0, 11 0))\"));" + qt_sql "SELECT ST_Touches(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Polygon(\"POLYGON ((10 10, 20 10, 20 20, 10 20, 10 10))\"));" + + qt_sql "SELECT ST_Touches(ST_Circle(1, 1, 1), ST_Point(2, 1));" + qt_sql "SELECT ST_Touches(ST_Circle(1, 1, 1), ST_Point(1, 2));" + qt_sql "SELECT ST_Touches(ST_Circle(1, 1, 1), ST_Point(1, 1));" + qt_sql "SELECT ST_Touches(ST_Circle(1, 1, 1), ST_Point(3, 1));" + + qt_sql "SELECT ST_Touches(ST_Circle(1, 1, 1), ST_LineFromText(\"LINESTRING (2 0, 2 2)\"));" + qt_sql "SELECT ST_Touches(ST_Circle(1, 1, 1), ST_LineFromText(\"LINESTRING (1.7 0, 1.7 2)\"));" + qt_sql "SELECT ST_Touches(ST_Circle(1, 1, 1), ST_LineFromText(\"LINESTRING (1 0.5, 1 1.5)\"));" + + qt_sql "SELECT ST_Touches(ST_Circle(1, 1, 1), ST_Polygon(\"POLYGON ((2 0, 12 0, 12 10, 2 10, 2 0))\"));" + qt_sql "SELECT ST_Touches(ST_Circle(5, 5, 1), ST_Polygon(\"POLYGON ((2 0, 12 0, 12 10, 2 10, 2 0))\"));" + qt_sql "SELECT ST_Touches(ST_Circle(2, 1, 1), ST_Polygon(\"POLYGON ((2 0, 12 0, 12 10, 2 10, 2 0))\"));" + qt_sql "SELECT ST_Touches(ST_Circle(0, 1, 1), ST_Polygon(\"POLYGON ((2 0, 12 0, 12 10, 2 10, 2 0))\"));" + + qt_sql "SELECT ST_Touches(ST_Circle(1, 1, 1), ST_Circle(3, 1, 1));" + qt_sql "SELECT ST_Touches(ST_Circle(1, 1, 1), ST_Circle(2, 1, 1));" + qt_sql "SELECT ST_Touches(ST_Circle(1, 1, 1), ST_Circle(4, 1, 1));" + qt_sql "SELECT ST_DISTANCE_SPHERE(116.35620117, 39.939093, 116.4274406433, 39.9020987219);" qt_sql "SELECT ST_ANGLE_SPHERE(116.35620117, 39.939093, 116.4274406433, 39.9020987219);" From 66b591ba7205f2d599a252e2cd000ceedf9275d3 Mon Sep 17 00:00:00 2001 From: linrrarity <142187136+linrrzqqq@users.noreply.github.com> Date: Tue, 15 Apr 2025 16:21:36 +0800 Subject: [PATCH 3/3] [Enhancement] (GEO) Support MultiPolygon for Geometry functions (#49665) ### What problem does this PR solve? 1. Enhances the `GeometryFromText` function to support parsing `MULTIPOLYGON` WKT 2. Support `ST_CONTAINS ` , `ST_INTERSECTS`, `ST_TOUCHES` for it and other shapes. 3. Fixed the behavior of `ST_Touches` between two polygons, which has been updated to return `true` when they touch at a single point that is not on a horizontal or vertical edge. For example, the following polygons now return true for ST_Touches: - `POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))` - `POLYGON((5 10, 0 15, 10 15))` Obviously, they touches at the point `(5 10)` --- be/src/geo/geo_common.h | 7 +- be/src/geo/geo_types.cpp | 442 ++++++++++++++++-- be/src/geo/geo_types.h | 32 ++ be/src/geo/wkt_parse_type.h | 2 + be/src/geo/wkt_yacc.y | 37 +- be/src/vec/common/assert_cast.h | 1 + be/test/geo/geo_types_test.cpp | 435 +++++++++++++++++ .../spatial_functions/test_gis_function.out | 326 +++++++++++++ .../test_gis_function.groovy | 119 ++++- 9 files changed, 1368 insertions(+), 33 deletions(-) diff --git a/be/src/geo/geo_common.h b/be/src/geo/geo_common.h index b2f32e5494a043..f4c78dff19078b 100644 --- a/be/src/geo/geo_common.h +++ b/be/src/geo/geo_common.h @@ -42,9 +42,10 @@ enum GeoParseStatus { GEO_PARSE_POLYGON_NOT_HOLE = 5, GEO_PARSE_POLYLINE_LACK_VERTICES = 6, GEO_PARSE_POLYLINE_INVALID = 7, - GEO_PARSE_CIRCLE_INVALID = 8, - GEO_PARSE_WKT_SYNTAX_ERROR = 9, - GEO_PARSE_WKB_SYNTAX_ERROR = 10, + GEO_PARSE_MULTIPOLYGON_OVERLAP = 8, + GEO_PARSE_CIRCLE_INVALID = 9, + GEO_PARSE_WKT_SYNTAX_ERROR = 10, + GEO_PARSE_WKB_SYNTAX_ERROR = 11, }; std::string to_string(GeoParseStatus status); diff --git a/be/src/geo/geo_types.cpp b/be/src/geo/geo_types.cpp index 04e5b5ef1acd23..19254be105b09e 100644 --- a/be/src/geo/geo_types.cpp +++ b/be/src/geo/geo_types.cpp @@ -62,6 +62,9 @@ GeoPolygon::~GeoPolygon() = default; GeoCircle::GeoCircle() = default; GeoCircle::~GeoCircle() = default; +GeoMultiPolygon::GeoMultiPolygon() = default; +GeoMultiPolygon::~GeoMultiPolygon() = default; + void print_s2point(std::ostream& os, const S2Point& point) { S2LatLng coord(point); os << std::setprecision(15) << coord.lng().degrees() << " " << coord.lat().degrees(); @@ -259,6 +262,30 @@ bool is_point_in_polygon(const S2Point& point, const S2Polygon* polygon) { return (crossings % 2 == 1); } +bool is_line_touches_line(const S2Point& Line1_Point1, const S2Point& Line1_Point2, + const S2Point& Line2_Point1, const S2Point& Line2_Point2) { + int count = 0; + if (compute_distance_to_line(Line1_Point1, Line2_Point1, Line2_Point2) < TOLERANCE) { + count++; + } + if (compute_distance_to_line(Line1_Point2, Line2_Point1, Line2_Point2) < TOLERANCE) { + count++; + } + if (compute_distance_to_line(Line2_Point1, Line1_Point1, Line1_Point2) < TOLERANCE) { + count++; + } + if (compute_distance_to_line(Line2_Point2, Line1_Point1, Line1_Point2) < TOLERANCE) { + count++; + } + // Two intersections are allowed when there is only one intersection, or when the intersection is an endpoint + if (count == 1 || + (count == 2 && ((Line1_Point1 == Line2_Point1 || Line1_Point1 == Line2_Point2) + + (Line1_Point2 == Line2_Point1 || Line1_Point2 == Line2_Point2)) == 1)) { + return true; + } + return false; +} + static inline GeoParseStatus to_s2point(const GeoCoordinate& coord, S2Point* point) { return to_s2point(coord.x, coord.y, point); } @@ -418,6 +445,10 @@ std::unique_ptr GeoShape::from_encoded(const void* ptr, size_t size) { shape = GeoCircle::create_unique(); break; } + case GEO_SHAPE_MULTI_POLYGON: { + shape = GeoMultiPolygon::create_unique(); + break; + } default: return nullptr; } @@ -483,6 +514,15 @@ const std::unique_ptr GeoPolygon::to_coords() const { return coordss; } +const std::vector> GeoMultiPolygon::to_coords() const { + std::vector> coordss; + for (const auto& polygon : _polygons) { + std::unique_ptr coords = polygon->to_coords(); + coordss.push_back(std::move(coords)); + } + return coordss; +} + bool GeoPoint::intersects(const GeoShape* rhs) const { switch (rhs->type()) { case GEO_SHAPE_POINT: { @@ -498,6 +538,10 @@ bool GeoPoint::intersects(const GeoShape* rhs) const { const GeoPolygon* polygon = assert_cast(rhs); return polygon->intersects(this); } + case GEO_SHAPE_MULTI_POLYGON: { + const GeoMultiPolygon* multi_polygon = assert_cast(rhs); + return multi_polygon->intersects(this); + } case GEO_SHAPE_CIRCLE: { const GeoCircle* circle = assert_cast(rhs); return circle->intersects(this); @@ -525,6 +569,10 @@ bool GeoPoint::touches(const GeoShape* rhs) const { const GeoPolygon* polygon = assert_cast(rhs); return polygon->touches(this); } + case GEO_SHAPE_MULTI_POLYGON: { + const GeoMultiPolygon* multi_polygon = assert_cast(rhs); + return multi_polygon->touches(this); + } case GEO_SHAPE_CIRCLE: { const GeoCircle* circle = assert_cast(rhs); return circle->touches(this); @@ -653,6 +701,15 @@ bool GeoLine::intersects(const GeoShape* rhs) const { const GeoPolygon* polygon = assert_cast(rhs); return polygon->polygon()->Intersects(*_polyline); } + case GEO_SHAPE_MULTI_POLYGON: { + const GeoMultiPolygon* multi_polygon = assert_cast(rhs); + for (const auto& polygon : multi_polygon->polygons()) { + if (polygon->intersects(this)) { + return true; + } + } + return false; + } case GEO_SHAPE_CIRCLE: { const GeoCircle* circle = assert_cast(rhs); return circle->intersects(this); @@ -686,29 +743,16 @@ bool GeoLine::touches(const GeoShape* rhs) const { const S2Point& p3 = other->polyline()->vertex(0); const S2Point& p4 = other->polyline()->vertex(1); - int count = 0; - if (compute_distance_to_line(p1, p3, p4) < TOLERANCE) { - count++; - } - if (compute_distance_to_line(p2, p3, p4) < TOLERANCE) { - count++; - } - if (compute_distance_to_line(p3, p1, p2) < TOLERANCE) { - count++; - } - if (compute_distance_to_line(p4, p1, p2) < TOLERANCE) { - count++; - } - // Two intersections are allowed when there is only one intersection, or when the intersection is an endpoint - if (count == 1 || (count == 2 && ((p1 == p3 || p1 == p4) + (p2 == p3 || p2 == p4)) == 1)) { - return true; - } - return false; + return is_line_touches_line(p1, p2, p3, p4); } case GEO_SHAPE_POLYGON: { const GeoPolygon* polygon = assert_cast(rhs); return polygon->touches(this); } + case GEO_SHAPE_MULTI_POLYGON: { + const GeoMultiPolygon* multi_polygon = assert_cast(rhs); + return multi_polygon->touches(this); + } case GEO_SHAPE_CIRCLE: { const GeoCircle* circle = assert_cast(rhs); return circle->touches(this); @@ -833,6 +877,15 @@ bool GeoPolygon::intersects(const GeoShape* rhs) const { } return true; } + case GEO_SHAPE_MULTI_POLYGON: { + const GeoMultiPolygon* multi_polygon = assert_cast(rhs); + for (const auto& other : multi_polygon->polygons()) { + if (this->intersects(other.get())) { + return true; + } + } + return false; + } case GEO_SHAPE_CIRCLE: { const GeoCircle* circle = assert_cast(rhs); return circle->intersects(this); @@ -862,17 +915,23 @@ bool GeoPolygon::polygon_touch_point(const S2Polygon* polygon, const S2Point* po } bool GeoPolygon::polygon_touch_polygon(const S2Polygon* polygon1, const S2Polygon* polygon2) const { + // Dual-check to avoid the following situations + // POLYGON((0 0, 20 0, 20 20, 0 20, 0 0), (5 5, 15 5, 15 15, 5 15, 5 5)) + // POLYGON((5 10, 10 5, 15 10, 10 15, 5 10)) for (int i = 0; i < polygon1->num_loops(); ++i) { const S2Loop* loop = polygon1->loop(i); for (int j = 0; j < loop->num_vertices(); ++j) { - const S2Point& p = loop->vertex(j); + const S2Point& p1 = loop->vertex(j); + const S2Point& p2 = loop->vertex((j + 1) % loop->num_vertices()); for (int k = 0; k < polygon2->num_loops(); ++k) { const S2Loop* innee_loop = polygon2->loop(k); for (int l = 0; l < innee_loop->num_vertices(); ++l) { - const S2Point& p1 = innee_loop->vertex(l); - const S2Point& p2 = innee_loop->vertex((l + 1) % loop->num_vertices()); - double distance = compute_distance_to_line(p, p1, p2); - if (distance < TOLERANCE) { + const S2Point& p3 = innee_loop->vertex(l); + const S2Point& p4 = innee_loop->vertex((l + 1) % innee_loop->num_vertices()); + if (compute_distance_to_line(p1, p3, p4) < TOLERANCE || + compute_distance_to_line(p2, p3, p4) < TOLERANCE || + compute_distance_to_line(p3, p1, p2) < TOLERANCE || + compute_distance_to_line(p4, p1, p2) < TOLERANCE) { return true; } } @@ -941,11 +1000,25 @@ bool GeoPolygon::touches(const GeoShape* rhs) const { const GeoPolygon* other = assert_cast(rhs); const S2Polygon* polygon1 = _polygon.get(); const S2Polygon* polygon2 = other->polygon(); - // when the two polygons do not have overlapping areas, then determine if the touch regulation is met. - if (!polygon1->Intersects(polygon2)) { - return polygon_touch_polygon(polygon1, polygon2); + + // "Touches" equivalent to boundary contact but no internal overlap + std::unique_ptr intersection(new S2Polygon()); + intersection->InitToIntersection(*polygon1, *polygon2); + return (intersection->GetArea() < S1Angle::Radians(TOLERANCE).radians() && + polygon_touch_polygon(polygon1, polygon2)); + } + case GEO_SHAPE_MULTI_POLYGON: { + const GeoMultiPolygon* multi_polygon = assert_cast(rhs); + bool has_touches = false; + for (const auto& other : multi_polygon->polygons()) { + if (this->intersects(other.get())) { + if (!this->touches(other.get())) { + return false; + } + has_touches = true; + } } - return false; + return has_touches; } case GEO_SHAPE_CIRCLE: { const GeoCircle* circle = assert_cast(rhs); @@ -970,6 +1043,15 @@ bool GeoPolygon::contains(const GeoShape* rhs) const { const GeoPolygon* other = (const GeoPolygon*)rhs; return _polygon->Contains(*other->polygon()); } + case GEO_SHAPE_MULTI_POLYGON: { + const GeoMultiPolygon* multi_polygon = (const GeoMultiPolygon*)rhs; + for (const auto& other : multi_polygon->polygons()) { + if (!this->contains(other.get())) { + return false; + } + } + return true; + } default: return false; } @@ -987,6 +1069,290 @@ S2Loop* GeoPolygon::getLoop(int i) const { return const_cast(_polygon->loop(i)); } +GeoParseStatus GeoMultiPolygon::from_coords(const std::vector& list) { + _polygons.clear(); + for (const auto& coords_list : list) { + std::unique_ptr polygon = GeoPolygon::create_unique(); + auto status = polygon->from_coords(coords_list); + if (status != GEO_PARSE_OK) { + return status; + } + _polygons.push_back(std::move(polygon)); + } + + return check_self_intersection(); +} + +GeoParseStatus GeoMultiPolygon::check_self_intersection() { + for (int i = 0; i < _polygons.size(); ++i) { + for (int j = i + 1; j < _polygons.size(); ++j) { + if (_polygons[i]->intersects(_polygons[j].get())) { + if (!_polygons[i]->touches(_polygons[j].get())) { + return GEO_PARSE_MULTIPOLYGON_OVERLAP; + } + } else { + continue; + } + + // Polygons in a multipolygon can only share discrete points, not edges. + for (int k = 0; k < _polygons[i]->numLoops(); ++k) { + const S2Loop* loop1 = _polygons[i]->getLoop(k); + for (int l = 0; l < _polygons[j]->numLoops(); ++l) { + const S2Loop* loop2 = _polygons[j]->getLoop(l); + for (int m = 0; m < loop1->num_vertices(); ++m) { + const S2Point& p1 = loop1->vertex(m); + const S2Point& p2 = loop1->vertex((m + 1) % loop1->num_vertices()); + for (int n = 0; n < loop2->num_vertices(); ++n) { + const S2Point& p3 = loop2->vertex(n); + const S2Point& p4 = loop2->vertex((n + 1) % loop2->num_vertices()); + + // 1. At least one endpoint of an edge is near another edge + // 2. Check the edges "touches" each other in a valid way + if ((compute_distance_to_line(p1, p3, p4) < TOLERANCE || + compute_distance_to_line(p2, p3, p4) < TOLERANCE || + compute_distance_to_line(p3, p1, p2) < TOLERANCE || + compute_distance_to_line(p4, p1, p2) < TOLERANCE) && + !is_line_touches_line(p1, p2, p3, p4)) { + return GEO_PARSE_MULTIPOLYGON_OVERLAP; + } + } + } + } + } + } + } + return GEO_PARSE_OK; +} + +bool GeoMultiPolygon::intersects(const GeoShape* rhs) const { + switch (rhs->type()) { + case GEO_SHAPE_POINT: { + const GeoPoint* point = assert_cast(rhs); + for (const auto& polygon : this->_polygons) { + if (polygon->intersects(point)) { + return true; + } + } + return false; + } + case GEO_SHAPE_LINE_STRING: { + const GeoLine* line = assert_cast(rhs); + for (const auto& polygon : this->_polygons) { + if (polygon->intersects(line)) { + return true; + } + } + return false; + } + case GEO_SHAPE_POLYGON: { + const GeoPolygon* other = assert_cast(rhs); + for (const auto& polygon : this->_polygons) { + if (polygon->intersects(other)) { + return true; + } + } + return false; + } + case GEO_SHAPE_MULTI_POLYGON: { + const GeoMultiPolygon* multi_polygon = assert_cast(rhs); + for (const auto& other : multi_polygon->polygons()) { + for (const auto& polygon : this->_polygons) { + if (polygon->intersects(other.get())) { + return true; + } + } + } + return false; + } + case GEO_SHAPE_CIRCLE: { + const GeoCircle* circle = assert_cast(rhs); + return circle->intersects(this); + } + default: + return false; + } +} + +bool GeoMultiPolygon::disjoint(const GeoShape* rhs) const { + return !intersects(rhs); +} + +bool GeoMultiPolygon::touches(const GeoShape* rhs) const { + switch (rhs->type()) { + case GEO_SHAPE_POINT: { + const GeoPoint* point = assert_cast(rhs); + for (const auto& polygon : this->_polygons) { + if (polygon->touches(point)) { + return true; + } + } + return false; + } + case GEO_SHAPE_LINE_STRING: { + const GeoLine* line = assert_cast(rhs); + bool has_touches = false; + for (const auto& polygon : this->_polygons) { + if (polygon->intersects(line)) { + if (!polygon->touches(line)) { + return false; + } + has_touches = true; + } + } + return has_touches; + } + case GEO_SHAPE_POLYGON: { + const GeoPolygon* other = assert_cast(rhs); + bool has_touches = false; + for (const auto& polygon : this->_polygons) { + if (polygon->intersects(other)) { + if (!polygon->touches(other)) { + return false; + } + has_touches = true; + } + } + return has_touches; + } + case GEO_SHAPE_MULTI_POLYGON: { + const GeoMultiPolygon* multi_polygon = assert_cast(rhs); + bool has_touches = false; + for (const auto& other : multi_polygon->polygons()) { + for (const auto& polygon : this->_polygons) { + if (polygon->intersects(other.get())) { + if (!polygon->touches(other.get())) { + return false; + } + has_touches = true; + } + } + } + return has_touches; + } + case GEO_SHAPE_CIRCLE: { + const GeoCircle* circle = assert_cast(rhs); + return circle->touches(this); + } + default: + return false; + } +} + +bool GeoMultiPolygon::contains(const GeoShape* rhs) const { + switch (rhs->type()) { + case GEO_SHAPE_POINT: { + const GeoPoint* point = assert_cast(rhs); + for (const auto& polygon : this->_polygons) { + if (polygon->contains(point)) { + return true; + } + } + return false; + } + case GEO_SHAPE_LINE_STRING: { + const GeoLine* line = assert_cast(rhs); + for (const auto& polygon : this->_polygons) { + if (polygon->contains(line)) { + return true; + } + } + return false; + } + case GEO_SHAPE_POLYGON: { + const GeoPolygon* other = assert_cast(rhs); + for (const auto& polygon : this->_polygons) { + if (polygon->contains(other)) { + return true; + } + } + return false; + } + case GEO_SHAPE_MULTI_POLYGON: { + //All polygons in rhs need to be contained + const GeoMultiPolygon* multi_polygon = assert_cast(rhs); + for (const auto& other : multi_polygon->polygons()) { + for (const auto& polygon : this->_polygons) { + if (polygon->contains(other.get())) { + continue; + } + return false; + } + } + return true; + } + default: + return false; + } +} + +std::string GeoMultiPolygon::as_wkt() const { + std::stringstream ss; + ss << "MULTIPOLYGON ("; + for (size_t i = 0; i < _polygons.size(); ++i) { + if (i != 0) { + ss << ", "; + } + ss << "("; + const S2Polygon* polygon = _polygons[i]->polygon(); + for (int j = 0; j < polygon->num_loops(); ++j) { + if (j != 0) { + ss << ", "; + } + ss << "("; + const S2Loop* loop = polygon->loop(j); + for (int k = 0; k < loop->num_vertices(); ++k) { + if (k != 0) { + ss << ", "; + } + print_s2point(ss, loop->vertex(k)); + } + ss << ", "; + print_s2point(ss, loop->vertex(0)); + ss << ")"; + } + ss << ")"; + } + ss << ")"; + return ss.str(); +} + +double GeoMultiPolygon::getArea() const { + double area = 0; + for (const auto& polygon : _polygons) { + area += polygon->getArea(); + } + return area; +} + +void GeoMultiPolygon::encode(std::string* buf) { + Encoder encoder; + encoder.Ensure(sizeof(size_t)); + encoder.put_varint32(_polygons.size()); + for (const auto& polygon : _polygons) { + polygon->polygon()->Encode(&encoder); + } + buf->append(encoder.base(), encoder.length()); +} + +bool GeoMultiPolygon::decode(const void* data, size_t size) { + Decoder decoder(data, size); + uint32_t num_polygons; + if (!decoder.get_varint32(&num_polygons)) { + return false; + } + + _polygons.clear(); + for (uint32_t i = 0; i < num_polygons; ++i) { + std::unique_ptr polygon = GeoPolygon::create_unique(); + polygon->_polygon.reset(new S2Polygon()); + if (!(polygon->_polygon->Decode(&decoder)) && polygon->_polygon->IsValid()) { + return false; + } + _polygons.push_back(std::move(polygon)); + } + return true; +} + GeoParseStatus GeoCircle::init(double lng, double lat, double radius_meter) { S2Point center; auto status = to_s2point(lng, lat, ¢er); @@ -1043,6 +1409,15 @@ bool GeoCircle::intersects(const GeoShape* rhs) const { } return false; } + case GEO_SHAPE_MULTI_POLYGON: { + const GeoMultiPolygon* multi_polygon = assert_cast(rhs); + for (const auto& polygon : multi_polygon->polygons()) { + if (this->intersects(polygon.get())) { + return true; + } + } + return false; + } case GEO_SHAPE_CIRCLE: { const GeoCircle* circle = assert_cast(rhs); S1ChordAngle radius_angle = _cap->radius(); @@ -1106,6 +1481,19 @@ bool GeoCircle::touches(const GeoShape* rhs) const { return false; } + case GEO_SHAPE_MULTI_POLYGON: { + const GeoMultiPolygon* multi_polygon = assert_cast(rhs); + bool has_touches = false; + for (const auto& polygon : multi_polygon->polygons()) { + if (this->intersects(polygon.get())) { + if (!this->touches(polygon.get())) { + return false; + } + has_touches = true; + } + } + return has_touches; + } case GEO_SHAPE_CIRCLE: { const GeoCircle* circle = assert_cast(rhs); S1ChordAngle radius_angle = _cap->radius(); diff --git a/be/src/geo/geo_types.h b/be/src/geo/geo_types.h index 0d08c9a80ef699..1f44fa3deb556c 100644 --- a/be/src/geo/geo_types.h +++ b/be/src/geo/geo_types.h @@ -21,6 +21,7 @@ #include #include +#include #include "common/factory_creator.h" #include "geo/geo_common.h" @@ -152,6 +153,7 @@ class GeoLine : public GeoShape { class GeoPolygon : public GeoShape { ENABLE_FACTORY_CREATOR(GeoPolygon); + friend class GeoMultiPolygon; public: GeoPolygon(); @@ -185,6 +187,36 @@ class GeoPolygon : public GeoShape { std::unique_ptr _polygon; }; +class GeoMultiPolygon : public GeoShape { + ENABLE_FACTORY_CREATOR(GeoMultiPolygon); + +public: + GeoMultiPolygon(); + ~GeoMultiPolygon() override; + + GeoParseStatus check_self_intersection(); + GeoParseStatus from_coords(const std::vector& list); + const std::vector> to_coords() const; + + GeoShapeType type() const override { return GEO_SHAPE_MULTI_POLYGON; } + const std::vector>& polygons() const { return _polygons; } + + bool intersects(const GeoShape* rhs) const override; + bool disjoint(const GeoShape* rhs) const override; + bool touches(const GeoShape* rhs) const override; + bool contains(const GeoShape* rhs) const override; + std::string as_wkt() const override; + + double getArea() const; + +protected: + void encode(std::string* buf) override; + bool decode(const void* data, size_t size) override; + +private: + std::vector> _polygons; +}; + class GeoCircle : public GeoShape { ENABLE_FACTORY_CREATOR(GeoCircle); diff --git a/be/src/geo/wkt_parse_type.h b/be/src/geo/wkt_parse_type.h index 8d4204049a22c6..a871ce10217f2d 100644 --- a/be/src/geo/wkt_parse_type.h +++ b/be/src/geo/wkt_parse_type.h @@ -38,6 +38,8 @@ struct GeoCoordinateListList { delete item; } } + GeoCoordinateListList() = default; + GeoCoordinateListList(GeoCoordinateListList&& other) : list(std::move(other.list)) {} void add(GeoCoordinateList* coordinates) { list.push_back(coordinates); } std::vector list; }; diff --git a/be/src/geo/wkt_yacc.y b/be/src/geo/wkt_yacc.y index 1af26e82eeeb17..02f59ea7068e12 100644 --- a/be/src/geo/wkt_yacc.y +++ b/be/src/geo/wkt_yacc.y @@ -33,6 +33,7 @@ void wkt_error(WktParseContext* ctx, const char* msg) { doris::GeoCoordinate coordinate_value; doris::GeoCoordinateList* coordinate_list_value; doris::GeoCoordinateListList* coordinate_list_list_value; + std::vector* multi_polygon_value; doris::GeoShape* shape_value; } @@ -67,16 +68,19 @@ void wkt_error(WktParseContext* ctx, const char* msg) { %token NUMERIC %type shape -%type point linestring polygon +%type point linestring polygon multi_polygon %type coordinate %type coordinate_list %type coordinate_list_list +%type multi_polygon_list %destructor { delete $$; } coordinate_list %destructor { delete $$; } coordinate_list_list %destructor { delete $$; } point %destructor { delete $$; } linestring %destructor { delete $$; } polygon +%destructor { delete $$; } multi_polygon +%destructor { delete $$; } multi_polygon_list %% @@ -87,6 +91,8 @@ shape: { ctx->shape = $1; } | polygon { ctx->shape = $1; } + | multi_polygon + { ctx->shape = $1; } ; point: @@ -129,6 +135,35 @@ polygon: } ; +multi_polygon: + KW_MULTI_POLYGON '(' multi_polygon_list ')' + { + // to avoid memory leak + std::unique_ptr> list($3); + std::unique_ptr multi_polygon = doris::GeoMultiPolygon::create_unique(); + ctx->parse_status = multi_polygon->from_coords(*$3); + if (ctx->parse_status != doris::GEO_PARSE_OK) { + YYABORT; + } + $$ = multi_polygon.release(); + } + ; + +multi_polygon_list: + multi_polygon_list ',' '(' coordinate_list_list ')' + { + $1->push_back(std::move(*$4)); + delete $4; + $$ = $1; + } + | '(' coordinate_list_list ')' + { + $$ = new std::vector(); + $$->push_back(std::move(*$2)); + delete $2; + } + ; + coordinate_list_list: coordinate_list_list ',' '(' coordinate_list ')' { diff --git a/be/src/vec/common/assert_cast.h b/be/src/vec/common/assert_cast.h index 6d8765befa203f..cf943dd0dae8a7 100644 --- a/be/src/vec/common/assert_cast.h +++ b/be/src/vec/common/assert_cast.h @@ -23,6 +23,7 @@ #include #include +#include "common/compiler_util.h" #include "common/logging.h" #include "vec/common/demangle.h" diff --git a/be/test/geo/geo_types_test.cpp b/be/test/geo/geo_types_test.cpp index 5f8dc105651d24..55504c5799af81 100644 --- a/be/test/geo/geo_types_test.cpp +++ b/be/test/geo/geo_types_test.cpp @@ -100,13 +100,17 @@ TEST_F(GeoTypesTest, point_intersects) { const char* wkt_linestring = "LINESTRING(-20 0, 20 0)"; const char* wkt_polygon = "POLYGON((0 0,10 0,10 10,0 10,0 0))"; + const char* wkt_multi_polygon = "MULTIPOLYGON(((0 0,10 0,10 10,0 10,0 0)))"; std::unique_ptr line( GeoShape::from_wkt(wkt_linestring, strlen(wkt_linestring), &status)); std::unique_ptr polygon( GeoShape::from_wkt(wkt_polygon, strlen(wkt_polygon), &status)); + std::unique_ptr multi_polygon( + GeoShape::from_wkt(wkt_multi_polygon, strlen(wkt_multi_polygon), &status)); ASSERT_NE(nullptr, line.get()); ASSERT_NE(nullptr, polygon.get()); + ASSERT_NE(nullptr, multi_polygon.get()); { // point on the line (center) @@ -132,24 +136,28 @@ TEST_F(GeoTypesTest, point_intersects) { GeoPoint point; point.from_coord(5, 5); EXPECT_TRUE(point.intersects(polygon.get())); + EXPECT_TRUE(point.intersects(multi_polygon.get())); } { // point on polygon boundary edges (not vertices) GeoPoint point; point.from_coord(5, 0); EXPECT_TRUE(point.intersects(polygon.get())); + EXPECT_TRUE(point.intersects(multi_polygon.get())); } { // point at polygon vertices GeoPoint point; point.from_coord(0, 0); EXPECT_TRUE(point.intersects(polygon.get())); + EXPECT_TRUE(point.intersects(multi_polygon.get())); } { // point outside the polygon GeoPoint point; point.from_coord(20, 20); EXPECT_FALSE(point.intersects(polygon.get())); + EXPECT_FALSE(point.intersects(multi_polygon.get())); } std::string buf; @@ -172,15 +180,20 @@ TEST_F(GeoTypesTest, linestring_intersects) { const char* base_line = "LINESTRING(-10 0, 10 0)"; const char* vertical_line = "LINESTRING(0 -10, 0 10)"; const char* polygon = "POLYGON((-5 -5,5 -5,5 5,-5 5,-5 -5))"; + const char* multi_polygon = + "MULTIPOLYGON(((30 30,35 30,35 35,30 35,30 30)), ((-5 -5,5 -5,5 5,-5 5,-5 -5)))"; std::unique_ptr base_line_shape( GeoShape::from_wkt(base_line, strlen(base_line), &status)); std::unique_ptr vertical_line_shape( GeoShape::from_wkt(vertical_line, strlen(vertical_line), &status)); std::unique_ptr polygon_shape(GeoShape::from_wkt(polygon, strlen(polygon), &status)); + std::unique_ptr multi_polygon_shape( + GeoShape::from_wkt(multi_polygon, strlen(multi_polygon), &status)); ASSERT_NE(nullptr, base_line_shape.get()); ASSERT_NE(nullptr, vertical_line_shape.get()); ASSERT_NE(nullptr, polygon_shape.get()); + ASSERT_NE(nullptr, multi_polygon_shape.get()); // ====================== // LineString vs Point @@ -252,6 +265,7 @@ TEST_F(GeoTypesTest, linestring_intersects) { std::unique_ptr inner_line( GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); EXPECT_TRUE(polygon_shape->intersects(inner_line.get())); + EXPECT_TRUE(multi_polygon_shape->intersects(inner_line.get())); } { // crossing the border @@ -259,6 +273,7 @@ TEST_F(GeoTypesTest, linestring_intersects) { std::unique_ptr cross_line( GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); EXPECT_TRUE(polygon_shape->intersects(cross_line.get())); + EXPECT_TRUE(multi_polygon_shape->intersects(cross_line.get())); } { // along the borderline @@ -266,6 +281,7 @@ TEST_F(GeoTypesTest, linestring_intersects) { std::unique_ptr edge_line( GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); EXPECT_TRUE(polygon_shape->intersects(edge_line.get())); + EXPECT_TRUE(multi_polygon_shape->intersects(edge_line.get())); } { // only one point @@ -273,6 +289,7 @@ TEST_F(GeoTypesTest, linestring_intersects) { std::unique_ptr edge_line( GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); EXPECT_TRUE(polygon_shape->intersects(edge_line.get())); + EXPECT_TRUE(multi_polygon_shape->intersects(edge_line.get())); } { // fully external @@ -280,6 +297,7 @@ TEST_F(GeoTypesTest, linestring_intersects) { std::unique_ptr outer_line( GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); EXPECT_FALSE(polygon_shape->intersects(outer_line.get())); + EXPECT_FALSE(multi_polygon_shape->intersects(outer_line.get())); } std::string buf; @@ -303,6 +321,8 @@ TEST_F(GeoTypesTest, polygon_intersects) { const char* test_line = "LINESTRING(-5 5,15 5)"; const char* overlap_polygon = "POLYGON((5 5,15 5,15 15,5 15,5 5))"; const char* base_polygon2 = "POLYGON((-5 -5,5 -5,5 5,-5 5,-5 -5))"; + const char* multi_polygons = + "MULTIPOLYGON(((35 35,40 35,40 40,35 40,35 35)), ((0 0,10 0,10 10,0 10,0 0)))"; std::unique_ptr polygon( GeoShape::from_wkt(base_polygon, strlen(base_polygon), &status)); @@ -311,10 +331,13 @@ TEST_F(GeoTypesTest, polygon_intersects) { std::unique_ptr line(GeoShape::from_wkt(test_line, strlen(test_line), &status)); std::unique_ptr other_polygon( GeoShape::from_wkt(overlap_polygon, strlen(overlap_polygon), &status)); + std::unique_ptr multi_polygon( + GeoShape::from_wkt(multi_polygons, strlen(multi_polygons), &status)); ASSERT_NE(nullptr, polygon.get()); ASSERT_NE(nullptr, polygon2.get()); ASSERT_NE(nullptr, line.get()); ASSERT_NE(nullptr, other_polygon.get()); + ASSERT_NE(nullptr, multi_polygon.get()); // ====================== // Polygon vs Point @@ -443,21 +466,25 @@ TEST_F(GeoTypesTest, polygon_intersects) { const char* wkt = "POLYGON((2 2,8 2,8 8,2 8,2 2))"; std::unique_ptr small_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); EXPECT_TRUE(polygon->intersects(small_polygon.get())); + EXPECT_TRUE(multi_polygon->intersects(small_polygon.get())); } { const char* wkt = "POLYGON((5 5,15 5,15 15,5 15,5 5))"; std::unique_ptr overlap_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); EXPECT_TRUE(polygon->intersects(overlap_polygon.get())); + EXPECT_TRUE(multi_polygon->intersects(overlap_polygon.get())); } { const char* wkt = "POLYGON((10 0,20 0,20 10,10 10,10 0))"; std::unique_ptr touch_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); EXPECT_TRUE(polygon->intersects(touch_polygon.get())); + EXPECT_TRUE(multi_polygon->intersects(touch_polygon.get())); } { const char* wkt = "POLYGON((20 20,30 20,30 30,20 30,20 20))"; std::unique_ptr separate_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); EXPECT_FALSE(polygon->intersects(separate_polygon.get())); + EXPECT_FALSE(multi_polygon->intersects(separate_polygon.get())); } std::string buf; @@ -474,6 +501,160 @@ TEST_F(GeoTypesTest, polygon_intersects) { } } +TEST_F(GeoTypesTest, multipolygon_intersects) { + GeoParseStatus status; + + const char* base_multipolygon = + "MULTIPOLYGON (" + "((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3))," + "((15 0, 25 0, 25 10, 15 10, 15 0))," + "((30 30, 40 30, 35 35, 30 30))" + ")"; + + const char* test_line = "LINESTRING(-5 5, 35 5)"; + const char* overlap_polygon = "POLYGON((8 8, 18 8, 18 18, 8 18, 8 8))"; + const char* external_polygon = "POLYGON((50 50, 60 50, 60 60, 50 60, 50 50))"; + + std::unique_ptr multipolygon( + GeoShape::from_wkt(base_multipolygon, strlen(base_multipolygon), &status)); + std::unique_ptr line(GeoShape::from_wkt(test_line, strlen(test_line), &status)); + std::unique_ptr poly_overlap( + GeoShape::from_wkt(overlap_polygon, strlen(overlap_polygon), &status)); + std::unique_ptr poly_external( + GeoShape::from_wkt(external_polygon, strlen(external_polygon), &status)); + + ASSERT_NE(nullptr, multipolygon.get()); + ASSERT_NE(nullptr, line.get()); + ASSERT_NE(nullptr, poly_overlap.get()); + ASSERT_NE(nullptr, poly_external.get()); + + // ====================== + // MultiPolygon vs Point + // ====================== + { + GeoPoint point; + point.from_coord(5, 5); + EXPECT_FALSE(multipolygon->intersects(&point)); + } + { + GeoPoint point; + point.from_coord(1.5, 1.8); + EXPECT_TRUE(multipolygon->intersects(&point)); + } + { + GeoPoint point; + point.from_coord(20, 5); + EXPECT_TRUE(multipolygon->intersects(&point)); + } + { + GeoPoint point; + point.from_coord(12, 0); + EXPECT_FALSE(multipolygon->intersects(&point)); + } + { + GeoPoint point; + point.from_coord(40, 30); + EXPECT_TRUE(multipolygon->intersects(&point)); + } + + // ====================== + // MultiPolygon vs LineString + // ====================== + { + const char* wkt = "LINESTRING(4 4, 7 7)"; + std::unique_ptr in_hole_line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(multipolygon->intersects(in_hole_line.get())); + } + { + const char* wkt = "LINESTRING(-5 5, 35 5)"; + std::unique_ptr cross_line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(multipolygon->intersects(cross_line.get())); + } + { + const char* wkt = "LINESTRING(3 3, 7 3)"; + std::unique_ptr inner_line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(multipolygon->intersects(inner_line.get())); + } + { + const char* wkt = "LINESTRING(30 30, 35 35)"; + std::unique_ptr triangle_line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(multipolygon->intersects(triangle_line.get())); + } + { + const char* wkt = "LINESTRING(50 50, 60 60)"; + std::unique_ptr outer_line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(multipolygon->intersects(outer_line.get())); + } + + // ====================== + // MultiPolygon vs Polygon + // ====================== + { + const char* wkt = "POLYGON((4 4, 7 4, 7 7, 4 7, 4 4))"; + std::unique_ptr in_hole_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(multipolygon->intersects(in_hole_polygon.get())); + } + { + const char* wkt = "POLYGON((20 0, 30 0, 30 10, 20 10, 20 0))"; + std::unique_ptr overlap_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(multipolygon->intersects(overlap_polygon.get())); + } + { + const char* wkt = "POLYGON((50 50, 60 50, 60 60, 50 60, 50 50))"; + std::unique_ptr external_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(multipolygon->intersects(external_polygon.get())); + } + { + const char* wkt = "POLYGON((10 0, 20 0, 20 5, 10 5, 10 0))"; + std::unique_ptr cross_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(multipolygon->intersects(cross_polygon.get())); + } + { + const char* wkt = "POLYGON((10 0, 15 0, 15 10, 10 10, 10 0))"; + std::unique_ptr touch_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(multipolygon->intersects(touch_polygon.get())); + } + + // ====================== + // MultiPolygon vs MultiPolygon + // ====================== + { + const char* wkt = "MULTIPOLYGON (((4 4, 5 4, 5 5, 4 5, 4 4)), ((6 6, 7 6, 7 7, 6 7, 6 6)))"; + std::unique_ptr in_hole_multi(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(multipolygon->intersects(in_hole_multi.get())); + } + { + const char* wkt = + "MULTIPOLYGON (((8 8, 18 8, 18 18, 8 18, 8 8)), ((30 30, 40 30, 35 35, 30 30)))"; + std::unique_ptr overlap_multi(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(multipolygon->intersects(overlap_multi.get())); + } + { + const char* wkt = + "MULTIPOLYGON (((-10 -10, 0 -10, 0 0, -10 0, -10 -10)), ((50 50, 60 50, 60 60, 50 " + "60, 50 50)))"; + std::unique_ptr separate_multi(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(multipolygon->intersects(separate_multi.get())); + } + + std::string buf; + multipolygon->encode_to(&buf); + { + std::unique_ptr decoded(GeoShape::from_encoded(buf.data(), buf.size())); + ASSERT_NE(nullptr, decoded.get()); + EXPECT_EQ(GEO_SHAPE_MULTI_POLYGON, decoded->type()); + + GeoPoint point; + point.from_coord(20, 5); + EXPECT_TRUE(decoded->intersects(&point)); + } + { + buf.resize(buf.size() - 2); + std::unique_ptr decoded(GeoShape::from_encoded(buf.data(), buf.size())); + EXPECT_EQ(nullptr, decoded.get()); + } +} + TEST_F(GeoTypesTest, circle_intersect) { GeoParseStatus status; @@ -553,13 +734,17 @@ TEST_F(GeoTypesTest, point_touches) { const char* wkt_linestring = "LINESTRING(-20 0, 20 0)"; const char* wkt_polygon = "POLYGON((0 0,10 0,10 10,0 10,0 0))"; + const char* wkt_multi_polygon = "MULTIPOLYGON(((0 0,10 0,10 10,0 10,0 0)))"; std::unique_ptr line( GeoShape::from_wkt(wkt_linestring, strlen(wkt_linestring), &status)); std::unique_ptr polygon( GeoShape::from_wkt(wkt_polygon, strlen(wkt_polygon), &status)); + std::unique_ptr multi_polygon( + GeoShape::from_wkt(wkt_multi_polygon, strlen(wkt_multi_polygon), &status)); ASSERT_NE(nullptr, line.get()); ASSERT_NE(nullptr, polygon.get()); + ASSERT_NE(nullptr, multi_polygon.get()); { // point touches the line at the center @@ -585,24 +770,28 @@ TEST_F(GeoTypesTest, point_touches) { GeoPoint point; point.from_coord(5, 5); EXPECT_FALSE(point.touches(polygon.get())); + EXPECT_FALSE(point.touches(multi_polygon.get())); } { // point touches the polygon boundary edge (not vertex) GeoPoint point; point.from_coord(5, 0); EXPECT_TRUE(point.touches(polygon.get())); + EXPECT_TRUE(point.touches(multi_polygon.get())); } { // point touches the polygon vertex GeoPoint point; point.from_coord(0, 0); EXPECT_TRUE(point.touches(polygon.get())); + EXPECT_TRUE(point.touches(multi_polygon.get())); } { // point does not touch the polygon GeoPoint point; point.from_coord(20, 20); EXPECT_FALSE(point.touches(polygon.get())); + EXPECT_FALSE(point.touches(multi_polygon.get())); } std::string buf; @@ -625,15 +814,19 @@ TEST_F(GeoTypesTest, linestring_touches) { const char* base_line = "LINESTRING(-10 0, 10 0)"; const char* vertical_line = "LINESTRING(0 -10, 0 10)"; const char* polygon = "POLYGON((-5 -5,5 -5,5 5,-5 5,-5 -5))"; + const char* multi_polygon = "MULTIPOLYGON(((-5 -5,5 -5,5 5,-5 5,-5 -5)))"; std::unique_ptr base_line_shape( GeoShape::from_wkt(base_line, strlen(base_line), &status)); std::unique_ptr vertical_line_shape( GeoShape::from_wkt(vertical_line, strlen(vertical_line), &status)); std::unique_ptr polygon_shape(GeoShape::from_wkt(polygon, strlen(polygon), &status)); + std::unique_ptr multi_polygon_shape( + GeoShape::from_wkt(multi_polygon, strlen(multi_polygon), &status)); ASSERT_NE(nullptr, base_line_shape.get()); ASSERT_NE(nullptr, vertical_line_shape.get()); ASSERT_NE(nullptr, polygon_shape.get()); + ASSERT_NE(nullptr, multi_polygon_shape.get()); // ====================== // LineString vs Point @@ -712,6 +905,7 @@ TEST_F(GeoTypesTest, linestring_touches) { std::unique_ptr inner_line( GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); EXPECT_FALSE(polygon_shape->touches(inner_line.get())); + EXPECT_FALSE(multi_polygon_shape->touches(inner_line.get())); } { // crossing the border @@ -719,6 +913,7 @@ TEST_F(GeoTypesTest, linestring_touches) { std::unique_ptr cross_line( GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); EXPECT_FALSE(polygon_shape->touches(cross_line.get())); + EXPECT_FALSE(multi_polygon_shape->touches(cross_line.get())); } { // along the borderline @@ -726,6 +921,7 @@ TEST_F(GeoTypesTest, linestring_touches) { std::unique_ptr edge_line( GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); EXPECT_TRUE(polygon_shape->touches(edge_line.get())); + EXPECT_TRUE(multi_polygon_shape->touches(edge_line.get())); } { // along the borderline @@ -733,6 +929,7 @@ TEST_F(GeoTypesTest, linestring_touches) { std::unique_ptr edge_line( GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); EXPECT_FALSE(polygon_shape->touches(edge_line.get())); + EXPECT_FALSE(multi_polygon_shape->touches(edge_line.get())); } { // along the borderline @@ -740,6 +937,7 @@ TEST_F(GeoTypesTest, linestring_touches) { std::unique_ptr edge_line( GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); EXPECT_FALSE(polygon_shape->touches(edge_line.get())); + EXPECT_FALSE(multi_polygon_shape->touches(edge_line.get())); } { // along the borderline @@ -747,6 +945,7 @@ TEST_F(GeoTypesTest, linestring_touches) { std::unique_ptr edge_line( GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); EXPECT_TRUE(polygon_shape->touches(edge_line.get())); + EXPECT_TRUE(multi_polygon_shape->touches(edge_line.get())); } { // fully external @@ -754,6 +953,7 @@ TEST_F(GeoTypesTest, linestring_touches) { std::unique_ptr outer_line( GeoShape::from_wkt(wkt_string, strlen(wkt_string), &status)); EXPECT_FALSE(polygon_shape->touches(outer_line.get())); + EXPECT_FALSE(multi_polygon_shape->touches(outer_line.get())); } std::string buf; @@ -776,15 +976,20 @@ TEST_F(GeoTypesTest, polygon_touches) { const char* base_polygon = "POLYGON((0 0,10 0,10 10,0 10,0 0))"; const char* test_line = "LINESTRING(-5 5,15 5)"; const char* overlap_polygon = "POLYGON((5 5,15 5,15 15,5 15,5 5))"; + const char* test_multi_polugon = + "MULTIPOLYGON(((30 30,35 30,35 35,30 35,30 30)), ((0 0,10 0,10 10,0 10,0 0)))"; std::unique_ptr polygon( GeoShape::from_wkt(base_polygon, strlen(base_polygon), &status)); std::unique_ptr line(GeoShape::from_wkt(test_line, strlen(test_line), &status)); std::unique_ptr other_polygon( GeoShape::from_wkt(overlap_polygon, strlen(overlap_polygon), &status)); + std::unique_ptr multi_polygon( + GeoShape::from_wkt(test_multi_polugon, strlen(test_multi_polugon), &status)); ASSERT_NE(nullptr, polygon.get()); ASSERT_NE(nullptr, line.get()); ASSERT_NE(nullptr, other_polygon.get()); + ASSERT_NE(nullptr, multi_polygon.get()); // ====================== // Polygon vs Point @@ -893,6 +1098,40 @@ TEST_F(GeoTypesTest, polygon_touches) { EXPECT_FALSE(polygon->touches(separate_polygon.get())); } + // ======================== + // Polygon vs MultiPolygon + // ======================== + { + const char* wkt = "MULTIPOLYGON(((2 2,8 2,8 8,2 8,2 2)))"; + std::unique_ptr small_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(polygon->touches(small_polygon.get())); + } + { + const char* wkt = "MULTIPOLYGON(((5 5,15 5,15 15,5 15,5 5)))"; + std::unique_ptr overlap_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(polygon->touches(overlap_polygon.get())); + } + { + const char* wkt = "MULTIPOLYGON(((10 0,20 0,20 10,10 10,10 0)))"; + std::unique_ptr touch_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(polygon->touches(touch_polygon.get())); + } + { + const char* wkt = "MULTIPOLYGON(((10.1 0,20 0,20 10,10.1 10,10.1 0)))"; + std::unique_ptr touch_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(polygon->touches(touch_polygon.get())); + } + { + const char* wkt = "MULTIPOLYGON(((9.99 0,20 0,20 10,9.99 10,9.99 0)))"; + std::unique_ptr touch_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(polygon->touches(touch_polygon.get())); + } + { + const char* wkt = "MULTIPOLYGON(((20 20,30 20,30 30,20 30,20 20)))"; + std::unique_ptr separate_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(polygon->touches(separate_polygon.get())); + } + std::string buf; polygon->encode_to(&buf); { @@ -907,6 +1146,155 @@ TEST_F(GeoTypesTest, polygon_touches) { } } +TEST_F(GeoTypesTest, multipolygon_touches) { + GeoParseStatus status; + + const char* base_multipolygon = + "MULTIPOLYGON (" + "((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3))," + "((15 0, 25 0, 25 10, 15 10, 15 0))," + "((30 30, 40 30, 35 35, 30 30))" + ")"; + const char* test_line = "LINESTRING(10 5, 20 5)"; + const char* overlap_polygon = "POLYGON((8 8, 18 8, 18 18, 8 18, 8 8))"; + const char* test_multi_polygon = + "MULTIPOLYGON (((-5 -5, 0 -5, 0 0, -5 0, -5 -5)), ((40 40, 50 40, 50 50, 40 50, 40 " + "40)))"; + + std::unique_ptr multipolygon( + GeoShape::from_wkt(base_multipolygon, strlen(base_multipolygon), &status)); + std::unique_ptr line(GeoShape::from_wkt(test_line, strlen(test_line), &status)); + std::unique_ptr other_polygon( + GeoShape::from_wkt(overlap_polygon, strlen(overlap_polygon), &status)); + std::unique_ptr other_multipolygon( + GeoShape::from_wkt(test_multi_polygon, strlen(test_multi_polygon), &status)); + ASSERT_NE(nullptr, multipolygon.get()); + ASSERT_NE(nullptr, line.get()); + ASSERT_NE(nullptr, other_polygon.get()); + ASSERT_NE(nullptr, other_multipolygon.get()); + + // ====================== + // MultiPolygon vs Point + // ====================== + { + GeoPoint point; + point.from_coord(0, 0); + EXPECT_TRUE(multipolygon->touches(&point)); + } + { + GeoPoint point; + point.from_coord(10, 5); + EXPECT_TRUE(multipolygon->touches(&point)); + } + { + GeoPoint point; + point.from_coord(30, 30); + EXPECT_TRUE(multipolygon->touches(&point)); + } + { + GeoPoint point; + point.from_coord(50, 50); + EXPECT_FALSE(multipolygon->touches(&point)); + } + + // =========================== + // MultiPolygon vs LineString + // =========================== + { + const char* wkt = "LINESTRING(3 5, 8 5)"; + std::unique_ptr in_hole_line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(multipolygon->touches(in_hole_line.get())); + } + { + const char* wkt = "LINESTRING(10 5, 20 5)"; + std::unique_ptr cross_line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(multipolygon->touches(cross_line.get())); + } + { + const char* wkt = "LINESTRING(30 30, 35 35)"; + std::unique_ptr edge_line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(multipolygon->touches(edge_line.get())); + } + { + const char* wkt = "LINESTRING(10 10, 15 0)"; + std::unique_ptr cross_line(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(multipolygon->touches(cross_line.get())); + } + + // ======================== + // MultiPolygon vs Polygon + // ======================== + { + const char* wkt = "POLYGON((3 3, 8 3, 8 8, 3 8, 3 3))"; + std::unique_ptr in_hole_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(multipolygon->touches(in_hole_polygon.get())); + } + { + const char* wkt = "POLYGON((3 3, 10 3, 10 8, 3 8, 3 3))"; + std::unique_ptr cross_hole_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(multipolygon->touches(cross_hole_polygon.get())); + } + { + const char* wkt = "POLYGON((25 0, 35 0, 35 10, 25 10, 25 0))"; + std::unique_ptr touch_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(multipolygon->touches(touch_polygon.get())); + } + { + const char* wkt = "POLYGON((8 8, 18 8, 18 18, 8 18, 8 8))"; + std::unique_ptr overlap_poly(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(multipolygon->touches(overlap_poly.get())); + } + { + const char* wkt = "POLYGON((10 0, 15 0, 15 10, 10 10, 10 0))"; + std::unique_ptr touch_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(multipolygon->touches(touch_polygon.get())); + } + { + const char* wkt = "POLYGON((20 20, 30 20, 30 30, 20 30, 20 20))"; + std::unique_ptr separate_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(multipolygon->touches(separate_polygon.get())); + } + { + const char* wkt = "POLYGON((10 0, 20 0, 20 5, 10 5, 10 0))"; + std::unique_ptr cross_polygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(multipolygon->touches(cross_polygon.get())); + } + + // ============================= + // MultiPolygon vs MultiPolygon + // ============================= + { + const char* wkt = + "MULTIPOLYGON (((-5 -5, 0 -5, 0 0, -5 0, -5 -5)), ((40 30, 50 30, 50 50, 40 50, 40 " + "30)))"; + std::unique_ptr touch_multi(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_TRUE(multipolygon->touches(touch_multi.get())); + } + { + const char* wkt = + "MULTIPOLYGON (((8 8, 18 8, 18 18, 8 18, 8 8)), ((30 30, 40 30, 35 25, 30 30)))"; + std::unique_ptr overlap_multi(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_FALSE(multipolygon->touches(overlap_multi.get())); + } + + std::string buf; + multipolygon->encode_to(&buf); + { + std::unique_ptr decoded(GeoShape::from_encoded(buf.data(), buf.size())); + ASSERT_NE(nullptr, decoded.get()); + EXPECT_EQ(GEO_SHAPE_MULTI_POLYGON, decoded->type()); + + GeoPoint point; + point.from_coord(10, 5); + EXPECT_TRUE(decoded->touches(&point)); + } + { + buf.resize(buf.size() - 2); + std::unique_ptr decoded(GeoShape::from_encoded(buf.data(), buf.size())); + EXPECT_EQ(nullptr, decoded.get()); + } +} + TEST_F(GeoTypesTest, circle_touches) { GeoParseStatus status; @@ -1098,6 +1486,53 @@ TEST_F(GeoTypesTest, polygon_hole_contains) { } } +TEST_F(GeoTypesTest, multipolygon_parse_fail) { + { + const char* wkt = "MULTIPOLYGON (((10 10, 50 10, 50 50, 10 50), (10 10 01)))"; + GeoParseStatus status; + std::unique_ptr multipolygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_EQ(GEO_PARSE_WKT_SYNTAX_ERROR, status); + EXPECT_EQ(nullptr, multipolygon.get()); + } + { + const char* wkt = "MULTIPOLYGON (((10 10, 50 10, 50 50, 10 50)))"; + GeoParseStatus status; + std::unique_ptr multipolygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_EQ(GEO_PARSE_LOOP_NOT_CLOSED, status); + EXPECT_EQ(nullptr, multipolygon.get()); + } + { + const char* wkt = "MULTIPOLYGON (((10 10, 50 10, 10 10)))"; + GeoParseStatus status; + std::unique_ptr multipolygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_EQ(GEO_PARSE_LOOP_LACK_VERTICES, status); + EXPECT_EQ(nullptr, multipolygon.get()); + } + { + const char* wkt = + "MULTIPOLYGON (((0 0, 0 10, 10 10, 10 0, 0 0)), ((5 5, 5 15, 15 15, 15 5, 5 5)))"; + GeoParseStatus status; + std::unique_ptr multipolygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_EQ(GEO_PARSE_MULTIPOLYGON_OVERLAP, status); + EXPECT_EQ(nullptr, multipolygon.get()); + } + { + const char* wkt = + "MULTIPOLYGON(((5 5, 5 8, 8 8, 8 5, 5 5)), ((8 6, 10 6, 10 10, 8 10, 8 6)))"; + GeoParseStatus status; + std::unique_ptr multipolygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_EQ(GEO_PARSE_MULTIPOLYGON_OVERLAP, status); + EXPECT_EQ(nullptr, multipolygon.get()); + } + { + const char* wkt = "MULTIPOLYGON((()))"; + GeoParseStatus status; + std::unique_ptr multipolygon(GeoShape::from_wkt(wkt, strlen(wkt), &status)); + EXPECT_EQ(GEO_PARSE_WKT_SYNTAX_ERROR, status); + EXPECT_EQ(nullptr, multipolygon.get()); + } +} + TEST_F(GeoTypesTest, circle) { GeoCircle circle; auto res = circle.init(110.123, 64, 1000); 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 bfcd4c28f730b2..f6f88cb5fcb1f0 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 @@ -8,6 +8,7 @@ POINT (24.7 56.7) -- !sql -- CIRCLE ((111 64), 10000) +-- ST_Contains -- -- !sql -- true @@ -17,9 +18,21 @@ false -- !sql -- true +-- !sql -- +true + +-- !sql -- +false + +-- ST_Intersects -- +-- POINT_Intersects +-- !sql -- +true + -- !sql -- false +-- LINESTRING_Intersects -- !sql -- true @@ -53,6 +66,7 @@ false -- !sql -- true +-- POLYGON_Intersects -- !sql -- true @@ -89,9 +103,23 @@ false -- !sql -- true +-- MULTIPOLYGON_Intersects_POINT +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +false + -- !sql -- true +-- !sql -- +false + +-- MULTIPOLYGON_Intersects_LINESTRING -- !sql -- true @@ -104,12 +132,29 @@ true -- !sql -- true +-- !sql -- +false + +-- MULTIPOLYGON_Intersects_POLYGON +-- !sql -- +false + -- !sql -- true +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + -- !sql -- true +-- MULTIPOLYGON_Intersects_MULTIPOLYGON -- !sql -- true @@ -119,21 +164,71 @@ true -- !sql -- false +-- !sql -- +false + +-- MULTIPOLYGON_Intersects_CIRCLE -- !sql -- true +-- !sql -- +false + -- !sql -- true -- !sql -- false +-- CIRCLE_Intersects +-- !sql -- +true + +-- !sql -- +true + -- !sql -- false +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + -- !sql -- false +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +false + +-- ST_Touches -- +-- POINT +-- !sql -- +false + +-- !sql -- +false + +-- LINESTRING -- !sql -- false @@ -152,6 +247,7 @@ false -- !sql -- false +-- POLYGON -- !sql -- true @@ -191,12 +287,23 @@ true -- !sql -- true +-- MULTIPOLYGON_TOUCH_POINT -- !sql -- true +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +false + -- !sql -- false +-- MULTIPOLYGON_TOUCH_LINESTRING -- !sql -- false @@ -209,6 +316,9 @@ false -- !sql -- false +-- !sql -- +false + -- !sql -- true @@ -218,6 +328,16 @@ false -- !sql -- false +-- MULTIPOLYGON_TOUCH_POLYGON +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + -- !sql -- false @@ -236,18 +356,29 @@ false -- !sql -- true +-- MULTIPOLYGON_TOUCH_MULTIPOLYGON +-- !sql -- +false + +-- !sql -- +true + -- !sql -- false -- !sql -- false +-- MULTIPOLYGON_TOUCH_CIRCLE -- !sql -- true -- !sql -- false +-- !sql -- +false + -- !sql -- true @@ -263,15 +394,34 @@ false -- !sql -- false +-- CIRCLE +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +false + -- !sql -- false +-- !sql -- +true + -- !sql -- false -- !sql -- false +-- !sql -- +true + +-- !sql -- +false + -- !sql -- false @@ -287,6 +437,145 @@ false -- !sql -- false +-- ST_Disjoint -- +-- POINT +-- !sql -- +false + +-- !sql -- +true + +-- LINESTRING +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- POLYGON +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- MULTIPOLYGON_Disjoint_POINT +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- MULTIPOLYGON_Disjoint_LINESTRING +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +true + +-- MULTIPOLYGON_Disjoint_POLYGON +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +false + +-- MULTIPOLYGON_Disjoint_MULTIPOLYGON +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +true + +-- MULTIPOLYGON_Disjoint_CIRCLE +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- CIRCLE +-- !sql -- +false + -- !sql -- false @@ -332,6 +621,7 @@ true -- !sql -- 45.0 +-- ST_AsText -- -- !sql -- LINESTRING (1 1, 2 2) @@ -356,6 +646,42 @@ POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0)) -- !sql -- POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0)) +-- !sql -- +MULTIPOLYGON (((5.2 0, 5.3 5.3, 0 4.9, 0 0, 5.2 0)), ((30 20.2, 30.6 30.4, 20.4 30.6, 20.1 20.3, 30 20.2)), ((60.5 50.5, 60.7 60.9, 50.1 60.7, 50.2 50.3, 60.5 50.5)), ((80 70, 80.3 80.2, 70.5 80.5, 70.3 70.1, 80 70))) + +-- !sql -- +MULTIPOLYGON (((8 5, 8 8, 5 8, 5 5, 8 5)), ((12 8, 12 12, 8 12, 8 8, 12 8))) + +-- !sql -- +\N + +-- !sql -- +\N + +-- !sql -- +MULTIPOLYGON (((10 -10, 10 10, -10 10, -10 -10, 10 -10)), ((30 20, 30 30, 20 30, 20 20, 30 20), (27 25, 27 27, 25 27, 25 25, 27 25))) + +-- !sql -- +MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0)), ((2 10, 5 15, 8 10, 10 15, 5 20, 0 15, 2 10))) + +-- !sql -- +MULTIPOLYGON (((0 0, 20 0, 20 20, 0 20, 0 0), (5 5, 15 5, 15 15, 5 15, 5 5)), ((5 10, 10 5, 15 10, 10 15, 5 10))) + +-- !sql -- +\N + +-- !sql -- +MULTIPOLYGON (((0 0, 4 1, 2 4, 0 0)), ((2 4, 5 6, 3 8, 2 4))) + +-- !sql -- +MULTIPOLYGON (((0 0, 1e-08 0, 1e-08 1e-08, 0 1e-08, 0 0))) + +-- !sql -- +MULTIPOLYGON (((0 0, 1e-06 0, 1e-06 1e-06, 0 1e-06, 0 0)), ((1e-05 0, 1 0, 1 1e-05, 1e-05 1e-05, 1e-05 0))) + +-- !sql -- +MULTIPOLYGON (((0 0, 1e-06 0, 1e-06 1e-06, 0 1e-06, 0 0), (1e-08 1e-08, 1e-07 1e-08, 1e-07 1e-07, 1e-08 1e-07, 1e-08 1e-08))) + -- !sql -- 1.0 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 b68254bd6510df..e8cc4ff96fc959 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 @@ -26,6 +26,10 @@ suite("test_gis_function") { qt_sql "SELECT ST_Contains(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Point(5, 5));" qt_sql "SELECT ST_Contains(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Point(50, 50));" + qt_sql "SELECT ST_Contains(ST_GeomFromText('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))'), ST_GeomFromText('POINT(2 10)'));" + qt_sql "SELECT ST_Contains(ST_GeomFromText(\"POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))\"),ST_GeomFromText(\"MULTIPOLYGON(((2 2, 4 2, 4 4, 2 4, 2 2)), ((6 6, 8 6, 8 8, 6 8, 6 6)))\"));" + qt_sql "SELECT ST_Contains(ST_GeomFromText(\"POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))\"),ST_GeomFromText(\"MULTIPOLYGON(((2 2, 2 8, 8 8, 8 2, 2 2)), ((10 10, 10 15, 15 15, 15 10, 10 10)))\"));" + qt_sql "SELECT ST_Intersects(ST_Point(0, 0), ST_Point(0, 0));" qt_sql "SELECT ST_Intersects(ST_Point(0, 0), ST_Point(5, 5));" @@ -58,6 +62,35 @@ suite("test_gis_function") { qt_sql "SELECT ST_Intersects(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Polygon(\"POLYGON ((11 0, 11 10, 21 10, 21 0, 11 0))\"));" qt_sql "SELECT ST_Intersects(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Polygon(\"POLYGON ((10 10, 20 10, 20 20, 10 20, 10 10))\"));" + qt_sql "SELECT ST_Intersects(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(0, 0));" + qt_sql "SELECT ST_Intersects(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(8, 8));" + qt_sql "SELECT ST_Intersects(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(5.000001, 0));" + qt_sql "SELECT ST_Intersects(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_GeometryFromText('POINT(5 3)'));" + qt_sql "SELECT ST_Intersects(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_GeometryFromText('POINT(5 5)'));" + + qt_sql "SELECT ST_Intersects(ST_GeometryFromText(\"MULTIPOLYGON(((0 0, 0 5, 5 5, 5 0, 0 0)), ((6 6, 6 10, 10 10, 10 6, 6 6)))\"), ST_LineFromText(\"LINESTRING (4 4, 7 7)\"));" + qt_sql "SELECT ST_Intersects(ST_GeometryFromText('MULTIPOLYGON(((0 0, 0 5, 5 5, 5 0, 0 0)), ((6 6, 6 10, 10 10, 10 6, 6 6)))'), ST_LineFromText('LINESTRING (5.000001 0, 5.999999 10)'));" + qt_sql "SELECT ST_Intersects(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_GeometryFromText('LINESTRING(3 3, 8 8)'));" + qt_sql "SELECT ST_Intersects(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_GeometryFromText('LINESTRING(4 3, 4 7)'));" + qt_sql "SELECT ST_Intersects(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_GeometryFromText('LINESTRING(3.000001 3.000001, 7.999999 7.999999)'));" + + qt_sql "SELECT ST_Intersects(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('POLYGON((12 12, 12 15, 15 15, 15 12, 12 12))'));" + qt_sql "SELECT ST_Intersects(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('POLYGON((3 3, 3 8, 8 8, 8 3, 3 3))'));" + qt_sql "SELECT ST_Intersects(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('POLYGON((5.000001 0, 5.999999 0, 5.999999 9.999999, 5.000001 9.999999, 5.000001 0))'));" + qt_sql "SELECT ST_Intersects(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_GeometryFromText('POLYGON((3.00001 3.00001, 7.99999 3.00001, 7.99999 7.99999, 3.00001 7.99999, 3.00001 3.00001))'));" + qt_sql "SELECT ST_Intersects(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_GeometryFromText('POLYGON((4 4, 6 4, 6 6, 4 6, 4 4))'));" + qt_sql "SELECT ST_Intersects(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_GeometryFromText('POLYGON((4 3, 7 3, 7 8, 4 8, 4 3))'));" + + qt_sql "SELECT ST_Intersects(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(((5 5, 5 8, 8 8, 8 5, 5 5)), ((8 8, 8 12, 12 12, 12 8, 8 8)))'));" + qt_sql "SELECT ST_Intersects(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(((2 2, 3 2, 3 3, 2 3, 2 2)), ((11 11, 11 12, 12 12, 12 11, 11 11)))'));" + qt_sql "SELECT ST_Intersects(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(((-2 -2, -3 -2, -3 -3, -2 -3, -2 -2)), ((11 11, 11 12, 12 12, 12 11, 11 11)))'));" + qt_sql "SELECT ST_Intersects(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(((5.000001 0, 6 0, 6 5.999999, 5.000001 5.999999, 5.000001 0)), ((0 5.000001, 5 5.000001, 5 6, 5.999999 6, 5.999999 10, 0 10, 0 5.000001)))'));" + + qt_sql "SELECT ST_Intersects(ST_GeometryFromText('MULTIPOLYGON(((0 0, 0 5, 5 5, 5 0, 0 0)), ((6 6, 6 10, 10 10, 10 6, 6 6)))'), ST_CIRCLE(2, 6, 1));" + qt_sql "SELECT ST_Intersects(ST_GeometryFromText('MULTIPOLYGON(((0 0, 0 5, 5 5, 5 0, 0 0)), ((6 6, 6 10, 10 10, 10 6, 6 6)))'), ST_CIRCLE(2, 6, 0.999999));" + qt_sql "SELECT ST_Intersects(ST_GeometryFromText('MULTIPOLYGON(((0 0, 0 5, 5 5, 5 0, 0 0)), ((6 6, 6 10, 10 10, 10 6, 6 6)))'), ST_CIRCLE(10, 10, 1));" + qt_sql "SELECT ST_Intersects(ST_GeometryFromText('MULTIPOLYGON(((0 0, 0 5, 5 5, 5 0, 0 0)), ((6 6, 6 10, 10 10, 10 6, 6 6)))'), ST_CIRCLE(5, 6, 0.999999));" + qt_sql "SELECT ST_Intersects(ST_Circle(1, 1, 1), ST_Point(2, 1));" qt_sql "SELECT ST_Intersects(ST_Circle(1, 1, 1), ST_Point(1, 1));" qt_sql "SELECT ST_Intersects(ST_Circle(1, 1, 1), ST_Point(3, 1));" @@ -98,8 +131,48 @@ suite("test_gis_function") { qt_sql "SELECT ST_Touches(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Polygon(\"POLYGON ((5 0, 15 0, 15 10, 5 10, 5 0))\"));" qt_sql "SELECT ST_Touches(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Polygon(\"POLYGON ((10 0, 10 10, 20 10, 20 0, 10 0))\"));" - qt_sql "SELECT ST_Touches(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Polygon(\"POLYGON ((11 0, 11 10, 21 10, 21 0, 11 0))\"));" - qt_sql "SELECT ST_Touches(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Polygon(\"POLYGON ((10 10, 20 10, 20 20, 10 20, 10 10))\"));" + qt_sql "SELECT ST_Touches(ST_Polygon('POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))'), ST_Polygon('POLYGON ((11 0, 11 10, 21 10, 21 0, 11 0))'));" + qt_sql "SELECT ST_Touches(ST_Polygon('POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))'), ST_Polygon('POLYGON ((10 10, 20 10, 20 20, 10 20, 10 10))'));" + qt_sql "SELECT ST_Touches(ST_GeomFromText('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))'),ST_GeomFromText('POLYGON((2 10, 5 15, 8 10, 10 15, 5 20, 0 15, 2 10))'));" + + qt_sql "SELECT ST_Touches(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(0, 3));" + qt_sql "SELECT ST_Touches(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(5, 5));" + qt_sql "SELECT ST_Touches(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(5, 6));" + qt_sql "SELECT ST_Touches(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(3, 3));" + qt_sql "SELECT ST_Touches(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(5.00001, 0));" + + qt_sql "SELECT ST_Touches(ST_GeometryFromText('MULTIPOLYGON(((0 0, 0 5, 5 5, 5 0, 0 0)), ((6 6, 6 10, 10 10, 10 6, 6 6)))'), ST_LineFromText('LINESTRING (5 5, 7 7)'));" + qt_sql "SELECT ST_Touches(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('LINESTRING (5 5, 5 10)'));" + qt_sql "SELECT ST_Touches(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('LINESTRING (0 3, 5 3)'));" + qt_sql "SELECT ST_Touches(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('LINESTRING (5.5 0, 5.5 10)'));" + qt_sql "SELECT ST_Touches(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('LINESTRING (5.000001 0, 5.999999 10)'));" + qt_sql "SELECT ST_Touches(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_GeometryFromText('LINESTRING(3 3, 8 8)'));" + qt_sql "SELECT ST_Touches(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_GeometryFromText('LINESTRING(4 4, 4 7)'));" + qt_sql "SELECT ST_Touches(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_GeometryFromText('LINESTRING(3.000001 3.000001, 7.999999 7.999999)'));" + + qt_sql "SELECT ST_Touches(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('POLYGON((5 5, 5 8, 8 8, 8 5, 5 5))'));" + qt_sql "SELECT ST_Touches(ST_GeometryFromText('MULTIPOLYGON(((0 0, 0 5, 5 5, 5 0, 0 0)))'), ST_GeometryFromText('POLYGON((5 5, 5 8, 8 8, 8 5, 5 5))'));" + qt_sql "SELECT ST_Touches(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('POLYGON((12 12, 12 15, 15 15, 15 12, 12 12))'));" + qt_sql "SELECT ST_Touches(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('POLYGON((0 0, 0 4, 4 4, 4 0, 0 0))'));" + qt_sql "SELECT ST_Touches(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('POLYGON((0 0, 0 -4, -4 -4, -4 0, 0 0))'));" + qt_sql "SELECT ST_Touches(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('POLYGON((5.000001 0, 6 0, 6 5.999999, 5.000001 5.999999, 5.000001 0))'));" + qt_sql "SELECT ST_Touches(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_GeometryFromText('POLYGON((3.00001 3.00001, 7.99999 3.00001, 7.99999 7.99999, 3.00001 7.99999, 3.00001 3.00001))'));" + qt_sql "SELECT ST_Touches(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_GeometryFromText('POLYGON((4 4, 6 4, 6 6, 4 6, 4 4))'));" + qt_sql "SELECT ST_Touches(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_GeometryFromText('POLYGON((3 4, 8 4, 8 7, 3 7, 3 4))'));" + + qt_sql "SELECT ST_Touches(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0)), ((15 0, 25 0, 25 10, 15 10, 15 0)), ((30 30, 35 35, 40 30, 30 30)))'),ST_GeometryFromText('MULTIPOLYGON (((10 0, 20 0, 20 10, 10 10, 10 0)))'));" + qt_sql "SELECT ST_Touches(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_GeometryFromText('MULTIPOLYGON(((3 4, 8 4, 8 7, 3 7, 3 4)), ((-8 -8, -8 -12, -12 -12, -12 -8, -8 -8)))'));" + qt_sql "SELECT ST_Touches(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_GeometryFromText('MULTIPOLYGON(((10.00001 0, 14.99999 0, 14.99999 10, 10.00001 10, 10.00001 0)), ((-8 -8, -8 -12, -12 -12, -12 -8, -8 -8)))'));" + qt_sql "SELECT ST_Touches(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_GeometryFromText('MULTIPOLYGON(((3 3, 8 3, 8 8, 3 8, 3 3)), ((8 8, 8 12, 12 12, 12 8, 8 8)))'));" + + qt_sql "SELECT ST_Touches(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_Circle(10, 15, 5));" + qt_sql "SELECT ST_Touches(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_Circle(10, 15, 5.00001));" + qt_sql "SELECT ST_Touches(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_Circle(3, 5, 2));" + qt_sql "SELECT ST_Touches(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_Circle(4.5, 5, 1.5));" + qt_sql "SELECT ST_Touches(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_Circle(20, 20, 1));" + qt_sql "SELECT ST_Touches(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_Circle(5.5, 5.5, 0.5));" + qt_sql "SELECT ST_Touches(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_Circle(1, 1, 0.5));" + qt_sql "SELECT ST_Touches(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_Circle(8, 8, 2));" qt_sql "SELECT ST_Touches(ST_Circle(1, 1, 1), ST_Point(2, 1));" qt_sql "SELECT ST_Touches(ST_Circle(1, 1, 1), ST_Point(1, 2));" @@ -143,6 +216,35 @@ suite("test_gis_function") { qt_sql "SELECT ST_Disjoint(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Polygon(\"POLYGON ((11 0, 11 10, 21 10, 21 0, 11 0))\"));" qt_sql "SELECT ST_Disjoint(ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"), ST_Polygon(\"POLYGON ((10 10, 20 10, 20 20, 10 20, 10 10))\"));" + qt_sql "SELECT ST_Disjoint(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(0, 0));" + qt_sql "SELECT ST_Disjoint(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(8, 8));" + qt_sql "SELECT ST_Disjoint(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(5.000001, 0));" + qt_sql "SELECT ST_Disjoint(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_GeometryFromText('POINT(5 3)'));" + qt_sql "SELECT ST_Disjoint(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_GeometryFromText('POINT(5 5)'));" + + qt_sql "SELECT ST_Disjoint(ST_GeometryFromText(\"MULTIPOLYGON(((0 0, 0 5, 5 5, 5 0, 0 0)), ((6 6, 6 10, 10 10, 10 6, 6 6)))\"), ST_LineFromText(\"LINESTRING (4 4, 7 7)\"));" + qt_sql "SELECT ST_Disjoint(ST_GeometryFromText('MULTIPOLYGON(((0 0, 0 5, 5 5, 5 0, 0 0)), ((6 6, 6 10, 10 10, 10 6, 6 6)))'), ST_LineFromText('LINESTRING (5.000001 0, 5.999999 10)'));" + qt_sql "SELECT ST_Disjoint(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_GeometryFromText('LINESTRING(3 3, 8 8)'));" + qt_sql "SELECT ST_Disjoint(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_GeometryFromText('LINESTRING(4 3, 4 7)'));" + qt_sql "SELECT ST_Disjoint(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_GeometryFromText('LINESTRING(3.000001 3.000001, 7.999999 7.999999)'));" + + qt_sql "SELECT ST_Disjoint(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('POLYGON((12 12, 12 15, 15 15, 15 12, 12 12))'));" + qt_sql "SELECT ST_Disjoint(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('POLYGON((3 3, 3 8, 8 8, 8 3, 3 3))'));" + qt_sql "SELECT ST_Disjoint(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('POLYGON((5.000001 0, 5.999999 0, 5.999999 9.999999, 5.000001 9.999999, 5.000001 0))'));" + qt_sql "SELECT ST_Disjoint(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_GeometryFromText('POLYGON((3.00001 3.00001, 7.99999 3.00001, 7.99999 7.99999, 3.00001 7.99999, 3.00001 3.00001))'));" + qt_sql "SELECT ST_Disjoint(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_GeometryFromText('POLYGON((4 4, 6 4, 6 6, 4 6, 4 4))'));" + qt_sql "SELECT ST_Disjoint(ST_GeometryFromText('MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3)),((15 0, 25 0, 25 10, 15 10, 15 0)),((30 30, 40 30, 35 35, 30 30)))'), ST_GeometryFromText('POLYGON((4 3, 7 3, 7 8, 4 8, 4 3))'));" + + qt_sql "SELECT ST_Disjoint(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(((5 5, 5 8, 8 8, 8 5, 5 5)), ((8 8, 8 12, 12 12, 12 8, 8 8)))'));" + qt_sql "SELECT ST_Disjoint(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(((2 2, 3 2, 3 3, 2 3, 2 2)), ((11 11, 11 12, 12 12, 12 11, 11 11)))'));" + qt_sql "SELECT ST_Disjoint(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(((-2 -2, -3 -2, -3 -3, -2 -3, -2 -2)), ((11 11, 11 12, 12 12, 12 11, 11 11)))'));" + qt_sql "SELECT ST_Disjoint(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(((5.000001 0, 6 0, 6 5.999999, 5.000001 5.999999, 5.000001 0)), ((0 5.000001, 5 5.000001, 5 6, 5.999999 6, 5.999999 10, 0 10, 0 5.000001)))'));" + + qt_sql "SELECT ST_Disjoint(ST_GeometryFromText('MULTIPOLYGON(((0 0, 0 5, 5 5, 5 0, 0 0)), ((6 6, 6 10, 10 10, 10 6, 6 6)))'), ST_CIRCLE(2, 6, 1));" + qt_sql "SELECT ST_Disjoint(ST_GeometryFromText('MULTIPOLYGON(((0 0, 0 5, 5 5, 5 0, 0 0)), ((6 6, 6 10, 10 10, 10 6, 6 6)))'), ST_CIRCLE(2, 6, 0.999999));" + qt_sql "SELECT ST_Disjoint(ST_GeometryFromText('MULTIPOLYGON(((0 0, 0 5, 5 5, 5 0, 0 0)), ((6 6, 6 10, 10 10, 10 6, 6 6)))'), ST_CIRCLE(10, 10, 1));" + qt_sql "SELECT ST_Disjoint(ST_GeometryFromText('MULTIPOLYGON(((0 0, 0 5, 5 5, 5 0, 0 0)), ((6 6, 6 10, 10 10, 10 6, 6 6)))'), ST_CIRCLE(5, 6, 0.999999));" + qt_sql "SELECT ST_Disjoint(ST_Circle(1, 1, 1), ST_Point(2, 1));" qt_sql "SELECT ST_Disjoint(ST_Circle(1, 1, 1), ST_Point(1, 1));" qt_sql "SELECT ST_Disjoint(ST_Circle(1, 1, 1), ST_Point(3, 1));" @@ -176,6 +278,19 @@ suite("test_gis_function") { qt_sql "SELECT ST_AsText(ST_PolyFromText(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"));" qt_sql "SELECT ST_AsText(ST_PolygonFromText(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"));" + qt_sql "SELECT ST_AsText(ST_GeometryFromText(\"MULTIPOLYGON (((0.0 0.0, 0.0 4.9, 5.3 5.3, 5.2 0.0, 0.0 0.0)), ((20.1 20.3, 20.4 30.6, 30.6 30.4, 30.0 20.2, 20.1 20.3)), ((50.2 50.3, 50.1 60.7, 60.7 60.9, 60.5 50.5, 50.2 50.3)), ((70.3 70.1, 70.5 80.5, 80.3 80.2, 80.0 70.0, 70.3 70.1)))\"));" + qt_sql "SELECT ST_AsText(ST_GeometryFromText('MULTIPOLYGON(((5 5, 5 8, 8 8, 8 5, 5 5)), ((8 8, 8 12, 12 12, 12 8, 8 8)))'));" + qt_sql "SELECT ST_AsText(ST_GeometryFromText('MULTIPOLYGON(((5 5, 5 8, 8 8, 8 5, 5 5)), ((8 6, 10 6, 10 10, 8 10, 8 6)))'));" + qt_sql "SELECT ST_AsText(ST_GeometryFromText(\"MULTIPOLYGON (((0 0, 0 10, 10 10, 10 0, 0 0)), ((5 5, 5 15, 15 15, 15 5, 5 5)))\"));" + qt_sql "SELECT ST_AsText(ST_GeomFromText('MULTIPOLYGON (((-10 -10, -10 10, 10 10, 10 -10, -10 -10)), ((20 20, 20 30, 30 30, 30 20, 20 20), (25 25, 25 27, 27 27, 27 25, 25 25)))'));" + qt_sql "SELECT ST_AsText(ST_GeometryFromText('MULTIPOLYGON(((0 0, 10 0, 10 10, 0 10, 0 0)), ((2 10, 5 15, 8 10, 10 15, 5 20, 0 15, 2 10)))'));" + qt_sql "SELECT ST_AsText(ST_GeomFromText('MULTIPOLYGON (((0 0, 20 0, 20 20, 0 20, 0 0), (5 5, 15 5, 15 15, 5 15, 5 5)), ((5 10, 10 5, 15 10, 10 15, 5 10)))'));" + qt_sql "SELECT ST_AsText(ST_GeomFromText('MULTIPOLYGON(((0 0, 0 10, 10 10, 10 0, 0 0), (3 3, 7 3, 7 7, 3 7, 3 3)), ((3 3, 3 7, 7 7, 7 3, 3 3)))'));" + qt_sql "SELECT ST_AsText(ST_GeomFromText('MULTIPOLYGON(((0 0, 4 1, 2 4, 0 0)), ((2 4, 5 6, 3 8, 2 4)))'));" + qt_sql "SELECT ST_AsText(ST_GeomFromText('MULTIPOLYGON(((0 0, 1e-8 0, 1e-8 1e-8, 0 1e-8, 0 0)))'));" + qt_sql "SELECT ST_AsText(ST_GeomFromText('MULTIPOLYGON(((0 0, 1e-6 0, 1e-6 1e-6, 0 1e-6, 0 0)), ((1e-5 0, 1 0, 1 1e-5, 1e-5 1e-5, 1e-5 0)))'));" + qt_sql "SELECT ST_AsText(ST_GeomFromText('MULTIPOLYGON(((0 0, 1e-6 0, 1e-6 1e-6, 0 1e-6, 0 0), (1e-8 1e-8, 1e-7 1e-8, 1e-7 1e-7, 1e-8 1e-7, 1e-8 1e-8)))'));" + qt_sql "SELECT ST_X(ST_Point(1, 2));" qt_sql "SELECT ST_X(ST_Point(24.7, 56.7));" qt_sql "SELECT ST_Y(ST_Point(2, 1));"