1 /******************************************************************************
2 * Copyright (c) 2011, Michael P. Gerlek (mpg@flaxen.com)
3 *
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following
8 * conditions are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided
15 * with the distribution.
16 * * Neither the name of Hobu, Inc. or Flaxen Geo Consulting nor the
17 * names of its contributors may be used to endorse or promote
18 * products derived from this software without specific prior
19 * written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
28 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
32 * OF SUCH DAMAGE.
33 ****************************************************************************/
34
35 #include <pdal/pdal_test_main.hpp>
36 #include <pdal/PDALUtils.hpp>
37 #include <pdal/SrsBounds.hpp>
38 #include <pdal/util/Bounds.hpp>
39 #include <pdal/util/ProgramArgs.hpp>
40
41 using namespace pdal;
42
TEST(BoundsTest,test_ctor)43 TEST(BoundsTest, test_ctor)
44 {
45 BOX3D b1;
46 EXPECT_TRUE(b1.empty());
47
48 b1.clear();
49 BOX3D b2;
50 EXPECT_EQ(b1, b2);
51 }
52
TEST(BoundsTest,test_equals)53 TEST(BoundsTest, test_equals)
54 {
55 BOX2D b1(1,2,3,4);
56 BOX2D b2(1,2,3,4);
57 BOX2D b3(1,2,3,6);
58
59 EXPECT_TRUE(b1 == b1);
60 EXPECT_TRUE(b1 == b2);
61 EXPECT_TRUE(b1 != b3);
62 EXPECT_TRUE(b2 != b3);
63
64 BOX3D b4(1,2,3,4,5,6);
65 BOX3D b5(1,2,3,4,5,6);
66 BOX3D b6(1,2,3,4,5,7);
67
68 EXPECT_TRUE(b4 == b4);
69 EXPECT_TRUE(b4 == b5);
70 EXPECT_TRUE(b5 == b4);
71 EXPECT_TRUE(b4 != b6);
72 EXPECT_TRUE(b6 == b6);
73 }
74
TEST(BoundsTest,test_copy)75 TEST(BoundsTest, test_copy)
76 {
77 BOX2D b1(1,2,3,4);
78 BOX2D b2(b1);
79 EXPECT_TRUE(b1==b2);
80
81 BOX3D b3(1,2,3,4,5,6);
82 BOX3D b4(b3);
83 EXPECT_TRUE(b1==b2);
84 }
85
TEST(BoundsTest,test_accessor)86 TEST(BoundsTest, test_accessor)
87 {
88 BOX2D b1(1,2,3,4);
89 EXPECT_DOUBLE_EQ(b1.minx, 1.0);
90 EXPECT_DOUBLE_EQ(b1.miny, 2.0);
91 EXPECT_DOUBLE_EQ(b1.maxx, 3.0);
92 EXPECT_DOUBLE_EQ(b1.maxy, 4.0);
93
94 BOX3D b2(1,2,3,4,5,6);
95 EXPECT_DOUBLE_EQ(b2.minx, 1.0);
96 EXPECT_DOUBLE_EQ(b2.miny, 2.0);
97 EXPECT_DOUBLE_EQ(b2.minz, 3.0);
98 EXPECT_DOUBLE_EQ(b2.maxx, 4.0);
99 EXPECT_DOUBLE_EQ(b2.maxy, 5.0);
100 EXPECT_DOUBLE_EQ(b2.maxz, 6.0);
101 }
102
TEST(BoundsTest,test_clip)103 TEST(BoundsTest, test_clip)
104 {
105 BOX2D r1(0,0,10,10);
106 BOX2D r2(1,1,11,11);
107 r1.clip(r2);
108
109 BOX2D r3(1,1,10,10);
110 EXPECT_TRUE(r1==r3);
111
112 BOX2D r4(2,4,6,8);
113 r1.clip(r4);
114
115 EXPECT_TRUE(r1==r4);
116
117 BOX2D r5(20,40,60,80);
118 r1.clip(r5);
119
120 // BUG: seems wrong -- need to better define semantics of clip, etc
121 // .clip() can make an invalid bounds, this should be fixed.
122 BOX2D r6(20,6, 40,8);
123
124 EXPECT_DOUBLE_EQ(r1.minx, 20);
125 EXPECT_DOUBLE_EQ(r1.maxx, 6);
126 EXPECT_DOUBLE_EQ(r1.miny, 40);
127 EXPECT_DOUBLE_EQ(r1.maxy, 8);
128
129 //ABELL - Need BOX3D example.
130 }
131
TEST(BoundsTest,test_intersect)132 TEST(BoundsTest, test_intersect)
133 {
134 BOX2D r1(0,0,10,10);
135 BOX2D r2(1,1,11,11);
136 BOX2D r3(100,100,101,101);
137 BOX2D r4(2,4,6,8);
138
139 EXPECT_TRUE(r1.overlaps(r1));
140
141 EXPECT_TRUE(r1.overlaps(r2));
142 EXPECT_TRUE(r2.overlaps(r1));
143
144 EXPECT_TRUE(!r1.overlaps(r3));
145 EXPECT_TRUE(!r3.overlaps(r1));
146
147 EXPECT_TRUE(r1.contains(r1));
148 EXPECT_TRUE(!r1.contains(r2));
149 EXPECT_TRUE(r1.contains(r4));
150
151 //ABELL - Need BOX3D example.
152 }
153
TEST(BoundsTest,test_grow)154 TEST(BoundsTest, test_grow)
155 {
156 BOX2D r1(50,51,100,101);
157 BOX2D r2(0,1,10,201);
158
159 r1.grow(r2);
160
161 BOX2D r3(0,1,100,201);
162 EXPECT_TRUE(r1 == r3);
163 //ABELL - Need BOX3D example.
164 }
165
TEST(BoundsTest,test_static)166 TEST(BoundsTest, test_static)
167 {
168 BOX2D t(BOX2D::getDefaultSpatialExtent());
169 double mind = (std::numeric_limits<double>::lowest)();
170 double maxd = (std::numeric_limits<double>::max)();
171 EXPECT_DOUBLE_EQ(t.minx, mind);
172 EXPECT_DOUBLE_EQ(t.maxx, maxd);
173 EXPECT_DOUBLE_EQ(t.miny, mind);
174 EXPECT_DOUBLE_EQ(t.maxy, maxd);
175
176 BOX3D u = BOX3D::getDefaultSpatialExtent();
177 EXPECT_DOUBLE_EQ(u.minx, mind);
178 EXPECT_DOUBLE_EQ(u.maxx, maxd);
179 EXPECT_DOUBLE_EQ(u.miny, mind);
180 EXPECT_DOUBLE_EQ(u.maxy, maxd);
181 EXPECT_DOUBLE_EQ(u.minz, mind);
182 EXPECT_DOUBLE_EQ(u.maxz, maxd);
183 }
184
TEST(BoundsTest,test_invalid)185 TEST(BoundsTest, test_invalid)
186 {
187 BOX2D t;
188 double mind = (std::numeric_limits<double>::lowest)();
189 double maxd = (std::numeric_limits<double>::max)();
190 EXPECT_DOUBLE_EQ(t.minx, maxd);
191 EXPECT_DOUBLE_EQ(t.maxx, mind);
192 EXPECT_DOUBLE_EQ(t.miny, maxd);
193 EXPECT_DOUBLE_EQ(t.maxy, mind);
194
195 BOX3D u;
196 EXPECT_DOUBLE_EQ(u.minx, maxd);
197 EXPECT_DOUBLE_EQ(u.maxx, mind);
198 EXPECT_DOUBLE_EQ(u.miny, maxd);
199 EXPECT_DOUBLE_EQ(u.maxy, mind);
200 EXPECT_DOUBLE_EQ(u.minz, maxd);
201 EXPECT_DOUBLE_EQ(u.maxz, mind);
202 }
203
TEST(BoundsTest,test_output)204 TEST(BoundsTest, test_output)
205 {
206 const BOX2D b2(1,2,101,102);
207 const BOX3D b3(1.1,2.2,3.3,101.1,102.2,103.3);
208
209 std::stringstream ss2(std::stringstream::in | std::stringstream::out);
210 std::stringstream ss3(std::stringstream::in | std::stringstream::out);
211
212 ss2 << b2;
213 ss3 << b3;
214
215 const std::string out2 = ss2.str();
216 const std::string out3 = ss3.str();
217
218 EXPECT_EQ(out2, "([1, 101], [2, 102])");
219 EXPECT_EQ(out3, "([1.1, 101.1], [2.2, 102.2], [3.3, 103.3])");
220 }
221
222
TEST(BoundsTest,test_input)223 TEST(BoundsTest, test_input)
224 {
225 std::stringstream ss("([1.1, 101.1], [2.2, 102.2], [3.3, 103.3])",
226 std::stringstream::in | std::stringstream::out);
227
228 BOX3D rr;
229 ss >> rr;
230 BOX3D r(1.1,2.2,3.3,101.1,102.2,103.3);
231 EXPECT_TRUE(r == rr);
232 }
233
TEST(BoundsTest,test_parse)234 TEST(BoundsTest, test_parse)
235 {
236 std::istringstream iss1("([1,101],[2,102],[3,103])");
237 std::istringstream iss2("([1, 101], [2, 102], [3, 103])");
238
239 BOX3D b1;
240 BOX3D b2;
241
242 iss1 >> b1;
243 iss2 >> b2;
244
245 EXPECT_EQ(b1, b2);
246 }
247
TEST(BoundsTest,test_wkt)248 TEST(BoundsTest, test_wkt)
249 {
250 BOX2D b(1.1,2.2,101.1,102.2);
251 std::string out = "POLYGON ((1.1 2.2, 1.1 102.2, 101.1 102.2, 101.1 2.2, 1.1 2.2))";
252 EXPECT_EQ(b.toWKT(1), out);
253
254 BOX3D b2(1.1,2.2,3.3,101.1,102.2,103.3);
255 std::string out2 = "POLYHEDRON Z ( ((1.1 2.2 3.3, 101.1 2.2 3.3, 101.1 102.2 3.3, 1.1 102.2 3.3, 1.1 2.2 3.3, )), ((1.1 2.2 3.3, 101.1 2.2 3.3, 101.1 2.2 103.3, 1.1 2.2 103.3, 1.1 2.2 3.3, )), ((101.1 2.2 3.3, 101.1 102.2 3.3, 101.1 102.2 103.3, 101.1 2.2 103.3, 101.1 2.2 3.3, )), ((101.1 102.2 3.3, 1.1 102.2 3.3, 1.1 102.2 103.3, 101.1 102.2 103.3, 101.1 102.2 3.3, )), ((1.1 102.2 3.3, 1.1 2.2 3.3, 1.1 2.2 103.3, 1.1 102.2 103.3, 1.1 102.2 3.3, )), ((1.1 2.2 103.3, 101.1 2.2 103.3, 101.1 102.2 103.3, 1.1 102.2 103.3, 1.1 2.2 103.3, )) )";
256 EXPECT_EQ(b2.toWKT(1), out2);
257 }
TEST(BoundsTest,test_json)258 TEST(BoundsTest, test_json)
259 {
260 BOX2D b(1.1,2.2,101.1,102.2);
261 std::string out = "{\"bbox\":[1.1, 2.2, 101.1,102.2]}";
262 EXPECT_EQ(b.toGeoJSON(1), out);
263 }
264
TEST(BoundsTest,test_2d_input)265 TEST(BoundsTest, test_2d_input)
266 {
267 std::stringstream ss("([1.1, 101.1], [2.2, 102.2])", std::stringstream::in | std::stringstream::out);
268 BOX2D rr;
269 ss >> rr;
270 BOX2D r(1.1,2.2,101.1,102.2);
271 EXPECT_EQ(r, rr);
272 }
273
TEST(BoundsTest,test_precisionloss)274 TEST(BoundsTest, test_precisionloss)
275 {
276 const BOX2D b1(0.123456789,0.0,0,0);
277 EXPECT_DOUBLE_EQ(b1.minx, 0.123456789);
278
279 // convert it to a string, which is what happens
280 // when you do something like:
281 // options.getValueOrDefault<BOX3D>("bounds", BOX3D());
282 std::ostringstream oss;
283 oss << b1;
284
285 // convert it back
286 std::istringstream iss(oss.str());
287 BOX2D b2;
288 iss >> b2;
289
290 EXPECT_DOUBLE_EQ(b2.minx, 0.123456789);
291 }
292
293 namespace
294 {
295 std::string fancySrs =
296 R"SRS(
297 COMPD_CS["OSGB36 / British National Grid + ODN",
298 PROJCS["OSGB 1936 / British National Grid",
299 GEOGCS["OSGB 1936",
300 DATUM["OSGB_1936",
301 SPHEROID["Airy 1830",6377563.396,299.3249646,
302 AUTHORITY["EPSG","7001"]],
303 TOWGS84[375,-111,431,0,0,0,0],
304 AUTHORITY["EPSG","6277"]],
305 PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],
306 UNIT["DMSH",0.0174532925199433,AUTHORITY["EPSG","9108"]],
307 AXIS["Lat",NORTH],
308 AXIS["Long",EAST],
309 AUTHORITY["EPSG","4277"]],
310 PROJECTION["Transverse_Mercator"],
311 PARAMETER["latitude_of_origin",49],
312 PARAMETER["central_meridian",-2],
313 PARAMETER["scale_factor",0.999601272],
314 PARAMETER["false_easting",400000],
315 PARAMETER["false_northing",-100000],
316 UNIT["metre",1,AUTHORITY["EPSG","9001"]],
317 AXIS["E",EAST],
318 AXIS["N",NORTH],
319 AUTHORITY["EPSG","27700"]],
320 VERT_CS["Newlyn",
321 VERT_DATUM["Ordnance Datum Newlyn",2005,AUTHORITY["EPSG","5101"]],
322 UNIT["metre",1,AUTHORITY["EPSG","9001"]],
323 AXIS["Up",UP],
324 AUTHORITY["EPSG","5701"]],
325 AUTHORITY["EPSG","7405"]]
326 )SRS";
327 }
328
TEST(BoundsTest,b1)329 TEST(BoundsTest, b1)
330 {
331 std::string s("([0,1],[0,1])");
332
333 Bounds b;
334
335 Utils::fromString(s, b);
336 EXPECT_FALSE(b.is3d());
337 EXPECT_TRUE(b.to3d().empty());
338
339 BOX2D box = b.to2d();
340 EXPECT_EQ(box.minx, 0.0);
341 EXPECT_EQ(box.miny, 0.0);
342 EXPECT_EQ(box.maxx, 1.0);
343 EXPECT_EQ(box.maxy, 1.0);
344
345 std::string t("([+0e0,1.00000],[0,1e0]) / EPSG:2596");
346
347 SrsBounds sb;
348 Utils::fromString(t, sb);
349
350 EXPECT_FALSE(sb.is3d());
351 EXPECT_TRUE(sb.to3d().empty());
352
353 box = sb.to2d();
354 EXPECT_EQ(box.minx, 0.0);
355 EXPECT_EQ(box.miny, 0.0);
356 EXPECT_EQ(box.maxx, 1.0);
357 EXPECT_EQ(box.maxy, 1.0);
358
359 EXPECT_NE(std::string::npos,
360 sb.spatialReference().getWKT().find("Krassowsky 1940"));
361
362 Utils::fromString("([0, -1.00000],[0,-1e0] ) / " + fancySrs, sb);
363
364 EXPECT_FALSE(sb.is3d());
365 EXPECT_TRUE(sb.to3d().empty());
366
367 box = sb.to2d();
368 EXPECT_EQ(box.minx, 0.0);
369 EXPECT_EQ(box.miny, 0.0);
370 EXPECT_EQ(box.maxx, -1.0);
371 EXPECT_EQ(box.maxy, -1.0);
372 EXPECT_TRUE(sb.spatialReference().valid());
373 EXPECT_NE(std::string::npos,
374 sb.spatialReference().getWKT().find("Ordnance Datum Newlyn"));
375 }
376
TEST(BoundsTest,fromstring)377 TEST(BoundsTest, fromstring)
378 {
379 ProgramArgs a;
380 BOX2D box;
381 a.add("bounds", "BOX", box);
382
383 std::string badbox("[0, 1]");
384 EXPECT_THROW(a.parse({"--bounds", badbox}), arg_error);
385 }
386
TEST(BoundsTest,b2)387 TEST(BoundsTest, b2)
388 {
389 std::string s("([0,1],[0,1], [0,2])");
390 Bounds b;
391
392 Utils::fromString(s, b);
393 EXPECT_TRUE(b.is3d());
394
395 BOX2D box = b.to2d();
396 EXPECT_EQ(box.minx, 0.0);
397 EXPECT_EQ(box.miny, 0.0);
398 EXPECT_EQ(box.maxx, 1.0);
399 EXPECT_EQ(box.maxy, 1.0);
400
401 BOX3D box3 = b.to3d();
402 EXPECT_EQ(box3.minx, 0.0);
403 EXPECT_EQ(box3.miny, 0.0);
404 EXPECT_EQ(box3.maxx, 1.0);
405 EXPECT_EQ(box3.maxy, 1.0);
406 EXPECT_EQ(box3.minz, 0.0);
407 EXPECT_EQ(box3.maxz, 2.0);
408
409 SrsBounds sb;
410 std::string t("([+0,1],[0,1.0000], [-0e0,2]) / EPSG:2596");
411 Utils::fromString(t, sb);
412 EXPECT_TRUE(sb.is3d());
413 box3 = sb.to3d();
414 EXPECT_EQ(box3.minx, 0.0);
415 EXPECT_EQ(box3.miny, 0.0);
416 EXPECT_EQ(box3.maxx, 1.0);
417 EXPECT_EQ(box3.maxy, 1.0);
418 EXPECT_EQ(box3.minz, 0.0);
419 EXPECT_EQ(box3.maxz, 2.0);
420
421 EXPECT_NE(std::string::npos,
422 sb.spatialReference().getWKT().find("Krassowsky 1940"));
423
424 Utils::fromString("([0,1],[0,1], [0,2]) / " + fancySrs, sb);
425 EXPECT_TRUE(sb.is3d());
426
427 box3 = sb.to3d();
428 EXPECT_EQ(box3.minx, 0.0);
429 EXPECT_EQ(box3.miny, 0.0);
430 EXPECT_EQ(box3.minz, 0.0);
431 EXPECT_EQ(box3.maxx, 1.0);
432 EXPECT_EQ(box3.maxy, 1.0);
433 EXPECT_EQ(box3.maxz, 2.0);
434 EXPECT_TRUE(sb.spatialReference().valid());
435 EXPECT_NE(std::string::npos,
436 sb.spatialReference().getWKT().find("Ordnance Datum Newlyn"));
437 }
438
TEST(BoundsTest,bounds_insertion)439 TEST(BoundsTest, bounds_insertion)
440 {
441 std::string s;
442 std::ostringstream oss(s);
443 Bounds b;
444 oss << b;
445 }
446