Commit 6291ea11 authored by Stefano Petrilli's avatar Stefano Petrilli Committed by Sergei Golubchik

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: default avatarTorje Digernes <torje.digernes@oracle.com>
Co-authored-by: default avatarHans H Melby <hans.h.melby@oracle.com>
Co-authored-by: default avatarJon Olav Hauglid <jon.hauglid@oracle.com>
Co-authored-by: default avatarErlend Dahl <erlend.dahl@oracle.com>
Co-authored-by: default avatarNorvald H. Ryeng <norvald.ryeng@oracle.com>
Co-authored-by: default avatarDavid.Zhao <david.zhao@oracle.com>
Co-authored-by: default avatarPavan <pavan.naik@oracle.com>
parent 8755d8b8
# 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
# 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;
......@@ -2073,6 +2073,7 @@ String *Item_func_buffer::val_str(String *str_value)
DBUG_RETURN(str_result);
}
longlong Item_func_isvalid::val_int()
{
String *wkb= args[0]->val_str(&tmp);
......@@ -2097,6 +2098,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);
}
longlong Item_func_isempty::val_int()
{
DBUG_ASSERT(fixed());
......@@ -4289,6 +4357,20 @@ class Create_func_isvalid : public Create_func_arg1
virtual ~Create_func_isvalid() = default;
};
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_validate(thd, arg1);
}
static Create_func_validate s_singleton;
protected:
Create_func_validate() = default;
virtual ~Create_func_validate() = default;
};
class Create_func_issimple : public Create_func_arg1
{
......@@ -4592,6 +4674,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;
......@@ -4663,6 +4746,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)},
......@@ -4748,6 +4832,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)},
......
......@@ -1040,6 +1040,26 @@ class Item_func_isvalid: public Item_long_func_args_geometry
{ 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 *do_get_copy(THD *thd) const override
{ return get_item_copy<Item_func_validate>(thd, this); }
};
class Item_func_dimension: public Item_long_func_args_geometry
{
public:
......
......@@ -12293,3 +12293,7 @@ ER_INCORRECT_COLUMN_NAME_COUNT
chi "派生表的列名计数不正确"
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]."
......@@ -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 @@ int Gis_polygon::store_shapes(Gcalc_shape_transporter *trn) const
}
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 @@ class Geometry
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 @@ class Gis_line_string: public Geometry
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 @@ class Gis_polygon: public Geometry
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 @@ class Gis_multi_polygon: public Geometry
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 @@ class Gis_geometry_collection: public Geometry
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;
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment