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:
parent
79a1fdd964
commit
4b720b027d
@ -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 table tb2 as SELECT (st_validate (ST_collect(( POINTFROMTEXT(' POINT( 4 1 ) ') )) )) a;
|
||||
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
|
||||
|
@ -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 table tb2 as SELECT (st_validate (ST_collect(( POINTFROMTEXT(' POINT( 4 1 ) ') )) )) a;
|
||||
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) ) ) '));
|
||||
|
||||
|
@ -252,6 +252,7 @@ public:
|
||||
#endif /*GCALC_CHECK_WITH_FLOAT*/
|
||||
double coord_extent;
|
||||
Gcalc_dyn_list::Item **get_cur_hook() { return m_hook; }
|
||||
int get_n_points() const { return m_n_points; }
|
||||
|
||||
private:
|
||||
Gcalc_dyn_list::Item *m_first;
|
||||
|
@ -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_shape= c_op & ~(op_any | op_not | v_mask); /* same as n_ops */
|
||||
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;
|
||||
|
||||
// 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);
|
||||
|
||||
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)
|
||||
{
|
||||
@ -211,6 +216,10 @@ int Gcalc_function::count_internal(const char *cur_func, uint set_type,
|
||||
else
|
||||
result= result_true;
|
||||
break;
|
||||
case op_any_intersection:
|
||||
t_counter+= next_res == result_true;
|
||||
result= (t_counter > 1) ? result_true : result_false;
|
||||
break;
|
||||
default:
|
||||
GCALC_DBUG_ASSERT(FALSE);
|
||||
};
|
||||
|
@ -30,6 +30,7 @@
|
||||
op_intersection ( A && B && C ... )
|
||||
op_symdifference ( A+B+C+... == 1 )
|
||||
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.
|
||||
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
|
||||
@ -55,23 +56,24 @@ public:
|
||||
enum op_type
|
||||
{
|
||||
v_empty= 0x00000000,
|
||||
v_find_t= 0x01000000,
|
||||
v_find_f= 0x02000000,
|
||||
v_t_found= 0x03000000,
|
||||
v_f_found= 0x04000000,
|
||||
v_mask= 0x07000000,
|
||||
v_find_t= 0x00100000,
|
||||
v_find_f= 0x00200000,
|
||||
v_t_found= 0x00300000,
|
||||
v_f_found= 0x00400000,
|
||||
v_mask= 0x00700000,
|
||||
|
||||
op_not= 0x80000000,
|
||||
op_shape= 0x00000000,
|
||||
op_union= 0x10000000,
|
||||
op_intersection= 0x20000000,
|
||||
op_symdifference= 0x30000000,
|
||||
op_difference= 0x40000000,
|
||||
op_repeat= 0x50000000,
|
||||
op_border= 0x60000000,
|
||||
op_internals= 0x70000000,
|
||||
op_false= 0x08000000,
|
||||
op_any= 0x78000000 /* The mask to get any of the operations */
|
||||
op_not= 0x80000000,
|
||||
op_shape= 0x00000000,
|
||||
op_union= 0x10000000,
|
||||
op_intersection= 0x20000000,
|
||||
op_symdifference= 0x30000000,
|
||||
op_difference= 0x40000000,
|
||||
op_repeat= 0x50000000,
|
||||
op_border= 0x60000000,
|
||||
op_internals= 0x70000000,
|
||||
op_false= 0x08000000,
|
||||
op_any_intersection= 0x07000000,
|
||||
op_any= 0x7F000000 /* The mask to get any of the operations */
|
||||
};
|
||||
enum shape_type
|
||||
{
|
||||
@ -144,7 +146,7 @@ protected:
|
||||
gcalc_shape_info m_si;
|
||||
public:
|
||||
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 start_line() override;
|
||||
|
@ -35,9 +35,18 @@ public:
|
||||
};
|
||||
|
||||
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()
|
||||
{
|
||||
@ -82,7 +91,13 @@ public:
|
||||
return err_msg;
|
||||
}
|
||||
|
||||
const char *get_wkt() const
|
||||
{
|
||||
return m_wkt;
|
||||
}
|
||||
|
||||
protected:
|
||||
const char *const m_wkt;
|
||||
const char *m_cur;
|
||||
const char *m_limit;
|
||||
char *m_err_msg;
|
||||
|
457
sql/spatial.cc
457
sql/spatial.cc
@ -1041,6 +1041,30 @@ const char *Geometry::get_mbr_for_points(MBR *mbr, const char *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 *******************************/
|
||||
|
||||
@ -2075,73 +2099,210 @@ bool Gis_polygon::get_mbr(MBR *mbr, const char **end) const
|
||||
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
|
||||
{
|
||||
Geometry *exterior_ring, *interior_ring;
|
||||
MBR exterior_mbr, interior_mbr;
|
||||
uint32 num_interior_ring;
|
||||
Geometry_buffer buffer;
|
||||
Gcalc_scan_iterator scan_it;
|
||||
Gcalc_heap collector;
|
||||
Gcalc_poly_transporter trn(&collector);
|
||||
MBR mbr;
|
||||
uint32 num_rings;
|
||||
const char *c_end;
|
||||
String wkb= 0;
|
||||
char *border_count, *touches_count, *internals;
|
||||
int result= 0;
|
||||
|
||||
*valid= 0;
|
||||
|
||||
if (wkb.reserve(SRID_SIZE + BYTE_ORDER_SIZE + WKB_HEADER_SIZE))
|
||||
if (this->num_interior_ring(&num_rings))
|
||||
return 1;
|
||||
|
||||
wkb.q_append(SRID_PLACEHOLDER);
|
||||
if (this->exterior_ring(&wkb) ||
|
||||
!(exterior_ring= Geometry::construct(&buffer, wkb.ptr(), wkb.length())))
|
||||
num_rings++;
|
||||
|
||||
if(this->get_mbr(&mbr, &c_end))
|
||||
return 1;
|
||||
|
||||
int valid_ring, simple;
|
||||
if (exterior_ring->is_valid(&valid_ring) ||
|
||||
exterior_ring->is_simple(&simple))
|
||||
collector.set_extent(mbr.xmin, mbr.xmax, mbr.ymin, mbr.ymax);
|
||||
|
||||
if (this->store_shapes(&trn))
|
||||
return 1;
|
||||
|
||||
if (!valid_ring || !simple)
|
||||
return 0;
|
||||
if (trn.get_error())
|
||||
goto exit;
|
||||
|
||||
if (exterior_ring->get_mbr(&exterior_mbr, &c_end) ||
|
||||
this->num_interior_ring(&num_interior_ring))
|
||||
return 1;
|
||||
collector.prepare_operation();
|
||||
scan_it.init(&collector);
|
||||
|
||||
std::vector<MBR> interior_mbrs;
|
||||
for(uint32 i= 1; i <= num_interior_ring; i++)
|
||||
border_count= (char *) my_alloca(num_rings);
|
||||
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;
|
||||
if (interior_wkb.reserve(SRID_SIZE + BYTE_ORDER_SIZE + WKB_HEADER_SIZE))
|
||||
return 1;
|
||||
const Gcalc_scan_iterator::event_point *events;
|
||||
|
||||
interior_wkb.q_append(SRID_PLACEHOLDER);
|
||||
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 (scan_it.step())
|
||||
{
|
||||
if (interior_mbr.equals(&mbr) || interior_mbr.within(&mbr))
|
||||
return 0;
|
||||
result= 1;
|
||||
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;
|
||||
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
|
||||
{
|
||||
Geometry_buffer buffer;
|
||||
int result= 0;
|
||||
Gcalc_scan_iterator scan_it;
|
||||
Gcalc_heap collector;
|
||||
Gcalc_multipoly_transporter trn(&collector);
|
||||
MBR mbr;
|
||||
uint32 num_geometries;
|
||||
std::vector<MBR> mbrs;
|
||||
Geometry *geometry;
|
||||
*valid= 0;
|
||||
const char *c_end;
|
||||
char *internals;
|
||||
|
||||
if (this->num_geometries(&num_geometries))
|
||||
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;
|
||||
if (wkb.reserve(SRID_SIZE + BYTE_ORDER_SIZE + WKB_HEADER_SIZE))
|
||||
return 0;
|
||||
const Gcalc_scan_iterator::event_point *events, *next_ev;
|
||||
|
||||
wkb.q_append(SRID_PLACEHOLDER);
|
||||
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 (scan_it.step())
|
||||
{
|
||||
if (interior_mbr.intersects(&mbr) && !interior_mbr.touches(&mbr))
|
||||
return 0;
|
||||
result= 1;
|
||||
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;
|
||||
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
|
||||
{
|
||||
Geometry_buffer buffer;
|
||||
|
@ -347,6 +347,7 @@ public:
|
||||
static Class_info *ci_collection[wkb_last+1];
|
||||
|
||||
static bool create_point(String *result, double x, double y);
|
||||
using PointContainer = std::vector<std::pair<double, double>>;
|
||||
protected:
|
||||
static Class_info *find_class(int type_id)
|
||||
{
|
||||
@ -359,8 +360,16 @@ protected:
|
||||
bool create_point(String *result, const char *data) const;
|
||||
const char *get_mbr_for_points(MBR *mbr, const char *data, uint offset)
|
||||
const;
|
||||
const char* get_points_common(const char* data, PointContainer &points) const;
|
||||
|
||||
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
|
||||
|
||||
@ -537,6 +546,8 @@ public:
|
||||
int store_shapes(Gcalc_shape_transporter *trn) const override;
|
||||
int make_clockwise(String *result) 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;
|
||||
const Class_info *get_class_info() const override;
|
||||
uint init_from_opresult(String *bin, const char *opres, uint res_len) override;
|
||||
private:
|
||||
int shapes_valid(int *valid) const;
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user