/src/geos/src/geom/util/Densifier.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) 2010 Sandro Santilli <strk@kbt.io> |
7 | | * Copyright (C) 2005-2006 Refractions Research Inc. |
8 | | * Copyright (C) 2001-2002 Vivid Solutions Inc. |
9 | | * |
10 | | * This is free software; you can redistribute and/or modify it under |
11 | | * the terms of the GNU Lesser General Public Licence as published |
12 | | * by the Free Software Foundation. |
13 | | * See the COPYING file for more information. |
14 | | * |
15 | | ********************************************************************** |
16 | | * |
17 | | * Last port: operation/polygonize/Polygonizer.java rev. 1.6 (JTS-1.10) |
18 | | * |
19 | | **********************************************************************/ |
20 | | |
21 | | #include <cmath> |
22 | | |
23 | | #include <geos/geom/util/Densifier.h> |
24 | | #include <geos/geom/CoordinateList.h> |
25 | | #include <geos/geom/Geometry.h> |
26 | | #include <geos/geom/GeometryFactory.h> |
27 | | #include <geos/geom/MultiPoint.h> |
28 | | #include <geos/geom/MultiPolygon.h> |
29 | | #include <geos/geom/MultiLineString.h> |
30 | | #include <geos/geom/CoordinateSequence.h> |
31 | | #include <geos/geom/PrecisionModel.h> |
32 | | #include <geos/geom/Polygon.h> |
33 | | #include <geos/geom/Point.h> |
34 | | #include <geos/geom/LineString.h> |
35 | | #include <geos/geom/LinearRing.h> |
36 | | #include <geos/geom/LineSegment.h> |
37 | | #include <geos/geom/GeometryCollection.h> |
38 | | #include <geos/util/Interrupt.h> |
39 | | #include <geos/util/IllegalArgumentException.h> |
40 | | #include <geos/util.h> |
41 | | |
42 | | #include <limits> |
43 | | #include <vector> |
44 | | |
45 | | using namespace geos::geom; |
46 | | using namespace geos::geom::util; |
47 | | |
48 | | namespace geos { |
49 | | namespace geom { // geos.geom |
50 | | namespace util { // geos.geom.util |
51 | | |
52 | | /* geom::util::Densifier::DensifyTransformer */ |
53 | | Densifier::DensifyTransformer::DensifyTransformer(double distTol): |
54 | 0 | distanceTolerance(distTol) |
55 | 0 | {} |
56 | | |
57 | | CoordinateSequence::Ptr |
58 | | Densifier::DensifyTransformer::transformCoordinates(const CoordinateSequence* coords, const Geometry* parent) |
59 | 0 | { |
60 | 0 | Coordinate::Vect emptyPts; |
61 | 0 | Coordinate::Vect inputPts; |
62 | 0 | auto newPts = Densifier::densifyPoints(*coords, distanceTolerance, parent->getPrecisionModel()); |
63 | |
|
64 | 0 | if(const LineString* ls = dynamic_cast<const LineString*>(parent)) { |
65 | 0 | if(ls->getNumPoints() <= 1) { |
66 | 0 | newPts->clear(); |
67 | 0 | } |
68 | 0 | } |
69 | |
|
70 | 0 | return newPts; |
71 | 0 | } |
72 | | |
73 | | Geometry::Ptr |
74 | | Densifier::DensifyTransformer::transformPolygon(const Polygon* geom, const Geometry* parent) |
75 | 0 | { |
76 | 0 | Geometry::Ptr roughGeom = GeometryTransformer::transformPolygon(geom, parent); |
77 | | // don't try and correct if the parent is going to do this |
78 | 0 | if(parent && parent->getGeometryTypeId() == GEOS_MULTIPOLYGON) { |
79 | 0 | return roughGeom; |
80 | 0 | } |
81 | 0 | Geometry::Ptr validGeom(createValidArea(roughGeom.get())); |
82 | 0 | return validGeom; |
83 | 0 | } |
84 | | |
85 | | Geometry::Ptr |
86 | | Densifier::DensifyTransformer::transformMultiPolygon(const MultiPolygon* geom, const Geometry* parent) |
87 | 0 | { |
88 | 0 | Geometry::Ptr roughGeom = GeometryTransformer::transformMultiPolygon(geom, parent); |
89 | 0 | Geometry::Ptr validGeom(createValidArea(roughGeom.get())); |
90 | 0 | return validGeom; |
91 | 0 | } |
92 | | |
93 | | std::unique_ptr<Geometry> |
94 | | Densifier::DensifyTransformer::createValidArea(const Geometry* roughAreaGeom) |
95 | 0 | { |
96 | 0 | if (roughAreaGeom->isValid()) |
97 | 0 | return Geometry::Ptr(roughAreaGeom->clone()); |
98 | 0 | return roughAreaGeom->buffer(0.0); |
99 | 0 | } |
100 | | |
101 | | /* util::Densifier */ |
102 | | |
103 | | Densifier::Densifier(const Geometry* geom): |
104 | 0 | inputGeom(geom) |
105 | 0 | {} |
106 | | |
107 | | std::unique_ptr<CoordinateSequence> |
108 | | Densifier::densifyPoints(const CoordinateSequence& pts, double distanceTolerance, const PrecisionModel* precModel) |
109 | 0 | { |
110 | 0 | geom::LineSegment seg; |
111 | 0 | auto coordList = detail::make_unique<CoordinateSequence>(); |
112 | |
|
113 | 0 | auto items = pts.items<Coordinate>(); |
114 | 0 | for(auto it = items.cbegin(), itEnd = items.cend() - 1; it < itEnd; ++it) { |
115 | 0 | seg.p0 = *it; |
116 | 0 | seg.p1 = *(it + 1); |
117 | 0 | coordList->add(seg.p0, false); |
118 | 0 | const double len = seg.getLength(); |
119 | 0 | const double densifiedSegCountDbl = ceil(len / distanceTolerance); |
120 | 0 | if(densifiedSegCountDbl > std::numeric_limits<int>::max()) { |
121 | 0 | throw geos::util::GEOSException( |
122 | 0 | "Tolerance is too small compared to geometry length"); |
123 | 0 | } |
124 | | |
125 | 0 | const int densifiedSegCount = static_cast<int>(densifiedSegCountDbl); |
126 | 0 | if(densifiedSegCount > 1) { |
127 | 0 | double densifiedSegLen = len / densifiedSegCount; |
128 | 0 | for(int j = 1; j < densifiedSegCount; j++) { |
129 | 0 | double segFract = (j * densifiedSegLen) / len; |
130 | 0 | Coordinate p; |
131 | 0 | seg.pointAlong(segFract, p); |
132 | 0 | precModel->makePrecise(p); |
133 | 0 | coordList->add(p, false); |
134 | 0 | } |
135 | 0 | } |
136 | 0 | else { |
137 | | // no densification required; insert the last coordinate and continue |
138 | 0 | coordList->add(seg.p1, false); |
139 | 0 | } |
140 | 0 | } |
141 | 0 | coordList->add(pts[pts.size() - 1], false); |
142 | |
|
143 | 0 | return coordList; |
144 | 0 | } |
145 | | |
146 | | /** |
147 | | * Densifies a geometry using a given distance tolerance, |
148 | | * and respecting the input geometry's {@link PrecisionModel}. |
149 | | * |
150 | | * @param geom the geometry to densify |
151 | | * @param distanceTolerance the distance tolerance to densify |
152 | | * @return the densified geometry |
153 | | */ |
154 | | Geometry::Ptr |
155 | | Densifier::densify(const Geometry* geom, double distTol) |
156 | 0 | { |
157 | 0 | util::Densifier densifier(geom); |
158 | 0 | densifier.setDistanceTolerance(distTol); |
159 | 0 | return densifier.getResultGeometry(); |
160 | 0 | } |
161 | | |
162 | | void |
163 | | Densifier::setDistanceTolerance(double tol) |
164 | 0 | { |
165 | | // Test written to catch NaN as well |
166 | 0 | if(!(tol > 0.0)) { |
167 | 0 | throw geos::util::IllegalArgumentException("Tolerance must be positive"); |
168 | 0 | } |
169 | 0 | distanceTolerance = tol; |
170 | 0 | } |
171 | | |
172 | | Geometry::Ptr |
173 | | Densifier::getResultGeometry() const |
174 | 0 | { |
175 | 0 | if (inputGeom->isEmpty()) { |
176 | 0 | return inputGeom->clone(); |
177 | 0 | } |
178 | | |
179 | 0 | DensifyTransformer dt(distanceTolerance); |
180 | 0 | return dt.transform(inputGeom); |
181 | 0 | } |
182 | | |
183 | | |
184 | | } // namespace geos.geom.util |
185 | | } // namespace geos.geom |
186 | | } // namespace geos |