MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
spatial.cc
1 /*
2  Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; version 2 of the License.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software Foundation,
15  51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
16 
17 #include "sql_priv.h"
18 #include "sql_string.h" // String
19 #include "my_global.h" // REQUIRED for HAVE_* below
20 #include "gstream.h" // Gis_read_stream
21 #include "spatial.h"
22 #include <mysqld_error.h>
23 
24 #ifdef HAVE_SPATIAL
25 
26 /*
27  exponential notation :
28  1 sign
29  1 number before the decimal point
30  1 decimal point
31  14 number of significant digits (see String::qs_append(double))
32  1 'e' sign
33  1 exponent sign
34  3 exponent digits
35  ==
36  22
37 
38  "f" notation :
39  1 optional 0
40  1 sign
41  14 number significant digits (see String::qs_append(double) )
42  1 decimal point
43  ==
44  17
45 */
46 
47 #define MAX_DIGITS_IN_DOUBLE 30
48 
49 /***************************** Gis_class_info *******************************/
50 
51 String Geometry::bad_geometry_data("Bad object", &my_charset_bin);
52 
53 Geometry::Class_info *Geometry::ci_collection[Geometry::wkb_last+1]=
54 {
55  NULL, NULL, NULL, NULL, NULL, NULL, NULL
56 };
57 
58 static Geometry::Class_info **ci_collection_end=
59  Geometry::ci_collection+Geometry::wkb_last + 1;
60 
61 Geometry::Class_info::Class_info(const char *name, int type_id,
62  create_geom_t create_func):
63  m_type_id(type_id), m_create_func(create_func)
64 {
65  m_name.str= (char *) name;
66  m_name.length= strlen(name);
67 
68  ci_collection[type_id]= this;
69 }
70 
71 static Geometry *create_point(char *buffer)
72 {
73  return new (buffer) Gis_point;
74 }
75 
76 static Geometry *create_linestring(char *buffer)
77 {
78  return new (buffer) Gis_line_string;
79 }
80 
81 static Geometry *create_polygon(char *buffer)
82 {
83  return new (buffer) Gis_polygon;
84 }
85 
86 static Geometry *create_multipoint(char *buffer)
87 {
88  return new (buffer) Gis_multi_point;
89 }
90 
91 static Geometry *create_multipolygon(char *buffer)
92 {
93  return new (buffer) Gis_multi_polygon;
94 }
95 
96 static Geometry *create_multilinestring(char *buffer)
97 {
98  return new (buffer) Gis_multi_line_string;
99 }
100 
101 static Geometry *create_geometrycollection(char *buffer)
102 {
103  return new (buffer) Gis_geometry_collection;
104 }
105 
106 
107 
108 static Geometry::Class_info point_class("POINT",
109  Geometry::wkb_point, create_point);
110 
111 static Geometry::Class_info linestring_class("LINESTRING",
112  Geometry::wkb_linestring,
113  create_linestring);
114 static Geometry::Class_info polygon_class("POLYGON",
115  Geometry::wkb_polygon,
116  create_polygon);
117 static Geometry::Class_info multipoint_class("MULTIPOINT",
118  Geometry::wkb_multipoint,
119  create_multipoint);
120 static Geometry::Class_info
121 multilinestring_class("MULTILINESTRING",
122  Geometry::wkb_multilinestring, create_multilinestring);
123 static Geometry::Class_info multipolygon_class("MULTIPOLYGON",
124  Geometry::wkb_multipolygon,
125  create_multipolygon);
126 static Geometry::Class_info
127 geometrycollection_class("GEOMETRYCOLLECTION",Geometry::wkb_geometrycollection,
128  create_geometrycollection);
129 
130 /***************************** Geometry *******************************/
131 
132 Geometry::Class_info *Geometry::find_class(const char *name, uint32 len)
133 {
134  for (Class_info **cur_rt= ci_collection;
135  cur_rt < ci_collection_end; cur_rt++)
136  {
137  if (*cur_rt &&
138  ((*cur_rt)->m_name.length == len) &&
139  (my_strnncoll(&my_charset_latin1,
140  (const uchar*) (*cur_rt)->m_name.str, len,
141  (const uchar*) name, len) == 0))
142  return *cur_rt;
143  }
144  return 0;
145 }
146 
147 
148 Geometry *Geometry::create_by_typeid(Geometry_buffer *buffer, int type_id)
149 {
150  Class_info *ci;
151  if (!(ci= find_class(type_id)))
152  return NULL;
153  return (*ci->m_create_func)(buffer->data);
154 }
155 
156 
157 Geometry *Geometry::construct(Geometry_buffer *buffer,
158  const char *data, uint32 data_len)
159 {
160  uint32 geom_type;
161  Geometry *result;
162 
163  if (data_len < SRID_SIZE + WKB_HEADER_SIZE) // < 4 + (1 + 4)
164  return NULL;
165  /* + 1 to skip the byte order (stored in position SRID_SIZE). */
166  geom_type= uint4korr(data + SRID_SIZE + 1);
167  if (!(result= create_by_typeid(buffer, (int) geom_type)))
168  return NULL;
169  result->set_data_ptr(data + SRID_SIZE + WKB_HEADER_SIZE,
170  data_len - SRID_SIZE - WKB_HEADER_SIZE);
171  return result;
172 }
173 
174 
175 Geometry *Geometry::create_from_wkt(Geometry_buffer *buffer,
176  Gis_read_stream *trs, String *wkt,
177  bool init_stream)
178 {
180  Class_info *ci;
181 
182  if (trs->get_next_word(&name))
183  {
184  trs->set_error_msg("Geometry name expected");
185  return NULL;
186  }
187  if (!(ci= find_class(name.str, name.length)) ||
188  wkt->reserve(WKB_HEADER_SIZE, 512))
189  return NULL;
190  Geometry *result= (*ci->m_create_func)(buffer->data);
191  wkt->q_append((char) wkb_ndr);
192  wkt->q_append((uint32) result->get_class_info()->m_type_id);
193  if (trs->check_next_symbol('(') ||
194  result->init_from_wkt(trs, wkt) ||
195  trs->check_next_symbol(')'))
196  return NULL;
197  if (init_stream)
198  result->set_data_ptr(wkt->ptr() + WKB_HEADER_SIZE,
199  wkt->length() - WKB_HEADER_SIZE);
200  return result;
201 }
202 
203 
204 static double wkb_get_double(const char *ptr, Geometry::wkbByteOrder bo)
205 {
206  double res;
207  if (bo != Geometry::wkb_xdr)
208  {
209  float8get(res, ptr);
210  }
211  else
212  {
213  char inv_array[8];
214  inv_array[0]= ptr[7];
215  inv_array[1]= ptr[6];
216  inv_array[2]= ptr[5];
217  inv_array[3]= ptr[4];
218  inv_array[4]= ptr[3];
219  inv_array[5]= ptr[2];
220  inv_array[6]= ptr[1];
221  inv_array[7]= ptr[0];
222  float8get(res, inv_array);
223  }
224  return res;
225 }
226 
227 
228 static uint32 wkb_get_uint(const char *ptr, Geometry::wkbByteOrder bo)
229 {
230  if (bo != Geometry::wkb_xdr)
231  return uint4korr(ptr);
232  /* else */
233  {
234  char inv_array[4], *inv_array_p= inv_array;
235  inv_array[0]= ptr[3];
236  inv_array[1]= ptr[2];
237  inv_array[2]= ptr[1];
238  inv_array[3]= ptr[0];
239  return uint4korr(inv_array_p);
240  }
241 }
242 
243 
244 Geometry *Geometry::create_from_wkb(Geometry_buffer *buffer,
245  const char *wkb, uint32 len, String *res)
246 {
247  uint32 geom_type;
248  Geometry *geom;
249 
250  if (len < WKB_HEADER_SIZE)
251  return NULL;
252  geom_type= wkb_get_uint(wkb+1, (wkbByteOrder)wkb[0]);
253  if (!(geom= create_by_typeid(buffer, (int) geom_type)) ||
254  res->reserve(WKB_HEADER_SIZE, 512))
255  return NULL;
256 
257  res->q_append((char) wkb_ndr);
258  res->q_append(geom_type);
259 
260  return geom->init_from_wkb(wkb + WKB_HEADER_SIZE, len - WKB_HEADER_SIZE,
261  (wkbByteOrder) wkb[0], res) ? geom : NULL;
262 }
263 
264 
265 int Geometry::create_from_opresult(Geometry_buffer *g_buf,
266  String *res, Gcalc_result_receiver &rr)
267 {
268  uint32 geom_type= rr.get_result_typeid();
269  Geometry *obj= create_by_typeid(g_buf, geom_type);
270 
271  if (!obj || res->reserve(WKB_HEADER_SIZE, 512))
272  return 1;
273 
274  res->q_append((char) wkb_ndr);
275  res->q_append(geom_type);
276  return obj->init_from_opresult(res, rr.result(), rr.length());
277 }
278 
279 
280 bool Geometry::envelope(String *result) const
281 {
282  MBR mbr;
283  wkb_parser wkb(&m_wkb_data);
284 
285  if (get_mbr(&mbr, &wkb) ||
286  result->reserve(1 + 4 * 3 + SIZEOF_STORED_DOUBLE * 10))
287  return true;
288 
289  result->q_append((char) wkb_ndr);
290  result->q_append((uint32) wkb_polygon);
291  result->q_append((uint32) 1);
292  result->q_append((uint32) 5);
293  result->q_append(mbr.xmin);
294  result->q_append(mbr.ymin);
295  result->q_append(mbr.xmax);
296  result->q_append(mbr.ymin);
297  result->q_append(mbr.xmax);
298  result->q_append(mbr.ymax);
299  result->q_append(mbr.xmin);
300  result->q_append(mbr.ymax);
301  result->q_append(mbr.xmin);
302  result->q_append(mbr.ymin);
303 
304  return false;
305 }
306 
307 
317 bool Geometry::create_point(String *result, wkb_parser *wkb) const
318 {
319  if (wkb->no_data(POINT_DATA_SIZE) ||
320  result->reserve(WKB_HEADER_SIZE + POINT_DATA_SIZE))
321  return true;
322  result->q_append((char) wkb_ndr);
323  result->q_append((uint32) wkb_point);
324  /* Copy two double in same format */
325  result->q_append(wkb->data(), POINT_DATA_SIZE);
326  return false;
327 }
328 
339 bool Geometry::create_point(String *result, point_xy p) const
340 {
341  if (result->reserve(1 + 4 + POINT_DATA_SIZE))
342  return true;
343 
344  result->q_append((char) wkb_ndr);
345  result->q_append((uint32) wkb_point);
346  result->q_append(p.x);
347  result->q_append(p.y);
348  return false;
349  }
350 
360 void Geometry::append_points(String *txt, uint32 n_points,
361  wkb_parser *wkb, uint32 offset) const
362 {
363  while (n_points--)
364  {
365  point_xy p;
366  wkb->skip_unsafe(offset);
367  wkb->scan_xy_unsafe(&p);
368  txt->qs_append(p.x);
369  txt->qs_append(' ');
370  txt->qs_append(p.y);
371  txt->qs_append(',');
372  }
373 }
374 
375 
386 bool Geometry::get_mbr_for_points(MBR *mbr, wkb_parser *wkb,
387  uint offset) const
388 {
389  uint32 n_points;
390 
391  if (wkb->scan_n_points_and_check_data(&n_points, offset))
392  return true;
393 
394  /* Calculate MBR for points */
395  while (n_points--)
396  {
397  wkb->skip_unsafe(offset);
398  mbr->add_xy(wkb->data(), wkb->data() + SIZEOF_STORED_DOUBLE);
399  wkb->skip_unsafe(POINT_DATA_SIZE);
400  }
401  return false;
402 }
403 
404 
405 int Geometry::collection_store_shapes(Gcalc_shape_transporter *trn,
406  Gcalc_shape_status *st,
407  Geometry *collection_item) const
408 {
409  uint32 n_objects;
410  wkb_parser wkb(&m_wkb_data);
411  Geometry_buffer buffer;
412 
413  if (wkb.scan_non_zero_uint4(&n_objects) ||
414  trn->start_collection(st, n_objects))
415  return 1;
416 
417  while (n_objects--)
418  {
419  Geometry *geom;
420  if (!(geom= collection_item))
421  {
422  /*
423  Item type is not known in advance, e.g. GeometryCollection.
424  Create an item object in every iteration,
425  according to the current wkb type.
426  */
427  if (!(geom= scan_header_and_create(&wkb, &buffer)))
428  return 1;
429  }
430  else
431  {
432  if (wkb.skip_wkb_header())
433  return 1;
434  geom->set_data_ptr(&wkb);
435  }
436  Gcalc_shape_status item_status;
437  if (geom->store_shapes(trn, &item_status) ||
438  trn->collection_add_item(st, &item_status))
439  return 1;
440  wkb.skip_unsafe(geom->get_data_size());
441  }
442  trn->complete_collection(st);
443  return 0;
444 }
445 
446 
447 bool Geometry::collection_area(double *ar, wkb_parser *wkb,
448  Geometry *collection_item) const
449 {
450  uint32 n_objects;
451  Geometry_buffer buffer;
452 
453  if (wkb->scan_non_zero_uint4(&n_objects))
454  return true;
455 
456  for (*ar= 0; n_objects; n_objects--)
457  {
458  Geometry *geom;
459  if (!(geom= collection_item))
460  {
461  /*
462  Item type is not known in advance, e.g. GeometryCollection.
463  Create an item object according to the wkb type.
464  */
465  if (!(geom= scan_header_and_create(wkb, &buffer)))
466  return true;
467  }
468  else
469  {
470  if (wkb->skip_wkb_header())
471  return true;
472  geom->set_data_ptr(wkb);
473  }
474  double item_area;
475  if (geom->area(&item_area, wkb))
476  return true;
477  *ar+= item_area;
478  }
479  return false;
480 }
481 
482 
483 uint Geometry::collection_init_from_opresult(String *bin,
484  const char *opres,
485  uint opres_length,
486  Geometry *collection_item)
487 {
488  Geometry_buffer buffer;
489  const char *opres_orig= opres;
490  int n_items_offs= bin->length();
491  uint n_items= 0;
492 
493  if (bin->reserve(4, 512))
494  return 0;
495  bin->q_append((uint32) 0);
496 
497  while (opres_length)
498  {
499  int item_len;
500 
501  if (bin->reserve(WKB_HEADER_SIZE, 512))
502  return 0;
503 
504  Geometry *item;
505  if (collection_item)
506  {
507  /*
508  MultiPoint, MultiLineString, or MultiPolygon pass
509  a pre-created collection item. Let's use it.
510  */
511  item= collection_item;
512  }
513  else
514  {
515  /*
516  GeometryCollection passes NULL. Let's create an item
517  according to wkb_type on every interation step.
518  */
519  uint32 wkb_type;
520  switch ((Gcalc_function::shape_type) uint4korr(opres))
521  {
522  case Gcalc_function::shape_point: wkb_type= wkb_point; break;
523  case Gcalc_function::shape_line: wkb_type= wkb_linestring; break;
524  case Gcalc_function::shape_polygon: wkb_type= wkb_polygon; break;
525  default:
526  /*
527  Something has gone really wrong while performing a spatial operation.
528  For now we'll return an error.
529  TODO: should be properly fixed.
530  */
531  my_error(ER_NOT_SUPPORTED_YET, MYF(0), "spatial self-intersecting operands");
532  return 0;
533  };
534  if (!(item= create_by_typeid(&buffer, wkb_type)))
535  return 0;
536  }
537 
538  bin->q_append((char) wkb_ndr);
539  bin->q_append((uint32) item->get_class_info()->m_type_id);
540 
541  if (!(item_len= item->init_from_opresult(bin, opres, opres_length)))
542  return 0;
543  opres+= item_len;
544  opres_length-= item_len;
545  n_items++;
546  }
547  bin->write_at_position(n_items_offs, n_items);
548  return (uint) (opres - opres_orig);
549 }
550 
551 
552 /***************************** Point *******************************/
553 
554 uint32 Gis_point::get_data_size() const
555 {
556  return POINT_DATA_SIZE;
557 }
558 
559 
560 bool Gis_point::init_from_wkt(Gis_read_stream *trs, String *wkb)
561 {
562  double x, y;
563  if (trs->get_next_number(&x) || trs->get_next_number(&y) ||
564  wkb->reserve(POINT_DATA_SIZE))
565  return true;
566  wkb->q_append(x);
567  wkb->q_append(y);
568  return false;
569 }
570 
571 
572 uint Gis_point::init_from_wkb(const char *wkb, uint len,
573  wkbByteOrder bo, String *res)
574 {
575  double x, y;
576  if (len < POINT_DATA_SIZE || res->reserve(POINT_DATA_SIZE))
577  return 0;
578  x= wkb_get_double(wkb, bo);
579  y= wkb_get_double(wkb + SIZEOF_STORED_DOUBLE, bo);
580  res->q_append(x);
581  res->q_append(y);
582  return POINT_DATA_SIZE;
583 }
584 
585 
586 bool Gis_point::get_data_as_wkt(String *txt, wkb_parser *wkb) const
587 {
588  point_xy p;
589  if (wkb->scan_xy(&p))
590  return true;
591  if (txt->reserve(MAX_DIGITS_IN_DOUBLE * 2 + 1))
592  return true;
593  txt->qs_append(p.x);
594  txt->qs_append(' ');
595  txt->qs_append(p.y);
596  return false;
597 }
598 
599 
600 bool Gis_point::get_mbr(MBR *mbr, wkb_parser *wkb) const
601 {
602  point_xy p;
603  if (wkb->scan_xy(&p))
604  return true;
605  mbr->add_xy(p);
606  return false;
607 }
608 
609 
610 int Gis_point::store_shapes(Gcalc_shape_transporter *trn,
611  Gcalc_shape_status *st) const
612 {
613  if (trn->skip_point())
614  return 0;
615  wkb_parser wkb(&m_wkb_data);
616  point_xy p;
617  return wkb.scan_xy(&p) || trn->single_point(st, p.x, p.y);
618 }
619 
620 
621 const Geometry::Class_info *Gis_point::get_class_info() const
622 {
623  return &point_class;
624 }
625 
626 
627 /***************************** LineString *******************************/
628 
629 uint32 Gis_line_string::get_data_size() const
630 {
631  uint32 n_points;
632  wkb_parser wkb(&m_wkb_data);
633  if (wkb.scan_n_points_and_check_data(&n_points))
634  return GET_SIZE_ERROR;
635 
636  return 4 + n_points * POINT_DATA_SIZE;
637 }
638 
639 
640 bool Gis_line_string::init_from_wkt(Gis_read_stream *trs, String *wkb)
641 {
642  uint32 n_points= 0;
643  uint32 np_pos= wkb->length();
644  Gis_point p;
645 
646  if (wkb->reserve(4, 512))
647  return true;
648  wkb->length(wkb->length()+4); // Reserve space for points
649 
650  for (;;)
651  {
652  if (p.init_from_wkt(trs, wkb))
653  return true;
654  n_points++;
655  if (trs->skip_char(',')) // Didn't find ','
656  break;
657  }
658  if (n_points < 1)
659  {
660  trs->set_error_msg("Too few points in LINESTRING");
661  return true;
662  }
663  wkb->write_at_position(np_pos, n_points);
664  return false;
665 }
666 
667 
668 uint Gis_line_string::init_from_wkb(const char *wkb, uint len,
669  wkbByteOrder bo, String *res)
670 {
671  uint32 n_points, proper_length;
672  const char *wkb_end;
673  Gis_point p;
674 
675  if (len < 4 ||
676  (n_points= wkb_get_uint(wkb, bo)) < 1 ||
677  n_points > max_n_points)
678  return 0;
679  proper_length= 4 + n_points * POINT_DATA_SIZE;
680 
681  if (len < proper_length || res->reserve(proper_length))
682  return 0;
683 
684  res->q_append(n_points);
685  wkb_end= wkb + proper_length;
686  for (wkb+= 4; wkb<wkb_end; wkb+= POINT_DATA_SIZE)
687  {
688  if (!p.init_from_wkb(wkb, POINT_DATA_SIZE, bo, res))
689  return 0;
690  }
691 
692  return proper_length;
693 }
694 
695 
696 bool Gis_line_string::get_data_as_wkt(String *txt, wkb_parser *wkb) const
697 {
698  uint32 n_points;
699  if (wkb->scan_n_points_and_check_data(&n_points) ||
700  txt->reserve(((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points))
701  return true;
702 
703  while (n_points--)
704  {
705  point_xy p;
706  wkb->scan_xy_unsafe(&p);
707  txt->qs_append(p.x);
708  txt->qs_append(' ');
709  txt->qs_append(p.y);
710  txt->qs_append(',');
711  }
712  txt->length(txt->length() - 1); // Remove end ','
713  return false;
714 }
715 
716 
717 bool Gis_line_string::get_mbr(MBR *mbr, wkb_parser *wkb) const
718 {
719  return get_mbr_for_points(mbr, wkb, 0);
720 }
721 
722 
723 int Gis_line_string::geom_length(double *len) const
724 {
725  uint32 n_points;
726  wkb_parser wkb(&m_wkb_data);
727 
728  *len= 0; // In case of errors
729  if (wkb.scan_n_points_and_check_data(&n_points))
730  return 1;
731 
732  point_xy prev;
733  wkb.scan_xy_unsafe(&prev);
734  while (--n_points)
735  {
736  point_xy p;
737  wkb.scan_xy_unsafe(&p);
738  *len+= prev.distance(p);
739  prev= p;
740  }
741  return 0;
742 }
743 
744 
745 int Gis_line_string::is_closed(int *closed) const
746 {
747  uint32 n_points;
748  wkb_parser wkb(&m_wkb_data);
749 
750  if (wkb.scan_n_points_and_check_data(&n_points))
751  return 1;
752 
753  if (n_points == 1)
754  {
755  *closed=1;
756  return 0;
757  }
758 
759  point_xy p1, p2;
760 
761  /* Get first point. */
762  wkb.scan_xy_unsafe(&p1);
763 
764  /* Get last point. */
765  wkb.skip_unsafe((n_points - 2) * POINT_DATA_SIZE);
766  wkb.scan_xy_unsafe(&p2);
767 
768  *closed= p1.eq(p2);
769  return 0;
770 }
771 
772 
773 int Gis_line_string::num_points(uint32 *n_points) const
774 {
775  wkb_parser wkb(&m_wkb_data);
776  return wkb.scan_uint4(n_points) ? 1 : 0;
777 }
778 
779 
780 int Gis_line_string::start_point(String *result) const
781 {
782  uint32 n_points;
783  wkb_parser wkb(&m_wkb_data);
784  if (wkb.scan_n_points_and_check_data(&n_points))
785  return 1;
786  return create_point(result, &wkb);
787 }
788 
789 
790 int Gis_line_string::end_point(String *result) const
791 {
792  uint32 n_points;
793  wkb_parser wkb(&m_wkb_data);
794  if (wkb.scan_n_points_and_check_data(&n_points))
795  return 1;
796  wkb.skip_unsafe((n_points - 1) * POINT_DATA_SIZE);
797  return create_point(result, &wkb);
798 }
799 
800 
801 int Gis_line_string::point_n(uint32 num, String *result) const
802 {
803  uint32 n_points;
804  wkb_parser wkb(&m_wkb_data);
805  if (num < 1 ||
806  wkb.scan_n_points_and_check_data(&n_points) ||
807  num > n_points)
808  return 1;
809  wkb.skip_unsafe((num - 1) * POINT_DATA_SIZE);
810  return create_point(result, &wkb);
811 }
812 
813 
814 int Gis_line_string::store_shapes(Gcalc_shape_transporter *trn,
815  Gcalc_shape_status *st) const
816 {
817  uint32 n_points;
818  wkb_parser wkb(&m_wkb_data);
819 
820  if (trn->skip_line_string())
821  return 0;
822 
823  if (wkb.scan_n_points_and_check_data(&n_points))
824  return 1;
825 
826  trn->start_line(st);
827  while (n_points--)
828  {
829  point_xy p;
830  wkb.scan_xy_unsafe(&p);
831  if (trn->add_point(st, p.x, p.y))
832  return 1;
833  }
834  return trn->complete_line(st);
835 }
836 
837 
838 const Geometry::Class_info *Gis_line_string::get_class_info() const
839 {
840  return &linestring_class;
841 }
842 
843 
844 /***************************** Polygon *******************************/
845 
846 uint32 Gis_polygon::get_data_size() const
847 {
848  uint32 n_linear_rings;
849  wkb_parser wkb(&m_wkb_data);
850 
851  if (wkb.scan_non_zero_uint4(&n_linear_rings))
852  return GET_SIZE_ERROR;
853 
854  while (n_linear_rings--)
855  {
856  uint32 n_points;
857  if (wkb.scan_n_points_and_check_data(&n_points))
858  return GET_SIZE_ERROR;
859  wkb.skip_unsafe(n_points * POINT_DATA_SIZE);
860  }
861  return (uint32) (wkb.data() - m_wkb_data.data());
862 }
863 
864 
865 bool Gis_polygon::init_from_wkt(Gis_read_stream *trs, String *wkb)
866 {
867  uint32 n_linear_rings= 0;
868  uint32 lr_pos= wkb->length();
869  int closed;
870 
871  if (wkb->reserve(4, 512))
872  return true;
873  wkb->length(wkb->length()+4); // Reserve space for points
874  for (;;)
875  {
876  Gis_line_string ls;
877  uint32 ls_pos=wkb->length();
878  if (trs->check_next_symbol('(') ||
879  ls.init_from_wkt(trs, wkb) ||
880  trs->check_next_symbol(')'))
881  return true;
882 
883  ls.set_data_ptr(wkb->ptr() + ls_pos, wkb->length() - ls_pos);
884  if (ls.is_closed(&closed) || !closed)
885  {
886  trs->set_error_msg("POLYGON's linear ring isn't closed");
887  return true;
888  }
889  n_linear_rings++;
890  if (trs->skip_char(',')) // Didn't find ','
891  break;
892  }
893  wkb->write_at_position(lr_pos, n_linear_rings);
894  return false;
895 }
896 
897 
898 uint Gis_polygon::init_from_opresult(String *bin,
899  const char *opres, uint opres_length)
900 {
901  const char *opres_orig= opres;
902  const char *opres_end= opres + opres_length;
903  uint32 position= bin->length();
904  uint32 poly_shapes= 0;
905 
906  if (bin->reserve(4, 512))
907  return 0;
908  bin->q_append(poly_shapes);
909 
910  while (opres < opres_end)
911  {
912  uint32 n_points, proper_length;
913  const char *op_end, *p1_position;
914  Gis_point p;
915  Gcalc_function::shape_type st;
916 
917  st= (Gcalc_function::shape_type) uint4korr(opres);
918  if (poly_shapes && st != Gcalc_function::shape_hole)
919  break;
920  poly_shapes++;
921  n_points= uint4korr(opres + 4) + 1; /* skip shape type id */
922  proper_length= 4 + n_points * POINT_DATA_SIZE;
923 
924  if (bin->reserve(proper_length, 512))
925  return 0;
926 
927  bin->q_append(n_points);
928  op_end= opres + 8 + (n_points-1) * 8 * 2;
929  p1_position= (opres+= 8);
930  for (; opres<op_end; opres+= POINT_DATA_SIZE)
931  {
932  if (!p.init_from_wkb(opres, POINT_DATA_SIZE, wkb_ndr, bin))
933  return 0;
934  }
935  if (!p.init_from_wkb(p1_position, POINT_DATA_SIZE, wkb_ndr, bin))
936  return 0;
937  }
938 
939  bin->write_at_position(position, poly_shapes);
940 
941  return (uint) (opres - opres_orig);
942 }
943 
944 
945 uint Gis_polygon::init_from_wkb(const char *wkb, uint len, wkbByteOrder bo,
946  String *res)
947 {
948  uint32 n_linear_rings;
949  const char *wkb_orig= wkb;
950 
951  if (len < 4)
952  return 0;
953 
954  if (0 == (n_linear_rings= wkb_get_uint(wkb, bo)) ||
955  res->reserve(4, 512))
956  return 0;
957  wkb+= 4;
958  len-= 4;
959  res->q_append(n_linear_rings);
960 
961  while (n_linear_rings--)
962  {
963  Gis_line_string ls;
964  uint32 ls_pos= res->length();
965  int ls_len;
966  int closed;
967 
968  if (!(ls_len= ls.init_from_wkb(wkb, len, bo, res)))
969  return 0;
970 
971  ls.set_data_ptr(res->ptr() + ls_pos, res->length() - ls_pos);
972 
973  if (ls.is_closed(&closed) || !closed)
974  return 0;
975  wkb+= ls_len;
976  }
977 
978  return (uint) (wkb - wkb_orig);
979 }
980 
981 
982 bool Gis_polygon::get_data_as_wkt(String *txt, wkb_parser *wkb) const
983 {
984  uint32 n_linear_rings;
985 
986  if (wkb->scan_non_zero_uint4(&n_linear_rings))
987  return true;
988 
989  while (n_linear_rings--)
990  {
991  uint32 n_points;
992  if (wkb->scan_n_points_and_check_data(&n_points) ||
993  txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points))
994  return true;
995  txt->qs_append('(');
996  append_points(txt, n_points, wkb, 0);
997  (*txt) [txt->length() - 1]= ')'; // Replace end ','
998  txt->qs_append(',');
999  }
1000  txt->length(txt->length() - 1); // Remove end ','
1001  return false;
1002 }
1003 
1004 
1005 bool Gis_polygon::get_mbr(MBR *mbr, wkb_parser *wkb) const
1006 {
1007  uint32 n_linear_rings;
1008 
1009  if (wkb->scan_non_zero_uint4(&n_linear_rings))
1010  return true;
1011 
1012  while (n_linear_rings--)
1013  {
1014  if (get_mbr_for_points(mbr, wkb, 0))
1015  return true;
1016  }
1017  return false;
1018 }
1019 
1020 
1021 bool Gis_polygon::area(double *ar, wkb_parser *wkb) const
1022 {
1023  uint32 n_linear_rings;
1024  double result= -1.0;
1025 
1026  if (wkb->scan_non_zero_uint4(&n_linear_rings))
1027  return true;
1028 
1029  while (n_linear_rings--)
1030  {
1031  double lr_area= 0;
1032  uint32 n_points;
1033 
1034  if (wkb->scan_n_points_and_check_data(&n_points))
1035  return true;
1036  point_xy prev;
1037  wkb->scan_xy_unsafe(&prev);
1038 
1039  while (--n_points) // One point is already read
1040  {
1041  point_xy p;
1042  wkb->scan_xy_unsafe(&p);
1043  lr_area+= (prev.x + p.x) * (prev.y - p.y);
1044  prev= p;
1045  }
1046  lr_area= fabs(lr_area)/2;
1047  if (result == -1.0)
1048  result= lr_area;
1049  else
1050  result-= lr_area;
1051  }
1052  *ar= fabs(result);
1053  return false;
1054 }
1055 
1056 
1057 int Gis_polygon::exterior_ring(String *result) const
1058 {
1059  uint32 n_points, n_linear_rings, length;
1060  wkb_parser wkb(&m_wkb_data);
1061 
1062  if (wkb.scan_non_zero_uint4(&n_linear_rings) ||
1063  wkb.scan_n_points_and_check_data(&n_points))
1064  return 1;
1065  length= n_points * POINT_DATA_SIZE;
1066  if (result->reserve(1 + 4 + 4 + length))
1067  return 1;
1068 
1069  result->q_append((char) wkb_ndr);
1070  result->q_append((uint32) wkb_linestring);
1071  result->q_append(n_points);
1072  result->q_append(wkb.data(), length);
1073  return 0;
1074 }
1075 
1076 
1077 int Gis_polygon::num_interior_ring(uint32 *n_int_rings) const
1078 {
1079  wkb_parser wkb(&m_wkb_data);
1080  if (wkb.scan_non_zero_uint4(n_int_rings))
1081  return 1;
1082  *n_int_rings-= 1;
1083  return 0;
1084 }
1085 
1086 
1087 int Gis_polygon::interior_ring_n(uint32 num, String *result) const
1088 {
1089  wkb_parser wkb(&m_wkb_data);
1090  uint32 n_linear_rings;
1091  uint32 n_points;
1092  uint32 points_size;
1093 
1094  if (num < 1 ||
1095  wkb.scan_non_zero_uint4(&n_linear_rings) ||
1096  num >= n_linear_rings)
1097  return 1;
1098 
1099  while (num--)
1100  {
1101  if (wkb.scan_n_points_and_check_data(&n_points))
1102  return 1;
1103  wkb.skip_unsafe(n_points * POINT_DATA_SIZE);
1104  }
1105  if (wkb.scan_n_points_and_check_data(&n_points))
1106  return 1;
1107  points_size= n_points * POINT_DATA_SIZE;
1108  if (result->reserve(1 + 4 + 4 + points_size))
1109  return 1;
1110 
1111  result->q_append((char) wkb_ndr);
1112  result->q_append((uint32) wkb_linestring);
1113  result->q_append(n_points);
1114  result->q_append(wkb.data(), points_size);
1115  return 0;
1116 }
1117 
1118 
1119 bool Gis_polygon::centroid_xy(point_xy *p) const
1120 {
1121  uint32 n_linear_rings;
1122  double UNINIT_VAR(res_area);
1123  point_xy res(0, 0); // Initialized only to make compiler happy
1124  wkb_parser wkb(&m_wkb_data);
1125  bool first_loop= 1;
1126 
1127  if (wkb.scan_non_zero_uint4(&n_linear_rings))
1128  return true;
1129 
1130  while (n_linear_rings--)
1131  {
1132  uint32 n_points, org_n_points;
1133  double cur_area= 0;
1134  point_xy prev, cur(0, 0);
1135 
1136  if (wkb.scan_n_points_and_check_data(&n_points))
1137  return true;
1138 
1139  org_n_points= n_points - 1;
1140  wkb.scan_xy_unsafe(&prev);
1141 
1142  while (--n_points) // One point is already read
1143  {
1144  point_xy tmp;
1145  wkb.scan_xy_unsafe(&tmp);
1146  cur_area+= (prev.x + tmp.x) * (prev.y - tmp.y);
1147  cur.x+= tmp.x;
1148  cur.y+= tmp.y;
1149  prev= tmp;
1150  }
1151  cur_area= fabs(cur_area) / 2;
1152  cur.x= cur.x / org_n_points;
1153  cur.y= cur.y / org_n_points;
1154 
1155  if (!first_loop)
1156  {
1157  double d_area= fabs(res_area - cur_area);
1158  res.x= (res_area * res.x - cur_area * cur.x) / d_area;
1159  res.y= (res_area * res.y - cur_area * cur.y) / d_area;
1160  }
1161  else
1162  {
1163  first_loop= 0;
1164  res_area= cur_area;
1165  res= cur;
1166  }
1167  }
1168 
1169  *p= res;
1170  return false;
1171 }
1172 
1173 
1174 int Gis_polygon::centroid(String *result) const
1175 {
1176  point_xy p;
1177  if (centroid_xy(&p))
1178  return 1;
1179  return create_point(result, p);
1180 }
1181 
1182 
1183 int Gis_polygon::store_shapes(Gcalc_shape_transporter *trn,
1184  Gcalc_shape_status *st) const
1185 {
1186  uint32 n_linear_rings;
1187  wkb_parser wkb(&m_wkb_data);
1188 
1189  if (trn->skip_poly())
1190  return 0;
1191 
1192  if (trn->start_poly(st))
1193  return 1;
1194 
1195  if (wkb.scan_non_zero_uint4(&n_linear_rings))
1196  return 1;
1197 
1198  while (n_linear_rings--)
1199  {
1200  uint32 n_points;
1201 
1202  if (wkb.scan_n_points_and_check_data(&n_points))
1203  return 1;
1204 
1205  trn->start_ring(st);
1206  while (--n_points)
1207  {
1208  point_xy p;
1209  wkb.scan_xy_unsafe(&p);
1210  if (trn->add_point(st, p.x, p.y))
1211  return 1;
1212  }
1213  wkb.skip_unsafe(POINT_DATA_SIZE); // Skip the last point in the ring.
1214  trn->complete_ring(st);
1215  }
1216 
1217  trn->complete_poly(st);
1218  return 0;
1219 }
1220 
1221 
1222 const Geometry::Class_info *Gis_polygon::get_class_info() const
1223 {
1224  return &polygon_class;
1225 }
1226 
1227 
1228 /***************************** MultiPoint *******************************/
1229 
1230 uint32 Gis_multi_point::get_data_size() const
1231 {
1232  uint32 n_points;
1233  wkb_parser wkb(&m_wkb_data);
1234  if (wkb.scan_n_points_and_check_data(&n_points, WKB_HEADER_SIZE))
1235  return GET_SIZE_ERROR;
1236 
1237  return 4 + n_points * (POINT_DATA_SIZE + WKB_HEADER_SIZE);
1238 }
1239 
1240 
1241 bool Gis_multi_point::init_from_wkt(Gis_read_stream *trs, String *wkb)
1242 {
1243  uint32 n_points= 0;
1244  uint32 np_pos= wkb->length();
1245  Gis_point p;
1246 
1247  if (wkb->reserve(4, 512))
1248  return true;
1249  wkb->length(wkb->length()+4); // Reserve space for points
1250 
1251  for (;;)
1252  {
1253  if (wkb->reserve(1 + 4, 512))
1254  return 1;
1255  wkb->q_append((char) wkb_ndr);
1256  wkb->q_append((uint32) wkb_point);
1257  if (p.init_from_wkt(trs, wkb))
1258  return true;
1259  n_points++;
1260  if (trs->skip_char(',')) // Didn't find ','
1261  break;
1262  }
1263  wkb->write_at_position(np_pos, n_points); // Store number of found points
1264  return false;
1265 }
1266 
1267 
1268 uint Gis_multi_point::init_from_opresult(String *bin,
1269  const char *opres, uint opres_length)
1270 {
1271  uint bin_size, n_points;
1272  Gis_point p;
1273  const char *opres_end;
1274 
1275  n_points= opres_length / (4 + 8 * 2);
1276  bin_size= n_points * (WKB_HEADER_SIZE + POINT_DATA_SIZE) + 4;
1277 
1278  if (bin->reserve(bin_size, 512))
1279  return 0;
1280 
1281  bin->q_append(n_points);
1282  opres_end= opres + opres_length;
1283  for (; opres < opres_end; opres+= (4 + 8*2))
1284  {
1285  bin->q_append((char)wkb_ndr);
1286  bin->q_append((uint32)wkb_point);
1287  if (!p.init_from_wkb(opres + 4, POINT_DATA_SIZE, wkb_ndr, bin))
1288  return 0;
1289  }
1290  return opres_length;
1291 }
1292 
1293 
1294 uint Gis_multi_point::init_from_wkb(const char *wkb, uint len, wkbByteOrder bo,
1295  String *res)
1296 {
1297  uint32 n_points;
1298  uint proper_size;
1299  Gis_point p;
1300  const char *wkb_end;
1301 
1302  if (len < 4 ||
1303  (n_points= wkb_get_uint(wkb, bo)) > max_n_points)
1304  return 0;
1305  proper_size= 4 + n_points * (WKB_HEADER_SIZE + POINT_DATA_SIZE);
1306 
1307  if (len < proper_size || res->reserve(proper_size))
1308  return 0;
1309 
1310  res->q_append(n_points);
1311  wkb_end= wkb + proper_size;
1312  for (wkb+=4; wkb < wkb_end; wkb+= (WKB_HEADER_SIZE + POINT_DATA_SIZE))
1313  {
1314  res->q_append((char)wkb_ndr);
1315  res->q_append((uint32)wkb_point);
1316  if (!p.init_from_wkb(wkb + WKB_HEADER_SIZE,
1317  POINT_DATA_SIZE, (wkbByteOrder) wkb[0], res))
1318  return 0;
1319  }
1320  return proper_size;
1321 }
1322 
1323 
1324 bool Gis_multi_point::get_data_as_wkt(String *txt, wkb_parser *wkb) const
1325 {
1326  uint32 n_points;
1327 
1328  if (wkb->scan_n_points_and_check_data(&n_points, WKB_HEADER_SIZE) ||
1329  txt->reserve(((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points))
1330  return true;
1331 
1332  append_points(txt, n_points, wkb, WKB_HEADER_SIZE);
1333  txt->length(txt->length()-1); // Remove end ','
1334  return false;
1335 }
1336 
1337 
1338 bool Gis_multi_point::get_mbr(MBR *mbr, wkb_parser *wkb) const
1339 {
1340  return get_mbr_for_points(mbr, wkb, WKB_HEADER_SIZE);
1341 }
1342 
1343 
1344 int Gis_multi_point::num_geometries(uint32 *num) const
1345 {
1346  wkb_parser wkb(&m_wkb_data);
1347  return wkb.scan_non_zero_uint4(num) ? 1 : 0;
1348 }
1349 
1350 
1351 int Gis_multi_point::geometry_n(uint32 num, String *result) const
1352 {
1353  uint32 n_points;
1354  wkb_parser wkb(&m_wkb_data);
1355 
1356  if (num < 1 ||
1357  wkb.scan_n_points_and_check_data(&n_points, WKB_HEADER_SIZE) ||
1358  num > n_points ||
1359  result->reserve(WKB_HEADER_SIZE + POINT_DATA_SIZE))
1360  return 1;
1361  wkb.skip_unsafe((num - 1) * (WKB_HEADER_SIZE + POINT_DATA_SIZE));
1362 
1363  result->q_append(wkb.data(), WKB_HEADER_SIZE + POINT_DATA_SIZE);
1364  return 0;
1365 }
1366 
1367 
1368 int Gis_multi_point::store_shapes(Gcalc_shape_transporter *trn,
1369  Gcalc_shape_status *st) const
1370 {
1371  if (trn->skip_point())
1372  return 0;
1373  Gis_point pt;
1374  return collection_store_shapes(trn, st, &pt);
1375 }
1376 
1377 
1378 const Geometry::Class_info *Gis_multi_point::get_class_info() const
1379 {
1380  return &multipoint_class;
1381 }
1382 
1383 
1384 /***************************** MultiLineString *******************************/
1385 
1386 uint32 Gis_multi_line_string::get_data_size() const
1387 {
1388  uint32 n_line_strings;
1389  wkb_parser wkb(&m_wkb_data);
1390 
1391  if (wkb.scan_non_zero_uint4(&n_line_strings))
1392  return GET_SIZE_ERROR;
1393 
1394  while (n_line_strings--)
1395  {
1396  uint32 n_points;
1397 
1398  if (wkb.skip_wkb_header() ||
1399  wkb.scan_n_points_and_check_data(&n_points))
1400  return GET_SIZE_ERROR;
1401 
1402  wkb.skip_unsafe(n_points * POINT_DATA_SIZE);
1403  }
1404  return (uint32) (wkb.data() - m_wkb_data.data());
1405 }
1406 
1407 
1408 bool Gis_multi_line_string::init_from_wkt(Gis_read_stream *trs, String *wkb)
1409 {
1410  uint32 n_line_strings= 0;
1411  uint32 ls_pos= wkb->length();
1412 
1413  if (wkb->reserve(4, 512))
1414  return true;
1415  wkb->length(wkb->length()+4); // Reserve space for points
1416 
1417  for (;;)
1418  {
1419  Gis_line_string ls;
1420 
1421  if (wkb->reserve(1 + 4, 512))
1422  return true;
1423  wkb->q_append((char) wkb_ndr); wkb->q_append((uint32) wkb_linestring);
1424 
1425  if (trs->check_next_symbol('(') ||
1426  ls.init_from_wkt(trs, wkb) ||
1427  trs->check_next_symbol(')'))
1428  return true;
1429  n_line_strings++;
1430  if (trs->skip_char(',')) // Didn't find ','
1431  break;
1432  }
1433  wkb->write_at_position(ls_pos, n_line_strings);
1434  return false;
1435 }
1436 
1437 
1438 uint Gis_multi_line_string::init_from_opresult(String *bin,
1439  const char *opres,
1440  uint opres_length)
1441 {
1442  Gis_line_string item;
1443  return collection_init_from_opresult(bin, opres, opres_length, &item);
1444 }
1445 
1446 
1447 uint Gis_multi_line_string::init_from_wkb(const char *wkb, uint len,
1448  wkbByteOrder bo, String *res)
1449 {
1450  uint32 n_line_strings;
1451  const char *wkb_orig= wkb;
1452 
1453  if (len < 4 ||
1454  (n_line_strings= wkb_get_uint(wkb, bo))< 1)
1455  return 0;
1456 
1457  if (res->reserve(4, 512))
1458  return 0;
1459  res->q_append(n_line_strings);
1460 
1461  wkb+= 4;
1462  while (n_line_strings--)
1463  {
1464  Gis_line_string ls;
1465  int ls_len;
1466 
1467  if ((len < WKB_HEADER_SIZE) ||
1468  res->reserve(WKB_HEADER_SIZE, 512))
1469  return 0;
1470 
1471  res->q_append((char) wkb_ndr);
1472  res->q_append((uint32) wkb_linestring);
1473 
1474  if (!(ls_len= ls.init_from_wkb(wkb + WKB_HEADER_SIZE, len,
1475  (wkbByteOrder) wkb[0], res)))
1476  return 0;
1477  ls_len+= WKB_HEADER_SIZE;;
1478  wkb+= ls_len;
1479  len-= ls_len;
1480  }
1481  return (uint) (wkb - wkb_orig);
1482 }
1483 
1484 
1485 bool Gis_multi_line_string::get_data_as_wkt(String *txt, wkb_parser *wkb) const
1486 {
1487  uint32 n_line_strings;
1488 
1489  if (wkb->scan_non_zero_uint4(&n_line_strings))
1490  return true;
1491 
1492  while (n_line_strings--)
1493  {
1494  uint32 n_points;
1495 
1496  if (wkb->skip_wkb_header() ||
1497  wkb->scan_n_points_and_check_data(&n_points) ||
1498  txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points))
1499  return true;
1500  txt->qs_append('(');
1501  append_points(txt, n_points, wkb, 0);
1502  (*txt) [txt->length() - 1]= ')';
1503  txt->qs_append(',');
1504  }
1505  txt->length(txt->length() - 1);
1506  return false;
1507 }
1508 
1509 
1510 bool Gis_multi_line_string::get_mbr(MBR *mbr, wkb_parser *wkb) const
1511 {
1512  uint32 n_line_strings;
1513 
1514  if (wkb->scan_non_zero_uint4(&n_line_strings))
1515  return true;
1516 
1517  while (n_line_strings--)
1518  {
1519  if (wkb->skip_wkb_header() ||
1520  get_mbr_for_points(mbr, wkb, 0))
1521  return true;
1522  }
1523  return false;
1524 }
1525 
1526 
1527 int Gis_multi_line_string::num_geometries(uint32 *num) const
1528 {
1529  wkb_parser wkb(&m_wkb_data);
1530  return wkb.scan_non_zero_uint4(num) ? 1 : 0;
1531 }
1532 
1533 
1534 int Gis_multi_line_string::geometry_n(uint32 num, String *result) const
1535 {
1536  uint32 n_line_strings, n_points, length;
1537  wkb_parser wkb(&m_wkb_data);
1538 
1539  if (wkb.scan_non_zero_uint4(&n_line_strings))
1540  return 1;
1541 
1542  if ((num > n_line_strings) || (num < 1))
1543  return 1;
1544 
1545  for (;;)
1546  {
1547  if (wkb.skip_wkb_header() ||
1548  wkb.scan_n_points_and_check_data(&n_points))
1549  return 1;
1550  length= POINT_DATA_SIZE * n_points;
1551  if (!--num)
1552  break;
1553  wkb.skip_unsafe(length);
1554  }
1555  return result->append(wkb.data() - 4 - WKB_HEADER_SIZE,
1556  length + 4 + WKB_HEADER_SIZE, (uint32) 0);
1557 }
1558 
1559 
1560 int Gis_multi_line_string::geom_length(double *len) const
1561 {
1562  uint32 n_line_strings;
1563  wkb_parser wkb(&m_wkb_data);
1564 
1565  if (wkb.scan_non_zero_uint4(&n_line_strings))
1566  return 1;
1567 
1568  *len=0;
1569  while (n_line_strings--)
1570  {
1571  double ls_len;
1572  Gis_line_string ls;
1573  if (wkb.skip_wkb_header())
1574  return 1;
1575  ls.set_data_ptr(&wkb);
1576  if (ls.geom_length(&ls_len))
1577  return 1;
1578  *len+= ls_len;
1579  /*
1580  We know here that ls was ok, so we can call the trivial function
1581  Gis_line_string::get_data_size without error checking
1582  */
1583  wkb.skip_unsafe(ls.get_data_size());
1584  }
1585  return 0;
1586 }
1587 
1588 
1589 int Gis_multi_line_string::is_closed(int *closed) const
1590 {
1591  uint32 n_line_strings;
1592  wkb_parser wkb(&m_wkb_data);
1593 
1594  if (wkb.scan_non_zero_uint4(&n_line_strings))
1595  return 1;
1596 
1597  while (n_line_strings--)
1598  {
1599  Gis_line_string ls;
1600  if (wkb.skip_wkb_header())
1601  return 1;
1602  ls.set_data_ptr(&wkb);
1603  if (ls.is_closed(closed))
1604  return 1;
1605  if (!*closed)
1606  return 0;
1607  wkb.skip_unsafe(ls.get_data_size());
1608  }
1609  return 0;
1610 }
1611 
1612 
1613 int Gis_multi_line_string::store_shapes(Gcalc_shape_transporter *trn,
1614  Gcalc_shape_status *st) const
1615 {
1616  if (trn->skip_line_string())
1617  return 0;
1618  Gis_line_string ls;
1619  return collection_store_shapes(trn, st, &ls);
1620 }
1621 
1622 
1623 const Geometry::Class_info *Gis_multi_line_string::get_class_info() const
1624 {
1625  return &multilinestring_class;
1626 }
1627 
1628 
1629 /***************************** MultiPolygon *******************************/
1630 
1631 uint32 Gis_multi_polygon::get_data_size() const
1632 {
1633  uint32 n_polygons;
1634  wkb_parser wkb(&m_wkb_data);
1635 
1636  if (wkb.scan_non_zero_uint4(&n_polygons))
1637  return GET_SIZE_ERROR;
1638 
1639  while (n_polygons--)
1640  {
1641  uint32 n_linear_rings;
1642  if (wkb.skip_wkb_header() ||
1643  wkb.scan_non_zero_uint4(&n_linear_rings))
1644  return GET_SIZE_ERROR;
1645 
1646  while (n_linear_rings--)
1647  {
1648  uint32 n_points;
1649 
1650  if (wkb.scan_n_points_and_check_data(&n_points))
1651  return GET_SIZE_ERROR;
1652 
1653  wkb.skip_unsafe(n_points * POINT_DATA_SIZE);
1654  }
1655  }
1656  return (uint32) (wkb.data() - m_wkb_data.data());
1657 }
1658 
1659 
1660 bool Gis_multi_polygon::init_from_wkt(Gis_read_stream *trs, String *wkb)
1661 {
1662  uint32 n_polygons= 0;
1663  int np_pos= wkb->length();
1664  Gis_polygon p;
1665 
1666  if (wkb->reserve(4, 512))
1667  return true;
1668  wkb->length(wkb->length()+4); // Reserve space for points
1669 
1670  for (;;)
1671  {
1672  if (wkb->reserve(1 + 4, 512))
1673  return true;
1674  wkb->q_append((char) wkb_ndr);
1675  wkb->q_append((uint32) wkb_polygon);
1676 
1677  if (trs->check_next_symbol('(') ||
1678  p.init_from_wkt(trs, wkb) ||
1679  trs->check_next_symbol(')'))
1680  return true;
1681  n_polygons++;
1682  if (trs->skip_char(',')) // Didn't find ','
1683  break;
1684  }
1685  wkb->write_at_position(np_pos, n_polygons);
1686  return false;
1687 }
1688 
1689 
1690 uint Gis_multi_polygon::init_from_wkb(const char *wkb, uint len,
1691  wkbByteOrder bo, String *res)
1692 {
1693  uint32 n_poly;
1694  const char *wkb_orig= wkb;
1695 
1696  if (len < 4)
1697  return 0;
1698  n_poly= wkb_get_uint(wkb, bo);
1699 
1700  if (res->reserve(4, 512))
1701  return 0;
1702  res->q_append(n_poly);
1703 
1704  wkb+=4;
1705  while (n_poly--)
1706  {
1707  Gis_polygon p;
1708  int p_len;
1709 
1710  if (len < WKB_HEADER_SIZE ||
1711  res->reserve(WKB_HEADER_SIZE, 512))
1712  return 0;
1713  res->q_append((char) wkb_ndr);
1714  res->q_append((uint32) wkb_polygon);
1715 
1716  if (!(p_len= p.init_from_wkb(wkb + WKB_HEADER_SIZE, len,
1717  (wkbByteOrder) wkb[0], res)))
1718  return 0;
1719  p_len+= WKB_HEADER_SIZE;
1720  wkb+= p_len;
1721  len-= p_len;
1722  }
1723  return (uint) (wkb - wkb_orig);
1724 }
1725 
1726 
1727 uint Gis_multi_polygon::init_from_opresult(String *bin,
1728  const char *opres, uint opres_length)
1729 {
1730  Gis_polygon item;
1731  return collection_init_from_opresult(bin, opres, opres_length, &item);
1732 }
1733 
1734 
1735 bool Gis_multi_polygon::get_data_as_wkt(String *txt, wkb_parser *wkb) const
1736 {
1737  uint32 n_polygons;
1738 
1739  if (wkb->scan_non_zero_uint4(&n_polygons))
1740  return true;
1741 
1742  while (n_polygons--)
1743  {
1744  uint32 n_linear_rings;
1745 
1746  if (wkb->skip_wkb_header() ||
1747  wkb->scan_non_zero_uint4(&n_linear_rings) ||
1748  txt->reserve(1, 512))
1749  return true;
1750  txt->q_append('(');
1751 
1752  while (n_linear_rings--)
1753  {
1754  uint32 n_points;
1755  if (wkb->scan_n_points_and_check_data(&n_points) ||
1756  txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points, 512))
1757  return true;
1758  txt->qs_append('(');
1759  append_points(txt, n_points, wkb, 0);
1760  (*txt) [txt->length() - 1]= ')';
1761  txt->qs_append(',');
1762  }
1763  (*txt) [txt->length() - 1]= ')';
1764  txt->qs_append(',');
1765  }
1766  txt->length(txt->length() - 1);
1767  return false;
1768 }
1769 
1770 
1771 bool Gis_multi_polygon::get_mbr(MBR *mbr, wkb_parser *wkb) const
1772 {
1773  uint32 n_polygons;
1774 
1775  if (wkb->scan_non_zero_uint4(&n_polygons))
1776  return true;
1777 
1778  while (n_polygons--)
1779  {
1780  uint32 n_linear_rings;
1781  if (wkb->skip_wkb_header() ||
1782  wkb->scan_non_zero_uint4(&n_linear_rings))
1783  return true;
1784 
1785  while (n_linear_rings--)
1786  {
1787  if (get_mbr_for_points(mbr, wkb, 0))
1788  return true;
1789  }
1790  }
1791  return false;
1792 }
1793 
1794 
1795 int Gis_multi_polygon::num_geometries(uint32 *num) const
1796 {
1797  wkb_parser wkb(&m_wkb_data);
1798  return wkb.scan_non_zero_uint4(num) ? 1 : 0;
1799 }
1800 
1801 
1802 int Gis_multi_polygon::geometry_n(uint32 num, String *result) const
1803 {
1804  uint32 n_polygons;
1805  wkb_parser wkb(&m_wkb_data);
1806  const char *start_of_polygon= wkb.data();
1807 
1808  if (wkb.scan_non_zero_uint4(&n_polygons))
1809  return 1;
1810 
1811  if (num > n_polygons || num < 1)
1812  return -1;
1813 
1814  do
1815  {
1816  uint32 n_linear_rings;
1817  start_of_polygon= wkb.data();
1818 
1819  if (wkb.skip_wkb_header() ||
1820  wkb.scan_non_zero_uint4(&n_linear_rings))
1821  return 1;
1822 
1823  while (n_linear_rings--)
1824  {
1825  uint32 n_points;
1826  if (wkb.scan_n_points_and_check_data(&n_points))
1827  return 1;
1828  wkb.skip_unsafe(POINT_DATA_SIZE * n_points);
1829  }
1830  } while (--num);
1831  if (wkb.no_data(0)) // We must check last segment
1832  return 1;
1833  return result->append(start_of_polygon,
1834  (uint32) (wkb.data() - start_of_polygon),
1835  (uint32) 0);
1836 }
1837 
1838 
1839 bool Gis_multi_polygon::area(double *ar, wkb_parser *wkb) const
1840 {
1841  Gis_polygon p;
1842  return collection_area(ar, wkb, &p);
1843 }
1844 
1845 
1846 int Gis_multi_polygon::centroid(String *result) const
1847 {
1848  uint32 n_polygons;
1849  bool first_loop= 1;
1850  Gis_polygon p;
1851  double UNINIT_VAR(res_area);
1852  point_xy res(0, 0); // Initialized only to make compiler happy
1853  wkb_parser wkb(&m_wkb_data);
1854 
1855  if (wkb.scan_non_zero_uint4(&n_polygons))
1856  return 1;
1857 
1858  while (n_polygons--)
1859  {
1860  double cur_area;
1861  point_xy cur;
1862  if (wkb.skip_wkb_header())
1863  return 1;
1864  p.set_data_ptr(&wkb);
1865  if (p.area(&cur_area, &wkb) ||
1866  p.centroid_xy(&cur))
1867  return 1;
1868 
1869  if (!first_loop)
1870  {
1871  double sum_area= res_area + cur_area;
1872  res.x= (res_area * res.x + cur_area * cur.x) / sum_area;
1873  res.y= (res_area * res.y + cur_area * cur.y) / sum_area;
1874  }
1875  else
1876  {
1877  first_loop= 0;
1878  res_area= cur_area;
1879  res= cur;
1880  }
1881  }
1882  return create_point(result, res);
1883 }
1884 
1885 
1886 int Gis_multi_polygon::store_shapes(Gcalc_shape_transporter *trn,
1887  Gcalc_shape_status *st) const
1888 {
1889  if (trn->skip_poly())
1890  return 0;
1891  Gis_polygon p;
1892  return collection_store_shapes(trn, st, &p);
1893 }
1894 
1895 
1896 const Geometry::Class_info *Gis_multi_polygon::get_class_info() const
1897 {
1898  return &multipolygon_class;
1899 }
1900 
1901 
1902 /************************* GeometryCollection ****************************/
1903 
1904 uint32 Gis_geometry_collection::get_data_size() const
1905 {
1906  uint32 n_objects;
1907  wkb_parser wkb(&m_wkb_data);
1908  Geometry_buffer buffer;
1909  Geometry *geom;
1910 
1911  if (wkb.scan_non_zero_uint4(&n_objects))
1912  return GET_SIZE_ERROR;
1913 
1914  while (n_objects--)
1915  {
1916  if (!(geom= scan_header_and_create(&wkb, &buffer)))
1917  return GET_SIZE_ERROR;
1918 
1919  uint32 object_size;
1920  if ((object_size= geom->get_data_size()) == GET_SIZE_ERROR)
1921  return GET_SIZE_ERROR;
1922  wkb.skip_unsafe(object_size);
1923  }
1924  return (uint32) (wkb.data() - m_wkb_data.data());
1925 }
1926 
1927 
1928 bool Gis_geometry_collection::init_from_wkt(Gis_read_stream *trs, String *wkb)
1929 {
1930  uint32 n_objects= 0;
1931  uint32 no_pos= wkb->length();
1932  Geometry_buffer buffer;
1933  Geometry *g;
1934 
1935  if (wkb->reserve(4, 512))
1936  return true;
1937  wkb->length(wkb->length()+4); // Reserve space for points
1938 
1939  for (;;)
1940  {
1941  if (!(g= create_from_wkt(&buffer, trs, wkb)))
1942  return true;
1943 
1944  if (g->get_class_info()->m_type_id == wkb_geometrycollection)
1945  {
1946  trs->set_error_msg("Unexpected GEOMETRYCOLLECTION");
1947  return true;
1948  }
1949  n_objects++;
1950  if (trs->skip_char(',')) // Didn't find ','
1951  break;
1952  }
1953 
1954  wkb->write_at_position(no_pos, n_objects);
1955  return false;
1956 }
1957 
1958 
1959 uint Gis_geometry_collection::init_from_opresult(String *bin,
1960  const char *opres,
1961  uint opres_length)
1962 {
1963  return collection_init_from_opresult(bin, opres, opres_length, NULL);
1964 }
1965 
1966 
1967 uint Gis_geometry_collection::init_from_wkb(const char *wkb, uint len,
1968  wkbByteOrder bo, String *res)
1969 {
1970  uint32 n_geom;
1971  const char *wkb_orig= wkb;
1972 
1973  if (len < 4)
1974  return 0;
1975  n_geom= wkb_get_uint(wkb, bo);
1976 
1977  if (res->reserve(4, 512))
1978  return 0;
1979  res->q_append(n_geom);
1980 
1981  wkb+= 4;
1982  while (n_geom--)
1983  {
1984  Geometry_buffer buffer;
1985  Geometry *geom;
1986  int g_len;
1987  uint32 wkb_type;
1988 
1989  if (len < WKB_HEADER_SIZE ||
1990  res->reserve(WKB_HEADER_SIZE, 512))
1991  return 0;
1992 
1993  res->q_append((char) wkb_ndr);
1994  wkb_type= wkb_get_uint(wkb+1, (wkbByteOrder) wkb[0]);
1995  res->q_append(wkb_type);
1996 
1997  if (!(geom= create_by_typeid(&buffer, wkb_type)) ||
1998  !(g_len= geom->init_from_wkb(wkb + WKB_HEADER_SIZE, len,
1999  (wkbByteOrder) wkb[0], res)))
2000  return 0;
2001  g_len+= WKB_HEADER_SIZE;
2002  wkb+= g_len;
2003  len-= g_len;
2004  }
2005  return (uint) (wkb - wkb_orig);
2006 }
2007 
2008 
2009 bool Gis_geometry_collection::get_data_as_wkt(String *txt,
2010  wkb_parser *wkb) const
2011 {
2012  uint32 n_objects;
2013  Geometry_buffer buffer;
2014  Geometry *geom;
2015 
2016  if (wkb->scan_non_zero_uint4(&n_objects))
2017  return true;
2018 
2019  while (n_objects--)
2020  {
2021  if (!(geom= scan_header_and_create(wkb, &buffer)) ||
2022  geom->as_wkt(txt, wkb) ||
2023  txt->append(STRING_WITH_LEN(","), 512))
2024  return true;
2025  }
2026  txt->length(txt->length() - 1);
2027  return false;
2028 }
2029 
2030 
2031 bool Gis_geometry_collection::get_mbr(MBR *mbr, wkb_parser *wkb) const
2032 {
2033  uint32 n_objects;
2034  Geometry_buffer buffer;
2035  Geometry *geom;
2036 
2037  if (wkb->scan_non_zero_uint4(&n_objects))
2038  return true;
2039 
2040  while (n_objects--)
2041  {
2042  if (!(geom= scan_header_and_create(wkb, &buffer)) ||
2043  geom->get_mbr(mbr, wkb))
2044  return true;
2045  }
2046  return false;
2047 }
2048 
2049 
2050 bool Gis_geometry_collection::area(double *ar, wkb_parser *wkb) const
2051 {
2052  return collection_area(ar, wkb, NULL);
2053 }
2054 
2055 
2056 int Gis_geometry_collection::num_geometries(uint32 *num) const
2057 {
2058  wkb_parser wkb(&m_wkb_data);
2059  return wkb.scan_non_zero_uint4(num) ? 1 : 0;
2060 }
2061 
2062 
2063 int Gis_geometry_collection::geometry_n(uint32 num, String *result) const
2064 {
2065  uint32 n_objects, length;
2066  wkb_parser wkb(&m_wkb_data);
2067  Geometry_buffer buffer;
2068  Geometry *geom;
2069 
2070  if (wkb.scan_non_zero_uint4(&n_objects))
2071  return 1;
2072 
2073  if (num > n_objects || num < 1)
2074  return 1;
2075 
2076  wkb_header header;
2077  do
2078  {
2079  if (wkb.scan_wkb_header(&header) ||
2080  !(geom= create_by_typeid(&buffer, header.wkb_type)))
2081  return 1;
2082  geom->set_data_ptr(&wkb);
2083  if ((length= geom->get_data_size()) == GET_SIZE_ERROR)
2084  return 1;
2085  wkb.skip_unsafe(length);
2086  } while (--num);
2087 
2088  /* Copy found object to result */
2089  if (result->reserve(1 + 4 + length))
2090  return 1;
2091  result->q_append((char) wkb_ndr);
2092  result->q_append((uint32) header.wkb_type);
2093  result->q_append(wkb.data() - length, length); // data-length = start_of_data
2094  return 0;
2095 }
2096 
2097 
2098 /*
2099  Return dimension for object
2100 
2101  SYNOPSIS
2102  dimension()
2103  res_dim Result dimension
2104  end End of object will be stored here. May be 0 for
2105  simple objects!
2106  RETURN
2107  0 ok
2108  1 error
2109 */
2110 
2111 bool Gis_geometry_collection::dimension(uint32 *res_dim,
2112  wkb_parser *wkb) const
2113 {
2114  uint32 n_objects;
2115  Geometry_buffer buffer;
2116  Geometry *geom;
2117 
2118  if (wkb->scan_non_zero_uint4(&n_objects))
2119  return true;
2120 
2121  *res_dim= 0;
2122  while (n_objects--)
2123  {
2124  uint32 dim;
2125  if (!(geom= scan_header_and_create(wkb, &buffer)) ||
2126  geom->dimension(&dim, wkb))
2127  return true;
2128  set_if_bigger(*res_dim, dim);
2129  }
2130  return false;
2131 }
2132 
2133 
2134 int Gis_geometry_collection::store_shapes(Gcalc_shape_transporter *trn,
2135  Gcalc_shape_status *st) const
2136 {
2137  return collection_store_shapes(trn, st, NULL);
2138 }
2139 
2140 
2141 const Geometry::Class_info *Gis_geometry_collection::get_class_info() const
2142 {
2143  return &geometrycollection_class;
2144 }
2145 
2146 #endif /*HAVE_SPATIAL*/