mariadb/sql/spatial.cc
Alexey Botchkov 11948d7586 MDEV-12180 ST_GeomFromGeoJSON option argument appears to have no effect.
Implement the 'option' argument for the ST_GeomFromGeoJSON.
2017-08-06 16:27:37 +04:00

3384 lines
75 KiB
C++

/*
Copyright (c) 2002, 2013, Oracle and/or its affiliates.
Copyright (c) 2011, 2013, Monty Program Ab.
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-1301 USA */
#include <my_global.h>
#include "sql_priv.h"
#include "spatial.h"
#include "gstream.h" // Gis_read_stream
#include "sql_string.h" // String
#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
/***************************** 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, uint32 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_strnncoll(&my_charset_latin1,
(const uchar*) (*cur_rt)->m_name.str, len,
(const uchar*) 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;
}
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 int type_keyname_len= 4;
static const uchar coord_keyname[]= "coordinates";
static const int coord_keyname_len= 11;
static const uchar geometries_keyname[]= "geometries";
static const int geometries_keyname_len= 10;
static const uchar features_keyname[]= "features";
static const int features_keyname_len= 8;
static const uchar geometry_keyname[]= "geometry";
static const int geometry_keyname_len= 8;
static const int 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;
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("{\"", 2);
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) ||
wkt->reserve(1))
return 1;
wkt->qs_append('}');
return 0;
}
static double wkb_get_double(const char *ptr, Geometry::wkbByteOrder bo)
{
double res;
if (bo != Geometry::wkb_xdr)
{
float8get(res, ptr);
}
else
{
char inv_array[8];
inv_array[0]= ptr[7];
inv_array[1]= ptr[6];
inv_array[2]= ptr[5];
inv_array[3]= ptr[4];
inv_array[4]= ptr[3];
inv_array[5]= ptr[2];
inv_array[6]= ptr[1];
inv_array[7]= ptr[0];
float8get(res, inv_array);
}
return res;
}
static uint32 wkb_get_uint(const char *ptr, Geometry::wkbByteOrder bo)
{
if (bo != Geometry::wkb_xdr)
return uint4korr(ptr);
/* else */
{
char inv_array[4];
inv_array[0]= ptr[3];
inv_array[1]= ptr[2];
inv_array[2]= ptr[1];
inv_array[3]= ptr[0];
return uint4korr(inv_array);
}
}
Geometry *Geometry::create_from_wkb(Geometry_buffer *buffer,
const char *wkb, uint32 len, String *res)
{
uint32 geom_type;
Geometry *geom;
if (len < WKB_HEADER_SIZE)
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 (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_strnncoll(&my_charset_latin1, 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_strnncoll(&my_charset_latin1, je->value, je->value_len,
feature_type, feature_type_len) == 0)
{
if (geometry_start)
goto handle_geometry_key;
feature_type_found= 1;
}
}
}
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;
}
}
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;
}
}
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);
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= my_strntod(je->s.cs, (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;
}
/***************************** 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= 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++;
}
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;
double cur_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;
get_point(&tmp_x, &tmp_y, data);
data+= POINT_DATA_SIZE;
cur_area+= (prev_x + tmp_x) * (prev_y - tmp_y);
cur_cx+= tmp_x;
cur_cy+= tmp_y;
prev_x= tmp_x;
prev_y= tmp_y;
}
cur_area= fabs(cur_area) / 2;
cur_cx= cur_cx / (org_n_points - 1);
cur_cy= cur_cy / (org_n_points - 1);
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++;
}
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;
}
/***************************** 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++;
}
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 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++;
}
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_strnncoll(&my_charset_latin1,
(const uchar*) "empty", 5,
(const uchar*) 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 (geom->as_json(txt, max_dec_digits, &data) ||
txt->append(STRING_WITH_LEN(", "), 512))
return 1;
}
txt->length(txt->length() - 2);
txt->qs_append(']');
*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*/