diff --git a/be/src/exprs/function/geo/functions_geo.cpp b/be/src/exprs/function/geo/functions_geo.cpp index b4967780d2c8dc..90d12d32ceadee 100644 --- a/be/src/exprs/function/geo/functions_geo.cpp +++ b/be/src/exprs/function/geo/functions_geo.cpp @@ -558,6 +558,11 @@ struct StTouchesFunc { static bool evaluate(GeoShape* shape1, GeoShape* shape2) { return shape1->touches(shape2); } }; +struct StWithinFunc { + static constexpr auto NAME = "st_within"; + static bool evaluate(GeoShape* shape1, GeoShape* shape2) { return shape1->within(shape2); } +}; + struct StGeometryFromText { static constexpr auto NAME = "st_geometryfromtext"; static constexpr GeoShapeType shape_type = GEO_SHAPE_ANY; @@ -1092,6 +1097,7 @@ 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>>(); diff --git a/be/src/exprs/function/geo/geo_types.h b/be/src/exprs/function/geo/geo_types.h index 146ebf2a847603..c971fc9b61ddf9 100644 --- a/be/src/exprs/function/geo/geo_types.h +++ b/be/src/exprs/function/geo/geo_types.h @@ -70,6 +70,8 @@ class GeoShape { virtual bool touches(const GeoShape* rhs) const { return false; } + virtual bool within(const GeoShape* rhs) const { return rhs->contains(this); } + virtual std::string GeometryType() const = 0; virtual double Length() const { return 0.0; } diff --git a/be/test/exprs/function/geo/geo_types_test.cpp b/be/test/exprs/function/geo/geo_types_test.cpp index 580218f4058e71..abc274ccca1f75 100644 --- a/be/test/exprs/function/geo/geo_types_test.cpp +++ b/be/test/exprs/function/geo/geo_types_test.cpp @@ -1978,6 +1978,220 @@ TEST_F(GeoTypesTest, polygon_hole_contains) { } } +TEST_F(GeoTypesTest, within) { + GeoParseStatus status; + + // ============================================================ + // GeoPoint.within(X) + // ============================================================ + { + // Point within simple Polygon + const char* wkt = "POLYGON ((10 10, 50 10, 50 50, 10 50, 10 10))"; + auto polygon(GeoShape::from_wkt(wkt, strlen(wkt), status)); + EXPECT_NE(nullptr, polygon.get()); + + GeoPoint point; + point.from_coord(20, 20); + EXPECT_TRUE(point.within(polygon.get())); + + point.from_coord(5, 5); + EXPECT_FALSE(point.within(polygon.get())); + + point.from_coord(50, 50); + EXPECT_FALSE(point.within(polygon.get())); + } + { + // Point within Polygon with hole + const char* wkt = + "POLYGON ((10 10, 50 10, 50 50, 10 50, 10 10), (20 20, 40 20, 40 40, 20 40, 20 " + "20))"; + auto polygon(GeoShape::from_wkt(wkt, strlen(wkt), status)); + EXPECT_EQ(GEO_PARSE_OK, status); + EXPECT_NE(nullptr, polygon.get()); + + GeoPoint point; + point.from_coord(15, 15); + EXPECT_TRUE(point.within(polygon.get())); + + point.from_coord(30, 30); + EXPECT_FALSE(point.within(polygon.get())); + + point.from_coord(20, 20); + EXPECT_FALSE(point.within(polygon.get())); + } + { + // Point within MultiPolygon + const char* wkt = + "MULTIPOLYGON(((0 0, 0 10, 10 10, 10 0, 0 0)), ((15 5, 15 10, 20 10, 20 5, 15 5)))"; + auto multipolygon(GeoShape::from_wkt(wkt, strlen(wkt), status)); + EXPECT_NE(nullptr, multipolygon.get()); + + GeoPoint point; + point.from_coord(5, 5); + EXPECT_TRUE(point.within(multipolygon.get())); + + point.from_coord(17, 7); + EXPECT_TRUE(point.within(multipolygon.get())); + + point.from_coord(12, 5); + EXPECT_FALSE(point.within(multipolygon.get())); + } + { + // Point within LineString (LineString does not implement contains) + const char* wkt = "LINESTRING (0 0, 10 0)"; + auto line(GeoShape::from_wkt(wkt, strlen(wkt), status)); + EXPECT_NE(nullptr, line.get()); + + GeoPoint point; + point.from_coord(5, 0); + EXPECT_FALSE(point.within(line.get())); + } + { + // Point within Circle + GeoCircle circle; + circle.init(0, 0, 1000000); + + GeoPoint point; + point.from_coord(1, 1); + EXPECT_TRUE(point.within(&circle)); + + point.from_coord(90, 0); + EXPECT_FALSE(point.within(&circle)); + } + + // ============================================================ + // GeoLine.within(X) + // ============================================================ + { + // LineString within Polygon + const char* wkt_polygon = "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))"; + auto polygon(GeoShape::from_wkt(wkt_polygon, strlen(wkt_polygon), status)); + EXPECT_NE(nullptr, polygon.get()); + + const char* wkt = "LINESTRING (2 5, 8 5)"; + auto line(GeoShape::from_wkt(wkt, strlen(wkt), status)); + EXPECT_EQ(GEO_PARSE_OK, status); + EXPECT_TRUE(line->within(polygon.get())); + } + { + // LineString within MultiPolygon + const char* wkt = + "MULTIPOLYGON(((0 0, 0 10, 10 10, 10 0, 0 0)), ((15 5, 15 10, 20 10, 20 5, 15 5)))"; + auto multipolygon(GeoShape::from_wkt(wkt, strlen(wkt), status)); + EXPECT_NE(nullptr, multipolygon.get()); + + const char* wkt_line = "LINESTRING(2 2, 8 2)"; + auto line(GeoShape::from_wkt(wkt_line, strlen(wkt_line), status)); + EXPECT_EQ(GEO_PARSE_OK, status); + EXPECT_TRUE(line->within(multipolygon.get())); + + wkt_line = "LINESTRING(16 6, 19 9)"; + auto line2(GeoShape::from_wkt(wkt_line, strlen(wkt_line), status)); + EXPECT_EQ(GEO_PARSE_OK, status); + EXPECT_TRUE(line2->within(multipolygon.get())); + + wkt_line = "LINESTRING(5 5, 16 7)"; + auto line3(GeoShape::from_wkt(wkt_line, strlen(wkt_line), status)); + EXPECT_EQ(GEO_PARSE_OK, status); + EXPECT_FALSE(line3->within(multipolygon.get())); + + wkt_line = "LINESTRING(0 0, 0 10)"; + auto line4(GeoShape::from_wkt(wkt_line, strlen(wkt_line), status)); + EXPECT_EQ(GEO_PARSE_OK, status); + EXPECT_FALSE(line4->within(multipolygon.get())); + } + + // ============================================================ + // GeoPolygon.within(X) + // ============================================================ + { + // Polygon within Polygon + const char* wkt_outer = "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))"; + auto outer(GeoShape::from_wkt(wkt_outer, strlen(wkt_outer), status)); + EXPECT_NE(nullptr, outer.get()); + + const char* wkt = "POLYGON ((3 3, 7 3, 7 7, 3 7, 3 3))"; + auto inner(GeoShape::from_wkt(wkt, strlen(wkt), status)); + EXPECT_EQ(GEO_PARSE_OK, status); + EXPECT_TRUE(inner->within(outer.get())); + + wkt = "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))"; + auto overlapping(GeoShape::from_wkt(wkt, strlen(wkt), status)); + EXPECT_EQ(GEO_PARSE_OK, status); + EXPECT_FALSE(overlapping->within(outer.get())); + + wkt = "POLYGON ((20 20, 30 20, 30 30, 20 30, 20 20))"; + auto separate(GeoShape::from_wkt(wkt, strlen(wkt), status)); + EXPECT_EQ(GEO_PARSE_OK, status); + EXPECT_FALSE(separate->within(outer.get())); + } + { + // Polygon within MultiPolygon + const char* wkt = + "MULTIPOLYGON(((0 0, 0 10, 10 10, 10 0, 0 0)), ((15 5, 15 10, 20 10, 20 5, 15 5)))"; + auto multipolygon(GeoShape::from_wkt(wkt, strlen(wkt), status)); + EXPECT_NE(nullptr, multipolygon.get()); + + const char* wkt_inner = "POLYGON((1 1, 1 9, 9 9, 9 1, 1 1))"; + auto inner(GeoShape::from_wkt(wkt_inner, strlen(wkt_inner), status)); + EXPECT_EQ(GEO_PARSE_OK, status); + EXPECT_TRUE(inner->within(multipolygon.get())); + + wkt_inner = "POLYGON((16 6, 16 9, 19 9, 19 6, 16 6))"; + auto inner2(GeoShape::from_wkt(wkt_inner, strlen(wkt_inner), status)); + EXPECT_EQ(GEO_PARSE_OK, status); + EXPECT_TRUE(inner2->within(multipolygon.get())); + + wkt_inner = "POLYGON((5 5, 5 15, 15 15, 15 5, 5 5))"; + auto inner3(GeoShape::from_wkt(wkt_inner, strlen(wkt_inner), status)); + EXPECT_EQ(GEO_PARSE_OK, status); + EXPECT_FALSE(inner3->within(multipolygon.get())); + } + + // ============================================================ + // MultiPolygon.within(X) + // ============================================================ + { + // MultiPolygon within Polygon + const char* wkt_outer = "POLYGON ((0 0, 20 0, 20 20, 0 20, 0 0))"; + auto outer(GeoShape::from_wkt(wkt_outer, strlen(wkt_outer), status)); + EXPECT_NE(nullptr, outer.get()); + + const char* wkt = "MULTIPOLYGON(((2 2, 4 2, 4 4, 2 4, 2 2)), ((6 6, 8 6, 8 8, 6 8, 6 6)))"; + auto inner(GeoShape::from_wkt(wkt, strlen(wkt), status)); + EXPECT_EQ(GEO_PARSE_OK, status); + EXPECT_TRUE(inner->within(outer.get())); + + wkt = "MULTIPOLYGON(((2 2, 4 2, 4 4, 2 4, 2 2)), ((15 15, 25 15, 25 25, 15 25, 15 15)))"; + auto partial(GeoShape::from_wkt(wkt, strlen(wkt), status)); + EXPECT_EQ(GEO_PARSE_OK, status); + EXPECT_FALSE(partial->within(outer.get())); + } + { + // MultiPolygon within MultiPolygon + const char* wkt_outer = + "MULTIPOLYGON(((0 0, 0 10, 10 10, 10 0, 0 0)), ((15 5, 15 10, 20 10, 20 5, 15 5)))"; + auto outer(GeoShape::from_wkt(wkt_outer, strlen(wkt_outer), status)); + EXPECT_NE(nullptr, outer.get()); + + const char* wkt = + "MULTIPOLYGON(((1 1, 1 9, 9 9, 9 1, 1 1)), ((16 6, 16 9, 19 9, 19 6, 16 6)))"; + auto inner(GeoShape::from_wkt(wkt, strlen(wkt), status)); + EXPECT_EQ(GEO_PARSE_OK, status); + EXPECT_TRUE(inner->within(outer.get())); + + wkt = "MULTIPOLYGON(((1 1, 1 5, 5 5, 5 1, 1 1)), ((12 6, 12 9, 14 9, 14 6, 12 6)))"; + auto inner2(GeoShape::from_wkt(wkt, strlen(wkt), status)); + EXPECT_EQ(GEO_PARSE_OK, status); + EXPECT_FALSE(inner2->within(outer.get())); + + wkt = "MULTIPOLYGON(((1 1, 1 4, 4 4, 4 1, 1 1)), ((5 5, 5 11, 12 11, 12 5, 5 5)))"; + auto inner3(GeoShape::from_wkt(wkt, strlen(wkt), status)); + EXPECT_EQ(GEO_PARSE_OK, status); + EXPECT_FALSE(inner3->within(outer.get())); + } +} + TEST_F(GeoTypesTest, multipolygon_parse_fail) { { const char* wkt = "MULTIPOLYGON (((10 10, 50 10, 50 50, 10 50), (10 10 01)))"; 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 3b1171a376ccf8..736c9e77edbedd 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 @@ -501,6 +501,7 @@ 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.StWithin; 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; @@ -1069,6 +1070,7 @@ public class BuiltinScalarFunctions implements FunctionHelper { scalar(StIntersects.class, "st_intersects"), scalar(StDisjoint.class, "st_disjoint"), scalar(StTouches.class, "st_touches"), + scalar(StWithin.class, "st_within"), scalar(StLength.class, "st_length"), scalar(StGeometryType.class, "st_geometrytype"), scalar(StNumGeometries.class, "st_numgeometries"), diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StWithin.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StWithin.java new file mode 100644 index 00000000000000..d3630beced8d36 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StWithin.java @@ -0,0 +1,77 @@ +// 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_within'. This class is generated by GenerateFunction. + */ +public class StWithin 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 StWithin(Expression arg0, Expression arg1) { + super("st_within", arg0, arg1); + } + + /** constructor for withChildren and reuse signature */ + private StWithin(ScalarFunctionParams functionParams) { + super(functionParams); + } + + /** + * withChildren. + */ + @Override + public StWithin withChildren(List children) { + Preconditions.checkArgument(children.size() == 2); + return new StWithin(getFunctionParams(children)); + } + + @Override + public List getSignatures() { + return SIGNATURES; + } + + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitStWithin(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 7b1724ae085497..902b6c74fd199a 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 @@ -521,6 +521,7 @@ 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.StWithin; 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; @@ -2403,6 +2404,10 @@ default R visitStTouches(StTouches stTouches, C context) { return visitScalarFunction(stTouches, context); } + default R visitStWithin(StWithin stWithin, C context) { + return visitScalarFunction(stWithin, context); + } + default R visitStLength(StLength stLength, C context) { return visitScalarFunction(stLength, context); } 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 c5404d8381cae1..acf0c020518642 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 @@ -182,6 +182,90 @@ true -- !sql -- false +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +false + +-- !sql -- +true + +-- !sql -- +false + -- !sql -- true 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 35bc505604dc08..1f68796e6290ce 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 @@ -86,6 +86,37 @@ suite("test_gis_function", "arrow_flight_sql") { qt_sql "SELECT ST_Contains(ST_GeomFromText('MULTIPOLYGON(((0 0, 0 10, 10 10, 10 0, 0 0)), ((15 5, 15 10, 20 10, 20 5, 15 5)))'), ST_GeomFromText('MULTIPOLYGON(((-0.000001 -0.000001, -0.000001 10.000001, 10.000001 10.000001, 10.000001 -0.000001, -0.000001 -0.000001)), ((15 5, 15 10, 20 10, 20 5, 15 5)))'));" qt_sql "SELECT ST_Contains(ST_GeomFromText('MULTIPOLYGON(((0 0, 0 10, 10 10, 10 0, 0 0), (3 3, 3 7, 7 7, 7 3, 3 3)), ((15 5, 15 10, 20 10, 20 5, 15 5)))'), ST_GeomFromText('MULTIPOLYGON(((1 1, 1 9, 9 9, 9 1, 1 1), (4 4, 4 6, 6 6, 6 4, 4 4)), ((16 6, 16 9, 19 9, 19 6, 16 6)))'));" + qt_sql "SELECT ST_Within(ST_Point(5, 5), ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"));" + qt_sql "SELECT ST_Within(ST_Point(50, 50), ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"));" + qt_sql "SELECT ST_Within(ST_GeomFromText('POINT(2 10)'), ST_GeomFromText('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))'));" + + qt_sql "SELECT ST_Within(ST_GeomFromText('LINESTRING(2 5, 8 5)'), ST_GeomFromText('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))'));" + qt_sql "SELECT ST_Within(ST_GeomFromText('LINESTRING(2 0, 8 0)'), ST_GeomFromText('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))'));" + qt_sql "SELECT ST_Within(ST_GeomFromText('LINESTRING(0 0, 10 0)'), ST_GeomFromText('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))'));" + qt_sql "SELECT ST_Within(ST_GeomFromText('LINESTRING(0 0, 10 10)'), ST_GeomFromText('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))'));" + qt_sql "SELECT ST_Within(ST_GeomFromText('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))'), ST_GeomFromText('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))'));" + qt_sql "SELECT ST_Within(ST_GeomFromText('POLYGON((3 3, 3 7, 7 7, 7 3, 3 3))'), ST_GeomFromText('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))'));" + qt_sql "SELECT ST_Within(ST_GeomFromText('POLYGON((10 10, 20 10, 20 20, 10 20, 10 10))'), ST_GeomFromText('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))'));" + + qt_sql "SELECT ST_Within(ST_GeomFromText('POLYGON((4 4, 7 4, 7 7, 4 7, 4 4))'), ST_GeomFromText('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3))'));" + qt_sql "SELECT ST_Within(ST_GeomFromText('POLYGON((1 1, 9 1, 9 9, 1 9, 1 1))'), ST_GeomFromText('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 8 3, 8 8, 3 8, 3 3))'));" + qt_sql "SELECT ST_Within(ST_GeomFromText('MULTIPOLYGON(((2 2, 4 2, 4 4, 2 4, 2 2)), ((6 6, 8 6, 8 8, 6 8, 6 6)))'), ST_GeomFromText('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))'));" + qt_sql "SELECT ST_Within(ST_GeomFromText('MULTIPOLYGON(((2 2, 2 8, 8 8, 8 2, 2 2)), ((10 10, 10 15, 15 15, 15 10, 10 10)))'), ST_GeomFromText('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))'));" + qt_sql "SELECT ST_Within(ST_Point(5, 5), ST_GeomFromText('MULTIPOLYGON(((0 0, 0 10, 10 10, 10 0, 0 0)), ((15 5, 15 10, 20 10, 20 5, 15 5)))'));" + qt_sql "SELECT ST_Within(ST_Point(17, 7), ST_GeomFromText('MULTIPOLYGON(((0 0, 0 10, 10 10, 10 0, 0 0)), ((15 5, 15 10, 20 10, 20 5, 15 5)))'));" + qt_sql "SELECT ST_Within(ST_Point(12, 7), ST_GeomFromText('MULTIPOLYGON(((0 0, 0 10, 10 10, 10 0, 0 0)), ((15 5, 15 10, 20 10, 20 5, 15 5)))'));" + qt_sql "SELECT ST_Within(ST_GeomFromText('LINESTRING(2 2, 8 8)'), ST_GeomFromText('MULTIPOLYGON(((0 0, 0 10, 10 10, 10 0, 0 0)), ((15 5, 15 10, 20 10, 20 5, 15 5)))'));" + qt_sql "SELECT ST_Within(ST_GeomFromText('LINESTRING(5 5, 16 6)'), ST_GeomFromText('MULTIPOLYGON(((0 0, 0 10, 10 10, 10 0, 0 0)), ((15 5, 15 10, 20 10, 20 5, 15 5)))'));" + qt_sql "SELECT ST_Within(ST_GeomFromText('POLYGON((1 1, 1 9, 9 9, 9 1, 1 1))'), ST_GeomFromText('MULTIPOLYGON(((0 0, 0 10, 10 10, 10 0, 0 0)), ((15 5, 15 10, 20 10, 20 5, 15 5)))'));" + qt_sql "SELECT ST_Within(ST_GeomFromText('POLYGON((16 6, 16 9, 19 9, 19 6, 16 6))'), ST_GeomFromText('MULTIPOLYGON(((0 0, 0 10, 10 10, 10 0, 0 0)), ((15 5, 15 10, 20 10, 20 5, 15 5)))'));" + qt_sql "SELECT ST_Within(ST_GeomFromText('POLYGON((5 5, 5 15, 15 15, 15 5, 5 5))'), ST_GeomFromText('MULTIPOLYGON(((0 0, 0 10, 10 10, 10 0, 0 0)), ((15 5, 15 10, 20 10, 20 5, 15 5)))'));" + qt_sql "SELECT ST_Within(ST_GeomFromText('MULTIPOLYGON(((1 1, 1 5, 5 5, 5 1, 1 1)), ((16 6, 16 9, 19 9, 19 6, 16 6)))'), ST_GeomFromText('MULTIPOLYGON(((0 0, 0 10, 10 10, 10 0, 0 0)), ((15 5, 15 10, 20 10, 20 5, 15 5)))'));" + qt_sql "SELECT ST_Within(ST_GeomFromText('MULTIPOLYGON(((1 1, 1 5, 5 5, 5 1, 1 1)), ((12 6, 12 9, 14 9, 14 6, 12 6)))'), ST_GeomFromText('MULTIPOLYGON(((0 0, 0 10, 10 10, 10 0, 0 0)), ((15 5, 15 10, 20 10, 20 5, 15 5)))'));" + qt_sql "SELECT ST_Within(ST_Point(0.5, 0.5), ST_Circle(0, 0, 1000000));" + qt_sql "SELECT ST_Within(ST_Point(90, 0), ST_Circle(0, 0, 1000000));" + qt_sql "SELECT ST_Within(ST_Point(0, 0), ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"));" + qt_sql "SELECT ST_Within(ST_Point(10, 10), ST_Polygon(\"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))\"));" + 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));"