diff options
Diffstat (limited to 'sql/spatial.cc')
-rw-r--r-- | sql/spatial.cc | 863 |
1 files changed, 852 insertions, 11 deletions
diff --git a/sql/spatial.cc b/sql/spatial.cc index b8378e4fdbb..7c78b0dcb71 100644 --- a/sql/spatial.cc +++ b/sql/spatial.cc @@ -21,6 +21,10 @@ #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 /* @@ -58,12 +62,14 @@ Geometry::Class_info *Geometry::ci_collection[Geometry::wkb_last+1]= static Geometry::Class_info **ci_collection_end= Geometry::ci_collection+Geometry::wkb_last + 1; -Geometry::Class_info::Class_info(const char *name, int type_id, - create_geom_t create_func): +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; } @@ -105,26 +111,27 @@ static Geometry *create_geometrycollection(char *buffer) -static Geometry::Class_info point_class("POINT", +static Geometry::Class_info point_class("POINT", "Point", Geometry::wkb_point, create_point); -static Geometry::Class_info linestring_class("LINESTRING", +static Geometry::Class_info linestring_class("LINESTRING", "LineString", Geometry::wkb_linestring, create_linestring); -static Geometry::Class_info polygon_class("POLYGON", +static Geometry::Class_info polygon_class("POLYGON", "Polygon", Geometry::wkb_polygon, create_polygon); -static Geometry::Class_info multipoint_class("MULTIPOINT", +static Geometry::Class_info multipoint_class("MULTIPOINT", "MultiPoint", Geometry::wkb_multipoint, create_multipoint); static Geometry::Class_info -multilinestring_class("MULTILINESTRING", +multilinestring_class("MULTILINESTRING", "MultiLineString", Geometry::wkb_multilinestring, create_multilinestring); -static Geometry::Class_info multipolygon_class("MULTIPOLYGON", +static Geometry::Class_info multipolygon_class("MULTIPOLYGON", "MultiPolygon", Geometry::wkb_multipolygon, create_multipolygon); static Geometry::Class_info -geometrycollection_class("GEOMETRYCOLLECTION",Geometry::wkb_geometrycollection, +geometrycollection_class("GEOMETRYCOLLECTION", "GeometryCollection", + Geometry::wkb_geometrycollection, create_geometrycollection); static void get_point(double *x, double *y, const char *data) @@ -230,6 +237,77 @@ int Geometry::as_wkt(String *wkt, const char **end) } +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; @@ -291,6 +369,196 @@ Geometry *Geometry::create_from_wkb(Geometry_buffer *buffer, } +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) { @@ -429,6 +697,52 @@ const char *Geometry::append_points(String *txt, uint32 n_points, } +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 @@ -502,6 +816,62 @@ uint Gis_point::init_from_wkb(const char *wkb, uint len, } +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; @@ -517,6 +887,17 @@ bool Gis_point::get_data_as_wkt(String *txt, const char **end) const } +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; @@ -630,6 +1011,44 @@ uint Gis_line_string::init_from_wkb(const char *wkb, uint len, } +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; @@ -661,6 +1080,28 @@ bool Gis_line_string::get_data_as_wkt(String *txt, const char **end) const } +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; @@ -854,7 +1295,7 @@ bool Gis_polygon::init_from_wkt(Gis_read_stream *trs, String *wkb) if (wkb->reserve(4, 512)) return 1; - wkb->length(wkb->length()+4); // Reserve space for points + wkb->length(wkb->length()+4); // Reserve space for n_rings for (;;) { Gis_line_string ls; @@ -964,6 +1405,55 @@ uint Gis_polygon::init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, } +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; @@ -996,6 +1486,39 @@ bool Gis_polygon::get_data_as_wkt(String *txt, const char **end) const } +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; @@ -1397,6 +1920,53 @@ uint Gis_multi_point::init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, } +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; @@ -1414,6 +1984,24 @@ bool Gis_multi_point::get_data_as_wkt(String *txt, const char **end) const } +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; @@ -1609,6 +2197,54 @@ uint Gis_multi_line_string::init_from_wkb(const char *wkb, uint len, } +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 { @@ -1641,6 +2277,38 @@ bool Gis_multi_line_string::get_data_as_wkt(String *txt, } +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; @@ -1923,7 +2591,53 @@ uint Gis_multi_polygon::init_from_opresult(String *bin, n_poly++; } bin->write_at_position(np_pos, n_poly); - return opres - opres_orig; + 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; } @@ -1971,6 +2685,51 @@ bool Gis_multi_polygon::get_data_as_wkt(String *txt, const char **end) const } +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; @@ -2319,6 +3078,48 @@ uint Gis_geometry_collection::init_from_wkb(const char *wkb, uint len, } +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 { @@ -2363,6 +3164,46 @@ exit: } +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; |