Coverage Report

Created: 2025-07-23 06:51

/src/geos/src/operation/union/CascadedPolygonUnion.cpp
Line
Count
Source (jump to first uncovered line)
1
/**********************************************************************
2
 *
3
 * GEOS - Geometry Engine Open Source
4
 * http://geos.osgeo.org
5
 *
6
 * Copyright (C) 2011 Sandro Santilli <strk@kbt.io>
7
 * Copyright (C) 2006 Refractions Research Inc.
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/union/CascadedPolygonUnion.java r487 (JTS-1.12+)
17
 * Includes custom code to deal with https://trac.osgeo.org/geos/ticket/837
18
 *
19
 **********************************************************************/
20
21
#include <geos/geom/Geometry.h>
22
#include <geos/geom/GeometryFactory.h>
23
#include <geos/geom/MultiPolygon.h>
24
#include <geos/geom/Polygon.h>
25
#include <geos/index/strtree/TemplateSTRtree.h>
26
#include <geos/operation/overlayng/OverlayNG.h>
27
#include <geos/operation/overlayng/OverlayNGRobust.h>
28
#include <geos/operation/union/CascadedPolygonUnion.h>
29
#include <geos/operation/valid/IsValidOp.h>
30
#include <geos/operation/valid/IsSimpleOp.h>
31
#include <geos/util/TopologyException.h>
32
33
// std
34
#include <cassert>
35
#include <cstddef>
36
#include <sstream>
37
#include <string>
38
39
40
namespace geos {
41
namespace operation { // geos.operation
42
namespace geounion {  // geos.operation.geounion
43
44
45
// ////////////////////////////////////////////////////////////////////////////
46
std::unique_ptr<geom::Geometry>
47
CascadedPolygonUnion::Union(std::vector<geom::Polygon*>* polys)
48
0
{
49
0
    CascadedPolygonUnion op(polys);
50
0
    return op.Union();
51
0
}
52
53
std::unique_ptr<geom::Geometry>
54
CascadedPolygonUnion::Union(std::vector<geom::Polygon*>* polys, UnionStrategy* unionFun)
55
4.53k
{
56
4.53k
    CascadedPolygonUnion op(polys, unionFun);
57
4.53k
    return op.Union();
58
4.53k
}
59
60
std::unique_ptr<geom::Geometry>
61
CascadedPolygonUnion::Union(const geom::MultiPolygon* multipoly)
62
0
{
63
0
    std::vector<geom::Polygon*> polys;
64
65
0
    for(const auto& g : *multipoly) {
66
0
        polys.push_back(dynamic_cast<geom::Polygon*>(g.get()));
67
0
    }
68
69
0
    CascadedPolygonUnion op(&polys);
70
0
    return op.Union();
71
0
}
72
73
std::unique_ptr<geom::Geometry>
74
CascadedPolygonUnion::Union()
75
4.53k
{
76
4.53k
    if(inputPolys->empty()) {
77
0
        return nullptr;
78
0
    }
79
80
4.53k
    geomFactory = inputPolys->front()->getFactory();
81
82
    /*
83
     * A spatial index to organize the collection
84
     * into groups of close geometries.
85
     * This makes unioning more efficient, since vertices are more likely
86
     * to be eliminated on each round.
87
     */
88
89
4.53k
    index::strtree::TemplateSTRtree<const geom::Geometry*> index(10, inputPolys->size());
90
4.46M
    for (const auto& p : *inputPolys) {
91
4.46M
        index.insert(p);
92
4.46M
    }
93
94
    // TODO avoid creating this vector and run binaryUnion off the iterators directly
95
4.53k
    std::vector<const geom::Geometry*> geoms(index.items().begin(), index.items().end());
96
97
4.53k
    return binaryUnion(geoms, 0, geoms.size());
98
4.53k
}
99
100
101
std::unique_ptr<geom::Geometry>
102
CascadedPolygonUnion::binaryUnion(const std::vector<const geom::Geometry*> & geoms,
103
                                  std::size_t start, std::size_t end)
104
149k
{
105
149k
    if(end - start == 0) {
106
0
        return nullptr;
107
0
    }
108
149k
    else if(end - start == 1) {
109
21.6k
        return unionSafe(geoms[start], nullptr);
110
21.6k
    }
111
128k
    else if(end - start == 2) {
112
51.2k
        return unionSafe(geoms[start], geoms[start + 1]);
113
51.2k
    }
114
76.8k
    else {
115
        // recurse on both halves of the list
116
76.8k
        std::size_t mid = (end + start) / 2;
117
76.8k
        std::unique_ptr<geom::Geometry> g0(binaryUnion(geoms, start, mid));
118
76.8k
        std::unique_ptr<geom::Geometry> g1(binaryUnion(geoms, mid, end));
119
76.8k
        return unionSafe(std::move(g0), std::move(g1));
120
76.8k
    }
121
149k
}
122
123
std::unique_ptr<geom::Geometry>
124
CascadedPolygonUnion::unionSafe(const geom::Geometry* g0, const geom::Geometry* g1) const
125
72.9k
{
126
72.9k
    if(g0 != nullptr && g1 != nullptr) {
127
51.2k
        return unionActual(g0, g1);
128
51.2k
    }
129
130
21.6k
    if(g1 != nullptr) {
131
0
        return g1->clone();
132
0
    }
133
21.6k
    if(g0 != nullptr) {
134
21.6k
        return g0->clone();
135
21.6k
    }
136
0
    return nullptr;
137
21.6k
}
138
139
std::unique_ptr<geom::Geometry>
140
CascadedPolygonUnion::unionSafe(std::unique_ptr<geom::Geometry> && g0, std::unique_ptr<geom::Geometry> && g1)
141
66.2k
{
142
66.2k
    if(g0 == nullptr && g1 == nullptr) {
143
0
        return nullptr;
144
0
    }
145
146
66.2k
    if(g0 == nullptr) {
147
0
        return std::move(g1);
148
0
    }
149
66.2k
    if(g1 == nullptr) {
150
0
        return std::move(g0);
151
0
    }
152
153
66.2k
    return unionActual(std::move(g0), std::move(g1));
154
66.2k
}
155
156
std::unique_ptr<geom::Geometry>
157
CascadedPolygonUnion::unionActual(const geom::Geometry* g0, const geom::Geometry* g1) const
158
51.2k
{
159
51.2k
    std::unique_ptr<geom::Geometry> ug;
160
51.2k
    ug = unionFunction->Union(g0, g1);
161
51.2k
    return restrictToPolygons(std::move(ug));
162
51.2k
}
163
164
std::unique_ptr<geom::Geometry>
165
CascadedPolygonUnion::unionActual(std::unique_ptr<geom::Geometry> && g0, std::unique_ptr<geom::Geometry> && g1) const
166
66.2k
{
167
66.2k
    std::unique_ptr<geom::Geometry> ug;
168
66.2k
    ug = unionFunction->Union(std::move(g0), std::move(g1));
169
66.2k
    return restrictToPolygons(std::move(ug));
170
66.2k
}
171
172
std::unique_ptr<geom::Geometry>
173
CascadedPolygonUnion::restrictToPolygons(std::unique_ptr<geom::Geometry> g)
174
116k
{
175
116k
    using namespace geom;
176
177
116k
    if(g->isPolygonal()) {
178
80.3k
        return g;
179
80.3k
    }
180
181
35.8k
    auto gfact = g->getFactory();
182
35.8k
    auto coordDim = g->getCoordinateDimension();
183
184
35.8k
    auto coll = dynamic_cast<GeometryCollection*>(g.get());
185
35.8k
    if (coll) {
186
        // Release polygons from the collection and re-form into MultiPolygon
187
31.3k
        auto components = coll->releaseGeometries();
188
84.6k
        components.erase(std::remove_if(components.begin(), components.end(), [](const std::unique_ptr<Geometry> & cmp) {
189
84.6k
            return !cmp->isPolygonal();
190
84.6k
        }), components.end());
191
192
31.3k
        return gfact->createMultiPolygon(std::move(components));
193
31.3k
    } else {
194
        // Not polygonal and not a collection? No polygons here.
195
4.54k
        return gfact->createPolygon(coordDim);
196
4.54k
    }
197
35.8k
}
198
199
/************************************************************************/
200
201
std::unique_ptr<geom::Geometry>
202
ClassicUnionStrategy::Union(const geom::Geometry* g0, const geom::Geometry* g1)
203
0
{
204
0
    try {
205
0
        return operation::overlayng::OverlayNGRobust::Overlay(g0, g1, operation::overlayng::OverlayNG::UNION);
206
0
    }
207
0
    catch (const util::TopologyException &ex) {
208
0
        ::geos::ignore_unused_variable_warning(ex);
209
        // union-by-buffer only works for polygons
210
0
        if (g0->getDimension() != 2 || g1->getDimension() != 2)
211
0
          throw;
212
0
        return unionPolygonsByBuffer(g0, g1);
213
0
    }
214
0
}
215
216
bool
217
ClassicUnionStrategy::isFloatingPrecision() const
218
0
{
219
0
  return true;
220
0
}
221
222
/*private*/
223
std::unique_ptr<geom::Geometry>
224
ClassicUnionStrategy::unionPolygonsByBuffer(const geom::Geometry* g0, const geom::Geometry* g1)
225
0
{
226
0
    std::vector<std::unique_ptr<geom::Geometry>> geoms;
227
0
    geoms.push_back(g0->clone());
228
0
    geoms.push_back(g1->clone());
229
0
    std::unique_ptr<geom::GeometryCollection> coll = g0->getFactory()->createGeometryCollection(std::move(geoms));
230
0
    return coll->buffer(0);
231
0
}
232
233
234
235
236
237
} // namespace geos.operation.union
238
} // namespace geos.operation
239
} // namespace geos