1 /**********************************************************************
2 *
3 * GEOS-Geometry Engine Open Source
4 * http://geos.osgeo.org
5 *
6 * Copyright (C) 2009-2011 Sandro Santilli <strk@kbt.io>
7 * Copyright (C) 2005 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/buffer/OffsetCurveBuilder.java r378 (JTS-1.12)
18 *
19 **********************************************************************/
20
21 #include <cassert>
22 #include <cmath>
23 #include <vector>
24
25 #include <geos/algorithm/Angle.h>
26 #include <geos/operation/buffer/OffsetCurveBuilder.h>
27 #include <geos/operation/buffer/BufferInputLineSimplifier.h>
28 #include <geos/operation/buffer/BufferOp.h>
29 #include <geos/operation/buffer/BufferParameters.h>
30 #include <geos/geom/Position.h>
31 #include <geos/geom/CoordinateArraySequence.h>
32 #include <geos/geom/CoordinateSequence.h>
33 #include <geos/geom/Coordinate.h>
34 #include <geos/geom/PrecisionModel.h>
35 #include <geos/algorithm/NotRepresentableException.h>
36 #include <geos/algorithm/HCoordinate.h>
37 #include <geos/util.h>
38 #include <geos/util/IllegalArgumentException.h>
39
40 #ifndef GEOS_DEBUG
41 #define GEOS_DEBUG 0
42 #endif
43
44 using namespace std;
45 using namespace geos::algorithm;
46 using namespace geos::geom;
47
48 namespace geos {
49 namespace operation { // geos.operation
50 namespace buffer { // geos.operation.buffer
51
52 /*private data*/
53 const double OffsetCurveBuilder::SIMPLIFY_FACTOR = 100.0;
54
55 /*public*/
56 void
getLineCurve(const CoordinateSequence * inputPts,double nDistance,vector<CoordinateSequence * > & lineList)57 OffsetCurveBuilder::getLineCurve(const CoordinateSequence* inputPts,
58 double nDistance, vector<CoordinateSequence*>& lineList)
59 {
60 distance = nDistance;
61
62 if (isLineOffsetEmpty(distance)) {
63 return;
64 }
65
66 double posDistance = std::abs(distance);
67
68 std::unique_ptr<OffsetSegmentGenerator> segGen = getSegGen(posDistance);
69 if(inputPts->getSize() <= 1) {
70 computePointCurve(inputPts->getAt(0), *segGen);
71 }
72 else {
73 if(bufParams.isSingleSided()) {
74 bool isRightSide = distance < 0.0;
75 computeSingleSidedBufferCurve(*inputPts, isRightSide, *segGen);
76 }
77 else {
78 computeLineBufferCurve(*inputPts, *segGen);
79 }
80 }
81
82 segGen->getCoordinates(lineList);
83 }
84
85 /* private */
86 void
computePointCurve(const Coordinate & pt,OffsetSegmentGenerator & segGen)87 OffsetCurveBuilder::computePointCurve(const Coordinate& pt,
88 OffsetSegmentGenerator& segGen)
89 {
90 switch(bufParams.getEndCapStyle()) {
91 case BufferParameters::CAP_ROUND:
92 segGen.createCircle(pt, distance);
93 break;
94 case BufferParameters::CAP_SQUARE:
95 segGen.createSquare(pt, distance);
96 break;
97 default:
98 // otherwise curve is empty (e.g. for a butt cap);
99 break;
100 }
101 }
102
103 /*public*/
104 void
getSingleSidedLineCurve(const CoordinateSequence * inputPts,double p_distance,vector<CoordinateSequence * > & lineList,bool leftSide,bool rightSide)105 OffsetCurveBuilder::getSingleSidedLineCurve(const CoordinateSequence* inputPts,
106 double p_distance, vector<CoordinateSequence*>& lineList, bool leftSide,
107 bool rightSide)
108 {
109 // A zero or negative width buffer of a line/point is empty.
110 if(p_distance <= 0.0) {
111 return ;
112 }
113
114 if(inputPts->getSize() < 2) {
115 // No cap, so just return.
116 return ;
117 }
118
119 double distTol = simplifyTolerance(p_distance);
120
121 std::unique_ptr<OffsetSegmentGenerator> segGen = getSegGen(p_distance);
122
123 if(leftSide) {
124 //--------- compute points for left side of line
125 // Simplify the appropriate side of the line before generating
126 std::unique_ptr<CoordinateSequence> simp1_ =
127 BufferInputLineSimplifier::simplify(*inputPts, distTol);
128 const CoordinateSequence& simp1 = *simp1_;
129
130
131 auto n1 = simp1.size() - 1;
132 if(! n1) {
133 throw util::IllegalArgumentException("Cannot get offset of single-vertex line");
134 }
135 segGen->initSideSegments(simp1[0], simp1[1], Position::LEFT);
136 segGen->addFirstSegment();
137 for(size_t i = 2; i <= n1; ++i) {
138 segGen->addNextSegment(simp1[i], true);
139 }
140 segGen->addLastSegment();
141 }
142
143 if(rightSide) {
144
145 //---------- compute points for right side of line
146 // Simplify the appropriate side of the line before generating
147 std::unique_ptr<CoordinateSequence> simp2_ =
148 BufferInputLineSimplifier::simplify(*inputPts, -distTol);
149 const CoordinateSequence& simp2 = *simp2_;
150
151 auto n2 = simp2.size() - 1;
152 if(! n2) {
153 throw util::IllegalArgumentException("Cannot get offset of single-vertex line");
154 }
155 segGen->initSideSegments(simp2[n2], simp2[n2 - 1], Position::LEFT);
156 segGen->addFirstSegment();
157 for(size_t i = n2 - 1; i > 0; --i) {
158 segGen->addNextSegment(simp2[i - 1], true);
159 }
160 segGen->addLastSegment();
161 }
162
163 segGen->getCoordinates(lineList);
164 }
165
166 /*public*/
167 bool
isLineOffsetEmpty(double p_distance)168 OffsetCurveBuilder::isLineOffsetEmpty(double p_distance)
169 {
170 // a zero width buffer of a line or point is empty
171 if (p_distance == 0.0) return true;
172 // a negative width buffer of a line or point is empty,
173 // except for single-sided buffers, where the sign indicates the side
174 if (p_distance < 0.0 && ! bufParams.isSingleSided()) return true;
175 return false;
176 }
177
178
179 /*public*/
180 void
getRingCurve(const CoordinateSequence * inputPts,int side,double nDistance,vector<CoordinateSequence * > & lineList)181 OffsetCurveBuilder::getRingCurve(const CoordinateSequence* inputPts,
182 int side, double nDistance,
183 vector<CoordinateSequence*>& lineList)
184 {
185 distance = nDistance;
186
187 // optimize creating ring for zero distance
188 if(distance == 0.0) {
189 lineList.push_back(inputPts->clone().release());
190 return;
191 }
192
193 if(inputPts->getSize() <= 2) {
194 getLineCurve(inputPts, distance, lineList);
195 return;
196 }
197
198 std::unique_ptr<OffsetSegmentGenerator> segGen = getSegGen(std::abs(distance));
199 computeRingBufferCurve(*inputPts, side, *segGen);
200 segGen->getCoordinates(lineList);
201 }
202
203 /* private */
204 double
simplifyTolerance(double bufDistance)205 OffsetCurveBuilder::simplifyTolerance(double bufDistance)
206 {
207 return bufDistance / SIMPLIFY_FACTOR;
208 }
209
210 /*private*/
211 void
computeLineBufferCurve(const CoordinateSequence & inputPts,OffsetSegmentGenerator & segGen)212 OffsetCurveBuilder::computeLineBufferCurve(const CoordinateSequence& inputPts,
213 OffsetSegmentGenerator& segGen)
214 {
215 double distTol = simplifyTolerance(distance);
216
217 //--------- compute points for left side of line
218 // Simplify the appropriate side of the line before generating
219 std::unique_ptr<CoordinateSequence> simp1_ =
220 BufferInputLineSimplifier::simplify(inputPts, distTol);
221 const CoordinateSequence& simp1 = *simp1_;
222
223
224 auto n1 = simp1.size() - 1;
225 segGen.initSideSegments(simp1[0], simp1[1], Position::LEFT);
226 for(size_t i = 2; i <= n1; ++i) {
227 segGen.addNextSegment(simp1[i], true);
228 }
229 segGen.addLastSegment();
230 // add line cap for end of line
231 segGen.addLineEndCap(simp1[n1 - 1], simp1[n1]);
232
233 //---------- compute points for right side of line
234 // Simplify the appropriate side of the line before generating
235 std::unique_ptr<CoordinateSequence> simp2_ =
236 BufferInputLineSimplifier::simplify(inputPts, -distTol);
237 const CoordinateSequence& simp2 = *simp2_;
238
239 auto n2 = simp2.size() - 1;
240 segGen.initSideSegments(simp2[n2], simp2[n2 - 1], Position::LEFT);
241 for(size_t i = n2 - 1; i > 0; --i) {
242 segGen.addNextSegment(simp2[i - 1], true);
243 }
244 segGen.addLastSegment();
245 // add line cap for start of line
246 segGen.addLineEndCap(simp2[1], simp2[0]);
247
248 segGen.closeRing();
249 }
250
251 /*private*/
252 void
computeRingBufferCurve(const CoordinateSequence & inputPts,int side,OffsetSegmentGenerator & segGen)253 OffsetCurveBuilder::computeRingBufferCurve(const CoordinateSequence& inputPts,
254 int side, OffsetSegmentGenerator& segGen)
255 {
256 // simplify input line to improve performance
257 double distTol = simplifyTolerance(distance);
258 // ensure that correct side is simplified
259 if(side == Position::RIGHT) {
260 distTol = -distTol;
261 }
262 std::unique_ptr<CoordinateSequence> simp_ =
263 BufferInputLineSimplifier::simplify(inputPts, distTol);
264 const CoordinateSequence& simp = *simp_;
265
266 auto n = simp.size() - 1;
267 segGen.initSideSegments(simp[n - 1], simp[0], side);
268 for(size_t i = 1; i <= n; i++) {
269 bool addStartPoint = i != 1;
270 segGen.addNextSegment(simp[i], addStartPoint);
271 }
272 segGen.closeRing();
273 }
274
275 /*private*/
276 void
computeSingleSidedBufferCurve(const CoordinateSequence & inputPts,bool isRightSide,OffsetSegmentGenerator & segGen)277 OffsetCurveBuilder::computeSingleSidedBufferCurve(
278 const CoordinateSequence& inputPts, bool isRightSide,
279 OffsetSegmentGenerator& segGen)
280 {
281 double distTol = simplifyTolerance(distance);
282
283 if(isRightSide) {
284
285 // add original line
286 segGen.addSegments(inputPts, true);
287
288 //---------- compute points for right side of line
289 // Simplify the appropriate side of the line before generating
290 std::unique_ptr<CoordinateSequence> simp2_ =
291 BufferInputLineSimplifier::simplify(inputPts, -distTol);
292 const CoordinateSequence& simp2 = *simp2_;
293
294 auto n2 = simp2.size() - 1;
295 segGen.initSideSegments(simp2[n2], simp2[n2 - 1], Position::LEFT);
296 segGen.addFirstSegment();
297 for(size_t i = n2 - 1; i > 0; --i) {
298 segGen.addNextSegment(simp2[i - 1], true);
299 }
300
301 }
302 else {
303
304 // add original line
305 segGen.addSegments(inputPts, false);
306
307 //--------- compute points for left side of line
308 // Simplify the appropriate side of the line before generating
309 std::unique_ptr<CoordinateSequence> simp1_ =
310 BufferInputLineSimplifier::simplify(inputPts, distTol);
311 const CoordinateSequence& simp1 = *simp1_;
312
313 auto n1 = simp1.size() - 1;
314 segGen.initSideSegments(simp1[0], simp1[1], Position::LEFT);
315 segGen.addFirstSegment();
316 for(size_t i = 2; i <= n1; ++i) {
317 segGen.addNextSegment(simp1[i], true);
318 }
319
320 }
321 segGen.addLastSegment();
322 segGen.closeRing();
323 }
324
325 /*private*/
326 std::unique_ptr<OffsetSegmentGenerator>
getSegGen(double dist)327 OffsetCurveBuilder::getSegGen(double dist)
328 {
329 std::unique_ptr<OffsetSegmentGenerator> osg(
330 new OffsetSegmentGenerator(precisionModel, bufParams, dist)
331 );
332 return osg;
333 }
334
335 } // namespace geos.operation.buffer
336 } // namespace geos.operation
337 } // namespace geos
338
339