QGIS API Documentation  3.23.0-Master (b5237dafc3)
qgscurve.cpp
Go to the documentation of this file.
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 #include "qgsvertexid.h"
26 
27 bool QgsCurve::operator==( const QgsAbstractGeometry &other ) const
28 {
29  const QgsCurve *otherCurve = qgsgeometry_cast< const QgsCurve * >( &other );
30  if ( !otherCurve )
31  return false;
32 
33  return equals( *otherCurve );
34 }
35 
36 bool QgsCurve::operator!=( const QgsAbstractGeometry &other ) const
37 {
38  return !operator==( other );
39 }
40 
42 {
43  if ( numPoints() == 0 )
44  return false;
45 
46  //don't consider M-coordinates when testing closedness
47  const QgsPoint start = startPoint();
48  const QgsPoint end = endPoint();
49 
50  return qgsDoubleNear( start.x(), end.x() ) &&
51  qgsDoubleNear( start.y(), end.y() );
52 }
53 bool QgsCurve::isClosed() const
54 {
55  bool closed = isClosed2D();
56  if ( is3D() && closed )
57  {
58  const QgsPoint start = startPoint();
59  const QgsPoint end = endPoint();
60  closed &= qgsDoubleNear( start.z(), end.z() ) || ( std::isnan( start.z() ) && std::isnan( end.z() ) );
61  }
62  return closed;
63 }
64 
65 bool QgsCurve::isRing() const
66 {
67  return ( isClosed() && numPoints() >= 4 );
68 }
69 
70 QPainterPath QgsCurve::asQPainterPath() const
71 {
72  QPainterPath p;
73  addToPainterPath( p );
74  return p;
75 }
76 
78 {
79  QgsCoordinateSequence sequence;
80  sequence.append( QgsRingSequence() );
81  sequence.back().append( QgsPointSequence() );
82  points( sequence.back().back() );
83 
84  return sequence;
85 }
86 
87 bool QgsCurve::nextVertex( QgsVertexId &id, QgsPoint &vertex ) const
88 {
89  if ( id.vertex < 0 )
90  {
91  id.vertex = 0;
92  if ( id.part < 0 )
93  {
94  id.part = 0;
95  }
96  if ( id.ring < 0 )
97  {
98  id.ring = 0;
99  }
100  }
101  else
102  {
103  if ( id.vertex + 1 >= numPoints() )
104  {
105  return false;
106  }
107  ++id.vertex;
108  }
109  return pointAt( id.vertex, vertex, id.type );
110 }
111 
112 void QgsCurve::adjacentVertices( QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex ) const
113 {
114  const int n = numPoints();
115  if ( vertex.vertex < 0 || vertex.vertex >= n )
116  {
117  previousVertex = QgsVertexId();
119  return;
120  }
121 
122  if ( vertex.vertex == 0 )
123  {
124  previousVertex = QgsVertexId();
125  }
126  else
127  {
128  previousVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex - 1 );
129  }
130  if ( vertex.vertex == n - 1 )
131  {
133  }
134  else
135  {
136  nextVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex + 1 );
137  }
138 }
139 
141 {
142  if ( id.part != 0 || id.ring != 0 )
143  return -1;
144  if ( id.vertex < 0 || id.vertex >= numPoints() )
145  return -1;
146  return id.vertex;
147 }
148 
150 {
151  if ( isEmpty() )
152  return nullptr;
153 
154  if ( isClosed() )
155  return nullptr;
156 
157  QgsMultiPoint *multiPoint = new QgsMultiPoint();
158  multiPoint->reserve( 2 );
159  multiPoint->addGeometry( new QgsPoint( startPoint() ) );
160  multiPoint->addGeometry( new QgsPoint( endPoint() ) );
161  return multiPoint;
162 }
163 
164 QString QgsCurve::asKml( int precision ) const
165 {
166  std::unique_ptr<QgsLineString> lineString( curveToLine() );
167  if ( !lineString )
168  {
169  return QString();
170  }
171  QString kml = lineString->asKml( precision );
172  return kml;
173 }
174 
175 QgsCurve *QgsCurve::segmentize( double tolerance, SegmentationToleranceType toleranceType ) const
176 {
177  return curveToLine( tolerance, toleranceType );
178 }
179 
180 int QgsCurve::vertexCount( int part, int ring ) const
181 {
182  Q_UNUSED( part )
183  Q_UNUSED( ring )
184  return numPoints();
185 }
186 
187 int QgsCurve::ringCount( int part ) const
188 {
189  Q_UNUSED( part )
190  return numPoints() > 0 ? 1 : 0;
191 }
192 
194 {
195  return numPoints() > 0 ? 1 : 0;
196 }
197 
199 {
200  QgsPoint v;
201  Qgis::VertexType type;
202  pointAt( id.vertex, v, type );
203  return v;
204 }
205 
207 {
208  return clone();
209 }
210 
212 {
213  if ( isEmpty() )
214  return;
215 
216  if ( !isClosed() )
217  {
218  return;
219  }
220 
221  int minCoordinateIndex = 0;
222  QgsPoint minCoord;
223  int i = 0;
224  for ( auto it = vertices_begin(); it != vertices_end(); ++it )
225  {
226  const QgsPoint vertex = *it;
227  if ( minCoord.isEmpty() || minCoord.compareTo( &vertex ) > 0 )
228  {
229  minCoord = vertex;
230  minCoordinateIndex = i;
231  }
232  i++;
233  }
234 
235  scroll( minCoordinateIndex );
236 }
237 
239 {
240  if ( mBoundingBox.isNull() )
241  {
243  }
244  return mBoundingBox;
245 }
246 
247 bool QgsCurve::isValid( QString &error, Qgis::GeometryValidityFlags flags ) const
248 {
249  if ( flags == 0 && mHasCachedValidity )
250  {
251  // use cached validity results
252  error = mValidityFailureReason;
253  return error.isEmpty();
254  }
255 
256  const QgsGeos geos( this );
257  const bool res = geos.isValid( &error, flags & Qgis::GeometryValidityFlag::AllowSelfTouchingHoles, nullptr );
258  if ( flags == 0 )
259  {
260  mValidityFailureReason = !res ? error : QString();
261  mHasCachedValidity = true;
262  }
263  return res;
264 }
265 
266 QPolygonF QgsCurve::asQPolygonF() const
267 {
268  std::unique_ptr< QgsLineString > segmentized( curveToLine() );
269  return segmentized->asQPolygonF();
270 }
271 
273 {
274  return startPoint().distance( endPoint() );
275 }
276 
277 double QgsCurve::sinuosity() const
278 {
279  const double d = straightDistance2d();
280  if ( qgsDoubleNear( d, 0.0 ) )
281  return std::numeric_limits<double>::quiet_NaN();
282 
283  return length() / d;
284 }
285 
287 {
288  double a = 0;
289  sumUpArea( a );
291 }
292 
294 {
296  mHasCachedValidity = false;
297  mValidityFailureReason.clear();
299 }
300 
302 {
303  return numPoints();
304 }
305 
306 QgsPoint QgsCurve::childPoint( int index ) const
307 {
308  QgsPoint point;
309  Qgis::VertexType type;
310  const bool res = pointAt( index, point, type );
311  Q_ASSERT( res );
312  Q_UNUSED( res )
313  return point;
314 }
315 
316 bool QgsCurve::snapToGridPrivate( double hSpacing, double vSpacing, double dSpacing, double mSpacing,
317  const QVector<double> &srcX, const QVector<double> &srcY, const QVector<double> &srcZ, const QVector<double> &srcM,
318  QVector<double> &outX, QVector<double> &outY, QVector<double> &outZ, QVector<double> &outM ) const
319 {
320  const int length = numPoints();
321 
322  if ( length <= 0 )
323  return false;
324 
325  const bool hasZ = is3D();
326  const bool hasM = isMeasure();
327 
328  // helper functions
329  auto roundVertex = [hSpacing, vSpacing, dSpacing, mSpacing, hasZ, hasM, &srcX, &srcY, &srcZ, &srcM]( QgsPoint & out, int i )
330  {
331  if ( hSpacing > 0 )
332  out.setX( std::round( srcX.at( i ) / hSpacing ) * hSpacing );
333  else
334  out.setX( srcX.at( i ) );
335 
336  if ( vSpacing > 0 )
337  out.setY( std::round( srcY.at( i ) / vSpacing ) * vSpacing );
338  else
339  out.setY( srcY.at( i ) );
340 
341  if ( hasZ )
342  {
343  if ( dSpacing > 0 )
344  out.setZ( std::round( srcZ.at( i ) / dSpacing ) * dSpacing );
345  else
346  out.setZ( srcZ.at( i ) );
347  }
348 
349  if ( hasM )
350  {
351  if ( mSpacing > 0 )
352  out.setM( std::round( srcM.at( i ) / mSpacing ) * mSpacing );
353  else
354  out.setM( srcM.at( i ) );
355  }
356  };
357 
358 
359  auto append = [hasZ, hasM, &outX, &outY, &outM, &outZ]( QgsPoint const & point )
360  {
361  outX.append( point.x() );
362 
363  outY.append( point.y() );
364 
365  if ( hasZ )
366  outZ.append( point.z() );
367 
368  if ( hasM )
369  outM.append( point.m() );
370  };
371 
372  auto isPointEqual = [dSpacing, mSpacing, hasZ, hasM]( const QgsPoint & a, const QgsPoint & b )
373  {
374  return ( a.x() == b.x() )
375  && ( a.y() == b.y() )
376  && ( !hasZ || dSpacing <= 0 || a.z() == b.z() )
377  && ( !hasM || mSpacing <= 0 || a.m() == b.m() );
378  };
379 
380  // temporary values
381  const QgsWkbTypes::Type pointType = QgsWkbTypes::zmType( QgsWkbTypes::Point, hasZ, hasM );
382  QgsPoint last( pointType );
383  QgsPoint current( pointType );
384 
385  // Actual code (what does all the work)
386  roundVertex( last, 0 );
387  append( last );
388 
389  for ( int i = 1; i < length; ++i )
390  {
391  roundVertex( current, i );
392  if ( !isPointEqual( current, last ) )
393  {
394  append( current );
395  last = current;
396  }
397  }
398 
399  // if it's not closed, with 2 points you get a correct line
400  // if it is, you need at least 4 (3 + the vertex that closes)
401  if ( outX.length() < 2 || ( isClosed() && outX.length() < 4 ) )
402  return false;
403 
404  return true;
405 }
AngularDirection
Angular directions.
Definition: qgis.h:1247
@ CounterClockwise
Counter-clockwise direction.
@ Clockwise
Clockwise direction.
VertexType
Types of vertex.
Definition: qgis.h:1008
Abstract base class for all geometries.
SegmentationToleranceType
Segmentation tolerance as maximum angle or maximum difference between approximation and circle.
vertex_iterator vertices_end() const
Returns STL-style iterator pointing to the imaginary vertex after the last vertex of the geometry.
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
virtual QgsRectangle calculateBoundingBox() const
Default calculator for the minimal bounding box for the geometry.
virtual void clearCache() const
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
virtual double length() const
Returns the planar, 2-dimensional length of the geometry.
virtual bool isEmpty() const
Returns true if the geometry is empty.
vertex_iterator vertices_begin() const
Returns STL-style iterator pointing to the first vertex of the geometry.
virtual int compareTo(const QgsAbstractGeometry *other) const
Comparator for sorting of geometry.
bool isMeasure() const SIP_HOLDGIL
Returns true if the geometry contains m values.
Abstract base class for curved geometry type.
Definition: qgscurve.h:36
virtual bool equals(const QgsCurve &other) const =0
Checks whether this curve exactly equals another curve.
Qgis::AngularDirection orientation() const
Returns the curve's orientation, e.g.
Definition: qgscurve.cpp:286
QgsCoordinateSequence coordinateSequence() const override
Retrieves the sequence of geometries, rings and nodes.
Definition: qgscurve.cpp:77
QgsRectangle boundingBox() const override
Returns the minimal bounding box for the geometry.
Definition: qgscurve.cpp:238
void normalize() final SIP_HOLDGIL
Reorganizes the geometry into a normalized form (or "canonical" form).
Definition: qgscurve.cpp:211
virtual int numPoints() const =0
Returns the number of points in the curve.
void clearCache() const override
Clears any cached parameters associated with the geometry, e.g., bounding boxes.
Definition: qgscurve.cpp:293
double sinuosity() const
Returns the curve sinuosity, which is the ratio of the curve length() to curve straightDistance2d().
Definition: qgscurve.cpp:277
QPainterPath asQPainterPath() const override
Returns the geometry represented as a QPainterPath.
Definition: qgscurve.cpp:70
QgsPoint childPoint(int index) const override
Returns point at index (for geometries without child geometries - i.e.
Definition: qgscurve.cpp:306
int vertexNumberFromVertexId(QgsVertexId id) const override
Returns the vertex number corresponding to a vertex id.
Definition: qgscurve.cpp:140
virtual void scroll(int firstVertexIndex)=0
Scrolls the curve vertices so that they start with the vertex at the given index.
int partCount() const override
Returns count of parts contained in the geometry.
Definition: qgscurve.cpp:193
QgsCurve * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
Definition: qgscurve.cpp:175
virtual bool pointAt(int node, QgsPoint &point, Qgis::VertexType &type) const =0
Returns the point and vertex id of a point within the curve.
virtual bool isClosed() const SIP_HOLDGIL
Returns true if the curve is closed.
Definition: qgscurve.cpp:53
int childCount() const override
Returns number of child geometries (for geometries with child geometries) or child points (for geomet...
Definition: qgscurve.cpp:301
virtual bool isRing() const SIP_HOLDGIL
Returns true if the curve is a ring.
Definition: qgscurve.cpp:65
bool snapToGridPrivate(double hSpacing, double vSpacing, double dSpacing, double mSpacing, const QVector< double > &srcX, const QVector< double > &srcY, const QVector< double > &srcZ, const QVector< double > &srcM, QVector< double > &outX, QVector< double > &outY, QVector< double > &outZ, QVector< double > &outM) const
Helper function for QgsCurve subclasses to snap to grids.
Definition: qgscurve.cpp:316
bool isValid(QString &error, Qgis::GeometryValidityFlags flags=Qgis::GeometryValidityFlags()) const override
Checks validity of the geometry, and returns true if the geometry is valid.
Definition: qgscurve.cpp:247
virtual QgsLineString * curveToLine(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const =0
Returns a new line string geometry corresponding to a segmentized approximation of the curve.
int vertexCount(int part=0, int ring=0) const override
Returns the number of vertices of which this geometry is built.
Definition: qgscurve.cpp:180
QgsPoint vertexAt(QgsVertexId id) const override
Returns the point corresponding to a specified vertex id.
Definition: qgscurve.cpp:198
QgsAbstractGeometry * boundary() const override
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
Definition: qgscurve.cpp:149
virtual QPolygonF asQPolygonF() const
Returns a QPolygonF representing the points.
Definition: qgscurve.cpp:266
void adjacentVertices(QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex) const override
Returns the vertices adjacent to a specified vertex within a geometry.
Definition: qgscurve.cpp:112
virtual void addToPainterPath(QPainterPath &path) const =0
Adds a curve to a painter path.
QgsCurve * toCurveType() const override
Returns the geometry converted to the more generic curve type.
Definition: qgscurve.cpp:206
virtual void sumUpArea(double &sum) const =0
Sums up the area of the curve by iterating over the vertices (shoelace formula).
bool operator==(const QgsAbstractGeometry &other) const override
Definition: qgscurve.cpp:27
virtual bool isClosed2D() const SIP_HOLDGIL
Returns true if the curve is closed.
Definition: qgscurve.cpp:41
QString asKml(int precision=17) const override
Returns a KML representation of the geometry.
Definition: qgscurve.cpp:164
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
int ringCount(int part=0) const override
Returns the number of rings of which this geometry is built.
Definition: qgscurve.cpp:187
virtual QgsPoint startPoint() const =0
Returns the starting point of the curve.
bool operator!=(const QgsAbstractGeometry &other) const override
Definition: qgscurve.cpp:36
double straightDistance2d() const
Returns the straight distance of the curve, i.e.
Definition: qgscurve.cpp:272
bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const override
Returns next vertex id and coordinates.
Definition: qgscurve.cpp:87
QgsRectangle mBoundingBox
Cached bounding box.
Definition: qgscurve.h:342
virtual QgsPoint endPoint() const =0
Returns the end point of the curve.
virtual void points(QgsPointSequence &pt) const =0
Returns a list of points within the curve.
void reserve(int size) SIP_HOLDGIL
Attempts to allocate memory for at least size geometries.
Does vector analysis using the geos library and handles import, export, exception handling*.
Definition: qgsgeos.h:104
Multi point geometry collection.
Definition: qgsmultipoint.h:30
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
double distance(double x, double y) const SIP_HOLDGIL
Returns the Cartesian 2D distance between this point and a specified x, y coordinate.
Definition: qgspoint.h:343
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
Definition: qgspoint.cpp:767
Q_GADGET double x
Definition: qgspoint.h:52
double z
Definition: qgspoint.h:54
double y
Definition: qgspoint.h:53
A rectangle specified with double values.
Definition: qgsrectangle.h:42
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:479
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
static Type zmType(Type type, bool hasZ, bool hasM) SIP_HOLDGIL
Returns the modified input geometry type according to hasZ / hasM.
Definition: qgswkbtypes.h:831
Contains geos related utilities and functions.
Definition: qgsgeos.h:42
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:1533
QVector< QgsRingSequence > QgsCoordinateSequence
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
int precision
Utility class for identifying a unique vertex within a geometry.
Definition: qgsvertexid.h:31
int vertex
Vertex number.
Definition: qgsvertexid.h:95
int part
Part number.
Definition: qgsvertexid.h:89
int ring
Ring number.
Definition: qgsvertexid.h:92