mirror of
https://github.com/MariaDB/server.git
synced 2025-08-08 11:22:35 +03:00
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:
committed by
Dave Gosselin
parent
869b4c243e
commit
b50366667b
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;
|
||||
|
Reference in New Issue
Block a user