BinaryOp.h

00001 /**********************************************************************
00002  * $Id: BinaryOp.h 1948 2006-12-18 18:44:20Z strk $
00003  *
00004  * GEOS - Geometry Engine Open Source
00005  * http://geos.refractions.net
00006  *
00007  * Copyright (C) 2006 Refractions Research Inc.
00008  *
00009  * This is free software; you can redistribute and/or modify it under
00010  * the terms of the GNU Lesser General Public Licence as published
00011  * by the Free Software Foundation. 
00012  * See the COPYING file for more information.
00013  *
00014  **********************************************************************
00015  *
00016  * Last port: ORIGINAL WORK
00017  *
00018  **********************************************************************
00019  *
00020  * This file provides a single templated function, taking two
00021  * const Geometry pointers, applying a binary operator to them
00022  * and returning a result Geometry in an auto_ptr<>.
00023  *
00024  * The binary operator is expected to take two const Geometry pointers
00025  * and return a newly allocated Geometry pointer, possibly throwing
00026  * a TopologyException to signal it couldn't succeed due to robustness
00027  * issues.
00028  *
00029  * This function will catch TopologyExceptions and try again with
00030  * slightly modified versions of the input. The following heuristic
00031  * is used:
00032  *
00033  *      - Try with original input.
00034  *      - Try removing common bits from input coordinate values
00035  *      - Try snaping input geometries to each other
00036  *      - Try snaping input coordinates to a increasing grid (size from 1/25 to 1)
00037  *      - Try simplifiying input with increasing tolerance (from 0.01 to 0.04)
00038  *
00039  * If none of the step succeeds the original exception is thrown.
00040  *
00041  * Note that you can skip Grid snapping, Geometry snapping and Simplify policies
00042  * by a compile-time define when building geos.
00043  * See USE_TP_SIMPLIFY_POLICY, USE_PRECISION_REDUCTION_POLICY and
00044  * USE_SNAPPING_POLICY macros below.
00045  *
00046  *
00047  **********************************************************************/
00048 
00049 #include <geos/geom/Geometry.h>
00050 #include <geos/geom/PrecisionModel.h>
00051 #include <geos/util/TopologyException.h>
00052 #include <geos/precision/CommonBitsRemover.h>
00053 #include <geos/precision/SimpleGeometryPrecisionReducer.h>
00054 #include <geos/precision/GeometrySnapper.h>
00055 #include <geos/simplify/TopologyPreservingSimplifier.h>
00056 #include <geos/operation/valid/IsValidOp.h>
00057 
00058 #include <memory> // for auto_ptr
00059 
00060 //#define GEOS_DEBUG_BINARYOP 1
00061 
00062 
00063 /*
00064  * Always try original input first
00065  */
00066 #ifndef USE_ORIGINAL_INPUT
00067 # define USE_ORIGINAL_INPUT 1
00068 #endif
00069 
00070 /*
00071  * Define this to use PrecisionReduction policy
00072  * in an attempt at by-passing binary operation
00073  * robustness problems (handles TopologyExceptions)
00074  */
00075 #ifndef USE_PRECISION_REDUCTION_POLICY
00076 //# define USE_PRECISION_REDUCTION_POLICY 1
00077 #endif
00078 
00079 /*
00080  * Define this to use TopologyPreserving simplification policy
00081  * in an attempt at by-passing binary operation
00082  * robustness problems (handles TopologyExceptions)
00083  */
00084 #ifndef USE_TP_SIMPLIFY_POLICY 
00085 //# define USE_TP_SIMPLIFY_POLICY 1
00086 #endif
00087 
00088 /*
00089  * Use common bits removal policy.
00090  * If enabled, this would be tried /before/
00091  * Geometry snapping.
00092  */
00093 #ifndef USE_COMMONBITS_POLICY
00094 # define USE_COMMONBITS_POLICY 1
00095 #endif
00096 
00097 /*
00098  * Use snapping policy
00099  */
00100 #ifndef USE_SNAPPING_POLICY
00101 # define USE_SNAPPING_POLICY 1
00102 #endif
00103 
00104 namespace geos {
00105 namespace geom { // geos::geom
00106 
00107 bool
00108 check_valid(const Geometry& g, const std::string& label)
00109 {
00110         operation::valid::IsValidOp ivo(&g);
00111         if ( ! ivo.isValid() )
00112         {
00113                 std::cerr << label << ": is invalid!"
00114                         << ivo.getValidationError()->toString() << std::endl;
00115                 return false;
00116         } 
00117         return true;
00118 }
00119 
00125 template <class BinOp>
00126 std::auto_ptr<Geometry>
00127 SnapOp(const Geometry* g0, const Geometry *g1, BinOp _Op)
00128 {
00129         typedef std::auto_ptr<Geometry> GeomPtr;
00130 
00131 #define CBR_BEFORE_SNAPPING 1
00132 
00133         using geos::precision::GeometrySnapper;
00134 
00135         // Snap tolerance must be computed on the original
00136         // (not commonbits-removed) geoms
00137         double snapTolerance = GeometrySnapper::computeOverlaySnapTolerance(*g0, *g1);
00138 #if GEOS_DEBUG_BINARYOP
00139         std::cerr<<"Computed snap tolerance: "<<snapTolerance<<std::endl;
00140 #endif
00141 
00142         geos::precision::CommonBitsRemover cbr;
00143 
00144 #if CBR_BEFORE_SNAPPING
00145         // Now remove common bits
00146         GeomPtr rG0( cbr.removeCommonBits(g0->clone()) );
00147         GeomPtr rG1( cbr.removeCommonBits(g1->clone()) );
00148 
00149 #if GEOS_DEBUG_BINARYOP
00150         check_valid(*rG0, "CBR: removed-bits geom 0");
00151         check_valid(*rG1, "CBR: removed-bits geom 1");
00152 #endif
00153 
00154         const Geometry& operand0 = *rG0;
00155         const Geometry& operand1 = *rG1;
00156 #else // don't CBR before snapping
00157         const Geometry& operand0 = *g0
00158         const Geometry& operand1 = *g1
00159 #endif
00160 
00161         GeometrySnapper snapper0( operand0 );
00162         GeomPtr snapG0( snapper0.snapTo(operand1, snapTolerance) );
00163 
00164         // NOTE: second geom is snapped on the snapped first one
00165         GeometrySnapper snapper1( operand1 );
00166         GeomPtr snapG1( snapper1.snapTo(*snapG0, snapTolerance) );
00167 
00168 #if GEOS_DEBUG_BINARYOP
00169         check_valid(*snapG0, "SNAP: snapped geom 0");
00170         check_valid(*snapG1, "SNAP: snapped geom 1");
00171 #endif
00172 
00173         // Run the binary op
00174         GeomPtr result( _Op(snapG0.get(), snapG1.get()) );
00175 
00176 #if GEOS_DEBUG_BINARYOP
00177         check_valid(*result, "Op result (before common-bits addition");
00178 #endif
00179 
00180 #if CBR_BEFORE_SNAPPING
00181         // Add common bits back in
00182         cbr.addCommonBits( result.get() );
00183 #endif
00184 
00185 #if GEOS_DEBUG_BINARYOP
00186         check_valid(*result, "Op result (after common-bits addition");
00187 #endif
00188 
00189         return result;
00190 }
00191 
00192 template <class BinOp>
00193 std::auto_ptr<Geometry>
00194 BinaryOp(const Geometry* g0, const Geometry *g1, BinOp _Op)
00195 {
00196         typedef std::auto_ptr<Geometry> GeomPtr;
00197 
00198         GeomPtr ret;
00199         util::TopologyException origException;
00200 
00201 #ifdef USE_ORIGINAL_INPUT
00202         // Try with original input
00203         try
00204         {
00205 #if GEOS_DEBUG_BINARYOP
00206                 std::cerr << "Trying with original input." << std::endl;
00207 #endif
00208                 ret.reset(_Op(g0, g1));
00209                 return ret;
00210         }
00211         catch (const util::TopologyException& ex)
00212         {
00213                 origException=ex;
00214 #if GEOS_DEBUG_BINARYOP
00215                 std::cerr << "Original exception: " << ex.what() << std::endl;
00216 #endif
00217         }
00218 #endif // USE_ORIGINAL_INPUT
00219 
00220 
00221 #ifdef USE_COMMONBITS_POLICY
00222         // Try removing common bits (possibly obsoleted by snapping below)
00223         try
00224         {
00225                 GeomPtr rG0;
00226                 GeomPtr rG1;
00227                 precision::CommonBitsRemover cbr;
00228 
00229 #if GEOS_DEBUG_BINARYOP
00230                 std::cerr << "Trying with Common bits remover." << std::endl;
00231 #endif
00232 
00233                 cbr.add(g0);
00234                 cbr.add(g1);
00235 
00236                 rG0.reset( cbr.removeCommonBits(g0->clone()) );
00237                 rG1.reset( cbr.removeCommonBits(g1->clone()) );
00238 
00239 #if GEOS_DEBUG_BINARYOP
00240                 if ( ! rG0->isValid() )
00241                 {
00242                         std::cerr << " CBR: geom 0 is invalid!" << std::endl;
00243                 }
00244 
00245                 if ( ! rG1->isValid() )
00246                 {
00247                         std::cerr << " CBR: geom 1 is invalid!" << std::endl;
00248                 }
00249 #endif
00250 
00251                 ret.reset( _Op(rG0.get(), rG1.get()) );
00252 
00253                 cbr.addCommonBits( ret.get() );
00254 
00255                 return ret;
00256         }
00257         catch (const util::TopologyException& ex)
00258         {
00259 #if GEOS_DEBUG_BINARYOP
00260                 std::cerr << "CBR: " << ex.what() << std::endl;
00261 #endif
00262         }
00263 #endif
00264 
00265         // Try with snapping
00266         //
00267         // TODO: possible optimization would be reusing the
00268         //       already common-bit-removed inputs and just
00269         //       apply geometry snapping, whereas the current
00270         //       SnapOp function does both.
00271 // {
00272 #if USE_SNAPPING_POLICY
00273 
00274 #if GEOS_DEBUG_BINARYOP
00275         std::cerr << "Trying with snapping " << std::endl;
00276 #endif
00277 
00278         try {
00279                 ret = SnapOp(g0, g1, _Op);
00280 #if GEOS_DEBUG_BINARYOP
00281         std::cerr << "SnapOp succeeded" << std::endl;
00282 #endif
00283                 return ret;
00284                 
00285         }
00286         catch (const util::TopologyException& ex)
00287         {
00288 #if GEOS_DEBUG_BINARYOP
00289                 std::cerr << "SNAP: " << ex.what() << std::endl;
00290 #endif
00291         }
00292 
00293 #endif // USE_SNAPPING_POLICY }
00294 
00295 
00296 
00297 // {
00298 #if USE_PRECISION_REDUCTION_POLICY
00299 
00300 
00301         // Try reducing precision
00302         try
00303         {
00304                 int maxPrecision=25;
00305 
00306                 for (int precision=maxPrecision; precision; --precision)
00307                 {
00308                         std::auto_ptr<PrecisionModel> pm(new PrecisionModel(precision));
00309 #if GEOS_DEBUG_BINARYOP
00310                         std::cerr << "Trying with precision " << precision << std::endl;
00311 #endif
00312 
00313                         precision::SimpleGeometryPrecisionReducer reducer( pm.get() );
00314                         GeomPtr rG0( reducer.reduce(g0) );
00315                         GeomPtr rG1( reducer.reduce(g1) );
00316 
00317                         try
00318                         {
00319                                 ret.reset( _Op(rG0.get(), rG1.get()) );
00320                                 return ret;
00321                         }
00322                         catch (const util::TopologyException& ex)
00323                         {
00324                                 if ( precision == 1 ) throw ex;
00325 #if GEOS_DEBUG_BINARYOP
00326                                 std::cerr << "Reduced with precision (" << precision << "): "
00327                                           << ex.what() << std::endl;
00328 #endif
00329                         }
00330 
00331                 }
00332 
00333         }
00334         catch (const util::TopologyException& ex)
00335         {
00336 #if GEOS_DEBUG_BINARYOP
00337                 std::cerr << "Reduced: " << ex.what() << std::endl;
00338 #endif
00339         }
00340 
00341 #endif
00342 // USE_PRECISION_REDUCTION_POLICY }
00343 
00344 // {
00345 #if USE_TP_SIMPLIFY_POLICY 
00346 
00347         // Try simplifying
00348         try
00349         {
00350 
00351                 double maxTolerance = 0.04;
00352                 double minTolerance = 0.01;
00353                 double tolStep = 0.01;
00354 
00355                 for (double tol = minTolerance; tol <= maxTolerance; tol += tolStep)
00356                 {
00357 #if GEOS_DEBUG_BINARYOP
00358                         std::cerr << "Trying simplifying with tolerance " << tol << std::endl;
00359 #endif
00360 
00361                         GeomPtr rG0( simplify::TopologyPreservingSimplifier::simplify(g0, tol) );
00362                         GeomPtr rG1( simplify::TopologyPreservingSimplifier::simplify(g1, tol) );
00363 
00364                         try
00365                         {
00366                                 ret.reset( _Op(rG0.get(), rG1.get()) );
00367                                 return ret;
00368                         }
00369                         catch (const util::TopologyException& ex)
00370                         {
00371                                 if ( tol >= maxTolerance ) throw ex;
00372 #if GEOS_DEBUG_BINARYOP
00373                                 std::cerr << "Simplified with tolerance (" << tol << "): "
00374                                           << ex.what() << std::endl;
00375 #endif
00376                         }
00377 
00378                 }
00379 
00380                 return ret;
00381 
00382         }
00383         catch (const util::TopologyException& ex)
00384         {
00385 #if GEOS_DEBUG_BINARYOP
00386                 std::cerr << "Simplified: " << ex.what() << std::endl;
00387 #endif
00388         }
00389 
00390 #endif
00391 // USE_TP_SIMPLIFY_POLICY }
00392 
00393         throw origException;
00394 }
00395 
00396 
00397 } // namespace geos::geom
00398 } // namespace geos

Generated on Fri Mar 27 04:52:34 2009 for GEOS by  doxygen 1.5.4