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);"