QGIS API Documentation  3.21.0-Master (5b68dc587e)
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 
26 bool QgsCurve::operator==( const QgsAbstractGeometry &other ) const
27 {
28  const QgsCurve *otherCurve = qgsgeometry_cast< const QgsCurve * >( &other );
29  if ( !otherCurve )
30  return false;
31 
32  return equals( *otherCurve );
33 }
34 
35 bool QgsCurve::operator!=( const QgsAbstractGeometry &other ) const
36 {
37  return !operator==( other );
38 }
39 
41 {
42  if ( numPoints() == 0 )
43  return false;
44 
45  //don't consider M-coordinates when testing closedness
46  const QgsPoint start = startPoint();
47  const QgsPoint end = endPoint();
48 
49  return qgsDoubleNear( start.x(), end.x() ) &&
50  qgsDoubleNear( start.y(), end.y() );
51 }
52 bool QgsCurve::isClosed() const
53 {
54  bool closed = isClosed2D();
55  if ( is3D() && closed )
56  {
57  const QgsPoint start = startPoint();
58  const QgsPoint end = endPoint();
59  closed &= qgsDoubleNear( start.z(), end.z() ) || ( std::isnan( start.z() ) && std::isnan( end.z() ) );
60  }
61  return closed;
62 }
63 
64 bool QgsCurve::isRing() const
65 {
66  return ( isClosed() && numPoints() >= 4 );
67 }
68 
69 QPainterPath QgsCurve::asQPainterPath() const
70 {
71  QPainterPath p;
72  addToPainterPath( p );
73  return p;
74 }
75 
77 {
78  QgsCoordinateSequence sequence;
79  sequence.append( QgsRingSequence() );
80  sequence.back().append( QgsPointSequence() );
81  points( sequence.back().back() );
82 
83  return sequence;
84 }
85 
86 bool QgsCurve::nextVertex( QgsVertexId &id, QgsPoint &vertex ) const
87 {
88  if ( id.vertex < 0 )
89  {
90  id.vertex = 0;
91  if ( id.part < 0 )
92  {
93  id.part = 0;
94  }
95  if ( id.ring < 0 )
96  {
97  id.ring = 0;
98  }
99  }
100  else
101  {
102  if ( id.vertex + 1 >= numPoints() )
103  {
104  return false;
105  }
106  ++id.vertex;
107  }
108  return pointAt( id.vertex, vertex, id.type );
109 }
110 
111 void QgsCurve::adjacentVertices( QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex ) const
112 {
113  const int n = numPoints();
114  if ( vertex.vertex < 0 || vertex.vertex >= n )
115  {
116  previousVertex = QgsVertexId();
118  return;
119  }
120 
121  if ( vertex.vertex == 0 )
122  {
123  previousVertex = QgsVertexId();
124  }
125  else
126  {
127  previousVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex - 1 );
128  }
129  if ( vertex.vertex == n - 1 )
130  {
132  }
133  else
134  {
135  nextVertex = QgsVertexId( vertex.part, vertex.ring, vertex.vertex + 1 );
136  }
137 }
138 
140 {
141  if ( id.part != 0 || id.ring != 0 )
142  return -1;
143  if ( id.vertex < 0 || id.vertex >= numPoints() )
144  return -1;
145  return id.vertex;
146 }
147 
149 {
150  if ( isEmpty() )
151  return nullptr;
152 
153  if ( isClosed() )
154  return nullptr;
155 
156  QgsMultiPoint *multiPoint = new QgsMultiPoint();
157  multiPoint->reserve( 2 );
158  multiPoint->addGeometry( new QgsPoint( startPoint() ) );
159  multiPoint->addGeometry( new QgsPoint( endPoint() ) );
160  return multiPoint;
161 }
162 
163 QString QgsCurve::asKml( int precision ) const
164 {
165  std::unique_ptr<QgsLineString> lineString( curveToLine() );
166  if ( !lineString )
167  {
168  return QString();
169  }
170  QString kml = lineString->asKml( precision );
171  return kml;
172 }
173 
174 QgsCurve *QgsCurve::segmentize( double tolerance, SegmentationToleranceType toleranceType ) const
175 {
176  return curveToLine( tolerance, toleranceType );
177 }
178 
179 int QgsCurve::vertexCount( int part, int ring ) const
180 {
181  Q_UNUSED( part )
182  Q_UNUSED( ring )
183  return numPoints();
184 }
185 
186 int QgsCurve::ringCount( int part ) const
187 {
188  Q_UNUSED( part )
189  return numPoints() > 0 ? 1 : 0;
190 }
191 
193 {
194  return numPoints() > 0 ? 1 : 0;
195 }
196 
198 {
199  QgsPoint v;
201  pointAt( id.vertex, v, type );
202  return v;
203 }
204 
206 {
207  return clone();
208 }
209 
211 {
212  if ( isEmpty() )
213  return;
214 
215  if ( !isClosed() )
216  {
217  return;
218  }
219 
220  int minCoordinateIndex = 0;
221  QgsPoint minCoord;
222  int i = 0;
223  for ( auto it = vertices_begin(); it != vertices_end(); ++it )
224  {
225  const QgsPoint vertex = *it;
226  if ( minCoord.isEmpty() || minCoord.compareTo( &vertex ) > 0 )
227  {
228  minCoord = vertex;
229  minCoordinateIndex = i;
230  }
231  i++;
232  }
233 
234  scroll( minCoordinateIndex );
235 }
236 
238 {
239  if ( mBoundingBox.isNull() )
240  {
242  }
243  return mBoundingBox;
244 }
245 
246 bool QgsCurve::isValid( QString &error, Qgis::GeometryValidityFlags flags ) const
247 {
248  if ( flags == 0 && mHasCachedValidity )
249  {
250  // use cached validity results
251  error = mValidityFailureReason;
252  return error.isEmpty();
253  }
254 
255  const QgsGeos geos( this );
256  const bool res = geos.isValid( &error, flags & Qgis::GeometryValidityFlag::AllowSelfTouchingHoles, nullptr );
257  if ( flags == 0 )
258  {
259  mValidityFailureReason = !res ? error : QString();
260  mHasCachedValidity = true;
261  }
262  return res;
263 }
264 
265 QPolygonF QgsCurve::asQPolygonF() const
266 {
267  std::unique_ptr< QgsLineString > segmentized( curveToLine() );
268  return segmentized->asQPolygonF();
269 }
270 
272 {
273  return startPoint().distance( endPoint() );
274 }
275 
276 double QgsCurve::sinuosity() const
277 {
278  const double d = straightDistance2d();
279  if ( qgsDoubleNear( d, 0.0 ) )
280  return std::numeric_limits<double>::quiet_NaN();
281 
282  return length() / d;
283 }
284 
286 {
287  double a = 0;
288  sumUpArea( a );
289  return a < 0 ? Clockwise : CounterClockwise;
290 }
291 
293 {
295  mHasCachedValidity = false;
296  mValidityFailureReason.clear();
298 }
299 
301 {
302  return numPoints();
303 }
304 
305 QgsPoint QgsCurve::childPoint( int index ) const
306 {
307  QgsPoint point;
309  const bool res = pointAt( index, point, type );
310  Q_ASSERT( res );
311  Q_UNUSED( res )
312  return point;
313 }
314 
315 bool QgsCurve::snapToGridPrivate( double hSpacing, double vSpacing, double dSpacing, double mSpacing,
316  const QVector<double> &srcX, const QVector<double> &srcY, const QVector<double> &srcZ, const QVector<double> &srcM,
317  QVector<double> &outX, QVector<double> &outY, QVector<double> &outZ, QVector<double> &outM ) const
318 {
319  const int length = numPoints();
320 
321  if ( length <= 0 )
322  return false;
323 
324  const bool hasZ = is3D();
325  const bool hasM = isMeasure();
326 
327  // helper functions
328  auto roundVertex = [hSpacing, vSpacing, dSpacing, mSpacing, hasZ, hasM, &srcX, &srcY, &srcZ, &srcM]( QgsPoint & out, int i )
329  {
330  if ( hSpacing > 0 )
331  out.setX( std::round( srcX.at( i ) / hSpacing ) * hSpacing );
332  else
333  out.setX( srcX.at( i ) );
334 
335  if ( vSpacing > 0 )
336  out.setY( std::round( srcY.at( i ) / vSpacing ) * vSpacing );
337  else
338  out.setY( srcY.at( i ) );
339 
340  if ( hasZ )
341  {
342  if ( dSpacing > 0 )
343  out.setZ( std::round( srcZ.at( i ) / dSpacing ) * dSpacing );
344  else
345  out.setZ( srcZ.at( i ) );
346  }
347 
348  if ( hasM )
349  {
350  if ( mSpacing > 0 )
351  out.setM( std::round( srcM.at( i ) / mSpacing ) * mSpacing );
352  else
353  out.setM( srcM.at( i ) );
354  }
355  };
356 
357 
358  auto append = [hasZ, hasM, &outX, &outY, &outM, &outZ]( QgsPoint const & point )
359  {
360  outX.append( point.x() );
361 
362  outY.append( point.y() );
363 
364  if ( hasZ )
365  outZ.append( point.z() );
366 
367  if ( hasM )
368  outM.append( point.m() );
369  };
370 
371  auto isPointEqual = [dSpacing, mSpacing, hasZ, hasM]( const QgsPoint & a, const QgsPoint & b )
372  {
373  return ( a.x() == b.x() )
374  && ( a.y() == b.y() )
375  && ( !hasZ || dSpacing <= 0 || a.z() == b.z() )
376  && ( !hasM || mSpacing <= 0 || a.m() == b.m() );
377  };
378 
379  // temporary values
380  const QgsWkbTypes::Type pointType = QgsWkbTypes::zmType( QgsWkbTypes::Point, hasZ, hasM );
381  QgsPoint last( pointType );
382  QgsPoint current( pointType );
383 
384  // Actual code (what does all the work)
385  roundVertex( last, 0 );
386  append( last );
387 
388  for ( int i = 1; i < length; ++i )
389  {
390  roundVertex( current, i );
391  if ( !isPointEqual( current, last ) )
392  {
393  append( current );
394  last = current;
395  }
396  }
397 
398  // if it's not closed, with 2 points you get a correct line
399  // if it is, you need at least 4 (3 + the vertex that closes)
400  if ( outX.length() < 2 || ( isClosed() && outX.length() < 4 ) )
401  return false;
402 
403  return true;
404 }
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.
QgsCoordinateSequence coordinateSequence() const override
Retrieves the sequence of geometries, rings and nodes.
Definition: qgscurve.cpp:76
QgsRectangle boundingBox() const override
Returns the minimal bounding box for the geometry.
Definition: qgscurve.cpp:237
void normalize() final SIP_HOLDGIL
Reorganizes the geometry into a normalized form (or "canonical" form).
Definition: qgscurve.cpp:210
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:292
double sinuosity() const
Returns the curve sinuosity, which is the ratio of the curve length() to curve straightDistance2d().
Definition: qgscurve.cpp:276
QPainterPath asQPainterPath() const override
Returns the geometry represented as a QPainterPath.
Definition: qgscurve.cpp:69
QgsPoint childPoint(int index) const override
Returns point at index (for geometries without child geometries - i.e.
Definition: qgscurve.cpp:305
int vertexNumberFromVertexId(QgsVertexId id) const override
Returns the vertex number corresponding to a vertex id.
Definition: qgscurve.cpp:139
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:192
QgsCurve * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
Definition: qgscurve.cpp:174
virtual bool isClosed() const SIP_HOLDGIL
Returns true if the curve is closed.
Definition: qgscurve.cpp:52
int childCount() const override
Returns number of child geometries (for geometries with child geometries) or child points (for geomet...
Definition: qgscurve.cpp:300
virtual bool isRing() const SIP_HOLDGIL
Returns true if the curve is a ring.
Definition: qgscurve.cpp:64
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:315
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:246
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.
Orientation orientation() const
Returns the curve's orientation, e.g.
Definition: qgscurve.cpp:285
int vertexCount(int part=0, int ring=0) const override
Returns the number of vertices of which this geometry is built.
Definition: qgscurve.cpp:179
QgsPoint vertexAt(QgsVertexId id) const override
Returns the point corresponding to a specified vertex id.
Definition: qgscurve.cpp:197
QgsAbstractGeometry * boundary() const override
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
Definition: qgscurve.cpp:148
virtual QPolygonF asQPolygonF() const
Returns a QPolygonF representing the points.
Definition: qgscurve.cpp:265
void adjacentVertices(QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex) const override
Returns the vertices adjacent to a specified vertex within a geometry.
Definition: qgscurve.cpp:111
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:205
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:26
virtual bool isClosed2D() const SIP_HOLDGIL
Returns true if the curve is closed.
Definition: qgscurve.cpp:40
QString asKml(int precision=17) const override
Returns a KML representation of the geometry.
Definition: qgscurve.cpp:163
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
Orientation
Curve orientation.
Definition: qgscurve.h:265
@ Clockwise
Clockwise orientation.
Definition: qgscurve.h:266
@ CounterClockwise
Counter-clockwise orientation.
Definition: qgscurve.h:267
int ringCount(int part=0) const override
Returns the number of rings of which this geometry is built.
Definition: qgscurve.cpp:186
virtual QgsPoint startPoint() const =0
Returns the starting point of the curve.
bool operator!=(const QgsAbstractGeometry &other) const override
Definition: qgscurve.cpp:35
double straightDistance2d() const
Returns the straight distance of the curve, i.e.
Definition: qgscurve.cpp:271
bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const override
Returns next vertex id and coordinates.
Definition: qgscurve.cpp:86
QgsRectangle mBoundingBox
Cached bounding box.
Definition: qgscurve.h:349
virtual bool pointAt(int node, QgsPoint &point, QgsVertexId::VertexType &type) const =0
Returns the point and vertex id of a point within the curve.
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:801
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:1234
QVector< QgsRingSequence > QgsCoordinateSequence
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
int precision
Utility class for identifying a unique vertex within a geometry.
int vertex
Vertex number.
int part
Part number.
int ring
Ring number.
VertexType
Type of vertex.