Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgscurve.cpp
3 : : --------------
4 : : begin : November 2014
5 : : copyright : (C) 2014 by Marco Hugentobler
6 : : email : marco at sourcepole dot ch
7 : : ***************************************************************************/
8 : :
9 : : /***************************************************************************
10 : : * *
11 : : * This program is free software; you can redistribute it and/or modify *
12 : : * it under the terms of the GNU General Public License as published by *
13 : : * the Free Software Foundation; either version 2 of the License, or *
14 : : * (at your option) any later version. *
15 : : * *
16 : : ***************************************************************************/
17 : :
18 : : #include <memory>
19 : :
20 : : #include "qgscurve.h"
21 : : #include "qgslinestring.h"
22 : : #include "qgspoint.h"
23 : : #include "qgsmultipoint.h"
24 : : #include "qgsgeos.h"
25 : :
26 : 310 : bool QgsCurve::operator==( const QgsAbstractGeometry &other ) const
27 : : {
28 : 310 : const QgsCurve *otherCurve = qgsgeometry_cast< const QgsCurve * >( &other );
29 : 310 : if ( !otherCurve )
30 : 2 : return false;
31 : :
32 : 308 : return equals( *otherCurve );
33 : 310 : }
34 : :
35 : 175 : bool QgsCurve::operator!=( const QgsAbstractGeometry &other ) const
36 : : {
37 : 175 : return !operator==( other );
38 : : }
39 : :
40 : 45 : bool QgsCurve::isClosed() const
41 : : {
42 : 45 : if ( numPoints() == 0 )
43 : 4 : return false;
44 : :
45 : : //don't consider M-coordinates when testing closedness
46 : 41 : QgsPoint start = startPoint();
47 : 41 : QgsPoint end = endPoint();
48 : :
49 : 61 : bool closed = qgsDoubleNear( start.x(), end.x() ) &&
50 : 20 : qgsDoubleNear( start.y(), end.y() );
51 : 41 : if ( is3D() && closed )
52 : 0 : closed &= qgsDoubleNear( start.z(), end.z() ) || ( std::isnan( start.z() ) && std::isnan( end.z() ) );
53 : 41 : return closed;
54 : 45 : }
55 : :
56 : 230 : bool QgsCurve::isRing() const
57 : : {
58 : 230 : return ( isClosed() && numPoints() >= 4 );
59 : : }
60 : :
61 : 0 : QPainterPath QgsCurve::asQPainterPath() const
62 : : {
63 : 0 : QPainterPath p;
64 : 0 : addToPainterPath( p );
65 : 0 : return p;
66 : 0 : }
67 : :
68 : 62 : QgsCoordinateSequence QgsCurve::coordinateSequence() const
69 : : {
70 : 62 : QgsCoordinateSequence sequence;
71 : 62 : sequence.append( QgsRingSequence() );
72 : 62 : sequence.back().append( QgsPointSequence() );
73 : 62 : points( sequence.back().back() );
74 : :
75 : 62 : return sequence;
76 : 62 : }
77 : :
78 : 686 : bool QgsCurve::nextVertex( QgsVertexId &id, QgsPoint &vertex ) const
79 : : {
80 : 686 : if ( id.vertex < 0 )
81 : : {
82 : 132 : id.vertex = 0;
83 : 132 : if ( id.part < 0 )
84 : : {
85 : 27 : id.part = 0;
86 : 27 : }
87 : 132 : if ( id.ring < 0 )
88 : : {
89 : 27 : id.ring = 0;
90 : 27 : }
91 : 132 : }
92 : : else
93 : : {
94 : 554 : if ( id.vertex + 1 >= numPoints() )
95 : : {
96 : 86 : return false;
97 : : }
98 : 468 : ++id.vertex;
99 : : }
100 : 600 : return pointAt( id.vertex, vertex, id.type );
101 : 686 : }
102 : :
103 : 12 : void QgsCurve::adjacentVertices( QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex ) const
104 : : {
105 : 12 : int n = numPoints();
106 : 12 : if ( vertex.vertex < 0 || vertex.vertex >= n )
107 : : {
108 : 2 : previousVertex = QgsVertexId();
109 : 2 : nextVertex = QgsVertexId();
110 : 2 : return;
111 : : }
112 : :
113 : 10 : if ( vertex.vertex == 0 )
114 : : {
115 : 3 : previousVertex = QgsVertexId();
116 : 3 : }
117 : : else
118 : : {
119 : 7 : previousVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex - 1 );
120 : : }
121 : 10 : if ( vertex.vertex == n - 1 )
122 : : {
123 : 2 : nextVertex = QgsVertexId();
124 : 2 : }
125 : : else
126 : : {
127 : 8 : nextVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex + 1 );
128 : : }
129 : 12 : }
130 : :
131 : 76 : int QgsCurve::vertexNumberFromVertexId( QgsVertexId id ) const
132 : : {
133 : 76 : if ( id.part != 0 || id.ring != 0 )
134 : 9 : return -1;
135 : 67 : if ( id.vertex < 0 || id.vertex >= numPoints() )
136 : 18 : return -1;
137 : 49 : return id.vertex;
138 : 76 : }
139 : :
140 : 12 : QgsAbstractGeometry *QgsCurve::boundary() const
141 : : {
142 : 12 : if ( isEmpty() )
143 : 3 : return nullptr;
144 : :
145 : 9 : if ( isClosed() )
146 : 3 : return nullptr;
147 : :
148 : 6 : QgsMultiPoint *multiPoint = new QgsMultiPoint();
149 : 6 : multiPoint->reserve( 2 );
150 : 6 : multiPoint->addGeometry( new QgsPoint( startPoint() ) );
151 : 6 : multiPoint->addGeometry( new QgsPoint( endPoint() ) );
152 : 6 : return multiPoint;
153 : 12 : }
154 : :
155 : 10 : QString QgsCurve::asKml( int precision ) const
156 : : {
157 : 10 : std::unique_ptr<QgsLineString> lineString( curveToLine() );
158 : 10 : if ( !lineString )
159 : : {
160 : 0 : return QString();
161 : : }
162 : 10 : QString kml = lineString->asKml( precision );
163 : 10 : return kml;
164 : 10 : }
165 : :
166 : 16 : QgsCurve *QgsCurve::segmentize( double tolerance, SegmentationToleranceType toleranceType ) const
167 : : {
168 : 16 : return curveToLine( tolerance, toleranceType );
169 : : }
170 : :
171 : 981 : int QgsCurve::vertexCount( int part, int ring ) const
172 : : {
173 : : Q_UNUSED( part )
174 : : Q_UNUSED( ring )
175 : 981 : return numPoints();
176 : : }
177 : :
178 : 286 : int QgsCurve::ringCount( int part ) const
179 : : {
180 : : Q_UNUSED( part )
181 : 286 : return numPoints() > 0 ? 1 : 0;
182 : : }
183 : :
184 : 222 : int QgsCurve::partCount() const
185 : : {
186 : 222 : return numPoints() > 0 ? 1 : 0;
187 : : }
188 : :
189 : 12615 : QgsPoint QgsCurve::vertexAt( QgsVertexId id ) const
190 : : {
191 : 12615 : QgsPoint v;
192 : : QgsVertexId::VertexType type;
193 : 12615 : pointAt( id.vertex, v, type );
194 : 12615 : return v;
195 : 12615 : }
196 : :
197 : 3 : QgsCurve *QgsCurve::toCurveType() const
198 : : {
199 : 3 : return clone();
200 : : }
201 : :
202 : 1510 : QgsRectangle QgsCurve::boundingBox() const
203 : : {
204 : 1510 : if ( mBoundingBox.isNull() )
205 : : {
206 : 1270 : mBoundingBox = calculateBoundingBox();
207 : 1270 : }
208 : 1510 : return mBoundingBox;
209 : : }
210 : :
211 : 11 : bool QgsCurve::isValid( QString &error, int flags ) const
212 : : {
213 : 11 : if ( flags == 0 && mHasCachedValidity )
214 : : {
215 : : // use cached validity results
216 : 5 : error = mValidityFailureReason;
217 : 5 : return error.isEmpty();
218 : : }
219 : :
220 : 6 : QgsGeos geos( this );
221 : 6 : bool res = geos.isValid( &error, flags & QgsGeometry::FlagAllowSelfTouchingHoles, nullptr );
222 : 6 : if ( flags == 0 )
223 : : {
224 : 6 : mValidityFailureReason = !res ? error : QString();
225 : 6 : mHasCachedValidity = true;
226 : 6 : }
227 : 6 : return res;
228 : 11 : }
229 : :
230 : 2 : QPolygonF QgsCurve::asQPolygonF() const
231 : : {
232 : 2 : std::unique_ptr< QgsLineString > segmentized( curveToLine() );
233 : 2 : return segmentized->asQPolygonF();
234 : 2 : }
235 : :
236 : 0 : double QgsCurve::straightDistance2d() const
237 : : {
238 : 0 : return startPoint().distance( endPoint() );
239 : 0 : }
240 : :
241 : 0 : double QgsCurve::sinuosity() const
242 : : {
243 : 0 : double d = straightDistance2d();
244 : 0 : if ( qgsDoubleNear( d, 0.0 ) )
245 : 0 : return std::numeric_limits<double>::quiet_NaN();
246 : :
247 : 0 : return length() / d;
248 : 0 : }
249 : :
250 : 17 : QgsCurve::Orientation QgsCurve::orientation() const
251 : : {
252 : 17 : double a = 0;
253 : 17 : sumUpArea( a );
254 : 17 : return a < 0 ? Clockwise : CounterClockwise;
255 : : }
256 : :
257 : 4975 : void QgsCurve::clearCache() const
258 : : {
259 : 4975 : mBoundingBox = QgsRectangle();
260 : 4975 : mHasCachedValidity = false;
261 : 4975 : mValidityFailureReason.clear();
262 : 4975 : QgsAbstractGeometry::clearCache();
263 : 4975 : }
264 : :
265 : 239 : int QgsCurve::childCount() const
266 : : {
267 : 239 : return numPoints();
268 : : }
269 : :
270 : 227 : QgsPoint QgsCurve::childPoint( int index ) const
271 : : {
272 : 227 : QgsPoint point;
273 : : QgsVertexId::VertexType type;
274 : 227 : bool res = pointAt( index, point, type );
275 : : Q_ASSERT( res );
276 : : Q_UNUSED( res )
277 : 227 : return point;
278 : 227 : }
279 : :
280 : 28 : bool QgsCurve::snapToGridPrivate( double hSpacing, double vSpacing, double dSpacing, double mSpacing,
281 : : const QVector<double> &srcX, const QVector<double> &srcY, const QVector<double> &srcZ, const QVector<double> &srcM,
282 : : QVector<double> &outX, QVector<double> &outY, QVector<double> &outZ, QVector<double> &outM ) const
283 : : {
284 : 28 : int length = numPoints();
285 : :
286 : 28 : if ( length <= 0 )
287 : 0 : return false;
288 : :
289 : 28 : bool hasZ = is3D();
290 : 28 : bool hasM = isMeasure();
291 : :
292 : : // helper functions
293 : 123 : auto roundVertex = [hSpacing, vSpacing, dSpacing, mSpacing, hasZ, hasM, &srcX, &srcY, &srcZ, &srcM]( QgsPoint & out, int i )
294 : : {
295 : 95 : if ( hSpacing > 0 )
296 : 89 : out.setX( std::round( srcX.at( i ) / hSpacing ) * hSpacing );
297 : : else
298 : 6 : out.setX( srcX.at( i ) );
299 : :
300 : 95 : if ( vSpacing > 0 )
301 : 89 : out.setY( std::round( srcY.at( i ) / vSpacing ) * vSpacing );
302 : : else
303 : 6 : out.setY( srcY.at( i ) );
304 : :
305 : 95 : if ( hasZ )
306 : : {
307 : 18 : if ( dSpacing > 0 )
308 : 12 : out.setZ( std::round( srcZ.at( i ) / dSpacing ) * dSpacing );
309 : : else
310 : 6 : out.setZ( srcZ.at( i ) );
311 : 18 : }
312 : :
313 : 95 : if ( hasM )
314 : : {
315 : 18 : if ( mSpacing > 0 )
316 : 12 : out.setM( std::round( srcM.at( i ) / mSpacing ) * mSpacing );
317 : : else
318 : 6 : out.setM( srcM.at( i ) );
319 : 18 : }
320 : 95 : };
321 : :
322 : :
323 : 116 : auto append = [hasZ, hasM, &outX, &outY, &outM, &outZ]( QgsPoint const & point )
324 : : {
325 : 88 : outX.append( point.x() );
326 : :
327 : 88 : outY.append( point.y() );
328 : :
329 : 88 : if ( hasZ )
330 : 18 : outZ.append( point.z() );
331 : :
332 : 88 : if ( hasM )
333 : 18 : outM.append( point.m() );
334 : 88 : };
335 : :
336 : 95 : auto isPointEqual = [dSpacing, mSpacing, hasZ, hasM]( const QgsPoint & a, const QgsPoint & b )
337 : : {
338 : 101 : return ( a.x() == b.x() )
339 : 67 : && ( a.y() == b.y() )
340 : 34 : && ( !hasZ || dSpacing <= 0 || a.z() == b.z() )
341 : 7 : && ( !hasM || mSpacing <= 0 || a.m() == b.m() );
342 : : };
343 : :
344 : : // temporary values
345 : 28 : QgsWkbTypes::Type pointType = QgsWkbTypes::zmType( QgsWkbTypes::Point, hasZ, hasM );
346 : 28 : QgsPoint last( pointType );
347 : 28 : QgsPoint current( pointType );
348 : :
349 : : // Actual code (what does all the work)
350 : 28 : roundVertex( last, 0 );
351 : 28 : append( last );
352 : :
353 : 95 : for ( int i = 1; i < length; ++i )
354 : : {
355 : 67 : roundVertex( current, i );
356 : 67 : if ( !isPointEqual( current, last ) )
357 : : {
358 : 60 : append( current );
359 : 60 : last = current;
360 : 60 : }
361 : 67 : }
362 : :
363 : : // if it's not closed, with 2 points you get a correct line
364 : : // if it is, you need at least 4 (3 + the vertex that closes)
365 : 28 : if ( outX.length() < 2 || ( isClosed() && outX.length() < 4 ) )
366 : 3 : return false;
367 : :
368 : 25 : return true;
369 : 28 : }
|