1 /* Copyright (c) 2014, 2021, Oracle and/or its affiliates.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software Foundation,
21 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
22
23
24 /**
25 @file
26
27 @brief
28 This file defines implementations of GIS set operation functions.
29 */
30 #include "my_config.h"
31 #include "item_geofunc_internal.h"
32
33 #include <set>
34
35 #define BGOPCALL(GeoOutType, geom_out, bgop, \
36 GeoType1, g1, GeoType2, g2, wkbres, nullval) \
37 do \
38 { \
39 const void *pg1= g1->normalize_ring_order(); \
40 const void *pg2= g2->normalize_ring_order(); \
41 geom_out= NULL; \
42 if (pg1 != NULL && pg2 != NULL) \
43 { \
44 GeoType1 geo1(pg1, g1->get_data_size(), g1->get_flags(), \
45 g1->get_srid()); \
46 GeoType2 geo2(pg2, g2->get_data_size(), g2->get_flags(), \
47 g2->get_srid()); \
48 auto_ptr<GeoOutType>geout(new GeoOutType()); \
49 geout->set_srid(g1->get_srid()); \
50 boost::geometry::bgop(geo1, geo2, *geout); \
51 (nullval)= false; \
52 if (geout->size() == 0 || \
53 (nullval= post_fix_result(&(m_ifso->bg_resbuf_mgr), \
54 *geout, wkbres))) \
55 { \
56 if (nullval) \
57 return NULL; \
58 } \
59 else \
60 geom_out= geout.release(); \
61 } \
62 else \
63 { \
64 (nullval)= true; \
65 my_error(ER_GIS_INVALID_DATA, MYF(0), "st_" #bgop); \
66 return NULL; \
67 } \
68 } while (0)
69
70
~Item_func_spatial_operation()71 Item_func_spatial_operation::~Item_func_spatial_operation()
72 {
73 }
74
75
76 /*
77 Write an empty geometry collection's wkb encoding into str, and create a
78 geometry object for this empty geometry colletion.
79 */
empty_result(String * str,uint32 srid)80 Geometry *Item_func_spatial_operation::empty_result(String *str, uint32 srid)
81 {
82 if ((null_value= str->reserve(GEOM_HEADER_SIZE + 4 + 16, 256)))
83 return 0;
84
85 write_geometry_header(str, srid, Geometry::wkb_geometrycollection, 0);
86 Gis_geometry_collection *gcol= new Gis_geometry_collection();
87 gcol->set_data_ptr(str->ptr() + GEOM_HEADER_SIZE, 4);
88 gcol->has_geom_header_space(true);
89 return gcol;
90 }
91
92
93 /**
94 Wraps and dispatches type specific BG function calls according to operation
95 type and the 1st or both operand type(s), depending on code complexity.
96
97 We want to isolate boost header file inclusion only inside this file, so we
98 can't put this class declaration in any header file. And we want to make the
99 methods static since no state is needed here.
100 @tparam Geom_types A wrapper for all geometry types.
101 */
102 template<typename Geom_types>
103 class BG_setop_wrapper
104 {
105 // Some computation in this class may rely on functions in
106 // Item_func_spatial_operation.
107 Item_func_spatial_operation *m_ifso;
108 my_bool null_value; // Whether computation has error.
109
110 // Some computation in this class may rely on functions in
111 // Item_func_spatial_operation, after each call of its functions, copy its
112 // null_value, we don't want to miss errors.
copy_ifso_state()113 void copy_ifso_state()
114 {
115 null_value= m_ifso->null_value;
116 }
117
118 public:
119 typedef typename Geom_types::Point Point;
120 typedef typename Geom_types::Linestring Linestring;
121 typedef typename Geom_types::Polygon Polygon;
122 typedef typename Geom_types::Polygon::ring_type Polygon_ring;
123 typedef typename Geom_types::Multipoint Multipoint;
124 typedef typename Geom_types::Multilinestring Multilinestring;
125 typedef typename Geom_types::Multipolygon Multipolygon;
126 typedef typename Geom_types::Coordinate_type Coord_type;
127 typedef typename Geom_types::Coordinate_system Coordsys;
128 typedef Item_func_spatial_rel Ifsr;
129 typedef Item_func_spatial_operation Ifso;
130 typedef std::set<Point, bgpt_lt> Point_set;
131 typedef std::vector<Point> Point_vector;
132
BG_setop_wrapper(Item_func_spatial_operation * ifso)133 BG_setop_wrapper(Item_func_spatial_operation *ifso)
134 {
135 m_ifso= ifso;
136 null_value= 0;
137 }
138
139
get_null_value() const140 my_bool get_null_value() const
141 {
142 return null_value;
143 }
144
145
146 /**
147 Do point insersection point operation.
148 @param g1 First geometry operand, must be a point.
149 @param g2 Second geometry operand, must be a point.
150 @param[out] result Holds WKB data of the result.
151 @return the result Geometry whose WKB data is in result.
152 */
point_intersection_point(Geometry * g1,Geometry * g2,String * result)153 Geometry *point_intersection_point(Geometry *g1, Geometry *g2,
154 String *result)
155 {
156 Geometry *retgeo= NULL;
157
158 Point pt1(g1->get_data_ptr(),
159 g1->get_data_size(), g1->get_flags(), g1->get_srid());
160 Point pt2(g2->get_data_ptr(),
161 g2->get_data_size(), g2->get_flags(), g2->get_srid());
162
163 if (bgpt_eq()(pt1, pt2))
164 {
165 retgeo= g1;
166 null_value= retgeo->as_geometry(result, true);
167 }
168 else
169 {
170 retgeo= m_ifso->empty_result(result, g1->get_srid());
171 copy_ifso_state();
172 }
173 return retgeo;
174 }
175
176
177 /*
178 Do point intersection Multipoint operation.
179 The parameters and return value has identical/similar meaning as to above
180 function, which can be inferred from the function name, we won't repeat
181 here or for the rest of the functions in this class.
182 */
point_intersection_multipoint(Geometry * g1,Geometry * g2,String * result)183 Geometry *point_intersection_multipoint(Geometry *g1, Geometry *g2,
184 String *result)
185 {
186 Geometry *retgeo= NULL;
187
188 Point pt(g1->get_data_ptr(),
189 g1->get_data_size(), g1->get_flags(), g1->get_srid());
190 Multipoint mpts(g2->get_data_ptr(),
191 g2->get_data_size(), g2->get_flags(), g2->get_srid());
192 Point_set ptset(mpts.begin(), mpts.end());
193
194 if (ptset.find(pt) != ptset.end())
195 {
196 retgeo= g1;
197 null_value= retgeo->as_geometry(result, true);
198 }
199 else
200 {
201 retgeo= m_ifso->empty_result(result, g1->get_srid());
202 copy_ifso_state();
203 }
204 return retgeo;
205 }
206
207
point_intersection_geometry(Geometry * g1,Geometry * g2,String * result)208 Geometry *point_intersection_geometry(Geometry *g1, Geometry *g2,
209 String *result)
210 {
211 #if !defined(NDEBUG)
212 Geometry::wkbType gt2= g2->get_type();
213 #endif
214 Geometry *retgeo= NULL;
215
216 bool is_out= !Ifsr::bg_geo_relation_check<Coordsys>
217 (g1, g2, Ifsr::SP_DISJOINT_FUNC, &null_value);
218
219 assert(gt2 == Geometry::wkb_linestring ||
220 gt2 == Geometry::wkb_polygon ||
221 gt2 == Geometry::wkb_multilinestring ||
222 gt2 == Geometry::wkb_multipolygon);
223 if (!null_value)
224 {
225 if (is_out)
226 {
227 null_value= g1->as_geometry(result, true);
228 retgeo= g1;
229 }
230 else
231 {
232 retgeo= m_ifso->empty_result(result, g1->get_srid());
233 copy_ifso_state();
234 }
235 }
236 return retgeo;
237 }
238
239
multipoint_intersection_multipoint(Geometry * g1,Geometry * g2,String * result)240 Geometry *multipoint_intersection_multipoint(Geometry *g1, Geometry *g2,
241 String *result)
242 {
243 Geometry *retgeo= NULL;
244 Point_set ptset1, ptset2;
245 Multipoint *mpts= new Multipoint();
246 auto_ptr<Multipoint> guard(mpts);
247
248 mpts->set_srid(g1->get_srid());
249
250 Multipoint mpts1(g1->get_data_ptr(),
251 g1->get_data_size(), g1->get_flags(), g1->get_srid());
252 Multipoint mpts2(g2->get_data_ptr(),
253 g2->get_data_size(), g2->get_flags(), g2->get_srid());
254
255 ptset1.insert(mpts1.begin(), mpts1.end());
256 ptset2.insert(mpts2.begin(), mpts2.end());
257
258 Point_vector respts;
259 TYPENAME Point_vector::iterator endpos;
260 size_t ptset1sz= ptset1.size(), ptset2sz= ptset2.size();
261 respts.resize(ptset1sz > ptset2sz ? ptset1sz : ptset2sz);
262
263 endpos= std::set_intersection(ptset1.begin(), ptset1.end(),
264 ptset2.begin(), ptset2.end(),
265 respts.begin(), bgpt_lt());
266 std::copy(respts.begin(), endpos, std::back_inserter(*mpts));
267 if (mpts->size() > 0)
268 {
269 null_value= m_ifso->assign_result(mpts, result);
270 retgeo= mpts;
271 guard.release();
272 }
273 else
274 {
275 retgeo= m_ifso->empty_result(result, g1->get_srid());
276 copy_ifso_state();
277 }
278
279 return retgeo;
280 }
281
282
multipoint_intersection_geometry(Geometry * g1,Geometry * g2,String * result)283 Geometry *multipoint_intersection_geometry(Geometry *g1, Geometry *g2,
284 String *result)
285 {
286 Geometry *retgeo= NULL;
287 #if !defined(NDEBUG)
288 Geometry::wkbType gt2= g2->get_type();
289 #endif
290 Point_set ptset;
291 Multipoint mpts(g1->get_data_ptr(),
292 g1->get_data_size(), g1->get_flags(), g1->get_srid());
293 Multipoint *mpts2= new Multipoint();
294 auto_ptr<Multipoint> guard(mpts2);
295
296 mpts2->set_srid(g1->get_srid());
297
298 assert(gt2 == Geometry::wkb_linestring ||
299 gt2 == Geometry::wkb_polygon ||
300 gt2 == Geometry::wkb_multilinestring ||
301 gt2 == Geometry::wkb_multipolygon);
302 ptset.insert(mpts.begin(), mpts.end());
303
304 for (TYPENAME Point_set::iterator i= ptset.begin(); i != ptset.end(); ++i)
305 {
306 Point &pt= const_cast<Point&>(*i);
307 if (!Ifsr::bg_geo_relation_check<Coordsys>
308 (&pt, g2, Ifsr::SP_DISJOINT_FUNC, &null_value) && !null_value)
309 {
310 mpts2->push_back(pt);
311 }
312
313 if (null_value)
314 return 0;
315 }
316
317 if (mpts2->size() > 0)
318 {
319 null_value= m_ifso->assign_result(mpts2, result);
320 retgeo= mpts2;
321 guard.release();
322 }
323 else
324 {
325 retgeo= m_ifso->empty_result(result, g1->get_srid());
326 copy_ifso_state();
327 }
328
329 return retgeo;
330 }
331
332
linestring_intersection_linestring(Geometry * g1,Geometry * g2,String * result)333 Geometry *linestring_intersection_linestring(Geometry *g1, Geometry *g2,
334 String *result)
335 {
336 Multilinestring *res= NULL;
337 Geometry *retgeo= NULL;
338
339 // BG will return all intersection lines and points in a
340 // multilinestring. Intersection points are represented as lines
341 // with the same start and end points.
342 BGOPCALL(Multilinestring, res, intersection,
343 Linestring, g1, Linestring, g2, NULL, null_value);
344
345 if (res == NULL)
346 return m_ifso->empty_result(result, g1->get_srid());
347
348 retgeo= m_ifso->simplify_multilinestring(res, result);
349 delete res;
350 return retgeo;
351 }
352
353
linestring_intersection_polygon(Geometry * g1,Geometry * g2,String * result)354 Geometry *linestring_intersection_polygon(Geometry *g1, Geometry *g2,
355 String *result)
356 {
357 Geometry::wkbType gt2= g2->get_type();
358 Geometry *retgeo= NULL, *tmp1= NULL, *tmp2= NULL;
359 // It is likely for there to be discrete intersection Points.
360 if (gt2 == Geometry::wkb_multipolygon)
361 {
362 BGOPCALL(Multilinestring, tmp1, intersection,
363 Linestring, g1, Multipolygon, g2, NULL, null_value);
364 BGOPCALL(Multipoint, tmp2, intersection,
365 Linestring, g1, Multipolygon, g2, NULL, null_value);
366 }
367 else
368 {
369 BGOPCALL(Multilinestring, tmp1, intersection,
370 Linestring, g1, Polygon, g2, NULL, null_value);
371 BGOPCALL(Multipoint, tmp2, intersection,
372 Linestring, g1, Polygon, g2, NULL, null_value);
373 }
374
375 // Need merge, exclude Points that are on the result Linestring.
376 retgeo= m_ifso->combine_sub_results<Coordsys>
377 (tmp1, tmp2, result);
378 copy_ifso_state();
379
380 return retgeo;
381 }
382
383
linestring_intersection_multilinestring(Geometry * g1,Geometry * g2,String * result)384 Geometry *linestring_intersection_multilinestring(Geometry *g1, Geometry *g2,
385 String *result)
386 {
387 Multilinestring *res= NULL;
388 Geometry *retgeo= NULL;
389
390 // BG will return all intersection lines and points in a
391 // multilinestring. Intersection points are represented as lines
392 // with the same start and end points.
393 BGOPCALL(Multilinestring, res, intersection,
394 Linestring, g1, Multilinestring, g2, NULL, null_value);
395
396 if (res == NULL || res->size() == 0)
397 return m_ifso->empty_result(result, g1->get_srid());
398
399 retgeo= m_ifso->simplify_multilinestring(res, result);
400 delete res;
401 return retgeo;
402 }
403
404
polygon_intersection_multilinestring(Geometry * g1,Geometry * g2,String * result)405 Geometry *polygon_intersection_multilinestring(Geometry *g1, Geometry *g2,
406 String *result)
407 {
408 Geometry *retgeo= NULL, *tmp1= NULL;
409 Multipoint *tmp2= NULL;
410 auto_ptr<Geometry> guard1;
411
412
413 BGOPCALL(Multilinestring, tmp1, intersection,
414 Polygon, g1, Multilinestring, g2, NULL, null_value);
415 guard1.reset(tmp1);
416
417 Multilinestring mlstr(g2->get_data_ptr(), g2->get_data_size(),
418 g2->get_flags(), g2->get_srid());
419 Multipoint mpts;
420 Point_set ptset;
421
422 const void *data_ptr= g1->normalize_ring_order();
423 if (data_ptr == NULL)
424 {
425 null_value= true;
426 my_error(ER_GIS_INVALID_DATA, MYF(0), "st_intersection");
427 return NULL;
428 }
429
430 Polygon plgn(data_ptr, g1->get_data_size(),
431 g1->get_flags(), g1->get_srid());
432
433 for (TYPENAME Multilinestring::iterator i= mlstr.begin();
434 i != mlstr.end(); ++i)
435 {
436 boost::geometry::intersection(plgn, *i, mpts);
437 if (mpts.size() > 0)
438 {
439 ptset.insert(mpts.begin(), mpts.end());
440 mpts.clear();
441 }
442 }
443
444 auto_ptr<Multipoint> guard2;
445 if (ptset.size() > 0)
446 {
447 tmp2= new Multipoint;
448 tmp2->set_srid(g1->get_srid());
449 guard2.reset(tmp2);
450 std::copy(ptset.begin(), ptset.end(), std::back_inserter(*tmp2));
451 }
452
453 retgeo= m_ifso->combine_sub_results<Coordsys>
454 (guard1.release(), guard2.release(), result);
455 copy_ifso_state();
456
457 return retgeo;
458 }
459
460
461 /**
462 Compute the multilinestring result if any for intersection of two polygons.
463 First get the multilinestrings mls1 and mls2 from polygons, then compute
464 mls3 = mls1 intersection mls2, and finally return mls3 - mls4 as result,
465 where mls4 is got from rings of the multipolygon intersection result of
466 plgn1 and plgn2.
467
468 @param plgn1 the 1st polygon operand
469 @param plgn2 the 2nd polygon operand
470 @param mplgn the areal intersection result of plgn1 and plgn2
471 @param [out] mls the result multilinestring
472 */
473 template<typename Polygon_type1, typename Polygon_type2>
plgn_intersection_plgn_mls(const Polygon_type1 & plgn1,const Polygon_type2 & plgn2,const Multipolygon & mplgn,Multilinestring & mls)474 void plgn_intersection_plgn_mls(const Polygon_type1 &plgn1,
475 const Polygon_type2 &plgn2,
476 const Multipolygon &mplgn,
477 Multilinestring &mls)
478 {
479 if (mplgn.size() > 0)
480 {
481 /*
482 Remove the linestring parts that are part of the result polygon rings.
483 The discrete intersection points that are also removed here.
484 */
485 Multilinestring mls3;
486 bg::intersection(plgn1, plgn2, mls3);
487 bg::detail::boundary_view<Multipolygon const> view(mplgn);
488 bg::difference(mls3, view, mls);
489 }
490 else
491 bg::intersection(plgn1, plgn2, mls);
492 }
493
494
polygon_intersection_polygon(Geometry * g1,Geometry * g2,String * result)495 Geometry *polygon_intersection_polygon(Geometry *g1, Geometry *g2,
496 String *result)
497 {
498 Geometry::wkbType gt2= g2->get_type();
499 Geometry *retgeo= NULL;
500
501 const void *pg1= g1->normalize_ring_order();
502 const void *pg2= g2->normalize_ring_order();
503 if (pg1 == NULL || pg2 == NULL)
504 {
505 null_value= true;
506 my_error(ER_GIS_INVALID_DATA, MYF(0), "st_intersection");
507 return NULL;
508 }
509
510 Multilinestring mls;
511 Polygon plgn1(pg1, g1->get_data_size(), g1->get_flags(),
512 g1->get_srid());
513
514 auto_ptr<Multipolygon> mplgn_result(new Multipolygon());
515 mplgn_result->set_srid(g1->get_srid());
516
517 if (gt2 == Geometry::wkb_polygon)
518 {
519 Polygon plgn2(pg2, g2->get_data_size(), g2->get_flags(),
520 g2->get_srid());
521 bg::intersection(plgn1, plgn2, *mplgn_result);
522 plgn_intersection_plgn_mls(plgn1, plgn2, *mplgn_result, mls);
523 }
524 else
525 {
526 Multipolygon mplgn2(pg2, g2->get_data_size(), g2->get_flags(),
527 g2->get_srid());
528 bg::intersection(plgn1, mplgn2, *mplgn_result);
529 plgn_intersection_plgn_mls(plgn1, mplgn2, *mplgn_result, mls);
530 }
531
532 retgeo= combine_mls_mplgn_results(&mls, mplgn_result, result);
533 copy_ifso_state();
534
535 return retgeo;
536 }
537
combine_mls_mplgn_results(Multilinestring * mls,auto_ptr<Multipolygon> mplgn_result,String * result)538 Geometry *combine_mls_mplgn_results(Multilinestring *mls,
539 auto_ptr<Multipolygon> mplgn_result,
540 String *result)
541 {
542 Geometry *geom= NULL, *retgeo= NULL;
543 assert(mls != NULL);
544
545 if (mls->size() > 0)
546 geom= m_ifso->simplify_multilinestring(mls, result);
547
548 if (mplgn_result->size() > 0)
549 {
550 if (mls->size() == 0)
551 {
552 // The multipolygon is the only result.
553 assert(result->length() == 0);
554 null_value= post_fix_result(&(m_ifso->bg_resbuf_mgr),
555 *mplgn_result, result);
556 if (null_value)
557 return NULL;
558 else
559 retgeo= mplgn_result.release();
560 }
561 else
562 {
563 String mplgn_resbuf;
564 null_value= post_fix_result(&(m_ifso->bg_resbuf_mgr),
565 *mplgn_result, &mplgn_resbuf);
566 if (null_value)
567 return NULL;
568 if (geom->get_type() == Geometry::wkb_geometrycollection)
569 {
570 down_cast<Gis_geometry_collection *>(geom)->
571 append_geometry(&(*mplgn_result), result);
572 retgeo= geom;
573 }
574 else
575 {
576 /*
577 The geom created from mls isn't a GC, so we have to create a GC to
578 hole both geom and mplgn_result.
579 */
580 String tmp_mls_resbuf;
581 tmp_mls_resbuf.takeover(*result);
582
583 Gis_geometry_collection *tmp_gc=
584 new Gis_geometry_collection(&(*mplgn_result), result);
585 tmp_gc->append_geometry(geom, result);
586 retgeo= tmp_gc;
587 delete geom;
588 }
589 }
590 }
591 else
592 retgeo= geom;
593
594 if (retgeo == NULL)
595 retgeo= m_ifso->empty_result(result, mplgn_result->get_srid());
596
597 return retgeo;
598 }
599
600
multilinestring_intersection_multilinestring(Geometry * g1,Geometry * g2,String * result)601 Geometry *multilinestring_intersection_multilinestring(Geometry *g1,
602 Geometry *g2,
603 String *result)
604 {
605 Multilinestring *res= NULL;
606 Geometry *retgeo= NULL;
607
608 // BG will return all intersection lines and points in a
609 // multilinestring. Intersection points are represented as lines
610 // with the same start and end points.
611 BGOPCALL(Multilinestring, res, intersection,
612 Multilinestring, g1, Multilinestring, g2, NULL, null_value);
613
614 if (res == NULL)
615 return m_ifso->empty_result(result, g1->get_srid());
616
617 retgeo= m_ifso->simplify_multilinestring(res, result);
618 delete res;
619 return retgeo;
620 }
621
622
multilinestring_intersection_multipolygon(Geometry * g1,Geometry * g2,String * result)623 Geometry *multilinestring_intersection_multipolygon(Geometry *g1,
624 Geometry *g2,
625 String *result)
626 {
627 Geometry *retgeo= NULL, *tmp1= NULL;
628 Multipoint *tmp2= NULL;
629
630 auto_ptr<Geometry> guard1;
631 BGOPCALL(Multilinestring, tmp1, intersection,
632 Multilinestring, g1, Multipolygon, g2,
633 NULL, null_value);
634 guard1.reset(tmp1);
635
636 Multilinestring mlstr(g1->get_data_ptr(), g1->get_data_size(),
637 g1->get_flags(), g1->get_srid());
638 Multipoint mpts;
639
640 const void *data_ptr= g2->normalize_ring_order();
641 if (data_ptr == NULL)
642 {
643 null_value= true;
644 my_error(ER_GIS_INVALID_DATA, MYF(0), "st_intersection");
645 return NULL;
646 }
647
648 Multipolygon mplgn(data_ptr, g2->get_data_size(),
649 g2->get_flags(), g2->get_srid());
650 Point_set ptset;
651
652 for (TYPENAME Multilinestring::iterator i= mlstr.begin();
653 i != mlstr.end(); ++i)
654 {
655 boost::geometry::intersection(*i, mplgn, mpts);
656 if (mpts.size() > 0)
657 {
658 ptset.insert(mpts.begin(), mpts.end());
659 mpts.clear();
660 }
661 }
662
663 auto_ptr<Multipoint> guard2;
664 if (ptset.empty() == false)
665 {
666 tmp2= new Multipoint;
667 tmp2->set_srid(g1->get_srid());
668 guard2.reset(tmp2);
669 std::copy(ptset.begin(), ptset.end(), std::back_inserter(*tmp2));
670 }
671
672 retgeo= m_ifso->combine_sub_results<Coordsys>
673 (guard1.release(), guard2.release(), result);
674 copy_ifso_state();
675
676 return retgeo;
677 }
678
679
multipolygon_intersection_multipolygon(Geometry * g1,Geometry * g2,String * result)680 Geometry *multipolygon_intersection_multipolygon(Geometry *g1, Geometry *g2,
681 String *result)
682 {
683 Geometry *retgeo= NULL;
684
685 const void *pg1= g1->normalize_ring_order();
686 const void *pg2= g2->normalize_ring_order();
687 if (pg1 == NULL || pg2 == NULL)
688 {
689 null_value= true;
690 my_error(ER_GIS_INVALID_DATA, MYF(0), "st_intersection");
691 return NULL;
692 }
693
694 Multilinestring mls;
695 Multipolygon mplgn1(pg1, g1->get_data_size(), g1->get_flags(),
696 g1->get_srid());
697
698 auto_ptr<Multipolygon> mplgn_result(new Multipolygon());
699 mplgn_result->set_srid(g1->get_srid());
700
701 Multipolygon mplgn2(pg2, g2->get_data_size(), g2->get_flags(),
702 g2->get_srid());
703 bg::intersection(mplgn1, mplgn2, *mplgn_result);
704 plgn_intersection_plgn_mls(mplgn1, mplgn2, *mplgn_result, mls);
705 retgeo= combine_mls_mplgn_results(&mls, mplgn_result, result);
706
707 copy_ifso_state();
708
709 return retgeo;
710 }
711
712
point_union_point(Geometry * g1,Geometry * g2,String * result)713 Geometry *point_union_point(Geometry *g1, Geometry *g2, String *result)
714 {
715 Geometry *retgeo= NULL;
716 Geometry::wkbType gt2= g2->get_type();
717 Point_set ptset;// Use set to make Points unique.
718
719 Point pt1(g1->get_data_ptr(),
720 g1->get_data_size(), g1->get_flags(), g1->get_srid());
721 Multipoint *mpts= new Multipoint();
722 auto_ptr<Multipoint> guard(mpts);
723
724 mpts->set_srid(g1->get_srid());
725 ptset.insert(pt1);
726 if (gt2 == Geometry::wkb_point)
727 {
728 Point pt2(g2->get_data_ptr(),
729 g2->get_data_size(), g2->get_flags(), g2->get_srid());
730 ptset.insert(pt2);
731 }
732 else
733 {
734 Multipoint mpts2(g2->get_data_ptr(),
735 g2->get_data_size(), g2->get_flags(), g2->get_srid());
736 ptset.insert(mpts2.begin(), mpts2.end());
737 }
738
739 std::copy(ptset.begin(), ptset.end(), std::back_inserter(*mpts));
740 if (mpts->size() > 0)
741 {
742 retgeo= mpts;
743 null_value= m_ifso->assign_result(mpts, result);
744 guard.release();
745 }
746 else
747 {
748 if (!null_value)
749 {
750 retgeo= m_ifso->empty_result(result, g1->get_srid());
751 copy_ifso_state();
752 }
753 }
754 return retgeo;
755 }
756
757
point_union_geometry(Geometry * g1,Geometry * g2,String * result)758 Geometry *point_union_geometry(Geometry *g1, Geometry *g2, String *result)
759 {
760 Geometry *retgeo= NULL;
761 #if !defined(NDEBUG)
762 Geometry::wkbType gt2= g2->get_type();
763 #endif
764 assert(gt2 == Geometry::wkb_linestring ||
765 gt2 == Geometry::wkb_polygon ||
766 gt2 == Geometry::wkb_multilinestring ||
767 gt2 == Geometry::wkb_multipolygon);
768 if (Ifsr::bg_geo_relation_check<Coordsys>
769 (g1, g2, Ifsr::SP_DISJOINT_FUNC, &null_value) && !null_value)
770 {
771 Gis_geometry_collection *geocol= new Gis_geometry_collection(g2, result);
772 null_value= (geocol == NULL || geocol->append_geometry(g1, result));
773 retgeo= geocol;
774 }
775 else if (null_value)
776 {
777 retgeo= NULL;
778 return 0;
779 }
780 else
781 {
782 retgeo= g2;
783 null_value= retgeo->as_geometry(result, true);
784 }
785
786 return retgeo;
787 }
788
789
linestring_union_linestring(Geometry * g1,Geometry * g2,String * result)790 Geometry *linestring_union_linestring(Geometry *g1, Geometry *g2,
791 String *result)
792 {
793 Linestring ls1(g1->get_data_ptr(), g1->get_data_size(),
794 g1->get_flags(), g1->get_srid());
795 Linestring ls2(g2->get_data_ptr(), g2->get_data_size(),
796 g2->get_flags(), g2->get_srid());
797 auto_ptr<Multilinestring> res(new Multilinestring());
798 res->set_srid(g1->get_srid());
799
800 boost::geometry::union_(ls1, ls2, *res);
801 assert(res.get() != 0);
802 assert(res->size() != 0);
803 if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result))
804 {
805 my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
806 null_value= TRUE;
807 return NULL;
808 }
809
810 return res.release();
811 }
812
813
linestring_union_polygon(Geometry * g1,Geometry * g2,String * result)814 Geometry *linestring_union_polygon(Geometry *g1, Geometry *g2,
815 String *result)
816 {
817 Geometry *retgeo= NULL;
818 const void *g2_wkb= g2->normalize_ring_order();
819 if (g2_wkb == NULL)
820 {
821 // Invalid polygon
822 my_error(ER_GIS_INVALID_DATA, MYF(0), m_ifso->func_name());
823 null_value= TRUE;
824 return NULL;
825 }
826
827 Linestring ls1(g1->get_data_ptr(), g1->get_data_size(),
828 g1->get_flags(), g1->get_srid());
829 Polygon py2(g2_wkb, g2->get_data_size(),
830 g2->get_flags(), g2->get_srid());
831 auto_ptr<Multilinestring> linestrings(new Multilinestring());
832 linestrings->set_srid(g1->get_srid());
833
834 // Union(LineString, Polygon) isn't supported by BG, but it's
835 // equivalent to GeometryCollection(Polygon, Difference(LineString,
836 // Polygon)).
837 boost::geometry::difference(ls1, py2, *linestrings);
838 assert(linestrings.get() != 0);
839 if (post_fix_result(&m_ifso->bg_resbuf_mgr, *linestrings, NULL) &&
840 linestrings->size() > 0)
841 {
842 my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
843 null_value= TRUE;
844 return NULL;
845 }
846
847 // Return the simplest result possible.
848 if (linestrings->size() == 0)
849 {
850 // Polygon result.
851 g2->as_geometry(result, true);
852 retgeo= g2;
853 }
854 else
855 {
856 // GeometryCollection result containing one Polygon and one or
857 // more LineStrings.
858 Gis_geometry_collection *collection= new Gis_geometry_collection();
859 py2.to_wkb_unparsed();
860 collection->append_geometry(&py2, result);
861 if (linestrings->size() > 1)
862 {
863 collection->append_geometry(&(*linestrings), result);
864 }
865 else
866 {
867 collection->append_geometry(&(*linestrings)[0], result);
868 }
869 collection->set_ownmem(false);
870 retgeo= collection;
871 }
872
873 return retgeo;
874 }
875
876
linestring_union_multilinestring(Geometry * g1,Geometry * g2,String * result)877 Geometry *linestring_union_multilinestring(Geometry *g1, Geometry *g2,
878 String *result)
879 {
880 Linestring ls1(g1->get_data_ptr(), g1->get_data_size(),
881 g1->get_flags(), g1->get_srid());
882 Multilinestring mls2(g2->get_data_ptr(), g2->get_data_size(),
883 g2->get_flags(), g2->get_srid());
884 auto_ptr<Multilinestring> res(new Multilinestring);
885 res->set_srid(g1->get_srid());
886
887 boost::geometry::union_(ls1, mls2, *res);
888 assert(res.get() != 0);
889 assert(res->size() != 0);
890 if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result))
891 {
892 my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
893 null_value= TRUE;
894 return NULL;
895 }
896
897 return res.release();
898 }
899
900
linestring_union_multipolygon(Geometry * g1,Geometry * g2,String * result)901 Geometry *linestring_union_multipolygon(Geometry *g1, Geometry *g2,
902 String *result)
903 {
904 Geometry *retgeo= NULL;
905 const void *g2_wkb= g2->normalize_ring_order();
906 if (g2_wkb == NULL)
907 {
908 // Invalid polygon
909 my_error(ER_GIS_INVALID_DATA, MYF(0), m_ifso->func_name());
910 null_value= TRUE;
911 return NULL;
912 }
913
914 Linestring ls1(g1->get_data_ptr(), g1->get_data_size(),
915 g1->get_flags(), g1->get_srid());
916 Multipolygon mpy2(g2_wkb, g2->get_data_size(),
917 g2->get_flags(), g2->get_srid());
918 auto_ptr<Multilinestring> linestrings(new Multilinestring());
919 linestrings->set_srid(g1->get_srid());
920
921 // Union(LineString, MultiPolygon) isn't supported by BG, but it's
922 // equivalent to GeometryCollection(MultiPolygon,
923 // Difference(LineString, MultiPolygon)).
924 boost::geometry::difference(ls1, mpy2, *linestrings);
925 assert(linestrings.get() != 0);
926 if (post_fix_result(&m_ifso->bg_resbuf_mgr, *linestrings, NULL) &&
927 linestrings->size() > 0)
928 {
929 my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
930 null_value= TRUE;
931 return NULL;
932 }
933
934 // Return the simplest result possible.
935 if (linestrings->size() == 0)
936 {
937 // MultiPolygon result.
938 g2->as_geometry(result, true);
939 retgeo= g2;
940 }
941 else
942 {
943 // GeometryCollection result containing one or more Polygons and
944 // one or more LineStrings.
945 Gis_geometry_collection *collection= new Gis_geometry_collection();
946
947 if (mpy2.size() > 1)
948 {
949 collection->append_geometry(&mpy2, result);
950 }
951 else
952 {
953 mpy2[0].to_wkb_unparsed();
954 collection->append_geometry(&mpy2[0], result);
955 }
956
957 if (linestrings->size() > 1)
958 {
959 collection->append_geometry(&(*linestrings), result);
960 }
961 else
962 {
963 collection->append_geometry(&(*linestrings)[0], result);
964 }
965
966 collection->set_ownmem(false);
967 retgeo= collection;
968 }
969
970 return retgeo;
971 }
972
973
polygon_union_multilinestring(Geometry * g1,Geometry * g2,String * result)974 Geometry *polygon_union_multilinestring(Geometry *g1, Geometry *g2,
975 String *result)
976 {
977 Geometry *retgeo= NULL;
978 const void *g1_wkb= g1->normalize_ring_order();
979 if (g1_wkb == NULL)
980 {
981 // Invalid polygon
982 my_error(ER_GIS_INVALID_DATA, MYF(0), m_ifso->func_name());
983 null_value= TRUE;
984 return NULL;
985 }
986
987 Polygon py1(g1_wkb, g1->get_data_size(),
988 g1->get_flags(), g1->get_srid());
989 Multilinestring mls2(g2->get_data_ptr(), g2->get_data_size(),
990 g2->get_flags(), g2->get_srid());
991 auto_ptr<Multilinestring> linestrings(new Multilinestring());
992 linestrings->set_srid(g1->get_srid());
993
994 // Union(Polygon, MultiLineString) isn't supported by BG, but it's
995 // equivalent to GeometryCollection(Polygon,
996 // Difference(MultiLineString, Polygon)).
997 boost::geometry::difference(mls2, py1, *linestrings);
998 assert(linestrings.get() != 0);
999 if (post_fix_result(&m_ifso->bg_resbuf_mgr, *linestrings, NULL) &&
1000 linestrings->size() > 0)
1001 {
1002 my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1003 null_value= TRUE;
1004 return NULL;
1005 }
1006
1007 // Return the simplest result possible.
1008 if (linestrings->size() == 0)
1009 {
1010 // Polygon result.
1011 g2->as_geometry(result, true);
1012 retgeo= g2;
1013 }
1014 else
1015 {
1016 // GeometryCollection result containing one Polygon and one or
1017 // more LineStrings.
1018 Gis_geometry_collection *collection= new Gis_geometry_collection();
1019 py1.to_wkb_unparsed();
1020 collection->append_geometry(&py1, result);
1021 if (linestrings->size() > 1)
1022 {
1023 collection->append_geometry(&(*linestrings), result);
1024 }
1025 else
1026 {
1027 collection->append_geometry(&(*linestrings)[0], result);
1028 }
1029 collection->set_ownmem(false);
1030 retgeo= collection;
1031 }
1032
1033 return retgeo;
1034 }
1035
1036
multipoint_union_multipoint(Geometry * g1,Geometry * g2,String * result)1037 Geometry *multipoint_union_multipoint(Geometry *g1, Geometry *g2,
1038 String *result)
1039 {
1040 Geometry *retgeo= NULL;
1041 Point_set ptset;
1042 Multipoint *mpts= new Multipoint();
1043 auto_ptr<Multipoint> guard(mpts);
1044
1045 mpts->set_srid(g1->get_srid());
1046 Multipoint mpts1(g1->get_data_ptr(),
1047 g1->get_data_size(), g1->get_flags(), g1->get_srid());
1048 Multipoint mpts2(g2->get_data_ptr(),
1049 g2->get_data_size(), g2->get_flags(), g2->get_srid());
1050
1051 ptset.insert(mpts1.begin(), mpts1.end());
1052 ptset.insert(mpts2.begin(), mpts2.end());
1053 std::copy(ptset.begin(), ptset.end(), std::back_inserter(*mpts));
1054
1055 if (mpts->size() > 0)
1056 {
1057 retgeo= mpts;
1058 null_value= m_ifso->assign_result(mpts, result);
1059 guard.release();
1060 }
1061 else
1062 {
1063 if (!null_value)
1064 {
1065 retgeo= m_ifso->empty_result(result, g1->get_srid());
1066 copy_ifso_state();
1067 }
1068 }
1069 return retgeo;
1070 }
1071
1072
multipoint_union_geometry(Geometry * g1,Geometry * g2,String * result)1073 Geometry *multipoint_union_geometry(Geometry *g1, Geometry *g2,
1074 String *result)
1075 {
1076 Geometry *retgeo= NULL;
1077 #if !defined(NDEBUG)
1078 Geometry::wkbType gt2= g2->get_type();
1079 #endif
1080 Point_set ptset;
1081 Multipoint mpts(g1->get_data_ptr(),
1082 g1->get_data_size(), g1->get_flags(), g1->get_srid());
1083
1084 assert(gt2 == Geometry::wkb_linestring ||
1085 gt2 == Geometry::wkb_polygon ||
1086 gt2 == Geometry::wkb_multilinestring ||
1087 gt2 == Geometry::wkb_multipolygon);
1088 ptset.insert(mpts.begin(), mpts.end());
1089
1090 Gis_geometry_collection *geocol= new Gis_geometry_collection(g2, result);
1091 auto_ptr<Gis_geometry_collection> guard(geocol);
1092 bool added= false;
1093
1094 for (TYPENAME Point_set::iterator i= ptset.begin(); i != ptset.end(); ++i)
1095 {
1096 Point &pt= const_cast<Point&>(*i);
1097 if (Ifsr::bg_geo_relation_check<Coordsys>
1098 (&pt, g2, Ifsr::SP_DISJOINT_FUNC, &null_value))
1099 {
1100 if (null_value || (null_value= geocol->append_geometry(&pt, result)))
1101 break;
1102 added= true;
1103 }
1104 }
1105
1106 if (null_value)
1107 return 0;
1108
1109 if (added)
1110 {
1111 // Result is already filled above.
1112 retgeo= geocol;
1113 guard.release();
1114 }
1115 else
1116 {
1117 retgeo= g2;
1118 null_value= g2->as_geometry(result, true);
1119 }
1120
1121 return retgeo;
1122 }
1123
1124
multilinestring_union_multilinestring(Geometry * g1,Geometry * g2,String * result)1125 Geometry *multilinestring_union_multilinestring(Geometry *g1, Geometry *g2,
1126 String *result)
1127 {
1128 Multilinestring mls1(g1->get_data_ptr(), g1->get_data_size(),
1129 g1->get_flags(), g1->get_srid());
1130 Multilinestring mls2(g2->get_data_ptr(), g2->get_data_size(),
1131 g2->get_flags(), g2->get_srid());
1132 auto_ptr<Multilinestring> res(new Multilinestring());
1133 res->set_srid(g1->get_srid());
1134
1135 boost::geometry::union_(mls1, mls2, *res);
1136 assert(res.get() != 0);
1137 assert(res->size() != 0);
1138 if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result))
1139 {
1140 my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1141 null_value= TRUE;
1142 return NULL;
1143 }
1144
1145 return res.release();
1146 }
1147
1148
multilinestring_union_multipolygon(Geometry * g1,Geometry * g2,String * result)1149 Geometry *multilinestring_union_multipolygon(Geometry *g1, Geometry *g2,
1150 String *result)
1151 {
1152 Geometry *retgeo= NULL;
1153 const void *g2_wkb= g2->normalize_ring_order();
1154 if (g2_wkb == NULL)
1155 {
1156 // Invalid polygon
1157 my_error(ER_GIS_INVALID_DATA, MYF(0), m_ifso->func_name());
1158 null_value= TRUE;
1159 return NULL;
1160 }
1161
1162 Multilinestring mls1(g1->get_data_ptr(), g1->get_data_size(),
1163 g1->get_flags(), g1->get_srid());
1164 Multipolygon mpy2(g2_wkb, g2->get_data_size(),
1165 g2->get_flags(), g2->get_srid());
1166 auto_ptr<Multilinestring> linestrings(new Multilinestring());
1167 linestrings->set_srid(g1->get_srid());
1168
1169 // Union(MultiLineString, MultiPolygon) isn't supported by BG, but
1170 // it's equivalent to GeometryCollection(MultiPolygon,
1171 // Difference(MultiLineString, MultiPolygon)).
1172 boost::geometry::difference(mls1, mpy2, *linestrings);
1173 assert(linestrings.get() != 0);
1174 if (post_fix_result(&m_ifso->bg_resbuf_mgr, *linestrings, NULL) &&
1175 linestrings->size() > 0)
1176 {
1177 my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1178 null_value= TRUE;
1179 return NULL;
1180 }
1181
1182 // Return the simplest result possible.
1183 if (linestrings->size() == 0)
1184 {
1185 // MultiPolygon result.
1186 g2->as_geometry(result, true);
1187 retgeo= g2;
1188 }
1189 else
1190 {
1191 // GeometryCollection result containing one or more Polygons and
1192 // one or more LineStrings.
1193 Gis_geometry_collection *collection= new Gis_geometry_collection();
1194
1195 if (mpy2.size() > 1)
1196 {
1197 collection->append_geometry(&mpy2, result);
1198 }
1199 else
1200 {
1201 mpy2[0].to_wkb_unparsed();
1202 collection->append_geometry(&mpy2[0], result);
1203 }
1204
1205 if (linestrings->size() > 1)
1206 {
1207 collection->append_geometry(&(*linestrings), result);
1208 }
1209 else
1210 {
1211 collection->append_geometry(&(*linestrings)[0], result);
1212 }
1213
1214 collection->set_ownmem(false);
1215 retgeo= collection;
1216 }
1217
1218 return retgeo;
1219 }
1220
1221
polygon_union_polygon(Geometry * g1,Geometry * g2,String * result)1222 Geometry *polygon_union_polygon(Geometry *g1, Geometry *g2, String *result)
1223 {
1224 Geometry *retgeo= NULL;
1225 const void *g1_wkb= g1->normalize_ring_order();
1226 const void *g2_wkb= g2->normalize_ring_order();
1227 if (g1_wkb == NULL || g2_wkb == NULL)
1228 {
1229 // Invalid polygon
1230 my_error(ER_GIS_INVALID_DATA, MYF(0), m_ifso->func_name());
1231 null_value= TRUE;
1232 return NULL;
1233 }
1234
1235 Polygon py1(g1->get_data_ptr(), g1->get_data_size(), g1->get_flags(),
1236 g1->get_srid());
1237 Polygon py2(g2_wkb, g2->get_data_size(), g2->get_flags(),
1238 g2->get_srid());
1239 auto_ptr<Multipolygon> res(new Multipolygon());
1240 res->set_srid(g1->get_srid());
1241
1242 boost::geometry::union_(py1, py2, *res);
1243 if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result) &&
1244 res->size() > 0)
1245 {
1246 my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1247 null_value= TRUE;
1248 return NULL;
1249 }
1250
1251 if (res->size() == 0)
1252 {
1253 // Invalid polygon
1254 my_error(ER_GIS_INVALID_DATA, MYF(0), m_ifso->func_name());
1255 null_value= TRUE;
1256 return NULL;
1257 }
1258
1259 retgeo= res.release();
1260 return retgeo;
1261 }
1262
1263
polygon_union_multipolygon(Geometry * g1,Geometry * g2,String * result)1264 Geometry *polygon_union_multipolygon(Geometry *g1, Geometry *g2,
1265 String *result)
1266 {
1267 Geometry *retgeo= NULL;
1268
1269 BGOPCALL(Multipolygon, retgeo, union_,
1270 Polygon, g1, Multipolygon, g2, result, null_value);
1271 return retgeo;
1272 }
1273
1274
multipolygon_union_multipolygon(Geometry * g1,Geometry * g2,String * result)1275 Geometry *multipolygon_union_multipolygon(Geometry *g1, Geometry *g2,
1276 String *result)
1277 {
1278 Geometry *retgeo= NULL;
1279
1280 BGOPCALL(Multipolygon, retgeo, union_,
1281 Multipolygon, g1, Multipolygon, g2, result, null_value);
1282
1283 return retgeo;
1284 }
1285
1286
point_difference_geometry(Geometry * g1,Geometry * g2,String * result)1287 Geometry *point_difference_geometry(Geometry *g1, Geometry *g2,
1288 String *result)
1289 {
1290 Geometry *retgeo= NULL;
1291 bool is_out= Ifsr::bg_geo_relation_check<Coordsys>
1292 (g1, g2, Ifsr::SP_DISJOINT_FUNC, &null_value);
1293
1294 if (!null_value)
1295 {
1296 if (is_out)
1297 {
1298 retgeo= g1;
1299 null_value= retgeo->as_geometry(result, true);
1300 }
1301 else
1302 {
1303 retgeo= m_ifso->empty_result(result, g1->get_srid());
1304 copy_ifso_state();
1305 }
1306 }
1307 return retgeo;
1308 }
1309
1310
multipoint_difference_geometry(Geometry * g1,Geometry * g2,String * result)1311 Geometry *multipoint_difference_geometry(Geometry *g1, Geometry *g2,
1312 String *result)
1313 {
1314 Geometry *retgeo= NULL;
1315 Multipoint *mpts= new Multipoint();
1316 auto_ptr<Multipoint> guard(mpts);
1317
1318 mpts->set_srid(g1->get_srid());
1319 Multipoint mpts1(g1->get_data_ptr(),
1320 g1->get_data_size(), g1->get_flags(), g1->get_srid());
1321 Point_set ptset;
1322
1323 for (TYPENAME Multipoint::iterator i= mpts1.begin();
1324 i != mpts1.end(); ++i)
1325 {
1326 if (Ifsr::bg_geo_relation_check<Coordsys>
1327 (&(*i), g2, Ifsr::SP_DISJOINT_FUNC, &null_value))
1328 {
1329 if (null_value)
1330 return 0;
1331 ptset.insert(*i);
1332 }
1333 }
1334
1335 if (ptset.empty() == false)
1336 {
1337 std::copy(ptset.begin(), ptset.end(), std::back_inserter(*mpts));
1338 null_value= m_ifso->assign_result(mpts, result);
1339 retgeo= mpts;
1340 guard.release();
1341 }
1342 else
1343 {
1344 if (!null_value)
1345 {
1346 retgeo= m_ifso->empty_result(result, g1->get_srid());
1347 copy_ifso_state();
1348 }
1349 }
1350 return retgeo;
1351 }
1352
1353
linestring_difference_linestring(Geometry * g1,Geometry * g2,String * result)1354 Geometry *linestring_difference_linestring(Geometry *g1, Geometry *g2,
1355 String *result)
1356 {
1357 Geometry *retgeo= NULL;
1358 Linestring ls1(g1->get_data_ptr(), g1->get_data_size(),
1359 g1->get_flags(), g1->get_srid());
1360 Linestring ls2(g2->get_data_ptr(), g2->get_data_size(),
1361 g2->get_flags(), g2->get_srid());
1362 auto_ptr<Multilinestring> res(new Multilinestring());
1363 res->set_srid(g1->get_srid());
1364
1365 boost::geometry::difference(ls1, ls2, *res);
1366
1367 // The call to ls->set_ptr() below assumes that result->length()
1368 // is the length of the LineString, which is only true if result
1369 // is empty to begin with.
1370 assert(result->length() == 0);
1371
1372 if (res->size() == 0)
1373 {
1374 post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result);
1375 retgeo= m_ifso->empty_result(result, g1->get_srid());
1376 }
1377 else if (res->size() == 1)
1378 {
1379 if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, NULL))
1380 {
1381 my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1382 null_value= TRUE;
1383 return NULL;
1384 }
1385 Linestring *ls= new Linestring();
1386 res->begin()->as_geometry(result, false);
1387 ls->set_ptr(const_cast<char*>(result->ptr()) + GEOM_HEADER_SIZE,
1388 result->length() - GEOM_HEADER_SIZE);
1389 ls->set_ownmem(false);
1390 retgeo= ls;
1391 }
1392 else
1393 {
1394 if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result))
1395 {
1396 my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1397 null_value= TRUE;
1398 return NULL;
1399 }
1400 retgeo= res.release();
1401 }
1402
1403 return retgeo;
1404 }
1405
1406
linestring_difference_polygon(Geometry * g1,Geometry * g2,String * result)1407 Geometry *linestring_difference_polygon(Geometry *g1, Geometry *g2,
1408 String *result)
1409 {
1410 Geometry *retgeo= NULL;
1411
1412 BGOPCALL(Multilinestring, retgeo, difference,
1413 Linestring, g1, Polygon, g2, result, null_value);
1414
1415 if (!retgeo && !null_value)
1416 {
1417 retgeo= m_ifso->empty_result(result, g1->get_srid());
1418 copy_ifso_state();
1419 }
1420
1421 return retgeo;
1422 }
1423
1424
linestring_difference_multilinestring(Geometry * g1,Geometry * g2,String * result)1425 Geometry *linestring_difference_multilinestring(Geometry *g1, Geometry *g2,
1426 String *result)
1427 {
1428 Geometry *retgeo= NULL;
1429 Linestring ls1(g1->get_data_ptr(), g1->get_data_size(),
1430 g1->get_flags(), g1->get_srid());
1431 Multilinestring mls2(g2->get_data_ptr(), g2->get_data_size(),
1432 g2->get_flags(), g2->get_srid());
1433 auto_ptr<Multilinestring> res(new Multilinestring());
1434 res->set_srid(g1->get_srid());
1435
1436 boost::geometry::difference(ls1, mls2, *res);
1437
1438 // The call to ls->set_ptr() below assumes that result->length()
1439 // is the length of the LineString, which is only true if result
1440 // is empty to begin with.
1441 assert(result->length() == 0);
1442
1443 if (res->size() == 0)
1444 {
1445 post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result);
1446 retgeo= m_ifso->empty_result(result, g1->get_srid());
1447 }
1448 else if (res->size() == 1)
1449 {
1450 if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, NULL))
1451 {
1452 my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1453 null_value= TRUE;
1454 return NULL;
1455 }
1456 Linestring *ls= new Linestring();
1457 res->begin()->as_geometry(result, false);
1458 ls->set_ptr(const_cast<char*>(result->ptr()) + GEOM_HEADER_SIZE,
1459 result->length() - GEOM_HEADER_SIZE);
1460 ls->set_ownmem(false);
1461 retgeo= ls;
1462 }
1463 else
1464 {
1465 if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result))
1466 {
1467 my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1468 null_value= TRUE;
1469 return NULL;
1470 }
1471 retgeo= res.release();
1472 }
1473
1474 return retgeo;
1475 }
1476
1477
linestring_difference_multipolygon(Geometry * g1,Geometry * g2,String * result)1478 Geometry *linestring_difference_multipolygon(Geometry *g1, Geometry *g2,
1479 String *result)
1480 {
1481 Geometry *retgeo= NULL;
1482
1483 BGOPCALL(Multilinestring, retgeo, difference,
1484 Linestring, g1, Multipolygon, g2, result, null_value);
1485
1486 if (!retgeo && !null_value)
1487 {
1488 retgeo= m_ifso->empty_result(result, g1->get_srid());
1489 copy_ifso_state();
1490 }
1491 return retgeo;
1492 }
1493
1494
polygon_difference_polygon(Geometry * g1,Geometry * g2,String * result)1495 Geometry *polygon_difference_polygon(Geometry *g1, Geometry *g2,
1496 String *result)
1497 {
1498 Geometry *retgeo= NULL;
1499
1500 BGOPCALL(Multipolygon, retgeo, difference,
1501 Polygon, g1, Polygon, g2, result, null_value);
1502
1503 if (!retgeo && !null_value)
1504 {
1505 retgeo= m_ifso->empty_result(result, g1->get_srid());
1506 copy_ifso_state();
1507 }
1508 return retgeo;
1509 }
1510
1511
polygon_difference_multipolygon(Geometry * g1,Geometry * g2,String * result)1512 Geometry *polygon_difference_multipolygon(Geometry *g1, Geometry *g2,
1513 String *result)
1514 {
1515 Geometry *retgeo= NULL;
1516
1517 BGOPCALL(Multipolygon, retgeo, difference,
1518 Polygon, g1, Multipolygon, g2, result, null_value);
1519
1520 if (!retgeo && !null_value)
1521 {
1522 retgeo= m_ifso->empty_result(result, g1->get_srid());
1523 copy_ifso_state();
1524 }
1525 return retgeo;
1526 }
1527
1528
multilinestring_difference_linestring(Geometry * g1,Geometry * g2,String * result)1529 Geometry *multilinestring_difference_linestring(Geometry *g1, Geometry *g2,
1530 String *result)
1531 {
1532 Geometry *retgeo= NULL;
1533 Multilinestring mls1(g1->get_data_ptr(), g1->get_data_size(),
1534 g1->get_flags(), g1->get_srid());
1535 Linestring ls2(g2->get_data_ptr(), g2->get_data_size(),
1536 g2->get_flags(), g2->get_srid());
1537 auto_ptr<Multilinestring> res(new Multilinestring());
1538 res->set_srid(g1->get_srid());
1539
1540 boost::geometry::difference(mls1, ls2, *res);
1541
1542 // The call to ls->set_ptr() below assumes that result->length()
1543 // is the length of the LineString, which is only true if result
1544 // is empty to begin with.
1545 assert(result->length() == 0);
1546
1547 if (res->size() == 0)
1548 {
1549 post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result);
1550 retgeo= m_ifso->empty_result(result, g1->get_srid());
1551 }
1552 else if (res->size() == 1)
1553 {
1554 if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, NULL))
1555 {
1556 my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1557 null_value= TRUE;
1558 return NULL;
1559 }
1560 Linestring *ls= new Linestring();
1561 res->begin()->as_geometry(result, false);
1562 ls->set_ptr(const_cast<char*>(result->ptr()) + GEOM_HEADER_SIZE,
1563 result->length() - GEOM_HEADER_SIZE);
1564 ls->set_ownmem(false);
1565 retgeo= ls;
1566 }
1567 else
1568 {
1569 if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result))
1570 {
1571 my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1572 null_value= TRUE;
1573 return NULL;
1574 }
1575 retgeo= res.release();
1576 }
1577
1578 return retgeo;
1579 }
1580
1581
multilinestring_difference_polygon(Geometry * g1,Geometry * g2,String * result)1582 Geometry *multilinestring_difference_polygon(Geometry *g1, Geometry *g2,
1583 String *result)
1584 {
1585 Geometry *retgeo= NULL;
1586
1587 BGOPCALL(Multilinestring, retgeo, difference,
1588 Multilinestring, g1, Polygon, g2, result, null_value);
1589
1590 if (!retgeo && !null_value)
1591 {
1592 retgeo= m_ifso->empty_result(result, g1->get_srid());
1593 copy_ifso_state();
1594 }
1595 return retgeo;
1596 }
1597
1598
multilinestring_difference_multilinestring(Geometry * g1,Geometry * g2,String * result)1599 Geometry *multilinestring_difference_multilinestring(Geometry *g1,
1600 Geometry *g2,
1601 String *result)
1602 {
1603 Geometry *retgeo= NULL;
1604 Multilinestring mls1(g1->get_data_ptr(), g1->get_data_size(),
1605 g1->get_flags(), g1->get_srid());
1606 Multilinestring mls2(g2->get_data_ptr(), g2->get_data_size(),
1607 g2->get_flags(), g2->get_srid());
1608 auto_ptr<Multilinestring> res(new Multilinestring());
1609 res->set_srid(g1->get_srid());
1610
1611 boost::geometry::difference(mls1, mls2, *res);
1612
1613 // The call to ls->set_ptr() below assumes that result->length()
1614 // is the length of the LineString, which is only true if result
1615 // is empty to begin with.
1616 assert(result->length() == 0);
1617
1618 if (res->size() == 0)
1619 {
1620 post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result);
1621 retgeo= m_ifso->empty_result(result, g1->get_srid());
1622 }
1623 else if (res->size() == 1)
1624 {
1625 if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, NULL))
1626 {
1627 my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1628 null_value= TRUE;
1629 return NULL;
1630 }
1631 Linestring *ls= new Linestring();
1632 res->begin()->as_geometry(result, false);
1633 ls->set_ptr(const_cast<char*>(result->ptr()) + GEOM_HEADER_SIZE,
1634 result->length() - GEOM_HEADER_SIZE);
1635 ls->set_ownmem(false);
1636 retgeo= ls;
1637 }
1638 else
1639 {
1640 if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result))
1641 {
1642 my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1643 null_value= TRUE;
1644 return NULL;
1645 }
1646 retgeo= res.release();
1647 }
1648
1649 return retgeo;
1650 }
1651
1652
multilinestring_difference_multipolygon(Geometry * g1,Geometry * g2,String * result)1653 Geometry *multilinestring_difference_multipolygon(Geometry *g1, Geometry *g2,
1654 String *result)
1655 {
1656 Geometry *retgeo= NULL;
1657
1658 BGOPCALL(Multilinestring, retgeo, difference,
1659 Multilinestring, g1, Multipolygon, g2, result, null_value);
1660
1661 if (!retgeo && !null_value)
1662 {
1663 retgeo= m_ifso->empty_result(result, g1->get_srid());
1664 copy_ifso_state();
1665 }
1666 return retgeo;
1667 }
1668
1669
multipolygon_difference_polygon(Geometry * g1,Geometry * g2,String * result)1670 Geometry *multipolygon_difference_polygon(Geometry *g1, Geometry *g2,
1671 String *result)
1672 {
1673 Geometry *retgeo= NULL;
1674
1675 BGOPCALL(Multipolygon, retgeo, difference,
1676 Multipolygon, g1, Polygon, g2, result, null_value);
1677
1678 if (!retgeo && !null_value)
1679 {
1680 retgeo= m_ifso->empty_result(result, g1->get_srid());
1681 copy_ifso_state();
1682 }
1683 return retgeo;
1684 }
1685
1686
multipolygon_difference_multipolygon(Geometry * g1,Geometry * g2,String * result)1687 Geometry *multipolygon_difference_multipolygon(Geometry *g1, Geometry *g2,
1688 String *result)
1689 {
1690 Geometry *retgeo= NULL;
1691
1692 BGOPCALL(Multipolygon, retgeo, difference,
1693 Multipolygon, g1, Multipolygon, g2, result, null_value);
1694
1695 if (!retgeo && !null_value)
1696 {
1697 retgeo= m_ifso->empty_result(result, g1->get_srid());
1698 copy_ifso_state();
1699 }
1700 return retgeo;
1701 }
1702
1703
linestring_symdifference_linestring(Geometry * g1,Geometry * g2,String * result)1704 Geometry *linestring_symdifference_linestring(Geometry *g1, Geometry *g2,
1705 String *result)
1706 {
1707 Geometry *retgeo= NULL;
1708 Linestring ls1(g1->get_data_ptr(), g1->get_data_size(),
1709 g1->get_flags(), g1->get_srid());
1710 Linestring ls2(g2->get_data_ptr(), g2->get_data_size(),
1711 g2->get_flags(), g2->get_srid());
1712 auto_ptr<Multilinestring> res(new Multilinestring());
1713 res->set_srid(g1->get_srid());
1714
1715 boost::geometry::sym_difference(ls1, ls2, *res);
1716 assert(res.get() != 0);
1717 if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result) &&
1718 res->size() > 0)
1719 {
1720 my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1721 null_value= TRUE;
1722 return NULL;
1723 }
1724
1725 if (res->size() == 0)
1726 {
1727 retgeo= m_ifso->empty_result(result, g1->get_srid());
1728 }
1729 else
1730 {
1731 retgeo= res.release();
1732 }
1733
1734 return retgeo;
1735 }
1736
1737
linestring_symdifference_multilinestring(Geometry * g1,Geometry * g2,String * result)1738 Geometry *linestring_symdifference_multilinestring(Geometry *g1, Geometry *g2,
1739 String *result)
1740 {
1741 Geometry *retgeo= NULL;
1742 Linestring ls1(g1->get_data_ptr(), g1->get_data_size(),
1743 g1->get_flags(), g1->get_srid());
1744 Multilinestring mls2(g2->get_data_ptr(), g2->get_data_size(),
1745 g2->get_flags(), g2->get_srid());
1746 auto_ptr<Multilinestring> res(new Multilinestring());
1747 res->set_srid(g1->get_srid());
1748
1749 boost::geometry::sym_difference(ls1, mls2, *res);
1750 assert(res.get() != 0);
1751 if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result) &&
1752 res->size() > 0)
1753 {
1754 my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1755 null_value= TRUE;
1756 return NULL;
1757 }
1758
1759 if (res->size() == 0)
1760 {
1761 retgeo= m_ifso->empty_result(result, g1->get_srid());
1762 }
1763 else
1764 {
1765 retgeo= res.release();
1766 }
1767
1768 return retgeo;
1769 }
1770
1771
polygon_symdifference_polygon(Geometry * g1,Geometry * g2,String * result)1772 Geometry *polygon_symdifference_polygon(Geometry *g1, Geometry *g2,
1773 String *result)
1774 {
1775 Geometry *retgeo= NULL;
1776
1777 BGOPCALL(Multipolygon, retgeo, sym_difference,
1778 Polygon, g1, Polygon, g2, result, null_value);
1779
1780 if (!retgeo && !null_value)
1781 {
1782 retgeo= m_ifso->empty_result(result, g1->get_srid());
1783 copy_ifso_state();
1784 }
1785 return retgeo;
1786 }
1787
1788
polygon_symdifference_multipolygon(Geometry * g1,Geometry * g2,String * result)1789 Geometry *polygon_symdifference_multipolygon(Geometry *g1, Geometry *g2,
1790 String *result)
1791 {
1792 Geometry *retgeo= NULL;
1793
1794 BGOPCALL(Multipolygon, retgeo, sym_difference,
1795 Polygon, g1, Multipolygon, g2, result, null_value);
1796
1797 if (!retgeo && !null_value)
1798 {
1799 retgeo= m_ifso->empty_result(result, g1->get_srid());
1800 copy_ifso_state();
1801 }
1802 return retgeo;
1803 }
1804
1805
multilinestring_symdifference_multilinestring(Geometry * g1,Geometry * g2,String * result)1806 Geometry *multilinestring_symdifference_multilinestring(Geometry *g1,
1807 Geometry *g2,
1808 String *result)
1809 {
1810 Geometry *retgeo= NULL;
1811 Multilinestring mls1(g1->get_data_ptr(), g1->get_data_size(),
1812 g1->get_flags(), g1->get_srid());
1813 Multilinestring mls2(g2->get_data_ptr(), g2->get_data_size(),
1814 g2->get_flags(), g2->get_srid());
1815 auto_ptr<Multilinestring> res(new Multilinestring());
1816 res->set_srid(g1->get_srid());
1817
1818 boost::geometry::sym_difference(mls1, mls2, *res);
1819 assert(res.get() != 0);
1820 if (post_fix_result(&m_ifso->bg_resbuf_mgr, *res, result) &&
1821 res->size() > 0)
1822 {
1823 my_error(ER_GIS_UNKNOWN_ERROR, MYF(0), m_ifso->func_name());
1824 null_value= TRUE;
1825 return NULL;
1826 }
1827
1828 if (res->size() == 0)
1829 {
1830 retgeo= m_ifso->empty_result(result, g1->get_srid());
1831 }
1832 else
1833 {
1834 retgeo= res.release();
1835 }
1836
1837 return retgeo;
1838 }
1839
1840
multipolygon_symdifference_polygon(Geometry * g1,Geometry * g2,String * result)1841 Geometry *multipolygon_symdifference_polygon(Geometry *g1, Geometry *g2,
1842 String *result)
1843 {
1844 Geometry *retgeo= NULL;
1845
1846 BGOPCALL(Multipolygon, retgeo, sym_difference,
1847 Multipolygon, g1, Polygon, g2, result, null_value);
1848
1849 if (!retgeo && !null_value)
1850 {
1851 retgeo= m_ifso->empty_result(result, g1->get_srid());
1852 copy_ifso_state();
1853 }
1854 return retgeo;
1855 }
1856
1857
multipolygon_symdifference_multipolygon(Geometry * g1,Geometry * g2,String * result)1858 Geometry *multipolygon_symdifference_multipolygon(Geometry *g1, Geometry *g2,
1859 String *result)
1860 {
1861 Geometry *retgeo= NULL;
1862
1863 BGOPCALL(Multipolygon, retgeo, sym_difference,
1864 Multipolygon, g1, Multipolygon, g2, result, null_value);
1865
1866 if (!retgeo && !null_value)
1867 {
1868 retgeo= m_ifso->empty_result(result, g1->get_srid());
1869 copy_ifso_state();
1870 }
1871 return retgeo;
1872 }
1873 };
1874
1875
1876 /**
1877 Do intersection operation for two geometries, dispatch to specific BG
1878 function wrapper calls according to set operation type, and the 1st or
1879 both operand types.
1880
1881 @tparam Geom_types A wrapper for all geometry types.
1882 @param g1 First Geometry operand, not a geometry collection.
1883 @param g2 Second Geometry operand, not a geometry collection.
1884 @param[out] result Holds WKB data of the result.
1885 @return The result geometry whose WKB data is held in result.
1886 */
1887 template <typename Geom_types>
1888 Geometry *Item_func_spatial_operation::
intersection_operation(Geometry * g1,Geometry * g2,String * result)1889 intersection_operation(Geometry *g1, Geometry *g2,
1890 String *result)
1891 {
1892 typedef typename Geom_types::Coordinate_type Coord_type;
1893 typedef typename Geom_types::Coordinate_system Coordsys;
1894
1895 BG_setop_wrapper<Geom_types> wrap(this);
1896 Geometry *retgeo= NULL;
1897 Geometry::wkbType gt1= g1->get_type();
1898 Geometry::wkbType gt2= g2->get_type();
1899
1900 switch (gt1)
1901 {
1902 case Geometry::wkb_point:
1903 switch (gt2)
1904 {
1905 case Geometry::wkb_point:
1906 retgeo= wrap.point_intersection_point(g1, g2, result);
1907 break;
1908 case Geometry::wkb_multipoint:
1909 retgeo= wrap.point_intersection_multipoint(g1, g2, result);
1910 break;
1911 case Geometry::wkb_linestring:
1912 case Geometry::wkb_polygon:
1913 case Geometry::wkb_multilinestring:
1914 case Geometry::wkb_multipolygon:
1915 retgeo= wrap.point_intersection_geometry(g1, g2, result);
1916 break;
1917 default:
1918 break;
1919 }
1920
1921 break;
1922 case Geometry::wkb_multipoint:
1923 switch (gt2)
1924 {
1925 case Geometry::wkb_point:
1926 retgeo= wrap.point_intersection_multipoint(g2, g1, result);
1927 break;
1928
1929 case Geometry::wkb_multipoint:
1930 retgeo= wrap.multipoint_intersection_multipoint(g1, g2, result);
1931 break;
1932 case Geometry::wkb_linestring:
1933 case Geometry::wkb_polygon:
1934 case Geometry::wkb_multilinestring:
1935 case Geometry::wkb_multipolygon:
1936 retgeo= wrap.multipoint_intersection_geometry(g1, g2, result);
1937 break;
1938 default:
1939 break;
1940 }
1941 break;
1942 case Geometry::wkb_linestring:
1943 switch (gt2)
1944 {
1945 case Geometry::wkb_point:
1946 case Geometry::wkb_multipoint:
1947 retgeo= intersection_operation<Geom_types>(g2, g1, result);
1948 break;
1949 case Geometry::wkb_linestring:
1950 retgeo= wrap.linestring_intersection_linestring(g1, g2, result);
1951 break;
1952 case Geometry::wkb_multilinestring:
1953 retgeo= wrap.linestring_intersection_multilinestring(g1, g2, result);
1954 break;
1955 case Geometry::wkb_polygon:
1956 case Geometry::wkb_multipolygon:
1957 retgeo= wrap.linestring_intersection_polygon(g1, g2, result);
1958 break;
1959 default:
1960 break;
1961 }
1962 break;
1963 case Geometry::wkb_polygon:
1964 switch (gt2)
1965 {
1966 case Geometry::wkb_point:
1967 case Geometry::wkb_multipoint:
1968 case Geometry::wkb_linestring:
1969 retgeo= intersection_operation<Geom_types>(g2, g1, result);
1970 break;
1971 case Geometry::wkb_multilinestring:
1972 retgeo= wrap.polygon_intersection_multilinestring(g1, g2, result);
1973 break;
1974 case Geometry::wkb_polygon:
1975 case Geometry::wkb_multipolygon:
1976 // Note: for now BG's set operations don't allow returning a
1977 // Multilinestring, thus this result isn't complete.
1978 retgeo= wrap.polygon_intersection_polygon(g1, g2, result);
1979 break;
1980 default:
1981 break;
1982 }
1983 break;
1984 case Geometry::wkb_multilinestring:
1985 switch (gt2)
1986 {
1987 case Geometry::wkb_point:
1988 case Geometry::wkb_multipoint:
1989 case Geometry::wkb_linestring:
1990 case Geometry::wkb_polygon:
1991 retgeo= intersection_operation<Geom_types>(g2, g1, result);
1992 break;
1993 case Geometry::wkb_multilinestring:
1994 retgeo= wrap.multilinestring_intersection_multilinestring(g1, g2, result);
1995 break;
1996
1997 case Geometry::wkb_multipolygon:
1998 retgeo= wrap.multilinestring_intersection_multipolygon(g1, g2, result);
1999 break;
2000 default:
2001 break;
2002 }
2003 break;
2004 case Geometry::wkb_multipolygon:
2005 switch (gt2)
2006 {
2007 case Geometry::wkb_point:
2008 case Geometry::wkb_multipoint:
2009 case Geometry::wkb_linestring:
2010 case Geometry::wkb_multilinestring:
2011 case Geometry::wkb_polygon:
2012 retgeo= intersection_operation<Geom_types>(g2, g1, result);
2013 break;
2014 case Geometry::wkb_multipolygon:
2015 retgeo= wrap.multipolygon_intersection_multipolygon(g1, g2, result);
2016 break;
2017 default:
2018 break;
2019 }
2020 break;
2021 default:
2022 break;
2023 }
2024
2025 if (!null_value)
2026 null_value= wrap.get_null_value();
2027 return retgeo;
2028 }
2029
2030
2031 /**
2032 Do union operation for two geometries, dispatch to specific BG
2033 function wrapper calls according to set operation type, and the 1st or
2034 both operand types.
2035
2036 @tparam Geom_types A wrapper for all geometry types.
2037 @param g1 First Geometry operand, not a geometry collection.
2038 @param g2 Second Geometry operand, not a geometry collection.
2039 @param[out] result Holds WKB data of the result.
2040 @return The result geometry whose WKB data is held in result.
2041 */
2042 template <typename Geom_types>
2043 Geometry *Item_func_spatial_operation::
union_operation(Geometry * g1,Geometry * g2,String * result)2044 union_operation(Geometry *g1, Geometry *g2, String *result)
2045 {
2046 typedef typename Geom_types::Coordinate_type Coord_type;
2047 typedef typename Geom_types::Coordinate_system Coordsys;
2048
2049 BG_setop_wrapper<Geom_types> wrap(this);
2050 Geometry *retgeo= NULL;
2051 Geometry::wkbType gt1= g1->get_type();
2052 Geometry::wkbType gt2= g2->get_type();
2053
2054 // Note that union can't produce empty point set unless given two empty
2055 // point set arguments.
2056 switch (gt1)
2057 {
2058 case Geometry::wkb_point:
2059 switch (gt2)
2060 {
2061 case Geometry::wkb_point:
2062 case Geometry::wkb_multipoint:
2063 retgeo= wrap.point_union_point(g1, g2, result);
2064 break;
2065 case Geometry::wkb_linestring:
2066 case Geometry::wkb_multilinestring:
2067 case Geometry::wkb_polygon:
2068 case Geometry::wkb_multipolygon:
2069 retgeo= wrap.point_union_geometry(g1, g2, result);
2070 break;
2071 default:
2072 break;
2073 }
2074 break;
2075 case Geometry::wkb_multipoint:
2076 switch (gt2)
2077 {
2078 case Geometry::wkb_point:
2079 retgeo= wrap.point_union_point(g2, g1, result);
2080 break;
2081 case Geometry::wkb_multipoint:
2082 retgeo= wrap.multipoint_union_multipoint(g1, g2, result);
2083 break;
2084 case Geometry::wkb_linestring:
2085 case Geometry::wkb_multilinestring:
2086 case Geometry::wkb_polygon:
2087 case Geometry::wkb_multipolygon:
2088 retgeo= wrap.multipoint_union_geometry(g1, g2, result);
2089 break;
2090 default:
2091 break;
2092 }
2093 break;
2094 case Geometry::wkb_linestring:
2095 switch (gt2)
2096 {
2097 case Geometry::wkb_point:
2098 case Geometry::wkb_multipoint:
2099 retgeo= union_operation<Geom_types>(g2, g1, result);
2100 break;
2101 case Geometry::wkb_linestring:
2102 retgeo= wrap.linestring_union_linestring(g1, g2, result);
2103 break;
2104 case Geometry::wkb_multilinestring:
2105 retgeo= wrap.linestring_union_multilinestring(g1, g2, result);
2106 break;
2107 case Geometry::wkb_polygon:
2108 retgeo= wrap.linestring_union_polygon(g1, g2, result);
2109 break;
2110 case Geometry::wkb_multipolygon:
2111 retgeo= wrap.linestring_union_multipolygon(g1, g2, result);
2112 break;
2113 default:
2114 break;
2115 }
2116 break;
2117 case Geometry::wkb_polygon:
2118 switch (gt2)
2119 {
2120 case Geometry::wkb_point:
2121 case Geometry::wkb_multipoint:
2122 case Geometry::wkb_linestring:
2123 retgeo= union_operation<Geom_types>(g2, g1, result);
2124 break;
2125 case Geometry::wkb_multilinestring:
2126 retgeo= wrap.polygon_union_multilinestring(g1, g2, result);
2127 break;
2128 case Geometry::wkb_polygon:
2129 retgeo= wrap.polygon_union_polygon(g1, g2, result);
2130 break;
2131 case Geometry::wkb_multipolygon:
2132 retgeo= wrap.polygon_union_multipolygon(g1, g2, result);
2133 break;
2134 default:
2135 break;
2136 }
2137 break;
2138 case Geometry::wkb_multilinestring:
2139 switch (gt2)
2140 {
2141 case Geometry::wkb_point:
2142 case Geometry::wkb_multipoint:
2143 case Geometry::wkb_linestring:
2144 case Geometry::wkb_polygon:
2145 retgeo= union_operation<Geom_types>(g2, g1, result);
2146 break;
2147 break;
2148 case Geometry::wkb_multilinestring:
2149 retgeo= wrap.multilinestring_union_multilinestring(g1, g2, result);
2150 break;
2151 case Geometry::wkb_multipolygon:
2152 retgeo= wrap.multilinestring_union_multipolygon(g1, g2, result);
2153 break;
2154 default:
2155 break;
2156 }
2157 break;
2158 case Geometry::wkb_multipolygon:
2159 switch (gt2)
2160 {
2161 case Geometry::wkb_point:
2162 case Geometry::wkb_multipoint:
2163 case Geometry::wkb_linestring:
2164 case Geometry::wkb_polygon:
2165 case Geometry::wkb_multilinestring:
2166 retgeo= union_operation<Geom_types>(g2, g1, result);
2167 break;
2168 case Geometry::wkb_multipolygon:
2169 retgeo= wrap.multipolygon_union_multipolygon(g1, g2, result);
2170 break;
2171 default:
2172 break;
2173 }
2174 break;
2175 default:
2176 break;
2177 }
2178
2179 if (!null_value)
2180 null_value= wrap.get_null_value() && maybe_null;
2181 if (!null_value && retgeo == NULL)
2182 {
2183 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
2184 error_str();
2185 return NULL;
2186 }
2187 return retgeo;
2188 }
2189
2190
2191 /**
2192 Do difference operation for two geometries, dispatch to specific BG
2193 function wrapper calls according to set operation type, and the 1st or
2194 both operand types.
2195
2196 @tparam Geom_types A wrapper for all geometry types.
2197 @param g1 First Geometry operand, not a geometry collection.
2198 @param g2 Second Geometry operand, not a geometry collection.
2199 @param[out] result Holds WKB data of the result.
2200 @return The result geometry whose WKB data is held in result.
2201 */
2202 template <typename Geom_types>
2203 Geometry *Item_func_spatial_operation::
difference_operation(Geometry * g1,Geometry * g2,String * result)2204 difference_operation(Geometry *g1, Geometry *g2, String *result)
2205 {
2206 BG_setop_wrapper<Geom_types> wrap(this);
2207 Geometry *retgeo= NULL;
2208 Geometry::wkbType gt1= g1->get_type();
2209 Geometry::wkbType gt2= g2->get_type();
2210
2211 /*
2212 Given two geometries g1 and g2, where g1.dimension < g2.dimension, then
2213 g2 - g1 is equal to g2, this is always true. This is how postgis works.
2214 Below implementation uses this fact.
2215 */
2216 switch (gt1)
2217 {
2218 case Geometry::wkb_point:
2219 switch (gt2)
2220 {
2221 case Geometry::wkb_point:
2222 case Geometry::wkb_multipoint:
2223 case Geometry::wkb_linestring:
2224 case Geometry::wkb_polygon:
2225 case Geometry::wkb_multilinestring:
2226 case Geometry::wkb_multipolygon:
2227 retgeo= wrap.point_difference_geometry(g1, g2, result);
2228 break;
2229 default:
2230 break;
2231 }
2232 break;
2233 case Geometry::wkb_multipoint:
2234 switch (gt2)
2235 {
2236 case Geometry::wkb_point:
2237 case Geometry::wkb_multipoint:
2238 case Geometry::wkb_linestring:
2239 case Geometry::wkb_polygon:
2240 case Geometry::wkb_multilinestring:
2241 case Geometry::wkb_multipolygon:
2242 retgeo= wrap.multipoint_difference_geometry(g1, g2, result);
2243 break;
2244 default:
2245 break;
2246 }
2247 break;
2248 case Geometry::wkb_linestring:
2249 switch (gt2)
2250 {
2251 case Geometry::wkb_point:
2252 case Geometry::wkb_multipoint:
2253 retgeo= g1;
2254 null_value= g1->as_geometry(result, true);
2255 break;
2256 case Geometry::wkb_linestring:
2257 retgeo= wrap.linestring_difference_linestring(g1, g2, result);
2258 break;
2259 case Geometry::wkb_polygon:
2260 retgeo= wrap.linestring_difference_polygon(g1, g2, result);
2261 break;
2262 case Geometry::wkb_multilinestring:
2263 retgeo= wrap.linestring_difference_multilinestring(g1, g2, result);
2264 break;
2265 case Geometry::wkb_multipolygon:
2266 retgeo= wrap.linestring_difference_multipolygon(g1, g2, result);
2267 break;
2268 default:
2269 break;
2270 }
2271 break;
2272 case Geometry::wkb_polygon:
2273 switch (gt2)
2274 {
2275 case Geometry::wkb_point:
2276 case Geometry::wkb_multipoint:
2277 case Geometry::wkb_linestring:
2278 case Geometry::wkb_multilinestring:
2279 retgeo= g1;
2280 null_value= g1->as_geometry(result, true);
2281 break;
2282 case Geometry::wkb_polygon:
2283 retgeo= wrap.polygon_difference_polygon(g1, g2, result);
2284 break;
2285 case Geometry::wkb_multipolygon:
2286 retgeo= wrap.polygon_difference_multipolygon(g1, g2, result);
2287 break;
2288 default:
2289 break;
2290 }
2291 break;
2292 case Geometry::wkb_multilinestring:
2293 switch (gt2)
2294 {
2295 case Geometry::wkb_point:
2296 case Geometry::wkb_multipoint:
2297 retgeo= g1;
2298 null_value= g1->as_geometry(result, true);
2299 break;
2300 case Geometry::wkb_linestring:
2301 retgeo= wrap.multilinestring_difference_linestring(g1, g2, result);
2302 break;
2303 case Geometry::wkb_polygon:
2304 retgeo= wrap.multilinestring_difference_polygon(g1, g2, result);
2305 break;
2306 case Geometry::wkb_multilinestring:
2307 retgeo= wrap.multilinestring_difference_multilinestring(g1, g2, result);
2308 break;
2309 case Geometry::wkb_multipolygon:
2310 retgeo= wrap.multilinestring_difference_multipolygon(g1, g2,
2311 result);
2312 break;
2313 default:
2314 break;
2315 }
2316 break;
2317 case Geometry::wkb_multipolygon:
2318 switch (gt2)
2319 {
2320 case Geometry::wkb_point:
2321 case Geometry::wkb_multipoint:
2322 case Geometry::wkb_linestring:
2323 case Geometry::wkb_multilinestring:
2324 retgeo= g1;
2325 null_value= g1->as_geometry(result, true);
2326 break;
2327 case Geometry::wkb_polygon:
2328 retgeo= wrap.multipolygon_difference_polygon(g1, g2, result);
2329 break;
2330 case Geometry::wkb_multipolygon:
2331 retgeo= wrap.multipolygon_difference_multipolygon(g1, g2, result);
2332 break;
2333 default:
2334 break;
2335 }
2336 break;
2337 default:
2338 break;
2339 }
2340
2341 if (!null_value)
2342 null_value= wrap.get_null_value();
2343 return retgeo;
2344 }
2345
2346
2347 /**
2348 Do symdifference operation for two geometries, dispatch to specific BG
2349 function wrapper calls according to set operation type, and the 1st or
2350 both operand types.
2351
2352 @tparam Geom_types A wrapper for all geometry types.
2353 @param g1 First Geometry operand, not a geometry collection.
2354 @param g2 Second Geometry operand, not a geometry collection.
2355 @param[out] result Holds WKB data of the result.
2356 @return The result geometry whose WKB data is held in result.
2357 */
2358 template <typename Geom_types>
2359 Geometry *Item_func_spatial_operation::
symdifference_operation(Geometry * g1,Geometry * g2,String * result)2360 symdifference_operation(Geometry *g1, Geometry *g2, String *result)
2361 {
2362 typedef typename Geom_types::Coordinate_type Coord_type;
2363 typedef typename Geom_types::Coordinate_system Coordsys;
2364
2365 BG_setop_wrapper<Geom_types> wrap(this);
2366 Geometry *retgeo= NULL;
2367 Geometry::wkbType gt1= g1->get_type();
2368 Geometry::wkbType gt2= g2->get_type();
2369
2370 /*
2371 SymDifference(L, A) isn't supported by BG, but it's equivalent
2372 to
2373
2374 GeometryCollection(Difference(A, L), Difference(L, A))
2375
2376 Since geometries must be closed, Difference(A, L) is equivalent
2377 to A, so we can simplify to
2378
2379 GeometryCollection(A, Difference(L, A))
2380
2381 This is equivalent to Union(L, A), so we reuse that implementation.
2382 */
2383 bool do_geocol_setop= false;
2384
2385 switch (gt1)
2386 {
2387 case Geometry::wkb_linestring:
2388 switch (gt2)
2389 {
2390 case Geometry::wkb_linestring:
2391 retgeo= wrap.linestring_symdifference_linestring(g1, g2, result);
2392 break;
2393 case Geometry::wkb_polygon:
2394 retgeo= wrap.linestring_union_polygon(g1, g2, result);
2395 break;
2396 case Geometry::wkb_multilinestring:
2397 retgeo= wrap.linestring_symdifference_multilinestring(g1, g2, result);
2398 break;
2399 case Geometry::wkb_multipolygon:
2400 retgeo= wrap.linestring_union_multipolygon(g1, g2, result);
2401 break;
2402 default:
2403 do_geocol_setop= true;
2404 break;
2405 }
2406 break;
2407 case Geometry::wkb_polygon:
2408
2409 switch (gt2)
2410 {
2411 case Geometry::wkb_linestring:
2412 retgeo= wrap.linestring_union_polygon(g2, g1, result);
2413 break;
2414 case Geometry::wkb_polygon:
2415 retgeo= wrap.polygon_symdifference_polygon(g1, g2, result);
2416 break;
2417 case Geometry::wkb_multilinestring:
2418 retgeo= wrap.polygon_union_multilinestring(g1, g2, result);
2419 break;
2420 case Geometry::wkb_multipolygon:
2421 retgeo= wrap.polygon_symdifference_multipolygon(g1, g2, result);
2422 break;
2423 default:
2424 do_geocol_setop= true;
2425 break;
2426 }
2427 break;
2428 case Geometry::wkb_multilinestring:
2429 switch (gt2)
2430 {
2431 case Geometry::wkb_linestring:
2432 retgeo= wrap.linestring_symdifference_multilinestring(g2, g1, result);
2433 break;
2434 case Geometry::wkb_polygon:
2435 retgeo= wrap.polygon_union_multilinestring(g2, g1, result);
2436 break;
2437 case Geometry::wkb_multilinestring:
2438 retgeo= wrap.multilinestring_symdifference_multilinestring(g1, g2,
2439 result);
2440 break;
2441 case Geometry::wkb_multipolygon:
2442 retgeo= wrap.multilinestring_union_multipolygon(g1, g2, result);
2443 break;
2444 default:
2445 do_geocol_setop= true;
2446 break;
2447 }
2448 break;
2449 case Geometry::wkb_multipolygon:
2450 switch (gt2)
2451 {
2452 case Geometry::wkb_linestring:
2453 retgeo= wrap.linestring_union_multipolygon(g2, g1, result);
2454 break;
2455 case Geometry::wkb_polygon:
2456 retgeo= wrap.multipolygon_symdifference_polygon(g1, g2, result);
2457 break;
2458 case Geometry::wkb_multilinestring:
2459 retgeo= wrap.multilinestring_union_multipolygon(g2, g1, result);
2460 break;
2461 case Geometry::wkb_multipolygon:
2462 retgeo= wrap.multipolygon_symdifference_multipolygon(g1, g2, result);
2463 break;
2464 default:
2465 do_geocol_setop= true;
2466 break;
2467 }
2468 break;
2469 default:
2470 do_geocol_setop= true;
2471 break;
2472 }
2473
2474 if (do_geocol_setop)
2475 retgeo= geometry_collection_set_operation<
2476 Coordsys>(g1, g2, result);
2477 else if (!null_value)
2478 null_value= wrap.get_null_value();
2479 return retgeo;
2480 }
2481
2482
2483 /**
2484 Call boost geometry set operations to compute set operation result, and
2485 returns the result as a Geometry object.
2486
2487 @tparam Coordsys Coordinate system type, specified using those defined in
2488 boost::geometry::cs.
2489 @param g1 First Geometry operand, not a geometry collection.
2490 @param g2 Second Geometry operand, not a geometry collection.
2491 @param[out] result buffer containing the GEOMETRY byte string of
2492 the returned geometry.
2493
2494 @return If the set operation results in an empty point set, return a
2495 geometry collection containing 0 objects. If null_value is set to
2496 true, always returns 0. The returned geometry object can be used in the same
2497 val_str call.
2498 */
2499 template<typename Coordsys>
2500 Geometry *Item_func_spatial_operation::
bg_geo_set_op(Geometry * g1,Geometry * g2,String * result)2501 bg_geo_set_op(Geometry *g1, Geometry *g2, String *result)
2502 {
2503 typedef BG_models<Coordsys> Geom_types;
2504
2505 Geometry *retgeo= NULL;
2506
2507 if (g1->get_coordsys() != g2->get_coordsys())
2508 return 0;
2509
2510
2511 switch (spatial_op)
2512 {
2513 case op_intersection:
2514 retgeo= intersection_operation<Geom_types>(g1, g2, result);
2515 break;
2516 case op_union:
2517 retgeo= union_operation<Geom_types>(g1, g2, result);
2518 break;
2519 case op_difference:
2520 retgeo= difference_operation<Geom_types>(g1, g2, result);
2521 break;
2522 case op_symdifference:
2523 retgeo= symdifference_operation<Geom_types>(g1, g2, result);
2524 break;
2525 default:
2526 // Other operations are not set operations.
2527 assert(false);
2528 break;
2529 }
2530
2531 /*
2532 null_value is set in above xxx_operatoin calls if error occured.
2533 */
2534 if (null_value)
2535 {
2536 error_str();
2537 assert(retgeo == NULL);
2538 }
2539
2540 // If we got effective result, the wkb encoding is written to 'result', and
2541 // the retgeo is effective geometry object whose data points into
2542 // 'result''s data.
2543 return retgeo;
2544
2545 }
2546
2547
2548 /**
2549 Combine sub-results of set operation into a geometry collection.
2550 This function eliminates points in geo2 that are within
2551 geo1(polygons or linestrings). We have to do so
2552 because BG set operations return results in 3 forms --- multipolygon,
2553 multilinestring and multipoint, however given a type of set operation and
2554 the operands, the returned 3 types of results may intersect, and we want to
2555 eliminate the points already in the polygons/linestrings. And in future we
2556 also need to remove the linestrings that are already in the polygons, this
2557 isn't done now because there are no such set operation results to combine.
2558
2559 @tparam Coordsys Coordinate system type, specified using those defined in
2560 boost::geometry::cs.
2561 @param geo1 First operand, a Multipolygon or Multilinestring object
2562 computed by BG set operation.
2563 @param geo2 Second operand, a Multipoint object
2564 computed by BG set operation.
2565 @param result Holds result geometry's WKB data in GEOMETRY format.
2566 @return A geometry combined from geo1 and geo2. Either or both of
2567 geo1 and geo2 can be NULL, so we may end up with a multipoint,
2568 a multipolygon/multilinestring, a geometry collection, or an
2569 empty geometry collection.
2570 */
2571 template<typename Coordsys>
2572 Geometry *Item_func_spatial_operation::
combine_sub_results(Geometry * geo1,Geometry * geo2,String * result)2573 combine_sub_results(Geometry *geo1, Geometry *geo2, String *result)
2574 {
2575 typedef BG_models<Coordsys> Geom_types;
2576 typedef typename Geom_types::Multipoint Multipoint;
2577 Geometry *retgeo= NULL;
2578 bool isin= false, added= false;
2579
2580 if (null_value)
2581 {
2582 delete geo1;
2583 delete geo2;
2584 return NULL;
2585 }
2586
2587 auto_ptr<Geometry> guard1(geo1), guard2(geo2);
2588
2589 Gis_geometry_collection *geocol= NULL;
2590 if (geo1 == NULL && geo2 == NULL)
2591 retgeo= empty_result(result, Geometry::default_srid);
2592 else if (geo1 != NULL && geo2 == NULL)
2593 {
2594 retgeo= geo1;
2595 null_value= assign_result(geo1, result);
2596 guard1.release();
2597 }
2598 else if (geo1 == NULL && geo2 != NULL)
2599 {
2600 retgeo= geo2;
2601 null_value= assign_result(geo2, result);
2602 guard2.release();
2603 }
2604
2605 if (geo1 == NULL || geo2 == NULL)
2606 {
2607 if (null_value)
2608 retgeo= NULL;
2609 return retgeo;
2610 }
2611
2612 assert((geo1->get_type() == Geometry::wkb_multilinestring ||
2613 geo1->get_type() == Geometry::wkb_multipolygon) &&
2614 geo2->get_type() == Geometry::wkb_multipoint);
2615 Multipoint mpts(geo2->get_data_ptr(), geo2->get_data_size(),
2616 geo2->get_flags(), geo2->get_srid());
2617 geocol= new Gis_geometry_collection(geo1, result);
2618 geocol->set_components_no_overlapped(geo1->is_components_no_overlapped());
2619 auto_ptr<Gis_geometry_collection> guard3(geocol);
2620 my_bool had_error= false;
2621
2622 for (TYPENAME Multipoint::iterator i= mpts.begin();
2623 i != mpts.end(); ++i)
2624 {
2625 isin= !Item_func_spatial_rel::bg_geo_relation_check<
2626 Coordsys>(&(*i), geo1, SP_DISJOINT_FUNC, &had_error);
2627
2628 if (had_error)
2629 {
2630 error_str();
2631 return NULL;
2632 }
2633
2634 if (!isin)
2635 {
2636 geocol->append_geometry(&(*i), result);
2637 added= true;
2638 }
2639 }
2640
2641 if (added)
2642 {
2643 retgeo= geocol;
2644 guard3.release();
2645 }
2646 else
2647 {
2648 retgeo= geo1;
2649 guard1.release();
2650 null_value= assign_result(geo1, result);
2651 }
2652
2653 if (null_value)
2654 error_str();
2655
2656 return retgeo;
2657 }
2658
2659
2660 /**
2661 Simplify a multilinestring result from the BG intersection function.
2662
2663 The multilinestring may contain linestrings with two points where
2664 both points are the same. Those are intersection points that should
2665 be returned to the user as points. This function loops through the
2666 multilinestring, separating true linestrings and linestring-encoded
2667 points, and returns the simplest geometric result possible: point,
2668 linestring, multilinestring, multipoint, or geometry collection.
2669
2670 @param mls The multilinestring to simplify
2671 @param[out] result The GEOMETRY string (SRID+WKB) of the returned object
2672 @return The simplest geometry type representing the input.
2673 */
2674 Geometry *Item_func_spatial_operation::
simplify_multilinestring(Gis_multi_line_string * mls,String * result)2675 simplify_multilinestring(Gis_multi_line_string *mls, String *result)
2676 {
2677 // Null values are handled by caller.
2678 assert(mls != NULL);
2679 Geometry *retgeo;
2680
2681 // Loop through the multilinestring and separate true linestrings
2682 // from points.
2683 auto_ptr<Gis_multi_line_string> linestrings(new Gis_multi_line_string());
2684 auto_ptr<Gis_multi_point> points(new Gis_multi_point());
2685 linestrings->set_srid(mls->get_srid());
2686 points->set_srid(mls->get_srid());
2687 // BG may return duplicate points, so use a point set to get unique
2688 // points before storing them into 'points'.
2689 typedef std::set<Gis_point, bgpt_lt> Point_set;
2690 Point_set point_set(points->begin(), points->end());
2691 Gis_point prev_point;
2692 bool has_prev_point= false;
2693
2694 for (Gis_multi_line_string::iterator i= mls->begin();
2695 i != mls->end();
2696 ++i)
2697 {
2698 if (i->size() != 2)
2699 {
2700 assert(i->size() > 2);
2701 linestrings->push_back(*i);
2702 continue;
2703 }
2704
2705 const Gis_point &start= (*i)[0];
2706 const Gis_point &end= (*i)[1];
2707 if (start == end)
2708 {
2709 if (!has_prev_point || prev_point != start)
2710 {
2711 has_prev_point= true;
2712 prev_point= start;
2713 point_set.insert(start);
2714 }
2715 }
2716 else
2717 linestrings->push_back(*i);
2718 }
2719
2720 for (Point_set::iterator i= point_set.begin();
2721 i != point_set.end(); ++i)
2722 {
2723 /*
2724 When computing [m]ls intersection [m]ls, BG may return points that are
2725 on the linestrings, so here we have to exclude such points.
2726 */
2727 if (bg::disjoint(*i, *linestrings))
2728 points->push_back(*i);
2729 }
2730
2731 String dummy;
2732 post_fix_result(&bg_resbuf_mgr, *linestrings, &dummy);
2733 post_fix_result(&bg_resbuf_mgr, *points, &dummy);
2734
2735 // Return the simplest type possible
2736 if (points->size() == 0 && linestrings->size() == 1)
2737 {
2738 // Linestring result
2739 Gis_line_string *linestringresult= new Gis_line_string();
2740 size_t oldlength= result->length();
2741 linestrings->begin()->as_geometry(result, false);
2742 size_t newlength= result->length();
2743 linestringresult->set_ptr(const_cast<char*>(result->ptr()) + oldlength +
2744 GEOM_HEADER_SIZE,
2745 (newlength - oldlength) - GEOM_HEADER_SIZE);
2746 linestringresult->set_ownmem(false);
2747 retgeo= linestringresult;
2748 }
2749 else if (points->size() == 0 && linestrings->size() > 1)
2750 {
2751 // Multilinestring result
2752 linestrings->as_geometry(result, false);
2753 linestrings->set_ownmem(false);
2754 retgeo= linestrings.release();
2755 }
2756 else if (points->size() == 1 && linestrings->size() == 0)
2757 {
2758 // Point result
2759 Gis_point *pointresult= new Gis_point();
2760 size_t oldlength= result->length();
2761 points->begin()->as_geometry(result, false);
2762 size_t newlength= result->length();
2763 pointresult->set_ptr(const_cast<char*>(result->ptr()) + oldlength +
2764 GEOM_HEADER_SIZE,
2765 (newlength - oldlength) - GEOM_HEADER_SIZE);
2766 pointresult->set_ownmem(false);
2767 retgeo= pointresult;
2768 }
2769 else if (points->size() > 1 && linestrings->size() == 0)
2770 {
2771 // Multipoint result
2772 points->as_geometry(result, false);
2773 points->set_ownmem(false);
2774 retgeo= points.release();
2775 }
2776 else
2777 {
2778 // GeometryCollection result
2779 Gis_geometry_collection *collection= new Gis_geometry_collection();
2780
2781 if (points->size() > 1)
2782 {
2783 collection->append_geometry(&(*points), result);
2784 }
2785 else
2786 {
2787 collection->append_geometry(&(*points)[0], result);
2788 }
2789
2790 if (linestrings->size() > 1)
2791 {
2792 collection->append_geometry(&(*linestrings), result);
2793 }
2794 else
2795 {
2796 collection->append_geometry(&(*linestrings)[0], result);
2797 }
2798
2799 collection->set_ownmem(false);
2800 retgeo= collection;
2801 }
2802
2803 return retgeo;
2804 }
2805
2806
2807 /**
2808 Extract a basic geometry component from a multi geometry or a geometry
2809 collection, if it's the only one in it.
2810 */
2811 class Singleton_extractor : public WKB_scanner_event_handler
2812 {
2813 /*
2814 If we see the nested geometries as a forest, seeing the outmost one as the
2815 ground where the trees grow, and seeing each of its components
2816 as a tree, then the search for a singleton in a geometry collection(GC) or
2817 multi-geometry(i.e. multipoint, multilinestring, multipolygon) is identical
2818 to searching on the ground to see if there is only one tree on the ground,
2819 if so we also need to record its starting address within the root node's
2820 memory buffer.
2821
2822 Some details complicate the problem:
2823 1. GCs can be nested into another GC, a nested GC should be see also as
2824 the 'ground' rather than a tree.
2825 2. A single multi-geometry contained in a GC may be a singleton or not.
2826 a. When it has only one component in itself, that component is
2827 the singleton;
2828 b. Otherwise itself is the singleton.
2829 3. Basic geometries are always atomic(undevidible).
2830 4. A multi-geometry can't be nested into another multigeometry, it can
2831 only be a component of a GC.
2832
2833 Below comment for the data members are based on this context information.
2834 */
2835 // The number of trees on the ground.
2836 int ntrees;
2837 // The number of trees inside all multi-geometries.
2838 int nsubtrees;
2839 // Current tree travasal stack depth, i.e. tree height.
2840 int depth;
2841 // The depth of the multi-geometry, if any.
2842 int mg_depth;
2843 // The effective stack depth, i.e. excludes the nested GCs.
2844 int levels;
2845 // The stack depth of heighest GC in current ground.
2846 int gc_depth;
2847 // Starting and ending address of tree on ground.
2848 const char *start, *end;
2849 // Starting address of and type of the basic geometry which is on top of the
2850 // multi-geometry.
2851 const char *bg_start;
2852 Geometry::wkbType bg_type;
2853
2854 // The type of the geometry on the ground.
2855 Geometry::wkbType gtype;
2856 public:
Singleton_extractor()2857 Singleton_extractor()
2858 {
2859 ntrees= nsubtrees= depth= mg_depth= levels= gc_depth= 0;
2860 bg_start= start= end= NULL;
2861 bg_type= gtype= Geometry::wkb_invalid_type;
2862 }
2863
is_basic_type(const Geometry::wkbType t)2864 static bool is_basic_type(const Geometry::wkbType t)
2865 {
2866 return t == Geometry::wkb_point || t == Geometry::wkb_linestring ||
2867 t == Geometry::wkb_polygon;
2868 }
2869
has_single_component() const2870 bool has_single_component() const
2871 {
2872 return ntrees == 1;
2873 }
2874
2875 // Functions to get singleton information.
2876
2877 /*
2878 Returns start of singleton. If only one sub-tree, the basic geometry
2879 is returned instead of the multi-geometry, otherwise the multi-geometry
2880 is returned.
2881 */
get_start() const2882 const char *get_start() const
2883 {
2884 return nsubtrees == 1 ? bg_start : start;
2885 }
2886
2887 /*
2888 Returns the end of the singleton geometry. For a singleton,
2889 its end is always also the end of the root geometry, so this function
2890 is correct only when the root geometry really contains a singleton.
2891 */
get_end() const2892 const char *get_end() const
2893 {
2894 return end;
2895 }
2896
get_type() const2897 Geometry::wkbType get_type() const
2898 {
2899 return nsubtrees == 1 ? bg_type : gtype;
2900 }
2901
2902
on_wkb_start(Geometry::wkbByteOrder bo,Geometry::wkbType geotype,const void * wkb,uint32 len,bool has_hdr)2903 virtual void on_wkb_start(Geometry::wkbByteOrder bo,
2904 Geometry::wkbType geotype,
2905 const void *wkb, uint32 len, bool has_hdr)
2906 {
2907 if (geotype != Geometry::wkb_geometrycollection)
2908 {
2909 if (gc_depth == 0)
2910 {
2911 gc_depth= depth;
2912 start= static_cast<const char *>(wkb);
2913 end= start + len;
2914 gtype= geotype;
2915 }
2916
2917 if (!is_basic_type(geotype))
2918 mg_depth= depth;
2919
2920 if (mg_depth + 1 == depth)
2921 {
2922 bg_type= geotype;
2923 bg_start= static_cast<const char *>(wkb);
2924 }
2925
2926 levels++;
2927 }
2928 else
2929 gc_depth= 0;
2930
2931 depth++;
2932 }
2933
2934
on_wkb_end(const void * wkb)2935 virtual void on_wkb_end(const void *wkb)
2936 {
2937 depth--;
2938 assert(depth >= 0);
2939
2940 if (levels > 0)
2941 {
2942 levels--;
2943 if (levels == 0)
2944 {
2945 assert(depth == gc_depth);
2946 ntrees++;
2947 end= static_cast<const char *>(wkb);
2948 mg_depth= 0;
2949 gc_depth= 0;
2950 }
2951 }
2952
2953 // The subtree is either a multi-geometry or a basic geometry.
2954 if (mg_depth != 0 && levels == 1)
2955 nsubtrees++;
2956 }
2957 };
2958
2959
base_type(Geometry::wkbType gt)2960 inline Geometry::wkbType base_type(Geometry::wkbType gt)
2961 {
2962 Geometry::wkbType ret;
2963
2964 switch (gt)
2965 {
2966 case Geometry::wkb_multipoint:
2967 ret= Geometry::wkb_point;
2968 break;
2969 case Geometry::wkb_multilinestring:
2970 ret= Geometry::wkb_linestring;
2971 break;
2972 case Geometry::wkb_multipolygon:
2973 ret= Geometry::wkb_polygon;
2974 break;
2975 default:
2976 ret= gt;
2977 }
2978 return ret;
2979 }
2980
2981
2982 /**
2983 Simplify multi-geometry data. If str contains a multi-geometry or geometry
2984 collection with one component, the component is made as content of str.
2985 If str contains a nested geometry collection, the effective concrete geometry
2986 object is returned.
2987 @param str A string buffer containing a GEOMETRY byte string.
2988 @param [out] result_buffer if not NULL and if a simplification is to be done,
2989 we will copy the GEOMETRY byte string from str into result_buffer
2990 and simplify the one stored in result_buffer, and the one in str
2991 is intact. If there is any data in result_buffer before calling
2992 this function, it is overwritten; If no simplification done, the
2993 result_buffer is intact if it is provided.
2994 @return whether the geometry is simplified or not.
2995 */
simplify_multi_geometry(String * str,String * result_buffer)2996 bool simplify_multi_geometry(String *str, String *result_buffer)
2997 {
2998 if (str->length() < GEOM_HEADER_SIZE)
2999 return false;
3000
3001 char *p= const_cast<char *>(str->ptr());
3002 Geometry::wkbType gtype= get_wkb_geotype(p + 5);
3003 bool ret= false;
3004
3005 if (gtype == Geometry::wkb_multipoint ||
3006 gtype == Geometry::wkb_multilinestring ||
3007 gtype == Geometry::wkb_multipolygon)
3008 {
3009 if (uint4korr(p + GEOM_HEADER_SIZE) == 1)
3010 {
3011 if (result_buffer)
3012 {
3013 result_buffer->length(0);
3014 result_buffer->append(*str);
3015 p= const_cast<char *>(result_buffer->ptr());
3016 str= result_buffer;
3017 }
3018 assert((str->length() - GEOM_HEADER_SIZE - 4 - WKB_HEADER_SIZE) > 0);
3019 int4store(p + 5, static_cast<uint32>(base_type(gtype)));
3020 memmove(p + GEOM_HEADER_SIZE, p + GEOM_HEADER_SIZE + 4 + WKB_HEADER_SIZE,
3021 str->length() - GEOM_HEADER_SIZE - 4 - WKB_HEADER_SIZE);
3022 str->length(str->length() - 4 - WKB_HEADER_SIZE);
3023 ret= true;
3024 }
3025 }
3026 else if (gtype == Geometry::wkb_geometrycollection)
3027 {
3028 Singleton_extractor ex;
3029 uint32 wkb_len= str->length() - GEOM_HEADER_SIZE;
3030 wkb_scanner(p + GEOM_HEADER_SIZE, &wkb_len,
3031 Geometry::wkb_geometrycollection, false, &ex);
3032 if (ex.has_single_component())
3033 {
3034 if (result_buffer)
3035 {
3036 result_buffer->length(0);
3037 result_buffer->append(*str);
3038 p= const_cast<char *>(result_buffer->ptr());
3039 str= result_buffer;
3040 }
3041 p= write_wkb_header(p + 4, ex.get_type());
3042 ptrdiff_t len= ex.get_end() - ex.get_start();
3043 assert(len > 0);
3044 memmove(p, ex.get_start(), len);
3045 str->length(GEOM_HEADER_SIZE + len);
3046 ret= true;
3047 }
3048 }
3049
3050 return ret;
3051 }
3052
3053
3054 /*
3055 Do set operations on geometries.
3056 Writes geometry set operation result into str_value_arg in wkb format.
3057 */
val_str(String * str_value_arg)3058 String *Item_func_spatial_operation::val_str(String *str_value_arg)
3059 {
3060 DBUG_ENTER("Item_func_spatial_operation::val_str");
3061 assert(fixed == 1);
3062
3063 tmp_value1.length(0);
3064 tmp_value2.length(0);
3065 String *res1= args[0]->val_str(&tmp_value1);
3066 String *res2= args[1]->val_str(&tmp_value2);
3067 Geometry_buffer buffer1, buffer2;
3068 Geometry *g1= NULL, *g2= NULL, *gres= NULL;
3069 bool had_except1= false, had_except2= false;
3070 bool result_is_args= false;
3071
3072 // Release last call's result buffer.
3073 bg_resbuf_mgr.free_result_buffer();
3074
3075 // Clean up the result first, since caller may give us one with non-NULL
3076 // buffer, we don't need it here.
3077 str_value_arg->set(NullS, 0, &my_charset_bin);
3078
3079 if ((null_value= (!res1 || args[0]->null_value ||
3080 !res2 || args[1]->null_value)))
3081 goto exit;
3082 if (!(g1= Geometry::construct(&buffer1, res1)) ||
3083 !(g2= Geometry::construct(&buffer2, res2)))
3084 {
3085 my_error(ER_GIS_INVALID_DATA, MYF(0), func_name());
3086 DBUG_RETURN(error_str());
3087 }
3088
3089 // The two geometry operand must be in the same coordinate system.
3090 if (g1->get_srid() != g2->get_srid())
3091 {
3092 my_error(ER_GIS_DIFFERENT_SRIDS, MYF(0), func_name(),
3093 g1->get_srid(), g2->get_srid());
3094 DBUG_RETURN(error_str());
3095 }
3096
3097 str_value_arg->set_charset(&my_charset_bin);
3098 str_value_arg->length(0);
3099
3100
3101 /*
3102 Catch all exceptions to make sure no exception can be thrown out of
3103 current function. Put all and any code that calls Boost.Geometry functions,
3104 STL functions into this try block. Code out of the try block should never
3105 throw any exception.
3106 */
3107 try
3108 {
3109 if (g1->get_type() != Geometry::wkb_geometrycollection &&
3110 g2->get_type() != Geometry::wkb_geometrycollection)
3111 gres= bg_geo_set_op<bgcs::cartesian>(g1, g2, str_value_arg);
3112 else
3113 gres= geometry_collection_set_operation<bgcs::cartesian>
3114 (g1, g2, str_value_arg);
3115
3116 }
3117 catch (...)
3118 {
3119 had_except1= true;
3120 handle_gis_exception(func_name());
3121 }
3122
3123 try
3124 {
3125 /*
3126 The buffers in res1 and res2 either belong to argument Item_xxx objects
3127 or simply belong to tmp_value1 or tmp_value2, they will be deleted
3128 properly by their owners, not by our bg_resbuf_mgr, so here we must
3129 forget them in order not to free the buffers before the Item_xxx
3130 owner nodes are destroyed.
3131 */
3132 bg_resbuf_mgr.forget_buffer(const_cast<char *>(res1->ptr()));
3133 bg_resbuf_mgr.forget_buffer(const_cast<char *>(res2->ptr()));
3134 bg_resbuf_mgr.forget_buffer(const_cast<char *>(tmp_value1.ptr()));
3135 bg_resbuf_mgr.forget_buffer(const_cast<char *>(tmp_value2.ptr()));
3136
3137 /*
3138 Release intermediate geometry data buffers accumulated during execution
3139 of this set operation.
3140 */
3141 if (!str_value_arg->is_alloced() && gres != g1 && gres != g2)
3142 bg_resbuf_mgr.set_result_buffer(const_cast<char *>(str_value_arg->ptr()));
3143 bg_resbuf_mgr.free_intermediate_result_buffers();
3144 }
3145 catch (...)
3146 {
3147 had_except2= true;
3148 handle_gis_exception(func_name());
3149 }
3150
3151 if (had_except1 || had_except2 || null_value)
3152 {
3153 if (gres != NULL && gres != g1 && gres != g2)
3154 {
3155 delete gres;
3156 gres= NULL;
3157 }
3158 DBUG_RETURN(error_str());
3159 }
3160
3161 assert(gres != NULL && !null_value && str_value_arg->length() > 0);
3162
3163 /*
3164 There are 4 ways to create the result geometry object and allocate
3165 memory for the result String object:
3166 1. Created in BGOPCALL and allocated by BG code using gis_wkb_alloc
3167 functions; The geometry result object's memory is took over by
3168 str_value_arg, thus not allocated by str_value_arg.
3169 2. Created as a new geometry object and allocated by
3170 str_value_arg's String member functions.
3171 3. One of g1 or g2 used as result and g1/g2's String object is used as
3172 final result without duplicating their byte strings. Also, g1 and/or
3173 g2 may be used as intermediate result and their byte strings are
3174 assigned to intermediate String objects without giving the ownerships
3175 to them, so they are always owned by tmp_value1 and/or tmp_value2.
3176 4. A geometry duplicated from a component of BG_geometry_collection.
3177 when both GCs have 1 member, we do set operation for the two members
3178 directly, and if such a component is the result we have to duplicate
3179 it and its WKB String buffer.
3180
3181 Among above 4 ways, #1, #2 and #4 write the byte string only once without
3182 any data copying, #3 doesn't write any byte strings.
3183
3184 And here we always have a GEOMETRY byte string in str_value_arg, although
3185 in some cases gres->has_geom_header_space() is false.
3186 */
3187 if (!str_value_arg->is_alloced() && gres != g1 && gres != g2)
3188 {
3189 assert(gres->has_geom_header_space() || gres->is_bg_adapter());
3190 }
3191 else
3192 {
3193 assert(gres->has_geom_header_space() || (gres == g1 || gres == g2));
3194 if (gres == g1)
3195 {
3196 str_value_arg= res1;
3197 result_is_args= true;
3198 }
3199 else if (gres == g2)
3200 {
3201 str_value_arg= res2;
3202 result_is_args= true;
3203 }
3204 }
3205
3206 /*
3207 We can not modify the geometry argument because it is assumed to be stable.
3208 So if returning one of the arguments as result directly, make sure the
3209 simplification is done in a separate buffer.
3210 */
3211 if (simplify_multi_geometry(str_value_arg,
3212 (result_is_args ? &m_result_buffer : NULL)) &&
3213 result_is_args)
3214 str_value_arg= &m_result_buffer;
3215
3216 exit:
3217 if (gres != g1 && gres != g2 && gres != NULL)
3218 delete gres;
3219 DBUG_RETURN(null_value ? NULL : str_value_arg);
3220 }
3221
3222
is_areal(const Geometry * g)3223 inline bool is_areal(const Geometry *g)
3224 {
3225 return g != NULL && (g->get_type() == Geometry::wkb_polygon ||
3226 g->get_type() == Geometry::wkb_multipolygon);
3227 }
3228
3229
3230 /**
3231 Do set operation on geometry collections.
3232 BG doesn't directly support geometry collections in any function, so we
3233 have to do so by computing the set operation result of all two operands'
3234 components, which must be the 6 basic types of geometries, and then we
3235 combine the sub-results.
3236
3237 This function dispatches to specific set operation types.
3238
3239 @tparam Coordsys Coordinate system type, specified using those defined in
3240 boost::geometry::cs.
3241 @param g1 First geometry operand, a geometry collection.
3242 @param g2 Second geometry operand, a geometry collection.
3243 @param[out] result Holds WKB data of the result, which must be a
3244 geometry collection.
3245 @return The set operation result, whose WKB data is stored in 'result'.
3246 */
3247 template<typename Coordsys>
3248 Geometry *Item_func_spatial_operation::
geometry_collection_set_operation(Geometry * g1,Geometry * g2,String * result)3249 geometry_collection_set_operation(Geometry *g1, Geometry *g2,
3250 String *result)
3251 {
3252 Geometry *gres= NULL;
3253 BG_geometry_collection bggc1, bggc2;
3254
3255 bggc1.set_srid(g1->get_srid());
3256 bggc2.set_srid(g2->get_srid());
3257 bool empty1= is_empty_geocollection(g1);
3258 bool empty2= is_empty_geocollection(g2);
3259
3260 /* Short cut for either one operand being empty. */
3261 if (empty1 || empty2)
3262 {
3263 if (spatial_op == op_intersection ||
3264 (empty1 && empty2 && (spatial_op == op_symdifference ||
3265 spatial_op == op_union)) ||
3266 (empty1 && spatial_op == op_difference))
3267 {
3268 return empty_result(result, g1->get_srid());
3269 }
3270
3271 // If spatial_op is op_union or op_symdifference and one of g1/g2 is empty,
3272 // we will continue in order to merge components in the argument.
3273
3274 if (empty2 && spatial_op == op_difference)
3275 {
3276 null_value= g1->as_geometry(result, true/* shallow copy */);
3277 return g1;
3278 }
3279 }
3280
3281 bggc1.fill(g1);
3282 bggc2.fill(g2);
3283 if (spatial_op != op_union)
3284 {
3285 bggc1.merge_components<Coordsys>(&null_value);
3286 if (null_value)
3287 return gres;
3288 bggc2.merge_components<Coordsys>(&null_value);
3289 if (null_value)
3290 return gres;
3291 }
3292
3293 BG_geometry_collection::Geometry_list &gv1= bggc1.get_geometries();
3294 BG_geometry_collection::Geometry_list &gv2= bggc2.get_geometries();
3295
3296 /*
3297 If there is only one component in one argument and the other is empty,
3298 no merge is possible.
3299 */
3300 if (spatial_op == op_union ||
3301 spatial_op == op_symdifference)
3302 {
3303 if (gv1.size() == 0 && gv2.size() == 1)
3304 {
3305 null_value= g2->as_geometry(result, true/* shallow copy */);
3306 return g2;
3307 }
3308
3309 if (gv1.size() == 1 && gv2.size() == 0)
3310 {
3311 null_value= g1->as_geometry(result, true/* shallow copy */);
3312 return g1;
3313 }
3314 }
3315
3316 /*
3317 If both collections have only one basic component, do basic set operation.
3318 The exception is symdifference with at least one operand being not a
3319 polygon or multipolygon, in which case this exact function is called to
3320 perform symdifference for the two basic components.
3321 */
3322 if (gv1.size() == 1 && gv2.size() == 1 &&
3323 (spatial_op != op_symdifference ||
3324 (is_areal(*(gv1.begin())) && is_areal(*(gv2.begin())))))
3325 {
3326 gres= bg_geo_set_op<Coordsys>(*(gv1.begin()), *(gv2.begin()),
3327 result);
3328 if (null_value)
3329 return NULL;
3330 if (gres == NULL && !null_value)
3331 {
3332 gres= empty_result(result, g1->get_srid());
3333 return gres;
3334 }
3335
3336 /*
3337 If this set operation gives us a gres that's a component/member of either
3338 bggc1 or bggc2, we have to duplicate the object and its buffer because
3339 they will be destroyed when bggc1/bggc2 goes out of scope.
3340 */
3341 bool do_dup= false;
3342 for (BG_geometry_collection::Geometry_list::iterator i= gv1.begin();
3343 i != gv1.end(); ++i)
3344 if (*i == gres)
3345 do_dup= true;
3346 if (!do_dup)
3347 for (BG_geometry_collection::Geometry_list::iterator i= gv2.begin();
3348 i != gv2.end(); ++i)
3349 if (*i == gres)
3350 do_dup= true;
3351
3352 if (do_dup)
3353 {
3354 String tmpres;
3355 Geometry *gres2= NULL;
3356 tmpres.append(result->ptr(), result->length());
3357 const void *data_start= static_cast<const char *>(tmpres.ptr()) +
3358 GEOM_HEADER_SIZE;
3359
3360 switch (gres->get_geotype())
3361 {
3362 case Geometry::wkb_point:
3363 gres2= new Gis_point;
3364 break;
3365 case Geometry::wkb_linestring:
3366 gres2= new Gis_line_string;
3367 break;
3368 case Geometry::wkb_polygon:
3369 gres2= new Gis_polygon;
3370 break;
3371 case Geometry::wkb_multipoint:
3372 gres2= new Gis_multi_point;
3373 break;
3374 case Geometry::wkb_multilinestring:
3375 gres2= new Gis_multi_line_string;
3376 break;
3377 case Geometry::wkb_multipolygon:
3378 gres2= new Gis_multi_polygon;
3379 break;
3380 default:
3381 assert(false);
3382 }
3383
3384 gres2->set_data_ptr(data_start, tmpres.length() - GEOM_HEADER_SIZE);
3385 gres2->has_geom_header_space(true);
3386 gres2->set_bg_adapter(false);
3387 result->takeover(tmpres);
3388 gres= gres2;
3389 }
3390
3391 return gres;
3392 }
3393
3394
3395 switch (this->spatial_op)
3396 {
3397 case op_intersection:
3398 gres= geocol_intersection<Coordsys>(bggc1, bggc2, result);
3399 break;
3400 case op_union:
3401 gres= geocol_union<Coordsys>(bggc1, bggc2, result);
3402 break;
3403 case op_difference:
3404 gres= geocol_difference<Coordsys>(bggc1, bggc2, result);
3405 break;
3406 case op_symdifference:
3407 gres= geocol_symdifference<Coordsys>(bggc1, bggc2, result);
3408 break;
3409 default:
3410 /* Only above four supported. */
3411 assert(false);
3412 break;
3413 }
3414
3415 if (gres == NULL && !null_value)
3416 gres= empty_result(result, g1->get_srid());
3417 return gres;
3418 }
3419
3420
3421 /**
3422 Do intersection operation on geometry collections. We do intersection for
3423 all pairs of components in g1 and g2, put the results in a geometry
3424 collection. If all subresults can be computed successfully, the geometry
3425 collection is our result.
3426
3427 @tparam Coordsys Coordinate system type, specified using those defined in
3428 boost::geometry::cs.
3429 @param bggc1 First geometry operand, a geometry collection.
3430 @param bggc2 Second geometry operand, a geometry collection.
3431 @param[out] result Holds WKB data of the result, which must be a
3432 geometry collection.
3433 @return The intersection result, whose WKB data is stored in 'result'.
3434 */
3435 template<typename Coordsys>
3436 Geometry *Item_func_spatial_operation::
geocol_intersection(const BG_geometry_collection & bggc1,const BG_geometry_collection & bggc2,String * result)3437 geocol_intersection(const BG_geometry_collection &bggc1,
3438 const BG_geometry_collection &bggc2,
3439 String *result)
3440 {
3441 Geometry *gres= NULL;
3442 String wkbres;
3443 Geometry *g0= NULL;
3444 BG_geometry_collection bggc;
3445 const BG_geometry_collection::Geometry_list &gv1= bggc1.get_geometries();
3446 const BG_geometry_collection::Geometry_list &gv2= bggc2.get_geometries();
3447 bggc.set_srid(bggc1.get_srid());
3448
3449 if (gv1.size() == 0 || gv2.size() == 0)
3450 {
3451 return empty_result(result, bggc1.get_srid());
3452 }
3453
3454 const typename BG_geometry_collection::Geometry_list *gv= NULL, *gvr= NULL;
3455
3456 if (gv1.size() > gv2.size())
3457 {
3458 gv= &gv2;
3459 gvr= &gv1;
3460 }
3461 else
3462 {
3463 gv= &gv1;
3464 gvr= &gv2;
3465 }
3466
3467 Rtree_index rtree;
3468 make_rtree(*gvr, &rtree);
3469
3470 for (BG_geometry_collection::
3471 Geometry_list::const_iterator i= gv->begin();
3472 i != gv->end(); ++i)
3473 {
3474 BG_box box;
3475 make_bg_box(*i, &box);
3476
3477 Rtree_index::const_query_iterator j= rtree.qbegin(bgi::intersects(box));
3478 if (j == rtree.qend())
3479 continue;
3480
3481 for (; j != rtree.qend(); ++j)
3482 {
3483 Geometry *geom= (*gvr)[j->second];
3484 // Free before using it, wkbres may have WKB data from last execution.
3485 wkbres.mem_free();
3486 wkbres.length(0);
3487 g0= bg_geo_set_op<Coordsys>(*i, geom, &wkbres);
3488
3489 if (null_value)
3490 {
3491 if (g0 != NULL && g0 != *i && g0 != geom)
3492 delete g0;
3493 return 0;
3494 }
3495
3496 if (g0 && !is_empty_geocollection(wkbres))
3497 bggc.fill(g0);
3498 if (g0 != NULL && g0 != *i && g0 != geom)
3499 {
3500 delete g0;
3501 g0= NULL;
3502 }
3503 }
3504 }
3505
3506 /*
3507 Note: result unify and merge
3508
3509 The result may have geometry elements that overlap, caused by overlap
3510 geos in either or both gc1 and/or gc2. Also, there may be geometries
3511 that can be merged into a larger one of the same type in the result.
3512 We will need to figure out how to make such enhancements.
3513 */
3514 bggc.merge_components<Coordsys>(&null_value);
3515 if (null_value)
3516 return NULL;
3517 gres= bggc.as_geometry_collection(result);
3518
3519 return gres;
3520 }
3521
3522
3523 /**
3524 Do union operation on geometry collections. We do union for
3525 all pairs of components in g1 and g2, whenever a union can be done, we do
3526 so and put the results in a geometry collection GC and remove the two
3527 components from g1 and g2 respectively. Finally no components in g1 and g2
3528 overlap and GC is our result.
3529
3530 @tparam Coordsys Coordinate system type, specified using those defined in
3531 boost::geometry::cs.
3532 @param bggc1 First geometry operand, a geometry collection.
3533 @param bggc2 Second geometry operand, a geometry collection.
3534 @param[out] result Holds WKB data of the result, which must be a
3535 geometry collection.
3536 @return The union result, whose WKB data is stored in 'result'.
3537 */
3538 template<typename Coordsys>
3539 Geometry *Item_func_spatial_operation::
geocol_union(const BG_geometry_collection & bggc1,const BG_geometry_collection & bggc2,String * result)3540 geocol_union(const BG_geometry_collection &bggc1,
3541 const BG_geometry_collection &bggc2,
3542 String *result)
3543 {
3544 Geometry *gres= NULL;
3545 BG_geometry_collection bggc;
3546 BG_geometry_collection::Geometry_list &gv= bggc.get_geometries();
3547 gv.insert(gv.end(), bggc1.get_geometries().begin(),
3548 bggc1.get_geometries().end());
3549 gv.insert(gv.end(), bggc2.get_geometries().begin(),
3550 bggc2.get_geometries().end());
3551 bggc.set_srid(bggc1.get_srid());
3552
3553 // It's likely that there are overlapping components in bggc because it
3554 // has components from both bggc1 and bggc2.
3555 bggc.merge_components<Coordsys>(&null_value);
3556 if (!null_value)
3557 gres= bggc.as_geometry_collection(result);
3558
3559 return gres;
3560 }
3561
3562
3563 /**
3564 Do difference operation on geometry collections. For each component CX in g1,
3565 we do CX:= CX difference CY for all components CY in g2. When at last CX isn't
3566 empty, it's put into result geometry collection GC.
3567 If all subresults can be computed successfully, the geometry
3568 collection GC is our result.
3569
3570 @tparam Coordsys Coordinate system type, specified using those defined in
3571 boost::geometry::cs.
3572 @param bggc1 First geometry operand, a geometry collection.
3573 @param bggc2 Second geometry operand, a geometry collection.
3574 @param[out] result Holds WKB data of the result, which must be a
3575 geometry collection.
3576 @return The difference result, whose WKB data is stored in 'result'.
3577 */
3578 template<typename Coordsys>
3579 Geometry *Item_func_spatial_operation::
geocol_difference(const BG_geometry_collection & bggc1,const BG_geometry_collection & bggc2,String * result)3580 geocol_difference(const BG_geometry_collection &bggc1,
3581 const BG_geometry_collection &bggc2,
3582 String *result)
3583 {
3584 Geometry *gres= NULL;
3585 String *wkbres= NULL;
3586 BG_geometry_collection bggc;
3587 const BG_geometry_collection::Geometry_list *gv1= &(bggc1.get_geometries());
3588 const BG_geometry_collection::Geometry_list *gv2= &(bggc2.get_geometries());
3589
3590 bggc.set_srid(bggc1.get_srid());
3591
3592 // Difference isn't symetric so we have to always build rtree tndex on gv2.
3593 Rtree_index rtree;
3594 make_rtree(*gv2, &rtree);
3595
3596 for (BG_geometry_collection::
3597 Geometry_list::const_iterator i= gv1->begin();
3598 i != gv1->end(); ++i)
3599 {
3600 bool g11_isempty= false;
3601 auto_ptr<Geometry> guard11;
3602 Geometry *g11= NULL;
3603 g11= *i;
3604 Inplace_vector<String> wkbstrs(PSI_INSTRUMENT_ME);
3605
3606 BG_box box;
3607 make_bg_box(*i, &box);
3608
3609 /*
3610 Above theory makes sure all results are in rtree_result, the logic
3611 here is sufficient when rtree_result is empty.
3612 */
3613 for (Rtree_index::const_query_iterator
3614 j= rtree.qbegin(bgi::intersects(box));
3615 j != rtree.qend(); ++j)
3616 {
3617 Geometry *geom= (*gv2)[j->second];
3618
3619 wkbres= wkbstrs.append_object();
3620 if (wkbres == NULL)
3621 {
3622 null_value= TRUE;
3623 return NULL;
3624 }
3625 Geometry *g0= bg_geo_set_op<Coordsys>(g11, geom, wkbres);
3626 auto_ptr<Geometry> guard0(g0);
3627
3628 if (null_value)
3629 {
3630 if (!(g0 != NULL && g0 != *i && g0 != geom))
3631 guard0.release();
3632 if (!(g11 != NULL && g11 != g0 && g11 != *i && g11 != geom))
3633 guard11.release();
3634 return NULL;
3635 }
3636
3637 if (g0 != NULL && !is_empty_geocollection(*wkbres))
3638 {
3639 if (g11 != NULL && g11 != *i && g11 != geom && g11 != g0)
3640 delete guard11.release();
3641 else
3642 guard11.release();
3643 guard0.release();
3644 g11= g0;
3645 if (g0 != NULL && g0 != *i && g0 != geom)
3646 guard11.reset(g11);
3647 }
3648 else
3649 {
3650 g11_isempty= true;
3651 if (!(g0 != NULL && g0 != *i && g0 != geom && g0 != g11))
3652 guard0.release();
3653 break;
3654 }
3655 }
3656
3657 if (!g11_isempty)
3658 bggc.fill(g11);
3659 if (!(g11 != NULL && g11 != *i))
3660 guard11.release();
3661 else
3662 guard11.reset(NULL);
3663 }
3664
3665 bggc.merge_components<Coordsys>(&null_value);
3666 if (null_value)
3667 return NULL;
3668 gres= bggc.as_geometry_collection(result);
3669
3670 return gres;
3671 }
3672
3673
3674 /**
3675 Symmetric difference of geometry collections.
3676
3677 Symdifference(g1, g2) is implemented as
3678 union(difference(g1, g2), difference(g2, g1)).
3679
3680 @tparam Coordsys Coordinate system type, specified using those defined in
3681 boost::geometry::cs.
3682 @param bggc1 First geometry operand, a geometry collection.
3683 @param bggc2 Second geometry operand, a geometry collection.
3684 @param[out] result Holds WKB data of the result, which must be a
3685 geometry collection.
3686 @return The symdifference result, whose WKB data is stored in 'result'.
3687 */
3688 template<typename Coordsys>
3689 Geometry *Item_func_spatial_operation::
geocol_symdifference(const BG_geometry_collection & bggc1,const BG_geometry_collection & bggc2,String * result)3690 geocol_symdifference(const BG_geometry_collection &bggc1,
3691 const BG_geometry_collection &bggc2,
3692 String *result)
3693 {
3694 Geometry *res= NULL;
3695 auto_ptr<Geometry> diff12(NULL);
3696 auto_ptr<Geometry> diff21(NULL);
3697 String diff12_wkb;
3698 String diff21_wkb;
3699
3700 Var_resetter<op_type>
3701 var_reset(&spatial_op, op_symdifference);
3702
3703 spatial_op= op_difference;
3704 diff12.reset(geocol_difference<Coordsys>(bggc1, bggc2, &diff12_wkb));
3705 if (null_value)
3706 return NULL;
3707 assert(diff12.get() != NULL);
3708
3709 diff21.reset(geocol_difference<Coordsys>(bggc2, bggc1, &diff21_wkb));
3710 if (null_value)
3711 return NULL;
3712 assert(diff21.get() != NULL);
3713
3714 spatial_op= op_union;
3715 res= geometry_collection_set_operation<Coordsys>(diff12.get(), diff21.get(),
3716 result);
3717 if (diff12.get() == res)
3718 {
3719 result->takeover(diff12_wkb);
3720 diff12.release();
3721 }
3722 else if (diff21.get() == res)
3723 {
3724 result->takeover(diff21_wkb);
3725 diff21.release();
3726 }
3727
3728 if (null_value)
3729 {
3730 if (res != NULL)
3731 delete res;
3732 return NULL;
3733 }
3734
3735 return res;
3736 }
3737
3738
assign_result(Geometry * geo,String * result)3739 bool Item_func_spatial_operation::assign_result(Geometry *geo, String *result)
3740 {
3741 assert(geo->has_geom_header_space());
3742 char *p= geo->get_cptr() - GEOM_HEADER_SIZE;
3743 write_geometry_header(p, geo->get_srid(), geo->get_geotype());
3744 result->set(p, GEOM_HEADER_SIZE + geo->get_nbytes(), &my_charset_bin);
3745 bg_resbuf_mgr.add_buffer(p);
3746 geo->set_ownmem(false);
3747
3748 return false;
3749 }
3750
3751
func_name() const3752 const char *Item_func_spatial_operation::func_name() const
3753 {
3754 switch (spatial_op) {
3755 case op_intersection:
3756 return "st_intersection";
3757 case op_difference:
3758 return "st_difference";
3759 case op_union:
3760 return "st_union";
3761 case op_symdifference:
3762 return "st_symdifference";
3763 default:
3764 assert(0); // Should never happen
3765 return "sp_unknown";
3766 }
3767 }
3768