From 1ddd011a7775eb213b926a2defa0d16bc2af7c95 Mon Sep 17 00:00:00 2001 From: koi Date: Mon, 7 Apr 2025 12:04:55 +0800 Subject: [PATCH 1/2] [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 6d75258d146ff7..38b8fc31019297 100644 --- a/be/src/vec/functions/functions_geo.cpp +++ b/be/src/vec/functions/functions_geo.cpp @@ -626,11 +626,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); @@ -642,8 +644,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(); @@ -660,55 +661,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); } } @@ -719,7 +715,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"; @@ -914,7 +930,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 3d9e12398f20b4..09bbd0bbf089c3 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 @@ -393,17 +393,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; @@ -873,6 +876,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 75c4a1669199f1..28c92423f27807 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 @@ -391,17 +391,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; @@ -1909,6 +1912,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 df93348581bd64..cf0146b0493e08 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 8c384f51ff7048..4ff443ad4fa521 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 f07aa27d71694859efe4d649068b25c3b47fd78e 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 2/2] [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 02dce99e967bdb..160853050cd540 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 cf0146b0493e08..f1169aa348644f 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 4ff443ad4fa521..d2c8d1006b87f2 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));"