summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorAlexey Botchkov <holyfoot@askmonty.org>2017-01-24 02:29:04 +0400
committerAlexey Botchkov <holyfoot@askmonty.org>2017-01-24 02:29:04 +0400
commit0d107a85b3dd6969e66cc9cb4bd29e1cc92a7d18 (patch)
tree4ea2e72a0886dae95c31ecdc9556b381c4e097d5 /sql
parent1f3ad6a4ba63074c51c84dff449c35a8314a7f36 (diff)
downloadmariadb-git-0d107a85b3dd6969e66cc9cb4bd29e1cc92a7d18.tar.gz
MDEV-11042 Implement GeoJSON functions.
ST_AsGeoJSON and ST_GeomFromGeoJSON functions implemented.
Diffstat (limited to 'sql')
-rw-r--r--sql/item_create.cc125
-rw-r--r--sql/item_geofunc.cc90
-rw-r--r--sql/item_geofunc.h34
-rw-r--r--sql/item_jsonfunc.cc6
-rw-r--r--sql/share/errmsg-utf8.txt6
-rw-r--r--sql/spatial.cc760
-rw-r--r--sql/spatial.h35
7 files changed, 1052 insertions, 4 deletions
diff --git a/sql/item_create.cc b/sql/item_create.cc
index a506ab948d1..add8da67810 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -1262,6 +1262,34 @@ protected:
#ifdef HAVE_SPATIAL
+class Create_func_geometry_from_json : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ static Create_func_geometry_from_json s_singleton;
+
+protected:
+ Create_func_geometry_from_json() {}
+ virtual ~Create_func_geometry_from_json() {}
+};
+
+
+class Create_func_as_geojson : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ static Create_func_as_geojson s_singleton;
+
+protected:
+ Create_func_as_geojson() {}
+ virtual ~Create_func_as_geojson() {}
+};
+#endif /*HAVE_SPATIAL*/
+
+
+#ifdef HAVE_SPATIAL
class Create_func_geometry_type : public Create_func_arg1
{
public:
@@ -4533,6 +4561,101 @@ Create_func_geometry_from_wkb::create_native(THD *thd, LEX_STRING name,
#ifdef HAVE_SPATIAL
+Create_func_geometry_from_json Create_func_geometry_from_json::s_singleton;
+
+Item*
+Create_func_geometry_from_json::create_native(THD *thd, LEX_STRING name,
+ List<Item> *item_list)
+{
+ Item *func= NULL;
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ switch (arg_count) {
+ case 1:
+ {
+ Item *json= item_list->pop();
+ func= new (thd->mem_root) Item_func_geometry_from_json(thd, json);
+ thd->lex->uncacheable(UNCACHEABLE_RAND);
+ break;
+ }
+ case 2:
+ {
+ Item *json= item_list->pop();
+ Item *options= item_list->pop();
+ func= new (thd->mem_root) Item_func_geometry_from_json(thd, json, options);
+ break;
+ }
+ case 3:
+ {
+ Item *json= item_list->pop();
+ Item *options= item_list->pop();
+ Item *srid= item_list->pop();
+ func= new (thd->mem_root) Item_func_geometry_from_json(thd, json, options,
+ srid);
+ break;
+ }
+ default:
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ break;
+ }
+ }
+
+ return func;
+}
+
+
+Create_func_as_geojson Create_func_as_geojson::s_singleton;
+
+Item*
+Create_func_as_geojson::create_native(THD *thd, LEX_STRING name,
+ List<Item> *item_list)
+{
+ Item *func= NULL;
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ switch (arg_count) {
+ case 1:
+ {
+ Item *geom= item_list->pop();
+ func= new (thd->mem_root) Item_func_as_geojson(thd, geom);
+ thd->lex->uncacheable(UNCACHEABLE_RAND);
+ break;
+ }
+ case 2:
+ {
+ Item *geom= item_list->pop();
+ Item *max_dec= item_list->pop();
+ func= new (thd->mem_root) Item_func_as_geojson(thd, geom, max_dec);
+ break;
+ }
+ case 3:
+ {
+ Item *geom= item_list->pop();
+ Item *max_dec= item_list->pop();
+ Item *options= item_list->pop();
+ func= new (thd->mem_root) Item_func_as_geojson(thd, geom, max_dec, options);
+ break;
+ }
+ default:
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ break;
+ }
+ }
+
+ return func;
+}
+#endif /*HAVE_SPATIAL*/
+
+
+#ifdef HAVE_SPATIAL
Create_func_geometry_type Create_func_geometry_type::s_singleton;
Item*
@@ -6723,6 +6846,7 @@ static Native_func_registry func_array[] =
{ { C_STRING_WITH_LEN("STR_TO_DATE") }, BUILDER(Create_func_str_to_date)},
{ { C_STRING_WITH_LEN("ST_AREA") }, GEOM_BUILDER(Create_func_area)},
{ { C_STRING_WITH_LEN("ST_ASBINARY") }, GEOM_BUILDER(Create_func_as_wkb)},
+ { { C_STRING_WITH_LEN("ST_ASGEOJSON") }, GEOM_BUILDER(Create_func_as_geojson)},
{ { C_STRING_WITH_LEN("ST_ASTEXT") }, GEOM_BUILDER(Create_func_as_wkt)},
{ { C_STRING_WITH_LEN("ST_ASWKB") }, GEOM_BUILDER(Create_func_as_wkb)},
{ { C_STRING_WITH_LEN("ST_ASWKT") }, GEOM_BUILDER(Create_func_as_wkt)},
@@ -6748,6 +6872,7 @@ static Native_func_registry func_array[] =
{ { C_STRING_WITH_LEN("ST_GEOMETRYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
{ { C_STRING_WITH_LEN("ST_GEOMETRYN") }, GEOM_BUILDER(Create_func_geometryn)},
{ { C_STRING_WITH_LEN("ST_GEOMETRYTYPE") }, GEOM_BUILDER(Create_func_geometry_type)},
+ { { C_STRING_WITH_LEN("ST_GEOMFROMGEOJSON") }, GEOM_BUILDER(Create_func_geometry_from_json)},
{ { C_STRING_WITH_LEN("ST_GEOMFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
{ { C_STRING_WITH_LEN("ST_GEOMFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
#ifndef DBUG_OFF
diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc
index c856aa985b3..7ac2e054e8c 100644
--- a/sql/item_geofunc.cc
+++ b/sql/item_geofunc.cc
@@ -121,6 +121,65 @@ String *Item_func_geometry_from_wkb::val_str(String *str)
}
+void report_json_error_ex(String *js, json_engine_t *je,
+ const char *fname, int n_param,
+ Sql_condition::enum_warning_level lv);
+
+String *Item_func_geometry_from_json::val_str(String *str)
+{
+ DBUG_ASSERT(fixed == 1);
+ Geometry_buffer buffer;
+ String *js= args[0]->val_str_ascii(&tmp_js);
+ uint32 srid= 0;
+ json_engine_t je;
+
+ if ((null_value= args[0]->null_value))
+ return 0;
+
+ if ((arg_count == 2) && !args[1]->null_value)
+ srid= (uint32)args[1]->val_int();
+
+ str->set_charset(&my_charset_bin);
+ if (str->reserve(SRID_SIZE, 512))
+ return 0;
+ str->length(0);
+ str->q_append(srid);
+
+ json_scan_start(&je, js->charset(), (const uchar *) js->ptr(),
+ (const uchar *) js->end());
+
+ if ((null_value= !Geometry::create_from_json(&buffer, &je, str)))
+ {
+ int code= 0;
+
+ switch (je.s.error)
+ {
+ case Geometry::GEOJ_INCORRECT_GEOJSON:
+ code= ER_GEOJSON_INCORRECT;
+ break;
+ case Geometry::GEOJ_TOO_FEW_POINTS:
+ code= ER_GEOJSON_TOO_FEW_POINTS;
+ break;
+ case Geometry::GEOJ_POLYGON_NOT_CLOSED:
+ code= ER_GEOJSON_NOT_CLOSED;
+ break;
+ default:
+ report_json_error_ex(js, &je, func_name(), 0, Sql_condition::WARN_LEVEL_WARN);
+ return NULL;
+ }
+
+ if (code)
+ {
+ THD *thd= current_thd;
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, code,
+ ER_THD(thd, code));
+ }
+ return 0;
+ }
+ return str;
+}
+
+
String *Item_func_as_wkt::val_str_ascii(String *str)
{
DBUG_ASSERT(fixed == 1);
@@ -170,6 +229,37 @@ String *Item_func_as_wkb::val_str(String *str)
}
+void Item_func_as_geojson::fix_length_and_dec()
+{
+ collation.set(default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
+ max_length=MAX_BLOB_WIDTH;
+ maybe_null= 1;
+}
+
+
+String *Item_func_as_geojson::val_str_ascii(String *str)
+{
+ DBUG_ASSERT(fixed == 1);
+ String arg_val;
+ String *swkb= args[0]->val_str(&arg_val);
+ Geometry_buffer buffer;
+ Geometry *geom= NULL;
+ const char *dummy;
+
+ if ((null_value=
+ (args[0]->null_value ||
+ !(geom= Geometry::construct(&buffer, swkb->ptr(), swkb->length())))))
+ return 0;
+
+ str->length(0);
+ str->set_charset(&my_charset_latin1);
+ if ((null_value= geom->as_json(str, FLOATING_POINT_DECIMALS, &dummy)))
+ return 0;
+
+ return str;
+}
+
+
String *Item_func_geometry_type::val_str_ascii(String *str)
{
DBUG_ASSERT(fixed == 1);
diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h
index 1584090c29e..a164f9dec89 100644
--- a/sql/item_geofunc.h
+++ b/sql/item_geofunc.h
@@ -67,6 +67,23 @@ public:
{ return get_item_copy<Item_func_geometry_from_wkb>(thd, mem_root, this); }
};
+
+class Item_func_geometry_from_json: public Item_geometry_func
+{
+ String tmp_js;
+public:
+ Item_func_geometry_from_json(THD *thd, Item *js): Item_geometry_func(thd, js) {}
+ Item_func_geometry_from_json(THD *thd, Item *js, Item *opt):
+ Item_geometry_func(thd, js, opt) {}
+ Item_func_geometry_from_json(THD *thd, Item *js, Item *opt, Item *srid):
+ Item_geometry_func(thd, js, opt, srid) {}
+ const char *func_name() const { return "st_geomfromgeojson"; }
+ String *val_str(String *);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_geometry_from_json>(thd, mem_root, this); }
+};
+
+
class Item_func_as_wkt: public Item_str_ascii_func
{
public:
@@ -89,6 +106,23 @@ public:
{ return get_item_copy<Item_func_as_wkb>(thd, mem_root, this); }
};
+
+class Item_func_as_geojson: public Item_str_ascii_func
+{
+public:
+ Item_func_as_geojson(THD *thd, Item *js): Item_str_ascii_func(thd, js) {}
+ Item_func_as_geojson(THD *thd, Item *js, Item *max_dec_digits):
+ Item_str_ascii_func(thd, js, max_dec_digits) {}
+ Item_func_as_geojson(THD *thd, Item *js, Item *max_dec_digits, Item *opt):
+ Item_str_ascii_func(thd, js, max_dec_digits, opt) {}
+ const char *func_name() const { return "st_asgeojson"; }
+ void fix_length_and_dec();
+ String *val_str_ascii(String *);
+ Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+ { return get_item_copy<Item_func_as_geojson>(thd, mem_root, this); }
+};
+
+
class Item_func_geometry_type: public Item_str_ascii_func
{
public:
diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc
index a500170ead0..b0576fbdaba 100644
--- a/sql/item_jsonfunc.cc
+++ b/sql/item_jsonfunc.cc
@@ -117,9 +117,9 @@ static int st_append_escaped(String *s, const String *a)
report_json_error_ex(js, je, func_name(), n_param, \
Sql_condition::WARN_LEVEL_WARN)
-static void report_json_error_ex(String *js, json_engine_t *je,
- const char *fname, int n_param,
- Sql_condition::enum_warning_level lv)
+void report_json_error_ex(String *js, json_engine_t *je,
+ const char *fname, int n_param,
+ Sql_condition::enum_warning_level lv)
{
THD *thd= current_thd;
int position= (const char *) je->s.c_str - js->ptr();
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index eea7f19d1f5..7f5df13ad97 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -7440,3 +7440,9 @@ ER_JSON_ONE_OR_ALL
eng "Argument 2 to function '%s' must be "one" or "all"."
ER_UNSUPPORT_COMPRESSED_TEMPORARY_TABLE
eng "CREATE TEMPORARY TABLE is not allowed with ROW_FORMAT=COMPRESSED or KEY_BLOCK_SIZE."
+ER_GEOJSON_INCORRECT
+ eng "Incorrect GeoJSON format specified for st_geomfromgeojson function."
+ER_GEOJSON_TOO_FEW_POINTS
+ eng "Incorrect GeoJSON format - too few points for linestring specified."
+ER_GEOJSON_NOT_CLOSED
+ eng "Incorrect GeoJSON format - polygon not closed."
diff --git a/sql/spatial.cc b/sql/spatial.cc
index 9c4fe168fe3..ef9c24dcef4 100644
--- a/sql/spatial.cc
+++ b/sql/spatial.cc
@@ -230,6 +230,50 @@ int Geometry::as_wkt(String *wkt, const char **end)
}
+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_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_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;
@@ -291,6 +335,196 @@ Geometry *Geometry::create_from_wkb(Geometry_buffer *buffer,
}
+Geometry *Geometry::create_from_json(Geometry_buffer *buffer,
+ json_engine_t *je, 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++]= 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, 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, res);
+
+err_return:
+ return NULL;
+}
+
+
Geometry *Geometry::create_from_opresult(Geometry_buffer *g_buf,
String *res, Gcalc_result_receiver &rr)
{
@@ -429,6 +663,47 @@ 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);
+ 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 +777,58 @@ uint Gis_point::init_from_wkb(const char *wkb, uint len,
}
+static int read_point_from_json(json_engine_t *je, 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++;
+ }
+
+ return 0;
+bad_coordinates:
+ je->s.error= Geometry::GEOJ_INCORRECT_GEOJSON;
+ return 1;
+}
+
+
+bool Gis_point::init_from_json(json_engine_t *je, 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, &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 +844,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 +968,43 @@ uint Gis_line_string::init_from_wkb(const char *wkb, uint len,
}
+bool Gis_line_string::init_from_json(json_engine_t *je, 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, 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;
@@ -661,6 +1036,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 +1251,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 +1361,46 @@ uint Gis_polygon::init_from_wkb(const char *wkb, uint len, wkbByteOrder bo,
}
+bool Gis_polygon::init_from_json(json_engine_t *je, 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, 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;
@@ -996,6 +1433,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;
@@ -1382,6 +1852,44 @@ 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, 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, 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;
@@ -1399,6 +1907,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;
@@ -1594,6 +2120,44 @@ 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, 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, 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
{
@@ -1626,6 +2190,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;
@@ -1912,6 +2508,44 @@ uint Gis_multi_polygon::init_from_opresult(String *bin,
}
+bool Gis_multi_polygon::init_from_json(json_engine_t *je, 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, 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;
@@ -1956,6 +2590,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;
@@ -2304,6 +2983,47 @@ uint Gis_geometry_collection::init_from_wkb(const char *wkb, uint len,
}
+bool Gis_geometry_collection::init_from_json(json_engine_t *je, 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, 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
{
@@ -2348,6 +3068,44 @@ 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 (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;
diff --git a/sql/spatial.h b/sql/spatial.h
index 2e449844d47..926f66d5357 100644
--- a/sql/spatial.h
+++ b/sql/spatial.h
@@ -20,6 +20,7 @@
#include "sql_string.h" /* String, LEX_STRING */
#include <my_compiler.h>
+#include <json_lib.h>
#ifdef HAVE_SPATIAL
@@ -249,6 +250,13 @@ public:
wkb_xdr= 0, /* Big Endian */
wkb_ndr= 1 /* Little Endian */
};
+ enum geojson_errors
+ {
+ GEOJ_INCORRECT_GEOJSON= 1,
+ GEOJ_TOO_FEW_POINTS= 2,
+ GEOJ_POLYGON_NOT_CLOSED= 3,
+ };
+
/** Callback which creates Geometry objects on top of a given placement. */
typedef Geometry *(*create_geom_t)(char *);
@@ -271,8 +279,11 @@ public:
virtual uint init_from_opresult(String *bin,
const char *opres, uint res_len)
{ return init_from_wkb(opres + 4, UINT_MAX32, wkb_ndr, bin) + 4; }
+ virtual bool init_from_json(json_engine_t *je, String *wkb) {return true;}
virtual bool get_data_as_wkt(String *txt, const char **end) const=0;
+ virtual bool get_data_as_json(String *txt, uint max_dec_digits,
+ const char **end) const=0;
virtual bool get_mbr(MBR *mbr, const char **end) const=0;
virtual bool dimension(uint32 *dim, const char **end) const=0;
virtual int get_x(double *x) const { return -1; }
@@ -302,9 +313,12 @@ public:
bool init_stream=1);
static Geometry *create_from_wkb(Geometry_buffer *buffer,
const char *wkb, uint32 len, String *res);
+ static Geometry *create_from_json(Geometry_buffer *buffer,
+ json_engine_t *je, String *res);
static Geometry *create_from_opresult(Geometry_buffer *g_buf,
String *res, Gcalc_result_receiver &rr);
int as_wkt(String *wkt, const char **end);
+ int as_json(String *wkt, uint max_dec_digits, const char **end);
inline void set_data_ptr(const char *data, uint32 data_len)
{
@@ -379,7 +393,10 @@ public:
uint32 get_data_size() const;
bool init_from_wkt(Gis_read_stream *trs, String *wkb);
uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res);
+ bool init_from_json(json_engine_t *je, String *wkb);
bool get_data_as_wkt(String *txt, const char **end) const;
+ bool get_data_as_json(String *txt, uint max_dec_digits,
+ const char **end) const;
bool get_mbr(MBR *mbr, const char **end) const;
int get_xy(double *x, double *y) const
@@ -431,7 +448,10 @@ public:
uint32 get_data_size() const;
bool init_from_wkt(Gis_read_stream *trs, String *wkb);
uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res);
+ bool init_from_json(json_engine_t *je, String *wkb);
bool get_data_as_wkt(String *txt, const char **end) const;
+ bool get_data_as_json(String *txt, uint max_dec_digits,
+ const char **end) const;
bool get_mbr(MBR *mbr, const char **end) const;
int geom_length(double *len, const char **end) const;
int area(double *ar, const char **end) const;
@@ -462,7 +482,10 @@ public:
bool init_from_wkt(Gis_read_stream *trs, String *wkb);
uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res);
uint init_from_opresult(String *bin, const char *opres, uint res_len);
+ bool init_from_json(json_engine_t *je, String *wkb);
bool get_data_as_wkt(String *txt, const char **end) const;
+ bool get_data_as_json(String *txt, uint max_dec_digits,
+ const char **end) const;
bool get_mbr(MBR *mbr, const char **end) const;
int area(double *ar, const char **end) const;
int exterior_ring(String *result) const;
@@ -496,7 +519,10 @@ public:
bool init_from_wkt(Gis_read_stream *trs, String *wkb);
uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res);
uint init_from_opresult(String *bin, const char *opres, uint res_len);
+ bool init_from_json(json_engine_t *je, String *wkb);
bool get_data_as_wkt(String *txt, const char **end) const;
+ bool get_data_as_json(String *txt, uint max_dec_digits,
+ const char **end) const;
bool get_mbr(MBR *mbr, const char **end) const;
int num_geometries(uint32 *num) const;
int geometry_n(uint32 num, String *result) const;
@@ -522,7 +548,10 @@ public:
bool init_from_wkt(Gis_read_stream *trs, String *wkb);
uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res);
uint init_from_opresult(String *bin, const char *opres, uint res_len);
+ bool init_from_json(json_engine_t *je, String *wkb);
bool get_data_as_wkt(String *txt, const char **end) const;
+ bool get_data_as_json(String *txt, uint max_dec_digits,
+ const char **end) const;
bool get_mbr(MBR *mbr, const char **end) const;
int num_geometries(uint32 *num) const;
int geometry_n(uint32 num, String *result) const;
@@ -549,7 +578,10 @@ public:
uint32 get_data_size() const;
bool init_from_wkt(Gis_read_stream *trs, String *wkb);
uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res);
+ bool init_from_json(json_engine_t *je, String *wkb);
bool get_data_as_wkt(String *txt, const char **end) const;
+ bool get_data_as_json(String *txt, uint max_dec_digits,
+ const char **end) const;
bool get_mbr(MBR *mbr, const char **end) const;
int num_geometries(uint32 *num) const;
int geometry_n(uint32 num, String *result) const;
@@ -578,7 +610,10 @@ public:
bool init_from_wkt(Gis_read_stream *trs, String *wkb);
uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, String *res);
uint init_from_opresult(String *bin, const char *opres, uint res_len);
+ bool init_from_json(json_engine_t *je, String *wkb);
bool get_data_as_wkt(String *txt, const char **end) const;
+ bool get_data_as_json(String *txt, uint max_dec_digits,
+ const char **end) const;
bool get_mbr(MBR *mbr, const char **end) const;
int area(double *ar, const char **end) const;
int geom_length(double *len, const char **end) const;