/src/geos/src/operation/overlayng/OverlayNG.cpp
Line | Count | Source |
1 | | /********************************************************************** |
2 | | * |
3 | | * GEOS - Geometry Engine Open Source |
4 | | * http://geos.osgeo.org |
5 | | * |
6 | | * Copyright (C) 2020 Sandro Santilli <strk@kbt.io> |
7 | | * Copyright (C) 2020 Paul Ramsey <pramsey@cleverelephant.ca> |
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: operation/overlayng/OverlayNG.java 4c88fea52 |
17 | | * |
18 | | **********************************************************************/ |
19 | | |
20 | | #include <geos/operation/overlayng/OverlayNG.h> |
21 | | |
22 | | #include <geos/operation/overlayng/Edge.h> |
23 | | #include <geos/operation/overlayng/EdgeNodingBuilder.h> |
24 | | #include <geos/operation/overlayng/ElevationModel.h> |
25 | | #include <geos/operation/overlayng/InputGeometry.h> |
26 | | #include <geos/operation/overlayng/IntersectionPointBuilder.h> |
27 | | #include <geos/operation/overlayng/LineBuilder.h> |
28 | | #include <geos/operation/overlayng/OverlayEdge.h> |
29 | | #include <geos/operation/overlayng/OverlayLabeller.h> |
30 | | #include <geos/operation/overlayng/OverlayMixedPoints.h> |
31 | | #include <geos/operation/overlayng/OverlayPoints.h> |
32 | | #include <geos/operation/overlayng/OverlayUtil.h> |
33 | | #include <geos/operation/overlayng/PolygonBuilder.h> |
34 | | #include <geos/geom/CoordinateSequence.h> |
35 | | #include <geos/geom/Envelope.h> |
36 | | #include <geos/geom/Location.h> |
37 | | #include <geos/geom/Geometry.h> |
38 | | #include <geos/util/Interrupt.h> |
39 | | #include <geos/util/TopologyException.h> |
40 | | |
41 | | #include <algorithm> |
42 | | |
43 | | #ifndef GEOS_DEBUG |
44 | | #define GEOS_DEBUG 0 |
45 | | #endif |
46 | | |
47 | | #if GEOS_DEBUG |
48 | | # include <iostream> |
49 | | # include <geos/io/WKTWriter.h> |
50 | | #endif |
51 | | |
52 | | |
53 | | |
54 | | namespace geos { // geos |
55 | | namespace operation { // geos.operation |
56 | | namespace overlayng { // geos.operation.overlayng |
57 | | |
58 | | using namespace geos::geom; |
59 | | |
60 | | |
61 | | /*public static*/ |
62 | | bool |
63 | | OverlayNG::isResultOfOpPoint(const OverlayLabel* label, int opCode) |
64 | 0 | { |
65 | 0 | Location loc0 = label->getLocation(0); |
66 | 0 | Location loc1 = label->getLocation(1); |
67 | 0 | return isResultOfOp(opCode, loc0, loc1); |
68 | 0 | } |
69 | | |
70 | | /*public static*/ |
71 | | bool |
72 | | OverlayNG::isResultOfOp(int overlayOpCode, Location loc0, Location loc1) |
73 | 6.05M | { |
74 | 6.05M | if (loc0 == Location::BOUNDARY) loc0 = Location::INTERIOR; |
75 | 6.05M | if (loc1 == Location::BOUNDARY) loc1 = Location::INTERIOR; |
76 | 6.05M | switch (overlayOpCode) { |
77 | 662k | case INTERSECTION: |
78 | 662k | return loc0 == Location::INTERIOR |
79 | 574k | && loc1 == Location::INTERIOR; |
80 | 3.97M | case UNION: |
81 | 3.97M | return loc0 == Location::INTERIOR |
82 | 1.15M | || loc1 == Location::INTERIOR; |
83 | 1.42M | case DIFFERENCE: |
84 | 1.42M | return loc0 == Location::INTERIOR |
85 | 1.34M | && loc1 != Location::INTERIOR; |
86 | 0 | case SYMDIFFERENCE: |
87 | 0 | return (loc0 == Location::INTERIOR && loc1 != Location::INTERIOR) |
88 | 0 | || (loc0 != Location::INTERIOR && loc1 == Location::INTERIOR); |
89 | 6.05M | } |
90 | 0 | return false; |
91 | 6.05M | } |
92 | | |
93 | | |
94 | | /*public static*/ |
95 | | std::unique_ptr<Geometry> |
96 | | OverlayNG::overlay(const Geometry* geom0, const Geometry* geom1, |
97 | | int opCode, const PrecisionModel* pm) |
98 | 244k | { |
99 | 244k | OverlayNG ov(geom0, geom1, pm, opCode); |
100 | 244k | return ov.getResult(); |
101 | 244k | } |
102 | | |
103 | | /*public static*/ |
104 | | std::unique_ptr<Geometry> |
105 | | OverlayNG::overlay(const Geometry* geom0, const Geometry* geom1, |
106 | | int opCode, const PrecisionModel* pm, noding::Noder* noder) |
107 | 0 | { |
108 | 0 | OverlayNG ov(geom0, geom1, pm, opCode); |
109 | 0 | ov.setNoder(noder); |
110 | 0 | return ov.getResult(); |
111 | 0 | } |
112 | | |
113 | | /*public static*/ |
114 | | std::unique_ptr<Geometry> |
115 | | OverlayNG::overlay(const Geometry* geom0, const Geometry* geom1, |
116 | | int opCode, noding::Noder* noder) |
117 | 105k | { |
118 | 105k | OverlayNG ov(geom0, geom1, static_cast<PrecisionModel*>(nullptr), opCode); |
119 | 105k | ov.setNoder(noder); |
120 | 105k | return ov.getResult(); |
121 | 105k | } |
122 | | |
123 | | /*public static*/ |
124 | | std::unique_ptr<Geometry> |
125 | | OverlayNG::overlay(const Geometry* geom0, const Geometry* geom1, int opCode) |
126 | 0 | { |
127 | 0 | OverlayNG ov(geom0, geom1, opCode); |
128 | 0 | return ov.getResult(); |
129 | 0 | } |
130 | | |
131 | | /*public static*/ |
132 | | std::unique_ptr<Geometry> |
133 | | OverlayNG::geomunion(const Geometry* geom, const PrecisionModel* pm) |
134 | 17.4k | { |
135 | 17.4k | OverlayNG ov(geom, pm); |
136 | 17.4k | return ov.getResult(); |
137 | 17.4k | } |
138 | | |
139 | | /*public static*/ |
140 | | std::unique_ptr<Geometry> |
141 | | OverlayNG::geomunion(const Geometry* geom, const PrecisionModel* pm, noding::Noder* noder) |
142 | 0 | { |
143 | 0 | OverlayNG ov(geom, pm); |
144 | 0 | ov.setNoder(noder); |
145 | 0 | ov.setStrictMode(true); |
146 | 0 | return ov.getResult(); |
147 | 0 | } |
148 | | |
149 | | /*public*/ |
150 | | std::unique_ptr<Geometry> |
151 | | OverlayNG::getResult() |
152 | 467k | { |
153 | 467k | const Geometry *ig0 = inputGeom.getGeometry(0); |
154 | 467k | const Geometry *ig1 = inputGeom.getGeometry(1); |
155 | | |
156 | 467k | if ( OverlayUtil::isEmptyResult(opCode, ig0, ig1, pm) ) |
157 | 74.7k | { |
158 | 74.7k | return createEmptyResult(); |
159 | 74.7k | } |
160 | | |
161 | | /** |
162 | | * The elevation model is only computed if the input geometries have Z values. |
163 | | */ |
164 | 392k | std::unique_ptr<ElevationModel> elevModel; |
165 | 392k | if ( ig1 ) { |
166 | 278k | elevModel = ElevationModel::create(*ig0, *ig1); |
167 | 278k | } else { |
168 | 114k | elevModel = ElevationModel::create(*ig0); |
169 | 114k | } |
170 | 392k | std::unique_ptr<Geometry> result; |
171 | | |
172 | 392k | if (inputGeom.isAllPoints()) { |
173 | | // handle Point-Point inputs |
174 | 1.01k | result = OverlayPoints::overlay(opCode, ig0, ig1, pm); |
175 | 1.01k | } |
176 | 391k | else if (! inputGeom.isSingle() && inputGeom.hasPoints()) { |
177 | | // handle Point-nonPoint inputs |
178 | 43.3k | result = OverlayMixedPoints::overlay(opCode, ig0, ig1, pm); |
179 | 43.3k | } |
180 | 348k | else { |
181 | | // handle case where both inputs are formed of edges (Lines and Polygons) |
182 | 348k | result = computeEdgeOverlay(); |
183 | 348k | } |
184 | | |
185 | | #if GEOS_DEBUG |
186 | | io::WKTWriter w; |
187 | | |
188 | | std::cerr << "Before populatingZ: " << w.write(result.get()) << std::endl; |
189 | | #endif |
190 | | |
191 | | /** |
192 | | * This is a no-op if the elevation model was not computed due to |
193 | | * Z not present |
194 | | */ |
195 | 392k | elevModel->populateZ(*result); |
196 | | |
197 | | #if GEOS_DEBUG |
198 | | std::cerr << " After populatingZ: " << w.write(result.get()) << std::endl; |
199 | | #endif |
200 | | |
201 | 392k | return result; |
202 | 467k | } |
203 | | |
204 | | |
205 | | /*private*/ |
206 | | std::unique_ptr<Geometry> |
207 | | OverlayNG::computeEdgeOverlay() |
208 | 348k | { |
209 | | /** |
210 | | * Node the edges, using whatever noder is being used |
211 | | * Formerly in nodeEdges()) |
212 | | */ |
213 | 348k | EdgeNodingBuilder nodingBuilder(pm, noder); |
214 | | // clipEnv not always used, but needs to remain in scope |
215 | | // as long as nodingBuilder when it is. |
216 | 348k | Envelope clipEnv; |
217 | | |
218 | 348k | GEOS_CHECK_FOR_INTERRUPTS(); |
219 | | |
220 | 348k | if (isOptimized) { |
221 | 348k | bool gotClipEnv = OverlayUtil::clippingEnvelope(opCode, &inputGeom, pm, clipEnv); |
222 | 348k | if (gotClipEnv) { |
223 | 32.1k | nodingBuilder.setClipEnvelope(&clipEnv); |
224 | 32.1k | } |
225 | 348k | } |
226 | | |
227 | 348k | std::vector<Edge*> edges = nodingBuilder.build( |
228 | 348k | inputGeom.getGeometry(0), |
229 | 348k | inputGeom.getGeometry(1)); |
230 | | |
231 | 348k | GEOS_CHECK_FOR_INTERRUPTS(); |
232 | | |
233 | | /** |
234 | | * Record if an input geometry has collapsed. |
235 | | * This is used to avoid trying to locate disconnected edges |
236 | | * against a geometry which has collapsed completely. |
237 | | */ |
238 | 348k | inputGeom.setCollapsed(0, ! nodingBuilder.hasEdgesFor(0)); |
239 | 348k | inputGeom.setCollapsed(1, ! nodingBuilder.hasEdgesFor(1)); |
240 | | |
241 | | /** |
242 | | * Inlined buildGraph() method here for memory purposes, so the |
243 | | * Edge* list allocated in the EdgeNodingBuilder survives |
244 | | * long enough to be copied into the OverlayGraph |
245 | | */ |
246 | | // Sort the edges first, for comparison with JTS results |
247 | | // std::sort(edges.begin(), edges.end(), EdgeComparator); |
248 | 348k | OverlayGraph graph; |
249 | 16.6M | for (Edge* e : edges) { |
250 | | // Write out edge coordinates |
251 | | // std::cout << *e->getCoordinatesRO() << std::endl; |
252 | 16.6M | graph.addEdge(e); |
253 | 16.6M | } |
254 | | |
255 | 348k | if (isOutputNodedEdges) { |
256 | 0 | return OverlayUtil::toLines(&graph, isOutputEdges, geomFact); |
257 | 0 | } |
258 | | |
259 | 348k | GEOS_CHECK_FOR_INTERRUPTS(); |
260 | 348k | labelGraph(&graph); |
261 | | |
262 | | // std::cout << std::endl << graph << std::endl; |
263 | | |
264 | 348k | if (isOutputEdges || isOutputResultEdges) { |
265 | 0 | return OverlayUtil::toLines(&graph, isOutputEdges, geomFact); |
266 | 0 | } |
267 | | |
268 | 348k | GEOS_CHECK_FOR_INTERRUPTS(); |
269 | 348k | std::unique_ptr<Geometry> result = extractResult(opCode, &graph); |
270 | | |
271 | | /** |
272 | | * Heuristic check on result area. |
273 | | * Catches cases where noding causes vertex to move |
274 | | * and make topology graph area "invert". |
275 | | */ |
276 | 348k | if (OverlayUtil::isFloating(pm)) { |
277 | 210k | bool isAreaConsistent = OverlayUtil::isResultAreaConsistent( |
278 | 210k | inputGeom.getGeometry(0), |
279 | 210k | inputGeom.getGeometry(1), |
280 | 210k | opCode, result.get()); |
281 | 210k | if (! isAreaConsistent) |
282 | 32.6k | throw util::TopologyException("Result area inconsistent with overlay operation"); |
283 | 210k | } |
284 | 315k | return result; |
285 | 348k | } |
286 | | |
287 | | /*private*/ |
288 | | void |
289 | | OverlayNG::labelGraph(OverlayGraph* graph) |
290 | 329k | { |
291 | 329k | OverlayLabeller labeller(graph, &inputGeom); |
292 | 329k | labeller.computeLabelling(); |
293 | 329k | labeller.markResultAreaEdges(opCode); |
294 | 329k | labeller.unmarkDuplicateEdgesFromResultArea(); |
295 | 329k | } |
296 | | |
297 | | |
298 | | /*private*/ |
299 | | std::unique_ptr<Geometry> |
300 | | OverlayNG::extractResult(int p_opCode, OverlayGraph* graph) |
301 | 236k | { |
302 | | |
303 | | #if GEOS_DEBUG |
304 | | std::cerr << "OverlayNG::extractResult: graph: " << *graph << std::endl; |
305 | | #endif |
306 | | |
307 | 236k | bool isAllowMixedIntResult = ! isStrictMode; |
308 | | |
309 | | //--- Build polygons |
310 | 236k | std::vector<OverlayEdge*> resultAreaEdges = graph->getResultAreaEdges(); |
311 | 236k | PolygonBuilder polyBuilder(resultAreaEdges, geomFact); |
312 | 236k | std::vector<std::unique_ptr<Polygon>> resultPolyList = polyBuilder.getPolygons(); |
313 | 236k | bool hasResultAreaComponents = (!resultPolyList.empty()); |
314 | | |
315 | 236k | std::vector<std::unique_ptr<LineString>> resultLineList; |
316 | 236k | std::vector<std::unique_ptr<Point>> resultPointList; |
317 | | |
318 | 236k | GEOS_CHECK_FOR_INTERRUPTS(); |
319 | 236k | if (!isAreaResultOnly) { |
320 | | //--- Build lines |
321 | 212k | bool allowResultLines = !hasResultAreaComponents || |
322 | 121k | isAllowMixedIntResult || |
323 | 21.5k | opCode == SYMDIFFERENCE || |
324 | 21.5k | opCode == UNION; |
325 | | |
326 | 212k | if (allowResultLines) { |
327 | 212k | LineBuilder lineBuilder(&inputGeom, graph, hasResultAreaComponents, p_opCode, geomFact); |
328 | 212k | lineBuilder.setStrictMode(isStrictMode); |
329 | 212k | resultLineList = lineBuilder.getLines(); |
330 | 212k | } |
331 | | /** |
332 | | * Operations with point inputs are handled elsewhere. |
333 | | * Only an intersection op can produce point results |
334 | | * from non-point inputs. |
335 | | */ |
336 | 212k | bool hasResultComponents = hasResultAreaComponents || (!resultLineList.empty()); |
337 | 212k | bool allowResultPoints = ! hasResultComponents || isAllowMixedIntResult; |
338 | 212k | if (opCode == INTERSECTION && allowResultPoints) { |
339 | 7.29k | IntersectionPointBuilder pointBuilder(graph, geomFact); |
340 | 7.29k | pointBuilder.setStrictMode(isStrictMode); |
341 | 7.29k | resultPointList = pointBuilder.getPoints(); |
342 | 7.29k | } |
343 | 212k | } |
344 | | |
345 | 236k | if (resultPolyList.empty() && |
346 | 91.0k | resultLineList.empty() && |
347 | 25.8k | resultPointList.empty()) |
348 | 25.0k | { |
349 | 25.0k | return createEmptyResult(); |
350 | 25.0k | } |
351 | | |
352 | 211k | std::unique_ptr<Geometry> resultGeom = OverlayUtil::createResultGeometry(resultPolyList, resultLineList, resultPointList, geomFact); |
353 | 211k | return resultGeom; |
354 | 236k | } |
355 | | |
356 | | /*private*/ |
357 | | std::unique_ptr<Geometry> |
358 | | OverlayNG::createEmptyResult() |
359 | 99.8k | { |
360 | 99.8k | uint8_t coordDim = OverlayUtil::resultCoordinateDimension( |
361 | 99.8k | inputGeom.getCoordinateDimension(0), |
362 | 99.8k | inputGeom.getCoordinateDimension(1)); |
363 | 99.8k | return OverlayUtil::createEmptyResult( |
364 | 99.8k | OverlayUtil::resultDimension(opCode, |
365 | 99.8k | inputGeom.getDimension(0), |
366 | 99.8k | inputGeom.getDimension(1)), |
367 | 99.8k | coordDim, |
368 | 99.8k | geomFact); |
369 | 99.8k | } |
370 | | |
371 | | |
372 | | |
373 | | |
374 | | } // namespace geos.operation.overlayng |
375 | | } // namespace geos.operation |
376 | | } // namespace geos |