QGIS API Documentation  3.14.0-Pi (9f7028fd23)
qgsinternalgeometryengine.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsinternalgeometryengine.cpp - QgsInternalGeometryEngine
3 
4  ---------------------
5  begin : 13.1.2016
6  copyright : (C) 2016 by Matthias Kuhn
7  email : [email protected]
8  ***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
16 
18 
19 #include "qgslinestring.h"
20 #include "qgsmultipolygon.h"
21 #include "qgspolygon.h"
22 #include "qgsmulticurve.h"
23 #include "qgscircularstring.h"
24 #include "qgsgeometry.h"
25 #include "qgsgeometryutils.h"
26 #include "qgslinesegment.h"
27 #include "qgscircle.h"
28 #include "qgslogger.h"
29 #include "qgstessellator.h"
30 #include "qgsfeedback.h"
31 #include <QTransform>
32 #include <functional>
33 #include <memory>
34 #include <queue>
35 #include <random>
36 
38  : mGeometry( geometry.constGet() )
39 {
40 
41 }
42 
43 /***************************************************************************
44  * This class is considered CRITICAL and any change MUST be accompanied with
45  * full unit tests.
46  * See details in QEP #17
47  ****************************************************************************/
48 
50 {
51  QVector<QgsLineString *> linesToProcess;
52 
53  const QgsMultiCurve *multiCurve = qgsgeometry_cast< const QgsMultiCurve * >( mGeometry );
54  if ( multiCurve )
55  {
56  linesToProcess.reserve( multiCurve->partCount() );
57  for ( int i = 0; i < multiCurve->partCount(); ++i )
58  {
59  linesToProcess << static_cast<QgsLineString *>( multiCurve->geometryN( i )->clone() );
60  }
61  }
62 
63  const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( mGeometry );
64  if ( curve )
65  {
66  linesToProcess << static_cast<QgsLineString *>( curve->segmentize() );
67  }
68 
69  std::unique_ptr<QgsMultiPolygon> multipolygon( linesToProcess.size() > 1 ? new QgsMultiPolygon() : nullptr );
70  QgsPolygon *polygon = nullptr;
71 
72  if ( !linesToProcess.empty() )
73  {
74  std::unique_ptr< QgsLineString > secondline;
75  for ( QgsLineString *line : qgis::as_const( linesToProcess ) )
76  {
77  QTransform transform = QTransform::fromTranslate( x, y );
78 
79  secondline.reset( line->reversed() );
80  secondline->transform( transform );
81 
82  line->append( secondline.get() );
83  line->addVertex( line->pointN( 0 ) );
84 
85  polygon = new QgsPolygon();
86  polygon->setExteriorRing( line );
87 
88  if ( multipolygon )
89  multipolygon->addGeometry( polygon );
90  }
91 
92  if ( multipolygon )
93  return QgsGeometry( multipolygon.release() );
94  else
95  return QgsGeometry( polygon );
96  }
97 
98  return QgsGeometry();
99 }
100 
101 
102 
103 // polylabel implementation
104 // ported from the original JavaScript implementation developed by Vladimir Agafonkin
105 // originally licensed under the ISC License
106 
108 class Cell
109 {
110  public:
111  Cell( double x, double y, double h, const QgsPolygon *polygon )
112  : x( x )
113  , y( y )
114  , h( h )
115  , d( polygon->pointDistanceToBoundary( x, y ) )
116  , max( d + h * M_SQRT2 )
117  {}
118 
120  double x;
122  double y;
124  double h;
126  double d;
128  double max;
129 };
130 
131 struct GreaterThanByMax
132 {
133  bool operator()( const Cell *lhs, const Cell *rhs )
134  {
135  return rhs->max > lhs->max;
136  }
137 };
138 
139 Cell *getCentroidCell( const QgsPolygon *polygon )
140 {
141  double area = 0;
142  double x = 0;
143  double y = 0;
144 
145  const QgsLineString *exterior = static_cast< const QgsLineString *>( polygon->exteriorRing() );
146  int len = exterior->numPoints() - 1; //assume closed
147  for ( int i = 0, j = len - 1; i < len; j = i++ )
148  {
149  double aX = exterior->xAt( i );
150  double aY = exterior->yAt( i );
151  double bX = exterior->xAt( j );
152  double bY = exterior->yAt( j );
153  double f = aX * bY - bX * aY;
154  x += ( aX + bX ) * f;
155  y += ( aY + bY ) * f;
156  area += f * 3;
157  }
158  if ( area == 0 )
159  return new Cell( exterior->xAt( 0 ), exterior->yAt( 0 ), 0, polygon );
160  else
161  return new Cell( x / area, y / area, 0.0, polygon );
162 }
163 
164 QgsPoint surfacePoleOfInaccessibility( const QgsSurface *surface, double precision, double &distanceFromBoundary )
165 {
166  std::unique_ptr< QgsPolygon > segmentizedPoly;
167  const QgsPolygon *polygon = qgsgeometry_cast< const QgsPolygon * >( surface );
168  if ( !polygon )
169  {
170  segmentizedPoly.reset( static_cast< QgsPolygon *>( surface->segmentize() ) );
171  polygon = segmentizedPoly.get();
172  }
173 
174  // start with the bounding box
175  QgsRectangle bounds = polygon->boundingBox();
176 
177  // initial parameters
178  double cellSize = std::min( bounds.width(), bounds.height() );
179 
180  if ( qgsDoubleNear( cellSize, 0.0 ) )
181  return QgsPoint( bounds.xMinimum(), bounds.yMinimum() );
182 
183  double h = cellSize / 2.0;
184  std::priority_queue< Cell *, std::vector<Cell *>, GreaterThanByMax > cellQueue;
185 
186  // cover polygon with initial cells
187  for ( double x = bounds.xMinimum(); x < bounds.xMaximum(); x += cellSize )
188  {
189  for ( double y = bounds.yMinimum(); y < bounds.yMaximum(); y += cellSize )
190  {
191  cellQueue.push( new Cell( x + h, y + h, h, polygon ) );
192  }
193  }
194 
195  // take centroid as the first best guess
196  std::unique_ptr< Cell > bestCell( getCentroidCell( polygon ) );
197 
198  // special case for rectangular polygons
199  std::unique_ptr< Cell > bboxCell( new Cell( bounds.xMinimum() + bounds.width() / 2.0,
200  bounds.yMinimum() + bounds.height() / 2.0,
201  0, polygon ) );
202  if ( bboxCell->d > bestCell->d )
203  {
204  bestCell = std::move( bboxCell );
205  }
206 
207  while ( !cellQueue.empty() )
208  {
209  // pick the most promising cell from the queue
210  std::unique_ptr< Cell > cell( cellQueue.top() );
211  cellQueue.pop();
212  Cell *currentCell = cell.get();
213 
214  // update the best cell if we found a better one
215  if ( currentCell->d > bestCell->d )
216  {
217  bestCell = std::move( cell );
218  }
219 
220  // do not drill down further if there's no chance of a better solution
221  if ( currentCell->max - bestCell->d <= precision )
222  continue;
223 
224  // split the cell into four cells
225  h = currentCell->h / 2.0;
226  cellQueue.push( new Cell( currentCell->x - h, currentCell->y - h, h, polygon ) );
227  cellQueue.push( new Cell( currentCell->x + h, currentCell->y - h, h, polygon ) );
228  cellQueue.push( new Cell( currentCell->x - h, currentCell->y + h, h, polygon ) );
229  cellQueue.push( new Cell( currentCell->x + h, currentCell->y + h, h, polygon ) );
230  }
231 
232  distanceFromBoundary = bestCell->d;
233 
234  return QgsPoint( bestCell->x, bestCell->y );
235 }
236 
238 
239 QgsGeometry QgsInternalGeometryEngine::poleOfInaccessibility( double precision, double *distanceFromBoundary ) const
240 {
241  if ( distanceFromBoundary )
242  *distanceFromBoundary = std::numeric_limits<double>::max();
243 
244  if ( !mGeometry || mGeometry->isEmpty() )
245  return QgsGeometry();
246 
247  if ( precision <= 0 )
248  return QgsGeometry();
249 
250  if ( const QgsGeometryCollection *gc = qgsgeometry_cast< const QgsGeometryCollection *>( mGeometry ) )
251  {
252  int numGeom = gc->numGeometries();
253  double maxDist = 0;
254  QgsPoint bestPoint;
255  bool found = false;
256  for ( int i = 0; i < numGeom; ++i )
257  {
258  const QgsSurface *surface = qgsgeometry_cast< const QgsSurface * >( gc->geometryN( i ) );
259  if ( !surface )
260  continue;
261 
262  found = true;
263  double dist = std::numeric_limits<double>::max();
264  QgsPoint p = surfacePoleOfInaccessibility( surface, precision, dist );
265  if ( dist > maxDist )
266  {
267  maxDist = dist;
268  bestPoint = p;
269  }
270  }
271 
272  if ( !found )
273  return QgsGeometry();
274 
275  if ( distanceFromBoundary )
276  *distanceFromBoundary = maxDist;
277  return QgsGeometry( new QgsPoint( bestPoint ) );
278  }
279  else
280  {
281  const QgsSurface *surface = qgsgeometry_cast< const QgsSurface * >( mGeometry );
282  if ( !surface )
283  return QgsGeometry();
284 
285  double dist = std::numeric_limits<double>::max();
286  QgsPoint p = surfacePoleOfInaccessibility( surface, precision, dist );
287  if ( distanceFromBoundary )
288  *distanceFromBoundary = dist;
289  return QgsGeometry( new QgsPoint( p ) );
290  }
291 }
292 
293 
294 // helpers for orthogonalize
295 // adapted from original code in potlatch/id osm editor
296 
297 bool dotProductWithinAngleTolerance( double dotProduct, double lowerThreshold, double upperThreshold )
298 {
299  return lowerThreshold > std::fabs( dotProduct ) || std::fabs( dotProduct ) > upperThreshold;
300 }
301 
302 double normalizedDotProduct( const QgsPoint &a, const QgsPoint &b, const QgsPoint &c )
303 {
304  QgsVector p = a - b;
305  QgsVector q = c - b;
306 
307  if ( p.length() > 0 )
308  p = p.normalized();
309  if ( q.length() > 0 )
310  q = q.normalized();
311 
312  return p * q;
313 }
314 
315 double squareness( QgsLineString *ring, double lowerThreshold, double upperThreshold )
316 {
317  double sum = 0.0;
318 
319  bool isClosed = ring->isClosed();
320  int numPoints = ring->numPoints();
321  QgsPoint a;
322  QgsPoint b;
323  QgsPoint c;
324 
325  for ( int i = 0; i < numPoints; ++i )
326  {
327  if ( !isClosed && i == numPoints - 1 )
328  break;
329  else if ( !isClosed && i == 0 )
330  {
331  b = ring->pointN( 0 );
332  c = ring->pointN( 1 );
333  }
334  else
335  {
336  if ( i == 0 )
337  {
338  a = ring->pointN( numPoints - 1 );
339  b = ring->pointN( 0 );
340  }
341  if ( i == numPoints - 1 )
342  c = ring->pointN( 0 );
343  else
344  c = ring->pointN( i + 1 );
345 
346  double dotProduct = normalizedDotProduct( a, b, c );
347  if ( !dotProductWithinAngleTolerance( dotProduct, lowerThreshold, upperThreshold ) )
348  continue;
349 
350  sum += 2.0 * std::min( std::fabs( dotProduct - 1.0 ), std::min( std::fabs( dotProduct ), std::fabs( dotProduct + 1 ) ) );
351  }
352  a = b;
353  b = c;
354  }
355 
356  return sum;
357 }
358 
359 QgsVector calcMotion( const QgsPoint &a, const QgsPoint &b, const QgsPoint &c,
360  double lowerThreshold, double upperThreshold )
361 {
362  QgsVector p = a - b;
363  QgsVector q = c - b;
364 
365  if ( qgsDoubleNear( p.length(), 0.0 ) || qgsDoubleNear( q.length(), 0.0 ) )
366  return QgsVector( 0, 0 );
367 
368  // 2.0 is a magic number from the original JOSM source code
369  double scale = 2.0 * std::min( p.length(), q.length() );
370 
371  p = p.normalized();
372  q = q.normalized();
373  double dotProduct = p * q;
374 
375  if ( !dotProductWithinAngleTolerance( dotProduct, lowerThreshold, upperThreshold ) )
376  {
377  return QgsVector( 0, 0 );
378  }
379 
380  // wonderful nasty hack which has survived through JOSM -> id -> QGIS
381  // to deal with almost-straight segments (angle is closer to 180 than to 90/270).
382  if ( dotProduct < -M_SQRT1_2 )
383  dotProduct += 1.0;
384 
385  QgsVector new_v = p + q;
386  // 0.1 magic number from JOSM implementation - think this is to limit each iterative step
387  return new_v.normalized() * ( 0.1 * dotProduct * scale );
388 }
389 
390 QgsLineString *doOrthogonalize( QgsLineString *ring, int iterations, double tolerance, double lowerThreshold, double upperThreshold )
391 {
392  double minScore = std::numeric_limits<double>::max();
393 
394  bool isClosed = ring->isClosed();
395  int numPoints = ring->numPoints();
396 
397  std::unique_ptr< QgsLineString > best( ring->clone() );
398 
399  QVector< QgsVector > /* yep */ motions;
400  motions.reserve( numPoints );
401 
402  for ( int it = 0; it < iterations; ++it )
403  {
404  motions.resize( 0 ); // avoid re-allocations
405 
406  // first loop through an calculate all motions
407  QgsPoint a;
408  QgsPoint b;
409  QgsPoint c;
410  for ( int i = 0; i < numPoints; ++i )
411  {
412  if ( isClosed && i == numPoints - 1 )
413  motions << motions.at( 0 ); //closed ring, so last point follows first point motion
414  else if ( !isClosed && ( i == 0 || i == numPoints - 1 ) )
415  {
416  b = ring->pointN( 0 );
417  c = ring->pointN( 1 );
418  motions << QgsVector( 0, 0 ); //non closed line, leave first/last vertex untouched
419  }
420  else
421  {
422  if ( i == 0 )
423  {
424  a = ring->pointN( numPoints - 1 );
425  b = ring->pointN( 0 );
426  }
427  if ( i == numPoints - 1 )
428  c = ring->pointN( 0 );
429  else
430  c = ring->pointN( i + 1 );
431 
432  motions << calcMotion( a, b, c, lowerThreshold, upperThreshold );
433  }
434  a = b;
435  b = c;
436  }
437 
438  // then apply them
439  for ( int i = 0; i < numPoints; ++i )
440  {
441  ring->setXAt( i, ring->xAt( i ) + motions.at( i ).x() );
442  ring->setYAt( i, ring->yAt( i ) + motions.at( i ).y() );
443  }
444 
445  double newScore = squareness( ring, lowerThreshold, upperThreshold );
446  if ( newScore < minScore )
447  {
448  best.reset( ring->clone() );
449  minScore = newScore;
450  }
451 
452  if ( minScore < tolerance )
453  break;
454  }
455 
456  delete ring;
457 
458  return best.release();
459 }
460 
461 
462 QgsAbstractGeometry *orthogonalizeGeom( const QgsAbstractGeometry *geom, int maxIterations, double tolerance, double lowerThreshold, double upperThreshold )
463 {
464  std::unique_ptr< QgsAbstractGeometry > segmentizedCopy;
465  if ( QgsWkbTypes::isCurvedType( geom->wkbType() ) )
466  {
467  segmentizedCopy.reset( geom->segmentize() );
468  geom = segmentizedCopy.get();
469  }
470 
472  {
473  return doOrthogonalize( static_cast< QgsLineString * >( geom->clone() ),
474  maxIterations, tolerance, lowerThreshold, upperThreshold );
475  }
476  else
477  {
478  // polygon
479  const QgsPolygon *polygon = static_cast< const QgsPolygon * >( geom );
480  QgsPolygon *result = new QgsPolygon();
481 
482  result->setExteriorRing( doOrthogonalize( static_cast< QgsLineString * >( polygon->exteriorRing()->clone() ),
483  maxIterations, tolerance, lowerThreshold, upperThreshold ) );
484  for ( int i = 0; i < polygon->numInteriorRings(); ++i )
485  {
486  result->addInteriorRing( doOrthogonalize( static_cast< QgsLineString * >( polygon->interiorRing( i )->clone() ),
487  maxIterations, tolerance, lowerThreshold, upperThreshold ) );
488  }
489 
490  return result;
491  }
492 }
493 
494 QgsGeometry QgsInternalGeometryEngine::orthogonalize( double tolerance, int maxIterations, double angleThreshold ) const
495 {
496  if ( !mGeometry || ( QgsWkbTypes::geometryType( mGeometry->wkbType() ) != QgsWkbTypes::LineGeometry
498  {
499  return QgsGeometry();
500  }
501 
502  double lowerThreshold = std::cos( ( 90 - angleThreshold ) * M_PI / 180.00 );
503  double upperThreshold = std::cos( angleThreshold * M_PI / 180.0 );
504 
505  if ( const QgsGeometryCollection *gc = qgsgeometry_cast< const QgsGeometryCollection *>( mGeometry ) )
506  {
507  int numGeom = gc->numGeometries();
508  QVector< QgsAbstractGeometry * > geometryList;
509  geometryList.reserve( numGeom );
510  for ( int i = 0; i < numGeom; ++i )
511  {
512  geometryList << orthogonalizeGeom( gc->geometryN( i ), maxIterations, tolerance, lowerThreshold, upperThreshold );
513  }
514 
515  QgsGeometry first = QgsGeometry( geometryList.takeAt( 0 ) );
516  for ( QgsAbstractGeometry *g : qgis::as_const( geometryList ) )
517  {
518  first.addPart( g );
519  }
520  return first;
521  }
522  else
523  {
524  return QgsGeometry( orthogonalizeGeom( mGeometry, maxIterations, tolerance, lowerThreshold, upperThreshold ) );
525  }
526 }
527 
528 // if extraNodesPerSegment < 0, then use distance based mode
529 QgsLineString *doDensify( const QgsLineString *ring, int extraNodesPerSegment = -1, double distance = 1 )
530 {
531  QVector< double > outX;
532  QVector< double > outY;
533  QVector< double > outZ;
534  QVector< double > outM;
535  double multiplier = 1.0 / double( extraNodesPerSegment + 1 );
536 
537  int nPoints = ring->numPoints();
538  outX.reserve( ( extraNodesPerSegment + 1 ) * nPoints );
539  outY.reserve( ( extraNodesPerSegment + 1 ) * nPoints );
540  bool withZ = ring->is3D();
541  if ( withZ )
542  outZ.reserve( ( extraNodesPerSegment + 1 ) * nPoints );
543  bool withM = ring->isMeasure();
544  if ( withM )
545  outM.reserve( ( extraNodesPerSegment + 1 ) * nPoints );
546  double x1 = 0;
547  double x2 = 0;
548  double y1 = 0;
549  double y2 = 0;
550  double z1 = 0;
551  double z2 = 0;
552  double m1 = 0;
553  double m2 = 0;
554  double xOut = 0;
555  double yOut = 0;
556  double zOut = 0;
557  double mOut = 0;
558  int extraNodesThisSegment = extraNodesPerSegment;
559  for ( int i = 0; i < nPoints - 1; ++i )
560  {
561  x1 = ring->xAt( i );
562  x2 = ring->xAt( i + 1 );
563  y1 = ring->yAt( i );
564  y2 = ring->yAt( i + 1 );
565  if ( withZ )
566  {
567  z1 = ring->zAt( i );
568  z2 = ring->zAt( i + 1 );
569  }
570  if ( withM )
571  {
572  m1 = ring->mAt( i );
573  m2 = ring->mAt( i + 1 );
574  }
575 
576  outX << x1;
577  outY << y1;
578  if ( withZ )
579  outZ << z1;
580  if ( withM )
581  outM << m1;
582 
583  if ( extraNodesPerSegment < 0 )
584  {
585  // distance mode
586  extraNodesThisSegment = std::floor( std::sqrt( ( x2 - x1 ) * ( x2 - x1 ) + ( y2 - y1 ) * ( y2 - y1 ) ) / distance );
587  if ( extraNodesThisSegment >= 1 )
588  multiplier = 1.0 / ( extraNodesThisSegment + 1 );
589  }
590 
591  for ( int j = 0; j < extraNodesThisSegment; ++j )
592  {
593  double delta = multiplier * ( j + 1 );
594  xOut = x1 + delta * ( x2 - x1 );
595  yOut = y1 + delta * ( y2 - y1 );
596  if ( withZ )
597  zOut = z1 + delta * ( z2 - z1 );
598  if ( withM )
599  mOut = m1 + delta * ( m2 - m1 );
600 
601  outX << xOut;
602  outY << yOut;
603  if ( withZ )
604  outZ << zOut;
605  if ( withM )
606  outM << mOut;
607  }
608  }
609  outX << ring->xAt( nPoints - 1 );
610  outY << ring->yAt( nPoints - 1 );
611  if ( withZ )
612  outZ << ring->zAt( nPoints - 1 );
613  if ( withM )
614  outM << ring->mAt( nPoints - 1 );
615 
616  QgsLineString *result = new QgsLineString( outX, outY, outZ, outM );
617  return result;
618 }
619 
620 QgsAbstractGeometry *densifyGeometry( const QgsAbstractGeometry *geom, int extraNodesPerSegment = 1, double distance = 1 )
621 {
622  std::unique_ptr< QgsAbstractGeometry > segmentizedCopy;
623  if ( QgsWkbTypes::isCurvedType( geom->wkbType() ) )
624  {
625  segmentizedCopy.reset( geom->segmentize() );
626  geom = segmentizedCopy.get();
627  }
628 
630  {
631  return doDensify( static_cast< const QgsLineString * >( geom ), extraNodesPerSegment, distance );
632  }
633  else
634  {
635  // polygon
636  const QgsPolygon *polygon = static_cast< const QgsPolygon * >( geom );
637  QgsPolygon *result = new QgsPolygon();
638 
639  result->setExteriorRing( doDensify( static_cast< const QgsLineString * >( polygon->exteriorRing() ),
640  extraNodesPerSegment, distance ) );
641  for ( int i = 0; i < polygon->numInteriorRings(); ++i )
642  {
643  result->addInteriorRing( doDensify( static_cast< const QgsLineString * >( polygon->interiorRing( i ) ),
644  extraNodesPerSegment, distance ) );
645  }
646 
647  return result;
648  }
649 }
650 
652 {
653  if ( !mGeometry )
654  {
655  return QgsGeometry();
656  }
657 
659  {
660  return QgsGeometry( mGeometry->clone() ); // point geometry, nothing to do
661  }
662 
663  if ( const QgsGeometryCollection *gc = qgsgeometry_cast< const QgsGeometryCollection *>( mGeometry ) )
664  {
665  int numGeom = gc->numGeometries();
666  QVector< QgsAbstractGeometry * > geometryList;
667  geometryList.reserve( numGeom );
668  for ( int i = 0; i < numGeom; ++i )
669  {
670  geometryList << densifyGeometry( gc->geometryN( i ), extraNodesPerSegment );
671  }
672 
673  QgsGeometry first = QgsGeometry( geometryList.takeAt( 0 ) );
674  for ( QgsAbstractGeometry *g : qgis::as_const( geometryList ) )
675  {
676  first.addPart( g );
677  }
678  return first;
679  }
680  else
681  {
682  return QgsGeometry( densifyGeometry( mGeometry, extraNodesPerSegment ) );
683  }
684 }
685 
687 {
688  if ( !mGeometry )
689  {
690  return QgsGeometry();
691  }
692 
694  {
695  return QgsGeometry( mGeometry->clone() ); // point geometry, nothing to do
696  }
697 
698  if ( const QgsGeometryCollection *gc = qgsgeometry_cast< const QgsGeometryCollection *>( mGeometry ) )
699  {
700  int numGeom = gc->numGeometries();
701  QVector< QgsAbstractGeometry * > geometryList;
702  geometryList.reserve( numGeom );
703  for ( int i = 0; i < numGeom; ++i )
704  {
705  geometryList << densifyGeometry( gc->geometryN( i ), -1, distance );
706  }
707 
708  QgsGeometry first = QgsGeometry( geometryList.takeAt( 0 ) );
709  for ( QgsAbstractGeometry *g : qgis::as_const( geometryList ) )
710  {
711  first.addPart( g );
712  }
713  return first;
714  }
715  else
716  {
717  return QgsGeometry( densifyGeometry( mGeometry, -1, distance ) );
718  }
719 }
720 
722 //
723 // QgsLineSegmentDistanceComparer
724 //
725 
726 // adapted for QGIS geometry classes from original work at https://github.com/trylock/visibility by trylock
727 bool QgsLineSegmentDistanceComparer::operator()( QgsLineSegment2D ab, QgsLineSegment2D cd ) const
728 {
729  Q_ASSERT_X( ab.pointLeftOfLine( mOrigin ) != 0,
730  "line_segment_dist_comparer",
731  "AB must not be collinear with the origin." );
732  Q_ASSERT_X( cd.pointLeftOfLine( mOrigin ) != 0,
733  "line_segment_dist_comparer",
734  "CD must not be collinear with the origin." );
735 
736  // flip the segments so that if there are common endpoints,
737  // they will be the segment's start points
738  if ( ab.end() == cd.start() || ab.end() == cd.end() )
739  ab.reverse();
740  if ( ab.start() == cd.end() )
741  cd.reverse();
742 
743  // cases with common endpoints
744  if ( ab.start() == cd.start() )
745  {
746  const int oad = QgsGeometryUtils::leftOfLine( cd.endX(), cd.endY(), mOrigin.x(), mOrigin.y(), ab.startX(), ab.startY() );
747  const int oab = ab.pointLeftOfLine( mOrigin );
748  if ( ab.end() == cd.end() || oad != oab )
749  return false;
750  else
751  return ab.pointLeftOfLine( cd.end() ) != oab;
752  }
753  else
754  {
755  // cases without common endpoints
756  const int cda = cd.pointLeftOfLine( ab.start() );
757  const int cdb = cd.pointLeftOfLine( ab.end() );
758  if ( cdb == 0 && cda == 0 )
759  {
760  return mOrigin.sqrDist( ab.start() ) < mOrigin.sqrDist( cd.start() );
761  }
762  else if ( cda == cdb || cda == 0 || cdb == 0 )
763  {
764  const int cdo = cd.pointLeftOfLine( mOrigin );
765  return cdo == cda || cdo == cdb;
766  }
767  else
768  {
769  const int abo = ab.pointLeftOfLine( mOrigin );
770  return abo != ab.pointLeftOfLine( cd.start() );
771  }
772  }
773 }
774 
775 //
776 // QgsClockwiseAngleComparer
777 //
778 
779 bool QgsClockwiseAngleComparer::operator()( const QgsPointXY &a, const QgsPointXY &b ) const
780 {
781  const bool aIsLeft = a.x() < mVertex.x();
782  const bool bIsLeft = b.x() < mVertex.x();
783  if ( aIsLeft != bIsLeft )
784  return bIsLeft;
785 
786  if ( qgsDoubleNear( a.x(), mVertex.x() ) && qgsDoubleNear( b.x(), mVertex.x() ) )
787  {
788  if ( a.y() >= mVertex.y() || b.y() >= mVertex.y() )
789  {
790  return b.y() < a.y();
791  }
792  else
793  {
794  return a.y() < b.y();
795  }
796  }
797  else
798  {
799  const QgsVector oa = a - mVertex;
800  const QgsVector ob = b - mVertex;
801  const double det = oa.crossProduct( ob );
802  if ( qgsDoubleNear( det, 0.0 ) )
803  {
804  return oa.lengthSquared() < ob.lengthSquared();
805  }
806  else
807  {
808  return det < 0;
809  }
810  }
811 }
812 
814 
815 //
816 // QgsRay2D
817 //
818 
819 bool QgsRay2D::intersects( const QgsLineSegment2D &segment, QgsPointXY &intersectPoint ) const
820 {
821  const QgsVector ao = origin - segment.start();
822  const QgsVector ab = segment.end() - segment.start();
823  const double det = ab.crossProduct( direction );
824  if ( qgsDoubleNear( det, 0.0 ) )
825  {
826  const int abo = segment.pointLeftOfLine( origin );
827  if ( abo != 0 )
828  {
829  return false;
830  }
831  else
832  {
833  const double distA = ao * direction;
834  const double distB = ( origin - segment.end() ) * direction;
835 
836  if ( distA > 0 && distB > 0 )
837  {
838  return false;
839  }
840  else
841  {
842  if ( ( distA > 0 ) != ( distB > 0 ) )
843  intersectPoint = origin;
844  else if ( distA > distB ) // at this point, both distances are negative
845  intersectPoint = segment.start(); // hence the nearest point is A
846  else
847  intersectPoint = segment.end();
848  return true;
849  }
850  }
851  }
852  else
853  {
854  const double u = ao.crossProduct( direction ) / det;
855  if ( u < 0.0 || 1.0 < u )
856  {
857  return false;
858  }
859  else
860  {
861  const double t = -ab.crossProduct( ao ) / det;
862  intersectPoint = origin + direction * t;
863  return qgsDoubleNear( t, 0.0 ) || t > 0;
864  }
865  }
866 }
867 
868 QVector<QgsPointXY> generateSegmentCurve( const QgsPoint &center1, const double radius1, const QgsPoint &center2, const double radius2 )
869 {
870  // ensure that first circle is smaller than second
871  if ( radius1 > radius2 )
872  return generateSegmentCurve( center2, radius2, center1, radius1 );
873 
874  QgsPointXY t1;
875  QgsPointXY t2;
876  QgsPointXY t3;
877  QgsPointXY t4;
878  QVector<QgsPointXY> points;
879  if ( QgsGeometryUtils::circleCircleOuterTangents( center1, radius1, center2, radius2, t1, t2, t3, t4 ) )
880  {
881  points << t1
882  << t2
883  << t4
884  << t3;
885  }
886  return points;
887 }
888 
889 // partially ported from JTS VariableWidthBuffer,
890 // https://github.com/topobyte/jts/blob/master/jts-lab/src/main/java/com/vividsolutions/jts/operation/buffer/VariableWidthBuffer.java
891 
892 QgsGeometry QgsInternalGeometryEngine::variableWidthBuffer( int segments, const std::function< std::unique_ptr< double[] >( const QgsLineString *line ) > &widthFunction ) const
893 {
894  if ( !mGeometry )
895  {
896  return QgsGeometry();
897  }
898 
899  std::vector< std::unique_ptr<QgsLineString > > linesToProcess;
900 
901  const QgsMultiCurve *multiCurve = qgsgeometry_cast< const QgsMultiCurve * >( mGeometry );
902  if ( multiCurve )
903  {
904  for ( int i = 0; i < multiCurve->partCount(); ++i )
905  {
906  if ( static_cast< const QgsCurve * >( multiCurve->geometryN( i ) )->nCoordinates() == 0 )
907  continue; // skip 0 length lines
908 
909  linesToProcess.emplace_back( static_cast<QgsLineString *>( multiCurve->geometryN( i )->clone() ) );
910  }
911  }
912 
913  const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( mGeometry );
914  if ( curve )
915  {
916  if ( curve->nCoordinates() > 0 )
917  linesToProcess.emplace_back( static_cast<QgsLineString *>( curve->segmentize() ) );
918  }
919 
920  if ( linesToProcess.empty() )
921  {
922  QgsGeometry g;
923  g.mLastError = QStringLiteral( "Input geometry was not a curve type geometry" );
924  return g;
925  }
926 
927  QVector<QgsGeometry> bufferedLines;
928  bufferedLines.reserve( linesToProcess.size() );
929 
930  for ( std::unique_ptr< QgsLineString > &line : linesToProcess )
931  {
932  QVector<QgsGeometry> parts;
933  QgsPoint prevPoint;
934  double prevRadius = 0;
935  QgsGeometry prevCircle;
936 
937  std::unique_ptr< double[] > widths = widthFunction( line.get() ) ;
938  for ( int i = 0; i < line->nCoordinates(); ++i )
939  {
940  QgsPoint thisPoint = line->pointN( i );
941  QgsGeometry thisCircle;
942  double thisRadius = widths[ i ] / 2.0;
943  if ( thisRadius > 0 )
944  {
945  QgsGeometry p = QgsGeometry( thisPoint.clone() );
946 
947  QgsCircle circ( thisPoint, thisRadius );
948  thisCircle = QgsGeometry( circ.toPolygon( segments * 4 ) );
949  parts << thisCircle;
950  }
951  else
952  {
953  thisCircle = QgsGeometry( thisPoint.clone() );
954  }
955 
956  if ( i > 0 )
957  {
958  if ( prevRadius > 0 || thisRadius > 0 )
959  {
960  QVector< QgsPointXY > points = generateSegmentCurve( prevPoint, prevRadius, thisPoint, thisRadius );
961  if ( !points.empty() )
962  {
963  // snap points to circle vertices
964 
965  int atVertex = 0;
966  int beforeVertex = 0;
967  int afterVertex = 0;
968  double sqrDist = 0;
969  double sqrDistPrev = 0;
970  for ( int j = 0; j < points.count(); ++j )
971  {
972  QgsPointXY pA = prevCircle.closestVertex( points.at( j ), atVertex, beforeVertex, afterVertex, sqrDistPrev );
973  QgsPointXY pB = thisCircle.closestVertex( points.at( j ), atVertex, beforeVertex, afterVertex, sqrDist );
974  points[j] = sqrDistPrev < sqrDist ? pA : pB;
975  }
976  // close ring
977  points.append( points.at( 0 ) );
978 
979  std::unique_ptr< QgsPolygon > poly = qgis::make_unique< QgsPolygon >();
980  poly->setExteriorRing( new QgsLineString( points ) );
981  if ( poly->area() > 0 )
982  parts << QgsGeometry( std::move( poly ) );
983  }
984  }
985  }
986  prevPoint = thisPoint;
987  prevRadius = thisRadius;
988  prevCircle = thisCircle;
989  }
990 
991  bufferedLines << QgsGeometry::unaryUnion( parts );
992  }
993 
994  return QgsGeometry::collectGeometry( bufferedLines );
995 }
996 
997 QgsGeometry QgsInternalGeometryEngine::taperedBuffer( double start, double end, int segments ) const
998 {
999  start = std::fabs( start );
1000  end = std::fabs( end );
1001 
1002  auto interpolateWidths = [ start, end ]( const QgsLineString * line )->std::unique_ptr< double [] >
1003  {
1004  // ported from JTS VariableWidthBuffer,
1005  // https://github.com/topobyte/jts/blob/master/jts-lab/src/main/java/com/vividsolutions/jts/operation/buffer/VariableWidthBuffer.java
1006  std::unique_ptr< double [] > widths( new double[ line->nCoordinates() ] );
1007  widths[0] = start;
1008  widths[line->nCoordinates() - 1] = end;
1009 
1010  double lineLength = line->length();
1011  double currentLength = 0;
1012  QgsPoint prevPoint = line->pointN( 0 );
1013  for ( int i = 1; i < line->nCoordinates() - 1; ++i )
1014  {
1015  QgsPoint point = line->pointN( i );
1016  double segmentLength = point.distance( prevPoint );
1017  currentLength += segmentLength;
1018  double lengthFraction = lineLength > 0 ? currentLength / lineLength : 1;
1019  double delta = lengthFraction * ( end - start );
1020  widths[i] = start + delta;
1021  prevPoint = point;
1022  }
1023  return widths;
1024  };
1025 
1026  return variableWidthBuffer( segments, interpolateWidths );
1027 }
1028 
1030 {
1031  auto widthByM = []( const QgsLineString * line )->std::unique_ptr< double [] >
1032  {
1033  std::unique_ptr< double [] > widths( new double[ line->nCoordinates() ] );
1034  for ( int i = 0; i < line->nCoordinates(); ++i )
1035  {
1036  widths[ i ] = line->mAt( i );
1037  }
1038  return widths;
1039  };
1040 
1041  return variableWidthBuffer( segments, widthByM );
1042 }
1043 
1044 QVector<QgsPointXY> QgsInternalGeometryEngine::randomPointsInPolygon( const QgsGeometry &polygon, int count,
1045  const std::function< bool( const QgsPointXY & ) > &acceptPoint, unsigned long seed, QgsFeedback *feedback )
1046 {
1047  if ( polygon.type() != QgsWkbTypes::PolygonGeometry || count == 0 )
1048  return QVector< QgsPointXY >();
1049 
1050  // step 1 - tessellate the polygon to triangles
1051  QgsRectangle bounds = polygon.boundingBox();
1052  QgsTessellator t( bounds, false, false, false, true );
1053 
1054  if ( polygon.isMultipart() )
1055  {
1056  const QgsMultiSurface *ms = qgsgeometry_cast< const QgsMultiSurface * >( polygon.constGet() );
1057  for ( int i = 0; i < ms->numGeometries(); ++i )
1058  {
1059  if ( feedback && feedback->isCanceled() )
1060  return QVector< QgsPointXY >();
1061 
1062  if ( QgsPolygon *poly = qgsgeometry_cast< QgsPolygon * >( ms->geometryN( i ) ) )
1063  {
1064  t.addPolygon( *poly, 0 );
1065  }
1066  else
1067  {
1068  std::unique_ptr< QgsPolygon > p( qgsgeometry_cast< QgsPolygon * >( ms->geometryN( i )->segmentize() ) );
1069  t.addPolygon( *p, 0 );
1070  }
1071  }
1072  }
1073  else
1074  {
1075  if ( const QgsPolygon *poly = qgsgeometry_cast< const QgsPolygon * >( polygon.constGet() ) )
1076  {
1077  t.addPolygon( *poly, 0 );
1078  }
1079  else
1080  {
1081  std::unique_ptr< QgsPolygon > p( qgsgeometry_cast< QgsPolygon * >( polygon.constGet()->segmentize() ) );
1082  t.addPolygon( *p, 0 );
1083  }
1084  }
1085 
1086  if ( feedback && feedback->isCanceled() )
1087  return QVector< QgsPointXY >();
1088 
1089  const QVector<float> triangleData = t.data();
1090  if ( triangleData.empty() )
1091  return QVector< QgsPointXY >(); //hm
1092 
1093  // calculate running sum of triangle areas
1094  std::vector< double > cumulativeAreas;
1095  cumulativeAreas.reserve( t.dataVerticesCount() / 3 );
1096  double totalArea = 0;
1097  for ( auto it = triangleData.constBegin(); it != triangleData.constEnd(); )
1098  {
1099  if ( feedback && feedback->isCanceled() )
1100  return QVector< QgsPointXY >();
1101 
1102  const float aX = *it++;
1103  ( void )it++; // z
1104  const float aY = -( *it++ );
1105  const float bX = *it++;
1106  ( void )it++; // z
1107  const float bY = -( *it++ );
1108  const float cX = *it++;
1109  ( void )it++; // z
1110  const float cY = -( *it++ );
1111 
1112  const double area = QgsGeometryUtils::triangleArea( aX, aY, bX, bY, cX, cY );
1113  totalArea += area;
1114  cumulativeAreas.emplace_back( totalArea );
1115  }
1116 
1117  std::random_device rd;
1118  std::mt19937 mt( seed == 0 ? rd() : seed );
1119  std::uniform_real_distribution<> uniformDist( 0, 1 );
1120 
1121  // selects a random triangle, weighted by triangle area
1122  auto selectRandomTriangle = [&cumulativeAreas, totalArea]( double random )->int
1123  {
1124  int triangle = 0;
1125  const double target = random * totalArea;
1126  for ( auto it = cumulativeAreas.begin(); it != cumulativeAreas.end(); ++it, triangle++ )
1127  {
1128  if ( *it > target )
1129  return triangle;
1130  }
1131  Q_ASSERT_X( false, "QgsInternalGeometryEngine::randomPointsInPolygon", "Invalid random triangle index" );
1132  return 0; // no warnings
1133  };
1134 
1135 
1136  QVector<QgsPointXY> result;
1137  result.reserve( count );
1138  for ( int i = 0; i < count; )
1139  {
1140  if ( feedback && feedback->isCanceled() )
1141  return QVector< QgsPointXY >();
1142 
1143  const double triangleIndexRnd = uniformDist( mt );
1144  // pick random triangle, weighted by triangle area
1145  const int triangleIndex = selectRandomTriangle( triangleIndexRnd );
1146 
1147  // generate a random point inside this triangle
1148  const double weightB = uniformDist( mt );
1149  const double weightC = uniformDist( mt );
1150  double x;
1151  double y;
1152 
1153  // get triangle
1154  const double aX = triangleData.at( triangleIndex * 9 ) + bounds.xMinimum();
1155  const double aY = -triangleData.at( triangleIndex * 9 + 2 ) + bounds.yMinimum();
1156  const double bX = triangleData.at( triangleIndex * 9 + 3 ) + bounds.xMinimum();
1157  const double bY = -triangleData.at( triangleIndex * 9 + 5 ) + bounds.yMinimum();
1158  const double cX = triangleData.at( triangleIndex * 9 + 6 ) + bounds.xMinimum();
1159  const double cY = -triangleData.at( triangleIndex * 9 + 8 ) + bounds.yMinimum();
1160  QgsGeometryUtils::weightedPointInTriangle( aX, aY, bX, bY, cX, cY, weightB, weightC, x, y );
1161 
1162  QgsPointXY candidate( x, y );
1163  if ( acceptPoint( candidate ) )
1164  {
1165  result << QgsPointXY( x, y );
1166  i++;
1167  }
1168  }
1169  return result;
1170 }
1171 
1172 // ported from PostGIS' lwgeom pta_unstroke
1173 
1174 std::unique_ptr< QgsCompoundCurve > lineToCurve( const QgsLineString *lineString, double distanceTolerance,
1175  double pointSpacingAngleTolerance )
1176 {
1177  std::unique_ptr< QgsCompoundCurve > out = qgis::make_unique< QgsCompoundCurve >();
1178 
1179  /* Minimum number of edges, per quadrant, required to define an arc */
1180  const unsigned int minQuadEdges = 2;
1181 
1182  /* Die on null input */
1183  if ( !lineString )
1184  return nullptr;
1185 
1186  /* Null on empty input? */
1187  if ( lineString->nCoordinates() == 0 )
1188  return nullptr;
1189 
1190  /* We can't desegmentize anything shorter than four points */
1191  if ( lineString->nCoordinates() < 4 )
1192  {
1193  out->addCurve( lineString->clone() );
1194  return out;
1195  }
1196 
1197  /* Allocate our result array of vertices that are part of arcs */
1198  int numEdges = lineString->nCoordinates() - 1;
1199  QVector< int > edgesInArcs( numEdges + 1, 0 );
1200 
1201  auto arcAngle = []( const QgsPoint & a, const QgsPoint & b, const QgsPoint & c )->double
1202  {
1203  double abX = b.x() - a.x();
1204  double abY = b.y() - a.y();
1205 
1206  double cbX = b.x() - c.x();
1207  double cbY = b.y() - c.y();
1208 
1209  double dot = ( abX * cbX + abY * cbY ); /* dot product */
1210  double cross = ( abX * cbY - abY * cbX ); /* cross product */
1211 
1212  double alpha = std::atan2( cross, dot );
1213 
1214  return alpha;
1215  };
1216 
1217  /* We make a candidate arc of the first two edges, */
1218  /* And then see if the next edge follows it */
1219  int i = 0;
1220  int j = 0;
1221  int k = 0;
1222  int currentArc = 1;
1223  QgsPoint a1;
1224  QgsPoint a2;
1225  QgsPoint a3;
1226  QgsPoint b;
1227  double centerX = 0.0;
1228  double centerY = 0.0;
1229  double radius = 0;
1230 
1231  while ( i < numEdges - 2 )
1232  {
1233  unsigned int arcEdges = 0;
1234  double numQuadrants = 0;
1235  double angle;
1236 
1237  bool foundArc = false;
1238  /* Make candidate arc */
1239  a1 = lineString->pointN( i );
1240  a2 = lineString->pointN( i + 1 );
1241  a3 = lineString->pointN( i + 2 );
1242  QgsPoint first = a1;
1243 
1244  for ( j = i + 3; j < numEdges + 1; j++ )
1245  {
1246  b = lineString->pointN( j );
1247 
1248  /* Does this point fall on our candidate arc? */
1249  if ( QgsGeometryUtils::pointContinuesArc( a1, a2, a3, b, distanceTolerance, pointSpacingAngleTolerance ) )
1250  {
1251  /* Yes. Mark this edge and the two preceding it as arc components */
1252  foundArc = true;
1253  for ( k = j - 1; k > j - 4; k-- )
1254  edgesInArcs[k] = currentArc;
1255  }
1256  else
1257  {
1258  /* No. So we're done with this candidate arc */
1259  currentArc++;
1260  break;
1261  }
1262 
1263  a1 = a2;
1264  a2 = a3;
1265  a3 = b;
1266  }
1267  /* Jump past all the edges that were added to the arc */
1268  if ( foundArc )
1269  {
1270  /* Check if an arc was composed by enough edges to be
1271  * really considered an arc
1272  * See http://trac.osgeo.org/postgis/ticket/2420
1273  */
1274  arcEdges = j - 1 - i;
1275  if ( first.x() == b.x() && first.y() == b.y() )
1276  {
1277  numQuadrants = 4;
1278  }
1279  else
1280  {
1281  QgsGeometryUtils::circleCenterRadius( first, b, a1, radius, centerX, centerY );
1282 
1283  angle = arcAngle( first, QgsPoint( centerX, centerY ), b );
1284  int p2Side = QgsGeometryUtils::leftOfLine( b.x(), b.y(), first.x(), first.y(), a1.x(), a1.y() );
1285  if ( p2Side >= 0 )
1286  angle = -angle;
1287 
1288  if ( angle < 0 )
1289  angle = 2 * M_PI + angle;
1290  numQuadrants = ( 4 * angle ) / ( 2 * M_PI );
1291  }
1292  /* a1 is first point, b is last point */
1293  if ( arcEdges < minQuadEdges * numQuadrants )
1294  {
1295  // LWDEBUGF( 4, "Not enough edges for a %g quadrants arc, %g needed", num_quadrants, min_quad_edges * num_quadrants );
1296  for ( k = j - 1; k >= i; k-- )
1297  edgesInArcs[k] = 0;
1298  }
1299 
1300  i = j - 1;
1301  }
1302  else
1303  {
1304  /* Mark this edge as a linear edge */
1305  edgesInArcs[i] = 0;
1306  i = i + 1;
1307  }
1308  }
1309 
1310  int start = 0;
1311  int end = 0;
1312  /* non-zero if edge is part of an arc */
1313  int edgeType = edgesInArcs[0];
1314 
1315  auto addPointsToCurve = [ lineString, &out ]( int start, int end, int type )
1316  {
1317  if ( type == 0 )
1318  {
1319  // straight segment
1320  QVector< QgsPoint > points;
1321  for ( int j = start; j < end + 2; ++ j )
1322  {
1323  points.append( lineString->pointN( j ) );
1324  }
1325  std::unique_ptr< QgsCurve > straightSegment = qgis::make_unique< QgsLineString >( points );
1326  out->addCurve( straightSegment.release() );
1327  }
1328  else
1329  {
1330  // curved segment
1331  QVector< QgsPoint > points;
1332  points.append( lineString->pointN( start ) );
1333  points.append( lineString->pointN( ( start + end + 1 ) / 2 ) );
1334  points.append( lineString->pointN( end + 1 ) );
1335  std::unique_ptr< QgsCircularString > curvedSegment = qgis::make_unique< QgsCircularString >();
1336  curvedSegment->setPoints( points );
1337  out->addCurve( curvedSegment.release() );
1338  }
1339  };
1340 
1341  for ( int i = 1; i < numEdges; i++ )
1342  {
1343  if ( edgeType != edgesInArcs[i] )
1344  {
1345  end = i - 1;
1346  addPointsToCurve( start, end, edgeType );
1347  start = i;
1348  edgeType = edgesInArcs[i];
1349  }
1350  }
1351 
1352  /* Roll out last item */
1353  end = numEdges - 1;
1354  addPointsToCurve( start, end, edgeType );
1355 
1356  return out;
1357 }
1358 
1359 std::unique_ptr< QgsAbstractGeometry > convertGeometryToCurves( const QgsAbstractGeometry *geom, double distanceTolerance, double angleTolerance )
1360 {
1362  {
1363  return lineToCurve( static_cast< const QgsLineString * >( geom ), distanceTolerance, angleTolerance );
1364  }
1365  else
1366  {
1367  // polygon
1368  const QgsPolygon *polygon = static_cast< const QgsPolygon * >( geom );
1369  std::unique_ptr< QgsCurvePolygon > result = qgis::make_unique< QgsCurvePolygon>();
1370 
1371  result->setExteriorRing( lineToCurve( static_cast< const QgsLineString * >( polygon->exteriorRing() ),
1372  distanceTolerance, angleTolerance ).release() );
1373  for ( int i = 0; i < polygon->numInteriorRings(); ++i )
1374  {
1375  result->addInteriorRing( lineToCurve( static_cast< const QgsLineString * >( polygon->interiorRing( i ) ),
1376  distanceTolerance, angleTolerance ).release() );
1377  }
1378 
1379  return result;
1380  }
1381 }
1382 
1383 QgsGeometry QgsInternalGeometryEngine::convertToCurves( double distanceTolerance, double angleTolerance ) const
1384 {
1385  if ( !mGeometry )
1386  {
1387  return QgsGeometry();
1388  }
1389 
1391  {
1392  return QgsGeometry( mGeometry->clone() ); // point geometry, nothing to do
1393  }
1394 
1395  if ( QgsWkbTypes::isCurvedType( mGeometry->wkbType() ) )
1396  {
1397  // already curved. In future we may want to allow this, and convert additional candidate segments
1398  // in an already curved geometry to curves
1399  return QgsGeometry( mGeometry->clone() );
1400  }
1401 
1402  if ( const QgsGeometryCollection *gc = qgsgeometry_cast< const QgsGeometryCollection *>( mGeometry ) )
1403  {
1404  int numGeom = gc->numGeometries();
1405  QVector< QgsAbstractGeometry * > geometryList;
1406  geometryList.reserve( numGeom );
1407  for ( int i = 0; i < numGeom; ++i )
1408  {
1409  geometryList << convertGeometryToCurves( gc->geometryN( i ), distanceTolerance, angleTolerance ).release();
1410  }
1411 
1412  QgsGeometry first = QgsGeometry( geometryList.takeAt( 0 ) );
1413  for ( QgsAbstractGeometry *g : qgis::as_const( geometryList ) )
1414  {
1415  first.addPart( g );
1416  }
1417  return first;
1418  }
1419  else
1420  {
1421  return QgsGeometry( convertGeometryToCurves( mGeometry, distanceTolerance, angleTolerance ) );
1422  }
1423 }
QgsCurve
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
qgspolygon.h
QgsVector::crossProduct
double crossProduct(QgsVector v) const
Returns the 2D cross product of this vector and another vector v.
Definition: qgsvector.cpp:102
QgsInternalGeometryEngine::orthogonalize
QgsGeometry orthogonalize(double tolerance=1.0E-8, int maxIterations=1000, double angleThreshold=15.0) const
Attempts to orthogonalize a line or polygon geometry by shifting vertices to make the geometries angl...
Definition: qgsinternalgeometryengine.cpp:494
QgsInternalGeometryEngine::extrude
QgsGeometry extrude(double x, double y) const
Will extrude a line or (segmentized) curve by a given offset and return a polygon representation of i...
Definition: qgsinternalgeometryengine.cpp:49
QgsLineSegment2D::endX
double endX() const
Returns the segment's end x-coordinate.
Definition: qgslinesegment.h:97
QgsLineString::pointN
QgsPoint pointN(int i) const
Returns the specified point from inside the line string.
Definition: qgslinestring.cpp:660
QgsInternalGeometryEngine::densifyByCount
QgsGeometry densifyByCount(int extraNodesPerSegment) const
Densifies the geometry by adding the specified number of extra nodes within each segment of the geome...
Definition: qgsinternalgeometryengine.cpp:651
QgsLineString::setYAt
void setYAt(int index, double y)
Sets the y-coordinate of the specified node in the line string.
Definition: qgslinestring.cpp:732
QgsPoint::distance
double distance(double x, double y) const
Returns the Cartesian 2D distance between this point and a specified x, y coordinate.
Definition: qgspoint.h:350
QgsLineString::xAt
double xAt(int index) const override
Returns the x-coordinate of the specified node in the line string.
Definition: qgslinestring.cpp:709
QgsPointXY::y
double y
Definition: qgspointxy.h:48
QgsGeometry::addPart
OperationResult addPart(const QVector< QgsPointXY > &points, QgsWkbTypes::GeometryType geomType=QgsWkbTypes::UnknownGeometry)
Adds a new part to a the geometry.
Definition: qgsgeometry.cpp:687
QgsAbstractGeometry::wkbType
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
Definition: qgsabstractgeometry.h:189
QgsCircle
Circle geometry type.
Definition: qgscircle.h:43
QgsLineSegment2D::pointLeftOfLine
int pointLeftOfLine(const QgsPointXY &point) const
Tests if a point is to the left of the line segment.
Definition: qgslinesegment.cpp:21
QgsLineSegment2D
Definition: qgslinesegment.h:31
QgsPolygon
Polygon geometry type.
Definition: qgspolygon.h:33
QgsLineString::mAt
double mAt(int index) const
Returns the m value of the specified node in the line string.
Definition: qgslinestring.h:320
qgslinestring.h
QgsRay2D::intersects
bool intersects(const QgsLineSegment2D &segment, QgsPointXY &intersectPoint) const
Finds the closest intersection point of the ray and a line segment.
Definition: qgsinternalgeometryengine.cpp:819
QgsPoint
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
densifyGeometry
QgsAbstractGeometry * densifyGeometry(const QgsAbstractGeometry *geom, int extraNodesPerSegment=1, double distance=1)
Definition: qgsinternalgeometryengine.cpp:620
QgsGeometryCollection::numGeometries
int numGeometries() const
Returns the number of geometries within the collection.
Definition: qgsgeometrycollection.h:51
QgsVector::normalized
QgsVector normalized() const
Returns the vector's normalized (or "unit") vector (ie same angle but length of 1....
Definition: qgsvector.cpp:114
QgsInternalGeometryEngine::variableWidthBufferByM
QgsGeometry variableWidthBufferByM(int segments) const
Calculates a variable width buffer using the m-values from a (multi)line geometry.
Definition: qgsinternalgeometryengine.cpp:1029
QgsGeometry::isMultipart
bool isMultipart() const
Returns true if WKB of the geometry is of WKBMulti* type.
Definition: qgsgeometry.cpp:377
QgsLineString::numPoints
int numPoints() const override
Returns the number of points in the curve.
Definition: qgslinestring.cpp:650
QgsLineString::yAt
double yAt(int index) const override
Returns the y-coordinate of the specified node in the line string.
Definition: qgslinestring.cpp:717
qgsinternalgeometryengine.h
QgsLineString::zAt
double zAt(int index) const
Returns the z-coordinate of the specified node in the line string.
Definition: qgslinestring.h:274
QgsInternalGeometryEngine::variableWidthBuffer
QgsGeometry variableWidthBuffer(int segments, const std::function< std::unique_ptr< double[] >(const QgsLineString *line) > &widthFunction) const
Calculates a variable width buffer for a (multi)curve geometry.
Definition: qgsinternalgeometryengine.cpp:892
QgsPolygon::setExteriorRing
void setExteriorRing(QgsCurve *ring) override
Sets the exterior ring of the polygon.
Definition: qgspolygon.cpp:214
QgsRectangle::xMaximum
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
dotProductWithinAngleTolerance
bool dotProductWithinAngleTolerance(double dotProduct, double lowerThreshold, double upperThreshold)
Definition: qgsinternalgeometryengine.cpp:297
QgsInternalGeometryEngine::poleOfInaccessibility
QgsGeometry poleOfInaccessibility(double precision, double *distanceFromBoundary=nullptr) const
Calculates the approximate pole of inaccessibility for a surface, which is the most distant internal ...
Definition: qgsinternalgeometryengine.cpp:239
QgsCurvePolygon::exteriorRing
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
Definition: qgscurvepolygon.h:86
QgsSurface
Definition: qgssurface.h:32
QgsLineString::nCoordinates
int nCoordinates() const override
Returns the number of nodes contained in the geometry.
Definition: qgslinestring.cpp:655
QgsLineString::setXAt
void setXAt(int index, double x)
Sets the x-coordinate of the specified node in the line string.
Definition: qgslinestring.cpp:725
QgsGeometryUtils::circleCenterRadius
static void circleCenterRadius(const QgsPoint &pt1, const QgsPoint &pt2, const QgsPoint &pt3, double &radius, double &centerX, double &centerY)
Returns radius and center of the circle through pt1, pt2, pt3.
Definition: qgsgeometryutils.cpp:673
QgsMultiSurface
Multi surface geometry collection.
Definition: qgsmultisurface.h:29
QgsLineString
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:43
QgsTessellator::addPolygon
void addPolygon(const QgsPolygon &polygon, float extrusionHeight)
Tessellates a triangle and adds its vertex entries to the output data array.
Definition: qgstessellator.cpp:423
doOrthogonalize
QgsLineString * doOrthogonalize(QgsLineString *ring, int iterations, double tolerance, double lowerThreshold, double upperThreshold)
Definition: qgsinternalgeometryengine.cpp:390
QgsGeometry::closestVertex
QgsPointXY closestVertex(const QgsPointXY &point, int &atVertex, int &beforeVertex, int &afterVertex, double &sqrDist) const
Returns the vertex closest to the given point, the corresponding vertex index, squared distance snap ...
Definition: qgsgeometry.cpp:386
QgsRectangle
Definition: qgsrectangle.h:41
qgslinesegment.h
QgsWkbTypes::PolygonGeometry
@ PolygonGeometry
Definition: qgswkbtypes.h:143
QgsCurvePolygon::interiorRing
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
Definition: qgscurvepolygon.h:99
QgsPoint::clone
QgsPoint * clone() const override
Clones the geometry by performing a deep copy.
Definition: qgspoint.cpp:102
doDensify
QgsLineString * doDensify(const QgsLineString *ring, int extraNodesPerSegment=-1, double distance=1)
Definition: qgsinternalgeometryengine.cpp:529
geos::unique_ptr
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
Definition: qgsgeos.h:79
QgsGeometryUtils::pointContinuesArc
static bool pointContinuesArc(const QgsPoint &a1, const QgsPoint &a2, const QgsPoint &a3, const QgsPoint &b, double distanceTolerance, double pointSpacingAngleTolerance)
Returns true if point b is on the arc formed by points a1, a2, and a3, but not within that arc portio...
Definition: qgsgeometryutils.cpp:878
QgsLineSegment2D::startY
double startY() const
Returns the segment's start y-coordinate.
Definition: qgslinesegment.h:87
QgsInternalGeometryEngine::randomPointsInPolygon
static QVector< QgsPointXY > randomPointsInPolygon(const QgsGeometry &polygon, int count, const std::function< bool(const QgsPointXY &) > &acceptPoint, unsigned long seed=0, QgsFeedback *feedback=nullptr)
Returns a list of count random points generated inside a polygon geometry.
Definition: qgsinternalgeometryengine.cpp:1044
QgsPoint::y
double y
Definition: qgspoint.h:59
precision
int precision
Definition: qgswfsgetfeature.cpp:103
QgsGeometryCollection
Geometry collection.
Definition: qgsgeometrycollection.h:35
QgsLineString::clone
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
Definition: qgslinestring.cpp:293
QgsGeometryCollection::partCount
int partCount() const override
Returns count of parts contained in the geometry.
Definition: qgsgeometrycollection.cpp:823
QgsLineSegment2D::start
QgsPointXY start() const
Returns the segment's start point.
Definition: qgslinesegment.h:118
QgsTessellator::dataVerticesCount
int dataVerticesCount() const
Returns the number of vertices stored in the output data array.
Definition: qgstessellator.cpp:646
qgsmultipolygon.h
QgsMultiCurve
Multi curve geometry collection.
Definition: qgsmulticurve.h:29
QgsInternalGeometryEngine::convertToCurves
QgsGeometry convertToCurves(double distanceTolerance, double angleTolerance) const
Attempts to convert a non-curved geometry into a curved geometry type (e.g.
Definition: qgsinternalgeometryengine.cpp:1383
convertGeometryToCurves
std::unique_ptr< QgsAbstractGeometry > convertGeometryToCurves(const QgsAbstractGeometry *geom, double distanceTolerance, double angleTolerance)
Definition: qgsinternalgeometryengine.cpp:1359
qgsDoubleNear
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:315
QgsFeedback
Definition: qgsfeedback.h:43
qgscircle.h
QgsAbstractGeometry::clone
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
orthogonalizeGeom
QgsAbstractGeometry * orthogonalizeGeom(const QgsAbstractGeometry *geom, int maxIterations, double tolerance, double lowerThreshold, double upperThreshold)
Definition: qgsinternalgeometryengine.cpp:462
qgstessellator.h
QgsWkbTypes::geometryType
static GeometryType geometryType(Type type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:937
QgsAbstractGeometry::is3D
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
Definition: qgsabstractgeometry.h:202
QgsMultiPolygon
Multi polygon geometry collection.
Definition: qgsmultipolygon.h:29
QgsAbstractGeometry::nCoordinates
virtual int nCoordinates() const
Returns the number of nodes contained in the geometry.
Definition: qgsabstractgeometry.cpp:116
squareness
double squareness(QgsLineString *ring, double lowerThreshold, double upperThreshold)
Definition: qgsinternalgeometryengine.cpp:315
QgsGeometry::constGet
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
Definition: qgsgeometry.cpp:128
QgsAbstractGeometry::isEmpty
virtual bool isEmpty() const
Returns true if the geometry is empty.
Definition: qgsabstractgeometry.cpp:298
qgscircularstring.h
QgsAbstractGeometry
Abstract base class for all geometries.
Definition: qgsabstractgeometry.h:71
QgsRectangle::yMaximum
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
normalizedDotProduct
double normalizedDotProduct(const QgsPoint &a, const QgsPoint &b, const QgsPoint &c)
Definition: qgsinternalgeometryengine.cpp:302
QgsAbstractGeometry::segmentize
virtual QgsAbstractGeometry * segmentize(double tolerance=M_PI/180., SegmentationToleranceType toleranceType=MaximumAngle) const
Returns a version of the geometry without curves.
Definition: qgsabstractgeometry.cpp:310
qgsgeometryutils.h
generateSegmentCurve
QVector< QgsPointXY > generateSegmentCurve(const QgsPoint &center1, const double radius1, const QgsPoint &center2, const double radius2)
Definition: qgsinternalgeometryengine.cpp:868
QgsPointXY
Definition: qgspointxy.h:43
QgsGeometryUtils::triangleArea
static double triangleArea(double aX, double aY, double bX, double bY, double cX, double cY)
Returns the area of the triangle denoted by the points (aX, aY), (bX, bY) and (cX,...
Definition: qgsgeometryutils.cpp:1664
QgsVector::lengthSquared
double lengthSquared() const
Returns the length of the vector.
Definition: qgsvector.h:117
QgsCurve::clone
QgsCurve * clone() const override=0
Clones the geometry by performing a deep copy.
QgsFeedback::isCanceled
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:66
QgsInternalGeometryEngine::densifyByDistance
QgsGeometry densifyByDistance(double distance) const
Densifies the geometry by adding regularly placed extra nodes inside each segment so that the maximum...
Definition: qgsinternalgeometryengine.cpp:686
QgsWkbTypes::LineGeometry
@ LineGeometry
Definition: qgswkbtypes.h:142
QgsCurvePolygon::numInteriorRings
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
Definition: qgscurvepolygon.h:76
QgsWkbTypes::PointGeometry
@ PointGeometry
Definition: qgswkbtypes.h:141
QgsLineSegment2D::reverse
void reverse()
Reverses the line segment, so that the start and end points are flipped.
Definition: qgslinesegment.h:216
QgsCurve::isClosed
virtual bool isClosed() const
Returns true if the curve is closed.
Definition: qgscurve.cpp:40
QgsGeometryCollection::geometryN
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
Definition: qgsgeometrycollection.h:79
qgsgeometry.h
c
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
Definition: porting_processing.dox:1
QgsGeometry
Definition: qgsgeometry.h:122
QgsTessellator
Definition: qgstessellator.h:40
calcMotion
QgsVector calcMotion(const QgsPoint &a, const QgsPoint &b, const QgsPoint &c, double lowerThreshold, double upperThreshold)
Definition: qgsinternalgeometryengine.cpp:359
QgsVector
Definition: qgsvector.h:29
QgsGeometryUtils::circleCircleOuterTangents
static int circleCircleOuterTangents(const QgsPointXY &center1, double radius1, const QgsPointXY &center2, double radius2, QgsPointXY &line1P1, QgsPointXY &line1P2, QgsPointXY &line2P1, QgsPointXY &line2P2)
Calculates the outer tangent points for two circles, centered at center1 and center2 and with radii o...
Definition: qgsgeometryutils.cpp:463
QgsGeometryUtils::weightedPointInTriangle
static void weightedPointInTriangle(double aX, double aY, double bX, double bY, double cX, double cY, double weightB, double weightC, double &pointX, double &pointY)
Returns a weighted point inside the triangle denoted by the points (aX, aY), (bX, bY) and (cX,...
Definition: qgsgeometryutils.cpp:1669
QgsPointXY::x
double x
Definition: qgspointxy.h:47
QgsRectangle::height
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:209
QgsGeometryUtils::leftOfLine
static int leftOfLine(const double x, const double y, const double x1, const double y1, const double x2, const double y2)
Returns a value < 0 if the point (x, y) is left of the line from (x1, y1) -> (x2, y2).
Definition: qgsgeometryutils.cpp:589
QgsRectangle::yMinimum
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
QgsAbstractGeometry::isMeasure
bool isMeasure() const
Returns true if the geometry contains m values.
Definition: qgsabstractgeometry.h:211
QgsWkbTypes::isCurvedType
static bool isCurvedType(Type type)
Returns true if the WKB type is a curved type or can contain curved geometries.
Definition: qgswkbtypes.h:880
QgsGeometry::boundingBox
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Definition: qgsgeometry.cpp:962
QgsEllipse::toPolygon
virtual QgsPolygon * toPolygon(unsigned int segments=36) const
Returns a segmented polygon.
Definition: qgsellipse.cpp:224
QgsLineSegment2D::endY
double endY() const
Returns the segment's end y-coordinate.
Definition: qgslinesegment.h:107
qgsmulticurve.h
lineToCurve
std::unique_ptr< QgsCompoundCurve > lineToCurve(const QgsLineString *lineString, double distanceTolerance, double pointSpacingAngleTolerance)
Definition: qgsinternalgeometryengine.cpp:1174
QgsLineSegment2D::startX
double startX() const
Returns the segment's start x-coordinate.
Definition: qgslinesegment.h:77
qgslogger.h
QgsCurve::segmentize
QgsCurve * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
Definition: qgscurve.cpp:159
QgsTessellator::data
QVector< float > data() const
Returns array of triangle vertex data.
Definition: qgstessellator.h:65
QgsVector::length
double length() const
Returns the length of the vector.
Definition: qgsvector.cpp:71
QgsGeometry::type
QgsWkbTypes::GeometryType type
Definition: qgsgeometry.h:126
qgsfeedback.h
MathUtils::angle
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:786
QgsLineSegment2D::end
QgsPointXY end() const
Returns the segment's end point.
Definition: qgslinesegment.h:129
QgsPolygon::addInteriorRing
void addInteriorRing(QgsCurve *ring) override
Adds an interior ring to the geometry (takes ownership)
Definition: qgspolygon.cpp:183
QgsPoint::x
double x
Definition: qgspoint.h:58
QgsInternalGeometryEngine::QgsInternalGeometryEngine
QgsInternalGeometryEngine(const QgsGeometry &geometry)
The caller is responsible that the geometry is available and unchanged for the whole lifetime of this...
Definition: qgsinternalgeometryengine.cpp:37
QgsRectangle::width
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:202
QgsSurface::boundingBox
QgsRectangle boundingBox() const override
Returns the minimal bounding box for the geometry.
Definition: qgssurface.h:42
QgsRectangle::xMinimum
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
QgsInternalGeometryEngine::taperedBuffer
QgsGeometry taperedBuffer(double startWidth, double endWidth, int segments) const
Calculates a tapered width buffer for a (multi)curve geometry.
Definition: qgsinternalgeometryengine.cpp:997
QgsGeometry::unaryUnion
static QgsGeometry unaryUnion(const QVector< QgsGeometry > &geometries)
Compute the unary union on a list of geometries.
Definition: qgsgeometry.cpp:2785
QgsGeometry::collectGeometry
static QgsGeometry collectGeometry(const QVector< QgsGeometry > &geometries)
Creates a new multipart geometry from a list of QgsGeometry objects.
Definition: qgsgeometry.cpp:247