# 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