GEOS 3.3.4

- Main Page
- Related Pages
- Namespaces
- Classes
- Files

- File List

BinaryOp.h
1 /**********************************************************************
2 * $Id: BinaryOp.h 3606 2012-04-10 15:02:23Z strk $
3 *
4 * GEOS - Geometry Engine Open Source
5 * http://geos.refractions.net
6 *
7 * Copyright (C) 2006 Refractions Research Inc.
8 *
9 * This is free software; you can redistribute and/or modify it under
10 * the terms of the GNU Lesser General Public Licence as published
11 * by the Free Software Foundation.
12 * See the COPYING file for more information.
13 *
14 **********************************************************************
15 *
16 * Last port: ORIGINAL WORK
17 *
18 **********************************************************************
19 *
20 * This file provides a single templated function, taking two
21 * const Geometry pointers, applying a binary operator to them
22 * and returning a result Geometry in an auto_ptr<>.
23 *
24 * The binary operator is expected to take two const Geometry pointers
25 * and return a newly allocated Geometry pointer, possibly throwing
26 * a TopologyException to signal it couldn't succeed due to robustness
27 * issues.
28 *
29 * This function will catch TopologyExceptions and try again with
30 * slightly modified versions of the input. The following heuristic
31 * is used:
32 *
33 * - Try with original input.
34 * - Try removing common bits from input coordinate values
35 * - Try snaping input geometries to each other
36 * - Try snaping input coordinates to a increasing grid (size from 1/25 to 1)
37 * - Try simplifiying input with increasing tolerance (from 0.01 to 0.04)
38 *
39 * If none of the step succeeds the original exception is thrown.
40 *
41 * Note that you can skip Grid snapping, Geometry snapping and Simplify policies
42 * by a compile-time define when building geos.
43 * See USE_TP_SIMPLIFY_POLICY, USE_PRECISION_REDUCTION_POLICY and
44 * USE_SNAPPING_POLICY macros below.
45 *
46 *
47 **********************************************************************/
48
49 #ifndef GEOS_GEOM_BINARYOP_H
50 #define GEOS_GEOM_BINARYOP_H
51
52 #include <geos/geom/Geometry.h>
53 #include <geos/geom/PrecisionModel.h>
54 #include <geos/precision/CommonBitsRemover.h>
55 #include <geos/precision/SimpleGeometryPrecisionReducer.h>
56
57 #include <geos/operation/overlay/snap/GeometrySnapper.h>
58
59 #include <geos/simplify/TopologyPreservingSimplifier.h>
60 #include <geos/operation/valid/IsValidOp.h>
61 #include <geos/operation/valid/TopologyValidationError.h>
62 #include <geos/util/TopologyException.h>
63 #include <geos/util.h>
64
65 #include <memory> // for auto_ptr
66
67 //#define GEOS_DEBUG_BINARYOP 1
68
69 #ifdef GEOS_DEBUG_BINARYOP
70 # include <iostream>
71 # include <iomanip>
72 # include <sstream>
73 #endif
74
75
76 /*
77 * Always try original input first
78 */
79 #ifndef USE_ORIGINAL_INPUT
80 # define USE_ORIGINAL_INPUT 1
81 #endif
82
83 /*
84 * Define this to use PrecisionReduction policy
85 * in an attempt at by-passing binary operation
86 * robustness problems (handles TopologyExceptions)
87 */
88 #ifndef USE_PRECISION_REDUCTION_POLICY
89 //# define USE_PRECISION_REDUCTION_POLICY 1
90 #endif
91
92 /*
93 * Define this to use TopologyPreserving simplification policy
94 * in an attempt at by-passing binary operation
95 * robustness problems (handles TopologyExceptions)
96 */
97 #ifndef USE_TP_SIMPLIFY_POLICY
98 //# define USE_TP_SIMPLIFY_POLICY 1
99 #endif
100
101 /*
102 * Use common bits removal policy.
103 * If enabled, this would be tried /before/
104 * Geometry snapping.
105 */
106 #ifndef USE_COMMONBITS_POLICY
107 # define USE_COMMONBITS_POLICY 1
108 #endif
109
110 /*
111 * Check validity of operation performed
112 * by common bits removal policy.
113 *
114 * This matches what EnhancedPrecisionOp does in JTS
115 * and fixes 5 tests of invalid outputs in our testsuite
116 * (stmlf-cases-20061020-invalid-output.xml)
117 * and breaks 1 test (robustness-invalid-output.xml) so much
118 * to prevent a result.
119 *
120 */
121 #define GEOS_CHECK_COMMONBITS_VALIDITY 1
122
123 /*
124 * Use snapping policy
125 */
126 #ifndef USE_SNAPPING_POLICY
127 # define USE_SNAPPING_POLICY 1
128 #endif
129
130 namespace geos {
131 namespace geom { // geos::geom
132
133 inline bool
134 check_valid(const Geometry& g, const std::string& label)
135 {
136 operation::valid::IsValidOp ivo(&g);
137 if ( ! ivo.isValid() )
138 {
139 #ifdef GEOS_DEBUG_BINARYOP
140 using operation::valid::TopologyValidationError;
141 TopologyValidationError* err = ivo.getValidationError();
142 std::cerr << label << " is INVALID: "
143 << err->toString()
144 << " (" << std::setprecision(20)
145 << err->getCoordinate() << ")"
146 << std::endl;
147 #else
148 (void)label;
149 #endif
150 return false;
151 }
152 return true;
153 }
154
155 /* A single component may become a multi component */
156 inline std::auto_ptr<Geometry>
157 fix_self_intersections(std::auto_ptr<Geometry> g, const std::string& label)
158 {
159 // TODO: check for presence of self-intersections first ?
160 return g->Union();
161 }
162
163
169 template <class BinOp>
170 std::auto_ptr<Geometry>
171 SnapOp(const Geometry* g0, const Geometry *g1, BinOp _Op)
172 {
173 typedef std::auto_ptr<Geometry> GeomPtr;
174
175 #define CBR_BEFORE_SNAPPING 1
176
177 //using geos::precision::GeometrySnapper;
178 using geos::operation::overlay::snap::GeometrySnapper;
179
180 // Snap tolerance must be computed on the original
181 // (not commonbits-removed) geoms
182 double snapTolerance = GeometrySnapper::computeOverlaySnapTolerance(*g0, *g1);
183 #if GEOS_DEBUG_BINARYOP
184 std::cerr<< std::setprecision(20) << "Computed snap tolerance: "<<snapTolerance<<std::endl;
185 #endif
186
187
188 #if CBR_BEFORE_SNAPPING
189 // Compute common bits
190 geos::precision::CommonBitsRemover cbr;
191 cbr.add(g0); cbr.add(g1);
192 #if GEOS_DEBUG_BINARYOP
193 std::cerr<<"Computed common bits: "<<cbr.getCommonCoordinate()<<std::endl;
194 #endif
195
196 // Now remove common bits
197 GeomPtr rG0( cbr.removeCommonBits(g0->clone()) );
198 GeomPtr rG1( cbr.removeCommonBits(g1->clone()) );
199
200 #if GEOS_DEBUG_BINARYOP
201 check_valid(*rG0, "CBR: removed-bits geom 0");
202 check_valid(*rG1, "CBR: removed-bits geom 1");
203 #endif
204
205 const Geometry& operand0 = *rG0;
206 const Geometry& operand1 = *rG1;
207 #else // don't CBR before snapping
208 const Geometry& operand0 = *g0;
209 const Geometry& operand1 = *g1;
210 #endif
211
212
213 GeometrySnapper snapper0( operand0 );
214 GeomPtr snapG0( snapper0.snapTo(operand1, snapTolerance) );
215 snapG0 = fix_self_intersections(snapG0, "SNAP: snapped geom 0");
216
217 // NOTE: second geom is snapped on the snapped first one
218 GeometrySnapper snapper1( operand1 );
219 GeomPtr snapG1( snapper1.snapTo(*snapG0, snapTolerance) );
220 snapG1 = fix_self_intersections(snapG1, "SNAP: snapped geom 1");
221
222 // Run the binary op
223 GeomPtr result( _Op(snapG0.get(), snapG1.get()) );
224
225 #if GEOS_DEBUG_BINARYOP
226 check_valid(*result, "SNAP: result (before common-bits addition");
227 #endif
228
229 #if CBR_BEFORE_SNAPPING
230 // Add common bits back in
231 cbr.addCommonBits( result.get() );
232 result = fix_self_intersections(result, "SNAP: result (after common-bits addition)");
233 #endif
234
235 #if GEOS_DEBUG_BINARYOP
236 check_valid(*result, "SNAP: result (after common-bits addition");
237 #endif
238
239 return result;
240 }
241
242 template <class BinOp>
243 std::auto_ptr<Geometry>
244 BinaryOp(const Geometry* g0, const Geometry *g1, BinOp _Op)
245 {
246 typedef std::auto_ptr<Geometry> GeomPtr;
247
248 GeomPtr ret;
249 geos::util::TopologyException origException;
250
251 #ifdef USE_ORIGINAL_INPUT
252 // Try with original input
253 try
254 {
255 #if GEOS_DEBUG_BINARYOP
256 std::cerr << "Trying with original input." << std::endl;
257 #endif
258 ret.reset(_Op(g0, g1));
259 return ret;
260 }
261 catch (const geos::util::TopologyException& ex)
262 {
263 origException=ex;
264 #if GEOS_DEBUG_BINARYOP
265 std::cerr << "Original exception: " << ex.what() << std::endl;
266 #endif
267 }
268
269 //#if GEOS_DEBUG_BINARYOP
270 // Should we just give up here ?
271 check_valid(*g0, "Input geom 0");
272 check_valid(*g1, "Input geom 1");
273 //#endif
274
275 #endif // USE_ORIGINAL_INPUT
276
277
278 #ifdef USE_COMMONBITS_POLICY
279 // Try removing common bits (possibly obsoleted by snapping below)
280 //
281 // NOTE: this policy was _later_ implemented
282 // in JTS as EnhancedPrecisionOp
283 // TODO: consider using the now-ported EnhancedPrecisionOp
284 // here too
285 //
286 try
287 {
288 GeomPtr rG0;
289 GeomPtr rG1;
290 precision::CommonBitsRemover cbr;
291
292 #if GEOS_DEBUG_BINARYOP
293 std::cerr << "Trying with Common Bits Remover (CBR)" << std::endl;
294 #endif
295
296 cbr.add(g0);
297 cbr.add(g1);
298
299 rG0.reset( cbr.removeCommonBits(g0->clone()) );
300 rG1.reset( cbr.removeCommonBits(g1->clone()) );
301
302 #if GEOS_DEBUG_BINARYOP
303 check_valid(*rG0, "CBR: geom 0 (after common-bits removal)");
304 check_valid(*rG1, "CBR: geom 1 (after common-bits removal)");
305 #endif
306
307 ret.reset( _Op(rG0.get(), rG1.get()) );
308
309 #if GEOS_DEBUG_BINARYOP
310 check_valid(*ret, "CBR: result (before common-bits addition)");
311 #endif
312
313 cbr.addCommonBits( ret.get() );
314
315 #if GEOS_DEBUG_BINARYOP
316 check_valid(*ret, "CBR: result (after common-bits addition)");
317 #endif
318
319 // Common-bits removal policy could introduce self-intersections
320 ret = fix_self_intersections(ret, "CBR: result (after common-bits addition)");
321
322 #if GEOS_DEBUG_BINARYOP
323 check_valid(*ret, "CBR: result (after common-bits addition and fix_self_intersections)");
324 #endif
325
326 #if GEOS_CHECK_COMMONBITS_VALIDITY
327 // check that result is a valid geometry after the
328 // reshift to orginal precision (see EnhancedPrecisionOp)
329 using operation::valid::IsValidOp;
330 using operation::valid::TopologyValidationError;
331 IsValidOp ivo(ret.get());
332 if ( ! ivo.isValid() )
333 {
334 TopologyValidationError* e = ivo.getValidationError();
335 throw geos::util::TopologyException(
336 "Result of overlay became invalid "
337 "after re-addin common bits of operand "
338 "coordinates: " + e->toString(),
339 e->getCoordinate());
340 }
341 #endif // GEOS_CHECK_COMMONBITS_VALIDITY
342
343 return ret;
344 }
345 catch (const geos::util::TopologyException& ex)
346 {
347 ::geos::ignore_unused_variable_warning(ex);
348 #if GEOS_DEBUG_BINARYOP
349 std::cerr << "CBR: " << ex.what() << std::endl;
350 #endif
351 }
352 #endif
353
354 // Try with snapping
355 //
356 // TODO: possible optimization would be reusing the
357 // already common-bit-removed inputs and just
358 // apply geometry snapping, whereas the current
359 // SnapOp function does both.
360 // {
361 #if USE_SNAPPING_POLICY
362
363 #if GEOS_DEBUG_BINARYOP
364 std::cerr << "Trying with snapping " << std::endl;
365 #endif
366
367 try {
368 ret = SnapOp(g0, g1, _Op);
369 #if GEOS_DEBUG_BINARYOP
370 std::cerr << "SnapOp succeeded" << std::endl;
371 #endif
372 return ret;
373
374 }
375 catch (const geos::util::TopologyException& ex)
376 {
377 ::geos::ignore_unused_variable_warning(ex);
378 #if GEOS_DEBUG_BINARYOP
379 std::cerr << "SNAP: " << ex.what() << std::endl;
380 #endif
381 }
382
383 #endif // USE_SNAPPING_POLICY }
384
385
386
387 // {
388 #if USE_PRECISION_REDUCTION_POLICY
389
390
391 // Try reducing precision
392 try
393 {
394 int maxPrecision=25;
395
396 for (int precision=maxPrecision; precision; --precision)
397 {
398 std::auto_ptr<PrecisionModel> pm(new PrecisionModel(precision));
399 #if GEOS_DEBUG_BINARYOP
400 std::cerr << "Trying with precision " << precision << std::endl;
401 #endif
402
403 precision::SimpleGeometryPrecisionReducer reducer( pm.get() );
404 GeomPtr rG0( reducer.reduce(g0) );
405 GeomPtr rG1( reducer.reduce(g1) );
406
407 try
408 {
409 ret.reset( _Op(rG0.get(), rG1.get()) );
410 return ret;
411 }
412 catch (const geos::util::TopologyException& ex)
413 {
414 if ( precision == 1 ) throw ex;
415 #if GEOS_DEBUG_BINARYOP
416 std::cerr << "Reduced with precision (" << precision << "): "
417 << ex.what() << std::endl;
418 #endif
419 }
420
421 }
422
423 }
424 catch (const geos::util::TopologyException& ex)
425 {
426 #if GEOS_DEBUG_BINARYOP
427 std::cerr << "Reduced: " << ex.what() << std::endl;
428 #endif
429 }
430
431 #endif
432 // USE_PRECISION_REDUCTION_POLICY }
433
434 // {
435 #if USE_TP_SIMPLIFY_POLICY
436
437 // Try simplifying
438 try
439 {
440
441 double maxTolerance = 0.04;
442 double minTolerance = 0.01;
443 double tolStep = 0.01;
444
445 for (double tol = minTolerance; tol <= maxTolerance; tol += tolStep)
446 {
447 #if GEOS_DEBUG_BINARYOP
448 std::cerr << "Trying simplifying with tolerance " << tol << std::endl;
449 #endif
450
451 GeomPtr rG0( simplify::TopologyPreservingSimplifier::simplify(g0, tol) );
452 GeomPtr rG1( simplify::TopologyPreservingSimplifier::simplify(g1, tol) );
453
454 try
455 {
456 ret.reset( _Op(rG0.get(), rG1.get()) );
457 return ret;
458 }
459 catch (const geos::util::TopologyException& ex)
460 {
461 if ( tol >= maxTolerance ) throw ex;
462 #if GEOS_DEBUG_BINARYOP
463 std::cerr << "Simplified with tolerance (" << tol << "): "
464 << ex.what() << std::endl;
465 #endif
466 }
467
468 }
469
470 return ret;
471
472 }
473 catch (const geos::util::TopologyException& ex)
474 {
475 #if GEOS_DEBUG_BINARYOP
476 std::cerr << "Simplified: " << ex.what() << std::endl;
477 #endif
478 }
479
480 #endif
481 // USE_TP_SIMPLIFY_POLICY }
482
483 throw origException;
484 }
485
486
487 } // namespace geos::geom
488 } // namespace geos
489
490 #endif // GEOS_GEOM_BINARYOP_H