/src/geos/src/operation/intersection/RectangleIntersection.cpp
Line | Count | Source |
1 | | /********************************************************************** |
2 | | * |
3 | | * GEOS - Geometry Engine Open Source |
4 | | * http://geos.osgeo.org |
5 | | * |
6 | | * Copyright (C) 2014 Mika Heiskanen <mika.heiskanen@fmi.fi> |
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 | | #include <geos/algorithm/PointLocation.h> |
16 | | #include <geos/algorithm/Orientation.h> |
17 | | #include <geos/operation/intersection/RectangleIntersection.h> |
18 | | #include <geos/operation/intersection/Rectangle.h> |
19 | | #include <geos/operation/intersection/RectangleIntersectionBuilder.h> |
20 | | #include <geos/operation/overlayng/ElevationModel.h> |
21 | | #include <geos/operation/predicate/RectangleIntersects.h> |
22 | | #include <geos/geom/GeometryFactory.h> |
23 | | #include <geos/geom/CoordinateSequence.h> |
24 | | #include <geos/geom/Polygon.h> |
25 | | #include <geos/geom/MultiPolygon.h> |
26 | | #include <geos/geom/Point.h> |
27 | | #include <geos/geom/Geometry.h> |
28 | | #include <geos/geom/LineString.h> |
29 | | #include <geos/geom/LinearRing.h> |
30 | | #include <geos/geom/Location.h> |
31 | | #include <geos/util/UnsupportedOperationException.h> |
32 | | #include <geos/util.h> |
33 | | #include <list> |
34 | | #include <stdexcept> |
35 | | |
36 | | using geos::operation::intersection::Rectangle; |
37 | | using geos::operation::intersection::RectangleIntersectionBuilder; |
38 | | using geos::operation::overlayng::ElevationModel; |
39 | | using namespace geos::geom; |
40 | | using namespace geos::algorithm; |
41 | | namespace geos { |
42 | | namespace operation { // geos::operation |
43 | | namespace intersection { // geos::operation::intersection |
44 | | |
45 | | /** |
46 | | * \brief Test if two coordinates are different |
47 | | */ |
48 | | |
49 | | inline |
50 | | bool |
51 | | different(double x1, double y1, double x2, double y2) |
52 | 0 | { |
53 | 0 | return !(x1 == x2 && y1 == y2); |
54 | 0 | } |
55 | | |
56 | | /** |
57 | | * \brief Calculate a line intersection point |
58 | | * |
59 | | * Note: |
60 | | * - Calling this with x1,y1 and x2,y2 swapped cuts the other end of the line |
61 | | * - Calling this with x and y swapped cuts in y-direction instead |
62 | | * - Calling with 1<->2 and x<->y swapped works too |
63 | | */ |
64 | | |
65 | | inline |
66 | | void |
67 | | clip_one_edge(double& x1, double& y1, double& z1, double x2, double y2, double z2, double limit) |
68 | 0 | { |
69 | 0 | if(x2 == limit) { |
70 | 0 | y1 = y2; |
71 | 0 | x1 = x2; |
72 | 0 | z1 = z2; |
73 | 0 | } |
74 | |
|
75 | 0 | if(x1 != x2) { |
76 | 0 | double fraction = (limit - x1) / (x2 - x1); |
77 | 0 | y1 += (y2 - y1) * fraction; |
78 | 0 | z1 += (z2 - z1) * fraction; |
79 | 0 | x1 = limit; |
80 | 0 | } |
81 | 0 | } |
82 | | |
83 | | /** |
84 | | * \brief Start point is outside, endpoint is definitely inside |
85 | | * |
86 | | * Note: Even though one might think using >= etc operators would produce |
87 | | * the same result, that is not the case. We rely on the fact |
88 | | * that nothing is clipped unless the point is truly outside the |
89 | | * rectangle! Without this handling lines ending on the edges of |
90 | | * the rectangle would be very difficult. |
91 | | */ |
92 | | |
93 | | void |
94 | | clip_to_edges(double& x1, double& y1, double& z1, |
95 | | double x2, double y2, double z2, |
96 | | const Rectangle& rect) |
97 | 0 | { |
98 | 0 | if(x1 < rect.xmin()) { |
99 | 0 | clip_one_edge(x1, y1, z1, x2, y2, z2, rect.xmin()); |
100 | 0 | } |
101 | 0 | else if(x1 > rect.xmax()) { |
102 | 0 | clip_one_edge(x1, y1, z1, x2, y2, z2, rect.xmax()); |
103 | 0 | } |
104 | |
|
105 | 0 | if(y1 < rect.ymin()) { |
106 | 0 | clip_one_edge(y1, x1, z1, y2, x2, z2, rect.ymin()); |
107 | 0 | } |
108 | 0 | else if(y1 > rect.ymax()) { |
109 | 0 | clip_one_edge(y1, x1, z1, y2, x2, z2, rect.ymax()); |
110 | 0 | } |
111 | 0 | } |
112 | | |
113 | | |
114 | | /** |
115 | | * \brief Clip geometry |
116 | | * |
117 | | * Here outGeom may also be a MultiPoint |
118 | | */ |
119 | | |
120 | | void |
121 | | RectangleIntersection::clip_point(const geom::Point* g, |
122 | | RectangleIntersectionBuilder& parts, |
123 | | const Rectangle& rect) |
124 | 0 | { |
125 | 0 | if(g == nullptr || g->isEmpty()) { |
126 | 0 | return; |
127 | 0 | } |
128 | | |
129 | 0 | double x = g->getX(); |
130 | 0 | double y = g->getY(); |
131 | |
|
132 | 0 | if(rect.position(x, y) == Rectangle::Inside) { |
133 | 0 | parts.add(g->clone().release()); |
134 | 0 | } |
135 | 0 | } |
136 | | |
137 | | bool |
138 | | RectangleIntersection::clip_linestring_parts(const geom::LineString* gi, |
139 | | RectangleIntersectionBuilder& parts, |
140 | | const Rectangle& rect) |
141 | 0 | { |
142 | 0 | auto n = gi->getNumPoints(); |
143 | |
|
144 | 0 | if(gi == nullptr || n < 1) { |
145 | 0 | return false; |
146 | 0 | } |
147 | | |
148 | | // For shorthand code |
149 | | |
150 | 0 | std::vector<Coordinate> cs; |
151 | 0 | gi->getCoordinatesRO()->toVector(cs); |
152 | | //const geom::CoordinateSequence &cs = *(gi->getCoordinatesRO()); |
153 | | |
154 | | // Keep a record of the point where a line segment entered |
155 | | // the rectangle. If the boolean is set, we must insert |
156 | | // the point to the beginning of the linestring which then |
157 | | // continues inside the rectangle. |
158 | |
|
159 | 0 | double x0 = 0; |
160 | 0 | double y0 = 0; |
161 | 0 | double z0 = 0; |
162 | 0 | bool add_start = false; |
163 | | |
164 | | // Start iterating |
165 | |
|
166 | 0 | std::size_t i = 0; |
167 | |
|
168 | 0 | while(i < n) { |
169 | | // Establish initial position |
170 | |
|
171 | 0 | double x = cs[i].x; |
172 | 0 | double y = cs[i].y; |
173 | 0 | double z = cs[i].z; |
174 | 0 | Rectangle::Position pos = rect.position(x, y); |
175 | |
|
176 | 0 | if(pos == Rectangle::Outside) { |
177 | | // Skip points as fast as possible until something has to be checked |
178 | | // in more detail. |
179 | |
|
180 | 0 | ++i; // we already know it is outside |
181 | |
|
182 | 0 | if(x < rect.xmin()) |
183 | 0 | while(i < n && cs[i].x < rect.xmin()) { |
184 | 0 | ++i; |
185 | 0 | } |
186 | | |
187 | 0 | else if(x > rect.xmax()) |
188 | 0 | while(i < n && cs[i].x > rect.xmax()) { |
189 | 0 | ++i; |
190 | 0 | } |
191 | | |
192 | 0 | else if(y < rect.ymin()) |
193 | 0 | while(i < n && cs[i].y < rect.ymin()) { |
194 | 0 | ++i; |
195 | 0 | } |
196 | | |
197 | 0 | else if(y > rect.ymax()) |
198 | 0 | while(i < n && cs[i].y > rect.ymax()) { |
199 | 0 | ++i; |
200 | 0 | } |
201 | |
|
202 | 0 | if(i >= n) { |
203 | 0 | return false; |
204 | 0 | } |
205 | | |
206 | | // Establish new position |
207 | 0 | x = cs[i].x; |
208 | 0 | y = cs[i].y; |
209 | 0 | z = cs[i].z; |
210 | 0 | pos = rect.position(x, y); |
211 | | |
212 | | // Handle all possible cases |
213 | 0 | x0 = cs[i - 1].x; |
214 | 0 | y0 = cs[i - 1].y; |
215 | 0 | z0 = cs[i - 1].z; |
216 | 0 | clip_to_edges(x0, y0, z0, x, y, z, rect); |
217 | |
|
218 | 0 | if(pos == Rectangle::Inside) { |
219 | 0 | add_start = true; // x0,y0 must have clipped the rectangle |
220 | | // Main loop will enter the Inside/Edge section |
221 | |
|
222 | 0 | } |
223 | 0 | else if(pos == Rectangle::Outside) { |
224 | | // From Outside to Outside. We need to check whether |
225 | | // we created a line segment inside the box. In any |
226 | | // case, we will continue the main loop after this |
227 | | // which will then enter the Outside section. |
228 | | |
229 | | // Clip the other end too |
230 | 0 | clip_to_edges(x, y, z, x0, y0, z0, rect); |
231 | |
|
232 | 0 | Rectangle::Position prev_pos = rect.position(x0, y0); |
233 | 0 | pos = rect.position(x, y); |
234 | |
|
235 | 0 | if(different(x0, y0, x, y) && // discard corners etc |
236 | 0 | Rectangle::onEdge(prev_pos) && // discard if does not intersect rect |
237 | 0 | Rectangle::onEdge(pos) && |
238 | 0 | !Rectangle::onSameEdge(prev_pos, pos) // discard if travels along edge |
239 | 0 | ) { |
240 | 0 | auto coords = detail::make_unique<geom::CoordinateSequence>(2u); |
241 | 0 | coords->setAt(Coordinate(x0, y0, z0), 0); |
242 | 0 | coords->setAt(Coordinate(x, y, z), 1); |
243 | 0 | auto line = _gf->createLineString(std::move(coords)); |
244 | 0 | parts.add(line.release()); |
245 | 0 | } |
246 | | |
247 | | // Continue main loop outside the rect |
248 | |
|
249 | 0 | } |
250 | 0 | else { |
251 | | // From outside to edge. If the edge we hit first when |
252 | | // following the line is not the edge we end at, then |
253 | | // clearly we must go through the rectangle and hence |
254 | | // a start point must be set. |
255 | |
|
256 | 0 | Rectangle::Position newpos = rect.position(x0, y0); |
257 | 0 | if(!Rectangle::onSameEdge(pos, newpos)) { |
258 | 0 | add_start = true; |
259 | 0 | } |
260 | 0 | else { |
261 | | // we ignore the travel along the edge and continue |
262 | | // the main loop at the last edge point |
263 | 0 | } |
264 | 0 | } |
265 | 0 | } |
266 | | |
267 | 0 | else { |
268 | | // The point is now strictly inside or on the edge. |
269 | | // Keep iterating until the end or the point goes |
270 | | // outside. We may have to output partial linestrings |
271 | | // while iterating until we go strictly outside |
272 | |
|
273 | 0 | auto start_index = i; // 1st valid original point |
274 | 0 | bool go_outside = false; |
275 | |
|
276 | 0 | while(!go_outside && ++i < n) { |
277 | 0 | x = cs[i].x; |
278 | 0 | y = cs[i].y; |
279 | 0 | z = cs[i].z; |
280 | |
|
281 | 0 | Rectangle::Position prev_pos = pos; |
282 | 0 | pos = rect.position(x, y); |
283 | |
|
284 | 0 | if(pos == Rectangle::Inside) { |
285 | | // Just keep going |
286 | 0 | } |
287 | 0 | else if(pos == Rectangle::Outside) { |
288 | 0 | go_outside = true; |
289 | | |
290 | | // Clip the outside point to edges |
291 | 0 | clip_to_edges(x, y, z, cs[i - 1].x, cs[i - 1].y, cs[i - 1].z, rect); |
292 | 0 | pos = rect.position(x, y); |
293 | | |
294 | | // Does the line exit through the inside of the box? |
295 | |
|
296 | 0 | bool through_box = (different(x, y, cs[i].x, cs[i].y) && |
297 | 0 | !Rectangle::onSameEdge(prev_pos, pos)); |
298 | | |
299 | | // Output a LineString if it at least one segment long |
300 | |
|
301 | 0 | if(start_index < i - 1 || add_start || through_box) { |
302 | 0 | auto coords = detail::make_unique<CoordinateSequence>(); |
303 | 0 | if(add_start) { |
304 | 0 | coords->add(Coordinate(x0, y0, z0)); |
305 | 0 | add_start = false; |
306 | 0 | } |
307 | | //line->addSubLineString(&g, start_index, i-1); |
308 | 0 | coords->add(cs.begin() + static_cast<long>(start_index), |
309 | 0 | cs.begin() + static_cast<long>(i)); |
310 | |
|
311 | 0 | if(through_box) { |
312 | 0 | coords->add(Coordinate(x, y, z)); |
313 | 0 | } |
314 | |
|
315 | 0 | auto line = _gf->createLineString(std::move(coords)); |
316 | 0 | parts.add(line.release()); |
317 | 0 | } |
318 | | // And continue main loop on the outside |
319 | 0 | } |
320 | 0 | else { |
321 | | // on same edge? |
322 | 0 | if(Rectangle::onSameEdge(prev_pos, pos)) { |
323 | | // Nothing to output if we haven't been elsewhere |
324 | 0 | if(start_index < i - 1 || add_start) { |
325 | 0 | auto coords = detail::make_unique<CoordinateSequence>(); |
326 | | //geom::LineString * line = new geom::LineString(); |
327 | 0 | if(add_start) { |
328 | | //line->addPoint(x0,y0); |
329 | 0 | coords->add(Coordinate(x0, y0, z0)); |
330 | 0 | add_start = false; |
331 | 0 | } |
332 | | //line->addSubLineString(&g, start_index, i-1); |
333 | 0 | coords->add(cs.begin() + static_cast<long>(start_index), |
334 | 0 | cs.begin() + static_cast<long>(i)); |
335 | |
|
336 | 0 | auto line = _gf->createLineString(std::move(coords)); |
337 | 0 | parts.add(line.release()); |
338 | 0 | } |
339 | 0 | start_index = i; |
340 | 0 | } |
341 | 0 | else { |
342 | | // On different edge. Must have gone through the box |
343 | | // then - keep collecting points that generate inside |
344 | | // line segments |
345 | 0 | } |
346 | 0 | } |
347 | 0 | } |
348 | | |
349 | | // Was everything in? If so, generate no output but return true in this case only |
350 | 0 | if(start_index == 0 && i >= n) { |
351 | 0 | return true; |
352 | 0 | } |
353 | | |
354 | | // Flush the last line segment if data ended and there is something to flush |
355 | | |
356 | 0 | if(!go_outside && // meaning data ended |
357 | 0 | (start_index < i - 1 || add_start)) { // meaning something has to be generated |
358 | 0 | auto coords = detail::make_unique<CoordinateSequence>(); |
359 | | //geom::LineString * line = new geom::LineString(); |
360 | 0 | if(add_start) { |
361 | | //line->addPoint(x0,y0); |
362 | 0 | coords->add(Coordinate(x0, y0, z0)); |
363 | 0 | add_start = false; |
364 | 0 | } |
365 | | //line->addSubLineString(&g, start_index, i-1); |
366 | 0 | coords->add(cs.begin() + static_cast<long>(start_index), |
367 | 0 | cs.begin() + static_cast<long>(i)); |
368 | |
|
369 | 0 | auto line = _gf->createLineString(std::move(coords)); |
370 | 0 | parts.add(line.release()); |
371 | 0 | } |
372 | |
|
373 | 0 | } |
374 | 0 | } |
375 | | |
376 | 0 | return false; |
377 | |
|
378 | 0 | } |
379 | | |
380 | | /** |
381 | | * \brief Clip polygon, do not close clipped ones |
382 | | */ |
383 | | |
384 | | void |
385 | | RectangleIntersection::clip_polygon_to_linestrings(const geom::Polygon* g, |
386 | | RectangleIntersectionBuilder& toParts, |
387 | | const Rectangle& rect) |
388 | 0 | { |
389 | 0 | if(g == nullptr || g->isEmpty()) { |
390 | 0 | return; |
391 | 0 | } |
392 | | |
393 | | // Clip the exterior first to see what's going on |
394 | | |
395 | 0 | RectangleIntersectionBuilder parts(*_gf); |
396 | | |
397 | | // If everything was in, just clone the original |
398 | |
|
399 | 0 | if(clip_linestring_parts(g->getExteriorRing(), parts, rect)) { |
400 | 0 | toParts.add(g->clone().release()); |
401 | 0 | return; |
402 | 0 | } |
403 | | |
404 | | // Now, if parts is empty, our rectangle may be inside the polygon |
405 | | // If not, holes are outside too |
406 | | |
407 | 0 | if(parts.empty()) { |
408 | | // We could now check whether the rectangle is inside the outer |
409 | | // ring to avoid checking the holes. However, if holes are much |
410 | | // smaller than the exterior ring just checking the holes |
411 | | // separately could be faster. |
412 | |
|
413 | 0 | if(g->getNumInteriorRing() == 0) { |
414 | 0 | return; |
415 | 0 | } |
416 | |
|
417 | 0 | } |
418 | 0 | else { |
419 | | // The exterior must have been clipped into linestrings. |
420 | | // Move them to the actual parts collector, clearing parts. |
421 | |
|
422 | 0 | parts.reconnect(); |
423 | 0 | parts.release(toParts); |
424 | 0 | } |
425 | | |
426 | | // Handle the holes now: |
427 | | // - Clipped ones become linestrings |
428 | | // - Intact ones become new polygons without holes |
429 | | |
430 | 0 | for(std::size_t i = 0, n = g->getNumInteriorRing(); i < n; ++i) { |
431 | 0 | if(clip_linestring_parts(g->getInteriorRingN(i), parts, rect)) { |
432 | | // clones |
433 | 0 | auto hole =g->getInteriorRingN(i)->clone(); |
434 | | // becomes exterior |
435 | 0 | Polygon* poly = _gf->createPolygon(std::move(hole)).release(); |
436 | 0 | toParts.add(poly); |
437 | 0 | } |
438 | 0 | else if(!parts.empty()) { |
439 | 0 | parts.reconnect(); |
440 | 0 | parts.release(toParts); |
441 | 0 | } |
442 | 0 | } |
443 | 0 | } |
444 | | |
445 | | /** |
446 | | * \brief Clip polygon, close clipped ones |
447 | | */ |
448 | | |
449 | | void |
450 | | RectangleIntersection::clip_polygon_to_polygons(const geom::Polygon* g, |
451 | | RectangleIntersectionBuilder& toParts, |
452 | | const Rectangle& rect) |
453 | 0 | { |
454 | 0 | if(g == nullptr || g->isEmpty()) { |
455 | 0 | return; |
456 | 0 | } |
457 | | |
458 | | // Clip the exterior first to see what's going on |
459 | | |
460 | 0 | RectangleIntersectionBuilder parts(*_gf); |
461 | | |
462 | | // If everything was in, just clone the original |
463 | |
|
464 | 0 | const LineString* shell = g->getExteriorRing(); |
465 | 0 | if(clip_linestring_parts(shell, parts, rect)) { |
466 | 0 | toParts.add(g->clone().release()); |
467 | 0 | return; |
468 | 0 | } |
469 | | |
470 | | // If there were no intersections, the outer ring might be |
471 | | // completely outside. |
472 | | |
473 | 0 | using geos::algorithm::Orientation; |
474 | 0 | if(parts.empty()) { |
475 | 0 | Coordinate rectCenter(rect.xmin(), rect.ymin()); |
476 | 0 | rectCenter.x += (rect.xmax() - rect.xmin()) / 2; |
477 | 0 | rectCenter.y += (rect.ymax() - rect.ymin()) / 2; |
478 | 0 | if(PointLocation::locateInRing(rectCenter, |
479 | 0 | *g->getExteriorRing()->getCoordinatesRO()) |
480 | 0 | != Location::INTERIOR) { |
481 | 0 | return; |
482 | 0 | } |
483 | 0 | } |
484 | 0 | else { |
485 | | // TODO: make CCW checking part of clip_linestring_parts ? |
486 | 0 | if(Orientation::isCCW(shell->getCoordinatesRO())) { |
487 | 0 | parts.reverseLines(); |
488 | 0 | } |
489 | 0 | } |
490 | | |
491 | | // Must do this to make sure all end points are on the edges |
492 | | |
493 | 0 | parts.reconnect(); |
494 | | |
495 | | // Handle the holes now: |
496 | | // - Clipped ones become part of the exterior |
497 | | // - Intact ones become holes in new polygons formed by exterior parts |
498 | | |
499 | |
|
500 | 0 | for(std::size_t i = 0, n = g->getNumInteriorRing(); i < n; ++i) { |
501 | 0 | RectangleIntersectionBuilder holeparts(*_gf); |
502 | 0 | const LinearRing* hole = g->getInteriorRingN(i); |
503 | 0 | if(clip_linestring_parts(hole, holeparts, rect)) { |
504 | | // becomes exterior |
505 | 0 | auto cloned = hole->clone(); |
506 | 0 | Polygon* poly = _gf->createPolygon(std::move(cloned)).release(); |
507 | 0 | parts.add(poly); |
508 | 0 | } |
509 | 0 | else { |
510 | 0 | if(!holeparts.empty()) { |
511 | | // TODO: make CCW checking part of clip_linestring_parts ? |
512 | 0 | if(! Orientation::isCCW(hole->getCoordinatesRO())) { |
513 | 0 | holeparts.reverseLines(); |
514 | 0 | } |
515 | 0 | holeparts.reconnect(); |
516 | 0 | holeparts.release(parts); |
517 | 0 | } |
518 | 0 | else { |
519 | |
|
520 | 0 | Coordinate rectCenter(rect.xmin(), rect.ymin()); |
521 | 0 | rectCenter.x += (rect.xmax() - rect.xmin()) / 2; |
522 | 0 | rectCenter.y += (rect.ymax() - rect.ymin()) / 2; |
523 | 0 | if(PointLocation::isInRing(rectCenter, |
524 | 0 | g->getInteriorRingN(i)->getCoordinatesRO())) { |
525 | | // Completely inside the hole |
526 | 0 | return; |
527 | 0 | } |
528 | 0 | } |
529 | 0 | } |
530 | 0 | } |
531 | | |
532 | 0 | parts.reconnectPolygons(rect); |
533 | 0 | parts.release(toParts); |
534 | 0 | } |
535 | | |
536 | | /** |
537 | | * \brief Clip geometry |
538 | | */ |
539 | | |
540 | | void |
541 | | RectangleIntersection::clip_polygon(const geom::Polygon* g, |
542 | | RectangleIntersectionBuilder& parts, |
543 | | const Rectangle& rect, |
544 | | bool keep_polygons) |
545 | 0 | { |
546 | 0 | if(g == nullptr || g->isEmpty()) { |
547 | 0 | return; |
548 | 0 | } |
549 | | |
550 | 0 | if(keep_polygons) { |
551 | 0 | clip_polygon_to_polygons(g, parts, rect); |
552 | 0 | } |
553 | 0 | else { |
554 | 0 | clip_polygon_to_linestrings(g, parts, rect); |
555 | 0 | } |
556 | 0 | } |
557 | | |
558 | | /** |
559 | | * \brief Clip geometry |
560 | | */ |
561 | | |
562 | | void |
563 | | RectangleIntersection::clip_linestring(const geom::LineString* g, |
564 | | RectangleIntersectionBuilder& parts, |
565 | | const Rectangle& rect) |
566 | 0 | { |
567 | 0 | if(g == nullptr || g->isEmpty()) { |
568 | 0 | return; |
569 | 0 | } |
570 | | |
571 | | // If everything was in, just clone the original |
572 | | |
573 | 0 | if(clip_linestring_parts(g, parts, rect)) { |
574 | 0 | parts.add(g->clone().release()); |
575 | 0 | } |
576 | |
|
577 | 0 | } |
578 | | |
579 | | void |
580 | | RectangleIntersection::clip_multipoint(const geom::MultiPoint* g, |
581 | | RectangleIntersectionBuilder& parts, |
582 | | const Rectangle& rect) |
583 | 0 | { |
584 | 0 | if(g == nullptr || g->isEmpty()) { |
585 | 0 | return; |
586 | 0 | } |
587 | 0 | for(std::size_t i = 0, n = g->getNumGeometries(); i < n; ++i) { |
588 | 0 | clip_point(g->getGeometryN(i), parts, rect); |
589 | 0 | } |
590 | 0 | } |
591 | | |
592 | | void |
593 | | RectangleIntersection::clip_multilinestring(const geom::MultiLineString* g, |
594 | | RectangleIntersectionBuilder& parts, |
595 | | const Rectangle& rect) |
596 | 0 | { |
597 | 0 | if(g == nullptr || g->isEmpty()) { |
598 | 0 | return; |
599 | 0 | } |
600 | | |
601 | 0 | for(std::size_t i = 0, n = g->getNumGeometries(); i < n; ++i) { |
602 | 0 | clip_linestring(g->getGeometryN(i), parts, rect); |
603 | 0 | } |
604 | 0 | } |
605 | | |
606 | | void |
607 | | RectangleIntersection::clip_multipolygon(const geom::MultiPolygon* g, |
608 | | RectangleIntersectionBuilder& parts, |
609 | | const Rectangle& rect, |
610 | | bool keep_polygons) |
611 | 0 | { |
612 | 0 | if(g == nullptr || g->isEmpty()) { |
613 | 0 | return; |
614 | 0 | } |
615 | | |
616 | 0 | for(std::size_t i = 0, n = g->getNumGeometries(); i < n; ++i) { |
617 | 0 | clip_polygon(g->getGeometryN(i), parts, rect, keep_polygons); |
618 | 0 | } |
619 | 0 | } |
620 | | |
621 | | void |
622 | | RectangleIntersection::clip_geometrycollection( |
623 | | const geom::GeometryCollection* g, |
624 | | RectangleIntersectionBuilder& parts, |
625 | | const Rectangle& rect, |
626 | | bool keep_polygons) |
627 | 0 | { |
628 | 0 | if(g == nullptr || g->isEmpty()) { |
629 | 0 | return; |
630 | 0 | } |
631 | | |
632 | 0 | for(std::size_t i = 0, n = g->getNumGeometries(); i < n; ++i) { |
633 | 0 | clip_geom(g->getGeometryN(i), |
634 | 0 | parts, rect, keep_polygons); |
635 | 0 | } |
636 | 0 | } |
637 | | |
638 | | void |
639 | | RectangleIntersection::clip_geom(const geom::Geometry* g, |
640 | | RectangleIntersectionBuilder& parts, |
641 | | const Rectangle& rect, |
642 | | bool keep_polygons) |
643 | 0 | { |
644 | 0 | if(const Point* p1 = dynamic_cast<const geom::Point*>(g)) { |
645 | 0 | return clip_point(p1, parts, rect); |
646 | 0 | } |
647 | 0 | else if(const MultiPoint* p2 = dynamic_cast<const geom::MultiPoint*>(g)) { |
648 | 0 | return clip_multipoint(p2, parts, rect); |
649 | 0 | } |
650 | 0 | else if(const LineString* p3 = dynamic_cast<const geom::LineString*>(g)) { |
651 | 0 | return clip_linestring(p3, parts, rect); |
652 | 0 | } |
653 | 0 | else if(const MultiLineString* p4 = dynamic_cast<const geom::MultiLineString*>(g)) { |
654 | 0 | return clip_multilinestring(p4, parts, rect); |
655 | 0 | } |
656 | 0 | else if(const Polygon* p5 = dynamic_cast<const geom::Polygon*>(g)) { |
657 | 0 | return clip_polygon(p5, parts, rect, keep_polygons); |
658 | 0 | } |
659 | 0 | else if(const MultiPolygon* p6 = dynamic_cast<const geom::MultiPolygon*>(g)) { |
660 | 0 | return clip_multipolygon(p6, parts, rect, keep_polygons); |
661 | 0 | } |
662 | 0 | else if(const GeometryCollection* p7 = dynamic_cast<const geom::GeometryCollection*>(g)) { |
663 | 0 | return clip_geometrycollection(p7, parts, rect, keep_polygons); |
664 | 0 | } |
665 | 0 | else { |
666 | 0 | throw util::UnsupportedOperationException("Encountered an unknown geometry component when clipping polygons"); |
667 | 0 | } |
668 | 0 | } |
669 | | |
670 | | /* public static */ |
671 | | std::unique_ptr<geom::Geometry> |
672 | | RectangleIntersection::clipBoundary(const geom::Geometry& g, const Rectangle& rect) |
673 | 0 | { |
674 | 0 | RectangleIntersection ri(g, rect); |
675 | 0 | return ri.clipBoundary(); |
676 | 0 | } |
677 | | |
678 | | std::unique_ptr<geom::Geometry> |
679 | | RectangleIntersection::clipBoundary() |
680 | 0 | { |
681 | 0 | RectangleIntersectionBuilder parts(*_gf); |
682 | |
|
683 | 0 | bool keep_polygons = false; |
684 | 0 | clip_geom(&_geom, parts, _rect, keep_polygons); |
685 | |
|
686 | 0 | return parts.build(); |
687 | 0 | } |
688 | | |
689 | | /* public static */ |
690 | | std::unique_ptr<geom::Geometry> |
691 | | RectangleIntersection::clip(const geom::Geometry& g, const Rectangle& rect) |
692 | 0 | { |
693 | 0 | RectangleIntersection ri(g, rect); |
694 | 0 | std::unique_ptr<geom::Geometry> result = ri.clip(); |
695 | |
|
696 | 0 | if (g.hasZ()) { |
697 | 0 | std::unique_ptr<ElevationModel> elevModel = ElevationModel::create(g); |
698 | 0 | elevModel->populateZ(*result); |
699 | 0 | } |
700 | |
|
701 | 0 | return result; |
702 | 0 | } |
703 | | |
704 | | std::unique_ptr<geom::Geometry> |
705 | | RectangleIntersection::clip() |
706 | 0 | { |
707 | 0 | RectangleIntersectionBuilder parts(*_gf); |
708 | |
|
709 | 0 | bool keep_polygons = true; |
710 | 0 | clip_geom(&_geom, parts, _rect, keep_polygons); |
711 | |
|
712 | 0 | return parts.build(); |
713 | 0 | } |
714 | | |
715 | | RectangleIntersection::RectangleIntersection(const geom::Geometry& geom, const Rectangle& rect) |
716 | 0 | : _geom(geom), _rect(rect), |
717 | 0 | _gf(geom.getFactory()) |
718 | 0 | { |
719 | 0 | } |
720 | | |
721 | | } // namespace geos::operation::intersection |
722 | | } // namespace geos::operation |
723 | | } // namespace geos |