MDEV-34137: Implement the GIS function ST_Validate
The GIS function ST_Validate takes ad input a geometry and verifies that - is compliant with the Well-Known Binary (WKB) format and Spatial Reference System Identifier (SRID) syntax. - is geometrically valid. If the input is valid return it, else it returns NULL. The use case of this function is to filter out invalid geometry data. Author: StefanoPetrilli <stefanop_1999@hotmail.it> Co-authored-by: Torje Digernes <torje.digernes@oracle.com> Co-authored-by: Hans H Melby <hans.h.melby@oracle.com> Co-authored-by: Jon Olav Hauglid <jon.hauglid@oracle.com> Co-authored-by: Erlend Dahl <erlend.dahl@oracle.com> Co-authored-by: Norvald H. Ryeng <norvald.ryeng@oracle.com> Co-authored-by: David.Zhao <david.zhao@oracle.com> Co-authored-by: Pavan <pavan.naik@oracle.com>
This commit is contained in:
parent
869b4c243e
commit
b50366667b
139
mysql-test/main/spatial_utility_function_validate.result
Normal file
139
mysql-test/main/spatial_utility_function_validate.result
Normal file
@ -0,0 +1,139 @@
|
||||
# Creating the spatial Geometry object
|
||||
USE test;
|
||||
# ST_VALIDATE must return null when its parameter is NULL
|
||||
SELECT ST_ASTEXT(ST_VALIDATE( NULL ));
|
||||
ST_ASTEXT(ST_VALIDATE( NULL ))
|
||||
NULL
|
||||
SELECT ST_ASTEXT(ST_VALIDATE( ST_GEOMFROMTEXT(NULL,0)));
|
||||
ST_ASTEXT(ST_VALIDATE( ST_GEOMFROMTEXT(NULL,0)))
|
||||
NULL
|
||||
SELECT ST_ASTEXT(ST_VALIDATE( ST_GEOMFROMTEXT(NULL,4053)));
|
||||
ST_ASTEXT(ST_VALIDATE( ST_GEOMFROMTEXT(NULL,4053)))
|
||||
NULL
|
||||
# ST_VALIDATE raises an error if the data is malformed
|
||||
SELECT ST_VALIDATE( x'00000000DEADBEEF');
|
||||
ERROR 22023: Invalid GIS data provided to function st_validate.
|
||||
# ST_VALIDATE return the input if it is valid
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('POINT(15 25)')));
|
||||
ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('POINT(15 25)')))
|
||||
POINT(15 25)
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('MULTIPOINT(5 0,25 0,15 10,15 25)')));
|
||||
ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('MULTIPOINT(5 0,25 0,15 10,15 25)')))
|
||||
MULTIPOINT(5 0,25 0,15 10,15 25)
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('LINESTRING(10 15,20 15)')));
|
||||
ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('LINESTRING(10 15,20 15)')))
|
||||
LINESTRING(10 15,20 15)
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('MULTILINESTRING((25 0,0 15,15 30,0 5))')));
|
||||
ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('MULTILINESTRING((25 0,0 15,15 30,0 5))')))
|
||||
MULTILINESTRING((25 0,0 15,15 30,0 5))
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('POLYGON((5 0,7 10,0 15,10 15,15 25,20 15,30 15,22 10,25 0,15 5,5 0))')));
|
||||
ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('POLYGON((5 0,7 10,0 15,10 15,15 25,20 15,30 15,22 10,25 0,15 5,5 0))')))
|
||||
POLYGON((5 0,7 10,0 15,10 15,15 25,20 15,30 15,22 10,25 0,15 5,5 0))
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('MULTIPOLYGON(((0 0,0 10,10 10,0 0)),((10 10,10 15,15 15,10 10)))')));
|
||||
ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('MULTIPOLYGON(((0 0,0 10,10 10,0 0)),((10 10,10 15,15 15,10 10)))')))
|
||||
MULTIPOLYGON(((0 0,0 10,10 10,0 0)),((10 10,10 15,15 15,10 10)))
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('GEOMETRYCOLLECTION(POINT(10 10),'
|
||||
'MULTIPOINT(0 0,10 10),'
|
||||
'LINESTRING(1 1,2 2,3 3),'
|
||||
'MULTILINESTRING((0 0,0 10,10 10,10 0),(10 10,10 15,15 15,10 10)))')));
|
||||
ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('GEOMETRYCOLLECTION(POINT(10 10),'
|
||||
'MULTIPOINT(0 0,10 10),'
|
||||
'LINESTRING(1 1,2 2,3 3),'
|
||||
|
||||
GEOMETRYCOLLECTION(POINT(10 10),MULTIPOINT(0 0,10 10),LINESTRING(1 1,2 2,3 3),MULTILINESTRING((0 0,0 10,10 10,10 0),(10 10,10 15,15 15,10 10)))
|
||||
# The only valid empty geometry is the empty geometrycollection
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('POINT()')));
|
||||
ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('POINT()')))
|
||||
NULL
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('MULTIPOINT()')));
|
||||
ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('MULTIPOINT()')))
|
||||
NULL
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('LINESTRING()')));
|
||||
ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('LINESTRING()')))
|
||||
NULL
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('MULTILINESTRING(())')));
|
||||
ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('MULTILINESTRING(())')))
|
||||
NULL
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('POLYGON(())')));
|
||||
ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('POLYGON(())')))
|
||||
NULL
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('MULTIPOLYGON((()),(()))')));
|
||||
ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('MULTIPOLYGON((()),(()))')))
|
||||
NULL
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('GEOMETRYCOLLECTION()')));
|
||||
ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('GEOMETRYCOLLECTION()')))
|
||||
GEOMETRYCOLLECTION EMPTY
|
||||
# Invalid geometries return null
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('LINESTRING(0 0,-0.00 0,0.0 0)')));
|
||||
ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('LINESTRING(0 0,-0.00 0,0.0 0)')))
|
||||
NULL
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('MULTILINESTRING((0 0))')));
|
||||
ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('MULTILINESTRING((0 0))')))
|
||||
NULL
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('POLYGON((0 0))')));
|
||||
ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('POLYGON((0 0))')))
|
||||
NULL
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('MULTIPOLYGON(((1 1, 1 1, 1 1, 1 1, 1 1)))')));
|
||||
ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('MULTIPOLYGON(((1 1, 1 1, 1 1, 1 1, 1 1)))')))
|
||||
NULL
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('GEOMETRYCOLLECTION(LINESTRING(0 0))')));
|
||||
ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('GEOMETRYCOLLECTION(LINESTRING(0 0))')))
|
||||
NULL
|
||||
# If a polygon or multipolygon has counterclockwise internal rings, the rings are returned counterclockwise
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0),(1 1, 2 1, 2 2, 1 2, 1 1))')));
|
||||
ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0),(1 1, 2 1, 2 2, 1 2, 1 1))')))
|
||||
POLYGON((0 0,10 0,10 10,0 10,0 0),(1 1,1 2,2 2,2 1,1 1))
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('MULTIPOLYGON(((0 0, 10 0, 10 10, 0 10, 0 0),(1 1, 2 1, 2 2, 1 2, 1 1)))')));
|
||||
ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('MULTIPOLYGON(((0 0, 10 0, 10 10, 0 10, 0 0),(1 1, 2 1, 2 2, 1 2, 1 1)))')))
|
||||
MULTIPOLYGON(((0 0,10 0,10 10,0 10,0 0),(1 1,1 2,2 2,2 1,1 1)))
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('GEOMETRYCOLLECTION(POLYGON((0 0, 10 0, 10 10, 0 10, 0 0),(1 1, 2 1, 2 2, 1 2, 1 1)))')));
|
||||
ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('GEOMETRYCOLLECTION(POLYGON((0 0, 10 0, 10 10, 0 10, 0 0),(1 1, 2 1, 2 2, 1 2, 1 1)))')))
|
||||
GEOMETRYCOLLECTION(POLYGON((0 0,10 0,10 10,0 10,0 0),(1 1,1 2,2 2,2 1,1 1)))
|
||||
# ST_VALIDATE raises an error if longitude is out of range
|
||||
SELECT ST_VALIDATE(ST_GEOMFROMTEXT('POINT(0 270)', 4326));
|
||||
ERROR HY000: Longitude 270.000000 is out of range in function st_validate. It must be within (-180.000000, 180.000000].
|
||||
# ST_VALIDATE raises an error if latitude is out of range
|
||||
SELECT ST_VALIDATE(ST_GEOMFROMTEXT('POINT(270 0)', 4326));
|
||||
ERROR HY000: Latitude 270.000000 is out of range in function st_validate. It must be within [-90.000000, 90.000000].
|
||||
# ST_VALIDATE returns the same geometry as it was given when it is valid
|
||||
SELECT ST_ASTEXT(ST_VALIDATE( ST_GEOMFROMTEXT('POLYGON((0 0, 1 0, 1 1, 0 1, 0
|
||||
0),( 0.25 0.25, 0.25 0.75, 0.75 0.75, 0.75 0.25, 0.25 0.25))'))) AS
|
||||
valid_polygon;
|
||||
valid_polygon
|
||||
POLYGON((0 0,1 0,1 1,0 1,0 0),(0.25 0.25,0.25 0.75,0.75 0.75,0.75 0.25,0.25 0.25))
|
||||
SELECT ST_ASTEXT(ST_VALIDATE( ST_GEOMFROMTEXT('POLYGON((0 0, 1 0, 1 1, 0 1, 0
|
||||
0),( 0.25 0.25, 0.25 0.75, 0.75 0.75, 0.75 0.25, 0.25 0.25))',4053))) AS
|
||||
valid_polygon;
|
||||
valid_polygon
|
||||
POLYGON((0 0,1 0,1 1,0 1,0 0),(0.25 0.25,0.25 0.75,0.75 0.75,0.75 0.25,0.25 0.25))
|
||||
SELECT ST_ASTEXT(ST_VALIDATE( ST_GEOMFROMTEXT('POLYGON((0 0, 1 0, 1 1, 0 1, 0
|
||||
0),( 0.25 0.25, 0.25 0.75, 0.75 0.75, 0.75 0.25, 0.25 0.25))',2000))) AS
|
||||
valid_polygon;
|
||||
valid_polygon
|
||||
POLYGON((0 0,1 0,1 1,0 1,0 0),(0.25 0.25,0.25 0.75,0.75 0.75,0.75 0.25,0.25 0.25))
|
||||
SELECT ST_ASTEXT(ST_VALIDATE( ST_GEOMFROMTEXT('POLYGON((0 0, 1 0, 1 1, 0 1, 0
|
||||
0),( 0.25 0.25, 0.25 0.75, 0.75 0.75, 0.75 0.25, 0.25 0.25))',4326))) AS
|
||||
valid_polygon;
|
||||
valid_polygon
|
||||
POLYGON((0 0,1 0,1 1,0 1,0 0),(0.25 0.25,0.25 0.75,0.75 0.75,0.75 0.25,0.25 0.25))
|
||||
# ST_VALIDATE returns NULL if the geometry is invalid
|
||||
SELECT ST_ASTEXT(ST_VALIDATE( ST_GEOMFROMTEXT('POLYGON((0 0, 1 0, 1 1, 0 1, 0
|
||||
0),( 0.25 0.25, 1.75 0.25, 0.75 0.75, 0.25 0.75, 0.25 0.25))'))) AS
|
||||
should_be_null;
|
||||
should_be_null
|
||||
NULL
|
||||
SELECT ST_ASTEXT(ST_VALIDATE( ST_GEOMFROMTEXT('POLYGON((0 0, 1 0, 1 1, 0 1, 0
|
||||
0),( 0.25 0.25, 1.75 0.25, 0.75 0.75, 0.25 0.75, 0.25 0.25))',4053))) AS
|
||||
should_be_null;
|
||||
should_be_null
|
||||
NULL
|
||||
SELECT ST_ASTEXT(ST_VALIDATE( ST_GEOMFROMTEXT('POLYGON((0 0, 1 0, 1 1, 0 1, 0
|
||||
0),( 0.25 0.25, 1.75 0.25, 0.75 0.75, 0.25 0.75, 0.25 0.25))',2000))) AS
|
||||
should_be_null;
|
||||
should_be_null
|
||||
NULL
|
||||
SELECT ST_ASTEXT(ST_VALIDATE( ST_GEOMFROMTEXT('POLYGON((0 0, 1 0, 1 1, 0 1, 0
|
||||
0),( 0.25 0.25, 1.75 0.25, 0.75 0.75, 0.25 0.75, 0.25 0.25))',4326))) AS
|
||||
should_be_null;
|
||||
should_be_null
|
||||
NULL
|
101
mysql-test/main/spatial_utility_function_validate.test
Normal file
101
mysql-test/main/spatial_utility_function_validate.test
Normal file
@ -0,0 +1,101 @@
|
||||
# Copyright (c) 2014, Oracle and/or its affiliates
|
||||
# Copyright (c) 2024, MariaDB Corporation.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; version 2 of the License.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA
|
||||
|
||||
|
||||
############################################################################################
|
||||
# Creating the spatial objects #
|
||||
############################################################################################
|
||||
|
||||
--echo # Creating the spatial Geometry object
|
||||
USE test;
|
||||
|
||||
--echo # ST_VALIDATE must return null when its parameter is NULL
|
||||
SELECT ST_ASTEXT(ST_VALIDATE( NULL ));
|
||||
SELECT ST_ASTEXT(ST_VALIDATE( ST_GEOMFROMTEXT(NULL,0)));
|
||||
SELECT ST_ASTEXT(ST_VALIDATE( ST_GEOMFROMTEXT(NULL,4053)));
|
||||
|
||||
--echo # ST_VALIDATE raises an error if the data is malformed
|
||||
--error ER_GIS_INVALID_DATA
|
||||
SELECT ST_VALIDATE( x'00000000DEADBEEF');
|
||||
|
||||
--echo # ST_VALIDATE return the input if it is valid
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('POINT(15 25)')));
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('MULTIPOINT(5 0,25 0,15 10,15 25)')));
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('LINESTRING(10 15,20 15)')));
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('MULTILINESTRING((25 0,0 15,15 30,0 5))')));
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('POLYGON((5 0,7 10,0 15,10 15,15 25,20 15,30 15,22 10,25 0,15 5,5 0))')));
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('MULTIPOLYGON(((0 0,0 10,10 10,0 0)),((10 10,10 15,15 15,10 10)))')));
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('GEOMETRYCOLLECTION(POINT(10 10),'
|
||||
'MULTIPOINT(0 0,10 10),'
|
||||
'LINESTRING(1 1,2 2,3 3),'
|
||||
'MULTILINESTRING((0 0,0 10,10 10,10 0),(10 10,10 15,15 15,10 10)))')));
|
||||
|
||||
--echo # The only valid empty geometry is the empty geometrycollection
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('POINT()')));
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('MULTIPOINT()')));
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('LINESTRING()')));
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('MULTILINESTRING(())')));
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('POLYGON(())')));
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('MULTIPOLYGON((()),(()))')));
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('GEOMETRYCOLLECTION()')));
|
||||
|
||||
--echo # Invalid geometries return null
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('LINESTRING(0 0,-0.00 0,0.0 0)')));
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('MULTILINESTRING((0 0))')));
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('POLYGON((0 0))')));
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('MULTIPOLYGON(((1 1, 1 1, 1 1, 1 1, 1 1)))')));
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('GEOMETRYCOLLECTION(LINESTRING(0 0))')));
|
||||
|
||||
--echo # If a polygon or multipolygon has counterclockwise internal rings, the rings are returned counterclockwise
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0),(1 1, 2 1, 2 2, 1 2, 1 1))')));
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('MULTIPOLYGON(((0 0, 10 0, 10 10, 0 10, 0 0),(1 1, 2 1, 2 2, 1 2, 1 1)))')));
|
||||
SELECT ST_ASTEXT(ST_VALIDATE(ST_GEOMFROMTEXT('GEOMETRYCOLLECTION(POLYGON((0 0, 10 0, 10 10, 0 10, 0 0),(1 1, 2 1, 2 2, 1 2, 1 1)))')));
|
||||
|
||||
--echo # ST_VALIDATE raises an error if longitude is out of range
|
||||
--error ER_LONGITUDE_OUT_OF_RANGE
|
||||
SELECT ST_VALIDATE(ST_GEOMFROMTEXT('POINT(0 270)', 4326));
|
||||
|
||||
--echo # ST_VALIDATE raises an error if latitude is out of range
|
||||
--error ER_LATITUDE_OUT_OF_RANGE
|
||||
SELECT ST_VALIDATE(ST_GEOMFROMTEXT('POINT(270 0)', 4326));
|
||||
|
||||
--echo # ST_VALIDATE returns the same geometry as it was given when it is valid
|
||||
SELECT ST_ASTEXT(ST_VALIDATE( ST_GEOMFROMTEXT('POLYGON((0 0, 1 0, 1 1, 0 1, 0
|
||||
0),( 0.25 0.25, 0.25 0.75, 0.75 0.75, 0.75 0.25, 0.25 0.25))'))) AS
|
||||
valid_polygon;
|
||||
SELECT ST_ASTEXT(ST_VALIDATE( ST_GEOMFROMTEXT('POLYGON((0 0, 1 0, 1 1, 0 1, 0
|
||||
0),( 0.25 0.25, 0.25 0.75, 0.75 0.75, 0.75 0.25, 0.25 0.25))',4053))) AS
|
||||
valid_polygon;
|
||||
SELECT ST_ASTEXT(ST_VALIDATE( ST_GEOMFROMTEXT('POLYGON((0 0, 1 0, 1 1, 0 1, 0
|
||||
0),( 0.25 0.25, 0.25 0.75, 0.75 0.75, 0.75 0.25, 0.25 0.25))',2000))) AS
|
||||
valid_polygon;
|
||||
SELECT ST_ASTEXT(ST_VALIDATE( ST_GEOMFROMTEXT('POLYGON((0 0, 1 0, 1 1, 0 1, 0
|
||||
0),( 0.25 0.25, 0.25 0.75, 0.75 0.75, 0.75 0.25, 0.25 0.25))',4326))) AS
|
||||
valid_polygon;
|
||||
|
||||
--echo # ST_VALIDATE returns NULL if the geometry is invalid
|
||||
SELECT ST_ASTEXT(ST_VALIDATE( ST_GEOMFROMTEXT('POLYGON((0 0, 1 0, 1 1, 0 1, 0
|
||||
0),( 0.25 0.25, 1.75 0.25, 0.75 0.75, 0.25 0.75, 0.25 0.25))'))) AS
|
||||
should_be_null;
|
||||
SELECT ST_ASTEXT(ST_VALIDATE( ST_GEOMFROMTEXT('POLYGON((0 0, 1 0, 1 1, 0 1, 0
|
||||
0),( 0.25 0.25, 1.75 0.25, 0.75 0.75, 0.25 0.75, 0.25 0.25))',4053))) AS
|
||||
should_be_null;
|
||||
SELECT ST_ASTEXT(ST_VALIDATE( ST_GEOMFROMTEXT('POLYGON((0 0, 1 0, 1 1, 0 1, 0
|
||||
0),( 0.25 0.25, 1.75 0.25, 0.75 0.75, 0.25 0.75, 0.25 0.25))',2000))) AS
|
||||
should_be_null;
|
||||
SELECT ST_ASTEXT(ST_VALIDATE( ST_GEOMFROMTEXT('POLYGON((0 0, 1 0, 1 1, 0 1, 0
|
||||
0),( 0.25 0.25, 1.75 0.25, 0.75 0.75, 0.25 0.75, 0.25 0.25))',4326))) AS
|
||||
should_be_null;
|
@ -2082,6 +2082,7 @@ mem_error:
|
||||
DBUG_RETURN(str_result);
|
||||
}
|
||||
|
||||
|
||||
longlong Item_func_isvalid::val_int()
|
||||
{
|
||||
String *wkb= args[0]->val_str(&tmp);
|
||||
@ -2106,6 +2107,73 @@ longlong Item_func_isvalid::val_int()
|
||||
return (longlong) valid;
|
||||
}
|
||||
|
||||
|
||||
String *Item_func_validate::val_str(String *str_value)
|
||||
{
|
||||
DBUG_ENTER("Item_func_buffer::val_str");
|
||||
DBUG_ASSERT(fixed());
|
||||
String *wkb= args[0]->val_str(&tmp);
|
||||
Geometry_buffer buffer;
|
||||
Geometry *geometry;
|
||||
int valid= 1;
|
||||
str_value= NULL;
|
||||
null_value= 1;
|
||||
|
||||
if(args[0]->null_value)
|
||||
DBUG_RETURN(str_value);
|
||||
|
||||
if (!(geometry= Geometry::construct(&buffer, wkb->ptr(), wkb->length())))
|
||||
{
|
||||
my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
|
||||
DBUG_RETURN(str_value);
|
||||
}
|
||||
|
||||
if (geometry->get_class_info()->m_type_id == Geometry::wkb_point)
|
||||
{
|
||||
double x, y;
|
||||
if(((Gis_point *) geometry)->get_xy(&x, &y))
|
||||
DBUG_RETURN(str_value);
|
||||
|
||||
if (x > MAX_LONGITUDE || x <= MIN_LONGITUDE)
|
||||
{
|
||||
my_error(ER_LATITUDE_OUT_OF_RANGE, MYF(0), x, "st_validate");
|
||||
DBUG_RETURN(str_value);
|
||||
}
|
||||
else if(y > MAX_LATITUDE || y < MIN_LATITUDE) {
|
||||
my_error(ER_LONGITUDE_OUT_OF_RANGE, MYF(0), y,"st_validate");
|
||||
DBUG_RETURN(str_value);
|
||||
}
|
||||
|
||||
null_value= 0;
|
||||
str_value= wkb;
|
||||
DBUG_RETURN(str_value);
|
||||
}
|
||||
|
||||
if (geometry->is_valid(&valid))
|
||||
DBUG_RETURN(str_value);
|
||||
|
||||
if (!valid)
|
||||
DBUG_RETURN(str_value);
|
||||
|
||||
if (geometry->get_class_info()->m_type_id == Geometry::wkb_polygon ||
|
||||
geometry->get_class_info()->m_type_id == Geometry::wkb_multipolygon ||
|
||||
geometry->get_class_info()->m_type_id ==
|
||||
Geometry::wkb_geometrycollection)
|
||||
{
|
||||
String clockwise_wkb;
|
||||
if(geometry->make_clockwise(&clockwise_wkb))
|
||||
DBUG_RETURN(str_value);
|
||||
|
||||
wkb->length(4); // keep the SRID
|
||||
wkb->append(clockwise_wkb.ptr(), clockwise_wkb.length());
|
||||
}
|
||||
|
||||
null_value= 0;
|
||||
str_value= wkb;
|
||||
DBUG_RETURN(str_value);
|
||||
}
|
||||
|
||||
|
||||
bool Item_func_isempty::val_bool()
|
||||
{
|
||||
DBUG_ASSERT(fixed());
|
||||
@ -4299,37 +4367,21 @@ protected:
|
||||
virtual ~Create_func_isvalid() = default;
|
||||
};
|
||||
|
||||
class Create_func_isvalid : public Create_func_arg1
|
||||
class Create_func_validate : public Create_func_arg1
|
||||
{
|
||||
public:
|
||||
Item *create_1_arg(THD *thd, Item *arg1) override
|
||||
{
|
||||
return new (thd->mem_root) Item_func_isvalid(thd, arg1);
|
||||
return new (thd->mem_root) Item_func_validate(thd, arg1);
|
||||
}
|
||||
|
||||
static Create_func_isvalid s_singleton;
|
||||
static Create_func_validate s_singleton;
|
||||
|
||||
protected:
|
||||
Create_func_isvalid() = default;
|
||||
virtual ~Create_func_isvalid() = default;
|
||||
Create_func_validate() = default;
|
||||
virtual ~Create_func_validate() = default;
|
||||
};
|
||||
|
||||
class Create_func_isvalid : public Create_func_arg1
|
||||
{
|
||||
public:
|
||||
Item *create_1_arg(THD *thd, Item *arg1) override
|
||||
{
|
||||
return new (thd->mem_root) Item_func_isvalid(thd, arg1);
|
||||
}
|
||||
|
||||
static Create_func_isvalid s_singleton;
|
||||
|
||||
protected:
|
||||
Create_func_isvalid() = default;
|
||||
virtual ~Create_func_isvalid() = default;
|
||||
};
|
||||
|
||||
|
||||
class Create_func_issimple : public Create_func_arg1
|
||||
{
|
||||
public:
|
||||
@ -4632,6 +4684,7 @@ Create_func_intersects Create_func_intersects::s_singleton;
|
||||
Create_func_isclosed Create_func_isclosed::s_singleton;
|
||||
Create_func_isempty Create_func_isempty::s_singleton;
|
||||
Create_func_isvalid Create_func_isvalid::s_singleton;
|
||||
Create_func_validate Create_func_validate::s_singleton;
|
||||
Create_func_isring Create_func_isring::s_singleton;
|
||||
Create_func_issimple Create_func_issimple::s_singleton;
|
||||
Create_func_simplify Create_func_simplify::s_singleton;
|
||||
@ -4703,6 +4756,7 @@ static Native_func_registry func_array_geom[] =
|
||||
{ { STRING_WITH_LEN("ISCLOSED") }, GEOM_BUILDER(Create_func_isclosed)},
|
||||
{ { STRING_WITH_LEN("ISEMPTY") }, GEOM_BUILDER(Create_func_isempty)},
|
||||
{ { STRING_WITH_LEN("ISVALID") }, GEOM_BUILDER(Create_func_isvalid)},
|
||||
{ { STRING_WITH_LEN("VALIDATE") }, GEOM_BUILDER(Create_func_validate)},
|
||||
{ { STRING_WITH_LEN("ISRING") }, GEOM_BUILDER(Create_func_isring)},
|
||||
{ { STRING_WITH_LEN("ISSIMPLE") }, GEOM_BUILDER(Create_func_issimple)},
|
||||
{ { STRING_WITH_LEN("SIMPLIFY") }, GEOM_BUILDER(Create_func_simplify)},
|
||||
@ -4787,6 +4841,7 @@ static Native_func_registry func_array_geom[] =
|
||||
{ { STRING_WITH_LEN("ST_ISCLOSED") }, GEOM_BUILDER(Create_func_isclosed)},
|
||||
{ { STRING_WITH_LEN("ST_ISEMPTY") }, GEOM_BUILDER(Create_func_isempty)},
|
||||
{ { STRING_WITH_LEN("ST_ISVALID") }, GEOM_BUILDER(Create_func_isvalid)},
|
||||
{ { STRING_WITH_LEN("ST_VALIDATE") }, GEOM_BUILDER(Create_func_validate)},
|
||||
{ { STRING_WITH_LEN("ST_ISRING") }, GEOM_BUILDER(Create_func_isring)},
|
||||
{ { STRING_WITH_LEN("ST_ISSIMPLE") }, GEOM_BUILDER(Create_func_issimple)},
|
||||
{ { STRING_WITH_LEN("ST_SIMPLIFY") }, GEOM_BUILDER(Create_func_simplify)},
|
||||
|
@ -1044,6 +1044,26 @@ public:
|
||||
{ return get_item_copy<Item_func_isvalid>(thd, this); }
|
||||
};
|
||||
|
||||
class Item_func_validate: public Item_geometry_func_args_geometry
|
||||
{
|
||||
public:
|
||||
String tmp;
|
||||
Item_func_validate(THD *thd, Item *a):
|
||||
Item_geometry_func_args_geometry(thd, a) {}
|
||||
LEX_CSTRING func_name_cstring() const override
|
||||
{
|
||||
static LEX_CSTRING name= {STRING_WITH_LEN("st_validate") };
|
||||
return name;
|
||||
}
|
||||
String *val_str(String *) override;
|
||||
const Type_handler *type_handler() const override
|
||||
{
|
||||
return &type_handler_point;
|
||||
}
|
||||
Item *get_copy(THD *thd) override
|
||||
{ return get_item_copy<Item_func_validate>(thd, this); }
|
||||
};
|
||||
|
||||
class Item_func_dimension: public Item_long_func_args_geometry
|
||||
{
|
||||
public:
|
||||
|
@ -12305,3 +12305,7 @@ ER_INCOMPATIBLE_EVENT_FLAG
|
||||
eng "Event flag '%s' in the condition expression is not compatible with the trigger event type '%s'"
|
||||
ER_NOT_IMPLEMENTED_FOR_GEOGRAPHIC_SRS
|
||||
eng "%s has not been implemented for geographic spatial reference systems."
|
||||
ER_LONGITUDE_OUT_OF_RANGE
|
||||
eng "Longitude %f is out of range in function %s. It must be within (-180.000000, 180.000000]."
|
||||
ER_LATITUDE_OUT_OF_RANGE
|
||||
eng "Latitude %f is out of range in function %s. It must be within [-90.000000, 90.000000]."
|
||||
|
199
sql/spatial.cc
199
sql/spatial.cc
@ -1739,6 +1739,59 @@ int Gis_line_string::store_shapes(Gcalc_shape_transporter *trn) const
|
||||
return trn->complete_line();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Calculate the internal area using the shoelace formula
|
||||
(https://en.wikipedia.org/wiki/Shoelace_formula). If the area is < 0 then
|
||||
it is clockwise. If the area is > 0 it is counterclockwise.
|
||||
If it is 0 is degenerate.
|
||||
*/
|
||||
int Gis_line_string::is_clockwise(int *result) const
|
||||
{
|
||||
uint32 num_points;
|
||||
double area= 0;
|
||||
|
||||
if (this->num_points(&num_points))
|
||||
return 1;
|
||||
|
||||
for (uint32 i= 1; i <= num_points; i++)
|
||||
{
|
||||
Geometry_buffer buffer_first, buffer_second;
|
||||
Geometry *point_first, *point_second;
|
||||
String wkb_first, wkb_second;
|
||||
|
||||
if (wkb_first.reserve(SRID_SIZE + WKB_HEADER_SIZE) ||
|
||||
wkb_second.reserve(SRID_SIZE + WKB_HEADER_SIZE))
|
||||
return 1;
|
||||
|
||||
wkb_first.q_append(SRID_PLACEHOLDER);
|
||||
wkb_second.q_append(SRID_PLACEHOLDER);
|
||||
|
||||
if (this->point_n(i, &wkb_first) ||
|
||||
this->point_n((i == num_points) ? 1 : i + 1, &wkb_second))
|
||||
return 1;
|
||||
|
||||
if (!(point_first=
|
||||
Geometry::construct(&buffer_first, wkb_first.ptr(),
|
||||
wkb_first.length())) ||
|
||||
!(point_second=
|
||||
Geometry::construct(&buffer_second, wkb_second.ptr(),
|
||||
wkb_second.length())))
|
||||
return 1;
|
||||
|
||||
double x1, x2, y1, y2;
|
||||
if (((Gis_point *) point_first)->get_xy(&x1, &y1) ||
|
||||
((Gis_point *) point_second)->get_xy(&x2, &y2))
|
||||
return 1;
|
||||
|
||||
area+= (x1 * y2) - (x2 * y1);
|
||||
}
|
||||
|
||||
*result= (area < 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
const Geometry::Class_info *Gis_line_string::get_class_info() const
|
||||
{
|
||||
return &linestring_class;
|
||||
@ -2444,6 +2497,68 @@ single_point_ring:
|
||||
}
|
||||
|
||||
|
||||
int Gis_polygon::make_clockwise(String *result) const
|
||||
{
|
||||
String ring_wkb= 0;
|
||||
uint32 num_interior_ring;
|
||||
Geometry *ring;
|
||||
Geometry_buffer buffer;
|
||||
int is_clockwise;
|
||||
uint32 ring_points;
|
||||
|
||||
if(ring_wkb.reserve(SRID_SIZE + WKB_HEADER_SIZE) ||
|
||||
result->reserve(SRID_SIZE + WKB_HEADER_SIZE))
|
||||
return 1;
|
||||
|
||||
if (this->num_interior_ring(&num_interior_ring) ||
|
||||
this->exterior_ring(&ring_wkb))
|
||||
return 1;
|
||||
|
||||
result->length(0);
|
||||
result->append((char) wkb_ndr);
|
||||
result->q_append((uint32) wkb_polygon);
|
||||
result->q_append((uint32) num_interior_ring + 1);
|
||||
result->append(ring_wkb.ptr() + WKB_HEADER_SIZE,
|
||||
ring_wkb.length() - WKB_HEADER_SIZE);
|
||||
|
||||
for(uint32 i= 1; i <= num_interior_ring; i++)
|
||||
{
|
||||
ring_wkb.length(0);
|
||||
ring_wkb.q_append(SRID_PLACEHOLDER);
|
||||
if (this->interior_ring_n(i, &ring_wkb))
|
||||
return 1;
|
||||
|
||||
if (!(ring= Geometry::construct(&buffer, ring_wkb.ptr(),
|
||||
ring_wkb.length())))
|
||||
return 1;
|
||||
|
||||
if (ring->is_clockwise(&is_clockwise))
|
||||
return 1;
|
||||
|
||||
if (is_clockwise)
|
||||
{
|
||||
result->append(ring_wkb.ptr() + WKB_HEADER_SIZE + SRID_SIZE,
|
||||
ring_wkb.length() - (WKB_HEADER_SIZE + SRID_SIZE));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ring->num_points(&ring_points))
|
||||
return 1;
|
||||
result->q_append((uint32) ring_points);
|
||||
|
||||
for (uint32 i= ring_points; i > 0; i--)
|
||||
{
|
||||
String point= 0;
|
||||
ring->point_n(i, &point);
|
||||
result->append(point.ptr() + WKB_HEADER_SIZE,
|
||||
point.length() - WKB_HEADER_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
const Geometry::Class_info *Gis_polygon::get_class_info() const
|
||||
{
|
||||
return &polygon_class;
|
||||
@ -3777,6 +3892,43 @@ int Gis_multi_polygon::store_shapes(Gcalc_shape_transporter *trn) const
|
||||
}
|
||||
|
||||
|
||||
int Gis_multi_polygon::make_clockwise(String *result) const
|
||||
{
|
||||
Geometry_buffer buffer;
|
||||
uint32 num_polygons;
|
||||
Geometry *polygon;
|
||||
|
||||
if(this->num_geometries(&num_polygons) ||
|
||||
result->reserve(SRID_SIZE + WKB_HEADER_SIZE))
|
||||
return 1;
|
||||
|
||||
result->q_append((char) wkb_ndr);
|
||||
result->q_append((uint32) wkb_multipolygon);
|
||||
result->q_append((uint32) num_polygons);
|
||||
for (uint32 i= 1; i <= num_polygons; i++)
|
||||
{
|
||||
String wkb= 0, clockwise_wkb= 0;
|
||||
if (wkb.reserve(SRID_SIZE + BYTE_ORDER_SIZE + WKB_HEADER_SIZE))
|
||||
return 0;
|
||||
|
||||
wkb.q_append(SRID_PLACEHOLDER);
|
||||
if (this->geometry_n(i, &wkb) ||
|
||||
!(polygon= Geometry::construct(&buffer, wkb.ptr(), wkb.length())))
|
||||
return 1;
|
||||
|
||||
if(polygon->make_clockwise(&clockwise_wkb))
|
||||
return 1;
|
||||
|
||||
result->q_append((char) wkb_ndr);
|
||||
result->q_append((uint32) wkb_polygon);
|
||||
result->append(clockwise_wkb.ptr() + WKB_HEADER_SIZE,
|
||||
clockwise_wkb.length() - WKB_HEADER_SIZE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
const Geometry::Class_info *Gis_multi_polygon::get_class_info() const
|
||||
{
|
||||
return &multipolygon_class;
|
||||
@ -4428,6 +4580,53 @@ int Gis_geometry_collection::store_shapes(Gcalc_shape_transporter *trn) const
|
||||
}
|
||||
|
||||
|
||||
int Gis_geometry_collection::make_clockwise(String *result) const
|
||||
{
|
||||
Geometry_buffer buffer;
|
||||
uint32 num_geometries;
|
||||
Geometry *geometry;
|
||||
|
||||
if(this->num_geometries(&num_geometries) ||
|
||||
result->reserve(SRID_SIZE + WKB_HEADER_SIZE))
|
||||
return 1;
|
||||
|
||||
result->q_append((char) wkb_ndr);
|
||||
result->q_append((uint32) wkb_geometrycollection);
|
||||
result->q_append((uint32) num_geometries);
|
||||
for (uint32 i= 1; i <= num_geometries; i++)
|
||||
{
|
||||
String wkb= 0, clockwise_wkb= 0;
|
||||
if (wkb.reserve(SRID_SIZE + BYTE_ORDER_SIZE + WKB_HEADER_SIZE))
|
||||
return 0;
|
||||
|
||||
wkb.q_append(SRID_PLACEHOLDER);
|
||||
if (this->geometry_n(i, &wkb) ||
|
||||
!(geometry= Geometry::construct(&buffer, wkb.ptr(), wkb.length())))
|
||||
return 1;
|
||||
|
||||
result->q_append((char) wkb_ndr);
|
||||
result->q_append((uint32) geometry->get_class_info()->m_type_id);
|
||||
if (geometry->get_class_info()->m_type_id == Geometry::wkb_polygon ||
|
||||
geometry->get_class_info()->m_type_id == Geometry::wkb_multipolygon ||
|
||||
geometry->get_class_info()->m_type_id ==
|
||||
Geometry::wkb_geometrycollection)
|
||||
{
|
||||
if(geometry->make_clockwise(&clockwise_wkb))
|
||||
return 1;
|
||||
result->append(clockwise_wkb.ptr() + WKB_HEADER_SIZE,
|
||||
clockwise_wkb.length() - WKB_HEADER_SIZE);
|
||||
}
|
||||
else
|
||||
{
|
||||
result->append(wkb.ptr() + SRID_SIZE + WKB_HEADER_SIZE,
|
||||
wkb.length() - (SRID_SIZE + WKB_HEADER_SIZE));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
const Geometry::Class_info *Gis_geometry_collection::get_class_info() const
|
||||
{
|
||||
return &geometrycollection_class;
|
||||
|
@ -305,7 +305,8 @@ public:
|
||||
virtual int interior_ring_n(uint32 num, String *result) const { return -1; }
|
||||
virtual int geometry_n(uint32 num, String *result) const { return -1; }
|
||||
virtual int store_shapes(Gcalc_shape_transporter *trn) const=0;
|
||||
|
||||
virtual int is_clockwise(int *result) const { return -1; }
|
||||
virtual int make_clockwise(String *result) const{ return -1; }
|
||||
public:
|
||||
static Geometry *create_by_typeid(Geometry_buffer *buffer, int type_id);
|
||||
|
||||
@ -498,6 +499,7 @@ public:
|
||||
return 0;
|
||||
}
|
||||
int store_shapes(Gcalc_shape_transporter *trn) const override;
|
||||
int is_clockwise(int *result) const override;
|
||||
const Class_info *get_class_info() const override;
|
||||
};
|
||||
|
||||
@ -533,6 +535,7 @@ public:
|
||||
return 0;
|
||||
}
|
||||
int store_shapes(Gcalc_shape_transporter *trn) const override;
|
||||
int make_clockwise(String *result) const override;
|
||||
const Class_info *get_class_info() const override;
|
||||
};
|
||||
|
||||
@ -634,6 +637,7 @@ public:
|
||||
return 0;
|
||||
}
|
||||
int store_shapes(Gcalc_shape_transporter *trn) const override;
|
||||
int make_clockwise(String *result) const override;
|
||||
const Class_info *get_class_info() const override;
|
||||
uint init_from_opresult(String *bin, const char *opres, uint res_len) override;
|
||||
};
|
||||
@ -663,6 +667,7 @@ public:
|
||||
int geometry_n(uint32 num, String *result) const override;
|
||||
bool dimension(uint32 *dim, const char **end) const override;
|
||||
int store_shapes(Gcalc_shape_transporter *trn) const override;
|
||||
int make_clockwise(String *result) const override;
|
||||
const Class_info *get_class_info() const override;
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user