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
21    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
22 
23 #include "my_config.h"
24 #include <gtest/gtest.h>
25 
26 #include "my_global.h"
27 #include "gstream.h"
28 #include "spatial.h"
29 
30 namespace gis_algo_unittest {
31 
32 /*
33   Testing Gis_polygon_ring::set_ring_order function.
34  */
35 class SetRingOrderTest : public ::testing::Test
36 {
37 public:
SetRingOrderTest()38   SetRingOrderTest() :my_flags(Geometry::wkb_linestring, 0)
39   {
40     latincc= &my_charset_latin1;
41   }
42   Geometry *geometry_from_text(const String &wkt, String *wkb,
43                                Geometry_buffer *geobuf);
44   void set_order_and_compare(const std::string &str, const std::string &str2,
45                              bool want_ccw= true);
46 
47 
48   const static uint32 srid= 0;
49   CHARSET_INFO *latincc;
50   String str, str2, wkt, wkt2;
51   Geometry_buffer buffer, buffer2;
52   Geometry::Flags_t my_flags;
53 };
54 
55 
geometry_from_text(const String & wkt,String * wkb,Geometry_buffer * geobuf)56 Geometry *SetRingOrderTest::geometry_from_text(const String &wkt, String *wkb,
57                                                Geometry_buffer *geobuf)
58 {
59   Gis_read_stream trs(wkt.charset(), wkt.ptr(), wkt.length());
60 
61   wkb->set_charset(&my_charset_bin);
62   wkb->length(0);
63   return Geometry::create_from_wkt(geobuf, &trs, wkb, 1);
64 }
65 
set_order_and_compare(const std::string & s1,const std::string & s2,bool want_ccw)66 void SetRingOrderTest::set_order_and_compare(const std::string &s1,
67                                              const std::string &s2,
68                                              bool want_ccw)
69 {
70   wkt.set(s1.c_str(), s1.length(), latincc);
71   wkt2.set(s2.c_str(), s2.length(), latincc);
72 
73   Gis_polygon_ring *ringp= static_cast<Gis_polygon_ring *>
74     (geometry_from_text(wkt, &str, &buffer));
75   assert(ringp->get_geotype() == Geometry::wkb_linestring);
76   Gis_polygon_ring ring(ringp->get_ptr(),
77                         ringp->get_nbytes(), my_flags, 0U);
78   EXPECT_EQ(ring.set_ring_order(want_ccw), false);
79 
80 
81   ringp= static_cast<Gis_polygon_ring *>(geometry_from_text(wkt2, &str2,
82                                                             &buffer2));
83   assert(ringp->get_geotype() == Geometry::wkb_linestring);
84   Gis_polygon_ring ring2(ringp->get_ptr(),
85                          ringp->get_nbytes(), my_flags, 0U);
86   EXPECT_EQ(ring2.set_ring_order(want_ccw), false);
87 
88   EXPECT_EQ(str.length(), str2.length());
89   EXPECT_EQ(memcmp(str.ptr(), str2.ptr(), str.length()), 0);
90 }
91 
TEST_F(SetRingOrderTest,SetRingOrderCCW)92 TEST_F(SetRingOrderTest, SetRingOrderCCW)
93 {
94   SCOPED_TRACE("SetRingOrderCCW");
95   std::string geom1("linestring(0 0, 0 1, 1 1, 1 0, 0 0)");
96   std::string geom2("linestring(0 0, 1 0, 1 1, 0 1, 0 0)");
97   set_order_and_compare(geom1, geom2);
98 }
99 
TEST_F(SetRingOrderTest,SetRingOrderCW)100 TEST_F(SetRingOrderTest, SetRingOrderCW)
101 {
102   SCOPED_TRACE("SetRingOrderCW");
103   std::string geom1("linestring(0 0, 0 1, 1 1, 1 0, 0 0)");
104   std::string geom2("linestring(0 0, 1 0, 1 1, 0 1, 0 0)");
105   set_order_and_compare(geom1, geom2, false);
106 }
107 
TEST_F(SetRingOrderTest,SetRingOrder2CCW)108 TEST_F(SetRingOrderTest, SetRingOrder2CCW)
109 {
110   SCOPED_TRACE("SetRingOrder2CCW");
111   std::string geom3("linestring(0 0, 0 1, 1 0, 0 0)");
112   std::string geom4("linestring(0 0, 1 0, 0 1, 0 0)");
113   set_order_and_compare(geom3, geom4);
114 }
115 
TEST_F(SetRingOrderTest,SetRingOrder2CW)116 TEST_F(SetRingOrderTest, SetRingOrder2CW)
117 {
118   SCOPED_TRACE("SetRingOrder2CW");
119   std::string geom3("linestring(0 0, 0 1, 1 0, 0 0)");
120   std::string geom4("linestring(0 0, 1 0, 0 1, 0 0)");
121   set_order_and_compare(geom3, geom4, false);
122 }
123 
TEST_F(SetRingOrderTest,DuplicateMinPointBeforeCCW)124 TEST_F(SetRingOrderTest, DuplicateMinPointBeforeCCW)
125 {
126   SCOPED_TRACE("DuplicateMinPointBeforeCCW");
127   std::string geom1("linestring(0 0, 0 1, 1 1, 1 0, 0 0, 0 0, 0 0)");
128   std::string geom2("linestring(0 0, 0 0, 0 0, 1 0, 1 1, 0 1, 0 0)");
129   set_order_and_compare(geom1, geom2);
130 }
131 
TEST_F(SetRingOrderTest,DuplicateMinPointBeforeCW)132 TEST_F(SetRingOrderTest, DuplicateMinPointBeforeCW)
133 {
134   SCOPED_TRACE("DuplicateMinPointBeforeCW");
135   std::string geom1("linestring(0 0, 0 1, 1 1, 1 0, 0 0, 0 0, 0 0)");
136   std::string geom2("linestring(0 0, 0 0, 0 0, 1 0, 1 1, 0 1, 0 0)");
137   set_order_and_compare(geom1, geom2, false);
138 }
139 
TEST_F(SetRingOrderTest,DuplicateMinPointAfterCCW)140 TEST_F(SetRingOrderTest, DuplicateMinPointAfterCCW)
141 {
142   SCOPED_TRACE("DuplicateMinPointAfterCCW");
143   std::string geom1("linestring(0 0, 0 0, 0 0, 0 1, 1 1, 1 0, 0 0)");
144   std::string geom2("linestring(0 0, 1 0, 1 1, 0 1, 0 0, 0 0, 0 0)");
145   set_order_and_compare(geom1, geom2);
146 }
147 
TEST_F(SetRingOrderTest,DuplicateMinPointAfterCW)148 TEST_F(SetRingOrderTest, DuplicateMinPointAfterCW)
149 {
150   SCOPED_TRACE("DuplicateMinPointAfterCW");
151   std::string geom1("linestring(0 0, 0 0, 0 0, 0 1, 1 1, 1 0, 0 0)");
152   std::string geom2("linestring(0 0, 1 0, 1 1, 0 1, 0 0, 0 0, 0 0)");
153   set_order_and_compare(geom1, geom2, false);
154 }
155 
TEST_F(SetRingOrderTest,RingDegradedToPointTest)156 TEST_F(SetRingOrderTest, RingDegradedToPointTest)
157 {
158   SCOPED_TRACE("RingDegradedToPointTest");
159   std::string s1("linestring(0 0, 0 0, 0 0, 0 0, 0 0)");
160   wkt.set(s1.c_str(), s1.length(), latincc);
161 
162   Gis_polygon_ring *ringp= static_cast<Gis_polygon_ring *>
163     (geometry_from_text(wkt, &str, &buffer));
164   assert(ringp->get_geotype() == Geometry::wkb_linestring);
165   Gis_polygon_ring ring(ringp->get_ptr(),
166                         ringp->get_nbytes(), my_flags, 0U);
167   EXPECT_EQ(ring.set_ring_order(true/*CCW*/), true);
168 }
169 
170 
171 /*
172   Testing functions in Geometry and its children classes that are not covered
173   by current BG functionalities.
174  */
175 class GeometryManipulationTest : public SetRingOrderTest
176 {
177 public:
assign_multipolygon_back(Gis_multi_polygon & mpl2,const Gis_polygon & pl)178   void assign_multipolygon_back(Gis_multi_polygon &mpl2, const Gis_polygon &pl)
179   {
180     for (size_t i = 0; i < pl.outer().size(); i++)
181       mpl2.back().outer().push_back(pl.outer()[i]);
182     for (size_t i = 0; i < pl.inners().size(); i+=2)
183     {
184       mpl2.back().inners().push_back(pl.inners()[i]);
185       if (i + 1 < pl.inners().size())
186       {
187         mpl2.back().inners().resize(mpl2.back().inners().size() + 1);
188         for (size_t j = 0; j < pl.inners()[i+1].size(); j++)
189           mpl2.back().inners().back().push_back(pl.inners()[i+1][j]);
190       }
191     }
192   }
193 };
194 
195 
TEST_F(GeometryManipulationTest,PolygonCopyTest)196 TEST_F(GeometryManipulationTest, PolygonCopyTest)
197 {
198   SCOPED_TRACE("PolygonCopyTest");
199   std::string s1("polygon((0 0, 1 0, 1 1, 0 1, 0 0))");
200   wkt.set(s1.c_str(), s1.length(), latincc);
201 
202   Gis_polygon *plgn=
203     static_cast<Gis_polygon *>(geometry_from_text(wkt, &str, &buffer));
204   Gis_polygon plgn1(plgn->get_data_ptr(), plgn->get_data_size(),
205                     plgn->get_flags(), plgn->get_srid());
206   Gis_polygon plgn2(plgn1);
207   Gis_polygon plgn3;
208 
209   plgn3= plgn2;
210 
211   String wkb3, wkb4, wkb5;
212   plgn3.as_wkb(&wkb3, false);
213   plgn3.to_wkb_unparsed();
214   plgn3.as_wkb(&wkb5, true);
215   EXPECT_EQ(wkb3.length(), wkb5.length());
216   EXPECT_EQ(memcmp(((char *)wkb3.ptr()) + WKB_HEADER_SIZE,
217                    ((char *)wkb5.ptr()) + WKB_HEADER_SIZE,
218                    wkb5.length() - WKB_HEADER_SIZE), 0);
219 
220   plgn2.as_geometry(&wkb4, false);
221   EXPECT_EQ(wkb3.length() + 4, wkb4.length());
222   EXPECT_EQ(memcmp(GEOM_HEADER_SIZE + ((char *)wkb4.ptr()),
223                    ((char *)wkb3.ptr()) + WKB_HEADER_SIZE,
224                    wkb3.length() - WKB_HEADER_SIZE), 0);
225 
226   // Check they have identical data. Can only do so in wkb form.
227   plgn1.to_wkb_unparsed();
228   plgn2.to_wkb_unparsed();
229   EXPECT_EQ(plgn1.get_data_size(), plgn2.get_data_size());
230   EXPECT_EQ(memcmp(plgn->get_data_ptr(), plgn2.get_data_ptr(),
231                    plgn2.get_data_size()), 0);
232 
233   EXPECT_EQ(plgn3.get_data_size(), plgn2.get_data_size());
234   EXPECT_EQ(memcmp(plgn3.get_data_ptr(), plgn2.get_data_ptr(),
235                    plgn2.get_data_size()), 0);
236 }
237 
TEST_F(GeometryManipulationTest,PolygonManipulationTest)238 TEST_F(GeometryManipulationTest, PolygonManipulationTest)
239 {
240   SCOPED_TRACE("PolygonManipulationTest");
241   std::string s1("polygon((0 0, 1 0, 1 1, 0 1, 0 0))");
242   std::string s2("multipolygon(((0 0, 1 0, 1 1, 0 1, 0 0)))");
243   std::string s3("linestring(0.5 0.25, 0.5 0.75, 0.75 0.75, 0.5 0.25)");
244   std::string s4("multipolygon(((0 0, 1 0, 1 1, 0 1, 0 0)),     \
245     ((0 0, 1 0, 1 1, 0 1, 0 0), (0.5 0.25, 0.5 0.75, 0.75  0.75, 0.5 0.25)),\
246     ((0 0, 1 0, 1 1, 0 1, 0 0), (0.5 0.25, 0.5 0.75, 0.75  0.75, 0.5 0.25)))");
247   std::string s5("polygon((0 0, 1 0, 1 1, 0 1, 0 0),\
248     (0.5 0.25, 0.5 0.75, 0.75  0.75, 0.5 0.25))");
249   wkt.set(s1.c_str(), s1.length(), latincc);
250   wkt2.set(s3.c_str(), s3.length(), latincc);
251 
252   Gis_polygon *plgn0=
253     static_cast<Gis_polygon *>(geometry_from_text(wkt, &str, &buffer));
254   Gis_line_string *ls0=
255     static_cast<Gis_line_string *>(geometry_from_text(wkt2, &str2, &buffer2));
256   Gis_polygon plgn(plgn0->get_data_ptr(), plgn0->get_data_size(),
257                    plgn0->get_flags(), plgn0->get_srid());
258   Gis_line_string ls(ls0->get_data_ptr(), ls0->get_data_size(),
259                      ls0->get_flags(), ls0->get_srid());
260   Gis_line_string ls00(*ls0);
261 
262   Geometry_buffer buffer3;
263   String wkt3, str3;
264 
265   wkt3.set(s2.c_str(), s2.length(), latincc);
266   Gis_multi_polygon *pmplgn= (static_cast<Gis_multi_polygon *>
267                               (geometry_from_text(wkt3, &str3, &buffer3)));
268   Gis_multi_polygon mplgn0(pmplgn->get_data_ptr(), pmplgn->get_data_size(),
269                            pmplgn->get_flags(), pmplgn->get_srid());
270   EXPECT_EQ(mplgn0.size(), 1U);
271   Gis_multi_polygon mplgn= mplgn0;
272 
273   plgn.inners().resize(1);
274   for (int i= 0; i < 4; i++)
275     (plgn.inners())[0].push_back(ls[i]);
276   mplgn.push_back(plgn);
277   plgn.to_wkb_unparsed();
278 
279   Geometry_buffer buffer5;
280   String wkt5, str5;
281   wkt5.set(s5.c_str(), s5.length(), latincc);
282 
283   Gis_polygon *plgn20=
284     static_cast<Gis_polygon *>(geometry_from_text(wkt5, &str5, &buffer5));
285   Gis_polygon plgn2(plgn20->get_data_ptr(), plgn20->get_data_size(),
286                     plgn20->get_flags(), plgn20->get_srid());
287   EXPECT_EQ(plgn.get_data_size(), plgn2.get_nbytes());
288 
289   mplgn.push_back(plgn2);
290 
291   plgn2.to_wkb_unparsed();
292   EXPECT_EQ(memcmp(plgn.get_data_ptr(), plgn2.get_data_ptr(),
293                    plgn2.get_data_size()), 0);
294 
295 
296   Geometry_buffer buffer4;
297   String wkt4, str4;
298   wkt4.set(s4.c_str(), s4.length(), latincc);
299 
300   Gis_multi_polygon *mplgn2=
301     static_cast<Gis_multi_polygon *>(geometry_from_text(wkt4, &str4, &buffer4));
302 
303   EXPECT_EQ(mplgn.get_data_size(), mplgn2->get_data_size());
304   EXPECT_EQ(memcmp(mplgn.get_data_ptr(), mplgn2->get_data_ptr(),
305                    mplgn2->get_data_size()), 0);
306 }
307 
308 
TEST_F(GeometryManipulationTest,ResizeAssignmentTest)309 TEST_F(GeometryManipulationTest, ResizeAssignmentTest)
310 {
311   Gis_polygon_ring ring1;
312   Gis_line_string ls4, ls5, ls6, ls7, ls8;
313   for (int i = 0; i < 5; i++)
314   {
315     Gis_point pt;
316     pt.set<0>(i);
317     pt.set<1>(i);
318     ring1.push_back(pt);
319     ls4.push_back(pt);
320     ls6.push_back(pt);
321     ls7.push_back(pt);
322     if (i != 4)
323       ls8.push_back(pt);
324   }
325   Gis_point pt;
326   pt.set<0>(0);
327   pt.set<1>(0);
328   ls7.push_back(pt);
329 
330 
331   Gis_polygon plgn3, plgn4, plgn5;
332   plgn3.outer() = ring1;
333   plgn3.inners().push_back(ring1);
334   plgn3.inners().resize(plgn3.inners().size());
335 
336   plgn5.outer() = *((Gis_polygon_ring *)(&ls8));
337   plgn5.inners().push_back(*((Gis_polygon_ring *)(&ls7)));
338   plgn5.inners().push_back(*((Gis_polygon_ring *)(&ls6)));
339 
340   Gis_multi_polygon mplgn3, mplgn4;
341   mplgn3.push_back(plgn3);
342   mplgn3.resize(2);
343   assign_multipolygon_back(mplgn3, plgn5);
344   mplgn3.push_back(plgn3);
345   mplgn3.resize(4);
346   assign_multipolygon_back(mplgn3, plgn5);
347 
348   mplgn4.resize(1);
349   assign_multipolygon_back(mplgn4, plgn3);
350   mplgn4.push_back(plgn5);
351   mplgn4.resize(3);
352   assign_multipolygon_back(mplgn4, plgn3);
353   mplgn4.push_back(plgn5);
354   mplgn3.reassemble();
355   mplgn4.reassemble();
356   EXPECT_EQ(mplgn3.get_ptr() != mplgn4.get_ptr() &&
357             mplgn3.get_nbytes() == mplgn4.get_nbytes() &&
358             memcmp(mplgn3.get_ptr(), mplgn4.get_ptr(),
359                    mplgn3.get_nbytes()) == 0, true);
360 
361 
362   Gis_multi_line_string mls1, mls2;
363   mls1.resize(1);
364   for (size_t i= 0; i < ls4.size(); i++)
365     mls1.back().push_back(ls4[i]);
366   mls1.push_back(ls6);
367   mls1.resize(3);
368   for (size_t i= 0; i < ls7.size(); i++)
369     mls1.back().push_back(ls7[i]);
370   mls1.push_back(ls8);
371 
372   mls2.push_back(ls4);
373   mls2.resize(2);
374   for (size_t i= 0; i < ls6.size(); i++)
375     mls2.back().push_back(ls6[i]);
376 
377   mls2.push_back(ls7);
378   mls2.resize(4);
379   for (size_t i= 0; i < ls8.size(); i++)
380     mls2.back().push_back(ls8[i]);
381 
382   mls1.reassemble();
383   mls2.reassemble();
384   EXPECT_EQ(mls1.get_ptr() != mls2.get_ptr() &&
385             mls1.get_nbytes() == mls2.get_nbytes() &&
386             memcmp(mls1.get_ptr(), mls2.get_ptr(), mls1.get_nbytes()) == 0,
387             true);
388   String str1, str2;
389   str1.append(mls1.get_cptr(), mls1.get_nbytes(), &my_charset_bin);
390   Gis_geometry_collection geocol(0, Geometry::wkb_multipolygon, &str1, &str2);
391 
392   ls4= ls5;
393   EXPECT_EQ(ls4.get_ptr() == NULL && ls4.get_nbytes() == 0, true);
394   EXPECT_EQ(ls5.get_ptr() == NULL && ls5.get_nbytes() == 0, true);
395   plgn3= plgn4;
396   plgn3.to_wkb_unparsed();
397   EXPECT_EQ(plgn3.get_ptr() == NULL && plgn3.get_nbytes() == 0, true);
398 
399   ls4= ls6;
400   EXPECT_EQ(ls4.get_ptr() != ls6.get_ptr() &&
401             ls4.get_nbytes() == ls6.get_nbytes() &&
402             memcmp(ls4.get_ptr(), ls6.get_ptr(), ls6.get_nbytes()) == 0, true);
403 
404   ls4= ls7;
405   EXPECT_EQ(ls4.get_ptr() != ls7.get_ptr() &&
406             ls4.get_nbytes() == ls7.get_nbytes() &&
407             memcmp(ls4.get_ptr(), ls7.get_ptr(), ls7.get_nbytes()) == 0, true);
408 
409   ls4= ls6;
410   EXPECT_EQ(ls4.get_ptr() != ls6.get_ptr() &&
411             ls4.get_nbytes() == ls6.get_nbytes() &&
412             memcmp(ls4.get_ptr(), ls6.get_ptr(), ls6.get_nbytes()) == 0, true);
413 
414   void *buf= gis_wkb_alloc(ls8.get_nbytes() + 32);
415   memcpy(buf, ls8.get_ptr(), ls8.get_nbytes());
416   char *cbuf= static_cast<char *>(buf);
417   memset(cbuf + ls8.get_nbytes(), 0xff, 32);
418   cbuf[ls8.get_nbytes() + 31] = '\0';
419 
420   ls4.set_ptr(buf, ls8.get_nbytes());
421   EXPECT_EQ(ls4.get_ptr() != ls8.get_ptr() &&
422             ls4.get_nbytes() == ls8.get_nbytes() &&
423             memcmp(ls4.get_ptr(), ls8.get_ptr(), ls8.get_nbytes()) == 0, true);
424 
425   for (size_t i= ls4.size() + 1; i < 64; i++)
426   {
427     ls4.resize(i);
428     ls4.back().set<0>(i);
429     ls4.back().set<1>(i);
430   }
431 }
432 
433 
434 /*
435   Tests of miscellineous GIS functionalities.
436 */
437 class GisMiscTests : public ::testing::Test
438 {
439 public:
440 };
441 
TEST_F(GisMiscTests,PointxyDistanceTest)442 TEST_F(GisMiscTests, PointxyDistanceTest)
443 {
444   const point_xy pt1(1.0, 1.0);
445   const point_xy pt2(1e300, -1e300);
446   const point_xy pt3(1e300, 1);
447   const point_xy pt4(1, 1e300);
448   const point_xy pt5(pt2);
449 
450   EXPECT_FALSE(my_isfinite(pt1.distance(pt2)));
451   EXPECT_FALSE(my_isfinite(pt1.distance(pt3)));
452   EXPECT_FALSE(my_isfinite(pt1.distance(pt4)));
453   EXPECT_FALSE(my_isfinite(pt2.distance(pt3)));
454   EXPECT_FALSE(my_isfinite(pt2.distance(pt4)));
455   EXPECT_FALSE(my_isfinite(pt3.distance(pt4)));
456   EXPECT_FLOAT_EQ(0.0, pt2.distance(pt5));
457 }
458 
459 }
460