/src/geos/src/operation/cluster/AbstractClusterFinder.cpp
Line | Count | Source |
1 | | /********************************************************************** |
2 | | * |
3 | | * GEOS - Geometry Engine Open Source |
4 | | * http://geos.osgeo.org |
5 | | * |
6 | | * Copyright (C) 2020-2022 Daniel Baston |
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/operation/cluster/AbstractClusterFinder.h> |
16 | | #include <geos/geom/Geometry.h> |
17 | | #include <geos/geom/GeometryCollection.h> |
18 | | #include <geos/geom/GeometryFactory.h> |
19 | | #include <geos/geom/prep/PreparedGeometry.h> |
20 | | |
21 | | #include <geos/util.h> |
22 | | #include <geos/index/strtree/TemplateSTRtree.h> |
23 | | #include <geos/operation/cluster/UnionFind.h> |
24 | | |
25 | | namespace geos { |
26 | | namespace operation { |
27 | | namespace cluster { |
28 | | |
29 | | using geom::Geometry; |
30 | | |
31 | | std::vector<std::unique_ptr<geom::Geometry>> |
32 | 0 | AbstractClusterFinder::clusterToVector(const geom::Geometry& g) { |
33 | 0 | return clusterToVector(g.clone()); |
34 | 0 | } |
35 | | |
36 | | std::unique_ptr<geom::Geometry> |
37 | 0 | AbstractClusterFinder::clusterToCollection(std::unique_ptr<geom::Geometry> && g) { |
38 | 0 | auto gfact = g->getFactory(); |
39 | |
|
40 | 0 | return gfact->createGeometryCollection(clusterToVector(std::move(g))); |
41 | 0 | } |
42 | | |
43 | | std::unique_ptr<geom::Geometry> |
44 | 0 | AbstractClusterFinder::clusterToCollection(const geom::Geometry & g) { |
45 | 0 | return clusterToCollection(g.clone()); |
46 | 0 | } |
47 | | |
48 | | |
49 | | Clusters |
50 | 0 | AbstractClusterFinder::cluster(const std::vector<const geom::Geometry*> & components) { |
51 | 0 | index::strtree::TemplateSTRtree<std::size_t> tree; |
52 | |
|
53 | 0 | for (std::size_t i = 0; i < components.size(); i++) { |
54 | 0 | tree.insert(*components[i]->getEnvelopeInternal(), i); |
55 | 0 | } |
56 | |
|
57 | 0 | UnionFind uf(components.size()); |
58 | 0 | return process(components, tree, uf); |
59 | 0 | } |
60 | | |
61 | | std::vector<std::unique_ptr<geom::Geometry>> |
62 | 0 | AbstractClusterFinder::clusterToVector(std::unique_ptr<geom::Geometry> && g) { |
63 | 0 | const geom::GeometryFactory& gfact = *g->getFactory(); |
64 | |
|
65 | 0 | std::vector<const Geometry*> components(g->getNumGeometries()); |
66 | 0 | for (size_t i = 0; i < g->getNumGeometries(); i++) { |
67 | 0 | components[i]= g->getGeometryN(i); |
68 | 0 | } |
69 | |
|
70 | 0 | const auto& clusters = cluster(components); |
71 | |
|
72 | 0 | std::vector<std::unique_ptr<Geometry>> component_geoms = getComponents(std::move(g)); |
73 | |
|
74 | 0 | std::vector<std::unique_ptr<Geometry>> cluster_geoms; |
75 | |
|
76 | 0 | for (size_t i = 0; i < clusters.getNumClusters(); i++) { |
77 | 0 | std::vector<std::unique_ptr<Geometry>> cluster_component_geoms; |
78 | 0 | cluster_component_geoms.reserve(clusters.getSize(i)); |
79 | 0 | for (auto it = clusters.begin(i); it != clusters.end(i); ++it) { |
80 | 0 | cluster_component_geoms.push_back(std::move(component_geoms[*it])); |
81 | 0 | } |
82 | 0 | cluster_geoms.push_back(gfact.buildGeometry(std::move(cluster_component_geoms))); |
83 | 0 | } |
84 | |
|
85 | 0 | return cluster_geoms; |
86 | 0 | } |
87 | | |
88 | | Clusters |
89 | | AbstractClusterFinder::process(const std::vector<const Geometry*> & components, |
90 | | index::strtree::TemplateSTRtree<std::size_t> & tree, |
91 | 0 | UnionFind & uf) { |
92 | |
|
93 | 0 | std::vector<size_t> hits; |
94 | |
|
95 | 0 | for (size_t i = 0; i < components.size(); i++) { |
96 | 0 | const geom::Geometry* gi = components[i]; |
97 | |
|
98 | 0 | hits.clear(); |
99 | | |
100 | | // Sort candidates based on envelope area to try and perform simpler intersection tests instead of |
101 | | // complex ones. This seems to perform better than sorting on the number of points. |
102 | 0 | tree.query(queryEnvelope(gi), hits); |
103 | 0 | std::sort(hits.begin(), hits.end(), [&components](std::size_t a, std::size_t b) { |
104 | 0 | return components[a]->getEnvelopeInternal()->getArea() < components[b]->getEnvelopeInternal()->getArea(); |
105 | 0 | }); |
106 | |
|
107 | 0 | for (std::size_t j : hits) { |
108 | 0 | if (uf.different(i, j)) { |
109 | 0 | const geom::Geometry* gj = components[j]; |
110 | | |
111 | | // Only call shouldJoin with the more complex geometry in the LHS, where it will become |
112 | | // the prepared geometry. |
113 | | // TODO move point check to subclasses that benefit to avoid for those that don't? |
114 | 0 | if (gi->getNumPoints() >= gj->getNumPoints() && shouldJoin(gi, gj)) { |
115 | 0 | uf.join(i, j); |
116 | 0 | } |
117 | 0 | } |
118 | 0 | } |
119 | |
|
120 | 0 | } |
121 | |
|
122 | 0 | return uf.getClusters(); |
123 | 0 | } |
124 | | |
125 | | std::vector<std::unique_ptr<Geometry>> |
126 | | AbstractClusterFinder::getComponents(std::unique_ptr<Geometry>&& g) |
127 | 0 | { |
128 | 0 | if (g->isCollection()) { |
129 | 0 | return detail::down_cast<geom::GeometryCollection*>(g.get())->releaseGeometries(); |
130 | 0 | } else { |
131 | 0 | std::vector<std::unique_ptr<Geometry>> ret(1); |
132 | 0 | ret[0] = std::move(g); |
133 | 0 | return ret; |
134 | 0 | } |
135 | 0 | } |
136 | | |
137 | | |
138 | | |
139 | | } |
140 | | } |
141 | | } |