1 #ifndef GEOFUNC_INTERNAL_INCLUDED
2 #define GEOFUNC_INTERNAL_INCLUDED
3 
4 /* Copyright (c) 2014, 2021, Oracle and/or its affiliates.
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License, version 2.0,
8    as published by the Free Software Foundation.
9 
10    This program is also distributed with certain software (including
11    but not limited to OpenSSL) that is licensed under separate terms,
12    as designated in a particular file or component or in included license
13    documentation.  The authors of MySQL hereby grant you an additional
14    permission to link the program and your derivative works with the
15    separately licensed software that they have included with MySQL.
16 
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20    GNU General Public License, version 2.0, for more details.
21 
22    You should have received a copy of the GNU General Public License
23    along with this program; if not, write to the Free Software Foundation,
24    51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
25 
26 
27 /**
28   @file
29 
30   @brief
31   This file defines common build blocks of GIS functions.
32 */
33 #include "my_config.h"
34 
35 #include <vector>
36 #include <algorithm>
37 #include <stdexcept>
38 #include <memory>
39 
40 #include <m_ctype.h>
41 #include "item_geofunc.h"
42 #include "gis_bg_traits.h"
43 
44 // Boost.Geometry
45 #include <boost/geometry/geometry.hpp>
46 #include <boost/geometry/index/rtree.hpp>
47 // Boost.Range
48 #include <boost/range.hpp>
49 
50 
51 // GCC requires typename whenever needing to access a type inside a template,
52 // but MSVC forbids this.
53 #ifdef HAVE_IMPLICIT_DEPENDENT_NAME_TYPING
54 #define TYPENAME
55 #else
56 #define TYPENAME typename
57 #endif
58 
59 
60 #define GIS_ZERO 0.00000000001
61 
62 extern bool simplify_multi_geometry(String *str, String *result_buffer);
63 
64 using std::auto_ptr;
65 
66 
67 /**
68   Handle a GIS exception of any type.
69 
70   This function constitutes the exception handling barrier between
71   Boost.Geometry and MySQL code. It handles all exceptions thrown in
72   GIS code and raises the corresponding error in MySQL.
73 
74   Pattern for use in other functions:
75 
76   @code
77   try
78   {
79     something_that_throws();
80   }
81   catch (...)
82   {
83     handle_gis_exception("st_foo");
84   }
85   @endcode
86 
87   Other exception handling code put into the catch block, before or
88   after the call to handle_gis_exception(), must not throw exceptions.
89 
90   @param funcname Function name for use in error message
91  */
92 void handle_gis_exception(const char *funcname);
93 
94 
95 /// A wrapper and interface for all geometry types used here. Make these
96 /// types as localized as possible. It's used as a type interface.
97 /// @tparam CoordinateSystemType Coordinate system type, specified using
98 //          those defined in boost::geometry::cs.
99 template<typename CoordinateSystemType>
100 class BG_models
101 {
102 public:
103   typedef Gis_point Point;
104   // An counter-clockwise, closed Polygon type. It can hold open Polygon data,
105   // but not clockwise ones, otherwise things can go wrong, e.g. intersection.
106   typedef Gis_polygon Polygon;
107   typedef Gis_line_string Linestring;
108   typedef Gis_multi_point Multipoint;
109   typedef Gis_multi_line_string Multilinestring;
110   typedef Gis_multi_polygon Multipolygon;
111 
112   typedef double Coordinate_type;
113   typedef CoordinateSystemType Coordinate_system;
114 };
115 
116 
117 template<>
118 class BG_models<
119       boost::geometry::cs::spherical_equatorial<boost::geometry::degree> >
120 {
121 public:
122   typedef Gis_point_spherical Point;
123   // An counter-clockwise, closed Polygon type. It can hold open Polygon data,
124   // but not clockwise ones, otherwise things can go wrong, e.g. intersection.
125   typedef Gis_polygon_spherical Polygon;
126   typedef Gis_line_string_spherical Linestring;
127   typedef Gis_multi_point_spherical Multipoint;
128   typedef Gis_multi_line_string_spherical Multilinestring;
129   typedef Gis_multi_polygon_spherical Multipolygon;
130 
131   typedef double Coordinate_type;
132   typedef boost::geometry::cs::spherical_equatorial<boost::geometry::degree>
133     Coordinate_system;
134 };
135 
136 
137 namespace bg= boost::geometry;
138 namespace bgm= boost::geometry::model;
139 namespace bgcs= boost::geometry::cs;
140 namespace bgi= boost::geometry::index;
141 namespace bgm= boost::geometry::model;
142 
143 typedef bgm::point<double, 2, bgcs::cartesian> BG_point;
144 typedef bgm::box<BG_point> BG_box;
145 typedef std::pair<BG_box, size_t> BG_rtree_entry;
146 typedef std::vector<BG_rtree_entry> BG_rtree_entries;
147 typedef bgi::rtree<BG_rtree_entry, bgi::quadratic<64> > Rtree_index;
148 typedef std::vector<BG_rtree_entry> Rtree_result;
149 
150 
make_bg_box(const Geometry * g,BG_box * box)151 inline void make_bg_box(const Geometry *g, BG_box *box)
152 {
153   MBR mbr;
154   g->envelope(&mbr);
155   box->min_corner().set<0>(mbr.xmin);
156   box->min_corner().set<1>(mbr.ymin);
157   box->max_corner().set<0>(mbr.xmax);
158   box->max_corner().set<1>(mbr.ymax);
159 }
160 
161 
is_box_valid(const BG_box & box)162 inline bool is_box_valid(const BG_box &box)
163 {
164   return
165     !(!my_isfinite(box.min_corner().get<0>()) ||
166       !my_isfinite(box.min_corner().get<1>()) ||
167       !my_isfinite(box.max_corner().get<0>()) ||
168       !my_isfinite(box.max_corner().get<1>()) ||
169       box.max_corner().get<0>() < box.min_corner().get<0>() ||
170       box.max_corner().get<1>() < box.min_corner().get<1>());
171 }
172 
173 
174 /**
175   Build an rtree set using a geometry collection.
176   @param gl geometry object pointers container.
177   @param [out] rtree entries which can be used to build an rtree.
178  */
179 void
180 make_rtree(const BG_geometry_collection::Geometry_list &gl,
181            Rtree_index *rtree);
182 
183 
184 /**
185   Build an rtree set using array of Boost.Geometry objects, which are
186   components of a multi geometry.
187   @param mg the multi geometry.
188   @param rtree the rtree to build.
189  */
190 template <typename MultiGeometry>
191 void
192 make_rtree_bggeom(const MultiGeometry &mg,
193                   Rtree_index *rtree);
194 
195 
196 inline Gis_geometry_collection *
empty_collection(String * str,uint32 srid)197 empty_collection(String *str, uint32 srid)
198 {
199   return new Gis_geometry_collection(srid, Geometry::wkb_invalid_type,
200                                      NULL, str);
201 }
202 
203 
204 /*
205   Check whether a geometry is an empty geometry collection, i.e. one that
206   doesn't contain any geometry component of [multi]point or [multi]linestring
207   or [multi]polygon type.
208   @param g the geometry to check.
209   @return true if g is such an empty geometry collection;
210           false otherwise.
211 */
212 bool is_empty_geocollection(const Geometry *g);
213 
214 
215 /*
216   Check whether wkbres is the data of an empty geometry collection, i.e. one
217   that doesn't contain any geometry component of [multi]point or
218   [multi]linestring or [multi]polygon type.
219 
220   @param wkbres a piece of geometry data of GEOMETRY format, i.e. an SRID
221                 prefixing a WKB.
222   @return true if wkbres contains such an empty geometry collection;
223           false otherwise.
224  */
225 bool is_empty_geocollection(const String &wkbres);
226 
227 
228 /**
229    Less than comparator for points used by BG.
230  */
231 struct bgpt_lt
232 {
233   template <typename Point>
operatorbgpt_lt234   bool operator ()(const Point &p1, const Point &p2) const
235   {
236     if (p1.template get<0>() != p2.template get<0>())
237       return p1.template get<0>() < p2.template get<0>();
238     else
239       return p1.template get<1>() < p2.template get<1>();
240   }
241 };
242 
243 
244 /**
245    Equals comparator for points used by BG.
246  */
247 struct bgpt_eq
248 {
249   template <typename Point>
operatorbgpt_eq250   bool operator ()(const Point &p1, const Point &p2) const
251   {
252     return p1.template get<0>() == p2.template get<0>() &&
253       p1.template get<1>() == p2.template get<1>();
254   }
255 };
256 
257 
258 /**
259   Utility class, reset specified variable 'valref' to specified 'oldval' when
260   val_resetter<valtype> instance is destroyed.
261   @tparam Valtype Variable type to reset.
262  */
263 template <typename Valtype>
264 class Var_resetter
265 {
266 private:
267   Valtype *valref;
268   Valtype oldval;
269 
270   // Forbid use, to eliminate a warning: oldval may be used uninitialized.
271   Var_resetter(const Var_resetter &o);
272   Var_resetter &operator=(const Var_resetter&);
273 public:
Var_resetter()274   Var_resetter() : valref(NULL)
275   {
276   }
277 
Var_resetter(Valtype * v,const Valtype & oval)278   Var_resetter(Valtype *v, const Valtype &oval) : valref(v), oldval(oval)
279   {
280   }
281 
~Var_resetter()282   ~Var_resetter()
283   {
284     if (valref)
285       *valref= oldval;
286   }
287 
set(Valtype * v,const Valtype & oldval)288   void set(Valtype *v, const Valtype &oldval)
289   {
290     valref= v;
291     this->oldval= oldval;
292   }
293 };
294 
295 
296 /**
297   For every Geometry object write-accessed by a boost geometry function, i.e.
298   those passed as out parameter into set operation functions, call this
299   function before using the result object's data.
300 
301   @param resbuf_mgr tracks the result buffer
302   @return true if an error occurred or if the geometry is an empty
303           collection; false if no error occurred.
304 */
305 template <typename BG_geotype>
306 bool post_fix_result(BG_result_buf_mgr *resbuf_mgr,
307                      BG_geotype &geout, String *res);
308 
309 
310 
311 #endif
312