/src/geos/src/operation/overlayng/OverlayNGRobust.cpp
Line | Count | Source |
1 | | /********************************************************************** |
2 | | * |
3 | | * GEOS - Geometry Engine Open Source |
4 | | * http://geos.osgeo.org |
5 | | * |
6 | | * Copyright (C) 2020 Paul Ramsey <pramsey@cleverelephant.ca> |
7 | | * |
8 | | * This is free software; you can redistribute and/or modify it under |
9 | | * the terms of the GNU Lesser General Public Licence as published |
10 | | * by the Free Software Foundation. |
11 | | * See the COPYING file for more information. |
12 | | * |
13 | | ********************************************************************** |
14 | | * |
15 | | * Last port: operation/overlayng/OverlayNGRobust.java 6ef89b09 |
16 | | * |
17 | | **********************************************************************/ |
18 | | |
19 | | #include <geos/operation/overlayng/OverlayNGRobust.h> |
20 | | |
21 | | #include <geos/operation/overlayng/OverlayNG.h> |
22 | | #include <geos/operation/overlayng/OverlayUtil.h> |
23 | | #include <geos/operation/overlayng/PrecisionUtil.h> |
24 | | #include <geos/operation/union/UnionStrategy.h> |
25 | | #include <geos/operation/union/UnaryUnionOp.h> |
26 | | #include <geos/noding/snap/SnappingNoder.h> |
27 | | #include <geos/geom/Geometry.h> |
28 | | #include <geos/geom/PrecisionModel.h> |
29 | | #include <geos/util/TopologyException.h> |
30 | | |
31 | | #include <stdexcept> |
32 | | |
33 | | #ifndef GEOS_DEBUG |
34 | | # define GEOS_DEBUG 0 |
35 | | #endif |
36 | | |
37 | | namespace geos { // geos |
38 | | namespace operation { // geos.operation |
39 | | namespace overlayng { // geos.operation.overlayng |
40 | | |
41 | | using namespace geos::geom; |
42 | | |
43 | | |
44 | | /*public static*/ |
45 | | std::unique_ptr<Geometry> |
46 | | OverlayNGRobust::Intersection(const Geometry* g0, const Geometry* g1) |
47 | 0 | { |
48 | 0 | return Overlay(g0, g1, OverlayNG::INTERSECTION); |
49 | 0 | } |
50 | | |
51 | | /*public static*/ |
52 | | std::unique_ptr<Geometry> |
53 | | OverlayNGRobust::Union(const Geometry* g0, const Geometry* g1) |
54 | 0 | { |
55 | 0 | return Overlay(g0, g1, OverlayNG::UNION); |
56 | 0 | } |
57 | | |
58 | | /*public static*/ |
59 | | std::unique_ptr<Geometry> |
60 | | OverlayNGRobust::Difference(const Geometry* g0, const Geometry* g1) |
61 | 0 | { |
62 | 0 | return Overlay(g0, g1, OverlayNG::DIFFERENCE); |
63 | 0 | } |
64 | | |
65 | | /*public static*/ |
66 | | std::unique_ptr<Geometry> |
67 | | OverlayNGRobust::SymDifference(const Geometry* g0, const Geometry* g1) |
68 | 0 | { |
69 | 0 | return Overlay(g0, g1, OverlayNG::SYMDIFFERENCE); |
70 | 0 | } |
71 | | |
72 | | /*public static*/ |
73 | | std::unique_ptr<Geometry> |
74 | | OverlayNGRobust::Union(const Geometry* a) |
75 | 55.6k | { |
76 | 55.6k | geounion::UnaryUnionOp op(*a); |
77 | 55.6k | SRUnionStrategy unionSRFun; |
78 | 55.6k | op.setUnionFunction(&unionSRFun); |
79 | 55.6k | geos::util::ProgressFunction* progressFunction = nullptr; |
80 | 55.6k | return op.Union(progressFunction); |
81 | 55.6k | } |
82 | | |
83 | | /*public static*/ |
84 | | std::unique_ptr<Geometry> |
85 | | OverlayNGRobust::Overlay(const Geometry* geom0, const Geometry* geom1, int opCode) |
86 | 158k | { |
87 | 158k | std::unique_ptr<Geometry> result; |
88 | 158k | std::runtime_error exOriginal(""); |
89 | | |
90 | | /** |
91 | | * If input geometry has a non-floating precision model, just run |
92 | | * in snap-rounding mode with that precision. |
93 | | */ |
94 | 158k | if (!geom0->getPrecisionModel()->isFloating()) { |
95 | | #if GEOS_DEBUG |
96 | | std::cerr << "Using fixed precision overlay." << std::endl; |
97 | | #endif |
98 | 0 | return OverlayNG::overlay(geom0, geom1, opCode, geom0->getPrecisionModel()); |
99 | 0 | } |
100 | | |
101 | | /** |
102 | | * First try overlay with a FLOAT noder, which is fastest and causes least |
103 | | * change to geometry coordinates |
104 | | * By default the noder is validated, which is required in order |
105 | | * to detect certain invalid noding situations which otherwise |
106 | | * cause incorrect overlay output. |
107 | | */ |
108 | 158k | try { |
109 | 158k | geom::PrecisionModel PM_FLOAT; |
110 | | #if GEOS_DEBUG |
111 | | std::cerr << "Using floating point overlay." << std::endl; |
112 | | #endif |
113 | 158k | result = OverlayNG::overlay(geom0, geom1, opCode, &PM_FLOAT); |
114 | | |
115 | | // Simple noding with no validation |
116 | | // There are cases where this succeeds with invalid noding (e.g. STMLF 1608). |
117 | | // So currently it is NOT safe to run overlay without noding validation |
118 | | //result = OverlayNG.overlay(geom0, geom1, opCode, createFloatingNoValidNoder()); |
119 | | // std::cerr << "Floating point overlay success." << std::endl; |
120 | 158k | return result; |
121 | 158k | } |
122 | 158k | catch (const std::runtime_error &ex) { |
123 | 35.2k | if (geom0->hasCurvedComponents() || geom1->hasCurvedComponents()) { |
124 | | // Snapping strategies do not support curved geometries. |
125 | 4.22k | throw; |
126 | 4.22k | } |
127 | | /** |
128 | | * Capture original exception, |
129 | | * so it can be rethrown if the remaining strategies all fail. |
130 | | */ |
131 | 31.0k | exOriginal = ex; |
132 | | #if GEOS_DEBUG |
133 | | std::cerr << "Floating point overlay FAILURE: " << ex.what() << std::endl; |
134 | | #endif |
135 | 31.0k | } |
136 | | |
137 | | |
138 | | /** |
139 | | * On failure retry using snapping noding with a "safe" tolerance. |
140 | | * if this throws an exception just let it go, |
141 | | * since it is something that is not a TopologyException |
142 | | */ |
143 | 31.0k | result = overlaySnapTries(geom0, geom1, opCode); |
144 | 31.0k | if (result != nullptr) |
145 | 10.6k | return result; |
146 | | |
147 | | /** |
148 | | * On failure retry using snap-rounding with a heuristic scale factor (grid size). |
149 | | */ |
150 | 20.3k | result = overlaySR(geom0, geom1, opCode); |
151 | 20.3k | if (result != nullptr) |
152 | 2.21k | return result; |
153 | | |
154 | | /** |
155 | | * Just can't get overlay to work, so throw original error. |
156 | | */ |
157 | 18.1k | throw exOriginal; |
158 | 20.3k | } |
159 | | |
160 | | |
161 | | /*private static*/ |
162 | | std::unique_ptr<Geometry> |
163 | | OverlayNGRobust::overlaySnapTries(const Geometry* geom0, const Geometry* geom1, int opCode) |
164 | 31.0k | { |
165 | 31.0k | std::unique_ptr<Geometry> result; |
166 | 31.0k | double snapTol = snapTolerance(geom0, geom1); |
167 | | |
168 | 99.5k | for (std::size_t i = 0; i < NUM_SNAP_TRIES; i++) { |
169 | | |
170 | | #if GEOS_DEBUG |
171 | | std::cerr << "Trying overlaySnapping(tol " << snapTol << ")." << std::endl; |
172 | | #endif |
173 | | |
174 | 79.2k | result = overlaySnapping(geom0, geom1, opCode, snapTol); |
175 | 79.2k | if (result != nullptr) return result; |
176 | | |
177 | | /** |
178 | | * Now try snapping each input individually, |
179 | | * and then doing the overlay. |
180 | | */ |
181 | 70.4k | result = overlaySnapBoth(geom0, geom1, opCode, snapTol); |
182 | 70.4k | if (result != nullptr) return result; |
183 | | |
184 | | // increase the snap tolerance and try again |
185 | 68.5k | snapTol = snapTol * 10; |
186 | 68.5k | } |
187 | | // failed to compute overlay |
188 | 20.3k | return nullptr; |
189 | 31.0k | } |
190 | | |
191 | | /*private static*/ |
192 | | std::unique_ptr<Geometry> |
193 | | OverlayNGRobust::overlaySnapping(const Geometry* geom0, const Geometry* geom1, int opCode, double snapTol) |
194 | 79.2k | { |
195 | 79.2k | try { |
196 | 79.2k | return overlaySnapTol(geom0, geom1, opCode, snapTol); |
197 | 79.2k | } |
198 | 79.2k | catch (const geos::util::TopologyException& ex) |
199 | 79.2k | { |
200 | 61.0k | ::geos::ignore_unused_variable_warning(ex); |
201 | | //---- ignore exception, return null result to indicate failure |
202 | | #if GEOS_DEBUG |
203 | | std::cerr << "overlaySnapping(tol " << snapTol << ") FAILURE: " << ex.what() << std::endl; |
204 | | #endif |
205 | 61.0k | } |
206 | 61.0k | return nullptr; |
207 | 79.2k | } |
208 | | |
209 | | /*private static*/ |
210 | | std::unique_ptr<Geometry> |
211 | | OverlayNGRobust::overlaySnapBoth(const Geometry* geom0, const Geometry* geom1, int opCode, double snapTol) |
212 | 61.0k | { |
213 | 61.0k | try { |
214 | 61.0k | std::unique_ptr<Geometry> snap0 = snapSelf(geom0, snapTol); |
215 | 61.0k | std::unique_ptr<Geometry> snap1 = snapSelf(geom1, snapTol); |
216 | 61.0k | return overlaySnapTol(snap0.get(), snap1.get(), opCode, snapTol); |
217 | 61.0k | } |
218 | 61.0k | catch (const geos::util::TopologyException& ex) |
219 | 61.0k | { |
220 | 58.8k | ::geos::ignore_unused_variable_warning(ex); |
221 | | //---- ignore this exception, just return a nullptr result to indicate failure |
222 | | #if GEOS_DEBUG |
223 | | std::cerr << "overlaySnapBoth(tol " << snapTol << ") FAILURE: " << ex.what() << std::endl; |
224 | | #endif |
225 | 58.8k | } |
226 | 58.8k | return nullptr; |
227 | 61.0k | } |
228 | | |
229 | | /*private static*/ |
230 | | std::unique_ptr<Geometry> |
231 | | OverlayNGRobust::snapSelf(const Geometry* geom, double snapTol) |
232 | 85.9k | { |
233 | 85.9k | OverlayNG ov(geom, nullptr); |
234 | 85.9k | noding::snap::SnappingNoder snapNoder(snapTol); |
235 | 85.9k | ov.setNoder(&snapNoder); |
236 | | /** |
237 | | * Ensure the result is not mixed-dimension, |
238 | | * since it will be used in further overlay computation. |
239 | | * It may however be lower dimension, if it collapses completely due to snapping. |
240 | | */ |
241 | 85.9k | ov.setStrictMode(true); |
242 | 85.9k | return ov.getResult(); |
243 | 85.9k | } |
244 | | |
245 | | /*private static*/ |
246 | | std::unique_ptr<Geometry> |
247 | | OverlayNGRobust::overlaySnapTol(const Geometry* geom0, const Geometry* geom1, int opCode, double snapTol) |
248 | 98.2k | { |
249 | 98.2k | noding::snap::SnappingNoder snapNoder(snapTol); |
250 | 98.2k | return OverlayNG::overlay(geom0, geom1, opCode, &snapNoder); |
251 | 98.2k | } |
252 | | |
253 | | /*public static*/ |
254 | | double |
255 | | OverlayNGRobust::snapTolerance(const Geometry* geom0, const Geometry* geom1) |
256 | 31.0k | { |
257 | 31.0k | double tol0 = snapTolerance(geom0); |
258 | 31.0k | double tol1 = snapTolerance(geom1); |
259 | 31.0k | double snapTol = std::max(tol0, tol1); |
260 | 31.0k | return snapTol; |
261 | 31.0k | } |
262 | | |
263 | | /*private static*/ |
264 | | double |
265 | | OverlayNGRobust::snapTolerance(const Geometry* geom) |
266 | 62.0k | { |
267 | 62.0k | double magnitude = ordinateMagnitude(geom); |
268 | 62.0k | return magnitude / SNAP_TOL_FACTOR; |
269 | 62.0k | } |
270 | | |
271 | | |
272 | | /*private static*/ |
273 | | double |
274 | | OverlayNGRobust::ordinateMagnitude(const Geometry* geom) |
275 | 62.0k | { |
276 | 62.0k | if (geom == nullptr || geom->isEmpty()) return 0; |
277 | 53.4k | const Envelope* env = geom->getEnvelopeInternal(); |
278 | 53.4k | double magMax = std::max( |
279 | 53.4k | std::abs(env->getMaxX()), |
280 | 53.4k | std::abs(env->getMaxY()) |
281 | 53.4k | ); |
282 | 53.4k | double magMin = std::max( |
283 | 53.4k | std::abs(env->getMinX()), |
284 | 53.4k | std::abs(env->getMinY()) |
285 | 53.4k | ); |
286 | 53.4k | return std::max(magMax, magMin); |
287 | 62.0k | } |
288 | | |
289 | | |
290 | | /*private static*/ |
291 | | std::unique_ptr<Geometry> |
292 | | OverlayNGRobust::overlaySR(const Geometry* geom0, const Geometry* geom1, int opCode) |
293 | 10.5k | { |
294 | 10.5k | std::unique_ptr<Geometry> result; |
295 | 10.5k | try { |
296 | 10.5k | double scaleSafe = PrecisionUtil::safeScale(geom0, geom1); |
297 | 10.5k | PrecisionModel pmSafe(scaleSafe); |
298 | 10.5k | result = OverlayNG::overlay(geom0, geom1, opCode, &pmSafe); |
299 | 10.5k | return result; |
300 | 10.5k | } |
301 | 10.5k | catch (const geos::util::TopologyException& ex) |
302 | 10.5k | { |
303 | 5.73k | ::geos::ignore_unused_variable_warning(ex); |
304 | | // ignore this exception, since the operation will be rerun |
305 | | #if GEOS_DEBUG |
306 | | std::cerr << "OverlayNGRobust::overlaySR FAILURE: " << ex.what() << std::endl; |
307 | | #endif |
308 | 5.73k | } |
309 | 5.73k | return nullptr; |
310 | 10.5k | } |
311 | | |
312 | | |
313 | | |
314 | | |
315 | | |
316 | | |
317 | | } // namespace geos.operation.overlayng |
318 | | } // namespace geos.operation |
319 | | } // namespace geos |