diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:07:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:07:14 +0000 |
commit | a175314c3e5827eb193872241446f2f8f5c9d33c (patch) | |
tree | cd3d60ca99ae00829c52a6ca79150a5b6e62528b /sql/spatial.cc | |
parent | Initial commit. (diff) | |
download | mariadb-10.5-upstream.tar.xz mariadb-10.5-upstream.zip |
Adding upstream version 1:10.5.12.upstream/1%10.5.12upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | sql/spatial.cc | 3792 |
1 files changed, 3792 insertions, 0 deletions
diff --git a/sql/spatial.cc b/sql/spatial.cc new file mode 100644 index 00000000..a06c7652 --- /dev/null +++ b/sql/spatial.cc @@ -0,0 +1,3792 @@ +/* + 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 + +/* 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); + +#ifdef HAVE_SPATIAL + +/* + 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 dimesion of an 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; +} + + +/***************************** Gis_class_info *******************************/ + +String Geometry::bad_geometry_data("Bad object", &my_charset_bin); + +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 (data_len < SRID_SIZE + WKB_HEADER_SIZE) // < 4 + (1 + 4) + 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->qs_append(')'); + 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("\"", 1); + 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("\"", 1); + 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("]", 1); + + 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) + return NULL; + geom_type= wkb_get_uint(wkb+1, (wkbByteOrder)wkb[0]); + 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); + + return geom->init_from_wkb(wkb + WKB_HEADER_SIZE, len - WKB_HEADER_SIZE, + (wkbByteOrder) wkb[0], res) ? geom : NULL; +} + + +Geometry *Geometry::create_from_json(Geometry_buffer *buffer, + json_engine_t *je, bool er_on_3D, String *res) +{ + Class_info *ci= NULL; + const uchar *coord_start= NULL, *geom_start= NULL, + *features_start= NULL, *geometry_start= NULL; + Geometry *result; + uchar key_buf[max_keyname_len]; + uint key_len; + int fcoll_type_found= 0, feature_type_found= 0; + + + if (json_read_value(je)) + goto err_return; + + 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) + { + DBUG_ASSERT(je->state == JST_KEY); + + 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 | 0x20; /* 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((const char *) je->value, je->value_len))) + { + if ((coord_start= + (ci == &geometrycollection_class) ? geom_start : coord_start)) + goto create_geom; + } + 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. + */ + if (features_start) + goto handle_feature_collection; + fcoll_type_found= 1; + } + else if (je->value_len == feature_type_len && + my_charset_latin1.strnncoll(je->value, je->value_len, + feature_type, feature_type_len) == 0) + { + if (geometry_start) + goto handle_geometry_key; + feature_type_found= 1; + } + 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) + { + /* + 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) + { + coord_start= je->value_begin; + if (ci && ci != &geometrycollection_class) + goto create_geom; + if (json_skip_level(je)) + goto err_return; + } + } + else if (key_len == geometries_keyname_len && + memcmp(key_buf, geometries_keyname, geometries_keyname_len) == 0) + { + /* + 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) + { + geom_start= je->value_begin; + if (ci == &geometrycollection_class) + { + coord_start= geom_start; + goto create_geom; + } + } + } + else if (key_len == features_keyname_len && + memcmp(key_buf, features_keyname, features_keyname_len) == 0) + { + /* + 'features' key found. Handle the 'Featurecollection'/'features' + GeoJSON construction. + */ + if (json_read_value(je)) + goto err_return; + if (je->value_type == JSON_VALUE_ARRAY) + { + features_start= je->value_begin; + if (fcoll_type_found) + goto handle_feature_collection; + } + } + 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) + { + geometry_start= je->value_begin; + if (feature_type_found) + goto handle_geometry_key; + } + goto err_return; + } + 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; + +handle_feature_collection: + ci= &geometrycollection_class; + coord_start= features_start; + +create_geom: + + json_scan_start(je, je->s.cs, coord_start, je->s.str_end); + + 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, er_on_3D, res)) + goto err_return; + + return result; + +handle_geometry_key: + json_scan_start(je, je->s.cs, geometry_start, je->s.str_end); + return create_from_json(buffer, je, er_on_3D, res); + +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; +} + + +/* + 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) +{ + txt->qs_append('['); + while (n_points--) + { + data+= offset; + append_json_point(txt, max_dec, data); + data+= POINT_DATA_SIZE; + txt->qs_append(", ", 2); + } + 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; +} + + +/***************************** Point *******************************/ + +uint32 Gis_point::get_data_size() const +{ + return 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) + { + DBUG_ASSERT(je->state == JST_VALUE); + 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 (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 (json_read_value(je)) + return TRUE; + + 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); + 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::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 caclulcated 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); + DBUG_ASSERT(gg); + if (static_cast<Gis_point *>(gg)->get_xy_radian(&x2r, &y2r)) + { + DBUG_ASSERT(0); + return -1; + } + } + else + { + if (static_cast<const Gis_point *>(g)->get_xy_radian(&x2r, &y2r)) + { + DBUG_ASSERT(0); + return -1; + } + } + if (this->get_xy_radian(&x1r, &y1r)) + { + DBUG_ASSERT(0); + 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; + + // First 4 bytes are handled already, make sure to create a Point + memset(s + 4, Geometry::wkb_point, 1); + memcpy(s + 5, g->get_data_ptr() + 5, 4); + memcpy(s + 4 + WKB_HEADER_SIZE, g->get_data_ptr() + 4 + WKB_HEADER_SIZE*i +\ + POINT_DATA_SIZE*(i-1), POINT_DATA_SIZE); + s[len-1]= '\0'; + temp= Geometry::construct(&buff_temp, s, len); + DBUG_ASSERT(temp); + 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 (json_read_value(je)) + return TRUE; + + if (je->value_type != JSON_VALUE_ARRAY) + { + 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) + { + DBUG_ASSERT(je->state == JST_VALUE); + + 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::num_points(uint32 *n_points) const +{ + *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(); +} + +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; + wkb+= 4; + 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; + wkb+= 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 (json_read_value(je)) + return TRUE; + + if (je->value_type != JSON_VALUE_ARRAY) + { + 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; + DBUG_ASSERT(je->state == JST_VALUE); + + 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; + + txt->qs_append('['); + 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(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); + } + 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; +} + + +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::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) > 1e-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; +} + + +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 (!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 (json_read_value(je)) + return TRUE; + + if (je->value_type != JSON_VALUE_ARRAY) + { + 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) + { + DBUG_ASSERT(je->state == JST_VALUE); + + 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; +} + + +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 +{ + *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. + */ + num_geometries(&num_of_points1); + 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]; + // First 4 bytes are handled already, make sure to create a Point + memset(s + 4, Geometry::wkb_point, 1); + memcpy(s + 5, this->get_data_ptr() + 5, 4); + memcpy(s + 4 + WKB_HEADER_SIZE, this->get_data_ptr() + 4 + WKB_HEADER_SIZE*i +\ + POINT_DATA_SIZE*(i-1), POINT_DATA_SIZE); + s[len-1]= '\0'; + temp= Geometry::construct(&buff_temp, s, len); + DBUG_ASSERT(temp); + // 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]; + // First 4 bytes are handled already, make sure to create a Point + memset(s2 + 4, Geometry::wkb_point, 1); + memcpy(s2 + 5, g->get_data_ptr() + 5, 4); + memcpy(s2 + 4 + WKB_HEADER_SIZE, g->get_data_ptr() + 4 + WKB_HEADER_SIZE*j +\ + POINT_DATA_SIZE*(j-1), POINT_DATA_SIZE); + s2[len-1]= '\0'; + temp2= Geometry::construct(&buff_temp2, s2, len); + DBUG_ASSERT(temp2); + 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); + + wkb+= 4; + while (n_line_strings--) + { + Gis_line_string ls; + int ls_len; + + 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); + + if (!(ls_len= ls.init_from_wkb(wkb + WKB_HEADER_SIZE, len, + (wkbByteOrder) wkb[0], res))) + return 0; + ls_len+= WKB_HEADER_SIZE;; + wkb+= ls_len; + 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 (json_read_value(je)) + return TRUE; + + if (je->value_type != JSON_VALUE_ARRAY) + { + 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; + DBUG_ASSERT(je->state == JST_VALUE); + + 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; + + txt->qs_append('['); + 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 * 2 + 6) * n_points)) + return 1; + data= append_json_points(txt, max_dec_digits, n_points, data, 0); + txt->qs_append(", ", 2); + } + txt->length(txt->length() - 2); + txt->qs_append(']'); + *end= data; + 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--) + { + data+= WKB_HEADER_SIZE; + if (!(data= get_mbr_for_points(mbr, data, 0))) + return 1; + } + *end= data; + return 0; +} + + +int Gis_multi_line_string::num_geometries(uint32 *num) const +{ + *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; + 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::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); + + wkb+=4; + while (n_poly--) + { + Gis_polygon p; + int p_len; + + if (len < WKB_HEADER_SIZE || + res->reserve(WKB_HEADER_SIZE, 512)) + return 0; + res->q_append((char) wkb_ndr); + res->q_append((uint32) wkb_polygon); + + if (!(p_len= p.init_from_wkb(wkb + WKB_HEADER_SIZE, len, + (wkbByteOrder) wkb[0], res))) + return 0; + p_len+= WKB_HEADER_SIZE; + wkb+= p_len; + 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 (json_read_value(je)) + return TRUE; + + if (je->value_type != JSON_VALUE_ARRAY) + { + 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) + { + DBUG_ASSERT(je->state == JST_VALUE); + + 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; + + txt->q_append('['); + 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 * 2 + 6) * n_points, + 512)) + return 1; + data= append_json_points(txt, max_dec_digits, n_points, data, 0); + txt->qs_append(", ", 2); + } + txt->length(txt->length() - 2); + txt->qs_append("], ", 3); + } + txt->length(txt->length() - 2); + txt->q_append(']'); + *end= data; + return 0; +} + + +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 +{ + *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; + + 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::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--) + { + 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; +} + + +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 (!(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) +{ + const char *opres_orig= opres; + Geometry_buffer buffer; + Geometry *geom; + int g_len; + 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. */ + opres+= 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; + n_objects++; + } +empty_geom: + bin->write_at_position(no_pos, n_objects); + return (uint) (opres - opres_orig); +} + + +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 (res->reserve(4, 512)) + return 0; + res->q_append(n_geom); + + wkb+= 4; + while (n_geom--) + { + Geometry_buffer buffer; + Geometry *geom; + int g_len; + uint32 wkb_type; + + if (len < WKB_HEADER_SIZE || + res->reserve(WKB_HEADER_SIZE, 512)) + return 0; + + res->q_append((char) wkb_ndr); + wkb_type= wkb_get_uint(wkb+1, (wkbByteOrder) wkb[0]); + res->q_append(wkb_type); + + if (!(geom= create_by_typeid(&buffer, wkb_type)) || + !(g_len= geom->init_from_wkb(wkb + WKB_HEADER_SIZE, len, + (wkbByteOrder) wkb[0], res))) + return 0; + g_len+= WKB_HEADER_SIZE; + wkb+= g_len; + 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 (json_read_value(je)) + return TRUE; + + if (je->value_type != JSON_VALUE_ARRAY) + { + 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) + { + json_engine_t sav_je= *je; + + DBUG_ASSERT(je->state == JST_VALUE); + + if (!(g= create_from_json(&buffer, je, er_on_3D, wkb))) + return TRUE; + + *je= sav_je; + if (json_skip_array_item(je)) + 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->qs_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->qs_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; + + txt->qs_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 (txt->append("{", 1) || + geom->as_json(txt, max_dec_digits, &data) || + txt->append(STRING_WITH_LEN("}, "), 512)) + return 1; + } + txt->length(txt->length() - 2); + if (txt->append("]", 1)) + return 1; + + *end= data; + 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::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; +} + + +const Geometry::Class_info *Gis_geometry_collection::get_class_info() const +{ + return &geometrycollection_class; +} + +#endif /*HAVE_SPATIAL*/ |