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