MDEV-35126 Wrong results from st_isvalid for multipolygon.

The Gis_polygon::is_valid() and the Gis_multi_polygon::is_valid()
  implemented with precise geometry engine.
This commit is contained in:
Alexey Botchkov 2024-12-20 16:01:03 -05:00 committed by Dave Gosselin
parent 79a1fdd964
commit 4b720b027d
8 changed files with 510 additions and 94 deletions

View File

@ -404,3 +404,42 @@ create or replace table tb1 as SELECT st_validate(POINTFROMTEXT(' POINT( 4 1 )
create or replace table tb1 as SELECT st_validate(ST_GeomFromText (' linestring( 4 1,4 4 ) ')) a; create or replace table tb1 as SELECT st_validate(ST_GeomFromText (' linestring( 4 1,4 4 ) ')) a;
create table tb2 as SELECT (st_validate (ST_collect(( POINTFROMTEXT(' POINT( 4 1 ) ') )) )) a; create table tb2 as SELECT (st_validate (ST_collect(( POINTFROMTEXT(' POINT( 4 1 ) ') )) )) a;
drop table tb1, tb2; drop table tb1, tb2;
########################################################################
# MDEV-35126 Wrong results from st_isvalid or st_validate function, while using multipolygon.
########################################################################
select ST_isvalid(ST_GEOMFROMTEXT('multipolygon(((28 26,28 0,84 0,84 42,28 26),(52 18,66 23,73 9,48 6,52 18)), ((59 18,67 18,67 13,59 13,59 18)))'));
ST_isvalid(ST_GEOMFROMTEXT('multipolygon(((28 26,28 0,84 0,84 42,28 26),(52 18,66 23,73 9,48 6,52 18)), ((59 18,67 18,67 13,59 13,59 18)))'))
1
select ST_isvalid(ST_GEOMFROMTEXT(' MULTIPOLYGON((( 0 0, 6 0, 1 2, 0 0)), (( 7 7, 1 8, 7 0, 7 7 ))) '));
ST_isvalid(ST_GEOMFROMTEXT(' MULTIPOLYGON((( 0 0, 6 0, 1 2, 0 0)), (( 7 7, 1 8, 7 0, 7 7 ))) '))
1
SELECT ST_isvalid(ST_GEOMFROMTEXT(' MULTIPOLYGON((( 40 40, 80 40, 100 0, 120 40, 160 40, 130 70, 150 110, 100 90, 50 110, 70 70, 40 40)), ((70 50, 80 50, 80 60, 70 60, 70 50)))'));
ST_isvalid(ST_GEOMFROMTEXT(' MULTIPOLYGON((( 40 40, 80 40, 100 0, 120 40, 160 40, 130 70, 150 110, 100 90, 50 110, 70 70, 40 40)), ((70 50, 80 50, 80 60, 70 60, 70 50)))'))
0
SELECT ST_isvalid(ST_GEOMFROMTEXT(' MULTIPOLYGON((( 40 40, 80 40, 100 0, 120 40, 160 40, 130 70, 150 110, 100 90, 50 110, 70 70, 40 40)), ((90 50, 100 50, 100 60, 90 60, 90 50)))'));
ST_isvalid(ST_GEOMFROMTEXT(' MULTIPOLYGON((( 40 40, 80 40, 100 0, 120 40, 160 40, 130 70, 150 110, 100 90, 50 110, 70 70, 40 40)), ((90 50, 100 50, 100 60, 90 60, 90 50)))'))
0
SELECT ST_isvalid(ST_GEOMFROMTEXT(' MULTIPOLYGON((( 40 40, 80 40, 100 0, 120 40, 160 40, 130 70, 150 110, 100 90, 50 110, 70 70, 40 40)), ((60 20, 70 20, 70 30, 60 30, 60 20)))'));
ST_isvalid(ST_GEOMFROMTEXT(' MULTIPOLYGON((( 40 40, 80 40, 100 0, 120 40, 160 40, 130 70, 150 110, 100 90, 50 110, 70 70, 40 40)), ((60 20, 70 20, 70 30, 60 30, 60 20)))'))
1
SELECT ST_isvalid(ST_GEOMFROMTEXT(' MULTIPOLYGON((( 80 200, 110 210, 140 270, 70 280, 40 250, 60 220, 80 200)), (( 120 210, 160 260, 140 260, 120 210)))'));
ST_isvalid(ST_GEOMFROMTEXT(' MULTIPOLYGON((( 80 200, 110 210, 140 270, 70 280, 40 250, 60 220, 80 200)), (( 120 210, 160 260, 140 260, 120 210)))'))
1
SELECT ST_isvalid(ST_GEOMFROMTEXT(' MULTIPOLYGON((( 80 200, 110 210, 140 270, 70 280, 40 250, 60 220, 80 200)), (( 90 220, 110 240, 60 250, 90 220)))'));
ST_isvalid(ST_GEOMFROMTEXT(' MULTIPOLYGON((( 80 200, 110 210, 140 270, 70 280, 40 250, 60 220, 80 200)), (( 90 220, 110 240, 60 250, 90 220)))'))
0
SELECT ST_isvalid(ST_GEOMFROMTEXT(' MULTIPOLYGON((( 0 0, 5 5, 5 0, 0 5, 0 0)), (( 2 0, 3 0, 2 1, 2 0 ))) '));
ST_isvalid(ST_GEOMFROMTEXT(' MULTIPOLYGON((( 0 0, 5 5, 5 0, 0 5, 0 0)), (( 2 0, 3 0, 2 1, 2 0 ))) '))
0
SELECT ST_isvalid(ST_GEOMFROMTEXT(' MULTIPOLYGON((( 0 0, 3 0, 0 3, 0 0)), (( 3 0, 3 3, 0 3, 3 0))) '));
ST_isvalid(ST_GEOMFROMTEXT(' MULTIPOLYGON((( 0 0, 3 0, 0 3, 0 0)), (( 3 0, 3 3, 0 3, 3 0))) '))
0
SELECT ST_IsValid(ST_GEOMFROMTEXT(' POLYGON( ( 0 0, 0 0, 8 0, 0 0 ) ) '));
ST_IsValid(ST_GEOMFROMTEXT(' POLYGON( ( 0 0, 0 0, 8 0, 0 0 ) ) '))
0
SELECT ST_IsValid(ST_GEOMFROMTEXT(' MULTIPOLYGON( ( ( 2 2, 2 8, 8 8, 8 2, 2 2 ), ( 4 4, 4 6, 6 6, 6 4, 4 4 ) ), ( ( 2 2, 1 2, 0 5, 2 9, 2 2 ) ) ) ')) c;
c
0
SELECT ST_IsValid(ST_GEOMFROMTEXT(' MULTIPOLYGON( ( (2 2, 2 4, 4 4, 4 2, 2 2) ), ( (3 5, 2 5, 2 4, 3 4, 3 5) ) ) '));
ST_IsValid(ST_GEOMFROMTEXT(' MULTIPOLYGON( ( (2 2, 2 4, 4 4, 4 2, 2 2) ), ( (3 5, 2 5, 2 4, 3 4, 3 5) ) ) '))
0

View File

@ -387,3 +387,31 @@ create or replace table tb1 as SELECT st_validate(POINTFROMTEXT(' POINT( 4 1 )
create or replace table tb1 as SELECT st_validate(ST_GeomFromText (' linestring( 4 1,4 4 ) ')) a; create or replace table tb1 as SELECT st_validate(ST_GeomFromText (' linestring( 4 1,4 4 ) ')) a;
create table tb2 as SELECT (st_validate (ST_collect(( POINTFROMTEXT(' POINT( 4 1 ) ') )) )) a; create table tb2 as SELECT (st_validate (ST_collect(( POINTFROMTEXT(' POINT( 4 1 ) ') )) )) a;
drop table tb1, tb2; drop table tb1, tb2;
--echo ########################################################################
--echo # MDEV-35126 Wrong results from st_isvalid or st_validate function, while using multipolygon.
--echo ########################################################################
select ST_isvalid(ST_GEOMFROMTEXT('multipolygon(((28 26,28 0,84 0,84 42,28 26),(52 18,66 23,73 9,48 6,52 18)), ((59 18,67 18,67 13,59 13,59 18)))'));
select ST_isvalid(ST_GEOMFROMTEXT(' MULTIPOLYGON((( 0 0, 6 0, 1 2, 0 0)), (( 7 7, 1 8, 7 0, 7 7 ))) '));
# Concave shapes are not well-supported. This is correctly marked as invalid.
SELECT ST_isvalid(ST_GEOMFROMTEXT(' MULTIPOLYGON((( 40 40, 80 40, 100 0, 120 40, 160 40, 130 70, 150 110, 100 90, 50 110, 70 70, 40 40)), ((70 50, 80 50, 80 60, 70 60, 70 50)))'));
# Concave shapes are not well-supported. This is correctly marked as invalid.
SELECT ST_isvalid(ST_GEOMFROMTEXT(' MULTIPOLYGON((( 40 40, 80 40, 100 0, 120 40, 160 40, 130 70, 150 110, 100 90, 50 110, 70 70, 40 40)), ((90 50, 100 50, 100 60, 90 60, 90 50)))'));
# Concave shapes are not well-supported. This is incorrectly marked as invalid.
SELECT ST_isvalid(ST_GEOMFROMTEXT(' MULTIPOLYGON((( 40 40, 80 40, 100 0, 120 40, 160 40, 130 70, 150 110, 100 90, 50 110, 70 70, 40 40)), ((60 20, 70 20, 70 30, 60 30, 60 20)))'));
SELECT ST_isvalid(ST_GEOMFROMTEXT(' MULTIPOLYGON((( 80 200, 110 210, 140 270, 70 280, 40 250, 60 220, 80 200)), (( 120 210, 160 260, 140 260, 120 210)))'));
SELECT ST_isvalid(ST_GEOMFROMTEXT(' MULTIPOLYGON((( 80 200, 110 210, 140 270, 70 280, 40 250, 60 220, 80 200)), (( 90 220, 110 240, 60 250, 90 220)))'));
SELECT ST_isvalid(ST_GEOMFROMTEXT(' MULTIPOLYGON((( 0 0, 5 5, 5 0, 0 5, 0 0)), (( 2 0, 3 0, 2 1, 2 0 ))) '));
SELECT ST_isvalid(ST_GEOMFROMTEXT(' MULTIPOLYGON((( 0 0, 3 0, 0 3, 0 0)), (( 3 0, 3 3, 0 3, 3 0))) '));
SELECT ST_IsValid(ST_GEOMFROMTEXT(' POLYGON( ( 0 0, 0 0, 8 0, 0 0 ) ) '));
SELECT ST_IsValid(ST_GEOMFROMTEXT(' MULTIPOLYGON( ( ( 2 2, 2 8, 8 8, 8 2, 2 2 ), ( 4 4, 4 6, 6 6, 6 4, 4 4 ) ), ( ( 2 2, 1 2, 0 5, 2 9, 2 2 ) ) ) ')) c;
SELECT ST_IsValid(ST_GEOMFROMTEXT(' MULTIPOLYGON( ( (2 2, 2 4, 4 4, 4 2, 2 2) ), ( (3 5, 2 5, 2 4, 3 4, 3 5) ) ) '));

View File

@ -252,6 +252,7 @@ public:
#endif /*GCALC_CHECK_WITH_FLOAT*/ #endif /*GCALC_CHECK_WITH_FLOAT*/
double coord_extent; double coord_extent;
Gcalc_dyn_list::Item **get_cur_hook() { return m_hook; } Gcalc_dyn_list::Item **get_cur_hook() { return m_hook; }
int get_n_points() const { return m_n_points; }
private: private:
Gcalc_dyn_list::Item *m_first; Gcalc_dyn_list::Item *m_first;

View File

@ -131,7 +131,7 @@ int Gcalc_function::count_internal(const char *cur_func, uint set_type,
uint n_ops= c_op & ~(op_any | op_not | v_mask); uint n_ops= c_op & ~(op_any | op_not | v_mask);
uint n_shape= c_op & ~(op_any | op_not | v_mask); /* same as n_ops */ uint n_shape= c_op & ~(op_any | op_not | v_mask); /* same as n_ops */
op_type v_state= (op_type) (c_op & v_mask); op_type v_state= (op_type) (c_op & v_mask);
int result= 0; int result= 0, t_counter;
const char *sav_cur_func= cur_func; const char *sav_cur_func= cur_func;
// GCALC_DBUG_ENTER("Gcalc_function::count_internal"); // GCALC_DBUG_ENTER("Gcalc_function::count_internal");
@ -175,6 +175,11 @@ int Gcalc_function::count_internal(const char *cur_func, uint set_type,
//GCALC_DBUG_RETURN(mask); //GCALC_DBUG_RETURN(mask);
result= count_internal(cur_func, set_type, &cur_func); result= count_internal(cur_func, set_type, &cur_func);
if (next_func == op_any_intersection)
{
t_counter= result == result_true;
result= result_false;
}
while (--n_ops) while (--n_ops)
{ {
@ -211,6 +216,10 @@ int Gcalc_function::count_internal(const char *cur_func, uint set_type,
else else
result= result_true; result= result_true;
break; break;
case op_any_intersection:
t_counter+= next_res == result_true;
result= (t_counter > 1) ? result_true : result_false;
break;
default: default:
GCALC_DBUG_ASSERT(FALSE); GCALC_DBUG_ASSERT(FALSE);
}; };

View File

@ -30,6 +30,7 @@
op_intersection ( A && B && C ... ) op_intersection ( A && B && C ... )
op_symdifference ( A+B+C+... == 1 ) op_symdifference ( A+B+C+... == 1 )
op_difference ( A && !(B||C||..)) op_difference ( A && !(B||C||..))
op_any_intersection ( A && B || A && C || ... || B && C ... )
with the calls of the add_operation(operation, n_operands) method. with the calls of the add_operation(operation, n_operands) method.
The relation is calculated over a set of shapes, that in turn have The relation is calculated over a set of shapes, that in turn have
to be added with the add_new_shape() method. All the 'shapes' can to be added with the add_new_shape() method. All the 'shapes' can
@ -55,23 +56,24 @@ public:
enum op_type enum op_type
{ {
v_empty= 0x00000000, v_empty= 0x00000000,
v_find_t= 0x01000000, v_find_t= 0x00100000,
v_find_f= 0x02000000, v_find_f= 0x00200000,
v_t_found= 0x03000000, v_t_found= 0x00300000,
v_f_found= 0x04000000, v_f_found= 0x00400000,
v_mask= 0x07000000, v_mask= 0x00700000,
op_not= 0x80000000, op_not= 0x80000000,
op_shape= 0x00000000, op_shape= 0x00000000,
op_union= 0x10000000, op_union= 0x10000000,
op_intersection= 0x20000000, op_intersection= 0x20000000,
op_symdifference= 0x30000000, op_symdifference= 0x30000000,
op_difference= 0x40000000, op_difference= 0x40000000,
op_repeat= 0x50000000, op_repeat= 0x50000000,
op_border= 0x60000000, op_border= 0x60000000,
op_internals= 0x70000000, op_internals= 0x70000000,
op_false= 0x08000000, op_false= 0x08000000,
op_any= 0x78000000 /* The mask to get any of the operations */ op_any_intersection= 0x07000000,
op_any= 0x7F000000 /* The mask to get any of the operations */
}; };
enum shape_type enum shape_type
{ {
@ -144,7 +146,7 @@ protected:
gcalc_shape_info m_si; gcalc_shape_info m_si;
public: public:
Gcalc_operation_transporter(Gcalc_function *fn, Gcalc_heap *heap) : Gcalc_operation_transporter(Gcalc_function *fn, Gcalc_heap *heap) :
Gcalc_shape_transporter(heap), m_fn(fn) {} Gcalc_shape_transporter(heap), m_fn(fn), m_si(0) {}
int single_point(double x, double y) override; int single_point(double x, double y) override;
int start_line() override; int start_line() override;

View File

@ -35,9 +35,18 @@ public:
}; };
Gis_read_stream(CHARSET_INFO *charset, const char *buffer, int size) Gis_read_stream(CHARSET_INFO *charset, const char *buffer, int size)
:m_cur(buffer), m_limit(buffer + size), m_err_msg(NULL), m_charset(charset) : m_wkt(buffer)
, m_cur(buffer)
, m_limit(buffer + size)
, m_err_msg(NULL)
, m_charset(charset)
{} {}
Gis_read_stream(): m_cur(NullS), m_limit(NullS), m_err_msg(NullS)
Gis_read_stream()
: m_wkt(NullS)
, m_cur(NullS)
, m_limit(NullS)
, m_err_msg(NullS)
{} {}
~Gis_read_stream() ~Gis_read_stream()
{ {
@ -82,7 +91,13 @@ public:
return err_msg; return err_msg;
} }
const char *get_wkt() const
{
return m_wkt;
}
protected: protected:
const char *const m_wkt;
const char *m_cur; const char *m_cur;
const char *m_limit; const char *m_limit;
char *m_err_msg; char *m_err_msg;

View File

@ -1041,6 +1041,30 @@ const char *Geometry::get_mbr_for_points(MBR *mbr, const char *data,
return data; return data;
} }
const char* Geometry::get_points_common(const char* data,
Geometry::PointContainer &points) const
{
uint32 expected_points;
if (no_data(data, 4))
return nullptr;
expected_points= uint4korr(data);
data+= 4;
if (not_enough_points(data, expected_points, 0))
return nullptr;
while (expected_points--)
{
double x, y;
float8get(x, data);
float8get(y, data + SIZEOF_STORED_DOUBLE);
points.push_back(std::make_pair(x, y));
data+= POINT_DATA_SIZE;
}
return data;
}
/***************************** Point *******************************/ /***************************** Point *******************************/
@ -2075,73 +2099,210 @@ bool Gis_polygon::get_mbr(MBR *mbr, const char **end) const
return 0; return 0;
} }
bool Gis_polygon::get_points(Geometry::PointContainer &points) const
{
uint32 n_linear_rings;
const char *data= m_data;
if (no_data(data, 4))
return true;
n_linear_rings= uint4korr(data);
data+= 4;
while (data && n_linear_rings--)
data= get_points_common(data, points);
return !data;
}
class Gcalc_poly_transporter : public Gcalc_shape_transporter
{
protected:
gcalc_shape_info m_si;
int m_points_in_ring;
int m_error;
public:
Gcalc_poly_transporter(Gcalc_heap *heap) :
Gcalc_shape_transporter(heap), m_si(0), m_error(0) {}
int get_error() const { return m_error; }
int single_point(double x, double y) override { return 0; }
int start_line() override { return 0; }
int complete_line() override { return 0; }
int start_poly() override
{
int_start_poly();
return 0;
}
int complete_poly() override
{
int_complete_poly();
return 0;
}
int start_ring() override
{
int_start_ring();
m_points_in_ring= m_heap->get_n_points();
return 0;
}
int complete_ring() override
{
int_complete_ring();
m_si++;
if (m_heap->get_n_points() - m_points_in_ring < 3)
m_error= 1;
return 0;
}
int add_point(double x, double y) override
{
return int_add_point(m_si, x, y);
}
int start_collection(int n_objects) override { return 0; }
int empty_shape() override { return 0; }
};
int Gis_polygon::is_valid(int *valid) const int Gis_polygon::is_valid(int *valid) const
{ {
Geometry *exterior_ring, *interior_ring; Gcalc_scan_iterator scan_it;
MBR exterior_mbr, interior_mbr; Gcalc_heap collector;
uint32 num_interior_ring; Gcalc_poly_transporter trn(&collector);
Geometry_buffer buffer; MBR mbr;
uint32 num_rings;
const char *c_end; const char *c_end;
String wkb= 0; char *border_count, *touches_count, *internals;
int result= 0;
*valid= 0; *valid= 0;
if (wkb.reserve(SRID_SIZE + BYTE_ORDER_SIZE + WKB_HEADER_SIZE)) if (this->num_interior_ring(&num_rings))
return 1; return 1;
wkb.q_append(SRID_PLACEHOLDER); num_rings++;
if (this->exterior_ring(&wkb) ||
!(exterior_ring= Geometry::construct(&buffer, wkb.ptr(), wkb.length()))) if(this->get_mbr(&mbr, &c_end))
return 1; return 1;
int valid_ring, simple; collector.set_extent(mbr.xmin, mbr.xmax, mbr.ymin, mbr.ymax);
if (exterior_ring->is_valid(&valid_ring) ||
exterior_ring->is_simple(&simple)) if (this->store_shapes(&trn))
return 1; return 1;
if (!valid_ring || !simple) if (trn.get_error())
return 0; goto exit;
if (exterior_ring->get_mbr(&exterior_mbr, &c_end) || collector.prepare_operation();
this->num_interior_ring(&num_interior_ring)) scan_it.init(&collector);
return 1;
std::vector<MBR> interior_mbrs; border_count= (char *) my_alloca(num_rings);
for(uint32 i= 1; i <= num_interior_ring; i++) bzero(border_count, num_rings);
touches_count= (char *) my_alloca(num_rings);
internals= (char *) my_alloca(num_rings);
while (scan_it.more_points())
{ {
String interior_wkb= 0; const Gcalc_scan_iterator::event_point *events;
if (interior_wkb.reserve(SRID_SIZE + BYTE_ORDER_SIZE + WKB_HEADER_SIZE))
return 1;
interior_wkb.q_append(SRID_PLACEHOLDER); if (scan_it.step())
if (this->interior_ring_n(i, &interior_wkb))
break;
if (!(interior_ring= Geometry::construct(&buffer, interior_wkb.ptr(),
interior_wkb.length())) ||
interior_ring->get_mbr(&interior_mbr, &c_end))
return 1;
if (!exterior_mbr.contains(&interior_mbr) ||
exterior_mbr.equals(&interior_mbr))
return 0;
if (interior_ring->is_simple(&simple))
return 1;
if (!simple)
return 0;
for (const auto &mbr : interior_mbrs)
{ {
if (interior_mbr.equals(&mbr) || interior_mbr.within(&mbr)) result= 1;
return 0; goto exit;
}
events= scan_it.get_events();
Gcalc_point_iterator pit(&scan_it);
int outer_border= 0;
bzero(internals, num_rings);
/* Walk to the event, marking polygons we met */
for (; pit.point() != scan_it.get_event_position(); ++pit)
{
gcalc_shape_info si= pit.point()->get_shape();
internals[si]^= 1;
if (si != 0) /* interior ring */
{
if (!internals[0])
{
/* Internal ring outside the outer. */
goto exit;
}
for (uint n=1; n<num_rings; n++)
{
if (n != si && internals[n]) /* Internal ring inside another internal */
goto exit;
}
}
}
if (events->simple_event())
continue;
bzero(touches_count, num_rings);
/* Check the status of the event point */
for (; events; events= events->get_next())
{
gcalc_shape_info si= events->get_shape();
if (events->event == scev_thread ||
events->event == scev_end || /* should never happen. */
events->event == scev_single_point ||
events->event == scev_intersection)
{
/* These types of events never happen in valid polygon. */
goto exit;
}
touches_count[si]++;
if (events->event == scev_two_threads || events->event == scev_two_ends)
{
if (touches_count[si] > 2)
goto exit;
}
else
{
if (touches_count[si] > 1)
goto exit;
}
if (si == 0) /* outer ring */
outer_border= 1;
else
{
if (!outer_border && !internals[0])
{
/* Inner ring outside the outer ring. */
goto exit;
}
if (outer_border)
{
if (border_count[si]++ > 1)
{
/*
We can't have more than one point of the
internal ring on the border of the outer ring.
*/
goto exit;
}
}
}
} }
interior_mbrs.push_back(interior_mbr);
} }
*valid= 1; *valid= 1;
return 0;
exit:
collector.reset();
scan_it.reset();
my_afree(border_count);
my_afree(touches_count);
my_afree(internals);
return result;
} }
@ -3639,48 +3800,164 @@ bool Gis_multi_polygon::get_data_as_json(String *txt, uint max_dec_digits,
} }
class Gcalc_multipoly_transporter : public Gcalc_shape_transporter
{
protected:
gcalc_shape_info m_si;
public:
Gcalc_multipoly_transporter(Gcalc_heap *heap) :
Gcalc_shape_transporter(heap), m_si(0) {}
int single_point(double x, double y) override { return 0; }
int start_line() override { return 0; }
int complete_line() override { return 0; }
int start_poly() override
{
int_start_poly();
return 0;
}
int complete_poly() override
{
int_complete_poly();
m_si++;
return 0;
}
int start_ring() override
{
int_start_ring();
return 0;
}
int complete_ring() override
{
int_complete_ring();
return 0;
}
int add_point(double x, double y) override
{
return int_add_point(m_si, x, y);
}
int start_collection(int n_objects) override
{
return 0;
}
int empty_shape() override { return 0; }
};
int Gis_multi_polygon::is_valid(int *valid) const int Gis_multi_polygon::is_valid(int *valid) const
{ {
Geometry_buffer buffer; int result= 0;
Gcalc_scan_iterator scan_it;
Gcalc_heap collector;
Gcalc_multipoly_transporter trn(&collector);
MBR mbr;
uint32 num_geometries; uint32 num_geometries;
std::vector<MBR> mbrs; const char *c_end;
Geometry *geometry; char *internals;
*valid= 0;
if (this->num_geometries(&num_geometries)) if (this->num_geometries(&num_geometries))
return 1; return 1;
for (uint32 i= 1; i <= num_geometries; i++) if (shapes_valid(valid))
return 1;
if (*valid == 0)
return 0;
*valid= 0;
if (num_geometries < 1)
return 0;
if(this->get_mbr(&mbr, &c_end))
return 1;
collector.set_extent(mbr.xmin, mbr.xmax, mbr.ymin, mbr.ymax);
if (this->store_shapes(&trn))
return 1;
collector.prepare_operation();
scan_it.init(&collector);
internals= (char *) my_alloca(num_geometries);
while (scan_it.more_points())
{ {
String wkb= 0; const Gcalc_scan_iterator::event_point *events, *next_ev;
if (wkb.reserve(SRID_SIZE + BYTE_ORDER_SIZE + WKB_HEADER_SIZE))
return 0;
wkb.q_append(SRID_PLACEHOLDER); if (scan_it.step())
if (this->geometry_n(i, &wkb) ||
!(geometry= Geometry::construct(&buffer, wkb.ptr(), wkb.length())))
return 1;
int internal_valid;
const char *c_end;
MBR interior_mbr;
if (geometry->is_valid(&internal_valid) ||
geometry->get_mbr(&interior_mbr, &c_end))
return 1;
if (!internal_valid)
return 0;
for (const auto &mbr : mbrs)
{ {
if (interior_mbr.intersects(&mbr) && !interior_mbr.touches(&mbr)) result= 1;
return 0; goto exit;
}
events= scan_it.get_events();
Gcalc_point_iterator pit(&scan_it);
bzero(internals, num_geometries);
/* Walk to the event, marking polygons we met */
for (; pit.point() != scan_it.get_event_position(); ++pit)
{
gcalc_shape_info si= pit.point()->get_shape();
internals[si]^= 1;
}
if (events->simple_event())
continue;
/* Check the status of the event point */
for (; events; events= events->get_next())
{
gcalc_shape_info si= events->get_shape();
if (events->event == scev_thread ||
events->event == scev_end || /* should never happen. */
events->event == scev_single_point ||
events->event == scev_intersection)
{
/* These types of events never happen in valid multipolygon. */
goto exit;
}
if ((internals[si]^= 1))
{
for (uint n=0; n<num_geometries; n++)
{
if (n != si && internals[n])
{
/* Polygons overlap */
goto exit;
}
}
}
if ((next_ev= events->get_next()))
{
if (next_ev->event != scev_two_ends &&
events->event != scev_two_ends &&
events->cmp_dx_dy(events->dx, events->dy,
next_ev->dx, next_ev->dy) == 0)
{
/* Only can touch at points, not lines. */
goto exit;
}
}
} }
mbrs.push_back(interior_mbr);
} }
*valid= 1; *valid= 1;
return 0;
exit:
collector.reset();
scan_it.reset();
return result;
} }
@ -3892,6 +4169,38 @@ int Gis_multi_polygon::store_shapes(Gcalc_shape_transporter *trn) const
} }
int Gis_multi_polygon::shapes_valid(int *valid) const
{
uint32 n_polygons;
Gis_polygon p;
const char *data= m_data;
if (no_data(data, 4))
return 1;
n_polygons= uint4korr(data);
data+= 4;
*valid= 0;
while (n_polygons--)
{
if (no_data(data, WKB_HEADER_SIZE))
return 1;
data+= WKB_HEADER_SIZE;
p.set_data_ptr(data, (uint32) (m_data_end - data));
if (p.is_valid(valid))
return 1;
if (*valid == 0)
break;
data+= p.get_data_size();
}
return 0;
}
int Gis_multi_polygon::make_clockwise(String *result) const int Gis_multi_polygon::make_clockwise(String *result) const
{ {
Geometry_buffer buffer; Geometry_buffer buffer;

View File

@ -347,6 +347,7 @@ public:
static Class_info *ci_collection[wkb_last+1]; static Class_info *ci_collection[wkb_last+1];
static bool create_point(String *result, double x, double y); static bool create_point(String *result, double x, double y);
using PointContainer = std::vector<std::pair<double, double>>;
protected: protected:
static Class_info *find_class(int type_id) static Class_info *find_class(int type_id)
{ {
@ -359,8 +360,16 @@ protected:
bool create_point(String *result, const char *data) const; bool create_point(String *result, const char *data) const;
const char *get_mbr_for_points(MBR *mbr, const char *data, uint offset) const char *get_mbr_for_points(MBR *mbr, const char *data, uint offset)
const; const;
const char* get_points_common(const char* data, PointContainer &points) const;
public: public:
virtual bool get_points(Geometry::PointContainer &points) const
{
// TODO implement this override for other types
assert(false);
return true;
}
/** /**
Check if there're enough data remaining as requested Check if there're enough data remaining as requested
@ -537,6 +546,8 @@ public:
int store_shapes(Gcalc_shape_transporter *trn) const override; int store_shapes(Gcalc_shape_transporter *trn) const override;
int make_clockwise(String *result) const override; int make_clockwise(String *result) const override;
const Class_info *get_class_info() const override; const Class_info *get_class_info() const override;
private:
bool get_points(Geometry::PointContainer &points) const override;
}; };
@ -640,6 +651,8 @@ public:
int make_clockwise(String *result) const override; int make_clockwise(String *result) const override;
const Class_info *get_class_info() const override; const Class_info *get_class_info() const override;
uint init_from_opresult(String *bin, const char *opres, uint res_len) override; uint init_from_opresult(String *bin, const char *opres, uint res_len) override;
private:
int shapes_valid(int *valid) const;
}; };