1 //
2 // GEOS Unit Test utilities, extension of TUT Framework namespace
3 //
4 #ifndef GEOS_TUT_UTILITY_H_INCLUDED
5 #define GEOS_TUT_UTILITY_H_INCLUDED
6
7 // tut
8 #include <tut/tut.hpp>
9 // geos
10 #include <geos/geom/Geometry.h>
11 #include <geos/geom/GeometryCollection.h>
12 #include <geos/geom/Coordinate.h>
13 #include <geos/geom/CoordinateArraySequence.h>
14 #include <geos/geom/CoordinateArraySequenceFactory.h>
15 #include <geos/geom/CoordinateSequenceFactory.h>
16 #include <geos/geom/Dimension.h>
17 #include <geos/geom/Point.h>
18 #include <geos/geom/Polygon.h>
19 #include <geos/geom/LinearRing.h>
20 #include <geos/geom/LineString.h>
21 #include <geos/geom/MultiPoint.h>
22 #include <geos/geom/MultiLineString.h>
23 #include <geos/geom/MultiPolygon.h>
24 #include <geos/geom/PrecisionModel.h>
25 #include <geos/geom/GeometryFactory.h>
26 #include <geos/geom/prep/PreparedGeometry.h>
27 #include <geos/io/WKTReader.h>
28 #include <geos/io/WKTWriter.h>
29 // std
30 #include <memory>
31 #include <cstdlib>
32 #include <cassert>
33 #include <string>
34 #include <vector>
35
36 namespace tut {
37
38 //
39 // Helper typedefs
40 //
41 typedef geos::geom::Coordinate* CoordinatePtr;
42 typedef geos::geom::Coordinate const* CoordinateCPtr;
43
44 typedef geos::geom::CoordinateSequence* CoordSeqPtr;
45 typedef geos::geom::CoordinateSequence const* CoordSeqCPtr;
46
47 typedef geos::geom::CoordinateArraySequence* CoordArrayPtr;
48 typedef geos::geom::CoordinateArraySequence const* CoordArrayCPtr;
49
50 typedef geos::geom::Geometry* GeometryPtr;
51 typedef geos::geom::Geometry const* GeometryCPtr;
52
53 typedef geos::geom::Point* PointPtr;
54 typedef geos::geom::Point const* PointCPtr;
55 typedef geos::geom::LinearRing* LinearRingPtr;
56 typedef geos::geom::LinearRing const* LinearRingCPtr;
57 typedef geos::geom::LineString* LineStringPtr;
58 typedef geos::geom::LineString const* LineStringCPtr;
59 typedef geos::geom::Polygon* PolygonPtr;
60 typedef geos::geom::Polygon const* PolygonCPtr;
61
62 typedef geos::geom::GeometryCollection* GeometryColPtr;
63 typedef geos::geom::GeometryCollection const* GeometryColCPtr;
64
65 typedef geos::geom::MultiPoint* MultiPointPtr;
66 typedef geos::geom::MultiPoint const* MultiPointCPtr;
67 typedef geos::geom::MultiLineString* MultiLineStringPtr;
68 typedef geos::geom::MultiLineString const* MultiLineStringCPtr;
69 typedef geos::geom::MultiPolygon* MultiPolygonPtr;
70 typedef geos::geom::MultiPolygon const* MultiPolygonCPtr;
71
72 // prepared geometries always returend as const
73 typedef geos::geom::prep::PreparedGeometry const* PreparedGeometryPtr;
74
75 //
76 // Type cast helper utilities
77 //
78
79 template<typename Type, typename InstanceType>
80 inline bool
isInstanceOf(InstanceType const * instance)81 isInstanceOf(InstanceType const* instance)
82 {
83 assert(nullptr != instance);
84 return (nullptr != dynamic_cast<Type const*>(instance));
85 }
86
87 template<typename Type, typename InstanceType>
88 inline Type const*
instanceOf(InstanceType const * instance)89 instanceOf(InstanceType const* instance)
90 {
91 assert(nullptr != instance);
92 return dynamic_cast<Type const*>(instance);
93 }
94
95 inline void
ensure_equals_xyz(geos::geom::Coordinate const & actual,geos::geom::Coordinate const & expected)96 ensure_equals_xyz(geos::geom::Coordinate const& actual,
97 geos::geom::Coordinate const& expected)
98 {
99 ensure_equals("Coordinate X", actual.x, expected.x );
100 ensure_equals("Coordinate Y", actual.y, expected.y );
101 if ( std::isnan(expected.z) ) {
102 ensure("Coordinate Z should be NaN", std::isnan(actual.z) );
103 } else {
104 ensure_equals("Coordinate Z", actual.z, expected.z );
105 }
106 }
107
108
109
110 //
111 // Geometries structure comparators
112 //
113
114
115
116
117
118 template <typename T1, typename T2>
119 inline void
ensure_equals_geometry(T1 const * lhs,T2 const * rhs)120 ensure_equals_geometry(T1 const* lhs, T2 const* rhs)
121 {
122 assert(0 != lhs);
123 assert(0 != rhs);
124 assert(!"DIFFERENT TYPES ARE NOT OF THE SAME STRUCTURE");
125 ensure(lhs != 0 && rhs != 0 && lhs != rhs);
126 }
127
128 template <typename T>
129 inline void
130 ensure_equals_geometry(T const* lhs_in, T const* rhs_in, double tolerance = 0.0)
131 {
132 assert(nullptr != lhs_in);
133 assert(nullptr != rhs_in);
134
135 using geos::geom::Polygon;
136 using geos::geom::GeometryCollection;
137
138 // Take clones so we can normalize them
139 std::unique_ptr<geos::geom::Geometry> lhs = lhs_in->clone();
140 std::unique_ptr<geos::geom::Geometry> rhs = rhs_in->clone();
141 lhs->normalize();
142 rhs->normalize();
143
144 ensure_equals("is-valid do not match",
145 lhs->isValid(), rhs->isValid());
146
147 ensure_equals("is-empty do not match",
148 lhs->isEmpty(), rhs->isEmpty());
149
150 if(!isInstanceOf<GeometryCollection>(lhs.get()) &&
151 !isInstanceOf<GeometryCollection>(rhs.get())) {
152 ensure_equals("is-simple do not match",
153 lhs->isSimple(), rhs->isSimple());
154 }
155
156 ensure_equals("type do not match",
157 lhs->getGeometryType(), rhs->getGeometryType());
158
159 ensure_equals("type id do not match",
160 lhs->getGeometryTypeId(), rhs->getGeometryTypeId());
161
162 ensure_equals("dimension do not match",
163 lhs->getDimension(), rhs->getDimension());
164
165 ensure_equals("boundary dimension do not match",
166 lhs->getBoundaryDimension(), rhs->getBoundaryDimension());
167
168 // NOTE - mloskot: Intentionally disabled, so simplified geometry
169 // can be compared to its original
170 ensure_equals("number of points do not match",
171 lhs->getNumPoints(), rhs->getNumPoints());
172
173 bool areEqual = lhs->equalsExact(rhs.get(), tolerance);
174 if(!areEqual) {
175 std::cout << std::endl << rhs->toText() << std::endl << lhs->toText() << std::endl;
176 }
177
178 ensure("coordinates do not match", areEqual);
179 // Dispatch to run more specific testes
180 // if(isInstanceOf<Polygon>(lhs)
181 // && isInstanceOf<Polygon>(rhs)) {
182 // ensure_equals_geometry(instanceOf<Polygon>(lhs),
183 // instanceOf<Polygon>(rhs));
184 // }
185 // else if(isInstanceOf<GeometryCollection>(lhs)
186 // && isInstanceOf<GeometryCollection>(rhs)) {
187 // ensure_equals_geometry(instanceOf<GeometryCollection>(lhs),
188 // instanceOf<GeometryCollection>(rhs));
189 // }
190 }
191
192
193
194 template <>
195 inline void
ensure_equals_geometry(geos::geom::Polygon const * lhs,geos::geom::Polygon const * rhs)196 ensure_equals_geometry(geos::geom::Polygon const* lhs,
197 geos::geom::Polygon const* rhs)
198 {
199 assert(nullptr != lhs);
200 assert(nullptr != rhs);
201
202 ensure_equals("number of interior ring do not match",
203 lhs->getNumInteriorRing(), rhs->getNumInteriorRing());
204 }
205
206 template <>
207 inline void
ensure_equals_geometry(geos::geom::GeometryCollection const * lhs,geos::geom::GeometryCollection const * rhs)208 ensure_equals_geometry(geos::geom::GeometryCollection const* lhs,
209 geos::geom::GeometryCollection const* rhs)
210 {
211 assert(nullptr != lhs);
212 assert(nullptr != rhs);
213
214 using geos::geom::Geometry;
215
216 ensure_equals("number of geometries do not match",
217 lhs->getNumGeometries(), rhs->getNumGeometries());
218
219 for(std::size_t i = 0, n = lhs->getNumGeometries(); i < n; ++i) {
220 Geometry const* g1 = lhs->getGeometryN(i);
221 Geometry const* g2 = rhs->getGeometryN(i);
222 ensure_equals_geometry(g1, g2); // breaks on failure
223 }
224 }
225
226 template <>
227 inline void
ensure_equals_geometry(geos::geom::Geometry const * lhs,geos::geom::prep::PreparedGeometry const * rhs)228 ensure_equals_geometry(geos::geom::Geometry const* lhs,
229 geos::geom::prep::PreparedGeometry const* rhs)
230 {
231 assert(nullptr != lhs);
232 assert(nullptr != rhs);
233
234 geos::geom::Geometry const& pg = rhs->getGeometry();
235 ensure_equals_geometry(lhs, &pg);
236 }
237
238 template <typename T>
239 inline void
240 ensure_equals_dims(T const *, T const *,
241 unsigned int dims,
242 double tolerance = 0.0);
243
244 template <>
245 inline void
ensure_equals_dims(const geos::geom::CoordinateSequence * seq1,const geos::geom::CoordinateSequence * seq2,unsigned int dims,double tolerance)246 ensure_equals_dims(const geos::geom::CoordinateSequence *seq1,
247 const geos::geom::CoordinateSequence *seq2,
248 unsigned int dims, double tolerance)
249 {
250 assert(nullptr != seq1);
251 assert(nullptr != seq2);
252
253 ensure_equals (seq1->size(), seq2->size());
254
255 ensure( seq1->getDimension() >= dims );
256 ensure( seq2->getDimension() >= dims );
257
258 for (unsigned int i = 0; i < seq1->size(); i++) {
259 for (unsigned int j = 0; j < dims; j++) {
260 double val1 = seq1->getOrdinate(i, j);
261 double val2 = seq2->getOrdinate(i, j);
262 if ( std::isnan(val1) )
263 {
264 ensure( std::isnan(val2) );
265 }
266 else
267 {
268 if ( tolerance > 0.0 )
269 ensure_distance( val1, val2, tolerance );
270 else
271 ensure_equals( val1, val2 );
272 }
273 }
274 }
275 }
276
277 template <typename T> inline void ensure_equals_exact_geometry_xyz(const T *lhs_in, const T *rhs_in, double tolerance = 0.0);
278
279 template <>
280 inline void
ensure_equals_exact_geometry_xyz(const geos::geom::Geometry * lhs_in,const geos::geom::Geometry * rhs_in,double tolerance)281 ensure_equals_exact_geometry_xyz(const geos::geom::Geometry *lhs_in,
282 const geos::geom::Geometry *rhs_in,
283 double tolerance)
284 {
285 assert(nullptr != lhs_in);
286 assert(nullptr != rhs_in);
287
288 using geos::geom::Point;
289 using geos::geom::LineString;
290 using geos::geom::Polygon;
291 using geos::geom::CoordinateSequence;
292 using geos::geom::GeometryCollection;
293
294 ensure_equals("type id do not match",
295 lhs_in->getGeometryTypeId(), rhs_in->getGeometryTypeId());
296
297
298 if (const Point* gpt1 = dynamic_cast<const Point *>(lhs_in)) {
299 const Point *gpt2 = static_cast<const Point *>(rhs_in);
300 return ensure_equals_dims( gpt1->getCoordinatesRO(), gpt2->getCoordinatesRO(), 3, tolerance);
301 }
302 else if (const LineString* gln1 = dynamic_cast<const LineString *>(lhs_in)) {
303 const LineString *gln2 = static_cast<const LineString *>(rhs_in);
304 return ensure_equals_dims( gln1->getCoordinatesRO(), gln2->getCoordinatesRO(), 3, tolerance);
305 }
306 else if (dynamic_cast<const Polygon *>(lhs_in)) {
307 assert("Not implemented yet" == 0);
308 }
309 else if (const GeometryCollection* gc1 = dynamic_cast<const GeometryCollection *>(lhs_in)) {
310 const GeometryCollection *gc2 = static_cast<const GeometryCollection *>(rhs_in);
311 for (unsigned int i = 0; i < gc1->getNumGeometries(); i++) {
312 ensure_equals_exact_geometry_xyz(gc1->getGeometryN(i), gc2->getGeometryN(i), tolerance);
313 }
314 }
315 }
316
317 template <typename T>
318 inline void
319 ensure_equals_geometry_xyz(const T *lhs_in,
320 const T *rhs_in,
321 double tolerance=0.0)
322 {
323 std::unique_ptr<geos::geom::Geometry> g1 = lhs_in->clone();
324 g1->normalize();
325 std::unique_ptr<geos::geom::Geometry> g2 = rhs_in->clone();
326 g2->normalize();
327 ensure_equals_exact_geometry_xyz(g1.get(), g2.get(), tolerance);
328 }
329
330 /*
331 * Checks for geometries exactly equal in XY only
332 */
333
334 template <typename T> inline void ensure_equals_exact_geometry(const T *lhs_in, const T *rhs_in, double tolerance = 0.0);
335
336 template <>
337 inline void
ensure_equals_exact_geometry(const geos::geom::Geometry * lhs_in,const geos::geom::Geometry * rhs_in,double tolerance)338 ensure_equals_exact_geometry(const geos::geom::Geometry *lhs_in,
339 const geos::geom::Geometry *rhs_in,
340 double tolerance)
341 {
342 assert(nullptr != lhs_in);
343 assert(nullptr != rhs_in);
344
345 using geos::geom::Point;
346 using geos::geom::LineString;
347 using geos::geom::Polygon;
348 using geos::geom::CoordinateSequence;
349 using geos::geom::GeometryCollection;
350
351 ensure_equals("type id do not match",
352 lhs_in->getGeometryTypeId(), rhs_in->getGeometryTypeId());
353
354 if (const Point* gpt1 = dynamic_cast<const Point *>(lhs_in)) {
355 const Point *gpt2 = static_cast<const Point *>(rhs_in);
356 return ensure_equals_dims( gpt1->getCoordinatesRO(), gpt2->getCoordinatesRO(), 2, tolerance);
357 }
358 else if (const LineString* gln1 = dynamic_cast<const LineString *>(lhs_in)) {
359 const LineString *gln2 = static_cast<const LineString *>(rhs_in);
360 return ensure_equals_dims( gln1->getCoordinatesRO(), gln2->getCoordinatesRO(), 2, tolerance);
361 }
362 else if (dynamic_cast<const Polygon *>(lhs_in)) {
363 assert("Not implemented yet" == 0);
364 }
365 else if (const GeometryCollection* gc1 = dynamic_cast<const GeometryCollection *>(lhs_in)) {
366 const GeometryCollection *gc2 = static_cast<const GeometryCollection *>(rhs_in);
367 for (unsigned int i = 0; i < gc1->getNumGeometries(); i++) {
368 ensure_equals_exact_geometry(gc1->getGeometryN(i), gc2->getGeometryN(i), tolerance);
369 }
370 }
371 }
372
373 //
374 // Utility functions
375 //
376
377 // Decodes hex-encoded WKB/EWKB to raw binary.
378 struct wkb_hex_decoder {
379 typedef std::vector<unsigned char> binary_type;
380
381 // bytes [out] - buffer for binary output
382 static void
decodewkb_hex_decoder383 decode(std::string const& hexstr, binary_type& bytes)
384 {
385 bytes.clear();
386 for(std::string::size_type i = 0; i < hexstr.size() / 2; ++i) {
387 std::istringstream iss(hexstr.substr(i * 2, 2));
388 unsigned int n;
389 iss >> std::hex >> n;
390 bytes.push_back(static_cast<unsigned char>(n));
391 }
392 }
393 };
394
395
396 } // namespace tut
397
398 #endif // #ifndef GEOS_TUT_UTILITY_H_INCLUDED
399