summaryrefslogtreecommitdiffstats
path: root/sql/spatial.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--sql/spatial.cc3792
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*/