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