1 /**********************************************************************
2 *
3 * GEOS - Geometry Engine Open Source
4 * http://geos.osgeo.org
5 *
6 * Copyright (C) 2006 Refractions Research Inc.
7 *
8 * This is free software; you can redistribute and/or modify it under
9 * the terms of the GNU Lesser General Licence as published
10 * by the Free Software Foundation.
11 * See the COPYING file for more information.
12 *
13 **********************************************************************
14 *
15 * Last port: simplify/DouglasPeuckerSimplifier.java rev. 1.5 (JTS-1.7)
16 *
17 **********************************************************************/
18
19 #include <geos/simplify/DouglasPeuckerSimplifier.h>
20 #include <geos/simplify/DouglasPeuckerLineSimplifier.h>
21 #include <geos/geom/Geometry.h> // for Ptr typedefs
22 #include <geos/geom/MultiPolygon.h>
23 #include <geos/geom/CoordinateSequence.h> // for Ptr typedefs
24 #include <geos/geom/GeometryFactory.h>
25 #include <geos/geom/CoordinateSequenceFactory.h>
26 #include <geos/geom/util/GeometryTransformer.h> // for DPTransformer inheritance
27 #include <geos/util/IllegalArgumentException.h>
28 #include <geos/util.h>
29
30 #include <memory> // for unique_ptr
31 #include <cassert>
32
33 #ifndef GEOS_DEBUG
34 #define GEOS_DEBUG 0
35 #endif
36
37 #ifdef GEOS_DEBUG
38 #include <iostream>
39 #endif
40
41 using namespace geos::geom;
42
43 namespace geos {
44 namespace simplify { // geos::simplify
45
46 class DPTransformer: public geom::util::GeometryTransformer {
47
48 public:
49
50 DPTransformer(double tolerance);
51
52 protected:
53
54 CoordinateSequence::Ptr transformCoordinates(
55 const CoordinateSequence* coords,
56 const Geometry* parent) override;
57
58 Geometry::Ptr transformPolygon(
59 const Polygon* geom,
60 const Geometry* parent) override;
61
62 Geometry::Ptr transformMultiPolygon(
63 const MultiPolygon* geom,
64 const Geometry* parent) override;
65
66 private:
67
68 /*
69 * Creates a valid area geometry from one that possibly has
70 * bad topology (i.e. self-intersections).
71 * Since buffer can handle invalid topology, but always returns
72 * valid geometry, constructing a 0-width buffer "corrects" the
73 * topology.
74 * Note this only works for area geometries, since buffer always returns
75 * areas. This also may return empty geometries, if the input
76 * has no actual area.
77 *
78 * @param roughAreaGeom an area geometry possibly containing
79 * self-intersections
80 * @return a valid area geometry
81 */
82 Geometry::Ptr createValidArea(const Geometry* roughAreaGeom);
83
84 double distanceTolerance;
85
86 };
87
DPTransformer(double t)88 DPTransformer::DPTransformer(double t)
89 :
90 distanceTolerance(t)
91 {
92 setSkipTransformedInvalidInteriorRings(true);
93 }
94
95 Geometry::Ptr
createValidArea(const Geometry * roughAreaGeom)96 DPTransformer::createValidArea(const Geometry* roughAreaGeom)
97 {
98 return Geometry::Ptr(roughAreaGeom->buffer(0.0));
99 }
100
101 CoordinateSequence::Ptr
transformCoordinates(const CoordinateSequence * coords,const Geometry * parent)102 DPTransformer::transformCoordinates(
103 const CoordinateSequence* coords,
104 const Geometry* parent)
105 {
106 ::geos::ignore_unused_variable_warning(parent);
107
108 Coordinate::Vect inputPts;
109 coords->toVector(inputPts);
110
111 std::unique_ptr<Coordinate::Vect> newPts =
112 DouglasPeuckerLineSimplifier::simplify(inputPts, distanceTolerance);
113
114 return CoordinateSequence::Ptr(
115 factory->getCoordinateSequenceFactory()->create(
116 newPts.release()
117 ));
118 }
119
120 Geometry::Ptr
transformPolygon(const Polygon * geom,const Geometry * parent)121 DPTransformer::transformPolygon(
122 const Polygon* geom,
123 const Geometry* parent)
124 {
125
126 #if GEOS_DEBUG
127 std::cerr << "DPTransformer::transformPolygon(Polygon " << geom << ", Geometry " << parent << ");" << std::endl;
128 #endif
129
130 Geometry::Ptr roughGeom(GeometryTransformer::transformPolygon(geom, parent));
131
132 // don't try and correct if the parent is going to do this
133 if(dynamic_cast<const MultiPolygon*>(parent)) {
134 return roughGeom;
135 }
136
137 return createValidArea(roughGeom.get());
138 }
139
140 Geometry::Ptr
transformMultiPolygon(const MultiPolygon * geom,const Geometry * parent)141 DPTransformer::transformMultiPolygon(
142 const MultiPolygon* geom,
143 const Geometry* parent)
144 {
145 #if GEOS_DEBUG
146 std::cerr << "DPTransformer::transformMultiPolygon(MultiPolygon " << geom << ", Geometry " << parent << ");" <<
147 std::endl;
148 #endif
149 Geometry::Ptr roughGeom(GeometryTransformer::transformMultiPolygon(geom, parent));
150 return createValidArea(roughGeom.get());
151 }
152
153 /************************************************************************/
154
155
156
157 //DouglasPeuckerSimplifier::
158
159 /*public static*/
160 Geometry::Ptr
simplify(const Geometry * geom,double tolerance)161 DouglasPeuckerSimplifier::simplify(const Geometry* geom,
162 double tolerance)
163 {
164 DouglasPeuckerSimplifier tss(geom);
165 tss.setDistanceTolerance(tolerance);
166 return tss.getResultGeometry();
167 }
168
169 /*public*/
DouglasPeuckerSimplifier(const Geometry * geom)170 DouglasPeuckerSimplifier::DouglasPeuckerSimplifier(const Geometry* geom)
171 :
172 inputGeom(geom)
173 {
174 }
175
176 /*public*/
177 void
setDistanceTolerance(double tol)178 DouglasPeuckerSimplifier::setDistanceTolerance(double tol)
179 {
180 if(tol < 0.0) {
181 throw util::IllegalArgumentException("Tolerance must be non-negative");
182 }
183 distanceTolerance = tol;
184 }
185
186 Geometry::Ptr
getResultGeometry()187 DouglasPeuckerSimplifier::getResultGeometry()
188 {
189 DPTransformer t(distanceTolerance);
190 return t.transform(inputGeom);
191
192 }
193
194 } // namespace geos::simplify
195 } // namespace geos
196