mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-31 02:46:29 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			4946 lines
		
	
	
	
		
			114 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			4946 lines
		
	
	
	
		
			114 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | ||
|    Copyright (c) 2002, 2013, Oracle and/or its affiliates.
 | ||
|    Copyright (c) 2011, 2021, MariaDB Corporation.
 | ||
| 
 | ||
|    This program is free software; you can redistribute it and/or modify
 | ||
|    it under the terms of the GNU General Public License as published by
 | ||
|    the Free Software Foundation; version 2 of the License.
 | ||
| 
 | ||
|    This program is distributed in the hope that it will be useful,
 | ||
|    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | ||
|    GNU General Public License for more details.
 | ||
| 
 | ||
|    You should have received a copy of the GNU General Public License
 | ||
|    along with this program; if not, write to the Free Software
 | ||
|    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA */
 | ||
| 
 | ||
| #include "mariadb.h"
 | ||
| #include "sql_priv.h"
 | ||
| #include "spatial.h"
 | ||
| #include "gstream.h"                            // Gis_read_stream
 | ||
| #include "sql_string.h"                         // String
 | ||
| #include <vector>
 | ||
| 
 | ||
| /* 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);
 | ||
| 
 | ||
| /*
 | ||
|   exponential notation :
 | ||
|   1   sign
 | ||
|   1   number before the decimal point
 | ||
|   1   decimal point
 | ||
|   14  number of significant digits (see String::qs_append(double))
 | ||
|   1   'e' sign
 | ||
|   1   exponent sign
 | ||
|   3   exponent digits
 | ||
|   ==
 | ||
|   22
 | ||
| 
 | ||
|   "f" notation :
 | ||
|   1   optional 0
 | ||
|   1   sign
 | ||
|   14  number significant digits (see String::qs_append(double) )
 | ||
|   1   decimal point
 | ||
|   ==
 | ||
|   17
 | ||
| */
 | ||
| 
 | ||
| #define MAX_DIGITS_IN_DOUBLE MY_GCVT_MAX_FIELD_WIDTH
 | ||
| 
 | ||
| int MBR::within(const MBR *mbr)
 | ||
| {
 | ||
|   /*
 | ||
|     We have to take into account the 'dimension' of
 | ||
|     the MBR, where the dimension of a single point is 0,
 | ||
|     the dimension of a vertical or horizontal line is 1,
 | ||
|     and finally the dimension of the solid rectangle is 2.
 | ||
|   */
 | ||
|     
 | ||
|   int dim1= dimension();
 | ||
|   int dim2= mbr->dimension();
 | ||
| 
 | ||
|   DBUG_ASSERT(dim1 >= 0 && dim1 <= 2 && dim2 >= 0 && dim2 <= 2);
 | ||
| 
 | ||
|   /*
 | ||
|     Either/both of the two operands can degrade to a point or a
 | ||
|     horizontal/vertical line segment, and we have to treat such cases
 | ||
|     separately.
 | ||
|    */
 | ||
|   switch (dim1)
 | ||
|   {
 | ||
|   case 0:
 | ||
|     DBUG_ASSERT(xmin == xmax && ymin == ymax);
 | ||
|     switch (dim2)
 | ||
|     {
 | ||
|     case 0:
 | ||
|       DBUG_ASSERT(mbr->xmin == mbr->xmax && mbr->ymin == mbr->ymax);
 | ||
|       return equals(mbr);
 | ||
|       break;
 | ||
|     case 1:
 | ||
|       DBUG_ASSERT((mbr->xmin == mbr->xmax && mbr->ymin != mbr->ymax) ||
 | ||
|                   (mbr->ymin == mbr->ymax && mbr->xmin != mbr->xmax));
 | ||
|       return ((xmin > mbr->xmin && xmin < mbr->xmax && ymin == mbr->ymin) ||
 | ||
|               (ymin > mbr->ymin && ymin < mbr->ymax && xmin == mbr->xmin));
 | ||
|       break;
 | ||
|     case 2:
 | ||
|       DBUG_ASSERT(mbr->xmin != mbr->xmax && mbr->ymin != mbr->ymax);
 | ||
|       return (xmin > mbr->xmin && xmax < mbr->xmax &&
 | ||
|               ymin > mbr->ymin && ymax < mbr->ymax);
 | ||
|       break;
 | ||
|     }
 | ||
|     break;
 | ||
|   case 1:
 | ||
|     DBUG_ASSERT((xmin == xmax && ymin != ymax) ||
 | ||
|                 (ymin == ymax && xmin != xmax));
 | ||
|     switch (dim2)
 | ||
|     {
 | ||
|     case 0:
 | ||
|       DBUG_ASSERT(mbr->xmin == mbr->xmax && mbr->ymin == mbr->ymax);
 | ||
|       return 0;
 | ||
|       break;
 | ||
|     case 1:
 | ||
|       DBUG_ASSERT((mbr->xmin == mbr->xmax && mbr->ymin != mbr->ymax) ||
 | ||
|                   (mbr->ymin == mbr->ymax && mbr->xmin != mbr->xmax));
 | ||
|       return ((xmin == xmax && mbr->xmin == mbr->xmax && mbr->xmin == xmin &&
 | ||
|                mbr->ymin <= ymin && mbr->ymax >= ymax) ||
 | ||
|               (ymin == ymax && mbr->ymin == mbr->ymax && mbr->ymin == ymin &&
 | ||
|                mbr->xmin <= xmin && mbr->xmax >= xmax));
 | ||
|       break;
 | ||
|     case 2:
 | ||
|       DBUG_ASSERT(mbr->xmin != mbr->xmax && mbr->ymin != mbr->ymax);
 | ||
|       return ((xmin == xmax && xmin > mbr->xmin && xmax < mbr->xmax &&
 | ||
|                ymin >= mbr->ymin && ymax <= mbr->ymax) ||
 | ||
|               (ymin == ymax && ymin > mbr->ymin && ymax < mbr->ymax &&
 | ||
|                xmin >= mbr->xmin && xmax <= mbr->xmax));
 | ||
|       break;
 | ||
|     }
 | ||
|     break;
 | ||
|   case 2:
 | ||
|     DBUG_ASSERT(xmin != xmax && ymin != ymax);
 | ||
|     switch (dim2)
 | ||
|     {
 | ||
|     case 0:
 | ||
|     case 1:
 | ||
|       return 0;
 | ||
|       break;
 | ||
|     case 2:
 | ||
|       DBUG_ASSERT(mbr->xmin != mbr->xmax && mbr->ymin != mbr->ymax);
 | ||
|       return ((mbr->xmin <= xmin) && (mbr->ymin <= ymin) &&
 | ||
|               (mbr->xmax >= xmax) && (mbr->ymax >= ymax));
 | ||
|       break;
 | ||
| 
 | ||
|     }
 | ||
|     break;
 | ||
|   }
 | ||
| 
 | ||
|   // Never reached.
 | ||
|   DBUG_ASSERT(false);
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| int MBR::coveredby(const MBR *mbr)
 | ||
| {
 | ||
|   int dim1= dimension();
 | ||
|   int dim2= mbr->dimension();
 | ||
| 
 | ||
|   if (dim1 > dim2)
 | ||
|     return 0;
 | ||
|   else if (dim1 == 0 && dim2 == 0)
 | ||
|     return equals(mbr);
 | ||
| 
 | ||
|   return ((xmin >= mbr->xmin) && (xmax <= mbr->xmax) &&
 | ||
|           (ymin >= mbr->ymin) && (ymax <= mbr->ymax));
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| /********************** Ramer–Douglas–Peucker algorithm **********************/
 | ||
| 
 | ||
| static double perpendicular_distance(const st_point_2d& point,
 | ||
|                             const st_point_2d& line_start,
 | ||
|                             const st_point_2d& line_end) {
 | ||
|   double difference_x= line_end.x - line_start.x;
 | ||
|   double difference_y= line_end.y - line_start.y;
 | ||
| 
 | ||
|   double magnitude = sqrt((difference_x * difference_x) +
 | ||
|                                (difference_y * difference_y));
 | ||
| 
 | ||
|   if (magnitude > 0.0)
 | ||
|   {
 | ||
|     difference_x /= magnitude;
 | ||
|     difference_y /= magnitude;
 | ||
|   }
 | ||
| 
 | ||
|   double point_vector_x= point.x - line_start.x;
 | ||
|   double point_vector_y= point.y - line_start.y;
 | ||
| 
 | ||
|   double point_vector_dot_product= ((difference_x * point_vector_x) +
 | ||
|                                    (difference_y * point_vector_y));
 | ||
| 
 | ||
|   double adjusted_x= point_vector_x - (point_vector_dot_product * difference_x);
 | ||
|   double adjusted_y= point_vector_y - (point_vector_dot_product * difference_y);
 | ||
| 
 | ||
|   return sqrt((adjusted_x * adjusted_x) + (adjusted_y * adjusted_y));
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| static void recursive_RDP(const std::vector<st_point_2d>& points,
 | ||
|                           const double max_distance,
 | ||
|                           std::vector<st_point_2d>& out,
 | ||
|                           const uint32 start, const uint32 end)
 | ||
| {
 | ||
|   if (start >= end) return;
 | ||
| 
 | ||
|   double greatest_distance= 0.0;
 | ||
|   uint32 index= start;
 | ||
| 
 | ||
|   for (uint32 i = start + 1; i < end; ++i) {
 | ||
|     double dist = perpendicular_distance(points[i], points[start], points[end]);
 | ||
|     if (dist > greatest_distance) {
 | ||
|       index = i;
 | ||
|       greatest_distance = dist;
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   if (greatest_distance > max_distance)
 | ||
|   {
 | ||
|     recursive_RDP(points, max_distance, out, start, index);
 | ||
|     recursive_RDP(points, max_distance, out, index, end);
 | ||
|   } else if (start != 0)
 | ||
|     out.push_back(points[start]);
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| /*
 | ||
|   Implements the Ramer–Douglas–Peucker. Given the points that compose a line,
 | ||
|   finds a similar curve with fewer points. The simplified curve consists of a
 | ||
|   subset of the points that defined the original curve.
 | ||
|   https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm
 | ||
| */
 | ||
| static void simplify_RDP(std::vector<st_point_2d>& points,
 | ||
|                          const double max_distance) {
 | ||
|   std::vector<st_point_2d> result;
 | ||
|   result.push_back(points.front());
 | ||
|   recursive_RDP(points, max_distance, result, 0,(uint32) points.size() - 1);
 | ||
|   result.push_back(points.back());
 | ||
|   points = std::move(result);
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| /***************************** Gis_class_info *******************************/
 | ||
| 
 | ||
| Geometry::Class_info *Geometry::ci_collection[Geometry::wkb_last+1]=
 | ||
| {
 | ||
|   NULL, NULL, NULL, NULL, NULL, NULL, NULL
 | ||
| };
 | ||
| 
 | ||
| static Geometry::Class_info **ci_collection_end=
 | ||
|                                 Geometry::ci_collection+Geometry::wkb_last + 1;
 | ||
| 
 | ||
| Geometry::Class_info::Class_info(const char *name, const char *geojson_name,
 | ||
|                                  int type_id, create_geom_t create_func):
 | ||
|   m_type_id(type_id), m_create_func(create_func)
 | ||
| {
 | ||
|   m_name.str= (char *) name;
 | ||
|   m_name.length= strlen(name);
 | ||
|   m_geojson_name.str= (char *) geojson_name;
 | ||
|   m_geojson_name.length= strlen(geojson_name);
 | ||
| 
 | ||
|   ci_collection[type_id]= this;
 | ||
| }
 | ||
| 
 | ||
| static Geometry *create_point(char *buffer)
 | ||
| {
 | ||
|   return new (buffer) Gis_point;
 | ||
| }
 | ||
| 
 | ||
| static Geometry *create_linestring(char *buffer)
 | ||
| {
 | ||
|   return new (buffer) Gis_line_string;
 | ||
| }
 | ||
| 
 | ||
| static Geometry *create_polygon(char *buffer)
 | ||
| {
 | ||
|   return new (buffer) Gis_polygon;
 | ||
| }
 | ||
| 
 | ||
| static Geometry *create_multipoint(char *buffer)
 | ||
| {
 | ||
|   return new (buffer) Gis_multi_point;
 | ||
| }
 | ||
| 
 | ||
| static Geometry *create_multipolygon(char *buffer)
 | ||
| {
 | ||
|   return new (buffer) Gis_multi_polygon;
 | ||
| }
 | ||
| 
 | ||
| static Geometry *create_multilinestring(char *buffer)
 | ||
| {
 | ||
|   return new (buffer) Gis_multi_line_string;
 | ||
| }
 | ||
| 
 | ||
| static Geometry *create_geometrycollection(char *buffer)
 | ||
| {
 | ||
|   return new (buffer) Gis_geometry_collection;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| static Geometry::Class_info point_class("POINT", "Point",
 | ||
| 					Geometry::wkb_point, create_point);
 | ||
| 
 | ||
| static Geometry::Class_info linestring_class("LINESTRING", "LineString",
 | ||
| 					     Geometry::wkb_linestring,
 | ||
| 					     create_linestring);
 | ||
| static Geometry::Class_info polygon_class("POLYGON", "Polygon",
 | ||
| 					      Geometry::wkb_polygon,
 | ||
| 					      create_polygon);
 | ||
| static Geometry::Class_info multipoint_class("MULTIPOINT", "MultiPoint",
 | ||
| 						 Geometry::wkb_multipoint,
 | ||
| 						 create_multipoint);
 | ||
| static Geometry::Class_info 
 | ||
| multilinestring_class("MULTILINESTRING", "MultiLineString",
 | ||
| 		      Geometry::wkb_multilinestring, create_multilinestring);
 | ||
| static Geometry::Class_info multipolygon_class("MULTIPOLYGON", "MultiPolygon",
 | ||
| 						   Geometry::wkb_multipolygon,
 | ||
| 						   create_multipolygon);
 | ||
| static Geometry::Class_info 
 | ||
| geometrycollection_class("GEOMETRYCOLLECTION", "GeometryCollection",
 | ||
|                          Geometry::wkb_geometrycollection,
 | ||
| 			 create_geometrycollection);
 | ||
| 
 | ||
| static void get_point(double *x, double *y, const char *data)
 | ||
| {
 | ||
|   float8get(*x, data);
 | ||
|   float8get(*y, data + SIZEOF_STORED_DOUBLE);
 | ||
| }
 | ||
| 
 | ||
| /***************************** Geometry *******************************/
 | ||
| 
 | ||
| Geometry::Class_info *Geometry::find_class(const char *name, size_t len)
 | ||
| {
 | ||
|   for (Class_info **cur_rt= ci_collection;
 | ||
|        cur_rt < ci_collection_end; cur_rt++)
 | ||
|   {
 | ||
|     if (*cur_rt &&
 | ||
| 	((*cur_rt)->m_name.length == len) &&
 | ||
| 	(my_charset_latin1.strnncoll((*cur_rt)->m_name.str, len,
 | ||
| 		                     name, len) == 0))
 | ||
|       return *cur_rt;
 | ||
|   }
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| Geometry *Geometry::create_by_typeid(Geometry_buffer *buffer, int type_id)
 | ||
| {
 | ||
|   Class_info *ci;
 | ||
|   if (!(ci= find_class(type_id)))
 | ||
|     return NULL;
 | ||
|   return (*ci->m_create_func)(buffer->data);
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| Geometry *Geometry::construct(Geometry_buffer *buffer,
 | ||
|                               const char *data, uint32 data_len)
 | ||
| {
 | ||
|   uint32 geom_type;
 | ||
|   Geometry *result;
 | ||
| 
 | ||
|   if (data_len < SRID_SIZE + WKB_HEADER_SIZE)   // < 4 + (1 + 4)
 | ||
|     return NULL;
 | ||
|   /* + 1 to skip the byte order (stored in position SRID_SIZE). */
 | ||
|   geom_type= uint4korr(data + SRID_SIZE + 1);
 | ||
|   if (!(result= create_by_typeid(buffer, (int) geom_type)))
 | ||
|     return NULL;
 | ||
|   result->m_data= data+ SRID_SIZE + WKB_HEADER_SIZE;
 | ||
|   result->m_data_end= data + data_len;
 | ||
|   return result;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| uint Geometry::get_key_image_itMBR(LEX_CSTRING &src, uchar *buff, uint length)
 | ||
| {
 | ||
|   const char *dummy;
 | ||
|   MBR mbr;
 | ||
|   Geometry_buffer buffer;
 | ||
|   Geometry *gobj;
 | ||
|   const uint image_length= SIZEOF_STORED_DOUBLE*4;
 | ||
| 
 | ||
|   if (src.length < SRID_SIZE)
 | ||
|   {
 | ||
|     bzero(buff, image_length);
 | ||
|     return image_length;
 | ||
|   }
 | ||
|   gobj= Geometry::construct(&buffer, (char*) src.str, (uint32) src.length);
 | ||
|   if (!gobj || gobj->get_mbr(&mbr, &dummy))
 | ||
|     bzero(buff, image_length);
 | ||
|   else
 | ||
|   {
 | ||
|     float8store(buff,    mbr.xmin);
 | ||
|     float8store(buff+8,  mbr.xmax);
 | ||
|     float8store(buff+16, mbr.ymin);
 | ||
|     float8store(buff+24, mbr.ymax);
 | ||
|   }
 | ||
|   return image_length;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| Geometry *Geometry::create_from_wkt(Geometry_buffer *buffer,
 | ||
| 				    Gis_read_stream *trs, String *wkt,
 | ||
| 				    bool init_stream)
 | ||
| {
 | ||
|   LEX_STRING name;
 | ||
|   Class_info *ci;
 | ||
|   char next_sym;
 | ||
| 
 | ||
|   if (trs->get_next_word(&name))
 | ||
|   {
 | ||
|     trs->set_error_msg("Geometry name expected");
 | ||
|     return NULL;
 | ||
|   }
 | ||
|   if (!(ci= find_class(name.str, name.length)) ||
 | ||
|       wkt->reserve(1 + 4, 512))
 | ||
|     return NULL;
 | ||
|   Geometry *result= (*ci->m_create_func)(buffer->data);
 | ||
|   wkt->q_append((char) wkb_ndr);
 | ||
|   wkt->q_append((uint32) result->get_class_info()->m_type_id);
 | ||
|   if (!(next_sym= trs->next_symbol()))
 | ||
|     return NULL;
 | ||
|   if (!(next_sym= trs->next_symbol()))
 | ||
|     return NULL;
 | ||
|   if ((next_sym == '(' && trs->check_next_symbol('(')) ||
 | ||
|       result->init_from_wkt(trs, wkt) ||
 | ||
|       (next_sym == '(' && trs->check_next_symbol(')')))
 | ||
|     return NULL;
 | ||
|   if (init_stream)  
 | ||
|   {
 | ||
|     result->set_data_ptr(wkt->ptr(), wkt->length());
 | ||
|     result->shift_wkb_header();
 | ||
|   }
 | ||
|   return result;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| int Geometry::as_wkt(String *wkt, const char **end)
 | ||
| {
 | ||
|   uint32 len= (uint) get_class_info()->m_name.length;
 | ||
|   if (wkt->reserve(len + 2, 512))
 | ||
|     return 1;
 | ||
|   wkt->qs_append(get_class_info()->m_name.str, len);
 | ||
|   if (get_class_info() != &geometrycollection_class)
 | ||
|     wkt->qs_append('(');
 | ||
|   if (get_data_as_wkt(wkt, end))
 | ||
|     return 1;
 | ||
|   if (get_class_info() != &geometrycollection_class)
 | ||
|     wkt->qs_append(')');
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| static const uchar type_keyname[]= "type";
 | ||
| static const uint type_keyname_len= 4;
 | ||
| static const uchar coord_keyname[]= "coordinates";
 | ||
| static const uint coord_keyname_len= 11;
 | ||
| static const uchar geometries_keyname[]= "geometries";
 | ||
| static const uint geometries_keyname_len= 10;
 | ||
| static const uchar features_keyname[]= "features";
 | ||
| static const uint features_keyname_len= 8;
 | ||
| static const uchar geometry_keyname[]= "geometry";
 | ||
| static const uint geometry_keyname_len= 8;
 | ||
| 
 | ||
| static const uint max_keyname_len= 11; /*'coordinates' keyname is the longest.*/
 | ||
| 
 | ||
| static const uchar feature_type[]= "feature";
 | ||
| static const int feature_type_len= 7;
 | ||
| static const uchar feature_coll_type[]= "featurecollection";
 | ||
| static const int feature_coll_type_len= 17;
 | ||
| static const uchar bbox_keyname[]= "bbox";
 | ||
| static const int bbox_keyname_len= 4;
 | ||
| 
 | ||
| 
 | ||
| int Geometry::as_json(String *wkt, uint max_dec_digits, const char **end)
 | ||
| {
 | ||
|   uint32 len= (uint) get_class_info()->m_geojson_name.length;
 | ||
|   if (wkt->reserve(4 + type_keyname_len + 2 + len + 2 + 2 +
 | ||
|                    coord_keyname_len + 4, 512))
 | ||
|     return 1;
 | ||
|   wkt->qs_append('"');
 | ||
|   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('"');
 | ||
|   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(']');
 | ||
| 
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| static double wkb_get_double(const char *ptr, Geometry::wkbByteOrder bo)
 | ||
| {
 | ||
|   double res;
 | ||
|   if (bo != Geometry::wkb_xdr)
 | ||
|   {
 | ||
|     float8get(res, ptr);
 | ||
|   }
 | ||
|   else
 | ||
|   {
 | ||
|     char inv_array[8];
 | ||
|     inv_array[0]= ptr[7];
 | ||
|     inv_array[1]= ptr[6];
 | ||
|     inv_array[2]= ptr[5];
 | ||
|     inv_array[3]= ptr[4];
 | ||
|     inv_array[4]= ptr[3];
 | ||
|     inv_array[5]= ptr[2];
 | ||
|     inv_array[6]= ptr[1];
 | ||
|     inv_array[7]= ptr[0];
 | ||
|     float8get(res, inv_array);
 | ||
|   }
 | ||
|   return res;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| static uint32 wkb_get_uint(const char *ptr, Geometry::wkbByteOrder bo)
 | ||
| {
 | ||
|   if (bo != Geometry::wkb_xdr)
 | ||
|     return uint4korr(ptr);
 | ||
|   /* else */
 | ||
|   {
 | ||
|     char inv_array[4];
 | ||
|     inv_array[0]= ptr[3];
 | ||
|     inv_array[1]= ptr[2];
 | ||
|     inv_array[2]= ptr[1];
 | ||
|     inv_array[3]= ptr[0];
 | ||
|     return uint4korr(inv_array);
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| Geometry *Geometry::create_from_wkb(Geometry_buffer *buffer,
 | ||
|                                     const char *wkb, uint32 len, String *res)
 | ||
| {
 | ||
|   uint32 geom_type;
 | ||
|   Geometry *geom;
 | ||
| 
 | ||
|   if (len < WKB_HEADER_SIZE)
 | ||
|     return NULL;
 | ||
|   geom_type= wkb_get_uint(wkb+1, (wkbByteOrder)wkb[0]);
 | ||
|   if (!(geom= create_by_typeid(buffer, (int) geom_type)) ||
 | ||
|       res->reserve(WKB_HEADER_SIZE, 512))
 | ||
|     return NULL;
 | ||
| 
 | ||
|   res->q_append((char) wkb_ndr);
 | ||
|   res->q_append(geom_type);
 | ||
| 
 | ||
|   return geom->init_from_wkb(wkb + WKB_HEADER_SIZE, len - WKB_HEADER_SIZE,
 | ||
|                              (wkbByteOrder) wkb[0], res) ? geom : NULL;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| Geometry *Geometry::create_from_json(Geometry_buffer *buffer,
 | ||
|                       json_engine_t *je, bool er_on_3D, String *res)
 | ||
| {
 | ||
|   Class_info *ci= NULL;
 | ||
|   const uchar *coord_start= NULL, *geom_start= NULL,
 | ||
|               *features_start= NULL, *geometry_start= NULL;
 | ||
|   Geometry *result;
 | ||
|   uchar key_buf[max_keyname_len];
 | ||
|   uint key_len;
 | ||
|   int fcoll_type_found= 0, feature_type_found= 0;
 | ||
| 
 | ||
| 
 | ||
|   if (json_read_value(je))
 | ||
|     goto err_return;
 | ||
|   
 | ||
|   if (je->value_type != JSON_VALUE_OBJECT)
 | ||
|   {
 | ||
|     je->s.error= GEOJ_INCORRECT_GEOJSON;
 | ||
|     goto err_return;
 | ||
|   }
 | ||
| 
 | ||
|   while (json_scan_next(je) == 0 && je->state != JST_OBJ_END)
 | ||
|   {
 | ||
|     DBUG_ASSERT(je->state == JST_KEY);
 | ||
| 
 | ||
|     key_len=0;
 | ||
|     while (json_read_keyname_chr(je) == 0)
 | ||
|     {
 | ||
|       if (je->s.c_next > 127 || key_len >= max_keyname_len)
 | ||
|       {
 | ||
|         /* Symbol out of range, or keyname too long. No need to compare.. */
 | ||
|         key_len=0;
 | ||
|         break;
 | ||
|       }
 | ||
|       key_buf[key_len++]= (uchar)je->s.c_next | 0x20; /* make it lowercase. */
 | ||
|     }
 | ||
| 
 | ||
|     if (unlikely(je->s.error))
 | ||
|       goto err_return;
 | ||
| 
 | ||
|     if (key_len == type_keyname_len &&
 | ||
|         memcmp(key_buf, type_keyname, type_keyname_len) == 0)
 | ||
|     {
 | ||
|       /*
 | ||
|          Found the "type" key. Let's check it's a string and remember
 | ||
|          the feature's type.
 | ||
|       */
 | ||
|       if (json_read_value(je))
 | ||
|         goto err_return;
 | ||
| 
 | ||
|       if (je->value_type == JSON_VALUE_STRING)
 | ||
|       {
 | ||
|         if ((ci= find_class((const char *) je->value, je->value_len)))
 | ||
|         {
 | ||
|           if ((coord_start=
 | ||
|                 (ci == &geometrycollection_class) ? geom_start : coord_start))
 | ||
|             goto create_geom;
 | ||
|         }
 | ||
|         else if (je->value_len == feature_coll_type_len &&
 | ||
|             my_charset_latin1.strnncoll(je->value, je->value_len,
 | ||
| 		                        feature_coll_type, feature_coll_type_len) == 0)
 | ||
|         {
 | ||
|           /*
 | ||
|             'FeatureCollection' type found. Handle the 'Featurecollection'/'features'
 | ||
|             GeoJSON construction.
 | ||
|           */
 | ||
|           if (features_start)
 | ||
|             goto handle_feature_collection;
 | ||
|           fcoll_type_found= 1;
 | ||
|         }
 | ||
|         else if (je->value_len == feature_type_len &&
 | ||
|                  my_charset_latin1.strnncoll(je->value, je->value_len,
 | ||
| 		                             feature_type, feature_type_len) == 0)
 | ||
|         {
 | ||
|           if (geometry_start)
 | ||
|             goto handle_geometry_key;
 | ||
|           feature_type_found= 1;
 | ||
|         }
 | ||
|         else /* can't understand the type. */
 | ||
|           break;
 | ||
|       }
 | ||
|       else /* The "type" value can only be string. */
 | ||
|         break;
 | ||
|     }
 | ||
|     else if (key_len == coord_keyname_len &&
 | ||
|              memcmp(key_buf, coord_keyname, coord_keyname_len) == 0)
 | ||
|     {
 | ||
|       /*
 | ||
|         Found the "coordinates" key. Let's check it's an array
 | ||
|         and remember where it starts.
 | ||
|       */
 | ||
|       if (json_read_value(je))
 | ||
|         goto err_return;
 | ||
| 
 | ||
|       if (je->value_type == JSON_VALUE_ARRAY)
 | ||
|       {
 | ||
|         coord_start= je->value_begin;
 | ||
|         if (ci && ci != &geometrycollection_class)
 | ||
|           goto create_geom;
 | ||
|         if (json_skip_level(je))
 | ||
|           goto err_return;
 | ||
|       }
 | ||
|     }
 | ||
|     else if (key_len == geometries_keyname_len &&
 | ||
|              memcmp(key_buf, geometries_keyname, geometries_keyname_len) == 0)
 | ||
|     {
 | ||
|       /*
 | ||
|         Found the "geometries" key. Let's check it's an array
 | ||
|         and remember where it starts.
 | ||
|       */
 | ||
|       if (json_read_value(je))
 | ||
|         goto err_return;
 | ||
| 
 | ||
|       if (je->value_type == JSON_VALUE_ARRAY)
 | ||
|       {
 | ||
|         geom_start= je->value_begin;
 | ||
|         if (ci == &geometrycollection_class)
 | ||
|         {
 | ||
|           coord_start= geom_start;
 | ||
|           goto create_geom;
 | ||
|         }
 | ||
|       }
 | ||
|     }
 | ||
|     else if (key_len == features_keyname_len &&
 | ||
|              memcmp(key_buf, features_keyname, features_keyname_len) == 0)
 | ||
|     {
 | ||
|       /*
 | ||
|         'features' key found. Handle the 'Featurecollection'/'features'
 | ||
|         GeoJSON construction.
 | ||
|       */
 | ||
|       if (json_read_value(je))
 | ||
|         goto err_return;
 | ||
|       if (je->value_type == JSON_VALUE_ARRAY)
 | ||
|       {
 | ||
|         features_start= je->value_begin;
 | ||
|         if (fcoll_type_found)
 | ||
|           goto handle_feature_collection;
 | ||
|       }
 | ||
|     }
 | ||
|     else if (key_len == geometry_keyname_len &&
 | ||
|              memcmp(key_buf, geometry_keyname, geometry_keyname_len) == 0)
 | ||
|     {
 | ||
|       if (json_read_value(je))
 | ||
|         goto err_return;
 | ||
|       if (je->value_type == JSON_VALUE_OBJECT)
 | ||
|       {
 | ||
|         geometry_start= je->value_begin;
 | ||
|         if (feature_type_found)
 | ||
|           goto handle_geometry_key;
 | ||
|       }
 | ||
|       else
 | ||
|         goto err_return;
 | ||
|     }
 | ||
|     else
 | ||
|     {
 | ||
|       if (json_skip_key(je))
 | ||
|         goto err_return;
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   if (je->s.error == 0)
 | ||
|   {
 | ||
|     /*
 | ||
|       We didn't find all the required keys. That are "type" and "coordinates"
 | ||
|       or "geometries" for GeometryCollection.
 | ||
|     */
 | ||
|     je->s.error= GEOJ_INCORRECT_GEOJSON;
 | ||
|   }
 | ||
|   goto err_return;
 | ||
| 
 | ||
| handle_feature_collection:
 | ||
|   ci= &geometrycollection_class;
 | ||
|   coord_start= features_start;
 | ||
| 
 | ||
| create_geom:
 | ||
| 
 | ||
|   json_scan_start(je, je->s.cs, coord_start, je->s.str_end);
 | ||
| 
 | ||
|   if (res->reserve(1 + 4, 512))
 | ||
|     goto err_return;
 | ||
| 
 | ||
|   result= (*ci->m_create_func)(buffer->data);
 | ||
|   res->q_append((char) wkb_ndr);
 | ||
|   res->q_append((uint32) result->get_class_info()->m_type_id);
 | ||
|   if (result->init_from_json(je, er_on_3D, res))
 | ||
|     goto err_return;
 | ||
| 
 | ||
|   return result;
 | ||
| 
 | ||
| handle_geometry_key:
 | ||
|   json_scan_start(je, je->s.cs, geometry_start, je->s.str_end);
 | ||
|   return create_from_json(buffer, je, er_on_3D, res);
 | ||
| 
 | ||
| err_return:
 | ||
|   return NULL;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| Geometry *Geometry::create_from_opresult(Geometry_buffer *g_buf,
 | ||
|                                    String *res, Gcalc_result_receiver &rr)
 | ||
| {
 | ||
|   uint32 geom_type= rr.get_result_typeid();
 | ||
|   Geometry *obj= create_by_typeid(g_buf, geom_type);
 | ||
| 
 | ||
|   if (!obj || res->reserve(WKB_HEADER_SIZE, 512))
 | ||
|     return NULL;
 | ||
| 
 | ||
|   res->q_append((char) wkb_ndr);
 | ||
|   res->q_append(geom_type);
 | ||
|   return obj->init_from_opresult(res, rr.result(), rr.length()) ? obj : NULL;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Geometry::envelope(String *result) const
 | ||
| {
 | ||
|   MBR mbr;
 | ||
|   const char *end;
 | ||
| 
 | ||
|   if (get_mbr(&mbr, &end))
 | ||
|     return 1;
 | ||
| 
 | ||
|   if (!mbr.valid())
 | ||
|   {
 | ||
|     /* Empty geometry */
 | ||
|     if (result->reserve(1 + 4*2))
 | ||
|       return 1;
 | ||
|     result->q_append((char) wkb_ndr);
 | ||
|     result->q_append((uint32) wkb_geometrycollection);
 | ||
|     result->q_append((uint32) 0);
 | ||
|     return 0;
 | ||
|   }
 | ||
|   if (result->reserve(1 + 4 * 3 + SIZEOF_STORED_DOUBLE * 10))
 | ||
|     return 1;
 | ||
| 
 | ||
|   result->q_append((char) wkb_ndr);
 | ||
|   result->q_append((uint32) wkb_polygon);
 | ||
|   result->q_append((uint32) 1);
 | ||
|   result->q_append((uint32) 5);
 | ||
|   result->q_append(mbr.xmin);
 | ||
|   result->q_append(mbr.ymin);
 | ||
|   result->q_append(mbr.xmax);
 | ||
|   result->q_append(mbr.ymin);
 | ||
|   result->q_append(mbr.xmax);
 | ||
|   result->q_append(mbr.ymax);
 | ||
|   result->q_append(mbr.xmin);
 | ||
|   result->q_append(mbr.ymax);
 | ||
|   result->q_append(mbr.xmin);
 | ||
|   result->q_append(mbr.ymin);
 | ||
| 
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| int Geometry::is_simple(int *simple) const {
 | ||
|   Gcalc_scan_iterator scan_it;
 | ||
|   Gcalc_heap collector;
 | ||
|   Gcalc_function func;
 | ||
|   Gcalc_operation_transporter trn(&func, &collector);
 | ||
|   const char *c_end;
 | ||
|   MBR mbr;
 | ||
|   *simple= 0;
 | ||
| 
 | ||
|  if(this->get_mbr(&mbr, &c_end))
 | ||
|     return 1;
 | ||
| 
 | ||
|   collector.set_extent(mbr.xmin, mbr.xmax, mbr.ymin, mbr.ymax);
 | ||
|   if (this->store_shapes(&trn))
 | ||
|     return 1;
 | ||
| 
 | ||
|   collector.prepare_operation();
 | ||
|   scan_it.init(&collector);
 | ||
| 
 | ||
|   while (scan_it.more_points())
 | ||
|   {
 | ||
|     const Gcalc_scan_iterator::event_point *ev, *next_ev;
 | ||
| 
 | ||
|     if (scan_it.step())
 | ||
|       return 1;
 | ||
| 
 | ||
|     ev= scan_it.get_events();
 | ||
|     if (ev->simple_event())
 | ||
|       continue;
 | ||
| 
 | ||
|     next_ev= ev->get_next();
 | ||
|     if ((ev->event & (scev_thread | scev_single_point)) && !next_ev)
 | ||
|       continue;
 | ||
| 
 | ||
|     if ((ev->event == scev_two_threads) && !next_ev->get_next())
 | ||
|       continue;
 | ||
| 
 | ||
|     /* If the first and last points of a curve coincide - that is     */
 | ||
|     /* an exception to the rule and the line is considered as simple. */
 | ||
|     if ((next_ev && !next_ev->get_next()) &&
 | ||
|         (ev->event & (scev_thread | scev_end)) &&
 | ||
|         (next_ev->event & (scev_thread | scev_end)))
 | ||
|       continue;
 | ||
| 
 | ||
|     return 0;
 | ||
|   }
 | ||
| 
 | ||
|   *simple= 1;
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| /*
 | ||
|   Create a point from data.
 | ||
| 
 | ||
|   SYNPOSIS
 | ||
|     create_point()
 | ||
|     result		Put result here
 | ||
|     data		Data for point is here.
 | ||
| 
 | ||
|   RETURN
 | ||
|     0	ok
 | ||
|     1	Can't reallocate 'result'
 | ||
| */
 | ||
| bool Geometry::create_point(String *result, const char *data) const
 | ||
| {
 | ||
|   if (no_data(data, POINT_DATA_SIZE) ||
 | ||
|       result->reserve(1 + 4 + POINT_DATA_SIZE))
 | ||
|     return 1;
 | ||
|   result->q_append((char) wkb_ndr);
 | ||
|   result->q_append((uint32) wkb_point);
 | ||
|   /* Copy two double in same format */
 | ||
|   result->q_append(data, POINT_DATA_SIZE);
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| /*
 | ||
|   Create a point from coordinates.
 | ||
| 
 | ||
|   SYNPOSIS
 | ||
|     create_point()
 | ||
|     result		Put result here
 | ||
|     x			x coordinate for point
 | ||
|     y			y coordinate for point
 | ||
| 
 | ||
|   RETURN
 | ||
|     0	ok
 | ||
|     1	Can't reallocate 'result'
 | ||
| */
 | ||
| 
 | ||
| bool Geometry::create_point(String *result, double x, double y)
 | ||
| {
 | ||
|   if (result->reserve(1 + 4 + POINT_DATA_SIZE))
 | ||
|     return 1;
 | ||
| 
 | ||
|   result->q_append((char) wkb_ndr);
 | ||
|   result->q_append((uint32) wkb_point);
 | ||
|   result->q_append(x);
 | ||
|   result->q_append(y);
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| /*
 | ||
|   Append N points from packed format to text
 | ||
| 
 | ||
|   SYNOPSIS
 | ||
|     append_points()
 | ||
|     txt			Append points here
 | ||
|     n_points		Number of points
 | ||
|     data		Packed data
 | ||
|     offset		Offset between points
 | ||
| 
 | ||
|   RETURN
 | ||
|     # end of data
 | ||
| */
 | ||
| 
 | ||
| const char *Geometry::append_points(String *txt, uint32 n_points,
 | ||
| 				    const char *data, uint32 offset) const
 | ||
| {			     
 | ||
|   while (n_points--)
 | ||
|   {
 | ||
|     double x,y;
 | ||
|     data+= offset;
 | ||
|     get_point(&x, &y, data);
 | ||
|     data+= POINT_DATA_SIZE;
 | ||
|     txt->qs_append(x);
 | ||
|     txt->qs_append(' ');
 | ||
|     txt->qs_append(y);
 | ||
|     txt->qs_append(',');
 | ||
|   }
 | ||
|   return data;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| static void append_json_point(String *txt, uint max_dec, const char *data)
 | ||
| {
 | ||
|   double x,y;
 | ||
|   get_point(&x, &y, data);
 | ||
|   if (max_dec < FLOATING_POINT_DECIMALS)
 | ||
|   {
 | ||
|     x= my_double_round(x, max_dec, FALSE, FALSE);
 | ||
|     y= my_double_round(y, max_dec, FALSE, FALSE);
 | ||
|   }
 | ||
|   txt->qs_append('[');
 | ||
|   txt->qs_append(x);
 | ||
|   txt->qs_append(", ", 2);
 | ||
|   txt->qs_append(y);
 | ||
|   txt->qs_append(']');
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| /*
 | ||
|   Append N points from packed format to json
 | ||
| 
 | ||
|   SYNOPSIS
 | ||
|     append_json_points()
 | ||
|     txt			Append points here
 | ||
|     n_points		Number of points
 | ||
|     data		Packed data
 | ||
|     offset		Offset between points
 | ||
| 
 | ||
|   RETURN
 | ||
|     # end of data
 | ||
| */
 | ||
| 
 | ||
| static const char *append_json_points(String *txt, uint max_dec,
 | ||
|     uint32 n_points, const char *data, uint32 offset)
 | ||
| {			     
 | ||
|   txt->qs_append('[');
 | ||
|   while (n_points--)
 | ||
|   {
 | ||
|     data+= offset;
 | ||
|     append_json_point(txt, max_dec, data);
 | ||
|     data+= POINT_DATA_SIZE;
 | ||
|     txt->qs_append(", ", 2);
 | ||
|   }
 | ||
|   txt->length(txt->length() - 2);// Remove ending ', '
 | ||
|   txt->qs_append(']');
 | ||
|   return data;
 | ||
| }
 | ||
| /*
 | ||
|   Get most bounding rectangle (mbr) for X points
 | ||
| 
 | ||
|   SYNOPSIS
 | ||
|     get_mbr_for_points()
 | ||
|     mbr			MBR (store rectangle here)
 | ||
|     points		Number of points
 | ||
|     data		Packed data
 | ||
|     offset		Offset between points
 | ||
| 
 | ||
|   RETURN
 | ||
|     0	Wrong data
 | ||
|     #	end of data
 | ||
| */
 | ||
| 
 | ||
| const char *Geometry::get_mbr_for_points(MBR *mbr, const char *data,
 | ||
| 					 uint offset) const
 | ||
| {
 | ||
|   uint32 points;
 | ||
|   /* read number of points */
 | ||
|   if (no_data(data, 4))
 | ||
|     return 0;
 | ||
|   points= uint4korr(data);
 | ||
|   data+= 4;
 | ||
| 
 | ||
|   if (not_enough_points(data, points, offset))
 | ||
|     return 0;
 | ||
| 
 | ||
|   /* Calculate MBR for points */
 | ||
|   while (points--)
 | ||
|   {
 | ||
|     data+= offset;
 | ||
|     mbr->add_xy(data, data + SIZEOF_STORED_DOUBLE);
 | ||
|     data+= POINT_DATA_SIZE;
 | ||
|   }
 | ||
|   return data;
 | ||
| }
 | ||
| 
 | ||
| const char* Geometry::get_points_common(const char* data,
 | ||
|                                         Geometry::PointContainer &points) const
 | ||
| {
 | ||
|   uint32 expected_points;
 | ||
|   if (no_data(data, 4))
 | ||
|     return nullptr;
 | ||
|   expected_points= uint4korr(data);
 | ||
|   data+= 4;
 | ||
| 
 | ||
|   if (not_enough_points(data, expected_points, 0))
 | ||
|     return nullptr;
 | ||
| 
 | ||
|   while (expected_points--)
 | ||
|   {
 | ||
|     double x, y;
 | ||
|     float8get(x, data);
 | ||
|     float8get(y, data + SIZEOF_STORED_DOUBLE);
 | ||
|     points.push_back(std::make_pair(x, y));
 | ||
|     data+= POINT_DATA_SIZE;
 | ||
|   }
 | ||
|   return data;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| /***************************** Point *******************************/
 | ||
| 
 | ||
| uint32 Gis_point::get_data_size() const
 | ||
| {
 | ||
|   return POINT_DATA_SIZE;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_point::init_from_wkt(Gis_read_stream *trs, String *wkb)
 | ||
| {
 | ||
|   double x, y;
 | ||
|   if (trs->get_next_number(&x) || trs->get_next_number(&y) ||
 | ||
|       wkb->reserve(POINT_DATA_SIZE, 512))
 | ||
|     return 1;
 | ||
|   wkb->q_append(x);
 | ||
|   wkb->q_append(y);
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| uint Gis_point::init_from_wkb(const char *wkb, uint len,
 | ||
|                               wkbByteOrder bo, String *res)
 | ||
| {
 | ||
|   double x, y;
 | ||
|   if (len < POINT_DATA_SIZE || res->reserve(POINT_DATA_SIZE))
 | ||
|     return 0;
 | ||
|   x= wkb_get_double(wkb, bo);
 | ||
|   y= wkb_get_double(wkb + SIZEOF_STORED_DOUBLE, bo);
 | ||
|   res->q_append(x);
 | ||
|   res->q_append(y);
 | ||
|   return POINT_DATA_SIZE;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| static int read_point_from_json(json_engine_t *je, bool er_on_3D,
 | ||
|                                 double *x, double *y)
 | ||
| {
 | ||
|   int n_coord= 0, err;
 | ||
|   double tmp, *d;
 | ||
|   char *endptr;
 | ||
| 
 | ||
|   while (json_scan_next(je) == 0 && je->state != JST_ARRAY_END)
 | ||
|   {
 | ||
|     DBUG_ASSERT(je->state == JST_VALUE);
 | ||
|     if (json_read_value(je))
 | ||
|       return 1;
 | ||
| 
 | ||
|     if (je->value_type != JSON_VALUE_NUMBER)
 | ||
|       goto bad_coordinates;
 | ||
| 
 | ||
|     d= (n_coord == 0) ? x : ((n_coord == 1) ? y : &tmp);
 | ||
|     *d= je->s.cs->strntod((char *) je->value, je->value_len, &endptr, &err);
 | ||
|     if (err)
 | ||
|       goto bad_coordinates;
 | ||
|     n_coord++;
 | ||
|   }
 | ||
| 
 | ||
|   if (n_coord <= 2 || !er_on_3D)
 | ||
|     return 0;
 | ||
|   je->s.error= Geometry::GEOJ_DIMENSION_NOT_SUPPORTED;
 | ||
|   return 1;
 | ||
| bad_coordinates:
 | ||
|   je->s.error= Geometry::GEOJ_INCORRECT_GEOJSON;
 | ||
|   return 1;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_point::init_from_json(json_engine_t *je, bool er_on_3D, String *wkb)
 | ||
| {
 | ||
|   double x, y;
 | ||
|   if (json_read_value(je))
 | ||
|     return TRUE;
 | ||
| 
 | ||
|   if (je->value_type != JSON_VALUE_ARRAY)
 | ||
|   {
 | ||
|     je->s.error= GEOJ_INCORRECT_GEOJSON;
 | ||
|     return TRUE;
 | ||
|   }
 | ||
| 
 | ||
|   if (read_point_from_json(je, er_on_3D, &x, &y) ||
 | ||
|       wkb->reserve(POINT_DATA_SIZE))
 | ||
|     return TRUE;
 | ||
| 
 | ||
|   wkb->q_append(x);
 | ||
|   wkb->q_append(y);
 | ||
|   return FALSE;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_point::get_data_as_wkt(String *txt, const char **end) const
 | ||
| {
 | ||
|   double x, y;
 | ||
|   if (get_xy(&x, &y))
 | ||
|     return 1;
 | ||
|   if (txt->reserve(MAX_DIGITS_IN_DOUBLE * 2 + 1))
 | ||
|     return 1;
 | ||
|   txt->qs_append(x);
 | ||
|   txt->qs_append(' ');
 | ||
|   txt->qs_append(y);
 | ||
|   *end= m_data+ POINT_DATA_SIZE;
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_point::get_data_as_json(String *txt, uint max_dec_digits,
 | ||
|                                  const char **end) const
 | ||
| {
 | ||
|   if (txt->reserve(MAX_DIGITS_IN_DOUBLE * 2 + 4))
 | ||
|     return 1;
 | ||
|   append_json_point(txt, max_dec_digits, m_data);
 | ||
|   *end= m_data+ POINT_DATA_SIZE;
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_point::get_mbr(MBR *mbr, const char **end) const
 | ||
| {
 | ||
|   double x, y;
 | ||
|   if (get_xy(&x, &y))
 | ||
|     return 1;
 | ||
|   mbr->add_xy(x, y);
 | ||
|   *end= m_data+ POINT_DATA_SIZE;
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| int Gis_point::is_valid(int *valid) const
 | ||
| {
 | ||
|   double x, y;
 | ||
|   if (get_xy(&x, &y))
 | ||
|     return 1;
 | ||
| 
 | ||
|   *valid= 1;
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| int Gis_point::area(double *ar, const char **end) const
 | ||
| {
 | ||
|   *ar= 0;
 | ||
|   *end= m_data+ POINT_DATA_SIZE;
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| int Gis_point::geom_length(double *len, const char **end) const
 | ||
| {
 | ||
|   *len= 0;
 | ||
|   *end= m_data+ POINT_DATA_SIZE;
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| int Gis_point::store_shapes(Gcalc_shape_transporter *trn) const
 | ||
| {
 | ||
|   double x, y;
 | ||
| 
 | ||
|   return get_xy(&x, &y) || trn->single_point(x, y);
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| const Geometry::Class_info *Gis_point::get_class_info() const
 | ||
| {
 | ||
|   return &point_class;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| /**
 | ||
|   Function to calculate haversine.
 | ||
|   Taking as arguments Point and Multipoint geometries.
 | ||
|   Multipoint geometry has to be single point only.
 | ||
|   It is up to caller to ensure valid input.
 | ||
| 
 | ||
|   @param    g      pointer to the Geometry
 | ||
|   @param    r      sphere radius
 | ||
|   @param    error  pointer describing the error in case of the boundary conditions
 | ||
| 
 | ||
|   @return distance in case without error, it is calculated distance (non-negative),
 | ||
|                    in case error exist, negative value.
 | ||
| */
 | ||
| double Gis_point::calculate_haversine(const Geometry *g,
 | ||
|                                       const double sphere_radius,
 | ||
|                                       int *error)
 | ||
| {
 | ||
|   DBUG_ASSERT(sphere_radius > 0);
 | ||
|   double x1r, x2r, y1r, y2r;
 | ||
| 
 | ||
|   // This check is done only for optimization purposes where we know it will
 | ||
|   // be one and only one point in Multipoint
 | ||
|   if (g->get_class_info()->m_type_id == Geometry::wkb_multipoint)
 | ||
|   {
 | ||
|     const char point_size= 4 + WKB_HEADER_SIZE + POINT_DATA_SIZE+1; //1 for the type
 | ||
|     char point_temp[point_size];
 | ||
|     memset(point_temp+4, Geometry::wkb_point, 1);
 | ||
|     memcpy(point_temp+5, static_cast<const Gis_multi_point *>(g)->get_data_ptr()+5, 4);
 | ||
|     memcpy(point_temp+4+WKB_HEADER_SIZE, g->get_data_ptr()+4+WKB_HEADER_SIZE,
 | ||
|            POINT_DATA_SIZE);
 | ||
|     point_temp[point_size-1]= '\0';
 | ||
|     Geometry_buffer gbuff;
 | ||
|     Geometry *gg= Geometry::construct(&gbuff, point_temp, point_size-1);
 | ||
|     if (!gg || static_cast<Gis_point *>(gg)->get_xy_radian(&x2r, &y2r))
 | ||
|     {
 | ||
|       *error= 2;
 | ||
|       return -1;
 | ||
|     }
 | ||
|   }
 | ||
|   else
 | ||
|   {
 | ||
|     if (static_cast<const Gis_point *>(g)->get_xy_radian(&x2r, &y2r))
 | ||
|     {
 | ||
|       *error= 2;
 | ||
|       return -1;
 | ||
|     }
 | ||
|   }
 | ||
|   if (this->get_xy_radian(&x1r, &y1r))
 | ||
|   {
 | ||
|     *error= 2;
 | ||
|     return -1;
 | ||
|   }
 | ||
|   //
 | ||
|   // Check boundary conditions: longitude[-180,180]
 | ||
|   if (!((x2r >= -M_PI && x2r <= M_PI) && (x1r >= -M_PI && x1r <= M_PI)))
 | ||
|   {
 | ||
|     *error=1;
 | ||
|     return -1;
 | ||
|   }
 | ||
|   // Check boundary conditions: latitude[-90,90]
 | ||
|   if (!((y2r >= -M_PI/2 && y2r <= M_PI/2) && (y1r >= -M_PI/2 && y1r <= M_PI/2)))
 | ||
|   {
 | ||
|     *error=-1;
 | ||
|     return -1;
 | ||
|   }
 | ||
|   double dlat= sin((y2r - y1r)/2)*sin((y2r - y1r)/2);
 | ||
|   double dlong= sin((x2r - x1r)/2)*sin((x2r - x1r)/2);
 | ||
|   return 2*sphere_radius*asin((sqrt(dlat + cos(y1r)*cos(y2r)*dlong)));
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| /**
 | ||
|   Function that calculate spherical distance of Point from Multipoint geometries.
 | ||
|   In case there is single point in Multipoint geometries calculate_haversine()
 | ||
|   can handle such case. Otherwise, new geometry (Point) has to be constructed.
 | ||
| 
 | ||
|   @param    g pointer to the Geometry
 | ||
|   @param    r sphere radius
 | ||
|   @param    result pointer to the result
 | ||
|   @param    err    pointer to the error obtained from calculate_haversin()
 | ||
| 
 | ||
|   @return state
 | ||
|   @retval TRUE  failed
 | ||
|   @retval FALSE success
 | ||
| */
 | ||
| int Gis_point::spherical_distance_multipoints(Geometry *g, const double r,
 | ||
|                                               double *result, int *err)
 | ||
| {  
 | ||
|   uint32 num_of_points2;
 | ||
|     // To find the minimum radius it cannot be greater than Earth radius
 | ||
|   double res= 6370986.0;
 | ||
|   double temp_res= 0.0;
 | ||
|   const uint32 len= 4 + WKB_HEADER_SIZE + POINT_DATA_SIZE + 1;
 | ||
|   char s[len];
 | ||
|   g->num_geometries(&num_of_points2);
 | ||
|   DBUG_ASSERT(num_of_points2 >= 1);
 | ||
|   if (num_of_points2 == 1)
 | ||
|   {
 | ||
|     *result= this->calculate_haversine(g, r, err);
 | ||
|     return 0;
 | ||
|   }
 | ||
|   for (uint32 i=1; i <= num_of_points2; i++)
 | ||
|   {
 | ||
|     Geometry_buffer buff_temp;
 | ||
|     Geometry *temp;
 | ||
|     const char *pt_ptr= g->get_data_ptr()+
 | ||
|       4+WKB_HEADER_SIZE*i + POINT_DATA_SIZE*(i-1);
 | ||
| 
 | ||
|     // First 4 bytes are handled already, make sure to create a Point
 | ||
|     memset(s + 4, Geometry::wkb_point, 1);
 | ||
|     if (g->no_data(pt_ptr, POINT_DATA_SIZE))
 | ||
|       return 1;
 | ||
| 
 | ||
|     memcpy(s + 5, g->get_data_ptr() + 5, 4);
 | ||
|     memcpy(s + 4 + WKB_HEADER_SIZE, pt_ptr, POINT_DATA_SIZE);
 | ||
|     s[len-1]= '\0';
 | ||
|     temp= Geometry::construct(&buff_temp, s, len);
 | ||
|     if (!temp)
 | ||
|       return 1;
 | ||
|     temp_res= this->calculate_haversine(temp, r, err);
 | ||
|     if (res > temp_res)
 | ||
|       res= temp_res;
 | ||
|   }
 | ||
|   *result= res;
 | ||
|   return 0;
 | ||
| }
 | ||
| /***************************** LineString *******************************/
 | ||
| 
 | ||
| uint32 Gis_line_string::get_data_size() const 
 | ||
| {
 | ||
|   uint32 n_points;
 | ||
|   if (no_data(m_data, 4))
 | ||
|     return GET_SIZE_ERROR;
 | ||
| 
 | ||
|   n_points= uint4korr(m_data);
 | ||
| 
 | ||
|   if (not_enough_points(m_data + 4, n_points))
 | ||
|     return GET_SIZE_ERROR;
 | ||
| 
 | ||
|   return 4 + n_points * POINT_DATA_SIZE;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_line_string::init_from_wkt(Gis_read_stream *trs, String *wkb)
 | ||
| {
 | ||
|   uint32 n_points= 0;
 | ||
|   uint32 np_pos= wkb->length();
 | ||
|   Gis_point p;
 | ||
| 
 | ||
|   if (wkb->reserve(4, 512))
 | ||
|     return 1;
 | ||
|   wkb->length(wkb->length()+4);			// Reserve space for points  
 | ||
| 
 | ||
|   for (;;)
 | ||
|   {
 | ||
|     if (p.init_from_wkt(trs, wkb))
 | ||
|       return 1;
 | ||
|     n_points++;
 | ||
|     if (trs->skip_char(','))			// Didn't find ','
 | ||
|       break;
 | ||
|   }
 | ||
|   if (n_points < 1)
 | ||
|   {
 | ||
|     trs->set_error_msg("Too few points in LINESTRING");
 | ||
|     return 1;
 | ||
|   }
 | ||
|   wkb->write_at_position(np_pos, n_points);
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| uint Gis_line_string::init_from_wkb(const char *wkb, uint len,
 | ||
|                                     wkbByteOrder bo, String *res)
 | ||
| {
 | ||
|   uint32 n_points, proper_length;
 | ||
|   const char *wkb_end;
 | ||
|   Gis_point p;
 | ||
| 
 | ||
|   if (len < 4 || (n_points= wkb_get_uint(wkb, bo)) < 1 ||
 | ||
|       ((len - 4) / POINT_DATA_SIZE) < n_points)
 | ||
|     return 0;
 | ||
|   proper_length= 4 + n_points * POINT_DATA_SIZE;
 | ||
| 
 | ||
|   if (len < proper_length || res->reserve(proper_length))
 | ||
|     return 0;
 | ||
| 
 | ||
|   res->q_append(n_points);
 | ||
|   wkb_end= wkb + proper_length;
 | ||
|   for (wkb+= 4; wkb<wkb_end; wkb+= POINT_DATA_SIZE)
 | ||
|   {
 | ||
|     if (!p.init_from_wkb(wkb, POINT_DATA_SIZE, bo, res))
 | ||
|       return 0;
 | ||
|   }
 | ||
| 
 | ||
|   return proper_length;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_line_string::init_from_json(json_engine_t *je, bool er_on_3D,
 | ||
|                                      String *wkb)
 | ||
| {
 | ||
|   uint32 n_points= 0;
 | ||
|   uint32 np_pos= wkb->length();
 | ||
|   Gis_point p;
 | ||
| 
 | ||
|   if (json_read_value(je))
 | ||
|     return TRUE;
 | ||
| 
 | ||
|   if (je->value_type != JSON_VALUE_ARRAY)
 | ||
|   {
 | ||
|     je->s.error= GEOJ_INCORRECT_GEOJSON;
 | ||
|     return TRUE;
 | ||
|   }
 | ||
| 
 | ||
|   if (wkb->reserve(4, 512))
 | ||
|     return TRUE;
 | ||
|   wkb->length(wkb->length()+4);	// Reserve space for n_points  
 | ||
| 
 | ||
|   while (json_scan_next(je) == 0 && je->state != JST_ARRAY_END)
 | ||
|   {
 | ||
|     DBUG_ASSERT(je->state == JST_VALUE);
 | ||
| 
 | ||
|     if (p.init_from_json(je, er_on_3D, wkb))
 | ||
|       return TRUE;
 | ||
|     n_points++;
 | ||
|   }
 | ||
|   if (n_points < 1)
 | ||
|   {
 | ||
|     je->s.error= Geometry::GEOJ_TOO_FEW_POINTS;
 | ||
|     return TRUE;
 | ||
|   }
 | ||
|   wkb->write_at_position(np_pos, n_points);
 | ||
|   return FALSE;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_line_string::get_data_as_wkt(String *txt, const char **end) const
 | ||
| {
 | ||
|   uint32 n_points;
 | ||
|   const char *data= m_data;
 | ||
| 
 | ||
|   if (no_data(data, 4))
 | ||
|     return 1;
 | ||
|   n_points= uint4korr(data);
 | ||
|   data += 4;
 | ||
| 
 | ||
|   if (n_points < 1 ||
 | ||
|       not_enough_points(data, n_points) ||
 | ||
|       txt->reserve(((MAX_DIGITS_IN_DOUBLE + 1)*2 + 1) * n_points))
 | ||
|     return 1;
 | ||
| 
 | ||
|   while (n_points--)
 | ||
|   {
 | ||
|     double x, y;
 | ||
|     get_point(&x, &y, data);
 | ||
|     data+= POINT_DATA_SIZE;
 | ||
|     txt->qs_append(x);
 | ||
|     txt->qs_append(' ');
 | ||
|     txt->qs_append(y);
 | ||
|     txt->qs_append(',');
 | ||
|   }
 | ||
|   txt->length(txt->length() - 1);		// Remove end ','
 | ||
|   *end= data;
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_line_string::get_data_as_json(String *txt, uint max_dec_digits,
 | ||
|                                        const char **end) const
 | ||
| {
 | ||
|   uint32 n_points;
 | ||
|   const char *data= m_data;
 | ||
| 
 | ||
|   if (no_data(data, 4))
 | ||
|     return 1;
 | ||
|   n_points= uint4korr(data);
 | ||
|   data += 4;
 | ||
| 
 | ||
|   if (n_points < 1 ||
 | ||
|       not_enough_points(data, n_points) ||
 | ||
|       txt->reserve((MAX_DIGITS_IN_DOUBLE*2 + 6) * n_points + 2))
 | ||
|     return 1;
 | ||
| 
 | ||
|   *end= append_json_points(txt, max_dec_digits, n_points, data, 0);
 | ||
| 
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_line_string::get_mbr(MBR *mbr, const char **end) const
 | ||
| {
 | ||
|   return (*end=get_mbr_for_points(mbr, m_data, 0)) == 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| int Gis_line_string::geom_length(double *len, const char **end) const
 | ||
| {
 | ||
|   uint32 n_points;
 | ||
|   double prev_x, prev_y;
 | ||
|   const char *data= m_data;
 | ||
| 
 | ||
|   *len= 0;					// In case of errors
 | ||
|   if (no_data(data, 4))
 | ||
|     return 1;
 | ||
|   n_points= uint4korr(data);
 | ||
|   data+= 4;
 | ||
|   if (n_points < 1 || not_enough_points(data, n_points))
 | ||
|     return 1;
 | ||
| 
 | ||
|   get_point(&prev_x, &prev_y, data);
 | ||
|   data+= POINT_DATA_SIZE;
 | ||
|   while (--n_points)
 | ||
|   {
 | ||
|     double x, y;
 | ||
|     get_point(&x, &y, data);
 | ||
|     data+= POINT_DATA_SIZE;
 | ||
|     *len+= sqrt(pow(prev_x-x,2)+pow(prev_y-y,2));
 | ||
|     prev_x= x;
 | ||
|     prev_y= y;
 | ||
|   }
 | ||
|   *end= data;
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| int Gis_line_string::area(double *ar, const char **end) const
 | ||
| {
 | ||
|   uint32 n_points;
 | ||
|   *ar= 0.0;
 | ||
| 
 | ||
|   /* read number of points */
 | ||
|   if (no_data(m_data, 4))
 | ||
|     return 1;
 | ||
|   n_points= uint4korr(m_data);
 | ||
|   *end= m_data + 4 + POINT_DATA_SIZE * n_points;
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| int Gis_line_string::is_closed(int *closed) const
 | ||
| {
 | ||
|   uint32 n_points;
 | ||
|   double x1, y1, x2, y2;
 | ||
|   const char *data= m_data;
 | ||
| 
 | ||
|   if (no_data(data, 4))
 | ||
|     return 1;
 | ||
|   n_points= uint4korr(data);
 | ||
|   if (n_points == 1)
 | ||
|   {
 | ||
|     *closed=1;
 | ||
|     return 0;
 | ||
|   }
 | ||
|   data+= 4;
 | ||
|   if (n_points == 0 || not_enough_points(data, n_points))
 | ||
|     return 1;
 | ||
| 
 | ||
|   /* Get first point */
 | ||
|   get_point(&x1, &y1, data);
 | ||
| 
 | ||
|   /* get last point */
 | ||
|   data+= POINT_DATA_SIZE + (n_points-2)*POINT_DATA_SIZE;
 | ||
|   get_point(&x2, &y2, data);
 | ||
| 
 | ||
|   *closed= (x1==x2) && (y1==y2);
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| int Gis_line_string::is_valid(int *valid) const
 | ||
| {
 | ||
|   Geometry_buffer buffer;
 | ||
|   Geometry *geometry;
 | ||
|   uint32 num_points;
 | ||
|   *valid= 0;
 | ||
| 
 | ||
|   if (no_data(m_data, 4))
 | ||
|     return 1;
 | ||
| 
 | ||
|   num_points= uint4korr(m_data);
 | ||
|   if (not_enough_points(m_data, num_points))
 | ||
|     return 1;
 | ||
| 
 | ||
|   double x, y, UNINIT_VAR(previous_x), UNINIT_VAR(previous_y);
 | ||
|   for (uint32 i = 1; i <= num_points; i++)
 | ||
|   {
 | ||
|     String wkb= 0;
 | ||
| 
 | ||
|     if (wkb.reserve(SRID_SIZE + BYTE_ORDER_SIZE + WKB_HEADER_SIZE))
 | ||
|       return 1;
 | ||
| 
 | ||
|     wkb.q_append(SRID_PLACEHOLDER);
 | ||
|     this->point_n(i, &wkb);
 | ||
|     if (!(geometry= Geometry::construct(&buffer, wkb.ptr(), wkb.length()))||
 | ||
|         ((Gis_point *) geometry)->get_xy(&x, &y))
 | ||
|       return 1;
 | ||
| 
 | ||
|     if ((i != 1) && (x != previous_x || y != previous_y))
 | ||
|     {
 | ||
|       *valid= 1;
 | ||
|       return 0;
 | ||
|     }
 | ||
| 
 | ||
|     previous_x = x;
 | ||
|     previous_y = y;
 | ||
|   }
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| int Gis_line_string::simplify(String *result, double max_distance) const {
 | ||
|   Geometry_buffer buffer;
 | ||
|   Geometry *geometry= NULL;
 | ||
|   std::vector<st_point_2d> points;
 | ||
|   double x, y;
 | ||
| 
 | ||
|   uint32 n_points= 0;
 | ||
|   if(this->num_points(&n_points))
 | ||
|     return 1;
 | ||
| 
 | ||
|   for (uint32 i = 1; i <= n_points; i++)
 | ||
|   {
 | ||
|     String wkb= 0;
 | ||
|     if (wkb.reserve(SRID_SIZE + WKB_HEADER_SIZE + POINT_DATA_SIZE))
 | ||
|       return 1;
 | ||
| 
 | ||
|     wkb.q_append(SRID_PLACEHOLDER);
 | ||
|     this->point_n(i, &wkb);
 | ||
|     if (!(geometry= Geometry::construct(&buffer, wkb.ptr(), wkb.length())))
 | ||
|       return 1;
 | ||
|     if(((Gis_point *) geometry)->get_xy(&x, &y))
 | ||
|       return 1;
 | ||
| 
 | ||
|     points.push_back(st_point_2d{x, y});
 | ||
|   }
 | ||
| 
 | ||
|   simplify_RDP(points, max_distance);
 | ||
| 
 | ||
|   result->length(0);
 | ||
|   result->reserve(SRID_SIZE + WKB_HEADER_SIZE +
 | ||
|                   (POINT_DATA_SIZE * points.size()));
 | ||
| 
 | ||
|   result->q_append(SRID_PLACEHOLDER);
 | ||
|   result->q_append((char) wkb_ndr);
 | ||
|   result->q_append((uint32) wkb_linestring);
 | ||
|   result->q_append((uint32) points.size());
 | ||
| 
 | ||
|   for (auto point : points)
 | ||
|   {
 | ||
|     result->q_append((double) point.x);
 | ||
|     result->q_append((double) point.y);
 | ||
|   }
 | ||
| 
 | ||
|   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();
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| /*
 | ||
|   Calculate the internal area using the shoelace formula
 | ||
|   (https://en.wikipedia.org/wiki/Shoelace_formula). If the area is < 0 then
 | ||
|   it is clockwise. If the area is > 0 it is counterclockwise.
 | ||
|   If it is 0 is degenerate.
 | ||
| */
 | ||
| int Gis_line_string::is_clockwise(int *result) const
 | ||
| {
 | ||
|   uint32 num_points;
 | ||
|   double area= 0;
 | ||
| 
 | ||
|   if (this->num_points(&num_points))
 | ||
|     return 1;
 | ||
| 
 | ||
|   for (uint32 i= 1; i <= num_points; i++)
 | ||
|   {
 | ||
|     Geometry_buffer buffer_first, buffer_second;
 | ||
|     Geometry *point_first, *point_second;
 | ||
|     String wkb_first, wkb_second;
 | ||
| 
 | ||
|     if (wkb_first.reserve(SRID_SIZE + WKB_HEADER_SIZE) ||
 | ||
|         wkb_second.reserve(SRID_SIZE + WKB_HEADER_SIZE))
 | ||
|       return 1;
 | ||
| 
 | ||
|     wkb_first.q_append(SRID_PLACEHOLDER);
 | ||
|     wkb_second.q_append(SRID_PLACEHOLDER);
 | ||
| 
 | ||
|     if (this->point_n(i, &wkb_first) ||
 | ||
|         this->point_n((i == num_points) ? 1 : i + 1, &wkb_second))
 | ||
|       return 1;
 | ||
| 
 | ||
|     if (!(point_first=
 | ||
|            Geometry::construct(&buffer_first, wkb_first.ptr(),
 | ||
|                                 wkb_first.length())) ||
 | ||
|         !(point_second=
 | ||
|            Geometry::construct(&buffer_second, wkb_second.ptr(),
 | ||
|                                 wkb_second.length())))
 | ||
|       return 1;
 | ||
| 
 | ||
|     double x1, x2, y1, y2;
 | ||
|     if (((Gis_point *) point_first)->get_xy(&x1, &y1) ||
 | ||
|         ((Gis_point *) point_second)->get_xy(&x2, &y2))
 | ||
|       return 1;
 | ||
| 
 | ||
|     area+= (x1 * y2) - (x2 * y1);
 | ||
|   }
 | ||
| 
 | ||
|   *result= (area < 0);
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| const Geometry::Class_info *Gis_line_string::get_class_info() const
 | ||
| {
 | ||
|   return &linestring_class;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| /***************************** Polygon *******************************/
 | ||
| 
 | ||
| uint32 Gis_polygon::get_data_size() const 
 | ||
| {
 | ||
|   uint32 n_linear_rings;
 | ||
|   uint32 n_points;
 | ||
|   const char *data= m_data;
 | ||
| 
 | ||
|   if (no_data(data, 4))
 | ||
|     return GET_SIZE_ERROR;
 | ||
|   n_linear_rings= uint4korr(data);
 | ||
|   data+= 4;
 | ||
| 
 | ||
|   while (n_linear_rings--)
 | ||
|   {
 | ||
|     if (no_data(data, 4) ||
 | ||
|         not_enough_points(data+4, n_points= uint4korr(data)))
 | ||
|       return GET_SIZE_ERROR;
 | ||
|     data+= 4 + n_points*POINT_DATA_SIZE;
 | ||
|   }
 | ||
|   if (no_data(data, 0))
 | ||
|     return GET_SIZE_ERROR;
 | ||
|   return (uint32) (data - m_data);
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_polygon::init_from_wkt(Gis_read_stream *trs, String *wkb)
 | ||
| {
 | ||
|   uint32 n_linear_rings= 0;
 | ||
|   uint32 lr_pos= wkb->length();
 | ||
|   int closed;
 | ||
| 
 | ||
|   if (wkb->reserve(4, 512))
 | ||
|     return 1;
 | ||
|   wkb->length(wkb->length()+4);	// Reserve space for n_rings
 | ||
|   for (;;)  
 | ||
|   {
 | ||
|     Gis_line_string ls;
 | ||
|     uint32 ls_pos=wkb->length();
 | ||
|     if (trs->check_next_symbol('(') ||
 | ||
| 	ls.init_from_wkt(trs, wkb) ||
 | ||
| 	trs->check_next_symbol(')'))
 | ||
|       return 1;
 | ||
| 
 | ||
|     ls.set_data_ptr(wkb->ptr() + ls_pos, wkb->length() - ls_pos);
 | ||
|     if (ls.is_closed(&closed) || !closed)
 | ||
|     {
 | ||
|       trs->set_error_msg("POLYGON's linear ring isn't closed");
 | ||
|       return 1;
 | ||
|     }
 | ||
|     n_linear_rings++;
 | ||
|     if (trs->skip_char(','))			// Didn't find ','
 | ||
|       break;
 | ||
|   }
 | ||
|   wkb->write_at_position(lr_pos, n_linear_rings);
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| uint Gis_polygon::init_from_opresult(String *bin,
 | ||
|                                      const char *opres, uint res_len)
 | ||
| {
 | ||
|   const char *opres_orig= opres;
 | ||
|   uint32 position= bin->length();
 | ||
|   uint32 poly_shapes= 0;
 | ||
| 
 | ||
|   if (bin->reserve(4, 512))
 | ||
|     return 0;
 | ||
|   bin->q_append(poly_shapes);
 | ||
| 
 | ||
|   while (opres_orig + res_len > opres)
 | ||
|   {
 | ||
|     uint32 n_points, proper_length;
 | ||
|     const char *op_end, *p1_position;
 | ||
|     Gis_point p;
 | ||
|     Gcalc_function::shape_type st;
 | ||
| 
 | ||
|     st= (Gcalc_function::shape_type) uint4korr(opres);
 | ||
|     if (poly_shapes && st != Gcalc_function::shape_hole)
 | ||
|       break;
 | ||
|     poly_shapes++;
 | ||
|     n_points= uint4korr(opres + 4) + 1; /* skip shape type id */
 | ||
|     proper_length= 4 + n_points * POINT_DATA_SIZE;
 | ||
| 
 | ||
|     if (bin->reserve(proper_length, 512))
 | ||
|       return 0;
 | ||
| 
 | ||
|     bin->q_append(n_points);
 | ||
|     op_end= opres + 8 + (n_points-1) * 8 * 2;
 | ||
|     p1_position= (opres+= 8);
 | ||
|     for (; opres<op_end; opres+= POINT_DATA_SIZE)
 | ||
|     {
 | ||
|       if (!p.init_from_wkb(opres, POINT_DATA_SIZE, wkb_ndr, bin))
 | ||
|         return 0;
 | ||
|     }
 | ||
|     if (!p.init_from_wkb(p1_position, POINT_DATA_SIZE, wkb_ndr, bin))
 | ||
|       return 0;
 | ||
|   }
 | ||
| 
 | ||
|   bin->write_at_position(position, poly_shapes);
 | ||
| 
 | ||
|   return (uint) (opres - opres_orig);
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| uint Gis_polygon::init_from_wkb(const char *wkb, uint len, wkbByteOrder bo,
 | ||
|                                 String *res)
 | ||
| {
 | ||
|   uint32 n_linear_rings;
 | ||
|   const char *wkb_orig= wkb;
 | ||
| 
 | ||
|   if (len < 4)
 | ||
|     return 0;
 | ||
| 
 | ||
|   if (!(n_linear_rings= wkb_get_uint(wkb, bo)))
 | ||
|     return 0;
 | ||
| 
 | ||
|   if (res->reserve(4, 512))
 | ||
|     return 0;
 | ||
|   wkb+= 4;
 | ||
|   len-= 4;
 | ||
|   res->q_append(n_linear_rings);
 | ||
| 
 | ||
|   while (n_linear_rings--)
 | ||
|   {
 | ||
|     Gis_line_string ls;
 | ||
|     uint32 ls_pos= res->length();
 | ||
|     int ls_len;
 | ||
|     int closed;
 | ||
| 
 | ||
|     if (!(ls_len= ls.init_from_wkb(wkb, len, bo, res)))
 | ||
|       return 0;
 | ||
| 
 | ||
|     ls.set_data_ptr(res->ptr() + ls_pos, res->length() - ls_pos);
 | ||
| 
 | ||
|     if (ls.is_closed(&closed) || !closed)
 | ||
|       return 0;
 | ||
|     wkb+= ls_len;
 | ||
|   }
 | ||
| 
 | ||
|   return (uint) (wkb - wkb_orig);
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_polygon::init_from_json(json_engine_t *je, bool er_on_3D, String *wkb)
 | ||
| {
 | ||
|   uint32 n_linear_rings= 0;
 | ||
|   uint32 lr_pos= wkb->length();
 | ||
|   int closed;
 | ||
| 
 | ||
|   if (json_read_value(je))
 | ||
|     return TRUE;
 | ||
| 
 | ||
|   if (je->value_type != JSON_VALUE_ARRAY)
 | ||
|   {
 | ||
|     je->s.error= GEOJ_INCORRECT_GEOJSON;
 | ||
|     return TRUE;
 | ||
|   }
 | ||
| 
 | ||
|   if (wkb->reserve(4, 512))
 | ||
|     return TRUE;
 | ||
|   wkb->length(wkb->length()+4);	// Reserve space for n_rings
 | ||
| 
 | ||
|   while (json_scan_next(je) == 0 && je->state != JST_ARRAY_END)
 | ||
|   {
 | ||
|     Gis_line_string ls;
 | ||
|     DBUG_ASSERT(je->state == JST_VALUE);
 | ||
| 
 | ||
|     uint32 ls_pos=wkb->length();
 | ||
|     if (ls.init_from_json(je, er_on_3D, wkb))
 | ||
|       return TRUE;
 | ||
|     ls.set_data_ptr(wkb->ptr() + ls_pos, wkb->length() - ls_pos);
 | ||
|     if (ls.is_closed(&closed) || !closed)
 | ||
|     {
 | ||
|       je->s.error= GEOJ_POLYGON_NOT_CLOSED;
 | ||
|       return TRUE;
 | ||
|     }
 | ||
|     n_linear_rings++;
 | ||
|   }
 | ||
| 
 | ||
|   if (je->s.error)
 | ||
|     return TRUE;
 | ||
| 
 | ||
|   if (n_linear_rings == 0)
 | ||
|   {
 | ||
|     je->s.error= Geometry::GEOJ_EMPTY_COORDINATES;
 | ||
|     return TRUE;
 | ||
|   }
 | ||
|   wkb->write_at_position(lr_pos, n_linear_rings);
 | ||
|   return FALSE;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_polygon::get_data_as_wkt(String *txt, const char **end) const
 | ||
| {
 | ||
|   uint32 n_linear_rings;
 | ||
|   const char *data= m_data;
 | ||
| 
 | ||
|   if (no_data(data, 4))
 | ||
|     return 1;
 | ||
| 
 | ||
|   n_linear_rings= uint4korr(data);
 | ||
|   data+= 4;
 | ||
| 
 | ||
|   while (n_linear_rings--)
 | ||
|   {
 | ||
|     uint32 n_points;
 | ||
|     if (no_data(data, 4))
 | ||
|       return 1;
 | ||
|     n_points= uint4korr(data);
 | ||
|     data+= 4;
 | ||
|     if (not_enough_points(data, n_points) ||
 | ||
| 	txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points))
 | ||
|       return 1;
 | ||
|     txt->qs_append('(');
 | ||
|     data= append_points(txt, n_points, data, 0);
 | ||
|     (*txt) [txt->length() - 1]= ')';		// Replace end ','
 | ||
|     txt->qs_append(',');
 | ||
|   }
 | ||
|   txt->length(txt->length() - 1);		// Remove end ','
 | ||
|   *end= data;
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_polygon::get_data_as_json(String *txt, uint max_dec_digits,
 | ||
|                                    const char **end) const
 | ||
| {
 | ||
|   uint32 n_linear_rings;
 | ||
|   const char *data= m_data;
 | ||
| 
 | ||
|   if (no_data(data, 4) || txt->reserve(1, 512))
 | ||
|     return 1;
 | ||
| 
 | ||
|   n_linear_rings= uint4korr(data);
 | ||
|   data+= 4;
 | ||
| 
 | ||
|   txt->qs_append('[');
 | ||
|   while (n_linear_rings--)
 | ||
|   {
 | ||
|     uint32 n_points;
 | ||
|     if (no_data(data, 4))
 | ||
|       return 1;
 | ||
|     n_points= uint4korr(data);
 | ||
|     data+= 4;
 | ||
|     if (not_enough_points(data, n_points) ||
 | ||
| 	txt->reserve(4 + (MAX_DIGITS_IN_DOUBLE * 2 + 6) * n_points))
 | ||
|       return 1;
 | ||
|     data= append_json_points(txt, max_dec_digits, n_points, data, 0);
 | ||
|     txt->qs_append(", ", 2);
 | ||
|   }
 | ||
|   txt->length(txt->length() - 2);// Remove ending ', '
 | ||
|   txt->qs_append(']');
 | ||
|   *end= data;
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_polygon::get_mbr(MBR *mbr, const char **end) const
 | ||
| {
 | ||
|   uint32 n_linear_rings;
 | ||
|   const char *data= m_data;
 | ||
| 
 | ||
|   if (no_data(data, 4))
 | ||
|     return 1;
 | ||
|   n_linear_rings= uint4korr(data);
 | ||
|   data+= 4;
 | ||
| 
 | ||
|   while (n_linear_rings--)
 | ||
|   {
 | ||
|     if (!(data= get_mbr_for_points(mbr, data, 0)))
 | ||
|       return 1;
 | ||
|   }
 | ||
|   *end= data;
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| bool Gis_polygon::get_points(Geometry::PointContainer &points) const
 | ||
| {
 | ||
|   uint32 n_linear_rings;
 | ||
|   const char *data= m_data;
 | ||
| 
 | ||
|   if (no_data(data, 4))
 | ||
|     return true;
 | ||
|   n_linear_rings= uint4korr(data);
 | ||
|   data+= 4;
 | ||
| 
 | ||
|   while (data && n_linear_rings--)
 | ||
|     data= get_points_common(data, points);
 | ||
|   return !data;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| class Gcalc_poly_transporter : public Gcalc_shape_transporter
 | ||
| {
 | ||
| protected:
 | ||
|   gcalc_shape_info m_si;
 | ||
|   int m_points_in_ring;
 | ||
|   int m_error;
 | ||
| public:
 | ||
|   Gcalc_poly_transporter(Gcalc_heap *heap) :
 | ||
|     Gcalc_shape_transporter(heap), m_si(0), m_error(0) {}
 | ||
| 
 | ||
|   int get_error() const { return m_error; }
 | ||
|   int single_point(double x, double y) override { return 0; }
 | ||
|   int start_line() override { return 0; }
 | ||
|   int complete_line() override { return 0; }
 | ||
| 
 | ||
|   int start_poly() override
 | ||
|   {
 | ||
|     int_start_poly();
 | ||
|     return 0;
 | ||
|   }
 | ||
| 
 | ||
|   int complete_poly() override
 | ||
|   {
 | ||
|     int_complete_poly();
 | ||
|     return 0;
 | ||
|   }
 | ||
|   int start_ring() override
 | ||
|   {
 | ||
|     int_start_ring();
 | ||
|     m_points_in_ring= m_heap->get_n_points();
 | ||
|     return 0;
 | ||
|   }
 | ||
|   int complete_ring() override
 | ||
|   {
 | ||
|     int_complete_ring();
 | ||
|     m_si++;
 | ||
|     if (m_heap->get_n_points() - m_points_in_ring < 3)
 | ||
|       m_error= 1;
 | ||
|     return 0;
 | ||
|   }
 | ||
|   int add_point(double x, double y) override
 | ||
|   {
 | ||
|     return int_add_point(m_si, x, y);
 | ||
|   }
 | ||
| 
 | ||
|   int start_collection(int n_objects) override { return 0; }
 | ||
|   int empty_shape() override { return 0; }
 | ||
| };
 | ||
| 
 | ||
| 
 | ||
| int Gis_polygon::is_valid(int *valid) const
 | ||
| {
 | ||
|   Gcalc_scan_iterator scan_it;
 | ||
|   Gcalc_heap collector;
 | ||
|   Gcalc_poly_transporter trn(&collector);
 | ||
|   MBR mbr;
 | ||
|   uint32 num_rings;
 | ||
|   const char *c_end;
 | ||
|   char *border_count= nullptr, *touches_count= nullptr, *internals= nullptr;
 | ||
|   int result= 0;
 | ||
| 
 | ||
|   *valid= 0;
 | ||
| 
 | ||
|   if (this->num_interior_ring(&num_rings))
 | ||
|     return 1;
 | ||
| 
 | ||
|   num_rings++;
 | ||
| 
 | ||
|   if(this->get_mbr(&mbr, &c_end))
 | ||
|     return 1;
 | ||
| 
 | ||
|   collector.set_extent(mbr.xmin, mbr.xmax, mbr.ymin, mbr.ymax);
 | ||
| 
 | ||
|   if (this->store_shapes(&trn))
 | ||
|     return 1;
 | ||
| 
 | ||
|   if (trn.get_error())
 | ||
|     goto exit;
 | ||
| 
 | ||
|   collector.prepare_operation();
 | ||
|   scan_it.init(&collector);
 | ||
| 
 | ||
|   border_count= (char *) my_alloca(num_rings);
 | ||
|   bzero(border_count, num_rings);
 | ||
|   touches_count= (char *) my_alloca(num_rings);
 | ||
|   internals= (char *) my_alloca(num_rings);
 | ||
| 
 | ||
|   while (scan_it.more_points())
 | ||
|   {
 | ||
|     const Gcalc_scan_iterator::event_point *events;
 | ||
| 
 | ||
|     if (scan_it.step())
 | ||
|     {
 | ||
|       result= 1;
 | ||
|       goto exit;
 | ||
|     }
 | ||
| 
 | ||
|     events= scan_it.get_events();
 | ||
| 
 | ||
|     Gcalc_point_iterator pit(&scan_it);
 | ||
|     int outer_border= 0;
 | ||
| 
 | ||
|     bzero(internals, num_rings);
 | ||
|     /* Walk to the event, marking polygons we met */
 | ||
|     for (; pit.point() != scan_it.get_event_position(); ++pit)
 | ||
|     {
 | ||
|       gcalc_shape_info si= pit.point()->get_shape();
 | ||
|       internals[si]^= 1;
 | ||
|       if (si != 0) /* interior ring */
 | ||
|       {
 | ||
|         if (!internals[0])
 | ||
|         {
 | ||
|           /* Internal ring outside the outer. */
 | ||
|           goto exit;
 | ||
|         }
 | ||
|         for (uint n=1; n<num_rings; n++)
 | ||
|         {
 | ||
|           if (n != si && internals[n]) /* Internal ring inside another internal */
 | ||
|             goto exit;
 | ||
|         }
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     if (events->simple_event())
 | ||
|       continue;
 | ||
| 
 | ||
|     bzero(touches_count, num_rings);
 | ||
| 
 | ||
|     /* Check the status of the event point */
 | ||
|     for (; events; events= events->get_next())
 | ||
|     {
 | ||
|       gcalc_shape_info si= events->get_shape();
 | ||
|       if (events->event == scev_thread ||
 | ||
|           events->event == scev_end || /* should never happen. */
 | ||
|           events->event == scev_single_point ||
 | ||
|           events->event == scev_intersection)
 | ||
|       {
 | ||
|         /* These types of events never happen in valid polygon. */
 | ||
|         goto exit;
 | ||
|       }
 | ||
| 
 | ||
|       touches_count[si]++;
 | ||
|       if (events->event == scev_two_threads || events->event == scev_two_ends)
 | ||
|       {
 | ||
|         if (touches_count[si] > 2)
 | ||
|           goto exit;
 | ||
|       }
 | ||
|       else
 | ||
|       {
 | ||
|         if (touches_count[si] > 1)
 | ||
|           goto exit;
 | ||
|       }
 | ||
| 
 | ||
|       if (si == 0) /* outer ring */
 | ||
|         outer_border= 1;
 | ||
|       else
 | ||
|       {
 | ||
|         if (!outer_border && !internals[0])
 | ||
|         {
 | ||
|           /* Inner ring outside the outer ring. */
 | ||
|           goto exit;
 | ||
|         }
 | ||
|         if (outer_border)
 | ||
|         {
 | ||
|           if (border_count[si]++ > 1)
 | ||
|           {
 | ||
|             /*
 | ||
|               We can't have more than one point of the
 | ||
|               internal ring on the border of the outer ring.
 | ||
|             */
 | ||
|             goto exit;
 | ||
|           }
 | ||
|         }
 | ||
|       }
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   *valid= 1;
 | ||
| 
 | ||
| exit:
 | ||
|   collector.reset();
 | ||
|   scan_it.reset();
 | ||
|   my_afree(border_count);
 | ||
|   my_afree(touches_count);
 | ||
|   my_afree(internals);
 | ||
| 
 | ||
|   return result;
 | ||
| 
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| 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::simplify(String *result, double max_distance) const
 | ||
| {
 | ||
|   uint32 num_interior_ring= 0, num_invalid_ring= 0, num_points;
 | ||
|   Geometry_buffer buffer;
 | ||
|   Geometry *geometry= NULL;
 | ||
|   String exterior_ring= 0;
 | ||
| 
 | ||
|   exterior_ring.q_append(SRID_PLACEHOLDER);
 | ||
|   if (this->num_interior_ring(&num_interior_ring) ||
 | ||
|       this->exterior_ring(&exterior_ring) ||
 | ||
|       !(geometry= Geometry::construct(&buffer, exterior_ring.ptr(),
 | ||
|       exterior_ring.length())))
 | ||
|     return 1;
 | ||
| 
 | ||
|   if (geometry->simplify(&exterior_ring, max_distance))
 | ||
|     return 1;
 | ||
| 
 | ||
|   if (!(geometry= Geometry::construct(&buffer, exterior_ring.ptr(),
 | ||
|                           exterior_ring.length())))
 | ||
|     return 1;
 | ||
| 
 | ||
|   if (geometry->num_points(&num_points) || num_points <= 3)
 | ||
|     return 1;
 | ||
| 
 | ||
|   result->length(0);
 | ||
|   result->reserve(SRID_SIZE + WKB_HEADER_SIZE);
 | ||
|   result->q_append(SRID_PLACEHOLDER);
 | ||
|   result->q_append((char) wkb_ndr);
 | ||
|   result->q_append((uint32) wkb_polygon);
 | ||
|   result->q_append((uint32) 1 + num_interior_ring);
 | ||
|   result->append(exterior_ring.ptr() + SRID_SIZE + WKB_HEADER_SIZE,
 | ||
|                  (exterior_ring.length() - SRID_SIZE -
 | ||
|                   WKB_HEADER_SIZE));
 | ||
| 
 | ||
|   for (uint32 i = 1; i <= num_interior_ring; i++)
 | ||
|   {
 | ||
|     String interior_ring= 0;
 | ||
|     interior_ring.q_append((uint) 0);
 | ||
|     if (this->interior_ring_n(i, &interior_ring) ||
 | ||
|         !(geometry= Geometry::construct(&buffer, interior_ring.ptr(),
 | ||
|           interior_ring.length())))
 | ||
|     {
 | ||
|       num_invalid_ring++;
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     if(geometry->simplify(&interior_ring, max_distance))
 | ||
|       return 1;
 | ||
| 
 | ||
|     if (!(geometry= Geometry::construct(&buffer, interior_ring.ptr(),
 | ||
|                           interior_ring.length())))
 | ||
|     {
 | ||
|       num_invalid_ring++;
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     if (geometry->num_points(&num_points))
 | ||
|       return 1;
 | ||
| 
 | ||
|     if (num_points <= 3)
 | ||
|     {
 | ||
|       num_invalid_ring++;
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     result->append(interior_ring.ptr() + SRID_SIZE + WKB_HEADER_SIZE,
 | ||
|                    (interior_ring.length() - SRID_SIZE - WKB_HEADER_SIZE));
 | ||
|   }
 | ||
| 
 | ||
|   result->write_at_position(SRID_SIZE + WKB_HEADER_SIZE,
 | ||
|                             ((uint32) 1 + num_interior_ring -
 | ||
|                              num_invalid_ring));
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| int Gis_polygon::exterior_ring(String *result) const
 | ||
| {
 | ||
|   uint32 n_points, length;
 | ||
|   const char *data= m_data + 4; // skip n_linerings
 | ||
| 
 | ||
|   if (no_data(data, 4))
 | ||
|     return 1;
 | ||
|   n_points= uint4korr(data);
 | ||
|   data+= 4;
 | ||
|   length= n_points * POINT_DATA_SIZE;
 | ||
|   if (not_enough_points(data, n_points) || result->reserve(1+4+4+ length))
 | ||
|     return 1;
 | ||
| 
 | ||
|   result->q_append((char) wkb_ndr);
 | ||
|   result->q_append((uint32) wkb_linestring);
 | ||
|   result->q_append(n_points);
 | ||
|   result->q_append(data, n_points * POINT_DATA_SIZE);
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| int Gis_polygon::num_interior_ring(uint32 *n_int_rings) const
 | ||
| {
 | ||
|   if (no_data(m_data, 4))
 | ||
|     return 1;
 | ||
|   *n_int_rings= uint4korr(m_data)-1;
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| int Gis_polygon::interior_ring_n(uint32 num, String *result) const
 | ||
| {
 | ||
|   const char *data= m_data;
 | ||
|   uint32 n_linear_rings;
 | ||
|   uint32 n_points;
 | ||
|   uint32 points_size;
 | ||
| 
 | ||
|   if (no_data(data, 4))
 | ||
|     return 1;
 | ||
|   n_linear_rings= uint4korr(data);
 | ||
|   data+= 4;
 | ||
| 
 | ||
|   if (num >= n_linear_rings || num < 1)
 | ||
|     return 1;
 | ||
| 
 | ||
|   while (num--)
 | ||
|   {
 | ||
|     if (no_data(data, 4))
 | ||
|       return 1;
 | ||
|     data+= 4 + uint4korr(data) * POINT_DATA_SIZE;
 | ||
|   }
 | ||
|   if (no_data(data, 4))
 | ||
|     return 1;
 | ||
|   n_points= uint4korr(data);
 | ||
|   points_size= n_points * POINT_DATA_SIZE;
 | ||
|   data+= 4;
 | ||
|   if (not_enough_points(data, n_points) || result->reserve(1+4+4+ points_size))
 | ||
|     return 1;
 | ||
| 
 | ||
|   result->q_append((char) wkb_ndr);
 | ||
|   result->q_append((uint32) wkb_linestring);
 | ||
|   result->q_append(n_points);
 | ||
|   result->q_append(data, points_size); 
 | ||
| 
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| int Gis_polygon::centroid_xy(double *x, double *y) const
 | ||
| {
 | ||
|   uint32 n_linear_rings;
 | ||
|   double UNINIT_VAR(res_area);
 | ||
|   double UNINIT_VAR(res_cx), UNINIT_VAR(res_cy);
 | ||
|   const char *data= m_data;
 | ||
|   bool first_loop= 1;
 | ||
| 
 | ||
|   if (no_data(data, 4) ||
 | ||
|       (n_linear_rings= uint4korr(data)) == 0)
 | ||
|     return 1;
 | ||
|   data+= 4;
 | ||
| 
 | ||
|   while (n_linear_rings--)
 | ||
|   {
 | ||
|     uint32 n_points, org_n_points;
 | ||
|     double prev_x, prev_y;
 | ||
|     double cur_area= 0;
 | ||
|     double cur_cx= 0, cur_cy= 0;
 | ||
|     double sum_cx= 0, sum_cy= 0;
 | ||
| 
 | ||
|     if (no_data(data, 4))
 | ||
|       return 1;
 | ||
|     org_n_points= n_points= uint4korr(data);
 | ||
|     data+= 4;
 | ||
|     if (n_points == 0 || not_enough_points(data, n_points))
 | ||
|       return 1;
 | ||
|     get_point(&prev_x, &prev_y, data);
 | ||
|     data+= POINT_DATA_SIZE;
 | ||
| 
 | ||
|     while (--n_points)				// One point is already read
 | ||
|     {
 | ||
|       double tmp_x, tmp_y;
 | ||
|       double loc_area;
 | ||
|       get_point(&tmp_x, &tmp_y, data);
 | ||
|       data+= POINT_DATA_SIZE;
 | ||
|       loc_area= prev_x * tmp_y - tmp_x * prev_y;
 | ||
|       cur_area+= loc_area;
 | ||
|       cur_cx+= tmp_x;
 | ||
|       cur_cy+= tmp_y;
 | ||
|       sum_cx+= (prev_x + tmp_x) * loc_area;
 | ||
|       sum_cy+= (prev_y + tmp_y) * loc_area;
 | ||
| 
 | ||
|       prev_x= tmp_x;
 | ||
|       prev_y= tmp_y;
 | ||
|     }
 | ||
| 
 | ||
|     if (fabs(cur_area) > 1e-10)
 | ||
|     {
 | ||
|       cur_cx= sum_cx / cur_area / 3.0;
 | ||
|       cur_cy= sum_cy / cur_area / 3.0;
 | ||
|     }
 | ||
|     else
 | ||
|     {
 | ||
|       cur_cx= cur_cx / (org_n_points - 1);
 | ||
|       cur_cy= cur_cy / (org_n_points - 1);
 | ||
|     }
 | ||
| 
 | ||
|     cur_area= fabs(cur_area);
 | ||
| 
 | ||
|     if (!first_loop)
 | ||
|     {
 | ||
|       double d_area= fabs(res_area - cur_area);
 | ||
|       res_cx= (res_area * res_cx - cur_area * cur_cx) / d_area;
 | ||
|       res_cy= (res_area * res_cy - cur_area * cur_cy) / d_area;
 | ||
|     }
 | ||
|     else
 | ||
|     {
 | ||
|       first_loop= 0;
 | ||
|       res_area= cur_area;
 | ||
|       res_cx= cur_cx;
 | ||
|       res_cy= cur_cy;
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   *x= res_cx;
 | ||
|   *y= res_cy;
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| int Gis_polygon::centroid(String *result) const
 | ||
| {
 | ||
|   double x, y;
 | ||
|   if (centroid_xy(&x, &y))
 | ||
|     return 1;
 | ||
|   return create_point(result, x, y);
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| int Gis_polygon::store_shapes(Gcalc_shape_transporter *trn) const
 | ||
| {
 | ||
|   uint32 n_linear_rings;
 | ||
|   const char *data= m_data;
 | ||
|   double first_x, first_y;
 | ||
|   double prev_x, prev_y;
 | ||
|   int was_equal_first= 0;
 | ||
| 
 | ||
|   if (trn->start_poly())
 | ||
|     return 1;
 | ||
| 
 | ||
|   if (no_data(data, 4))
 | ||
|     return 1;
 | ||
|   n_linear_rings= uint4korr(data);
 | ||
|   data+= 4;
 | ||
| 
 | ||
|   while (n_linear_rings--)
 | ||
|   {
 | ||
|     uint32 n_points;
 | ||
| 
 | ||
|     if (no_data(data, 4))
 | ||
|       return 1;
 | ||
|     n_points= uint4korr(data);
 | ||
|     data+= 4;
 | ||
|     if (!n_points || not_enough_points(data, n_points))
 | ||
|       return 1;
 | ||
| 
 | ||
|     trn->start_ring();
 | ||
|     get_point(&first_x, &first_y, data);
 | ||
|     data+= POINT_DATA_SIZE;
 | ||
| 
 | ||
|     prev_x= first_x;
 | ||
|     prev_y= first_y;
 | ||
|     if (trn->add_point(first_x, first_y))
 | ||
|       return 1;
 | ||
| 
 | ||
|     if (--n_points == 0)
 | ||
|       goto single_point_ring;
 | ||
| 
 | ||
|     while (--n_points)
 | ||
|     {
 | ||
|       double x, y;
 | ||
|       get_point(&x, &y, data);
 | ||
|       data+= POINT_DATA_SIZE;
 | ||
|       if (x == prev_x && y == prev_y)
 | ||
|         continue;
 | ||
|       prev_x= x;
 | ||
|       prev_y= y;
 | ||
|       if (was_equal_first)
 | ||
|       {
 | ||
|         if (trn->add_point(first_x, first_y))
 | ||
|           return 1;
 | ||
|         was_equal_first= 0;
 | ||
|       }
 | ||
|       if (x == first_x && y == first_y)
 | ||
|       {
 | ||
|         was_equal_first= 1;
 | ||
|         continue;
 | ||
|       }
 | ||
|       if (trn->add_point(x, y))
 | ||
|         return 1;
 | ||
|     }
 | ||
|     data+= POINT_DATA_SIZE;
 | ||
| 
 | ||
| single_point_ring:
 | ||
|     trn->complete_ring();
 | ||
|   }
 | ||
| 
 | ||
|   trn->complete_poly();
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| int Gis_polygon::make_clockwise(String *result) const
 | ||
| {
 | ||
|   String ring_wkb= 0;
 | ||
|   uint32 num_interior_ring;
 | ||
|   Geometry *ring;
 | ||
|   Geometry_buffer buffer;
 | ||
|   int is_clockwise;
 | ||
|   uint32 ring_points;
 | ||
| 
 | ||
|   if(ring_wkb.reserve(SRID_SIZE + WKB_HEADER_SIZE) ||
 | ||
|      result->reserve(SRID_SIZE + WKB_HEADER_SIZE))
 | ||
|     return 1;
 | ||
| 
 | ||
|   if (this->num_interior_ring(&num_interior_ring) ||
 | ||
|       this->exterior_ring(&ring_wkb))
 | ||
|     return 1;
 | ||
| 
 | ||
|   result->length(0);
 | ||
|   result->append((char) wkb_ndr);
 | ||
|   result->q_append((uint32) wkb_polygon);
 | ||
|   result->q_append((uint32) num_interior_ring + 1);
 | ||
|   result->append(ring_wkb.ptr() + WKB_HEADER_SIZE,
 | ||
|                  ring_wkb.length() - WKB_HEADER_SIZE);
 | ||
| 
 | ||
|   for(uint32 i= 1; i <= num_interior_ring; i++)
 | ||
|   {
 | ||
|     ring_wkb.length(0);
 | ||
|     ring_wkb.q_append(SRID_PLACEHOLDER);
 | ||
|     if (this->interior_ring_n(i, &ring_wkb))
 | ||
|       return 1;
 | ||
| 
 | ||
|     if (!(ring= Geometry::construct(&buffer, ring_wkb.ptr(),
 | ||
|                                     ring_wkb.length())))
 | ||
|       return 1;
 | ||
| 
 | ||
|     if (ring->is_clockwise(&is_clockwise))
 | ||
|       return 1;
 | ||
| 
 | ||
|     if (is_clockwise)
 | ||
|     {
 | ||
|       result->append(ring_wkb.ptr() + WKB_HEADER_SIZE + SRID_SIZE,
 | ||
|                      ring_wkb.length() - (WKB_HEADER_SIZE + SRID_SIZE));
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     if (ring->num_points(&ring_points))
 | ||
|       return 1;
 | ||
|     result->q_append((uint32) ring_points);
 | ||
| 
 | ||
|     for (uint32 i= ring_points; i > 0; i--)
 | ||
|     {
 | ||
|       String point= 0;
 | ||
|       ring->point_n(i, &point);
 | ||
|       result->append(point.ptr() + WKB_HEADER_SIZE,
 | ||
|                      point.length() - WKB_HEADER_SIZE);
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| const Geometry::Class_info *Gis_polygon::get_class_info() const
 | ||
| {
 | ||
|   return &polygon_class;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| /***************************** MultiPoint *******************************/
 | ||
| 
 | ||
| uint32 Gis_multi_point::get_data_size() const 
 | ||
| {
 | ||
|   uint32 n_points;
 | ||
| 
 | ||
|   if (no_data(m_data, 4) ||
 | ||
|       not_enough_points(m_data+4, (n_points= uint4korr(m_data)),
 | ||
|         WKB_HEADER_SIZE))
 | ||
|      return GET_SIZE_ERROR;
 | ||
|   return  4 + n_points * (POINT_DATA_SIZE + WKB_HEADER_SIZE);
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_multi_point::init_from_wkt(Gis_read_stream *trs, String *wkb)
 | ||
| {
 | ||
|   uint32 n_points= 0;
 | ||
|   uint32 np_pos= wkb->length();
 | ||
|   Gis_point p;
 | ||
| 
 | ||
|   if (wkb->reserve(4, 512))
 | ||
|     return 1;
 | ||
|   wkb->length(wkb->length()+4);			// Reserve space for points
 | ||
| 
 | ||
|   for (;;)
 | ||
|   {
 | ||
|     if (wkb->reserve(1 + 4, 512))
 | ||
|       return 1;
 | ||
|     wkb->q_append((char) wkb_ndr);
 | ||
|     wkb->q_append((uint32) wkb_point);
 | ||
|     if (p.init_from_wkt(trs, wkb))
 | ||
|       return 1;
 | ||
|     n_points++;
 | ||
|     if (trs->skip_char(','))			// Didn't find ','
 | ||
|       break;
 | ||
|   }
 | ||
|   wkb->write_at_position(np_pos, n_points);	// Store number of found points
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| uint Gis_multi_point::init_from_opresult(String *bin,
 | ||
|                                          const char *opres, uint res_len)
 | ||
| {
 | ||
|   uint bin_size, n_points;
 | ||
|   Gis_point p;
 | ||
|   const char *opres_end;
 | ||
| 
 | ||
|   n_points= res_len/(4+8*2);
 | ||
|   bin_size= n_points * (WKB_HEADER_SIZE + POINT_DATA_SIZE) + 4;
 | ||
|  
 | ||
|   if (bin->reserve(bin_size, 512))
 | ||
|     return 0;
 | ||
|     
 | ||
|   bin->q_append(n_points);
 | ||
|   opres_end= opres + res_len;
 | ||
|   for (; opres < opres_end; opres+= (4 + 8*2))
 | ||
|   {
 | ||
|     bin->q_append((char)wkb_ndr);
 | ||
|     bin->q_append((uint32)wkb_point);
 | ||
|     if (!p.init_from_wkb(opres + 4, POINT_DATA_SIZE, wkb_ndr, bin))
 | ||
|       return 0;
 | ||
|   }
 | ||
|   return res_len;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| uint Gis_multi_point::init_from_wkb(const char *wkb, uint len, wkbByteOrder bo,
 | ||
|                                     String *res)
 | ||
| {
 | ||
|   uint32 n_points;
 | ||
|   uint proper_size;
 | ||
|   Gis_point p;
 | ||
|   const char *wkb_end;
 | ||
| 
 | ||
|   if (len < 4 ||
 | ||
|       (n_points= wkb_get_uint(wkb, bo)) > max_n_points)
 | ||
|     return 0;
 | ||
|   proper_size= 4 + n_points * (WKB_HEADER_SIZE + POINT_DATA_SIZE);
 | ||
|  
 | ||
|   if (len < proper_size || res->reserve(proper_size))
 | ||
|     return 0;
 | ||
|     
 | ||
|   res->q_append(n_points);
 | ||
|   wkb_end= wkb + proper_size;
 | ||
|   for (wkb+=4; wkb < wkb_end; wkb+= (WKB_HEADER_SIZE + POINT_DATA_SIZE))
 | ||
|   {
 | ||
|     res->q_append((char)wkb_ndr);
 | ||
|     res->q_append((uint32)wkb_point);
 | ||
|     if (!p.init_from_wkb(wkb + WKB_HEADER_SIZE,
 | ||
|                          POINT_DATA_SIZE, (wkbByteOrder) wkb[0], res))
 | ||
|       return 0;
 | ||
|   }
 | ||
|   return proper_size;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_multi_point::init_from_json(json_engine_t *je, bool er_on_3D,
 | ||
|                                      String *wkb)
 | ||
| {
 | ||
|   uint32 n_points= 0;
 | ||
|   uint32 np_pos= wkb->length();
 | ||
|   Gis_point p;
 | ||
| 
 | ||
|   if (json_read_value(je))
 | ||
|     return TRUE;
 | ||
| 
 | ||
|   if (je->value_type != JSON_VALUE_ARRAY)
 | ||
|   {
 | ||
|     je->s.error= GEOJ_INCORRECT_GEOJSON;
 | ||
|     return TRUE;
 | ||
|   }
 | ||
| 
 | ||
|   if (wkb->reserve(4, 512))
 | ||
|     return TRUE;
 | ||
|   wkb->length(wkb->length()+4);	// Reserve space for n_points  
 | ||
| 
 | ||
|   while (json_scan_next(je) == 0 && je->state != JST_ARRAY_END)
 | ||
|   {
 | ||
|     DBUG_ASSERT(je->state == JST_VALUE);
 | ||
| 
 | ||
|     if (wkb->reserve(1 + 4, 512))
 | ||
|       return TRUE;
 | ||
|     wkb->q_append((char) wkb_ndr);
 | ||
|     wkb->q_append((uint32) wkb_point);
 | ||
| 
 | ||
|     if (p.init_from_json(je, er_on_3D, wkb))
 | ||
|       return TRUE;
 | ||
|     n_points++;
 | ||
|   }
 | ||
| 
 | ||
|   if (je->s.error)
 | ||
|     return TRUE;
 | ||
| 
 | ||
|   if (n_points == 0)
 | ||
|   {
 | ||
|     je->s.error= Geometry::GEOJ_EMPTY_COORDINATES;
 | ||
|     return TRUE;
 | ||
|   }
 | ||
| 
 | ||
|   wkb->write_at_position(np_pos, n_points);
 | ||
|   return FALSE;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_multi_point::get_data_as_wkt(String *txt, const char **end) const
 | ||
| {
 | ||
|   uint32 n_points;
 | ||
|   if (no_data(m_data, 4))
 | ||
|     return 1;
 | ||
| 
 | ||
|   n_points= uint4korr(m_data);
 | ||
|   if (n_points > max_n_points ||
 | ||
|       not_enough_points(m_data+4, n_points, WKB_HEADER_SIZE) ||
 | ||
|       txt->reserve(((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points))
 | ||
|     return 1;
 | ||
|   *end= append_points(txt, n_points, m_data+4, WKB_HEADER_SIZE);
 | ||
|   txt->length(txt->length()-1);			// Remove end ','
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_multi_point::get_data_as_json(String *txt, uint max_dec_digits,
 | ||
|                                        const char **end) const
 | ||
| {
 | ||
|   uint32 n_points;
 | ||
|   if (no_data(m_data, 4))
 | ||
|     return 1;
 | ||
| 
 | ||
|   n_points= uint4korr(m_data);
 | ||
|   if (n_points > max_n_points ||
 | ||
|       not_enough_points(m_data+4, n_points, WKB_HEADER_SIZE) ||
 | ||
|       txt->reserve((MAX_DIGITS_IN_DOUBLE * 2 + 6) * n_points + 2))
 | ||
|     return 1;
 | ||
|   *end= append_json_points(txt, max_dec_digits, n_points, m_data+4,
 | ||
|                            WKB_HEADER_SIZE);
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| int Gis_multi_point::is_valid(int *valid) const
 | ||
| {
 | ||
|   uint32 num_points;
 | ||
|   if (no_data(m_data, 4))
 | ||
|     return 1;
 | ||
| 
 | ||
|   num_points= uint4korr(m_data);
 | ||
|   if (not_enough_points(m_data, num_points))
 | ||
|     return 1;
 | ||
| 
 | ||
|   *valid= 1;
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_multi_point::get_mbr(MBR *mbr, const char **end) const
 | ||
| {
 | ||
|   return (*end= get_mbr_for_points(mbr, m_data, WKB_HEADER_SIZE)) == 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| int Gis_multi_point::num_geometries(uint32 *num) const
 | ||
| {
 | ||
|   *num= uint4korr(m_data);
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| int Gis_multi_point::geometry_n(uint32 num, String *result) const
 | ||
| {
 | ||
|   const char *data= m_data;
 | ||
|   uint32 n_points;
 | ||
| 
 | ||
|   if (no_data(data, 4))
 | ||
|     return 1;
 | ||
|   n_points= uint4korr(data);
 | ||
|   data+= 4+ (num - 1) * (WKB_HEADER_SIZE + POINT_DATA_SIZE);
 | ||
| 
 | ||
|   if (num > n_points || num < 1 ||
 | ||
|       no_data(data, WKB_HEADER_SIZE + POINT_DATA_SIZE) ||
 | ||
|       result->reserve(WKB_HEADER_SIZE + POINT_DATA_SIZE))
 | ||
|     return 1;
 | ||
| 
 | ||
|   result->q_append(data, WKB_HEADER_SIZE + POINT_DATA_SIZE);
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| int Gis_multi_point::store_shapes(Gcalc_shape_transporter *trn) const
 | ||
| {
 | ||
|   uint32 n_points;
 | ||
|   Gis_point pt;
 | ||
|   const char *data= m_data;
 | ||
| 
 | ||
|   if (no_data(data, 4))
 | ||
|     return 1;
 | ||
|   n_points= uint4korr(data);
 | ||
|   data+= 4;
 | ||
| 
 | ||
|   if (trn->start_collection(n_points))
 | ||
|     return 1;
 | ||
| 
 | ||
|   while (n_points--)
 | ||
|   {
 | ||
|     if (no_data(data, WKB_HEADER_SIZE))
 | ||
|       return 1;
 | ||
|     data+= WKB_HEADER_SIZE;
 | ||
|     pt.set_data_ptr(data, (uint32) (m_data_end - data));
 | ||
|     if (pt.store_shapes(trn))
 | ||
|       return 1;
 | ||
|     data+= pt.get_data_size();
 | ||
|   }
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| const Geometry::Class_info *Gis_multi_point::get_class_info() const
 | ||
| {
 | ||
|   return &multipoint_class;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| /**
 | ||
|   Function that calculate spherical distance of Multipoints geometries.
 | ||
|   In case there is single point in Multipoint geometries calculate_haversine()
 | ||
|   can handle such case. Otherwise, new geometry (Point) has to be constructed.
 | ||
| 
 | ||
|   @param    g pointer to the Geometry
 | ||
|   @param    r sphere radius
 | ||
|   @param    result pointer to the result
 | ||
|   @param    err    pointer to the error obtained from calculate_haversin()
 | ||
| 
 | ||
|   @return state
 | ||
|   @retval TRUE  failed
 | ||
|   @retval FALSE success
 | ||
| */
 | ||
| int Gis_multi_point::spherical_distance_multipoints(Geometry *g, const double r,
 | ||
|                                                     double *result, int *err)
 | ||
| {
 | ||
|   const uint32 len= 4 + WKB_HEADER_SIZE + POINT_DATA_SIZE + 1;
 | ||
|   // Check how many points are stored in Multipoints
 | ||
|   uint32 num_of_points1, num_of_points2;
 | ||
|   // To find the minimum radius it cannot be greater than Earth radius
 | ||
|   double res= 6370986.0;
 | ||
| 
 | ||
|   /* From Item_func_sphere_distance::spherical_distance_points,
 | ||
|      we are sure that there will be multiple points and we have to construct
 | ||
|      Point geometry and return the smallest result.
 | ||
|   */
 | ||
|   num_geometries(&num_of_points1);
 | ||
|   DBUG_ASSERT(num_of_points1 >= 1);
 | ||
|   g->num_geometries(&num_of_points2);
 | ||
|   DBUG_ASSERT(num_of_points2 >= 1);
 | ||
| 
 | ||
|   for (uint32 i=1; i <= num_of_points1; i++)
 | ||
|   {
 | ||
|     Geometry_buffer buff_temp;
 | ||
|     Geometry *temp;
 | ||
|     double temp_res= 0.0;
 | ||
|     char s[len];
 | ||
|     const char *pt_ptr= get_data_ptr()+
 | ||
|       4+WKB_HEADER_SIZE*i + POINT_DATA_SIZE*(i-1);
 | ||
|     // First 4 bytes are handled already, make sure to create a Point
 | ||
|     memset(s + 4, Geometry::wkb_point, 1);
 | ||
|     if (no_data(pt_ptr, POINT_DATA_SIZE))
 | ||
|       return 1;
 | ||
|     memcpy(s + 5, this->get_data_ptr() + 5, 4);
 | ||
|     memcpy(s + 4 + WKB_HEADER_SIZE, pt_ptr, POINT_DATA_SIZE);
 | ||
|     s[len-1]= '\0';
 | ||
|     temp= Geometry::construct(&buff_temp, s, len);
 | ||
|     if (!temp)
 | ||
|       return 1;
 | ||
|     // Optimization for single Multipoint
 | ||
|     if (num_of_points2 == 1)
 | ||
|     {
 | ||
|       *result= static_cast<Gis_point *>(temp)->calculate_haversine(g, r, err);
 | ||
|       return 0;
 | ||
|     }
 | ||
|     for (uint32 j=1; j<= num_of_points2; j++)
 | ||
|     {
 | ||
|       Geometry_buffer buff_temp2;
 | ||
|       Geometry *temp2;
 | ||
|       char s2[len];
 | ||
|       const char *pt_ptr= g->get_data_ptr()+
 | ||
|         4+WKB_HEADER_SIZE*j + POINT_DATA_SIZE*(j-1);
 | ||
|       // First 4 bytes are handled already, make sure to create a Point
 | ||
|       memset(s2 + 4, Geometry::wkb_point, 1);
 | ||
|       if (g->no_data(pt_ptr, POINT_DATA_SIZE))
 | ||
|         return 1;
 | ||
|       memcpy(s2 + 5, g->get_data_ptr() + 5, 4);
 | ||
|       memcpy(s2 + 4 + WKB_HEADER_SIZE, pt_ptr, POINT_DATA_SIZE);
 | ||
|       s2[len-1]= '\0';
 | ||
|       temp2= Geometry::construct(&buff_temp2, s2, len);
 | ||
|       if (!temp2)
 | ||
|         return 1;
 | ||
|       temp_res= static_cast<Gis_point *>(temp)->calculate_haversine(temp2, r, err);
 | ||
|       if (res > temp_res)
 | ||
|         res= temp_res;
 | ||
|     }
 | ||
|   }
 | ||
|   *result= res;
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| /***************************** MultiLineString *******************************/
 | ||
| 
 | ||
| uint32 Gis_multi_line_string::get_data_size() const 
 | ||
| {
 | ||
|   uint32 n_line_strings;
 | ||
|   uint32 n_points;
 | ||
|   const char *data= m_data;
 | ||
| 
 | ||
|   if (no_data(data, 4))
 | ||
|     return GET_SIZE_ERROR;
 | ||
|   n_line_strings= uint4korr(data);
 | ||
|   data+= 4;
 | ||
| 
 | ||
|   while (n_line_strings--)
 | ||
|   {
 | ||
|     if (no_data(data, WKB_HEADER_SIZE + 4) ||
 | ||
|         not_enough_points(data + WKB_HEADER_SIZE+4,
 | ||
|                           (n_points= uint4korr(data + WKB_HEADER_SIZE))))
 | ||
|       return GET_SIZE_ERROR;
 | ||
|     data+= (WKB_HEADER_SIZE + 4 + n_points*POINT_DATA_SIZE);
 | ||
|   }
 | ||
|   if (no_data(data, 0))
 | ||
|     return GET_SIZE_ERROR;
 | ||
|   return (uint32) (data - m_data);
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_multi_line_string::init_from_wkt(Gis_read_stream *trs, String *wkb)
 | ||
| {
 | ||
|   uint32 n_line_strings= 0;
 | ||
|   uint32 ls_pos= wkb->length();
 | ||
| 
 | ||
|   if (wkb->reserve(4, 512))
 | ||
|     return 1;
 | ||
|   wkb->length(wkb->length()+4);			// Reserve space for points
 | ||
| 
 | ||
|   for (;;)
 | ||
|   {
 | ||
|     Gis_line_string ls;
 | ||
| 
 | ||
|     if (wkb->reserve(1 + 4, 512))
 | ||
|       return 1;
 | ||
|     wkb->q_append((char) wkb_ndr);
 | ||
|     wkb->q_append((uint32) wkb_linestring);
 | ||
| 
 | ||
|     if (trs->check_next_symbol('(') ||
 | ||
| 	      ls.init_from_wkt(trs, wkb) ||
 | ||
| 	      trs->check_next_symbol(')'))
 | ||
|       return 1;
 | ||
|     n_line_strings++;
 | ||
|     if (trs->skip_char(','))			// Didn't find ','
 | ||
|       break;
 | ||
|   }
 | ||
|   wkb->write_at_position(ls_pos, n_line_strings);
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| uint Gis_multi_line_string::init_from_opresult(String *bin,
 | ||
|                                                const char *opres, uint res_len)
 | ||
| {
 | ||
|   const char *opres_orig= opres;
 | ||
|   int ns_pos= bin->length();
 | ||
|   uint n_linestring= 0;
 | ||
| 
 | ||
|   if (bin->reserve(4, 512))
 | ||
|     return 0;
 | ||
|   bin->q_append(n_linestring);
 | ||
|   
 | ||
|   while (res_len)
 | ||
|   {
 | ||
|     Gis_line_string ls;
 | ||
|     int ls_len;
 | ||
| 
 | ||
|     if (bin->reserve(WKB_HEADER_SIZE, 512))
 | ||
|       return 0;
 | ||
| 
 | ||
|     bin->q_append((char) wkb_ndr);
 | ||
|     bin->q_append((uint32) wkb_linestring);
 | ||
| 
 | ||
|     if (!(ls_len= ls.init_from_opresult(bin, opres, res_len)))
 | ||
|       return 0;
 | ||
|     opres+= ls_len;
 | ||
|     res_len-= ls_len;
 | ||
|     n_linestring++;
 | ||
|   }
 | ||
|   bin->write_at_position(ns_pos, n_linestring);
 | ||
|   return (uint) (opres - opres_orig);
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| uint Gis_multi_line_string::init_from_wkb(const char *wkb, uint len,
 | ||
|                                           wkbByteOrder bo, String *res)
 | ||
| {
 | ||
|   uint32 n_line_strings;
 | ||
|   const char *wkb_orig= wkb;
 | ||
| 
 | ||
|   if (len < 4 ||
 | ||
|       (n_line_strings= wkb_get_uint(wkb, bo))< 1)
 | ||
|     return 0;
 | ||
| 
 | ||
|   if (res->reserve(4, 512))
 | ||
|     return 0;
 | ||
|   res->q_append(n_line_strings);
 | ||
|   
 | ||
|   wkb+= 4;
 | ||
|   while (n_line_strings--)
 | ||
|   {
 | ||
|     Gis_line_string ls;
 | ||
|     int ls_len;
 | ||
| 
 | ||
|     if ((len < WKB_HEADER_SIZE) ||
 | ||
|         res->reserve(WKB_HEADER_SIZE, 512))
 | ||
|       return 0;
 | ||
| 
 | ||
|     res->q_append((char) wkb_ndr);
 | ||
|     res->q_append((uint32) wkb_linestring);
 | ||
| 
 | ||
|     if (!(ls_len= ls.init_from_wkb(wkb + WKB_HEADER_SIZE, len,
 | ||
|                                    (wkbByteOrder) wkb[0], res)))
 | ||
|       return 0;
 | ||
|     ls_len+= WKB_HEADER_SIZE;
 | ||
|     wkb+= ls_len;
 | ||
|     len-= ls_len;
 | ||
|   }
 | ||
|   return (uint) (wkb - wkb_orig);
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_multi_line_string::init_from_json(json_engine_t *je, bool er_on_3D,
 | ||
|                                            String *wkb)
 | ||
| {
 | ||
|   uint32 n_line_strings= 0;
 | ||
|   uint32 ls_pos= wkb->length();
 | ||
| 
 | ||
|   if (json_read_value(je))
 | ||
|     return TRUE;
 | ||
| 
 | ||
|   if (je->value_type != JSON_VALUE_ARRAY)
 | ||
|   {
 | ||
|     je->s.error= GEOJ_INCORRECT_GEOJSON;
 | ||
|     return TRUE;
 | ||
|   }
 | ||
| 
 | ||
|   if (wkb->reserve(4, 512))
 | ||
|     return TRUE;
 | ||
|   wkb->length(wkb->length()+4);	// Reserve space for n_rings
 | ||
| 
 | ||
|   while (json_scan_next(je) == 0 && je->state != JST_ARRAY_END)
 | ||
|   {
 | ||
|     Gis_line_string ls;
 | ||
|     DBUG_ASSERT(je->state == JST_VALUE);
 | ||
| 
 | ||
|     if (wkb->reserve(1 + 4, 512))
 | ||
|       return TRUE;
 | ||
|     wkb->q_append((char) wkb_ndr);
 | ||
|     wkb->q_append((uint32) wkb_linestring);
 | ||
| 
 | ||
|     if (ls.init_from_json(je, er_on_3D, wkb))
 | ||
|       return TRUE;
 | ||
| 
 | ||
|     n_line_strings++;
 | ||
|   }
 | ||
| 
 | ||
|   if (je->s.error)
 | ||
|     return TRUE;
 | ||
| 
 | ||
|   if (n_line_strings == 0)
 | ||
|   {
 | ||
|     je->s.error= Geometry::GEOJ_EMPTY_COORDINATES;
 | ||
|     return TRUE;
 | ||
|   }
 | ||
| 
 | ||
|   wkb->write_at_position(ls_pos, n_line_strings);
 | ||
|   return FALSE;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_multi_line_string::get_data_as_wkt(String *txt, 
 | ||
| 					     const char **end) const
 | ||
| {
 | ||
|   uint32 n_line_strings;
 | ||
|   const char *data= m_data;
 | ||
| 
 | ||
|   if (no_data(data, 4))
 | ||
|     return 1;
 | ||
|   n_line_strings= uint4korr(data);
 | ||
|   data+= 4;
 | ||
| 
 | ||
|   while (n_line_strings--)
 | ||
|   {
 | ||
|     uint32 n_points;
 | ||
|     if (no_data(data, (WKB_HEADER_SIZE + 4)))
 | ||
|       return 1;
 | ||
|     n_points= uint4korr(data + WKB_HEADER_SIZE);
 | ||
|     data+= WKB_HEADER_SIZE + 4;
 | ||
|     if (not_enough_points(data, n_points) ||
 | ||
| 	txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points))
 | ||
|       return 1;
 | ||
|     txt->qs_append('(');
 | ||
|     data= append_points(txt, n_points, data, 0);
 | ||
|     (*txt) [txt->length() - 1]= ')';
 | ||
|     txt->qs_append(',');
 | ||
|   }
 | ||
|   txt->length(txt->length() - 1);
 | ||
|   *end= data;
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_multi_line_string::get_data_as_json(String *txt, uint max_dec_digits,
 | ||
|                                              const char **end) const
 | ||
| {
 | ||
|   uint32 n_line_strings;
 | ||
|   const char *data= m_data;
 | ||
| 
 | ||
|   if (no_data(data, 4) || txt->reserve(1, 512))
 | ||
|     return 1;
 | ||
|   n_line_strings= uint4korr(data);
 | ||
|   data+= 4;
 | ||
| 
 | ||
|   txt->qs_append('[');
 | ||
|   while (n_line_strings--)
 | ||
|   {
 | ||
|     uint32 n_points;
 | ||
|     if (no_data(data, (WKB_HEADER_SIZE + 4)))
 | ||
|       return 1;
 | ||
|     n_points= uint4korr(data + WKB_HEADER_SIZE);
 | ||
|     data+= WKB_HEADER_SIZE + 4;
 | ||
|     if (not_enough_points(data, n_points) ||
 | ||
| 	txt->reserve(2 + (MAX_DIGITS_IN_DOUBLE * 2 + 6) * n_points))
 | ||
|       return 1;
 | ||
|     data= append_json_points(txt, max_dec_digits, n_points, data, 0);
 | ||
|     txt->qs_append(", ", 2);
 | ||
|   }
 | ||
|   txt->length(txt->length() - 2);
 | ||
|   txt->qs_append(']');
 | ||
|   *end= data;
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| int Gis_multi_line_string::is_valid(int *valid) const
 | ||
| {
 | ||
|   uint32 num_linestring;
 | ||
|   Geometry_buffer buffer;
 | ||
|   Geometry *geometry= NULL;
 | ||
|   *valid= 0;
 | ||
| 
 | ||
|   if (no_data(m_data, 4))
 | ||
|     return 1;
 | ||
|   num_linestring= uint4korr(m_data);
 | ||
| 
 | ||
|   for (uint32 i = 1; i <= num_linestring; i++)
 | ||
|   {
 | ||
|     String wkb = 0;
 | ||
| 
 | ||
|     wkb.q_append(SRID_PLACEHOLDER);
 | ||
|     if (this->geometry_n(i, &wkb) ||
 | ||
|         !(geometry= Geometry::construct(&buffer, wkb.ptr(), wkb.length())))
 | ||
|       return 1;
 | ||
| 
 | ||
|     int line_valid;
 | ||
|     if(geometry->is_valid(&line_valid))
 | ||
|       return 1;
 | ||
| 
 | ||
|     if (!line_valid)
 | ||
|       return 0;
 | ||
|   }
 | ||
| 
 | ||
|   *valid= 1;
 | ||
|   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::simplify(String *result, double max_distance) const
 | ||
| {
 | ||
|   uint32 num_lines= 0;
 | ||
|   Geometry_buffer buffer;
 | ||
|   Geometry *geometry= NULL;
 | ||
| 
 | ||
|   if (this->num_geometries(&num_lines))
 | ||
|     return 1;
 | ||
| 
 | ||
|   result->length(0);
 | ||
|   result->reserve(SRID_SIZE + WKB_HEADER_SIZE);
 | ||
|   result->q_append(SRID_PLACEHOLDER);
 | ||
|   result->q_append((char) wkb_ndr);
 | ||
|   result->q_append((uint32) wkb_multilinestring);
 | ||
|   result->q_append((uint32) num_lines);
 | ||
| 
 | ||
|   for (uint32 i = 1; i <= num_lines; i++)
 | ||
|   {
 | ||
|     String wkb= 0;
 | ||
|     wkb.q_append((uint) 0);
 | ||
|     this->geometry_n(i, &wkb);
 | ||
|     if (!(geometry= Geometry::construct(&buffer, wkb.ptr(), wkb.length())))
 | ||
|       return 1;
 | ||
|     geometry->simplify(&wkb, max_distance);
 | ||
|     result->append(wkb.ptr() + SRID_SIZE, wkb.length() - SRID_SIZE);
 | ||
|   }
 | ||
| 
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| int Gis_multi_line_string::store_shapes(Gcalc_shape_transporter *trn) const
 | ||
| {
 | ||
|   uint32 n_lines;
 | ||
|   Gis_line_string ls;
 | ||
|   const char *data= m_data;
 | ||
| 
 | ||
|   if (no_data(data, 4))
 | ||
|     return 1;
 | ||
|   n_lines= uint4korr(data);
 | ||
|   data+= 4;
 | ||
| 
 | ||
|   if (trn->start_collection(n_lines))
 | ||
|     return 1;
 | ||
| 
 | ||
|   while (n_lines--)
 | ||
|   {
 | ||
|     if (no_data(data, WKB_HEADER_SIZE))
 | ||
|       return 1;
 | ||
|     data+= WKB_HEADER_SIZE;
 | ||
|     ls.set_data_ptr(data, (uint32) (m_data_end - data));
 | ||
|     if (ls.store_shapes(trn))
 | ||
|       return 1;
 | ||
|     data+= ls.get_data_size();
 | ||
|   }
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| const Geometry::Class_info *Gis_multi_line_string::get_class_info() const
 | ||
| {
 | ||
|   return &multilinestring_class;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| /***************************** MultiPolygon *******************************/
 | ||
| 
 | ||
| uint32 Gis_multi_polygon::get_data_size() const 
 | ||
| {
 | ||
|   uint32 n_polygons;
 | ||
|   uint32 n_points;
 | ||
|   const char *data= m_data;
 | ||
| 
 | ||
|   if (no_data(data, 4))
 | ||
|     return GET_SIZE_ERROR;
 | ||
|   n_polygons= uint4korr(data);
 | ||
|   data+= 4;
 | ||
| 
 | ||
|   while (n_polygons--)
 | ||
|   {
 | ||
|     uint32 n_linear_rings;
 | ||
|     if (no_data(data, 4 + WKB_HEADER_SIZE))
 | ||
|       return GET_SIZE_ERROR;
 | ||
| 
 | ||
|     n_linear_rings= uint4korr(data + WKB_HEADER_SIZE);
 | ||
|     data+= 4 + WKB_HEADER_SIZE;
 | ||
| 
 | ||
|     while (n_linear_rings--)
 | ||
|     {
 | ||
|       if (no_data(data, 4) ||
 | ||
|           not_enough_points(data+4, (n_points= uint4korr(data))))
 | ||
| 	return GET_SIZE_ERROR;
 | ||
|       data+= 4 + n_points * POINT_DATA_SIZE;
 | ||
|     }
 | ||
|   }
 | ||
|   if (no_data(data, 0))
 | ||
|     return GET_SIZE_ERROR;
 | ||
|   return (uint32) (data - m_data);
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_multi_polygon::init_from_wkt(Gis_read_stream *trs, String *wkb)
 | ||
| {
 | ||
|   uint32 n_polygons= 0;
 | ||
|   int np_pos= wkb->length();
 | ||
|   Gis_polygon p;
 | ||
| 
 | ||
|   if (wkb->reserve(4, 512))
 | ||
|     return 1;
 | ||
|   wkb->length(wkb->length()+4);			// Reserve space for points
 | ||
| 
 | ||
|   for (;;)  
 | ||
|   {
 | ||
|     if (wkb->reserve(1 + 4, 512))
 | ||
|       return 1;
 | ||
|     wkb->q_append((char) wkb_ndr);
 | ||
|     wkb->q_append((uint32) wkb_polygon);
 | ||
| 
 | ||
|     if (trs->check_next_symbol('(') ||
 | ||
| 	p.init_from_wkt(trs, wkb) ||
 | ||
| 	trs->check_next_symbol(')'))
 | ||
|       return 1;
 | ||
|     n_polygons++;
 | ||
|     if (trs->skip_char(','))			// Didn't find ','
 | ||
|       break;
 | ||
|   }
 | ||
|   wkb->write_at_position(np_pos, n_polygons);
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| uint Gis_multi_polygon::init_from_wkb(const char *wkb, uint len,
 | ||
|                                       wkbByteOrder bo, String *res)
 | ||
| {
 | ||
|   uint32 n_poly;
 | ||
|   const char *wkb_orig= wkb;
 | ||
| 
 | ||
|   if (len < 4)
 | ||
|     return 0;
 | ||
|   n_poly= wkb_get_uint(wkb, bo);
 | ||
| 
 | ||
|   if (res->reserve(4, 512))
 | ||
|     return 0;
 | ||
|   res->q_append(n_poly);
 | ||
|   
 | ||
|   wkb+=4;
 | ||
|   while (n_poly--)
 | ||
|   {
 | ||
|     Gis_polygon p;
 | ||
|     int p_len;
 | ||
| 
 | ||
|     if (len < WKB_HEADER_SIZE ||
 | ||
|         res->reserve(WKB_HEADER_SIZE, 512))
 | ||
|       return 0;
 | ||
|     res->q_append((char) wkb_ndr);
 | ||
|     res->q_append((uint32) wkb_polygon);
 | ||
| 
 | ||
|     if (!(p_len= p.init_from_wkb(wkb + WKB_HEADER_SIZE, len,
 | ||
|                                  (wkbByteOrder) wkb[0], res)))
 | ||
|       return 0;
 | ||
|     p_len+= WKB_HEADER_SIZE;
 | ||
|     wkb+= p_len;
 | ||
|     len-= p_len;
 | ||
|   }
 | ||
|   return (uint) (wkb - wkb_orig);
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| uint Gis_multi_polygon::init_from_opresult(String *bin,
 | ||
|                                            const char *opres, uint res_len)
 | ||
| {
 | ||
|   Gis_polygon p;
 | ||
|   const char *opres_orig= opres;
 | ||
|   uint p_len;
 | ||
|   uint32 n_poly= 0;
 | ||
|   uint32 np_pos= bin->length();
 | ||
| 
 | ||
|   if (bin->reserve(4, 512))
 | ||
|     return 0;
 | ||
|     
 | ||
|   bin->q_append(n_poly);
 | ||
|   while (res_len)
 | ||
|   {
 | ||
|     if (bin->reserve(1 + 4, 512))
 | ||
|       return 0;
 | ||
|     bin->q_append((char)wkb_ndr);
 | ||
|     bin->q_append((uint32)wkb_polygon);
 | ||
|     if (!(p_len= p.init_from_opresult(bin, opres, res_len)))
 | ||
|       return 0;
 | ||
|     opres+= p_len;
 | ||
|     res_len-= p_len;
 | ||
|     n_poly++;
 | ||
|   }
 | ||
|   bin->write_at_position(np_pos, n_poly);
 | ||
|   return (uint)(opres - opres_orig);
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_multi_polygon::init_from_json(json_engine_t *je, bool er_on_3D,
 | ||
|                                        String *wkb)
 | ||
| {
 | ||
|   uint32 n_polygons= 0;
 | ||
|   int np_pos= wkb->length();
 | ||
|   Gis_polygon p;
 | ||
| 
 | ||
|   if (json_read_value(je))
 | ||
|     return TRUE;
 | ||
| 
 | ||
|   if (je->value_type != JSON_VALUE_ARRAY)
 | ||
|   {
 | ||
|     je->s.error= GEOJ_INCORRECT_GEOJSON;
 | ||
|     return TRUE;
 | ||
|   }
 | ||
| 
 | ||
|   if (wkb->reserve(4, 512))
 | ||
|     return TRUE;
 | ||
|   wkb->length(wkb->length()+4);	// Reserve space for n_rings
 | ||
| 
 | ||
|   while (json_scan_next(je) == 0 && je->state != JST_ARRAY_END)
 | ||
|   {
 | ||
|     DBUG_ASSERT(je->state == JST_VALUE);
 | ||
| 
 | ||
|     if (wkb->reserve(1 + 4, 512))
 | ||
|       return TRUE;
 | ||
|     wkb->q_append((char) wkb_ndr);
 | ||
|     wkb->q_append((uint32) wkb_polygon);
 | ||
| 
 | ||
|     if (p.init_from_json(je, er_on_3D, wkb))
 | ||
|       return TRUE;
 | ||
| 
 | ||
|     n_polygons++;
 | ||
|   }
 | ||
| 
 | ||
|   if (je->s.error)
 | ||
|     return TRUE;
 | ||
| 
 | ||
|   if (n_polygons == 0)
 | ||
|   {
 | ||
|     je->s.error= Geometry::GEOJ_EMPTY_COORDINATES;
 | ||
|     return TRUE;
 | ||
|   }
 | ||
|   wkb->write_at_position(np_pos, n_polygons);
 | ||
|   return FALSE;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_multi_polygon::get_data_as_wkt(String *txt, const char **end) const
 | ||
| {
 | ||
|   uint32 n_polygons;
 | ||
|   const char *data= m_data;
 | ||
| 
 | ||
|   if (no_data(data, 4))
 | ||
|     return 1;
 | ||
|   n_polygons= uint4korr(data);
 | ||
|   data+= 4;
 | ||
| 
 | ||
|   while (n_polygons--)
 | ||
|   {
 | ||
|     uint32 n_linear_rings;
 | ||
|     if (no_data(data, 4 + WKB_HEADER_SIZE) ||
 | ||
| 	txt->reserve(1, 512))
 | ||
|       return 1;
 | ||
|     n_linear_rings= uint4korr(data+WKB_HEADER_SIZE);
 | ||
|     data+= 4 + WKB_HEADER_SIZE;
 | ||
|     txt->q_append('(');
 | ||
| 
 | ||
|     while (n_linear_rings--)
 | ||
|     {
 | ||
|       if (no_data(data, 4))
 | ||
|         return 1;
 | ||
|       uint32 n_points= uint4korr(data);
 | ||
|       data+= 4;
 | ||
|       if (not_enough_points(data, n_points) ||
 | ||
| 	  txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points,
 | ||
| 		       512))
 | ||
| 	return 1;
 | ||
|       txt->qs_append('(');
 | ||
|       data= append_points(txt, n_points, data, 0);
 | ||
|       (*txt) [txt->length() - 1]= ')';
 | ||
|       txt->qs_append(',');
 | ||
|     }
 | ||
|     (*txt) [txt->length() - 1]= ')';
 | ||
|     txt->qs_append(',');
 | ||
|   }
 | ||
|   txt->length(txt->length() - 1);
 | ||
|   *end= data;
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_multi_polygon::get_data_as_json(String *txt, uint max_dec_digits,
 | ||
|                                          const char **end) const
 | ||
| {
 | ||
|   uint32 n_polygons;
 | ||
|   const char *data= m_data;
 | ||
| 
 | ||
|   if (no_data(data, 4) || txt->reserve(1, 512))
 | ||
|     return 1;
 | ||
|   n_polygons= uint4korr(data);
 | ||
|   data+= 4;
 | ||
| 
 | ||
|   txt->q_append('[');
 | ||
|   while (n_polygons--)
 | ||
|   {
 | ||
|     uint32 n_linear_rings;
 | ||
|     if (no_data(data, 4 + WKB_HEADER_SIZE) ||
 | ||
| 	txt->reserve(1, 512))
 | ||
|       return 1;
 | ||
|     n_linear_rings= uint4korr(data+WKB_HEADER_SIZE);
 | ||
|     data+= 4 + WKB_HEADER_SIZE;
 | ||
|     txt->q_append('[');
 | ||
| 
 | ||
|     while (n_linear_rings--)
 | ||
|     {
 | ||
|       if (no_data(data, 4))
 | ||
|         return 1;
 | ||
|       uint32 n_points= uint4korr(data);
 | ||
|       data+= 4;
 | ||
|       if (not_enough_points(data, n_points) ||
 | ||
| 	  txt->reserve(2 + (MAX_DIGITS_IN_DOUBLE * 2 + 6) * n_points,
 | ||
| 		       512))
 | ||
| 	return 1;
 | ||
|       data= append_json_points(txt, max_dec_digits, n_points, data, 0);
 | ||
|       txt->qs_append(", ", 2);
 | ||
|     }
 | ||
|     txt->length(txt->length() - 2);
 | ||
|     txt->qs_append("], ", 3);
 | ||
|   }
 | ||
|   txt->length(txt->length() - 2);
 | ||
|   txt->q_append(']');
 | ||
|   *end= data;
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| class Gcalc_multipoly_transporter : public Gcalc_shape_transporter
 | ||
| {
 | ||
| protected:
 | ||
|   gcalc_shape_info m_si;
 | ||
| public:
 | ||
|   Gcalc_multipoly_transporter(Gcalc_heap *heap) :
 | ||
|     Gcalc_shape_transporter(heap), m_si(0) {}
 | ||
| 
 | ||
|   int single_point(double x, double y) override { return 0; }
 | ||
|   int start_line() override { return 0; }
 | ||
|   int complete_line() override { return 0; }
 | ||
| 
 | ||
|   int start_poly() override
 | ||
|   {
 | ||
|     int_start_poly();
 | ||
|     return 0;
 | ||
|   }
 | ||
| 
 | ||
|   int complete_poly() override
 | ||
|   {
 | ||
|     int_complete_poly();
 | ||
|     m_si++;
 | ||
|     return 0;
 | ||
|   }
 | ||
|   int start_ring() override
 | ||
|   {
 | ||
|     int_start_ring();
 | ||
|     return 0;
 | ||
|   }
 | ||
|   int complete_ring() override
 | ||
|   {
 | ||
|     int_complete_ring();
 | ||
|     return 0;
 | ||
|   }
 | ||
|   int add_point(double x, double y) override
 | ||
|   {
 | ||
|     return int_add_point(m_si, x, y);
 | ||
|   }
 | ||
| 
 | ||
|   int start_collection(int n_objects) override
 | ||
|   {
 | ||
|     return 0;
 | ||
|   }
 | ||
| 
 | ||
|   int empty_shape() override { return 0; }
 | ||
| };
 | ||
| 
 | ||
| 
 | ||
| int Gis_multi_polygon::is_valid(int *valid) const
 | ||
| {
 | ||
|   int result= 0;
 | ||
|   Gcalc_scan_iterator scan_it;
 | ||
|   Gcalc_heap collector;
 | ||
|   Gcalc_multipoly_transporter trn(&collector);
 | ||
|   MBR mbr;
 | ||
|   uint32 num_geometries;
 | ||
|   const char *c_end;
 | ||
|   char *internals;
 | ||
| 
 | ||
|   if (this->num_geometries(&num_geometries))
 | ||
|     return 1;
 | ||
| 
 | ||
|   if (shapes_valid(valid))
 | ||
|     return 1;
 | ||
| 
 | ||
|   if (*valid == 0)
 | ||
|     return 0;
 | ||
| 
 | ||
|   *valid= 0;
 | ||
| 
 | ||
|   if (num_geometries < 1)
 | ||
|     return 0;
 | ||
|   
 | ||
|   if(this->get_mbr(&mbr, &c_end))
 | ||
|     return 1;
 | ||
| 
 | ||
| 
 | ||
|   collector.set_extent(mbr.xmin, mbr.xmax, mbr.ymin, mbr.ymax);
 | ||
| 
 | ||
|   if (this->store_shapes(&trn))
 | ||
|     return 1;
 | ||
| 
 | ||
|   
 | ||
|   collector.prepare_operation();
 | ||
|   scan_it.init(&collector);
 | ||
|   internals= (char *) my_alloca(num_geometries);
 | ||
| 
 | ||
|   while (scan_it.more_points())
 | ||
|   {
 | ||
|     const Gcalc_scan_iterator::event_point *events, *next_ev;
 | ||
| 
 | ||
|     if (scan_it.step())
 | ||
|     {
 | ||
|       result= 1;
 | ||
|       goto exit;
 | ||
|     }
 | ||
| 
 | ||
|     events= scan_it.get_events();
 | ||
| 
 | ||
|     Gcalc_point_iterator pit(&scan_it);
 | ||
| 
 | ||
|     bzero(internals, num_geometries);
 | ||
|     /* Walk to the event, marking polygons we met */
 | ||
|     for (; pit.point() != scan_it.get_event_position(); ++pit)
 | ||
|     {
 | ||
|       gcalc_shape_info si= pit.point()->get_shape();
 | ||
|       internals[si]^= 1;
 | ||
|     }
 | ||
| 
 | ||
|     if (events->simple_event())
 | ||
|       continue;
 | ||
| 
 | ||
|     /* Check the status of the event point */
 | ||
|     for (; events; events= events->get_next())
 | ||
|     {
 | ||
|       gcalc_shape_info si= events->get_shape();
 | ||
|       if (events->event == scev_thread ||
 | ||
|           events->event == scev_end || /* should never happen. */
 | ||
|           events->event == scev_single_point ||
 | ||
|           events->event == scev_intersection)
 | ||
|       {
 | ||
|         /* These types of events never happen in valid multipolygon. */
 | ||
|         goto exit;
 | ||
|       }
 | ||
| 
 | ||
|       if ((internals[si]^= 1))
 | ||
|       {
 | ||
|         for (uint n=0; n<num_geometries; n++)
 | ||
|         {
 | ||
|           if (n != si && internals[n])
 | ||
|           {
 | ||
|             /* Polygons overlap */
 | ||
|             goto exit;
 | ||
|           }
 | ||
|         }
 | ||
|       }
 | ||
| 
 | ||
|       if ((next_ev= events->get_next()))
 | ||
|       {
 | ||
|         if (next_ev->event != scev_two_ends &&
 | ||
|             events->event != scev_two_ends &&
 | ||
|             events->cmp_dx_dy(events->dx, events->dy,
 | ||
|                               next_ev->dx, next_ev->dy) == 0)
 | ||
|         {
 | ||
|           /* Only can touch at points, not lines. */
 | ||
|           goto exit;
 | ||
|         }
 | ||
|       }
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   *valid= 1;
 | ||
| 
 | ||
| exit:
 | ||
|   collector.reset();
 | ||
|   scan_it.reset();
 | ||
|   my_afree(internals);
 | ||
|   return result;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| 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::simplify(String *result, double max_distance) const
 | ||
| {
 | ||
|   uint32 num_polygon= 0, num_invalid_polygon= 0;
 | ||
|   Geometry_buffer buffer;
 | ||
|   Geometry *geometry= NULL;
 | ||
| 
 | ||
|   if (this->num_geometries(&num_polygon))
 | ||
|     return 1;
 | ||
| 
 | ||
|   result->length(0);
 | ||
|   result->reserve(SRID_SIZE + WKB_HEADER_SIZE);
 | ||
|   result->q_append(SRID_PLACEHOLDER);
 | ||
|   result->q_append((char) wkb_ndr);
 | ||
|   result->q_append((uint32) wkb_multipolygon);
 | ||
|   result->q_append((uint32) num_polygon);
 | ||
| 
 | ||
|   for (uint32 i = 1; i <= num_polygon; i++)
 | ||
|   {
 | ||
|     String polygon= 0, simplified_polygon= 0;
 | ||
|     polygon.q_append((uint) 0);
 | ||
|     if (this->geometry_n(i, &polygon) ||
 | ||
|         !(geometry= Geometry::construct(&buffer, polygon.ptr(),
 | ||
|           polygon.length())))
 | ||
|       return 1;
 | ||
| 
 | ||
|     if(geometry->simplify(&simplified_polygon, max_distance))
 | ||
|     {
 | ||
|       num_invalid_polygon++;
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     result->append(simplified_polygon.ptr() + SRID_SIZE,
 | ||
|                    simplified_polygon.length() - SRID_SIZE);
 | ||
|   }
 | ||
| 
 | ||
|   if (num_polygon == num_invalid_polygon)
 | ||
|     return 1;
 | ||
| 
 | ||
|   result->write_at_position(SRID_SIZE + WKB_HEADER_SIZE,
 | ||
|                             ((uint32) num_polygon - num_invalid_polygon));
 | ||
|   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;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| int Gis_multi_polygon::shapes_valid(int *valid) 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;
 | ||
| 
 | ||
|   *valid= 0;
 | ||
| 
 | ||
|   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.is_valid(valid))
 | ||
|       return 1;
 | ||
| 
 | ||
|     if (*valid == 0)
 | ||
|       break;
 | ||
| 
 | ||
|     data+= p.get_data_size();
 | ||
|   }
 | ||
| 
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| int Gis_multi_polygon::make_clockwise(String *result) const
 | ||
| {
 | ||
|   Geometry_buffer buffer;
 | ||
|   uint32 num_polygons;
 | ||
|   Geometry *polygon;
 | ||
| 
 | ||
|   if(this->num_geometries(&num_polygons) ||
 | ||
|      result->reserve(SRID_SIZE + WKB_HEADER_SIZE))
 | ||
|     return 1;
 | ||
| 
 | ||
|   result->q_append((char) wkb_ndr);
 | ||
|   result->q_append((uint32) wkb_multipolygon);
 | ||
|   result->q_append((uint32) num_polygons);
 | ||
|   for (uint32 i= 1; i <= num_polygons; i++)
 | ||
|   {
 | ||
|     String wkb= 0, clockwise_wkb= 0;
 | ||
|     if (wkb.reserve(SRID_SIZE + BYTE_ORDER_SIZE + WKB_HEADER_SIZE))
 | ||
|       return 0;
 | ||
| 
 | ||
|     wkb.q_append(SRID_PLACEHOLDER);
 | ||
|     if (this->geometry_n(i, &wkb) ||
 | ||
|         !(polygon= Geometry::construct(&buffer, wkb.ptr(), wkb.length())))
 | ||
|       return 1;
 | ||
| 
 | ||
|     if (polygon->make_clockwise(&clockwise_wkb))
 | ||
|       return 1;
 | ||
| 
 | ||
|     // Reserve space for the byte order and GIS type.
 | ||
|     if (result->reserve(sizeof(char) + sizeof(uint32)))
 | ||
|       return 1;
 | ||
|     result->q_append((char) wkb_ndr);
 | ||
|     result->q_append((uint32) wkb_polygon);
 | ||
|     result->append(clockwise_wkb.ptr() + WKB_HEADER_SIZE,
 | ||
|                    clockwise_wkb.length() - WKB_HEADER_SIZE);
 | ||
|   }
 | ||
| 
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| const Geometry::Class_info *Gis_multi_polygon::get_class_info() const
 | ||
| {
 | ||
|   return &multipolygon_class;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| /************************* GeometryCollection ****************************/
 | ||
| 
 | ||
| uint32 Gis_geometry_collection::get_data_size() const 
 | ||
| {
 | ||
|   uint32 n_objects;
 | ||
|   const char *data= m_data;
 | ||
|   Geometry_buffer buffer;
 | ||
|   Geometry *geom;
 | ||
| 
 | ||
|   if (no_data(data, 4))
 | ||
|     return GET_SIZE_ERROR;
 | ||
|   n_objects= uint4korr(data);
 | ||
|   data+= 4;
 | ||
| 
 | ||
|   while (n_objects--)
 | ||
|   {
 | ||
|     uint32 wkb_type,object_size;
 | ||
| 
 | ||
|     if (no_data(data, WKB_HEADER_SIZE))
 | ||
|       return GET_SIZE_ERROR;
 | ||
|     wkb_type= uint4korr(data + 1);
 | ||
|     data+= WKB_HEADER_SIZE;
 | ||
| 
 | ||
|     if (!(geom= create_by_typeid(&buffer, wkb_type)))
 | ||
|       return GET_SIZE_ERROR;
 | ||
|     geom->set_data_ptr(data, (uint) (m_data_end - data));
 | ||
|     if ((object_size= geom->get_data_size()) == GET_SIZE_ERROR)
 | ||
|       return GET_SIZE_ERROR;
 | ||
|     data+= object_size;
 | ||
|   }
 | ||
|   return (uint32) (data - m_data);
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_geometry_collection::init_from_wkt(Gis_read_stream *trs, String *wkb)
 | ||
| {
 | ||
|   uint32 n_objects= 0;
 | ||
|   uint32 no_pos= wkb->length();
 | ||
|   Geometry_buffer buffer;
 | ||
|   Geometry *g;
 | ||
|   char next_sym;
 | ||
| 
 | ||
|   if (wkb->reserve(4, 512))
 | ||
|     return 1;
 | ||
|   wkb->length(wkb->length()+4);			// Reserve space for points
 | ||
| 
 | ||
|   if (!(next_sym= trs->next_symbol()))
 | ||
|     return 1;
 | ||
| 
 | ||
|   if (next_sym != ')')
 | ||
|   {
 | ||
|     LEX_STRING next_word;
 | ||
|     if (trs->lookup_next_word(&next_word))
 | ||
|       return 1;
 | ||
| 
 | ||
|     if (next_word.length != 5 ||
 | ||
| 	     (my_charset_latin1.strnncoll("empty", 5, next_word.str, 5) != 0))
 | ||
|     {
 | ||
|       for (;;)
 | ||
|       {
 | ||
|         if (!(g= create_from_wkt(&buffer, trs, wkb)))
 | ||
|           return 1;
 | ||
| 
 | ||
|         if (g->get_class_info()->m_type_id == wkb_geometrycollection)
 | ||
|         {
 | ||
|           trs->set_error_msg("Unexpected GEOMETRYCOLLECTION");
 | ||
|           return 1;
 | ||
|         }
 | ||
|         n_objects++;
 | ||
|         if (trs->skip_char(','))			// Didn't find ','
 | ||
|           break;
 | ||
|       }
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   wkb->write_at_position(no_pos, n_objects);
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| uint Gis_geometry_collection::init_from_opresult(String *bin,
 | ||
|                                                  const char *opres,
 | ||
|                                                  uint res_len)
 | ||
| {
 | ||
|   const char *opres_orig= opres;
 | ||
|   Geometry_buffer buffer;
 | ||
|   Geometry *geom;
 | ||
|   int g_len;
 | ||
|   uint32 wkb_type;
 | ||
|   int no_pos= bin->length();
 | ||
|   uint32 n_objects= 0;
 | ||
| 
 | ||
|   if (bin->reserve(4, 512))
 | ||
|     return 0;
 | ||
|   bin->q_append(n_objects);
 | ||
|   
 | ||
|   if (res_len == 0)
 | ||
|   {
 | ||
|     /* Special case of GEOMETRYCOLLECTION EMPTY. */
 | ||
|     opres+= 1;
 | ||
|     goto empty_geom;
 | ||
|   }
 | ||
|   
 | ||
|   while (res_len)
 | ||
|   {
 | ||
|     switch ((Gcalc_function::shape_type) uint4korr(opres))
 | ||
|     {
 | ||
|       case Gcalc_function::shape_point:   wkb_type= wkb_point; break;
 | ||
|       case Gcalc_function::shape_line:    wkb_type= wkb_linestring; break;
 | ||
|       case Gcalc_function::shape_polygon: wkb_type= wkb_polygon; break;
 | ||
|       default: wkb_type= 0; DBUG_ASSERT(FALSE);
 | ||
|     };
 | ||
| 
 | ||
|     if (bin->reserve(WKB_HEADER_SIZE, 512))
 | ||
|       return 0;
 | ||
| 
 | ||
|     bin->q_append((char) wkb_ndr);
 | ||
|     bin->q_append(wkb_type);
 | ||
| 
 | ||
|     if (!(geom= create_by_typeid(&buffer, wkb_type)) ||
 | ||
|         !(g_len= geom->init_from_opresult(bin, opres, res_len)))
 | ||
|       return 0;
 | ||
|     opres+= g_len;
 | ||
|     res_len-= g_len;
 | ||
|     n_objects++;
 | ||
|   }
 | ||
| empty_geom:
 | ||
|   bin->write_at_position(no_pos, n_objects);
 | ||
|   return (uint) (opres - opres_orig);
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| uint Gis_geometry_collection::init_from_wkb(const char *wkb, uint len,
 | ||
|                                             wkbByteOrder bo, String *res)
 | ||
| {
 | ||
|   uint32 n_geom;
 | ||
|   const char *wkb_orig= wkb;
 | ||
| 
 | ||
|   if (len < 4)
 | ||
|     return 0;
 | ||
|   n_geom= wkb_get_uint(wkb, bo);
 | ||
| 
 | ||
|   if (res->reserve(4, 512))
 | ||
|     return 0;
 | ||
|   res->q_append(n_geom);
 | ||
|   
 | ||
|   wkb+= 4;
 | ||
|   while (n_geom--)
 | ||
|   {
 | ||
|     Geometry_buffer buffer;
 | ||
|     Geometry *geom;
 | ||
|     int g_len;
 | ||
|     uint32 wkb_type;
 | ||
| 
 | ||
|     if (len < WKB_HEADER_SIZE ||
 | ||
|         res->reserve(WKB_HEADER_SIZE, 512))
 | ||
|       return 0;
 | ||
| 
 | ||
|     res->q_append((char) wkb_ndr);
 | ||
|     wkb_type= wkb_get_uint(wkb+1, (wkbByteOrder) wkb[0]);
 | ||
|     res->q_append(wkb_type);
 | ||
| 
 | ||
|     if (!(geom= create_by_typeid(&buffer, wkb_type)) ||
 | ||
|         !(g_len= geom->init_from_wkb(wkb + WKB_HEADER_SIZE, len,
 | ||
|                                      (wkbByteOrder)  wkb[0], res)))
 | ||
|       return 0;
 | ||
|     g_len+= WKB_HEADER_SIZE;
 | ||
|     wkb+= g_len;
 | ||
|     len-= g_len;
 | ||
|   }
 | ||
|   return (uint) (wkb - wkb_orig);
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_geometry_collection::init_from_json(json_engine_t *je, bool er_on_3D,
 | ||
|                                              String *wkb)
 | ||
| {
 | ||
|   uint32 n_objects= 0;
 | ||
|   uint32 no_pos= wkb->length();
 | ||
|   Geometry_buffer buffer;
 | ||
|   Geometry *g;
 | ||
| 
 | ||
|   if (json_read_value(je))
 | ||
|     return TRUE;
 | ||
| 
 | ||
|   if (je->value_type != JSON_VALUE_ARRAY)
 | ||
|   {
 | ||
|     je->s.error= GEOJ_INCORRECT_GEOJSON;
 | ||
|     return TRUE;
 | ||
|   }
 | ||
| 
 | ||
|   if (wkb->reserve(4, 512))
 | ||
|     return TRUE;
 | ||
|   wkb->length(wkb->length()+4);	// Reserve space for n_objects
 | ||
| 
 | ||
|   while (json_scan_next(je) == 0 && je->state != JST_ARRAY_END)
 | ||
|   {
 | ||
|     json_engine_t sav_je= *je;
 | ||
| 
 | ||
|     DBUG_ASSERT(je->state == JST_VALUE);
 | ||
| 
 | ||
|     if (!(g= create_from_json(&buffer, je, er_on_3D, wkb)))
 | ||
|       return TRUE;
 | ||
| 
 | ||
|     *je= sav_je;
 | ||
|     if (json_skip_array_item(je))
 | ||
|       return TRUE;
 | ||
| 
 | ||
|     n_objects++;
 | ||
|   }
 | ||
| 
 | ||
|   wkb->write_at_position(no_pos, n_objects);
 | ||
|   return FALSE;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_geometry_collection::get_data_as_wkt(String *txt,
 | ||
| 					     const char **end) const
 | ||
| {
 | ||
|   uint32 n_objects;
 | ||
|   Geometry_buffer buffer;
 | ||
|   Geometry *geom;
 | ||
|   const char *data= m_data;
 | ||
| 
 | ||
|   if (no_data(data, 4))
 | ||
|     return 1;
 | ||
|   n_objects= uint4korr(data);
 | ||
|   data+= 4;
 | ||
| 
 | ||
|   if (n_objects == 0)
 | ||
|   {
 | ||
|     txt->append(STRING_WITH_LEN(" EMPTY"), 512);
 | ||
|     goto exit;
 | ||
|   }
 | ||
| 
 | ||
|   txt->qs_append('(');
 | ||
|   while (n_objects--)
 | ||
|   {
 | ||
|     uint32 wkb_type;
 | ||
| 
 | ||
|     if (no_data(data, WKB_HEADER_SIZE))
 | ||
|       return 1;
 | ||
|     wkb_type= uint4korr(data + 1);
 | ||
|     data+= WKB_HEADER_SIZE;
 | ||
| 
 | ||
|     if (!(geom= create_by_typeid(&buffer, wkb_type)))
 | ||
|       return 1;
 | ||
|     geom->set_data_ptr(data, (uint) (m_data_end - data));
 | ||
|     if (geom->as_wkt(txt, &data))
 | ||
|       return 1;
 | ||
|     if (n_objects && txt->append(STRING_WITH_LEN(","), 512))
 | ||
|       return 1;
 | ||
|   }
 | ||
|   txt->qs_append(')');
 | ||
| exit:
 | ||
|   *end= data;
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| bool Gis_geometry_collection::get_data_as_json(String *txt, uint max_dec_digits,
 | ||
|                                                const char **end) const
 | ||
| {
 | ||
|   uint32 n_objects;
 | ||
|   Geometry_buffer buffer;
 | ||
|   Geometry *geom;
 | ||
|   const char *data= m_data;
 | ||
| 
 | ||
|   if (no_data(data, 4) || txt->reserve(1, 512))
 | ||
|     return 1;
 | ||
|   n_objects= uint4korr(data);
 | ||
|   data+= 4;
 | ||
| 
 | ||
|   txt->qs_append('[');
 | ||
|   while (n_objects--)
 | ||
|   {
 | ||
|     uint32 wkb_type;
 | ||
| 
 | ||
|     if (no_data(data, WKB_HEADER_SIZE))
 | ||
|       return 1;
 | ||
|     wkb_type= uint4korr(data + 1);
 | ||
|     data+= WKB_HEADER_SIZE;
 | ||
| 
 | ||
|     if (!(geom= create_by_typeid(&buffer, wkb_type)))
 | ||
|       return 1;
 | ||
|     geom->set_data_ptr(data, (uint) (m_data_end - data));
 | ||
|     if (txt->append('{') ||
 | ||
|         geom->as_json(txt, max_dec_digits, &data) ||
 | ||
|         txt->append(STRING_WITH_LEN("}, "), 512))
 | ||
|       return 1;
 | ||
|   }
 | ||
|   txt->length(txt->length() - 2);
 | ||
|   if (txt->append(']'))
 | ||
|     return 1;
 | ||
| 
 | ||
|   *end= data;
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| int Gis_geometry_collection::is_valid(int *valid) const
 | ||
| {
 | ||
|   Geometry_buffer buffer;
 | ||
|   uint32 num_geometries;
 | ||
|   Geometry *geometry;
 | ||
|   *valid= 0;
 | ||
| 
 | ||
|   if (this->num_geometries(&num_geometries))
 | ||
|     return 1;
 | ||
| 
 | ||
|   for (uint32 i= 1; i <= num_geometries; i++)
 | ||
|   {
 | ||
|     String wkb= 0;
 | ||
| 
 | ||
|     if (wkb.reserve(SRID_SIZE + BYTE_ORDER_SIZE + WKB_HEADER_SIZE))
 | ||
|       return 1;
 | ||
| 
 | ||
|     wkb.q_append(SRID_PLACEHOLDER);
 | ||
|     if(this->geometry_n(i, &wkb) ||
 | ||
|        !(geometry= Geometry::construct(&buffer, wkb.ptr(), wkb.length())))
 | ||
|       return 1;
 | ||
| 
 | ||
|     int internal_valid;
 | ||
|     if (geometry->is_valid(&internal_valid))
 | ||
|       return 1;
 | ||
| 
 | ||
|     if (!internal_valid)
 | ||
|       return 0;
 | ||
|   }
 | ||
| 
 | ||
|   *valid= 1;
 | ||
|   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::simplify(String *result,
 | ||
|                                       double max_distance) const
 | ||
| {
 | ||
|   uint32 num_geometries= 0, num_invalid_geometries= 0;
 | ||
|   Geometry_buffer buffer;
 | ||
|   Geometry *geometry= NULL;
 | ||
| 
 | ||
|   if (this->num_geometries(&num_geometries))
 | ||
|     return 1;
 | ||
| 
 | ||
|   result->length(0);
 | ||
|   result->reserve(SRID_SIZE + BYTE_ORDER_SIZE + WKB_HEADER_SIZE);
 | ||
|   result->q_append(SRID_PLACEHOLDER);
 | ||
|   result->q_append((char) wkb_ndr);
 | ||
|   result->q_append((uint32) wkb_geometrycollection);
 | ||
|   result->q_append((uint32) num_geometries);
 | ||
| 
 | ||
|   for (uint32 i = 1; i <= num_geometries; i++)
 | ||
|   {
 | ||
|     String wkb= 0, simplified_wkb= 0;
 | ||
| 
 | ||
|     wkb.q_append((uint) 0);
 | ||
|     if (this->geometry_n(i, &wkb) ||
 | ||
|         !(geometry= Geometry::construct(&buffer, wkb.ptr(), wkb.length())))
 | ||
|       return 1;
 | ||
| 
 | ||
|     if (geometry->get_class_info()->m_type_id == Geometry::wkb_point ||
 | ||
|         geometry->get_class_info()->m_type_id == Geometry::wkb_multipoint)
 | ||
|     {
 | ||
|       result->append(wkb.ptr() + SRID_SIZE, wkb.length() - SRID_SIZE);
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     if(geometry->simplify(&simplified_wkb, max_distance))
 | ||
|     {
 | ||
|       num_invalid_geometries++;
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     result->append(simplified_wkb.ptr() + SRID_SIZE,
 | ||
|                    simplified_wkb.length() - SRID_SIZE);
 | ||
|   }
 | ||
| 
 | ||
|   if (num_geometries == num_invalid_geometries)
 | ||
|     return 1;
 | ||
| 
 | ||
|   result->write_at_position(SRID_SIZE + WKB_HEADER_SIZE,
 | ||
|                             (uint32) num_geometries - num_invalid_geometries);
 | ||
|   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;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| int Gis_geometry_collection::make_clockwise(String *result) const
 | ||
| {
 | ||
|   Geometry_buffer buffer;
 | ||
|   uint32 num_geometries;
 | ||
|   Geometry *geometry;
 | ||
| 
 | ||
|   if(this->num_geometries(&num_geometries) ||
 | ||
|      result->reserve(SRID_SIZE + WKB_HEADER_SIZE))
 | ||
|     return 1;
 | ||
| 
 | ||
|   result->q_append((char) wkb_ndr);
 | ||
|   result->q_append((uint32) wkb_geometrycollection);
 | ||
|   result->q_append((uint32) num_geometries);
 | ||
|   for (uint32 i= 1; i <= num_geometries; i++)
 | ||
|   {
 | ||
|     String wkb= 0, clockwise_wkb= 0;
 | ||
|     if (wkb.reserve(SRID_SIZE + BYTE_ORDER_SIZE + WKB_HEADER_SIZE))
 | ||
|       return 0;
 | ||
| 
 | ||
|     wkb.q_append(SRID_PLACEHOLDER);
 | ||
|     if (this->geometry_n(i, &wkb) ||
 | ||
|         !(geometry= Geometry::construct(&buffer, wkb.ptr(), wkb.length())))
 | ||
|       return 1;
 | ||
| 
 | ||
|     result->reserve(sizeof(char) + sizeof(uint32));
 | ||
|     result->q_append((char) wkb_ndr);
 | ||
|     result->q_append((uint32) geometry->get_class_info()->m_type_id);
 | ||
|     if (geometry->get_class_info()->m_type_id == Geometry::wkb_polygon ||
 | ||
|         geometry->get_class_info()->m_type_id == Geometry::wkb_multipolygon ||
 | ||
|         geometry->get_class_info()->m_type_id ==
 | ||
|           Geometry::wkb_geometrycollection)
 | ||
|     {
 | ||
|       if(geometry->make_clockwise(&clockwise_wkb))
 | ||
|         return 1;
 | ||
|       result->append(clockwise_wkb.ptr() + WKB_HEADER_SIZE,
 | ||
|                     clockwise_wkb.length() - WKB_HEADER_SIZE);
 | ||
|     }
 | ||
|     else
 | ||
|     {
 | ||
|       result->append(wkb.ptr() + SRID_SIZE + WKB_HEADER_SIZE,
 | ||
|                      wkb.length() - (SRID_SIZE + WKB_HEADER_SIZE));
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| const Geometry::Class_info *Gis_geometry_collection::get_class_info() const
 | ||
| {
 | ||
|   return &geometrycollection_class;
 | ||
| }
 | 
