/*
Copyright ( c ) 2002 , 2013 , Oracle and / or its affiliates .
Copyright ( c ) 2011 , 2021 , 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 */
#include "mariadb.h"
#include "sql_priv.h"
#include "spatial.h"
#include "gstream.h" // Gis_read_stream
#include "sql_string.h" // String
#include <vector>
#include "sql_parse.h"
/* This is from item_func.h. Didn't want to #include the whole file. */
double my_double_round(double value, longlong dec, bool dec_unsigned,
bool truncate);
#define advance(wkb,len,N) do { wkb+=(N); len-=(N); } while (0 )
/*
exponential notation :
1 sign
1 number before the decimal point
1 decimal point
14 number of significant digits ( see String : : qs_append ( double ) )
1 ' e ' sign
1 exponent sign
3 exponent digits
= =
22
" f " notation :
1 optional 0
1 sign
14 number significant digits ( see String : : qs_append ( double ) )
1 decimal point
= =
17
*/
#define MAX_DIGITS_IN_DOUBLE MY_GCVT_MAX_FIELD_WIDTH
int MBR::within(const MBR *mbr)
{
/*
We have to take into account the ' dimension ' of
the MBR , where the dimension of a single point is 0 ,
the dimension of a vertical or horizontal line is 1 ,
and finally the dimension of the solid rectangle is 2 .
*/
int dim1= dimension();
int dim2= mbr->dimension();
DBUG_ASSERT(dim1 >= 0 && dim1 <= 2 && dim2 >= 0 && dim2 <= 2 );
/*
Either / both of the two operands can degrade to a point or a
horizontal / vertical line segment , and we have to treat such cases
separately .
*/
switch (dim1)
{
case 0 :
DBUG_ASSERT(xmin == xmax && ymin == ymax);
switch (dim2)
{
case 0 :
DBUG_ASSERT(mbr->xmin == mbr->xmax && mbr->ymin == mbr->ymax);
return equals(mbr);
break ;
case 1 :
DBUG_ASSERT((mbr->xmin == mbr->xmax && mbr->ymin != mbr->ymax) ||
(mbr->ymin == mbr->ymax && mbr->xmin != mbr->xmax));
return ((xmin > mbr->xmin && xmin < mbr->xmax && ymin == mbr->ymin) ||
(ymin > mbr->ymin && ymin < mbr->ymax && xmin == mbr->xmin));
break ;
case 2 :
DBUG_ASSERT(mbr->xmin != mbr->xmax && mbr->ymin != mbr->ymax);
return (xmin > mbr->xmin && xmax < mbr->xmax &&
ymin > mbr->ymin && ymax < mbr->ymax);
break ;
}
break ;
case 1 :
DBUG_ASSERT((xmin == xmax && ymin != ymax) ||
(ymin == ymax && xmin != xmax));
switch (dim2)
{
case 0 :
DBUG_ASSERT(mbr->xmin == mbr->xmax && mbr->ymin == mbr->ymax);
return 0 ;
break ;
case 1 :
DBUG_ASSERT((mbr->xmin == mbr->xmax && mbr->ymin != mbr->ymax) ||
(mbr->ymin == mbr->ymax && mbr->xmin != mbr->xmax));
return ((xmin == xmax && mbr->xmin == mbr->xmax && mbr->xmin == xmin &&
mbr->ymin <= ymin && mbr->ymax >= ymax) ||
(ymin == ymax && mbr->ymin == mbr->ymax && mbr->ymin == ymin &&
mbr->xmin <= xmin && mbr->xmax >= xmax));
break ;
case 2 :
DBUG_ASSERT(mbr->xmin != mbr->xmax && mbr->ymin != mbr->ymax);
return ((xmin == xmax && xmin > mbr->xmin && xmax < mbr->xmax &&
ymin >= mbr->ymin && ymax <= mbr->ymax) ||
(ymin == ymax && ymin > mbr->ymin && ymax < mbr->ymax &&
xmin >= mbr->xmin && xmax <= mbr->xmax));
break ;
}
break ;
case 2 :
DBUG_ASSERT(xmin != xmax && ymin != ymax);
switch (dim2)
{
case 0 :
case 1 :
return 0 ;
break ;
case 2 :
DBUG_ASSERT(mbr->xmin != mbr->xmax && mbr->ymin != mbr->ymax);
return ((mbr->xmin <= xmin) && (mbr->ymin <= ymin) &&
(mbr->xmax >= xmax) && (mbr->ymax >= ymax));
break ;
}
break ;
}
// Never reached.
DBUG_ASSERT(false );
return 0 ;
}
int MBR::coveredby(const MBR *mbr)
{
int dim1= dimension();
int dim2= mbr->dimension();
if (dim1 > dim2)
return 0 ;
else if (dim1 == 0 && dim2 == 0 )
return equals(mbr);
return ((xmin >= mbr->xmin) && (xmax <= mbr->xmax) &&
(ymin >= mbr->ymin) && (ymax <= mbr->ymax));
}
/********************** Ramer–Douglas–Peucker algorithm **********************/
static double perpendicular_distance(const st_point_2d& point,
const st_point_2d& line_start,
const st_point_2d& line_end) {
double difference_x= line_end.x - line_start.x;
double difference_y= line_end.y - line_start.y;
double magnitude = sqrt((difference_x * difference_x) +
(difference_y * difference_y));
if (magnitude > 0 .0 )
{
difference_x /= magnitude;
difference_y /= magnitude;
}
double point_vector_x= point.x - line_start.x;
double point_vector_y= point.y - line_start.y;
double point_vector_dot_product= ((difference_x * point_vector_x) +
(difference_y * point_vector_y));
double adjusted_x= point_vector_x - (point_vector_dot_product * difference_x);
double adjusted_y= point_vector_y - (point_vector_dot_product * difference_y);
return sqrt((adjusted_x * adjusted_x) + (adjusted_y * adjusted_y));
}
static void recursive_RDP(const std::vector<st_point_2d>& points,
const double max_distance,
std::vector<st_point_2d>& out,
const uint32 start, const uint32 end)
{
if (start >= end) return ;
double greatest_distance= 0 .0 ;
uint32 index= start;
for (uint32 i = start + 1 ; i < end; ++i) {
double dist = perpendicular_distance(points[i], points[start], points[end]);
if (dist > greatest_distance) {
index = i;
greatest_distance = dist;
}
}
if (greatest_distance > max_distance)
{
recursive_RDP(points, max_distance, out, start, index);
recursive_RDP(points, max_distance, out, index, end);
} else if (start != 0 )
out.push_back(points[start]);
}
/*
Implements the Ramer – Douglas – Peucker . Given the points that compose a line ,
finds a similar curve with fewer points . The simplified curve consists of a
subset of the points that defined the original curve .
https : //en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm
*/
static void simplify_RDP(std::vector<st_point_2d>& points,
const double max_distance) {
std::vector<st_point_2d> result;
result.push_back(points.front());
recursive_RDP(points, max_distance, result, 0 ,(uint32) points.size() - 1 );
result.push_back(points.back());
points = std::move(result);
}
/***************************** Gis_class_info *******************************/
Geometry::Class_info *Geometry::ci_collection[Geometry::wkb_last+1 ]=
{
NULL, NULL, NULL, NULL, NULL, NULL, NULL
};
static Geometry::Class_info **ci_collection_end=
Geometry::ci_collection+Geometry::wkb_last + 1 ;
Geometry::Class_info::Class_info(const char *name, const char *geojson_name,
int type_id, create_geom_t create_func):
m_type_id(type_id), m_create_func(create_func)
{
m_name.str= (char *) name;
m_name.length= strlen(name);
m_geojson_name.str= (char *) geojson_name;
m_geojson_name.length= strlen(geojson_name);
ci_collection[type_id]= this ;
}
static Geometry *create_point(char *buffer)
{
return new (buffer) Gis_point;
}
static Geometry *create_linestring(char *buffer)
{
return new (buffer) Gis_line_string;
}
static Geometry *create_polygon(char *buffer)
{
return new (buffer) Gis_polygon;
}
static Geometry *create_multipoint(char *buffer)
{
return new (buffer) Gis_multi_point;
}
static Geometry *create_multipolygon(char *buffer)
{
return new (buffer) Gis_multi_polygon;
}
static Geometry *create_multilinestring(char *buffer)
{
return new (buffer) Gis_multi_line_string;
}
static Geometry *create_geometrycollection(char *buffer)
{
return new (buffer) Gis_geometry_collection;
}
static Geometry::Class_info point_class("POINT" , "Point" ,
Geometry::wkb_point, create_point);
static Geometry::Class_info linestring_class("LINESTRING" , "LineString" ,
Geometry::wkb_linestring,
create_linestring);
static Geometry::Class_info polygon_class("POLYGON" , "Polygon" ,
Geometry::wkb_polygon,
create_polygon);
static Geometry::Class_info multipoint_class("MULTIPOINT" , "MultiPoint" ,
Geometry::wkb_multipoint,
create_multipoint);
static Geometry::Class_info
multilinestring_class("MULTILINESTRING" , "MultiLineString" ,
Geometry::wkb_multilinestring, create_multilinestring);
static Geometry::Class_info multipolygon_class("MULTIPOLYGON" , "MultiPolygon" ,
Geometry::wkb_multipolygon,
create_multipolygon);
static Geometry::Class_info
geometrycollection_class("GEOMETRYCOLLECTION" , "GeometryCollection" ,
Geometry::wkb_geometrycollection,
create_geometrycollection);
static void get_point(double *x, double *y, const char *data)
{
float8get(*x, data);
float8get(*y, data + SIZEOF_STORED_DOUBLE);
}
/***************************** Geometry *******************************/
Geometry::Class_info *Geometry::find_class(const char *name, size_t len)
{
for (Class_info **cur_rt= ci_collection;
cur_rt < ci_collection_end; cur_rt++)
{
if (*cur_rt &&
((*cur_rt)->m_name.length == len) &&
(my_charset_latin1.strnncoll((*cur_rt)->m_name.str, len,
name, len) == 0 ))
return *cur_rt;
}
return 0 ;
}
Geometry *Geometry::create_by_typeid(Geometry_buffer *buffer, int type_id)
{
Class_info *ci;
if (!(ci= find_class(type_id)))
return NULL;
return (*ci->m_create_func)(buffer->data);
}
Geometry *Geometry::construct(Geometry_buffer *buffer,
const char *data, uint32 data_len)
{
uint32 geom_type;
Geometry *result;
if (!is_valid_geometry_length(data_len))
return NULL;
/* + 1 to skip the byte order (stored in position SRID_SIZE). */
geom_type= uint4korr(data + SRID_SIZE + 1 );
if (!(result= create_by_typeid(buffer, (int ) geom_type)))
return NULL;
result->m_data= data+ SRID_SIZE + WKB_HEADER_SIZE;
result->m_data_end= data + data_len;
return result;
}
uint Geometry::get_key_image_itMBR(LEX_CSTRING &src, uchar *buff, uint length)
{
const char *dummy;
MBR mbr;
Geometry_buffer buffer;
Geometry *gobj;
const uint image_length= SIZEOF_STORED_DOUBLE*4 ;
if (src.length < SRID_SIZE)
{
bzero(buff, image_length);
return image_length;
}
gobj= Geometry::construct(&buffer, (char *) src.str, (uint32) src.length);
if (!gobj || gobj->get_mbr(&mbr, &dummy))
bzero(buff, image_length);
else
{
float8store(buff, mbr.xmin);
float8store(buff+8 , mbr.xmax);
float8store(buff+16 , mbr.ymin);
float8store(buff+24 , mbr.ymax);
}
return image_length;
}
Geometry *Geometry::create_from_wkt(Geometry_buffer *buffer,
Gis_read_stream *trs, String *wkt,
bool init_stream)
{
LEX_STRING name;
Class_info *ci;
char next_sym;
if (trs->get_next_word(&name))
{
trs->set_error_msg("Geometry name expected" );
return NULL;
}
if (!(ci= find_class(name.str, name.length)) ||
wkt->reserve(1 + 4 , 512 ))
return NULL;
Geometry *result= (*ci->m_create_func)(buffer->data);
wkt->q_append((char ) wkb_ndr);
wkt->q_append((uint32) result->get_class_info()->m_type_id);
if (!(next_sym= trs->next_symbol()))
return NULL;
if (!(next_sym= trs->next_symbol()))
return NULL;
if ((next_sym == '(' && trs->check_next_symbol('(' )) ||
result->init_from_wkt(trs, wkt) ||
(next_sym == '(' && trs->check_next_symbol(')' )))
return NULL;
if (init_stream)
{
result->set_data_ptr(wkt->ptr(), wkt->length());
result->shift_wkb_header();
}
return result;
}
int Geometry::as_wkt(String *wkt, const char **end)
{
uint32 len= (uint) get_class_info()->m_name.length;
if (wkt->reserve(len + 2 , 512 ))
return 1 ;
wkt->qs_append(get_class_info()->m_name.str, len);
if (get_class_info() != &geometrycollection_class)
wkt->qs_append('(' );
if (get_data_as_wkt(wkt, end))
return 1 ;
if (get_class_info() != &geometrycollection_class)
wkt->append(')' ); // NOT qs_append, get_data_as_wkt consumed reserved space
return 0 ;
}
static const uchar type_keyname[]= "type" ;
static const uint type_keyname_len= 4 ;
static const uchar coord_keyname[]= "coordinates" ;
static const uint coord_keyname_len= 11 ;
static const uchar geometries_keyname[]= "geometries" ;
static const uint geometries_keyname_len= 10 ;
static const uchar features_keyname[]= "features" ;
static const uint features_keyname_len= 8 ;
static const uchar geometry_keyname[]= "geometry" ;
static const uint geometry_keyname_len= 8 ;
static const uint max_keyname_len= 11 ; /*'coordinates' keyname is the longest.*/
static const uchar feature_type[]= "feature" ;
static const int feature_type_len= 7 ;
static const uchar feature_coll_type[]= "featurecollection" ;
static const int feature_coll_type_len= 17 ;
static const uchar bbox_keyname[]= "bbox" ;
static const int bbox_keyname_len= 4 ;
int Geometry::as_json(String *wkt, uint max_dec_digits, const char **end)
{
uint32 len= (uint) get_class_info()->m_geojson_name.length;
if (wkt->reserve(4 + type_keyname_len + 2 + len + 2 + 2 +
coord_keyname_len + 4 , 512 ))
return 1 ;
wkt->qs_append('"' );
wkt->qs_append((const char *) type_keyname, type_keyname_len);
wkt->qs_append("\" : \"" , 4 );
wkt->qs_append(get_class_info()->m_geojson_name.str, len);
wkt->qs_append("\" , \"" , 4 );
if (get_class_info() == &geometrycollection_class)
wkt->qs_append((const char *) geometries_keyname, geometries_keyname_len);
else
wkt->qs_append((const char *) coord_keyname, coord_keyname_len);
wkt->qs_append("\" : ", 3);
if (get_data_as_json(wkt, max_dec_digits, end))
return 1 ;
return 0 ;
}
int Geometry::bbox_as_json(String *wkt)
{
MBR mbr;
const char *end;
if (wkt->reserve(5 + bbox_keyname_len + (FLOATING_POINT_DECIMALS+2 )*4 , 512 ))
return 1 ;
wkt->qs_append('"' );
wkt->qs_append((const char *) bbox_keyname, bbox_keyname_len);
wkt->qs_append("\" : [", 4);
if (get_mbr(&mbr, &end))
return 1 ;
wkt->qs_append(mbr.xmin);
wkt->qs_append(", " , 2 );
wkt->qs_append(mbr.ymin);
wkt->qs_append(", " , 2 );
wkt->qs_append(mbr.xmax);
wkt->qs_append(", " , 2 );
wkt->qs_append(mbr.ymax);
wkt->qs_append(']' );
return 0 ;
}
static double wkb_get_double(const char *ptr, Geometry::wkbByteOrder bo)
{
double res;
if (bo != Geometry::wkb_xdr)
{
float8get(res, ptr);
}
else
{
char inv_array[8 ];
inv_array[0 ]= ptr[7 ];
inv_array[1 ]= ptr[6 ];
inv_array[2 ]= ptr[5 ];
inv_array[3 ]= ptr[4 ];
inv_array[4 ]= ptr[3 ];
inv_array[5 ]= ptr[2 ];
inv_array[6 ]= ptr[1 ];
inv_array[7 ]= ptr[0 ];
float8get(res, inv_array);
}
return res;
}
static uint32 wkb_get_uint(const char *ptr, Geometry::wkbByteOrder bo)
{
if (bo != Geometry::wkb_xdr)
return uint4korr(ptr);
/* else */
{
char inv_array[4 ];
inv_array[0 ]= ptr[3 ];
inv_array[1 ]= ptr[2 ];
inv_array[2 ]= ptr[1 ];
inv_array[3 ]= ptr[0 ];
return uint4korr(inv_array);
}
}
Geometry *Geometry::create_from_wkb(Geometry_buffer *buffer,
const char *wkb, uint32 len, String *res)
{
uint32 geom_type;
Geometry *geom;
if (len < WKB_HEADER_SIZE || (uchar) wkb[0 ] > wkb_ndr)
return NULL;
wkbByteOrder bo= (wkbByteOrder)wkb[0 ];
geom_type= wkb_get_uint(wkb+1 , bo);
if (!(geom= create_by_typeid(buffer, (int ) geom_type)) ||
res->reserve(WKB_HEADER_SIZE, 512 ))
return NULL;
res->q_append((char ) wkb_ndr);
res->q_append(geom_type);
advance(wkb,len,WKB_HEADER_SIZE);
return geom->init_from_wkb(wkb, len, bo, res) ? geom : 0 ;
}
Geometry *Geometry::create_from_json(Geometry_buffer *buffer,
json_engine_t *je, bool er_on_3D, String *res)
{
Class_info *ci= NULL;
enum t_enum { T_NONE, T_GEOMETRY, T_GEOMETRIES, T_COORD, T_FEATURE };
enum t_enum arg= T_NONE;
json_engine_t argje, *je_arg;
Geometry *result;
uchar key_buf[max_keyname_len];
uint key_len;
bool feature_type_found= false ;
if (check_stack_overrun(current_thd, STACK_MIN_SIZE, NULL))
return NULL;
if (je->value_type != JSON_VALUE_OBJECT)
{
je->s.error= GEOJ_INCORRECT_GEOJSON;
goto err_return;
}
while (json_scan_next(je) == 0 && je->state != JST_OBJ_END)
{
if (je->state != JST_KEY)
break ;
key_len=0 ;
while (json_read_keyname_chr(je) == 0 )
{
if (je->s.c_next > 127 || key_len >= max_keyname_len)
{
/* Symbol out of range, or keyname too long. No need to compare.. */
key_len=0 ;
break ;
}
key_buf[key_len++]= (uchar)je->s.c_next | 0 x20; /* make it lowercase. */
}
if (unlikely(je->s.error))
goto err_return;
if (key_len == type_keyname_len &&
memcmp(key_buf, type_keyname, type_keyname_len) == 0 )
{
/*
Found the " type " key . Let ' s check it ' s a string and remember
the feature ' s type .
*/
if (json_read_value(je))
goto err_return;
if (je->value_type == JSON_VALUE_STRING)
{
if ((ci= find_class(reinterpret_cast <const char *>(je->value), je->value_len)))
{
if ((ci == &geometrycollection_class && arg == T_GEOMETRIES) || arg == T_COORD)
goto create_geom;
if (arg != T_NONE)
break ; /* invalid arg present for current geometry */
}
else if (je->value_len == feature_coll_type_len &&
my_charset_latin1.strnncoll(je->value, je->value_len,
feature_coll_type, feature_coll_type_len) == 0 )
{
/*
' FeatureCollection ' type found . Handle the ' Featurecollection '
/ ' features ' GeoJSON construction .
*/
ci= &geometrycollection_class;
if (arg == T_FEATURE)
goto create_geom;
if (arg != T_NONE)
break ; /* invalid arg present for current geometry */
}
else if (je->value_len == feature_type_len &&
my_charset_latin1.strnncoll(je->value, je->value_len,
feature_type, feature_type_len) == 0 )
{
if (arg == T_GEOMETRY)
goto handle_geometry_key;
feature_type_found= true ;
if (arg != T_NONE)
break ; /* invalid arg present for current geometry */
}
else /* can't understand the type. */
break ;
}
else /* The "type" value can only be string. */
break ;
}
else if (key_len == coord_keyname_len &&
memcmp(key_buf, coord_keyname, coord_keyname_len) == 0 )
{
if (arg != T_NONE)
break ; /* previous arg unprocessed */
/*
Found the " coordinates " key . Let ' s check it ' s an array
and remember where it starts .
*/
if (json_read_value(je))
goto err_return;
if (je->value_type == JSON_VALUE_ARRAY)
{
arg= T_COORD;
if (ci && ci != &geometrycollection_class)
{
je_arg= je;
goto create_geom;
}
argje= *je;
je_arg= &argje;
if (json_skip_level(je))
goto err_return;
}
else
break ; /* coordinates needs to be an array */
}
else if (key_len == geometries_keyname_len &&
memcmp(key_buf, geometries_keyname, geometries_keyname_len) == 0 )
{
if (arg != T_NONE)
break ; /* previous arg unprocessed */
/*
Found the " geometries " key . Let ' s check it ' s an array
and remember where it starts .
*/
if (json_read_value(je))
goto err_return;
if (je->value_type == JSON_VALUE_ARRAY)
{
if (ci == &geometrycollection_class)
{
je_arg= je;
goto create_geom;
}
if (ci != nullptr)
break ; /* geometries only valid inside geometrycollation */
arg= T_GEOMETRIES;
argje= *je;
je_arg= &argje;
/* skip geometries for now and search for type */
if (json_skip_level(je))
goto err_return;
}
else
break ; /* geometries needs to be an array */
}
else if (key_len == features_keyname_len &&
memcmp(key_buf, features_keyname, features_keyname_len) == 0 )
{
if (arg != T_NONE)
break ; /* previous arg unprocessed */
/*
' features ' key found . Handle the ' Featurecollection ' / ' features '
GeoJSON construction .
*/
if (json_read_value(je))
goto err_return;
if (je->value_type == JSON_VALUE_ARRAY)
{
if (ci == &geometrycollection_class)
{
je_arg= je;
goto create_geom;
}
if (ci != nullptr)
break ; /* features only valid inside featurecollation */
arg= T_FEATURE;
argje= *je;
je_arg= &argje;
/* skip features for now and search for type */
if (json_skip_level(je))
goto err_return;
}
else
break ; /* feature collections needs to be an array */
}
else if (key_len == geometry_keyname_len &&
memcmp(key_buf, geometry_keyname, geometry_keyname_len) == 0 )
{
if (json_read_value(je))
goto err_return;
if (je->value_type == JSON_VALUE_OBJECT)
{
if (feature_type_found)
{
je_arg= je;
goto handle_geometry_key;
}
if (ci != nullptr)
break ; /* geometry only valid inside feature */
arg= T_GEOMETRY;
argje= *je;
je_arg= &argje;
if (json_skip_level(je))
goto err_return;
}
else
break ; /* geometry needs to be an object */
}
else
{
if (json_skip_key(je))
goto err_return;
}
}
if (je->s.error == 0 )
{
/*
We didn ' t find all the required keys . That are " type " and " coordinates "
or " geometries " for GeometryCollection .
*/
je->s.error= GEOJ_INCORRECT_GEOJSON;
}
goto err_return;
create_geom:
if (res->reserve(1 + 4 , 512 ))
goto err_return;
result= (*ci->m_create_func)(buffer->data);
res->q_append((char ) wkb_ndr);
res->q_append((uint32) result->get_class_info()->m_type_id);
if (result->init_from_json(je_arg, er_on_3D, res))
{
if (je_arg != je) /* copy error out of copied engine */
*je= *je_arg;
goto err_return;
}
/* finish of the object scan for validation/geomcollection */
if (json_skip_level(je))
goto err_return;
return result;
handle_geometry_key: /* feature */
result= create_from_json(buffer, je_arg, er_on_3D, res);
/* skip rest of feature - can be arbitrary fields */
if (json_skip_level(je))
goto err_return;
return result;
err_return:
return NULL;
}
Geometry *Geometry::create_from_opresult(Geometry_buffer *g_buf,
String *res, Gcalc_result_receiver &rr)
{
uint32 geom_type= rr.get_result_typeid();
Geometry *obj= create_by_typeid(g_buf, geom_type);
if (!obj || res->reserve(WKB_HEADER_SIZE, 512 ))
return NULL;
res->q_append((char ) wkb_ndr);
res->q_append(geom_type);
return obj->init_from_opresult(res, rr.result(), rr.length()) ? obj : NULL;
}
bool Geometry::envelope(String *result) const
{
MBR mbr;
const char *end;
if (get_mbr(&mbr, &end))
return 1 ;
if (!mbr.valid())
{
/* Empty geometry */
if (result->reserve(1 + 4 *2 ))
return 1 ;
result->q_append((char ) wkb_ndr);
result->q_append((uint32) wkb_geometrycollection);
result->q_append((uint32) 0 );
return 0 ;
}
if (result->reserve(1 + 4 * 3 + SIZEOF_STORED_DOUBLE * 10 ))
return 1 ;
result->q_append((char ) wkb_ndr);
result->q_append((uint32) wkb_polygon);
result->q_append((uint32) 1 );
result->q_append((uint32) 5 );
result->q_append(mbr.xmin);
result->q_append(mbr.ymin);
result->q_append(mbr.xmax);
result->q_append(mbr.ymin);
result->q_append(mbr.xmax);
result->q_append(mbr.ymax);
result->q_append(mbr.xmin);
result->q_append(mbr.ymax);
result->q_append(mbr.xmin);
result->q_append(mbr.ymin);
return 0 ;
}
int Geometry::is_simple(int *simple) const {
Gcalc_scan_iterator scan_it;
Gcalc_heap collector;
Gcalc_function func;
Gcalc_operation_transporter trn(&func, &collector);
const char *c_end;
MBR mbr;
*simple= 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);
while (scan_it.more_points())
{
const Gcalc_scan_iterator::event_point *ev, *next_ev;
if (scan_it.step())
return 1 ;
ev= scan_it.get_events();
if (ev->simple_event())
continue ;
next_ev= ev->get_next();
if ((ev->event & (scev_thread | scev_single_point)) && !next_ev)
continue ;
if ((ev->event == scev_two_threads) && !next_ev->get_next())
continue ;
/* If the first and last points of a curve coincide - that is */
/* an exception to the rule and the line is considered as simple. */
if ((next_ev && !next_ev->get_next()) &&
(ev->event & (scev_thread | scev_end)) &&
(next_ev->event & (scev_thread | scev_end)))
continue ;
return 0 ;
}
*simple= 1 ;
return 0 ;
}
/*
Create a point from data .
SYNPOSIS
create_point ( )
result Put result here
data Data for point is here .
RETURN
0 ok
1 Can ' t reallocate ' result '
*/
bool Geometry::create_point(String *result, const char *data) const
{
if (no_data(data, POINT_DATA_SIZE) ||
result->reserve(1 + 4 + POINT_DATA_SIZE))
return 1 ;
result->q_append((char ) wkb_ndr);
result->q_append((uint32) wkb_point);
/* Copy two double in same format */
result->q_append(data, POINT_DATA_SIZE);
return 0 ;
}
/*
Create a point from coordinates .
SYNPOSIS
create_point ( )
result Put result here
x x coordinate for point
y y coordinate for point
RETURN
0 ok
1 Can ' t reallocate ' result '
*/
bool Geometry::create_point(String *result, double x, double y)
{
if (result->reserve(1 + 4 + POINT_DATA_SIZE))
return 1 ;
result->q_append((char ) wkb_ndr);
result->q_append((uint32) wkb_point);
result->q_append(x);
result->q_append(y);
return 0 ;
}
/*
Append N points from packed format to text
SYNOPSIS
append_points ( )
txt Append points here
n_points Number of points
data Packed data
offset Offset between points
RETURN
# end of data
*/
const char *Geometry::append_points(String *txt, uint32 n_points,
const char *data, uint32 offset) const
{
while (n_points--)
{
double x,y;
data+= offset;
get_point(&x, &y, data);
data+= POINT_DATA_SIZE;
txt->qs_append(x);
txt->qs_append(' ' );
txt->qs_append(y);
txt->qs_append(',' );
}
return data;
}
static void append_json_point(String *txt, uint max_dec, const char *data)
{
double x,y;
get_point(&x, &y, data);
if (max_dec < FLOATING_POINT_DECIMALS)
{
x= my_double_round(x, max_dec, FALSE , FALSE );
y= my_double_round(y, max_dec, FALSE , FALSE );
}
txt->qs_append('[' );
txt->qs_append(x);
txt->qs_append(", " , 2 );
txt->qs_append(y);
txt->qs_append(']' );
}
/*
Append N points from packed format to json
SYNOPSIS
append_json_points ( )
txt Append points here
n_points Number of points
data Packed data
offset Offset between points
RETURN
# end of data
*/
static const char *append_json_points(String *txt, uint max_dec,
uint32 n_points, const char *data, uint32 offset)
{
bool any_points= false ;
txt->qs_append('[' );
while (n_points--)
{
any_points= true ;
data+= offset;
append_json_point(txt, max_dec, data);
data+= POINT_DATA_SIZE;
txt->qs_append(", " , 2 );
}
if (any_points)
txt->length(txt->length() - 2 );// Remove ending ', '
txt->qs_append(']' );
return data;
}
/*
Get most bounding rectangle ( mbr ) for X points
SYNOPSIS
get_mbr_for_points ( )
mbr MBR ( store rectangle here )
points Number of points
data Packed data
offset Offset between points
RETURN
0 Wrong data
# end of data
*/
const char *Geometry::get_mbr_for_points(MBR *mbr, const char *data,
uint offset) const
{
uint32 points;
/* read number of points */
if (no_data(data, 4 ))
return 0 ;
points= uint4korr(data);
data+= 4 ;
if (not_enough_points(data, points, offset))
return 0 ;
/* Calculate MBR for points */
while (points--)
{
data+= offset;
mbr->add_xy(data, data + SIZEOF_STORED_DOUBLE);
data+= POINT_DATA_SIZE;
}
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.emplace_back(x, y);
data+= POINT_DATA_SIZE;
}
return data;
}
/***************************** Point *******************************/
uint32 Gis_point::get_data_size() const
{
return no_data(m_data, POINT_DATA_SIZE) ? GET_SIZE_ERROR : POINT_DATA_SIZE;
}
bool Gis_point::init_from_wkt(Gis_read_stream *trs, String *wkb)
{
double x, y;
if (trs->get_next_number(&x) || trs->get_next_number(&y) ||
wkb->reserve(POINT_DATA_SIZE, 512 ))
return 1 ;
wkb->q_append(x);
wkb->q_append(y);
return 0 ;
}
uint Gis_point::init_from_wkb(const char *wkb, uint len,
wkbByteOrder bo, String *res)
{
double x, y;
if (len < POINT_DATA_SIZE || res->reserve(POINT_DATA_SIZE))
return 0 ;
x= wkb_get_double(wkb, bo);
y= wkb_get_double(wkb + SIZEOF_STORED_DOUBLE, bo);
res->q_append(x);
res->q_append(y);
return POINT_DATA_SIZE;
}
static int read_point_from_json(json_engine_t *je, bool er_on_3D,
double *x, double *y)
{
int n_coord= 0 , err;
double tmp, *d;
char *endptr;
while (json_scan_next(je) == 0 && je->state != JST_ARRAY_END)
{
if (json_read_value(je))
return 1 ;
if (je->value_type != JSON_VALUE_NUMBER)
goto bad_coordinates;
d= (n_coord == 0 ) ? x : ((n_coord == 1 ) ? y : &tmp);
*d= je->s.cs->strntod((char *) je->value, je->value_len, &endptr, &err);
if (err)
goto bad_coordinates;
n_coord++;
}
if (je->s.error != 0 )
return 1 ;
if (n_coord <= 2 || !er_on_3D)
return 0 ;
je->s.error= Geometry::GEOJ_DIMENSION_NOT_SUPPORTED;
return 1 ;
bad_coordinates:
je->s.error= Geometry::GEOJ_INCORRECT_GEOJSON;
return 1 ;
}
bool Gis_point::init_from_json(json_engine_t *je, bool er_on_3D, String *wkb)
{
double x, y;
if (je->value_type != JSON_VALUE_ARRAY)
{
je->s.error= GEOJ_INCORRECT_GEOJSON;
return TRUE ;
}
if (read_point_from_json(je, er_on_3D, &x, &y) ||
wkb->reserve(POINT_DATA_SIZE))
return TRUE ;
wkb->q_append(x);
wkb->q_append(y);
/* Note GEOJSON RFC7946 3.3.1 - 3D possible, but not WKB? */
return FALSE ;
}
bool Gis_point::get_data_as_wkt(String *txt, const char **end) const
{
double x, y;
if (get_xy(&x, &y))
return 1 ;
if (txt->reserve(MAX_DIGITS_IN_DOUBLE * 2 + 1 ))
return 1 ;
txt->qs_append(x);
txt->qs_append(' ' );
txt->qs_append(y);
*end= m_data+ POINT_DATA_SIZE;
return 0 ;
}
bool Gis_point::get_data_as_json(String *txt, uint max_dec_digits,
const char **end) const
{
if (txt->reserve(MAX_DIGITS_IN_DOUBLE * 2 + 4 ))
return 1 ;
append_json_point(txt, max_dec_digits, m_data);
*end= m_data+ POINT_DATA_SIZE;
return 0 ;
}
bool Gis_point::get_mbr(MBR *mbr, const char **end) const
{
double x, y;
if (get_xy(&x, &y))
return 1 ;
mbr->add_xy(x, y);
*end= m_data+ POINT_DATA_SIZE;
return 0 ;
}
int Gis_point::is_valid(int *valid) const
{
double x, y;
if (get_xy(&x, &y))
return 1 ;
*valid= 1 ;
return 0 ;
}
int Gis_point::area(double *ar, const char **end) const
{
*ar= 0 ;
*end= m_data+ POINT_DATA_SIZE;
return 0 ;
}
int Gis_point::geom_length(double *len, const char **end) const
{
*len= 0 ;
*end= m_data+ POINT_DATA_SIZE;
return 0 ;
}
int Gis_point::store_shapes(Gcalc_shape_transporter *trn) const
{
double x, y;
return get_xy(&x, &y) || trn->single_point(x, y);
}
const Geometry::Class_info *Gis_point::get_class_info() const
{
return &point_class;
}
/**
Function to calculate haversine .
Taking as arguments Point and Multipoint geometries .
Multipoint geometry has to be single point only .
It is up to caller to ensure valid input .
@ param g pointer to the Geometry
@ param r sphere radius
@ param error pointer describing the error in case of the boundary conditions
@ return distance in case without error , it is calculated distance ( non - negative ) ,
in case error exist , negative value .
*/
double Gis_point::calculate_haversine(const Geometry *g,
const double sphere_radius,
int *error)
{
DBUG_ASSERT(sphere_radius > 0 );
double x1r, x2r, y1r, y2r;
// This check is done only for optimization purposes where we know it will
// be one and only one point in Multipoint
if (g->get_class_info()->m_type_id == Geometry::wkb_multipoint)
{
const char point_size= 4 + WKB_HEADER_SIZE + POINT_DATA_SIZE+1 ; //1 for the type
char point_temp[point_size];
memset(point_temp+4 , Geometry::wkb_point, 1 );
memcpy(point_temp+5 , static_cast <const Gis_multi_point *>(g)->get_data_ptr()+5 , 4 );
memcpy(point_temp+4 +WKB_HEADER_SIZE, g->get_data_ptr()+4 +WKB_HEADER_SIZE,
POINT_DATA_SIZE);
point_temp[point_size-1 ]= '\0' ;
Geometry_buffer gbuff;
Geometry *gg= Geometry::construct(&gbuff, point_temp, point_size-1 );
if (!gg || static_cast <Gis_point *>(gg)->get_xy_radian(&x2r, &y2r))
{
*error= 2 ;
return -1 ;
}
}
else
{
if (static_cast <const Gis_point *>(g)->get_xy_radian(&x2r, &y2r))
{
*error= 2 ;
return -1 ;
}
}
if (this ->get_xy_radian(&x1r, &y1r))
{
*error= 2 ;
return -1 ;
}
//
// Check boundary conditions: longitude[-180,180]
if (!((x2r >= -M_PI && x2r <= M_PI) && (x1r >= -M_PI && x1r <= M_PI)))
{
*error=1 ;
return -1 ;
}
// Check boundary conditions: latitude[-90,90]
if (!((y2r >= -M_PI/2 && y2r <= M_PI/2 ) && (y1r >= -M_PI/2 && y1r <= M_PI/2 )))
{
*error=-1 ;
return -1 ;
}
double dlat= sin((y2r - y1r)/2 )*sin((y2r - y1r)/2 );
double dlong= sin((x2r - x1r)/2 )*sin((x2r - x1r)/2 );
return 2 *sphere_radius*asin((sqrt(dlat + cos(y1r)*cos(y2r)*dlong)));
}
/**
Function that calculate spherical distance of Point from Multipoint geometries .
In case there is single point in Multipoint geometries calculate_haversine ( )
can handle such case . Otherwise , new geometry ( Point ) has to be constructed .
@ param g pointer to the Geometry
@ param r sphere radius
@ param result pointer to the result
@ param err pointer to the error obtained from calculate_haversin ( )
@ return state
@ retval TRUE failed
@ retval FALSE success
*/
int Gis_point::spherical_distance_multipoints(Geometry *g, const double r,
double *result, int *err)
{
uint32 num_of_points2;
// To find the minimum radius it cannot be greater than Earth radius
double res= 6370986 .0 ;
double temp_res= 0 .0 ;
const uint32 len= 4 + WKB_HEADER_SIZE + POINT_DATA_SIZE + 1 ;
char s[len];
g->num_geometries(&num_of_points2);
DBUG_ASSERT(num_of_points2 >= 1 );
if (num_of_points2 == 1 )
{
*result= this ->calculate_haversine(g, r, err);
return 0 ;
}
for (uint32 i=1 ; i <= num_of_points2; i++)
{
Geometry_buffer buff_temp;
Geometry *temp;
const char *pt_ptr= g->get_data_ptr()+
4 +WKB_HEADER_SIZE*i + POINT_DATA_SIZE*(i-1 );
// First 4 bytes are handled already, make sure to create a Point
memset(s + 4 , Geometry::wkb_point, 1 );
if (g->no_data(pt_ptr, POINT_DATA_SIZE))
return 1 ;
memcpy(s + 5 , g->get_data_ptr() + 5 , 4 );
memcpy(s + 4 + WKB_HEADER_SIZE, pt_ptr, POINT_DATA_SIZE);
s[len-1 ]= '\0' ;
temp= Geometry::construct(&buff_temp, s, len);
if (!temp)
return 1 ;
temp_res= this ->calculate_haversine(temp, r, err);
if (res > temp_res)
res= temp_res;
}
*result= res;
return 0 ;
}
/***************************** LineString *******************************/
uint32 Gis_line_string::get_data_size() const
{
uint32 n_points;
if (no_data(m_data, 4 ))
return GET_SIZE_ERROR;
n_points= uint4korr(m_data);
if (not_enough_points(m_data + 4 , n_points))
return GET_SIZE_ERROR;
return 4 + n_points * POINT_DATA_SIZE;
}
bool Gis_line_string::init_from_wkt(Gis_read_stream *trs, String *wkb)
{
uint32 n_points= 0 ;
uint32 np_pos= wkb->length();
Gis_point p;
if (wkb->reserve(4 , 512 ))
return 1 ;
wkb->length(wkb->length()+4 ); // Reserve space for points
for (;;)
{
if (p.init_from_wkt(trs, wkb))
return 1 ;
n_points++;
if (trs->skip_char(',' )) // Didn't find ','
break ;
}
if (n_points < 1 )
{
trs->set_error_msg("Too few points in LINESTRING" );
return 1 ;
}
wkb->write_at_position(np_pos, n_points);
return 0 ;
}
uint Gis_line_string::init_from_wkb(const char *wkb, uint len,
wkbByteOrder bo, String *res)
{
uint32 n_points, proper_length;
const char *wkb_end;
Gis_point p;
if (len < 4 || (n_points= wkb_get_uint(wkb, bo)) < 1 ||
((len - 4 ) / POINT_DATA_SIZE) < n_points)
return 0 ;
proper_length= 4 + n_points * POINT_DATA_SIZE;
if (len < proper_length || res->reserve(proper_length))
return 0 ;
res->q_append(n_points);
wkb_end= wkb + proper_length;
for (wkb+= 4 ; wkb<wkb_end; wkb+= POINT_DATA_SIZE)
{
if (!p.init_from_wkb(wkb, POINT_DATA_SIZE, bo, res))
return 0 ;
}
return proper_length;
}
bool Gis_line_string::init_from_json(json_engine_t *je, bool er_on_3D,
String *wkb)
{
uint32 n_points= 0 ;
uint32 np_pos= wkb->length();
Gis_point p;
if (je->value_type != JSON_VALUE_ARRAY)
{
err_geoj_incorrect:
je->s.error= GEOJ_INCORRECT_GEOJSON;
return TRUE ;
}
if (wkb->reserve(4 , 512 ))
return TRUE ;
wkb->length(wkb->length()+4 ); // Reserve space for n_points
while (json_scan_next(je) == 0 && je->state != JST_ARRAY_END)
{
if (je->state != JST_VALUE)
goto err_geoj_incorrect;
if (json_read_value(je))
return TRUE ;
if (je->value_type != JSON_VALUE_ARRAY)
goto err_geoj_incorrect;
if (p.init_from_json(je, er_on_3D, wkb))
return TRUE ;
n_points++;
}
if (n_points < 1 )
{
je->s.error= Geometry::GEOJ_TOO_FEW_POINTS;
return TRUE ;
}
wkb->write_at_position(np_pos, n_points);
return FALSE ;
}
bool Gis_line_string::get_data_as_wkt(String *txt, const char **end) const
{
uint32 n_points;
const char *data= m_data;
if (no_data(data, 4 ))
return 1 ;
n_points= uint4korr(data);
data += 4 ;
if (n_points < 1 ||
not_enough_points(data, n_points) ||
txt->reserve(((MAX_DIGITS_IN_DOUBLE + 1 )*2 + 1 ) * n_points))
return 1 ;
while (n_points--)
{
double x, y;
get_point(&x, &y, data);
data+= POINT_DATA_SIZE;
txt->qs_append(x);
txt->qs_append(' ' );
txt->qs_append(y);
txt->qs_append(',' );
}
txt->length(txt->length() - 1 ); // Remove end ','
*end= data;
return 0 ;
}
bool Gis_line_string::get_data_as_json(String *txt, uint max_dec_digits,
const char **end) const
{
uint32 n_points;
const char *data= m_data;
if (no_data(data, 4 ))
return 1 ;
n_points= uint4korr(data);
data += 4 ;
if (n_points < 1 ||
not_enough_points(data, n_points) ||
txt->reserve((MAX_DIGITS_IN_DOUBLE*2 + 6 ) * n_points + 2 ))
return 1 ;
*end= append_json_points(txt, max_dec_digits, n_points, data, 0 );
return 0 ;
}
bool Gis_line_string::get_mbr(MBR *mbr, const char **end) const
{
return (*end=get_mbr_for_points(mbr, m_data, 0 )) == 0 ;
}
int Gis_line_string::geom_length(double *len, const char **end) const
{
uint32 n_points;
double prev_x, prev_y;
const char *data= m_data;
*len= 0 ; // In case of errors
if (no_data(data, 4 ))
return 1 ;
n_points= uint4korr(data);
data+= 4 ;
if (n_points < 1 || not_enough_points(data, n_points))
return 1 ;
get_point(&prev_x, &prev_y, data);
data+= POINT_DATA_SIZE;
while (--n_points)
{
double x, y;
get_point(&x, &y, data);
data+= POINT_DATA_SIZE;
*len+= sqrt(pow(prev_x-x,2 )+pow(prev_y-y,2 ));
prev_x= x;
prev_y= y;
}
*end= data;
return 0 ;
}
int Gis_line_string::area(double *ar, const char **end) const
{
uint32 n_points;
*ar= 0 .0 ;
/* read number of points */
if (no_data(m_data, 4 ))
return 1 ;
n_points= uint4korr(m_data);
*end= m_data + 4 + POINT_DATA_SIZE * n_points;
return 0 ;
}
int Gis_line_string::is_closed(int *closed) const
{
uint32 n_points;
double x1, y1, x2, y2;
const char *data= m_data;
if (no_data(data, 4 ))
return 1 ;
n_points= uint4korr(data);
if (n_points == 1 )
{
*closed=1 ;
return 0 ;
}
data+= 4 ;
if (n_points == 0 || not_enough_points(data, n_points))
return 1 ;
/* Get first point */
get_point(&x1, &y1, data);
/* get last point */
data+= POINT_DATA_SIZE + (n_points-2 )*POINT_DATA_SIZE;
get_point(&x2, &y2, data);
*closed= (x1==x2) && (y1==y2);
return 0 ;
}
int Gis_line_string::is_valid(int *valid) const
{
Geometry_buffer buffer;
Geometry *geometry;
uint32 num_points;
*valid= 0 ;
if (no_data(m_data, 4 ))
return 1 ;
num_points= uint4korr(m_data);
if (not_enough_points(m_data, num_points))
return 1 ;
double x, y, UNINIT_VAR(previous_x), UNINIT_VAR(previous_y);
for (uint32 i = 1 ; i <= num_points; i++)
{
String wkb= 0 ;
if (wkb.reserve(SRID_SIZE + BYTE_ORDER_SIZE + WKB_HEADER_SIZE))
return 1 ;
wkb.q_append(SRID_PLACEHOLDER);
this ->point_n(i, &wkb);
if (!(geometry= Geometry::construct(&buffer, wkb.ptr(), wkb.length()))||
((Gis_point *) geometry)->get_xy(&x, &y))
return 1 ;
if ((i != 1 ) && (x != previous_x || y != previous_y))
{
*valid= 1 ;
return 0 ;
}
previous_x = x;
previous_y = y;
}
return 0 ;
}
int Gis_line_string::simplify(String *result, double max_distance) const {
Geometry_buffer buffer;
Geometry *geometry= NULL;
std::vector<st_point_2d> points;
double x, y;
uint32 n_points= 0 ;
if (this ->num_points(&n_points))
return 1 ;
for (uint32 i = 1 ; i <= n_points; i++)
{
String wkb= 0 ;
if (wkb.reserve(SRID_SIZE + WKB_HEADER_SIZE + POINT_DATA_SIZE))
return 1 ;
wkb.q_append(SRID_PLACEHOLDER);
this ->point_n(i, &wkb);
if (!(geometry= Geometry::construct(&buffer, wkb.ptr(), wkb.length())))
return 1 ;
if (((Gis_point *) geometry)->get_xy(&x, &y))
return 1 ;
points.push_back(st_point_2d{x, y});
}
simplify_RDP(points, max_distance);
result->length(0 );
result->reserve(SRID_SIZE + WKB_HEADER_SIZE +
(POINT_DATA_SIZE * points.size()));
result->q_append(SRID_PLACEHOLDER);
result->q_append((char ) wkb_ndr);
result->q_append((uint32) wkb_linestring);
result->q_append((uint32) points.size());
for (auto point : points)
{
result->q_append((double ) point.x);
result->q_append((double ) point.y);
}
return 0 ;
}
int Gis_line_string::num_points(uint32 *n_points) const
{
if (no_data(m_data, 4 ))
return 1 ;
*n_points= uint4korr(m_data);
return 0 ;
}
int Gis_line_string::start_point(String *result) const
{
/* +4 is for skipping over number of points */
return create_point(result, m_data + 4 );
}
int Gis_line_string::end_point(String *result) const
{
uint32 n_points;
if (no_data(m_data, 4 ))
return 1 ;
n_points= uint4korr(m_data);
if (n_points == 0 || not_enough_points(m_data+4 , n_points))
return 1 ;
return create_point(result, m_data + 4 + (n_points - 1 ) * POINT_DATA_SIZE);
}
int Gis_line_string::point_n(uint32 num, String *result) const
{
uint32 n_points;
if (no_data(m_data, 4 ))
return 1 ;
num--;
n_points= uint4korr(m_data);
if (num >= n_points || not_enough_points(m_data+4 , n_points))
return 1 ;
return create_point(result, m_data + 4 + num*POINT_DATA_SIZE);
}
int Gis_line_string::store_shapes(Gcalc_shape_transporter *trn) const
{
uint32 n_points;
double x, y;
double UNINIT_VAR(prev_x), UNINIT_VAR(prev_y);
int first_point= 1 ;
const char *data= m_data;
if (no_data(m_data, 4 ))
return 1 ;
n_points= uint4korr(data);
data+= 4 ;
if (n_points < 1 || not_enough_points(data, n_points))
return 1 ;
trn->start_line();
while (n_points--)
{
get_point(&x, &y, data);
data+= POINT_DATA_SIZE;
if (!first_point && x == prev_x && y == prev_y)
continue ;
if (trn->add_point(x, y))
return 1 ;
first_point= 0 ;
prev_x= x;
prev_y= y;
}
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;
}
/***************************** Polygon *******************************/
uint32 Gis_polygon::get_data_size() const
{
uint32 n_linear_rings;
uint32 n_points;
const char *data= m_data;
if (no_data(data, 4 ))
return GET_SIZE_ERROR;
n_linear_rings= uint4korr(data);
data+= 4 ;
while (n_linear_rings--)
{
if (no_data(data, 4 ) ||
not_enough_points(data+4 , n_points= uint4korr(data)))
return GET_SIZE_ERROR;
data+= 4 + n_points*POINT_DATA_SIZE;
}
if (no_data(data, 0 ))
return GET_SIZE_ERROR;
return (uint32) (data - m_data);
}
bool Gis_polygon::init_from_wkt(Gis_read_stream *trs, String *wkb)
{
uint32 n_linear_rings= 0 ;
uint32 lr_pos= wkb->length();
int closed;
if (wkb->reserve(4 , 512 ))
return 1 ;
wkb->length(wkb->length()+4 ); // Reserve space for n_rings
for (;;)
{
Gis_line_string ls;
uint32 ls_pos=wkb->length();
if (trs->check_next_symbol('(' ) ||
ls.init_from_wkt(trs, wkb) ||
trs->check_next_symbol(')' ))
return 1 ;
ls.set_data_ptr(wkb->ptr() + ls_pos, wkb->length() - ls_pos);
if (ls.is_closed(&closed) || !closed)
{
trs->set_error_msg("POLYGON's linear ring isn't closed" );
return 1 ;
}
n_linear_rings++;
if (trs->skip_char(',' )) // Didn't find ','
break ;
}
wkb->write_at_position(lr_pos, n_linear_rings);
return 0 ;
}
uint Gis_polygon::init_from_opresult(String *bin,
const char *opres, uint res_len)
{
const char *opres_orig= opres;
uint32 position= bin->length();
uint32 poly_shapes= 0 ;
if (bin->reserve(4 , 512 ))
return 0 ;
bin->q_append(poly_shapes);
while (opres_orig + res_len > opres)
{
uint32 n_points, proper_length;
const char *op_end, *p1_position;
Gis_point p;
Gcalc_function::shape_type st;
st= (Gcalc_function::shape_type) uint4korr(opres);
if (poly_shapes && st != Gcalc_function::shape_hole)
break ;
poly_shapes++;
n_points= uint4korr(opres + 4 ) + 1 ; /* skip shape type id */
proper_length= 4 + n_points * POINT_DATA_SIZE;
if (bin->reserve(proper_length, 512 ))
return 0 ;
bin->q_append(n_points);
op_end= opres + 8 + (n_points-1 ) * 8 * 2 ;
p1_position= (opres+= 8 );
for (; opres<op_end; opres+= POINT_DATA_SIZE)
{
if (!p.init_from_wkb(opres, POINT_DATA_SIZE, wkb_ndr, bin))
return 0 ;
}
if (!p.init_from_wkb(p1_position, POINT_DATA_SIZE, wkb_ndr, bin))
return 0 ;
}
bin->write_at_position(position, poly_shapes);
return (uint) (opres - opres_orig);
}
uint Gis_polygon::init_from_wkb(const char *wkb, uint len, wkbByteOrder bo,
String *res)
{
uint32 n_linear_rings;
const char *wkb_orig= wkb;
if (len < 4 )
return 0 ;
if (!(n_linear_rings= wkb_get_uint(wkb, bo)))
return 0 ;
if (res->reserve(4 , 512 ))
return 0 ;
advance(wkb,len,4 );
res->q_append(n_linear_rings);
while (n_linear_rings--)
{
Gis_line_string ls;
uint32 ls_pos= res->length();
int ls_len;
int closed;
if (!(ls_len= ls.init_from_wkb(wkb, len, bo, res)))
return 0 ;
ls.set_data_ptr(res->ptr() + ls_pos, res->length() - ls_pos);
if (ls.is_closed(&closed) || !closed)
return 0 ;
advance(wkb,len,ls_len);
}
return (uint) (wkb - wkb_orig);
}
bool Gis_polygon::init_from_json(json_engine_t *je, bool er_on_3D, String *wkb)
{
uint32 n_linear_rings= 0 ;
uint32 lr_pos= wkb->length();
int closed;
if (je->value_type != JSON_VALUE_ARRAY)
{
err_geoj_incorrect:
je->s.error= GEOJ_INCORRECT_GEOJSON;
return TRUE ;
}
if (wkb->reserve(4 , 512 ))
return TRUE ;
wkb->length(wkb->length()+4 ); // Reserve space for n_rings
while (json_scan_next(je) == 0 && je->state != JST_ARRAY_END)
{
Gis_line_string ls;
if (je->state != JST_VALUE)
goto err_geoj_incorrect;
if (json_read_value(je))
return TRUE ;
if (je->value_type != JSON_VALUE_ARRAY)
goto err_geoj_incorrect;
uint32 ls_pos=wkb->length();
if (ls.init_from_json(je, er_on_3D, wkb))
return TRUE ;
ls.set_data_ptr(wkb->ptr() + ls_pos, wkb->length() - ls_pos);
if (ls.is_closed(&closed) || !closed)
{
je->s.error= GEOJ_POLYGON_NOT_CLOSED;
return TRUE ;
}
n_linear_rings++;
}
if (je->s.error)
return TRUE ;
if (n_linear_rings == 0 )
{
je->s.error= Geometry::GEOJ_EMPTY_COORDINATES;
return TRUE ;
}
wkb->write_at_position(lr_pos, n_linear_rings);
return FALSE ;
}
bool Gis_polygon::get_data_as_wkt(String *txt, const char **end) const
{
uint32 n_linear_rings;
const char *data= m_data;
if (no_data(data, 4 ))
return 1 ;
n_linear_rings= uint4korr(data);
data+= 4 ;
while (n_linear_rings--)
{
uint32 n_points;
if (no_data(data, 4 ))
return 1 ;
n_points= uint4korr(data);
data+= 4 ;
if (not_enough_points(data, n_points) ||
txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1 ) * 2 + 1 ) * n_points))
return 1 ;
txt->qs_append('(' );
data= append_points(txt, n_points, data, 0 );
(*txt) [txt->length() - 1 ]= ')' ; // Replace end ','
txt->qs_append(',' );
}
txt->length(txt->length() - 1 ); // Remove end ','
*end= data;
return 0 ;
}
bool Gis_polygon::get_data_as_json(String *txt, uint max_dec_digits,
const char **end) const
{
uint32 n_linear_rings;
const char *data= m_data;
if (no_data(data, 4 ) || txt->reserve(1 , 512 ))
return 1 ;
n_linear_rings= uint4korr(data);
data+= 4 ;
bool any_rings= false ;
txt->qs_append('[' );
while (n_linear_rings--)
{
any_rings= true ;
uint32 n_points;
if (no_data(data, 4 ))
return 1 ;
n_points= uint4korr(data);
data+= 4 ;
if (not_enough_points(data, n_points) ||
txt->reserve(4 + (MAX_DIGITS_IN_DOUBLE * 2 + 6 ) * n_points))
return 1 ;
data= append_json_points(txt, max_dec_digits, n_points, data, 0 );
txt->qs_append(", " , 2 );
}
if (any_rings)
txt->length(txt->length() - 2 );// Remove ending ', '
txt->qs_append(']' );
*end= data;
return 0 ;
}
bool Gis_polygon::get_mbr(MBR *mbr, const char **end) const
{
uint32 n_linear_rings;
const char *data= m_data;
if (no_data(data, 4 ))
return 1 ;
n_linear_rings= uint4korr(data);
data+= 4 ;
while (n_linear_rings--)
{
if (!(data= get_mbr_for_points(mbr, data, 0 )))
return 1 ;
}
*end= data;
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
{
Gcalc_scan_iterator scan_it;
Gcalc_heap collector;
Gcalc_poly_transporter trn(&collector);
MBR mbr;
uint32 num_rings;
const char *c_end;
char *border_count= nullptr, *touches_count= nullptr, *internals= nullptr;
int result= 0 ;
*valid= 0 ;
if (this ->num_interior_ring(&num_rings))
return 1 ;
num_rings++;
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 ;
if (trn.get_error())
goto exit ;
collector.prepare_operation();
scan_it.init(&collector);
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())
{
const Gcalc_scan_iterator::event_point *events;
if (scan_it.step())
{
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 ;
}
}
}
}
}
*valid= 1 ;
exit :
collector.reset();
scan_it.reset();
my_afree(border_count);
my_afree(touches_count);
my_afree(internals);
return result;
}
int Gis_polygon::area(double *ar, const char **end_of_data) const
{
uint32 n_linear_rings;
double result= -1 .0 ;
const char *data= m_data;
if (no_data(data, 4 ))
return 1 ;
n_linear_rings= uint4korr(data);
data+= 4 ;
while (n_linear_rings--)
{
double prev_x, prev_y;
double lr_area= 0 ;
uint32 n_points;
if (no_data(data, 4 ))
return 1 ;
n_points= uint4korr(data);
if (n_points == 0 ||
not_enough_points(data, n_points))
return 1 ;
get_point(&prev_x, &prev_y, data+4 );
data+= (4 +POINT_DATA_SIZE);
while (--n_points) // One point is already read
{
double x, y;
get_point(&x, &y, data);
data+= POINT_DATA_SIZE;
lr_area+= (prev_x + x)* (prev_y - y);
prev_x= x;
prev_y= y;
}
lr_area= fabs(lr_area)/2 ;
if (result == -1 .0 )
result= lr_area;
else
result-= lr_area;
}
*ar= fabs(result);
*end_of_data= data;
return 0 ;
}
int Gis_polygon::simplify(String *result, double max_distance) const
{
uint32 num_interior_ring= 0 , num_invalid_ring= 0 , num_points;
Geometry_buffer buffer;
Geometry *geometry= NULL;
String exterior_ring= 0 ;
exterior_ring.q_append(SRID_PLACEHOLDER);
if (this ->num_interior_ring(&num_interior_ring) ||
this ->exterior_ring(&exterior_ring) ||
!(geometry= Geometry::construct(&buffer, exterior_ring.ptr(),
exterior_ring.length())))
return 1 ;
if (geometry->simplify(&exterior_ring, max_distance))
return 1 ;
if (!(geometry= Geometry::construct(&buffer, exterior_ring.ptr(),
exterior_ring.length())))
return 1 ;
if (geometry->num_points(&num_points) || num_points <= 3 )
return 1 ;
result->length(0 );
// Reserve room for the SRID, byte order, type, and ring count.
if (result->reserve(SRID_SIZE + WKB_HEADER_SIZE + sizeof (uint32)))
return 1 ;
result->q_append(SRID_PLACEHOLDER);
result->q_append((char ) wkb_ndr);
result->q_append((uint32) wkb_polygon);
result->q_append((uint32) 1 + num_interior_ring);
result->append(exterior_ring.ptr() + SRID_SIZE + WKB_HEADER_SIZE,
(exterior_ring.length() - SRID_SIZE -
WKB_HEADER_SIZE));
for (uint32 i = 1 ; i <= num_interior_ring; i++)
{
String interior_ring= 0 ;
interior_ring.q_append((uint) 0 );
if (this ->interior_ring_n(i, &interior_ring) ||
!(geometry= Geometry::construct(&buffer, interior_ring.ptr(),
interior_ring.length())))
{
num_invalid_ring++;
continue ;
}
if (geometry->simplify(&interior_ring, max_distance))
return 1 ;
if (!(geometry= Geometry::construct(&buffer, interior_ring.ptr(),
interior_ring.length())))
{
num_invalid_ring++;
continue ;
}
if (geometry->num_points(&num_points))
return 1 ;
if (num_points <= 3 )
{
num_invalid_ring++;
continue ;
}
result->append(interior_ring.ptr() + SRID_SIZE + WKB_HEADER_SIZE,
(interior_ring.length() - SRID_SIZE - WKB_HEADER_SIZE));
}
result->write_at_position(SRID_SIZE + WKB_HEADER_SIZE,
((uint32) 1 + num_interior_ring -
num_invalid_ring));
return 0 ;
}
int Gis_polygon::exterior_ring(String *result) const
{
uint32 n_points, length;
const char *data= m_data + 4 ; // skip n_linerings
if (no_data(data, 4 ))
return 1 ;
n_points= uint4korr(data);
data+= 4 ;
length= n_points * POINT_DATA_SIZE;
if (not_enough_points(data, n_points) || result->reserve(1 +4 +4 + length))
return 1 ;
result->q_append((char ) wkb_ndr);
result->q_append((uint32) wkb_linestring);
result->q_append(n_points);
result->q_append(data, n_points * POINT_DATA_SIZE);
return 0 ;
}
int Gis_polygon::num_interior_ring(uint32 *n_int_rings) const
{
if (no_data(m_data, 4 ))
return 1 ;
*n_int_rings= uint4korr(m_data)-1 ;
return 0 ;
}
int Gis_polygon::interior_ring_n(uint32 num, String *result) const
{
const char *data= m_data;
uint32 n_linear_rings;
uint32 n_points;
uint32 points_size;
if (no_data(data, 4 ))
return 1 ;
n_linear_rings= uint4korr(data);
data+= 4 ;
if (num >= n_linear_rings || num < 1 )
return 1 ;
while (num--)
{
if (no_data(data, 4 ))
return 1 ;
data+= 4 + uint4korr(data) * POINT_DATA_SIZE;
}
if (no_data(data, 4 ))
return 1 ;
n_points= uint4korr(data);
points_size= n_points * POINT_DATA_SIZE;
data+= 4 ;
if (not_enough_points(data, n_points) || result->reserve(1 +4 +4 + points_size))
return 1 ;
result->q_append((char ) wkb_ndr);
result->q_append((uint32) wkb_linestring);
result->q_append(n_points);
result->q_append(data, points_size);
return 0 ;
}
int Gis_polygon::centroid_xy(double *x, double *y) const
{
uint32 n_linear_rings;
double UNINIT_VAR(res_area);
double UNINIT_VAR(res_cx), UNINIT_VAR(res_cy);
const char *data= m_data;
bool first_loop= 1 ;
if (no_data(data, 4 ) ||
(n_linear_rings= uint4korr(data)) == 0 )
return 1 ;
data+= 4 ;
while (n_linear_rings--)
{
uint32 n_points, org_n_points;
double prev_x, prev_y;
double cur_area= 0 ;
double cur_cx= 0 , cur_cy= 0 ;
double sum_cx= 0 , sum_cy= 0 ;
if (no_data(data, 4 ))
return 1 ;
org_n_points= n_points= uint4korr(data);
data+= 4 ;
if (n_points == 0 || not_enough_points(data, n_points))
return 1 ;
get_point(&prev_x, &prev_y, data);
data+= POINT_DATA_SIZE;
while (--n_points) // One point is already read
{
double tmp_x, tmp_y;
double loc_area;
get_point(&tmp_x, &tmp_y, data);
data+= POINT_DATA_SIZE;
loc_area= prev_x * tmp_y - tmp_x * prev_y;
cur_area+= loc_area;
cur_cx+= tmp_x;
cur_cy+= tmp_y;
sum_cx+= (prev_x + tmp_x) * loc_area;
sum_cy+= (prev_y + tmp_y) * loc_area;
prev_x= tmp_x;
prev_y= tmp_y;
}
if (fabs(cur_area) > 1 e-10 )
{
cur_cx= sum_cx / cur_area / 3 .0 ;
cur_cy= sum_cy / cur_area / 3 .0 ;
}
else
{
cur_cx= cur_cx / (org_n_points - 1 );
cur_cy= cur_cy / (org_n_points - 1 );
}
cur_area= fabs(cur_area);
if (!first_loop)
{
double d_area= fabs(res_area - cur_area);
res_cx= (res_area * res_cx - cur_area * cur_cx) / d_area;
res_cy= (res_area * res_cy - cur_area * cur_cy) / d_area;
}
else
{
first_loop= 0 ;
res_area= cur_area;
res_cx= cur_cx;
res_cy= cur_cy;
}
}
*x= res_cx;
*y= res_cy;
return 0 ;
}
int Gis_polygon::centroid(String *result) const
{
double x, y;
if (centroid_xy(&x, &y))
return 1 ;
return create_point(result, x, y);
}
int Gis_polygon::store_shapes(Gcalc_shape_transporter *trn) const
{
uint32 n_linear_rings;
const char *data= m_data;
double first_x, first_y;
double prev_x, prev_y;
int was_equal_first= 0 ;
if (trn->start_poly())
return 1 ;
if (no_data(data, 4 ))
return 1 ;
n_linear_rings= uint4korr(data);
data+= 4 ;
while (n_linear_rings--)
{
uint32 n_points;
if (no_data(data, 4 ))
return 1 ;
n_points= uint4korr(data);
data+= 4 ;
if (!n_points || not_enough_points(data, n_points))
return 1 ;
trn->start_ring();
get_point(&first_x, &first_y, data);
data+= POINT_DATA_SIZE;
prev_x= first_x;
prev_y= first_y;
if (trn->add_point(first_x, first_y))
return 1 ;
if (--n_points == 0 )
goto single_point_ring;
while (--n_points)
{
double x, y;
get_point(&x, &y, data);
data+= POINT_DATA_SIZE;
if (x == prev_x && y == prev_y)
continue ;
prev_x= x;
prev_y= y;
if (was_equal_first)
{
if (trn->add_point(first_x, first_y))
return 1 ;
was_equal_first= 0 ;
}
if (x == first_x && y == first_y)
{
was_equal_first= 1 ;
continue ;
}
if (trn->add_point(x, y))
return 1 ;
}
data+= POINT_DATA_SIZE;
single_point_ring:
trn->complete_ring();
}
trn->complete_poly();
return 0 ;
}
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;
}
/***************************** MultiPoint *******************************/
uint32 Gis_multi_point::get_data_size() const
{
uint32 n_points;
if (no_data(m_data, 4 ) ||
not_enough_points(m_data+4 , (n_points= uint4korr(m_data)),
WKB_HEADER_SIZE))
return GET_SIZE_ERROR;
return 4 + n_points * (POINT_DATA_SIZE + WKB_HEADER_SIZE);
}
bool Gis_multi_point::init_from_wkt(Gis_read_stream *trs, String *wkb)
{
uint32 n_points= 0 ;
uint32 np_pos= wkb->length();
Gis_point p;
if (wkb->reserve(4 , 512 ))
return 1 ;
wkb->length(wkb->length()+4 ); // Reserve space for points
for (;;)
{
if (wkb->reserve(1 + 4 , 512 ))
return 1 ;
wkb->q_append((char ) wkb_ndr);
wkb->q_append((uint32) wkb_point);
if (p.init_from_wkt(trs, wkb))
return 1 ;
n_points++;
if (trs->skip_char(',' )) // Didn't find ','
break ;
}
wkb->write_at_position(np_pos, n_points); // Store number of found points
return 0 ;
}
uint Gis_multi_point::init_from_opresult(String *bin,
const char *opres, uint res_len)
{
uint bin_size, n_points;
Gis_point p;
const char *opres_end;
n_points= res_len/(4 +8 *2 );
bin_size= n_points * (WKB_HEADER_SIZE + POINT_DATA_SIZE) + 4 ;
if (bin->reserve(bin_size, 512 ))
return 0 ;
bin->q_append(n_points);
opres_end= opres + res_len;
for (; opres < opres_end; opres+= (4 + 8 *2 ))
{
bin->q_append((char )wkb_ndr);
bin->q_append((uint32)wkb_point);
if (!p.init_from_wkb(opres + 4 , POINT_DATA_SIZE, wkb_ndr, bin))
return 0 ;
}
return res_len;
}
uint Gis_multi_point::init_from_wkb(const char *wkb, uint len, wkbByteOrder bo,
String *res)
{
uint32 n_points;
uint proper_size;
Gis_point p;
const char *wkb_end;
if (len < 4 ||
(n_points= wkb_get_uint(wkb, bo)) > max_n_points)
return 0 ;
proper_size= 4 + n_points * (WKB_HEADER_SIZE + POINT_DATA_SIZE);
if (len < proper_size || res->reserve(proper_size))
return 0 ;
res->q_append(n_points);
wkb_end= wkb + proper_size;
for (wkb+=4 ; wkb < wkb_end; wkb+= (WKB_HEADER_SIZE + POINT_DATA_SIZE))
{
res->q_append((char )wkb_ndr);
res->q_append((uint32)wkb_point);
if ((uchar) wkb[0 ] > wkb_ndr) /* invalid */
return 0 ;
if (!p.init_from_wkb(wkb + WKB_HEADER_SIZE,
POINT_DATA_SIZE, (wkbByteOrder) wkb[0 ], res))
return 0 ;
}
return proper_size;
}
bool Gis_multi_point::init_from_json(json_engine_t *je, bool er_on_3D,
String *wkb)
{
uint32 n_points= 0 ;
uint32 np_pos= wkb->length();
Gis_point p;
if (je->value_type != JSON_VALUE_ARRAY)
{
err_geoj_incorrect:
je->s.error= GEOJ_INCORRECT_GEOJSON;
return TRUE ;
}
if (wkb->reserve(4 , 512 ))
return TRUE ;
wkb->length(wkb->length()+4 ); // Reserve space for n_points
while (json_scan_next(je) == 0 && je->state != JST_ARRAY_END)
{
if (je->state != JST_VALUE)
goto err_geoj_incorrect;
if (json_read_value(je))
return TRUE ;
if (je->value_type != JSON_VALUE_ARRAY)
goto err_geoj_incorrect;
if (wkb->reserve(1 + 4 , 512 ))
return TRUE ;
wkb->q_append((char ) wkb_ndr);
wkb->q_append((uint32) wkb_point);
if (p.init_from_json(je, er_on_3D, wkb))
return TRUE ;
n_points++;
}
if (je->s.error)
return TRUE ;
if (n_points == 0 )
{
je->s.error= Geometry::GEOJ_EMPTY_COORDINATES;
return TRUE ;
}
wkb->write_at_position(np_pos, n_points);
return FALSE ;
}
bool Gis_multi_point::get_data_as_wkt(String *txt, const char **end) const
{
uint32 n_points;
if (no_data(m_data, 4 ))
return 1 ;
n_points= uint4korr(m_data);
if (n_points > max_n_points ||
not_enough_points(m_data+4 , n_points, WKB_HEADER_SIZE) ||
txt->reserve(((MAX_DIGITS_IN_DOUBLE + 1 ) * 2 + 1 ) * n_points))
return 1 ;
*end= append_points(txt, n_points, m_data+4 , WKB_HEADER_SIZE);
txt->length(txt->length()-1 ); // Remove end ','
return 0 ;
}
bool Gis_multi_point::get_data_as_json(String *txt, uint max_dec_digits,
const char **end) const
{
uint32 n_points;
if (no_data(m_data, 4 ))
return 1 ;
n_points= uint4korr(m_data);
if (n_points > max_n_points ||
not_enough_points(m_data+4 , n_points, WKB_HEADER_SIZE) ||
txt->reserve((MAX_DIGITS_IN_DOUBLE * 2 + 6 ) * n_points + 2 ))
return 1 ;
*end= append_json_points(txt, max_dec_digits, n_points, m_data+4 ,
WKB_HEADER_SIZE);
return 0 ;
}
int Gis_multi_point::is_valid(int *valid) const
{
uint32 num_points;
if (no_data(m_data, 4 ))
return 1 ;
num_points= uint4korr(m_data);
if (not_enough_points(m_data, num_points))
return 1 ;
*valid= 1 ;
return 0 ;
}
bool Gis_multi_point::get_mbr(MBR *mbr, const char **end) const
{
return (*end= get_mbr_for_points(mbr, m_data, WKB_HEADER_SIZE)) == 0 ;
}
int Gis_multi_point::num_geometries(uint32 *num) const
{
if (no_data(m_data, 4 ))
return 1 ;
*num= uint4korr(m_data);
return 0 ;
}
int Gis_multi_point::geometry_n(uint32 num, String *result) const
{
const char *data= m_data;
uint32 n_points;
if (no_data(data, 4 ))
return 1 ;
n_points= uint4korr(data);
data+= 4 + (num - 1 ) * (WKB_HEADER_SIZE + POINT_DATA_SIZE);
if (num > n_points || num < 1 ||
no_data(data, WKB_HEADER_SIZE + POINT_DATA_SIZE) ||
result->reserve(WKB_HEADER_SIZE + POINT_DATA_SIZE))
return 1 ;
result->q_append(data, WKB_HEADER_SIZE + POINT_DATA_SIZE);
return 0 ;
}
int Gis_multi_point::store_shapes(Gcalc_shape_transporter *trn) const
{
uint32 n_points;
Gis_point pt;
const char *data= m_data;
if (no_data(data, 4 ))
return 1 ;
n_points= uint4korr(data);
data+= 4 ;
if (trn->start_collection(n_points))
return 1 ;
while (n_points--)
{
if (no_data(data, WKB_HEADER_SIZE))
return 1 ;
data+= WKB_HEADER_SIZE;
pt.set_data_ptr(data, (uint32) (m_data_end - data));
if (pt.store_shapes(trn))
return 1 ;
data+= pt.get_data_size();
}
return 0 ;
}
const Geometry::Class_info *Gis_multi_point::get_class_info() const
{
return &multipoint_class;
}
/**
Function that calculate spherical distance of Multipoints geometries .
In case there is single point in Multipoint geometries calculate_haversine ( )
can handle such case . Otherwise , new geometry ( Point ) has to be constructed .
@ param g pointer to the Geometry
@ param r sphere radius
@ param result pointer to the result
@ param err pointer to the error obtained from calculate_haversin ( )
@ return state
@ retval TRUE failed
@ retval FALSE success
*/
int Gis_multi_point::spherical_distance_multipoints(Geometry *g, const double r,
double *result, int *err)
{
const uint32 len= 4 + WKB_HEADER_SIZE + POINT_DATA_SIZE + 1 ;
// Check how many points are stored in Multipoints
uint32 num_of_points1, num_of_points2;
// To find the minimum radius it cannot be greater than Earth radius
double res= 6370986 .0 ;
/* From Item_func_sphere_distance::spherical_distance_points,
we are sure that there will be multiple points and we have to construct
Point geometry and return the smallest result .
*/
if (num_geometries(&num_of_points1))
return 1 ;
DBUG_ASSERT(num_of_points1 >= 1 );
g->num_geometries(&num_of_points2);
DBUG_ASSERT(num_of_points2 >= 1 );
for (uint32 i=1 ; i <= num_of_points1; i++)
{
Geometry_buffer buff_temp;
Geometry *temp;
double temp_res= 0 .0 ;
char s[len];
const char *pt_ptr= get_data_ptr()+
4 +WKB_HEADER_SIZE*i + POINT_DATA_SIZE*(i-1 );
// First 4 bytes are handled already, make sure to create a Point
memset(s + 4 , Geometry::wkb_point, 1 );
if (no_data(pt_ptr, POINT_DATA_SIZE))
return 1 ;
memcpy(s + 5 , this ->get_data_ptr() + 5 , 4 );
memcpy(s + 4 + WKB_HEADER_SIZE, pt_ptr, POINT_DATA_SIZE);
s[len-1 ]= '\0' ;
temp= Geometry::construct(&buff_temp, s, len);
if (!temp)
return 1 ;
// Optimization for single Multipoint
if (num_of_points2 == 1 )
{
*result= static_cast <Gis_point *>(temp)->calculate_haversine(g, r, err);
return 0 ;
}
for (uint32 j=1 ; j<= num_of_points2; j++)
{
Geometry_buffer buff_temp2;
Geometry *temp2;
char s2[len];
const char *pt_ptr= g->get_data_ptr()+
4 +WKB_HEADER_SIZE*j + POINT_DATA_SIZE*(j-1 );
// First 4 bytes are handled already, make sure to create a Point
memset(s2 + 4 , Geometry::wkb_point, 1 );
if (g->no_data(pt_ptr, POINT_DATA_SIZE))
return 1 ;
memcpy(s2 + 5 , g->get_data_ptr() + 5 , 4 );
memcpy(s2 + 4 + WKB_HEADER_SIZE, pt_ptr, POINT_DATA_SIZE);
s2[len-1 ]= '\0' ;
temp2= Geometry::construct(&buff_temp2, s2, len);
if (!temp2)
return 1 ;
temp_res= static_cast <Gis_point *>(temp)->calculate_haversine(temp2, r, err);
if (res > temp_res)
res= temp_res;
}
}
*result= res;
return 0 ;
}
/***************************** MultiLineString *******************************/
uint32 Gis_multi_line_string::get_data_size() const
{
uint32 n_line_strings;
uint32 n_points;
const char *data= m_data;
if (no_data(data, 4 ))
return GET_SIZE_ERROR;
n_line_strings= uint4korr(data);
data+= 4 ;
while (n_line_strings--)
{
if (no_data(data, WKB_HEADER_SIZE + 4 ) ||
not_enough_points(data + WKB_HEADER_SIZE+4 ,
(n_points= uint4korr(data + WKB_HEADER_SIZE))))
return GET_SIZE_ERROR;
data+= (WKB_HEADER_SIZE + 4 + n_points*POINT_DATA_SIZE);
}
if (no_data(data, 0 ))
return GET_SIZE_ERROR;
return (uint32) (data - m_data);
}
bool Gis_multi_line_string::init_from_wkt(Gis_read_stream *trs, String *wkb)
{
uint32 n_line_strings= 0 ;
uint32 ls_pos= wkb->length();
if (wkb->reserve(4 , 512 ))
return 1 ;
wkb->length(wkb->length()+4 ); // Reserve space for points
for (;;)
{
Gis_line_string ls;
if (wkb->reserve(1 + 4 , 512 ))
return 1 ;
wkb->q_append((char ) wkb_ndr);
wkb->q_append((uint32) wkb_linestring);
if (trs->check_next_symbol('(' ) ||
ls.init_from_wkt(trs, wkb) ||
trs->check_next_symbol(')' ))
return 1 ;
n_line_strings++;
if (trs->skip_char(',' )) // Didn't find ','
break ;
}
wkb->write_at_position(ls_pos, n_line_strings);
return 0 ;
}
uint Gis_multi_line_string::init_from_opresult(String *bin,
const char *opres, uint res_len)
{
const char *opres_orig= opres;
int ns_pos= bin->length();
uint n_linestring= 0 ;
if (bin->reserve(4 , 512 ))
return 0 ;
bin->q_append(n_linestring);
while (res_len)
{
Gis_line_string ls;
int ls_len;
if (bin->reserve(WKB_HEADER_SIZE, 512 ))
return 0 ;
bin->q_append((char ) wkb_ndr);
bin->q_append((uint32) wkb_linestring);
if (!(ls_len= ls.init_from_opresult(bin, opres, res_len)))
return 0 ;
opres+= ls_len;
res_len-= ls_len;
n_linestring++;
}
bin->write_at_position(ns_pos, n_linestring);
return (uint) (opres - opres_orig);
}
uint Gis_multi_line_string::init_from_wkb(const char *wkb, uint len,
wkbByteOrder bo, String *res)
{
uint32 n_line_strings;
const char *wkb_orig= wkb;
if (len < 4 ||
(n_line_strings= wkb_get_uint(wkb, bo))< 1 )
return 0 ;
if (res->reserve(4 , 512 ))
return 0 ;
res->q_append(n_line_strings);
advance(wkb,len,4 );
while (n_line_strings--)
{
Gis_line_string ls;
int ls_len;
if ((uchar) wkb[0 ] > wkb_ndr) /* invalid */
return 0 ;
if ((len < WKB_HEADER_SIZE) ||
res->reserve(WKB_HEADER_SIZE, 512 ))
return 0 ;
res->q_append((char ) wkb_ndr);
res->q_append((uint32) wkb_linestring);
wkbByteOrder bo= (wkbByteOrder)wkb[0 ];
advance(wkb,len,WKB_HEADER_SIZE);
if (!(ls_len= ls.init_from_wkb(wkb, len, bo, res)))
return 0 ;
advance(wkb,len,ls_len);
}
return (uint) (wkb - wkb_orig);
}
bool Gis_multi_line_string::init_from_json(json_engine_t *je, bool er_on_3D,
String *wkb)
{
uint32 n_line_strings= 0 ;
uint32 ls_pos= wkb->length();
if (je->value_type != JSON_VALUE_ARRAY)
{
err_geoj_incorrect:
je->s.error= GEOJ_INCORRECT_GEOJSON;
return TRUE ;
}
if (wkb->reserve(4 , 512 ))
return TRUE ;
wkb->length(wkb->length()+4 ); // Reserve space for n_rings
while (json_scan_next(je) == 0 && je->state != JST_ARRAY_END)
{
if (je->state != JST_VALUE)
goto err_geoj_incorrect;
if (json_read_value(je))
return TRUE ;
if (je->value_type != JSON_VALUE_ARRAY)
goto err_geoj_incorrect;
Gis_line_string ls;
if (wkb->reserve(1 + 4 , 512 ))
return TRUE ;
wkb->q_append((char ) wkb_ndr);
wkb->q_append((uint32) wkb_linestring);
if (ls.init_from_json(je, er_on_3D, wkb))
return TRUE ;
n_line_strings++;
}
if (je->s.error)
return TRUE ;
if (n_line_strings == 0 )
{
je->s.error= Geometry::GEOJ_EMPTY_COORDINATES;
return TRUE ;
}
wkb->write_at_position(ls_pos, n_line_strings);
return FALSE ;
}
bool Gis_multi_line_string::get_data_as_wkt(String *txt,
const char **end) const
{
uint32 n_line_strings;
const char *data= m_data;
if (no_data(data, 4 ))
return 1 ;
n_line_strings= uint4korr(data);
data+= 4 ;
while (n_line_strings--)
{
uint32 n_points;
if (no_data(data, (WKB_HEADER_SIZE + 4 )))
return 1 ;
n_points= uint4korr(data + WKB_HEADER_SIZE);
data+= WKB_HEADER_SIZE + 4 ;
if (not_enough_points(data, n_points) ||
txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1 ) * 2 + 1 ) * n_points))
return 1 ;
txt->qs_append('(' );
data= append_points(txt, n_points, data, 0 );
(*txt) [txt->length() - 1 ]= ')' ;
txt->qs_append(',' );
}
txt->length(txt->length() - 1 );
*end= data;
return 0 ;
}
bool Gis_multi_line_string::get_data_as_json(String *txt, uint max_dec_digits,
const char **end) const
{
uint32 n_line_strings;
const char *data= m_data;
if (no_data(data, 4 ) || txt->reserve(1 , 512 ))
return 1 ;
n_line_strings= uint4korr(data);
data+= 4 ;
bool any_ls= false ;
txt->qs_append('[' );
while (n_line_strings--)
{
any_ls= true ;
uint32 n_points;
if (no_data(data, (WKB_HEADER_SIZE + 4 )))
return 1 ;
n_points= uint4korr(data + WKB_HEADER_SIZE);
data+= WKB_HEADER_SIZE + 4 ;
if (not_enough_points(data, n_points) ||
txt->reserve(2 + (MAX_DIGITS_IN_DOUBLE * 2 + 6 ) * n_points))
return 1 ;
data= append_json_points(txt, max_dec_digits, n_points, data, 0 );
txt->qs_append(", " , 2 );
}
if (any_ls)
txt->length(txt->length() - 2 );
txt->qs_append(']' );
*end= data;
return 0 ;
}
int Gis_multi_line_string::is_valid(int *valid) const
{
uint32 num_linestring;
Geometry_buffer buffer;
Geometry *geometry= NULL;
*valid= 0 ;
if (no_data(m_data, 4 ))
return 1 ;
num_linestring= uint4korr(m_data);
for (uint32 i = 1 ; i <= num_linestring; i++)
{
String wkb = 0 ;
wkb.q_append(SRID_PLACEHOLDER);
if (this ->geometry_n(i, &wkb) ||
!(geometry= Geometry::construct(&buffer, wkb.ptr(), wkb.length())))
return 1 ;
int line_valid;
if (geometry->is_valid(&line_valid))
return 1 ;
if (!line_valid)
return 0 ;
}
*valid= 1 ;
return 0 ;
}
bool Gis_multi_line_string::get_mbr(MBR *mbr, const char **end) const
{
uint32 n_line_strings;
const char *data= m_data;
if (no_data(data, 4 ))
return 1 ;
n_line_strings= uint4korr(data);
data+= 4 ;
while (n_line_strings--)
{
if (!(data= get_mbr_for_points(mbr, data + WKB_HEADER_SIZE, 0 )))
return 1 ;
}
*end= data;
return 0 ;
}
int Gis_multi_line_string::num_geometries(uint32 *num) const
{
if (no_data(m_data, 4 ))
return 1 ;
*num= uint4korr(m_data);
return 0 ;
}
int Gis_multi_line_string::geometry_n(uint32 num, String *result) const
{
uint32 n_line_strings, n_points, length;
const char *data= m_data;
if (no_data(data, 4 ))
return 1 ;
n_line_strings= uint4korr(data);
data+= 4 ;
if ((num > n_line_strings) || (num < 1 ))
return 1 ;
for (;;)
{
if (no_data(data, WKB_HEADER_SIZE + 4 ))
return 1 ;
n_points= uint4korr(data + WKB_HEADER_SIZE);
length= WKB_HEADER_SIZE + 4 + POINT_DATA_SIZE * n_points;
if (not_enough_points(data+WKB_HEADER_SIZE+4 , n_points))
return 1 ;
if (!--num)
break ;
data+= length;
}
return result->append(data, length, (uint32) 0 );
}
int Gis_multi_line_string::geom_length(double *len, const char **end) const
{
uint32 n_line_strings;
const char *data= m_data;
const char *line_end;
if (no_data(data, 4 ))
return 1 ;
n_line_strings= uint4korr(data);
data+= 4 ;
*len=0 ;
while (n_line_strings--)
{
double ls_len;
Gis_line_string ls;
if (no_data(data, WKB_HEADER_SIZE))
return 1 ;
data+= WKB_HEADER_SIZE;
ls.set_data_ptr(data, (uint32) (m_data_end - data));
if (ls.geom_length(&ls_len, &line_end))
return 1 ;
*len+= ls_len;
/*
We know here that ls was ok , so we can call the trivial function
Gis_line_string : : get_data_size without error checking
*/
data+= ls.get_data_size();
}
*end= data;
return 0 ;
}
int Gis_multi_line_string::is_closed(int *closed) const
{
uint32 n_line_strings;
const char *data= m_data;
if (no_data(data, 4 + WKB_HEADER_SIZE))
return 1 ;
n_line_strings= uint4korr(data);
data+= 4 + WKB_HEADER_SIZE;
while (n_line_strings--)
{
Gis_line_string ls;
if (no_data(data, 0 ))
return 1 ;
ls.set_data_ptr(data, (uint32) (m_data_end - data));
if (ls.is_closed(closed))
return 1 ;
if (!*closed)
return 0 ;
/*
We know here that ls was ok , so we can call the trivial function
Gis_line_string : : get_data_size without error checking
*/
data+= ls.get_data_size() + WKB_HEADER_SIZE;
}
return 0 ;
}
int Gis_multi_line_string::simplify(String *result, double max_distance) const
{
uint32 num_lines= 0 ;
Geometry_buffer buffer;
Geometry *geometry= NULL;
if (this ->num_geometries(&num_lines))
return 1 ;
result->length(0 );
// Reserve room for the SRID, byte order, type, and line count.
if (result->reserve(SRID_SIZE + WKB_HEADER_SIZE + sizeof (uint32)))
return 1 ;
result->q_append(SRID_PLACEHOLDER);
result->q_append((char ) wkb_ndr);
result->q_append((uint32) wkb_multilinestring);
result->q_append((uint32) num_lines);
for (uint32 i = 1 ; i <= num_lines; i++)
{
String wkb= 0 ;
wkb.q_append((uint) 0 );
this ->geometry_n(i, &wkb);
if (!(geometry= Geometry::construct(&buffer, wkb.ptr(), wkb.length())))
return 1 ;
geometry->simplify(&wkb, max_distance);
result->append(wkb.ptr() + SRID_SIZE, wkb.length() - SRID_SIZE);
}
return 0 ;
}
int Gis_multi_line_string::store_shapes(Gcalc_shape_transporter *trn) const
{
uint32 n_lines;
Gis_line_string ls;
const char *data= m_data;
if (no_data(data, 4 ))
return 1 ;
n_lines= uint4korr(data);
data+= 4 ;
if (trn->start_collection(n_lines))
return 1 ;
while (n_lines--)
{
if (no_data(data, WKB_HEADER_SIZE))
return 1 ;
data+= WKB_HEADER_SIZE;
ls.set_data_ptr(data, (uint32) (m_data_end - data));
if (ls.store_shapes(trn))
return 1 ;
data+= ls.get_data_size();
}
return 0 ;
}
const Geometry::Class_info *Gis_multi_line_string::get_class_info() const
{
return &multilinestring_class;
}
/***************************** MultiPolygon *******************************/
uint32 Gis_multi_polygon::get_data_size() const
{
uint32 n_polygons;
uint32 n_points;
const char *data= m_data;
if (no_data(data, 4 ))
return GET_SIZE_ERROR;
n_polygons= uint4korr(data);
data+= 4 ;
while (n_polygons--)
{
uint32 n_linear_rings;
if (no_data(data, 4 + WKB_HEADER_SIZE))
return GET_SIZE_ERROR;
n_linear_rings= uint4korr(data + WKB_HEADER_SIZE);
data+= 4 + WKB_HEADER_SIZE;
while (n_linear_rings--)
{
if (no_data(data, 4 ) ||
not_enough_points(data+4 , (n_points= uint4korr(data))))
return GET_SIZE_ERROR;
data+= 4 + n_points * POINT_DATA_SIZE;
}
}
if (no_data(data, 0 ))
return GET_SIZE_ERROR;
return (uint32) (data - m_data);
}
bool Gis_multi_polygon::init_from_wkt(Gis_read_stream *trs, String *wkb)
{
uint32 n_polygons= 0 ;
int np_pos= wkb->length();
Gis_polygon p;
if (wkb->reserve(4 , 512 ))
return 1 ;
wkb->length(wkb->length()+4 ); // Reserve space for points
for (;;)
{
if (wkb->reserve(1 + 4 , 512 ))
return 1 ;
wkb->q_append((char ) wkb_ndr);
wkb->q_append((uint32) wkb_polygon);
if (trs->check_next_symbol('(' ) ||
p.init_from_wkt(trs, wkb) ||
trs->check_next_symbol(')' ))
return 1 ;
n_polygons++;
if (trs->skip_char(',' )) // Didn't find ','
break ;
}
wkb->write_at_position(np_pos, n_polygons);
return 0 ;
}
uint Gis_multi_polygon::init_from_wkb(const char *wkb, uint len,
wkbByteOrder bo, String *res)
{
uint32 n_poly;
const char *wkb_orig= wkb;
if (len < 4 )
return 0 ;
n_poly= wkb_get_uint(wkb, bo);
if (res->reserve(4 , 512 ))
return 0 ;
res->q_append(n_poly);
advance(wkb,len,4 );
while (n_poly--)
{
Gis_polygon p;
int p_len;
if (len < WKB_HEADER_SIZE
|| (uchar) wkb[0 ] > wkb_ndr
|| res->reserve(WKB_HEADER_SIZE, 512 ))
return 0 ;
res->q_append((char ) wkb_ndr);
res->q_append((uint32) wkb_polygon);
wkbByteOrder bo= (wkbByteOrder)wkb[0 ];
advance(wkb,len,WKB_HEADER_SIZE);
if (!(p_len= p.init_from_wkb(wkb, len, bo, res)))
return 0 ;
advance(wkb,len,p_len);
}
return (uint) (wkb - wkb_orig);
}
uint Gis_multi_polygon::init_from_opresult(String *bin,
const char *opres, uint res_len)
{
Gis_polygon p;
const char *opres_orig= opres;
uint p_len;
uint32 n_poly= 0 ;
uint32 np_pos= bin->length();
if (bin->reserve(4 , 512 ))
return 0 ;
bin->q_append(n_poly);
while (res_len)
{
if (bin->reserve(1 + 4 , 512 ))
return 0 ;
bin->q_append((char )wkb_ndr);
bin->q_append((uint32)wkb_polygon);
if (!(p_len= p.init_from_opresult(bin, opres, res_len)))
return 0 ;
opres+= p_len;
res_len-= p_len;
n_poly++;
}
bin->write_at_position(np_pos, n_poly);
return (uint)(opres - opres_orig);
}
bool Gis_multi_polygon::init_from_json(json_engine_t *je, bool er_on_3D,
String *wkb)
{
uint32 n_polygons= 0 ;
int np_pos= wkb->length();
Gis_polygon p;
if (je->value_type != JSON_VALUE_ARRAY)
{
err_geoj_incorrect:
je->s.error= GEOJ_INCORRECT_GEOJSON;
return TRUE ;
}
if (wkb->reserve(4 , 512 ))
return TRUE ;
wkb->length(wkb->length()+4 ); // Reserve space for n_rings
while (json_scan_next(je) == 0 && je->state != JST_ARRAY_END)
{
if (je->state != JST_VALUE)
goto err_geoj_incorrect;
if (json_read_value(je))
return TRUE ;
if (je->value_type != JSON_VALUE_ARRAY)
goto err_geoj_incorrect;
if (wkb->reserve(1 + 4 , 512 ))
return TRUE ;
wkb->q_append((char ) wkb_ndr);
wkb->q_append((uint32) wkb_polygon);
if (p.init_from_json(je, er_on_3D, wkb))
return TRUE ;
n_polygons++;
}
if (je->s.error)
return TRUE ;
if (n_polygons == 0 )
{
je->s.error= Geometry::GEOJ_EMPTY_COORDINATES;
return TRUE ;
}
wkb->write_at_position(np_pos, n_polygons);
return FALSE ;
}
bool Gis_multi_polygon::get_data_as_wkt(String *txt, const char **end) const
{
uint32 n_polygons;
const char *data= m_data;
if (no_data(data, 4 ))
return 1 ;
n_polygons= uint4korr(data);
data+= 4 ;
while (n_polygons--)
{
uint32 n_linear_rings;
if (no_data(data, 4 + WKB_HEADER_SIZE) ||
txt->reserve(1 , 512 ))
return 1 ;
n_linear_rings= uint4korr(data+WKB_HEADER_SIZE);
data+= 4 + WKB_HEADER_SIZE;
txt->q_append('(' );
while (n_linear_rings--)
{
if (no_data(data, 4 ))
return 1 ;
uint32 n_points= uint4korr(data);
data+= 4 ;
if (not_enough_points(data, n_points) ||
txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1 ) * 2 + 1 ) * n_points,
512 ))
return 1 ;
txt->qs_append('(' );
data= append_points(txt, n_points, data, 0 );
(*txt) [txt->length() - 1 ]= ')' ;
txt->qs_append(',' );
}
(*txt) [txt->length() - 1 ]= ')' ;
txt->qs_append(',' );
}
txt->length(txt->length() - 1 );
*end= data;
return 0 ;
}
bool Gis_multi_polygon::get_data_as_json(String *txt, uint max_dec_digits,
const char **end) const
{
uint32 n_polygons;
const char *data= m_data;
if (no_data(data, 4 ) || txt->reserve(1 , 512 ))
return 1 ;
n_polygons= uint4korr(data);
data+= 4 ;
bool any_polygons= false ;
txt->q_append('[' );
while (n_polygons--)
{
any_polygons= true ;
uint32 n_linear_rings;
if (no_data(data, 4 + WKB_HEADER_SIZE) ||
txt->reserve(1 , 512 ))
return 1 ;
n_linear_rings= uint4korr(data+WKB_HEADER_SIZE);
data+= 4 + WKB_HEADER_SIZE;
txt->q_append('[' );
bool any_rings= false ;
while (n_linear_rings--)
{
any_rings= true ;
if (no_data(data, 4 ))
return 1 ;
uint32 n_points= uint4korr(data);
data+= 4 ;
if (not_enough_points(data, n_points) ||
txt->reserve(2 + (MAX_DIGITS_IN_DOUBLE * 2 + 6 ) * n_points,
512 ))
return 1 ;
data= append_json_points(txt, max_dec_digits, n_points, data, 0 );
txt->qs_append(", " , 2 );
}
if (any_rings)
txt->length(txt->length() - 2 );
txt->qs_append("], " , 3 );
}
if (any_polygons)
txt->length(txt->length() - 2 );
txt->q_append(']' );
*end= data;
return 0 ;
}
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 result= 0 ;
Gcalc_scan_iterator scan_it;
Gcalc_heap collector;
Gcalc_multipoly_transporter trn(&collector);
MBR mbr;
uint32 num_geometries;
const char *c_end;
char *internals;
if (this ->num_geometries(&num_geometries))
return 1 ;
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())
{
const Gcalc_scan_iterator::event_point *events, *next_ev;
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 ;
}
}
}
}
*valid= 1 ;
exit :
collector.reset();
scan_it.reset();
my_afree(internals);
return result;
}
bool Gis_multi_polygon::get_mbr(MBR *mbr, const char **end) const
{
uint32 n_polygons;
const char *data= m_data;
if (no_data(data, 4 ))
return 1 ;
n_polygons= uint4korr(data);
data+= 4 ;
while (n_polygons--)
{
uint32 n_linear_rings;
if (no_data(data, 4 +WKB_HEADER_SIZE))
return 1 ;
n_linear_rings= uint4korr(data + WKB_HEADER_SIZE);
data+= WKB_HEADER_SIZE + 4 ;
while (n_linear_rings--)
{
if (!(data= get_mbr_for_points(mbr, data, 0 )))
return 1 ;
}
}
*end= data;
return 0 ;
}
int Gis_multi_polygon::num_geometries(uint32 *num) const
{
if (no_data(m_data, 4 ))
return 1 ;
*num= uint4korr(m_data);
return 0 ;
}
int Gis_multi_polygon::geometry_n(uint32 num, String *result) const
{
uint32 n_polygons;
const char *data= m_data, *start_of_polygon;
if (no_data(data, 4 ))
return 1 ;
n_polygons= uint4korr(data);
data+= 4 ;
if (num > n_polygons || num < 1 )
return -1 ;
do
{
uint32 n_linear_rings;
start_of_polygon= data;
if (no_data(data, WKB_HEADER_SIZE + 4 ))
return 1 ;
n_linear_rings= uint4korr(data + WKB_HEADER_SIZE);
data+= WKB_HEADER_SIZE + 4 ;
while (n_linear_rings--)
{
uint32 n_points;
if (no_data(data, 4 ))
return 1 ;
n_points= uint4korr(data);
if (not_enough_points(data + 4 , n_points))
return 1 ;
data+= 4 + POINT_DATA_SIZE * n_points;
}
} while (--num);
if (no_data(data, 0 )) // We must check last segment
return 1 ;
return result->append(start_of_polygon, (uint32) (data - start_of_polygon),
(uint32) 0 );
}
int Gis_multi_polygon::area(double *ar, const char **end_of_data) const
{
uint32 n_polygons;
const char *data= m_data;
double result= 0 ;
if (no_data(data, 4 ))
return 1 ;
n_polygons= uint4korr(data);
data+= 4 ;
while (n_polygons--)
{
double p_area;
Gis_polygon p;
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.area(&p_area, &data))
return 1 ;
result+= p_area;
}
*ar= result;
*end_of_data= data;
return 0 ;
}
int Gis_multi_polygon::simplify(String *result, double max_distance) const
{
uint32 num_polygon= 0 , num_invalid_polygon= 0 ;
Geometry_buffer buffer;
Geometry *geometry= NULL;
if (this ->num_geometries(&num_polygon))
return 1 ;
result->length(0 );
// Reserve room for the SRID, byte order, type, and polygon count.
if (result->reserve(SRID_SIZE + WKB_HEADER_SIZE + sizeof (uint32)))
return 1 ;
result->q_append(SRID_PLACEHOLDER);
result->q_append((char ) wkb_ndr);
result->q_append((uint32) wkb_multipolygon);
result->q_append((uint32) num_polygon);
for (uint32 i = 1 ; i <= num_polygon; i++)
{
String polygon= 0 , simplified_polygon= 0 ;
polygon.q_append((uint) 0 );
if (this ->geometry_n(i, &polygon) ||
!(geometry= Geometry::construct(&buffer, polygon.ptr(),
polygon.length())))
return 1 ;
if (geometry->simplify(&simplified_polygon, max_distance))
{
num_invalid_polygon++;
continue ;
}
result->append(simplified_polygon.ptr() + SRID_SIZE,
simplified_polygon.length() - SRID_SIZE);
}
if (num_polygon == num_invalid_polygon)
return 1 ;
result->write_at_position(SRID_SIZE + WKB_HEADER_SIZE,
((uint32) num_polygon - num_invalid_polygon));
return 0 ;
}
int Gis_multi_polygon::centroid(String *result) const
{
uint32 n_polygons;
Gis_polygon p;
double res_area= 0 .0 , res_cx= 0 .0 , res_cy= 0 .0 ;
double cur_area, cur_cx, cur_cy;
const char *data= m_data;
if (no_data(data, 4 ))
return 1 ;
n_polygons= uint4korr(data);
data+= 4 ;
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.area(&cur_area, &data) ||
p.centroid_xy(&cur_cx, &cur_cy))
return 1 ;
res_area+= cur_area;
res_cx+= cur_area * cur_cx;
res_cy+= cur_area * cur_cy;
}
res_cx/= res_area;
res_cy/= res_area;
return create_point(result, res_cx, res_cy);
}
int Gis_multi_polygon::store_shapes(Gcalc_shape_transporter *trn) 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 ;
if (trn->start_collection(n_polygons))
return 1 ;
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.store_shapes(trn))
return 1 ;
data+= p.get_data_size();
}
return 0 ;
}
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;
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 ;
// Reserve space for the byte order and GIS type.
if (result->reserve(sizeof (char ) + sizeof (uint32)))
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;
}
/************************* GeometryCollection ****************************/
uint32 Gis_geometry_collection::get_data_size() const
{
uint32 n_objects;
const char *data= m_data;
Geometry_buffer buffer;
Geometry *geom;
if (no_data(data, 4 ))
return GET_SIZE_ERROR;
n_objects= uint4korr(data);
data+= 4 ;
while (n_objects--)
{
uint32 wkb_type,object_size;
if (no_data(data, WKB_HEADER_SIZE))
return GET_SIZE_ERROR;
wkb_type= uint4korr(data + 1 );
data+= WKB_HEADER_SIZE;
if (!(geom= create_by_typeid(&buffer, wkb_type)))
return GET_SIZE_ERROR;
geom->set_data_ptr(data, (uint) (m_data_end - data));
if ((object_size= geom->get_data_size()) == GET_SIZE_ERROR)
return GET_SIZE_ERROR;
data+= object_size;
}
return (uint32) (data - m_data);
}
bool Gis_geometry_collection::init_from_wkt(Gis_read_stream *trs, String *wkb)
{
uint32 n_objects= 0 ;
uint32 no_pos= wkb->length();
Geometry_buffer buffer;
Geometry *g;
char next_sym;
if (wkb->reserve(4 , 512 ))
return 1 ;
wkb->length(wkb->length()+4 ); // Reserve space for points
if (check_stack_overrun(current_thd, STACK_MIN_SIZE, (uchar*)&buffer))
return 1 ;
if (!(next_sym= trs->next_symbol()))
return 1 ;
if (next_sym != ')' )
{
LEX_STRING next_word;
if (trs->lookup_next_word(&next_word))
return 1 ;
if (next_word.length != 5 ||
(my_charset_latin1.strnncoll("empty" , 5 , next_word.str, 5 ) != 0 ))
{
for (;;)
{
if (!(g= create_from_wkt(&buffer, trs, wkb)))
return 1 ;
if (g->get_class_info()->m_type_id == wkb_geometrycollection)
{
trs->set_error_msg("Unexpected GEOMETRYCOLLECTION" );
return 1 ;
}
n_objects++;
if (trs->skip_char(',' )) // Didn't find ','
break ;
}
}
}
wkb->write_at_position(no_pos, n_objects);
return 0 ;
}
uint Gis_geometry_collection::init_from_opresult(String *bin,
const char *opres,
uint res_len)
{
Geometry_buffer buffer;
Geometry *geom;
uint g_len, result= 0 ;
uint32 wkb_type;
int no_pos= bin->length();
uint32 n_objects= 0 ;
if (bin->reserve(4 , 512 ))
return 0 ;
bin->q_append(n_objects);
if (res_len == 0 )
{
/* Special case of GEOMETRYCOLLECTION EMPTY. */
result= 1 ;
goto empty_geom;
}
while (res_len)
{
switch ((Gcalc_function::shape_type) uint4korr(opres))
{
case Gcalc_function::shape_point: wkb_type= wkb_point; break ;
case Gcalc_function::shape_line: wkb_type= wkb_linestring; break ;
case Gcalc_function::shape_polygon: wkb_type= wkb_polygon; break ;
default : wkb_type= 0 ; DBUG_ASSERT(FALSE );
};
if (bin->reserve(WKB_HEADER_SIZE, 512 ))
return 0 ;
bin->q_append((char ) wkb_ndr);
bin->q_append(wkb_type);
if (!(geom= create_by_typeid(&buffer, wkb_type)) ||
!(g_len= geom->init_from_opresult(bin, opres, res_len)))
return 0 ;
opres+= g_len;
res_len-= g_len;
result+= g_len;
n_objects++;
}
empty_geom:
bin->write_at_position(no_pos, n_objects);
return result;
}
uint Gis_geometry_collection::init_from_wkb(const char *wkb, uint len,
wkbByteOrder bo, String *res)
{
uint32 n_geom;
const char *wkb_orig= wkb;
if (len < 4 )
return 0 ;
n_geom= wkb_get_uint(wkb, bo);
if (check_stack_overrun(current_thd, STACK_MIN_SIZE, (uchar*)&wkb_orig))
return 1 ;
if (res->reserve(4 , 512 ))
return 0 ;
res->q_append(n_geom);
advance(wkb,len,4 );
while (n_geom--)
{
Geometry_buffer buffer;
Geometry *geom;
int g_len;
uint32 wkb_type;
if (len < WKB_HEADER_SIZE
|| (uchar) wkb[0 ] > wkb_ndr
|| res->reserve(WKB_HEADER_SIZE, 512 ))
return 0 ;
wkbByteOrder bo= (wkbByteOrder)wkb[0 ];
res->q_append((char ) wkb_ndr);
wkb_type= wkb_get_uint(wkb+1 , bo);
res->q_append(wkb_type);
advance(wkb,len,WKB_HEADER_SIZE);
if (!(geom= create_by_typeid(&buffer, wkb_type)) ||
!(g_len= geom->init_from_wkb(wkb, len, bo, res)))
return 0 ;
advance(wkb,len,g_len);
}
return (uint) (wkb - wkb_orig);
}
bool Gis_geometry_collection::init_from_json(json_engine_t *je, bool er_on_3D,
String *wkb)
{
uint32 n_objects= 0 ;
uint32 no_pos= wkb->length();
Geometry_buffer buffer;
Geometry *g;
if (je->value_type != JSON_VALUE_ARRAY)
{
err_geoj_incorrect:
je->s.error= GEOJ_INCORRECT_GEOJSON;
return TRUE ;
}
if (wkb->reserve(4 , 512 ))
return TRUE ;
wkb->length(wkb->length()+4 ); // Reserve space for n_objects
while (json_scan_next(je) == 0 && je->state != JST_ARRAY_END)
{
if (je->state == JST_VALUE)
{
if (json_read_value(je))
return TRUE ;
}
if (je->state != JST_OBJ_START)
goto err_geoj_incorrect;
if (!(g= create_from_json(&buffer, je, er_on_3D, wkb)))
return TRUE ;
n_objects++;
}
wkb->write_at_position(no_pos, n_objects);
return FALSE ;
}
bool Gis_geometry_collection::get_data_as_wkt(String *txt,
const char **end) const
{
uint32 n_objects;
Geometry_buffer buffer;
Geometry *geom;
const char *data= m_data;
if (no_data(data, 4 ))
return 1 ;
n_objects= uint4korr(data);
data+= 4 ;
if (n_objects == 0 )
{
txt->append(STRING_WITH_LEN(" EMPTY" ), 512 );
goto exit ;
}
txt->append('(' );
while (n_objects--)
{
uint32 wkb_type;
if (no_data(data, WKB_HEADER_SIZE))
return 1 ;
wkb_type= uint4korr(data + 1 );
data+= WKB_HEADER_SIZE;
if (!(geom= create_by_typeid(&buffer, wkb_type)))
return 1 ;
geom->set_data_ptr(data, (uint) (m_data_end - data));
if (geom->as_wkt(txt, &data))
return 1 ;
if (n_objects && txt->append(STRING_WITH_LEN("," ), 512 ))
return 1 ;
}
txt->append(')' );
exit :
*end= data;
return 0 ;
}
bool Gis_geometry_collection::get_data_as_json(String *txt, uint max_dec_digits,
const char **end) const
{
uint32 n_objects;
Geometry_buffer buffer;
Geometry *geom;
const char *data= m_data;
if (no_data(data, 4 ) || txt->reserve(1 , 512 ))
return 1 ;
n_objects= uint4korr(data);
data+= 4 ;
bool any_objects= false ;
txt->qs_append('[' );
while (n_objects--)
{
any_objects= true ;
uint32 wkb_type;
if (no_data(data, WKB_HEADER_SIZE))
return 1 ;
wkb_type= uint4korr(data + 1 );
data+= WKB_HEADER_SIZE;
if (!(geom= create_by_typeid(&buffer, wkb_type)))
return 1 ;
geom->set_data_ptr(data, (uint) (m_data_end - data));
if (txt->append('{' ) ||
geom->as_json(txt, max_dec_digits, &data) ||
txt->append(STRING_WITH_LEN("}, " ), 512 ))
return 1 ;
}
if (any_objects)
txt->length(txt->length() - 2 );
if (txt->append(']' ))
return 1 ;
*end= data;
return 0 ;
}
int Gis_geometry_collection::is_valid(int *valid) const
{
Geometry_buffer buffer;
uint32 num_geometries;
Geometry *geometry;
*valid= 0 ;
if (this ->num_geometries(&num_geometries))
return 1 ;
for (uint32 i= 1 ; i <= num_geometries; i++)
{
String wkb= 0 ;
if (wkb.reserve(SRID_SIZE + BYTE_ORDER_SIZE + WKB_HEADER_SIZE))
return 1 ;
wkb.q_append(SRID_PLACEHOLDER);
if (this ->geometry_n(i, &wkb) ||
!(geometry= Geometry::construct(&buffer, wkb.ptr(), wkb.length())))
return 1 ;
int internal_valid;
if (geometry->is_valid(&internal_valid))
return 1 ;
if (!internal_valid)
return 0 ;
}
*valid= 1 ;
return 0 ;
}
bool Gis_geometry_collection::get_mbr(MBR *mbr, const char **end) const
{
uint32 n_objects;
const char *data= m_data;
Geometry_buffer buffer;
Geometry *geom;
if (no_data(data, 4 ))
return 1 ;
n_objects= uint4korr(data);
data+= 4 ;
if (n_objects == 0 )
goto exit ;
while (n_objects--)
{
uint32 wkb_type;
if (no_data(data, WKB_HEADER_SIZE))
return 1 ;
wkb_type= uint4korr(data + 1 );
data+= WKB_HEADER_SIZE;
if (!(geom= create_by_typeid(&buffer, wkb_type)))
return 1 ;
geom->set_data_ptr(data, (uint32) (m_data_end - data));
if (geom->get_mbr(mbr, &data))
return 1 ;
}
exit :
*end= data;
return 0 ;
}
int Gis_geometry_collection::area(double *ar, const char **end) const
{
uint32 n_objects;
const char *data= m_data;
Geometry_buffer buffer;
Geometry *geom;
double result;
if (no_data(data, 4 ))
return 1 ;
n_objects= uint4korr(data);
data+= 4 ;
result= 0 .0 ;
if (n_objects == 0 )
goto exit ;
while (n_objects--)
{
uint32 wkb_type;
if (no_data(data, WKB_HEADER_SIZE))
return 1 ;
wkb_type= uint4korr(data + 1 );
data+= WKB_HEADER_SIZE;
if (!(geom= create_by_typeid(&buffer, wkb_type)))
return 1 ;
geom->set_data_ptr(data, (uint32) (m_data_end - data));
if (geom->area(ar, &data))
return 1 ;
result+= *ar;
}
exit :
*end= data;
*ar= result;
return 0 ;
}
int Gis_geometry_collection::simplify(String *result,
double max_distance) const
{
uint32 num_geometries= 0 , num_invalid_geometries= 0 ;
Geometry_buffer buffer;
Geometry *geometry= NULL;
if (this ->num_geometries(&num_geometries))
return 1 ;
result->length(0 );
// Reserve room for the SRID, byte order, type, and geometry count.
if (result->reserve(SRID_SIZE + WKB_HEADER_SIZE + sizeof (uint32)))
return 1 ;
result->q_append(SRID_PLACEHOLDER);
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 , simplified_wkb= 0 ;
wkb.q_append((uint) 0 );
if (this ->geometry_n(i, &wkb) ||
!(geometry= Geometry::construct(&buffer, wkb.ptr(), wkb.length())))
return 1 ;
if (geometry->get_class_info()->m_type_id == Geometry::wkb_point ||
geometry->get_class_info()->m_type_id == Geometry::wkb_multipoint)
{
result->append(wkb.ptr() + SRID_SIZE, wkb.length() - SRID_SIZE);
continue ;
}
if (geometry->simplify(&simplified_wkb, max_distance))
{
num_invalid_geometries++;
continue ;
}
result->append(simplified_wkb.ptr() + SRID_SIZE,
simplified_wkb.length() - SRID_SIZE);
}
if (num_geometries == num_invalid_geometries)
return 1 ;
result->write_at_position(SRID_SIZE + WKB_HEADER_SIZE,
(uint32) num_geometries - num_invalid_geometries);
return 0 ;
}
int Gis_geometry_collection::geom_length(double *len, const char **end) const
{
uint32 n_objects;
const char *data= m_data;
Geometry_buffer buffer;
Geometry *geom;
double result;
if (no_data(data, 4 ))
return 1 ;
n_objects= uint4korr(data);
data+= 4 ;
result= 0 .0 ;
if (n_objects == 0 )
goto exit ;
while (n_objects--)
{
uint32 wkb_type;
if (no_data(data, WKB_HEADER_SIZE))
return 1 ;
wkb_type= uint4korr(data + 1 );
data+= WKB_HEADER_SIZE;
if (!(geom= create_by_typeid(&buffer, wkb_type)))
return 1 ;
geom->set_data_ptr(data, (uint32) (m_data_end - data));
if (geom->geom_length(len, &data))
return 1 ;
result+= *len;
}
exit :
*end= data;
*len= result;
return 0 ;
}
int Gis_geometry_collection::num_geometries(uint32 *num) const
{
if (no_data(m_data, 4 ))
return 1 ;
*num= uint4korr(m_data);
return 0 ;
}
int Gis_geometry_collection::geometry_n(uint32 num, String *result) const
{
uint32 n_objects, wkb_type, length;
const char *data= m_data;
Geometry_buffer buffer;
Geometry *geom;
if (no_data(data, 4 ))
return 1 ;
n_objects= uint4korr(data);
data+= 4 ;
if (num > n_objects || num < 1 )
return 1 ;
do
{
if (no_data(data, WKB_HEADER_SIZE))
return 1 ;
wkb_type= uint4korr(data + 1 );
data+= WKB_HEADER_SIZE;
if (!(geom= create_by_typeid(&buffer, wkb_type)))
return 1 ;
geom->set_data_ptr(data, (uint) (m_data_end - data));
if ((length= geom->get_data_size()) == GET_SIZE_ERROR)
return 1 ;
data+= length;
} while (--num);
/* Copy found object to result */
if (result->reserve(1 + 4 + length))
return 1 ;
result->q_append((char ) wkb_ndr);
result->q_append((uint32) wkb_type);
result->q_append(data-length, length); // data-length = start_of_data
return 0 ;
}
/*
Return dimension for object
SYNOPSIS
dimension ( )
res_dim Result dimension
end End of object will be stored here . May be 0 for
simple objects !
RETURN
0 ok
1 error
*/
bool Gis_geometry_collection::dimension(uint32 *res_dim, const char **end) const
{
uint32 n_objects;
const char *data= m_data;
Geometry_buffer buffer;
Geometry *geom;
if (no_data(data, 4 ))
return 1 ;
n_objects= uint4korr(data);
data+= 4 ;
*res_dim= 0 ;
while (n_objects--)
{
uint32 wkb_type, length, dim;
const char *end_data;
if (no_data(data, WKB_HEADER_SIZE))
return 1 ;
wkb_type= uint4korr(data + 1 );
data+= WKB_HEADER_SIZE;
if (!(geom= create_by_typeid(&buffer, wkb_type)))
return 1 ;
geom->set_data_ptr(data, (uint32) (m_data_end - data));
if (geom->dimension(&dim, &end_data))
return 1 ;
set_if_bigger(*res_dim, dim);
if (end_data) // Complex object
data= end_data;
else if ((length= geom->get_data_size()) == GET_SIZE_ERROR)
return 1 ;
else
data+= length;
}
*end= data;
return 0 ;
}
int Gis_geometry_collection::store_shapes(Gcalc_shape_transporter *trn) const
{
uint32 n_objects;
const char *data= m_data;
Geometry_buffer buffer;
Geometry *geom;
if (no_data(data, 4 ))
return 1 ;
n_objects= uint4korr(data);
data+= 4 ;
if (!n_objects)
{
trn->empty_shape();
return 0 ;
}
if (trn->start_collection(n_objects))
return 1 ;
while (n_objects--)
{
uint32 wkb_type;
if (no_data(data, WKB_HEADER_SIZE))
return 1 ;
wkb_type= uint4korr(data + 1 );
data+= WKB_HEADER_SIZE;
if (!(geom= create_by_typeid(&buffer, wkb_type)))
return 1 ;
geom->set_data_ptr(data, (uint32) (m_data_end - data));
if (geom->store_shapes(trn))
return 1 ;
data+= geom->get_data_size();
}
return 0 ;
}
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->reserve(sizeof (char ) + sizeof (uint32));
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;
}
Messung V0.5 in Prozent C=98 H=87 G=92
¤ Dauer der Verarbeitung: 0.107 Sekunden
(vorverarbeitet am 2026-10-08)
¤
*© Formatika GbR, Deutschland