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 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
|
||||||
|
@ -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) ) ) '));
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
};
|
};
|
||||||
|
@ -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,11 +56,11 @@ 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,
|
||||||
@ -71,7 +72,8 @@ public:
|
|||||||
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;
|
||||||
|
@ -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;
|
||||||
|
451
sql/spatial.cc
451
sql/spatial.cc
@ -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))
|
||||||
{
|
|
||||||
String 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;
|
return 1;
|
||||||
|
|
||||||
int internal_valid;
|
if (*valid == 0)
|
||||||
const char *c_end;
|
return 0;
|
||||||
MBR interior_mbr;
|
|
||||||
if (geometry->is_valid(&internal_valid) ||
|
*valid= 0;
|
||||||
geometry->get_mbr(&interior_mbr, &c_end))
|
|
||||||
|
if (num_geometries < 1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if(this->get_mbr(&mbr, &c_end))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (!internal_valid)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
for (const auto &mbr : mbrs)
|
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())
|
||||||
{
|
{
|
||||||
if (interior_mbr.intersects(&mbr) && !interior_mbr.touches(&mbr))
|
const Gcalc_scan_iterator::event_point *events, *next_ev;
|
||||||
return 0;
|
|
||||||
|
if (scan_it.step())
|
||||||
|
{
|
||||||
|
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;
|
*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;
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user