QGIS API Documentation  3.12.1-BucureČ™ti (121cc00ff0)
qgsgeos.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsgeos.cpp
3  -------------------------------------------------------------------
4 Date : 22 Sept 2014
5 Copyright : (C) 2014 by Marco Hugentobler
6 email : marco.hugentobler at sourcepole dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgsgeos.h"
17 #include "qgsabstractgeometry.h"
18 #include "qgsgeometrycollection.h"
19 #include "qgsgeometryfactory.h"
20 #include "qgslinestring.h"
21 #include "qgsmulticurve.h"
22 #include "qgsmultilinestring.h"
23 #include "qgsmultipoint.h"
24 #include "qgsmultipolygon.h"
25 #include "qgslogger.h"
26 #include "qgspolygon.h"
27 #include "qgsgeometryeditutils.h"
28 #include <limits>
29 #include <cstdio>
30 
31 #define DEFAULT_QUADRANT_SEGMENTS 8
32 
33 #define CATCH_GEOS(r) \
34  catch (GEOSException &) \
35  { \
36  return r; \
37  }
38 
39 #define CATCH_GEOS_WITH_ERRMSG(r) \
40  catch (GEOSException &e) \
41  { \
42  if ( errorMsg ) \
43  { \
44  *errorMsg = e.what(); \
45  } \
46  return r; \
47  }
48 
50 
51 static void throwGEOSException( const char *fmt, ... )
52 {
53  va_list ap;
54  char buffer[1024];
55 
56  va_start( ap, fmt );
57  vsnprintf( buffer, sizeof buffer, fmt, ap );
58  va_end( ap );
59 
60  qWarning( "GEOS exception: %s", buffer );
61  QString message = QString::fromUtf8( buffer );
62 
63 #ifdef _MSC_VER
64  // stupid stupid MSVC, *SOMETIMES* raises it's own exception if we throw GEOSException, resulting in a crash!
65  // see https://github.com/qgis/QGIS/issues/22709
66  // if you want to test alternative fixes for this, run the testqgsexpression.cpp test suite - that will crash
67  // and burn on the "line_interpolate_point point" test if a GEOSException is thrown.
68  // TODO - find a real fix for the underlying issue
69  try
70  {
71  throw GEOSException( message );
72  }
73  catch ( ... )
74  {
75  // oops, msvc threw an exception when we tried to throw the exception!
76  // just throw nothing instead (except your mouse at your monitor)
77  }
78 #else
79  throw GEOSException( message );
80 #endif
81 }
82 
83 
84 static void printGEOSNotice( const char *fmt, ... )
85 {
86 #if defined(QGISDEBUG)
87  va_list ap;
88  char buffer[1024];
89 
90  va_start( ap, fmt );
91  vsnprintf( buffer, sizeof buffer, fmt, ap );
92  va_end( ap );
93 
94  QgsDebugMsg( QStringLiteral( "GEOS notice: %1" ).arg( QString::fromUtf8( buffer ) ) );
95 #else
96  Q_UNUSED( fmt )
97 #endif
98 }
99 
100 class GEOSInit
101 {
102  public:
103  GEOSContextHandle_t ctxt;
104 
105  GEOSInit()
106  {
107  ctxt = initGEOS_r( printGEOSNotice, throwGEOSException );
108  }
109 
110  ~GEOSInit()
111  {
112  finishGEOS_r( ctxt );
113  }
114 
115  GEOSInit( const GEOSInit &rh ) = delete;
116  GEOSInit &operator=( const GEOSInit &rh ) = delete;
117 };
118 
119 Q_GLOBAL_STATIC( GEOSInit, geosinit )
120 
121 void geos::GeosDeleter::operator()( GEOSGeometry *geom )
122 {
123  GEOSGeom_destroy_r( geosinit()->ctxt, geom );
124 }
125 
126 void geos::GeosDeleter::operator()( const GEOSPreparedGeometry *geom )
127 {
128  GEOSPreparedGeom_destroy_r( geosinit()->ctxt, geom );
129 }
130 
131 void geos::GeosDeleter::operator()( GEOSBufferParams *params )
132 {
133  GEOSBufferParams_destroy_r( geosinit()->ctxt, params );
134 }
135 
136 void geos::GeosDeleter::operator()( GEOSCoordSequence *sequence )
137 {
138  GEOSCoordSeq_destroy_r( geosinit()->ctxt, sequence );
139 }
140 
141 
143 
144 
146  : QgsGeometryEngine( geometry )
147  , mGeos( nullptr )
148  , mPrecision( precision )
149 {
150  cacheGeos();
151 }
152 
154 {
155  QgsGeometry g( QgsGeos::fromGeos( geos ) );
156  GEOSGeom_destroy_r( QgsGeos::getGEOSHandler(), geos );
157  return g;
158 }
159 
161 {
162  QgsGeometry g( QgsGeos::fromGeos( geos.get() ) );
163  return g;
164 }
165 
167 {
168  if ( geometry.isNull() )
169  {
170  return nullptr;
171  }
172 
173  return asGeos( geometry.constGet(), precision );
174 }
175 
176 QgsGeometry::OperationResult QgsGeos::addPart( QgsGeometry &geometry, GEOSGeometry *newPart )
177 {
178  if ( geometry.isNull() )
179  {
181  }
182  if ( !newPart )
183  {
185  }
186 
187  std::unique_ptr< QgsAbstractGeometry > geom = fromGeos( newPart );
188  return QgsGeometryEditUtils::addPart( geometry.get(), std::move( geom ) );
189 }
190 
192 {
193  mGeos.reset();
194  mGeosPrepared.reset();
195  cacheGeos();
196 }
197 
199 {
200  mGeosPrepared.reset();
201  if ( mGeos )
202  {
203  mGeosPrepared.reset( GEOSPrepare_r( geosinit()->ctxt, mGeos.get() ) );
204  }
205 }
206 
207 void QgsGeos::cacheGeos() const
208 {
209  if ( !mGeometry )
210  {
211  return;
212  }
213 
214  mGeos = asGeos( mGeometry, mPrecision );
215 }
216 
217 QgsAbstractGeometry *QgsGeos::intersection( const QgsAbstractGeometry *geom, QString *errorMsg ) const
218 {
219  return overlay( geom, OverlayIntersection, errorMsg ).release();
220 }
221 
222 QgsAbstractGeometry *QgsGeos::difference( const QgsAbstractGeometry *geom, QString *errorMsg ) const
223 {
224  return overlay( geom, OverlayDifference, errorMsg ).release();
225 }
226 
227 std::unique_ptr<QgsAbstractGeometry> QgsGeos::clip( const QgsRectangle &rect, QString *errorMsg ) const
228 {
229  if ( !mGeos || rect.isNull() || rect.isEmpty() )
230  {
231  return nullptr;
232  }
233 
234  try
235  {
236  geos::unique_ptr opGeom( GEOSClipByRect_r( geosinit()->ctxt, mGeos.get(), rect.xMinimum(), rect.yMinimum(), rect.xMaximum(), rect.yMaximum() ) );
237  return fromGeos( opGeom.get() );
238  }
239  catch ( GEOSException &e )
240  {
241  if ( errorMsg )
242  {
243  *errorMsg = e.what();
244  }
245  return nullptr;
246  }
247 }
248 
249 
250 
251 
252 void QgsGeos::subdivideRecursive( const GEOSGeometry *currentPart, int maxNodes, int depth, QgsGeometryCollection *parts, const QgsRectangle &clipRect ) const
253 {
254  int partType = GEOSGeomTypeId_r( geosinit()->ctxt, currentPart );
255  if ( qgsDoubleNear( clipRect.width(), 0.0 ) && qgsDoubleNear( clipRect.height(), 0.0 ) )
256  {
257  if ( partType == GEOS_POINT )
258  {
259  parts->addGeometry( fromGeos( currentPart ).release() );
260  return;
261  }
262  else
263  {
264  return;
265  }
266  }
267 
268  if ( partType == GEOS_MULTILINESTRING || partType == GEOS_MULTIPOLYGON || partType == GEOS_GEOMETRYCOLLECTION )
269  {
270  int partCount = GEOSGetNumGeometries_r( geosinit()->ctxt, currentPart );
271  for ( int i = 0; i < partCount; ++i )
272  {
273  subdivideRecursive( GEOSGetGeometryN_r( geosinit()->ctxt, currentPart, i ), maxNodes, depth, parts, clipRect );
274  }
275  return;
276  }
277 
278  if ( depth > 50 )
279  {
280  parts->addGeometry( fromGeos( currentPart ).release() );
281  return;
282  }
283 
284  int vertexCount = GEOSGetNumCoordinates_r( geosinit()->ctxt, currentPart );
285  if ( vertexCount == 0 )
286  {
287  return;
288  }
289  else if ( vertexCount < maxNodes )
290  {
291  parts->addGeometry( fromGeos( currentPart ).release() );
292  return;
293  }
294 
295  // chop clipping rect in half by longest side
296  double width = clipRect.width();
297  double height = clipRect.height();
298  QgsRectangle halfClipRect1 = clipRect;
299  QgsRectangle halfClipRect2 = clipRect;
300  if ( width > height )
301  {
302  halfClipRect1.setXMaximum( clipRect.xMinimum() + width / 2.0 );
303  halfClipRect2.setXMinimum( halfClipRect1.xMaximum() );
304  }
305  else
306  {
307  halfClipRect1.setYMaximum( clipRect.yMinimum() + height / 2.0 );
308  halfClipRect2.setYMinimum( halfClipRect1.yMaximum() );
309  }
310 
311  if ( height <= 0 )
312  {
313  halfClipRect1.setYMinimum( halfClipRect1.yMinimum() - std::numeric_limits<double>::epsilon() );
314  halfClipRect2.setYMinimum( halfClipRect2.yMinimum() - std::numeric_limits<double>::epsilon() );
315  halfClipRect1.setYMaximum( halfClipRect1.yMaximum() + std::numeric_limits<double>::epsilon() );
316  halfClipRect2.setYMaximum( halfClipRect2.yMaximum() + std::numeric_limits<double>::epsilon() );
317  }
318  if ( width <= 0 )
319  {
320  halfClipRect1.setXMinimum( halfClipRect1.xMinimum() - std::numeric_limits<double>::epsilon() );
321  halfClipRect2.setXMinimum( halfClipRect2.xMinimum() - std::numeric_limits<double>::epsilon() );
322  halfClipRect1.setXMaximum( halfClipRect1.xMaximum() + std::numeric_limits<double>::epsilon() );
323  halfClipRect2.setXMaximum( halfClipRect2.xMaximum() + std::numeric_limits<double>::epsilon() );
324  }
325 
326  geos::unique_ptr clipPart1( GEOSClipByRect_r( geosinit()->ctxt, currentPart, halfClipRect1.xMinimum(), halfClipRect1.yMinimum(), halfClipRect1.xMaximum(), halfClipRect1.yMaximum() ) );
327  geos::unique_ptr clipPart2( GEOSClipByRect_r( geosinit()->ctxt, currentPart, halfClipRect2.xMinimum(), halfClipRect2.yMinimum(), halfClipRect2.xMaximum(), halfClipRect2.yMaximum() ) );
328 
329  ++depth;
330 
331  if ( clipPart1 )
332  {
333  subdivideRecursive( clipPart1.get(), maxNodes, depth, parts, halfClipRect1 );
334  }
335  if ( clipPart2 )
336  {
337  subdivideRecursive( clipPart2.get(), maxNodes, depth, parts, halfClipRect2 );
338  }
339 }
340 
341 std::unique_ptr<QgsAbstractGeometry> QgsGeos::subdivide( int maxNodes, QString *errorMsg ) const
342 {
343  if ( !mGeos )
344  {
345  return nullptr;
346  }
347 
348  // minimum allowed max is 8
349  maxNodes = std::max( maxNodes, 8 );
350 
351  std::unique_ptr< QgsGeometryCollection > parts = QgsGeometryFactory::createCollectionOfType( mGeometry->wkbType() );
352  try
353  {
354  subdivideRecursive( mGeos.get(), maxNodes, 0, parts.get(), mGeometry->boundingBox() );
355  }
356  CATCH_GEOS_WITH_ERRMSG( nullptr )
357 
358  return std::move( parts );
359 }
360 
361 QgsAbstractGeometry *QgsGeos::combine( const QgsAbstractGeometry *geom, QString *errorMsg ) const
362 {
363  return overlay( geom, OverlayUnion, errorMsg ).release();
364 }
365 
366 QgsAbstractGeometry *QgsGeos::combine( const QVector<QgsAbstractGeometry *> &geomList, QString *errorMsg ) const
367 {
368  QVector< GEOSGeometry * > geosGeometries;
369  geosGeometries.reserve( geomList.size() );
370  for ( const QgsAbstractGeometry *g : geomList )
371  {
372  if ( !g )
373  continue;
374 
375  geosGeometries << asGeos( g, mPrecision ).release();
376  }
377 
378  geos::unique_ptr geomUnion;
379  try
380  {
381  geos::unique_ptr geomCollection = createGeosCollection( GEOS_GEOMETRYCOLLECTION, geosGeometries );
382  geomUnion.reset( GEOSUnaryUnion_r( geosinit()->ctxt, geomCollection.get() ) );
383  }
384  CATCH_GEOS_WITH_ERRMSG( nullptr )
385 
386  std::unique_ptr< QgsAbstractGeometry > result = fromGeos( geomUnion.get() );
387  return result.release();
388 }
389 
390 QgsAbstractGeometry *QgsGeos::combine( const QVector<QgsGeometry> &geomList, QString *errorMsg ) const
391 {
392  QVector< GEOSGeometry * > geosGeometries;
393  geosGeometries.reserve( geomList.size() );
394  for ( const QgsGeometry &g : geomList )
395  {
396  if ( g.isNull() )
397  continue;
398 
399  geosGeometries << asGeos( g.constGet(), mPrecision ).release();
400  }
401 
402  geos::unique_ptr geomUnion;
403  try
404  {
405  geos::unique_ptr geomCollection = createGeosCollection( GEOS_GEOMETRYCOLLECTION, geosGeometries );
406  geomUnion.reset( GEOSUnaryUnion_r( geosinit()->ctxt, geomCollection.get() ) );
407  }
408  CATCH_GEOS_WITH_ERRMSG( nullptr )
409 
410  std::unique_ptr< QgsAbstractGeometry > result = fromGeos( geomUnion.get() );
411  return result.release();
412 }
413 
414 QgsAbstractGeometry *QgsGeos::symDifference( const QgsAbstractGeometry *geom, QString *errorMsg ) const
415 {
416  return overlay( geom, OverlaySymDifference, errorMsg ).release();
417 }
418 
419 double QgsGeos::distance( const QgsAbstractGeometry *geom, QString *errorMsg ) const
420 {
421  double distance = -1.0;
422  if ( !mGeos )
423  {
424  return distance;
425  }
426 
427  geos::unique_ptr otherGeosGeom( asGeos( geom, mPrecision ) );
428  if ( !otherGeosGeom )
429  {
430  return distance;
431  }
432 
433  try
434  {
435  GEOSDistance_r( geosinit()->ctxt, mGeos.get(), otherGeosGeom.get(), &distance );
436  }
437  CATCH_GEOS_WITH_ERRMSG( -1.0 )
438 
439  return distance;
440 }
441 
442 double QgsGeos::hausdorffDistance( const QgsAbstractGeometry *geom, QString *errorMsg ) const
443 {
444  double distance = -1.0;
445  if ( !mGeos )
446  {
447  return distance;
448  }
449 
450  geos::unique_ptr otherGeosGeom( asGeos( geom, mPrecision ) );
451  if ( !otherGeosGeom )
452  {
453  return distance;
454  }
455 
456  try
457  {
458  GEOSHausdorffDistance_r( geosinit()->ctxt, mGeos.get(), otherGeosGeom.get(), &distance );
459  }
460  CATCH_GEOS_WITH_ERRMSG( -1.0 )
461 
462  return distance;
463 }
464 
465 double QgsGeos::hausdorffDistanceDensify( const QgsAbstractGeometry *geom, double densifyFraction, QString *errorMsg ) const
466 {
467  double distance = -1.0;
468  if ( !mGeos )
469  {
470  return distance;
471  }
472 
473  geos::unique_ptr otherGeosGeom( asGeos( geom, mPrecision ) );
474  if ( !otherGeosGeom )
475  {
476  return distance;
477  }
478 
479  try
480  {
481  GEOSHausdorffDistanceDensify_r( geosinit()->ctxt, mGeos.get(), otherGeosGeom.get(), densifyFraction, &distance );
482  }
483  CATCH_GEOS_WITH_ERRMSG( -1.0 )
484 
485  return distance;
486 }
487 
488 bool QgsGeos::intersects( const QgsAbstractGeometry *geom, QString *errorMsg ) const
489 {
490  return relation( geom, RelationIntersects, errorMsg );
491 }
492 
493 bool QgsGeos::touches( const QgsAbstractGeometry *geom, QString *errorMsg ) const
494 {
495  return relation( geom, RelationTouches, errorMsg );
496 }
497 
498 bool QgsGeos::crosses( const QgsAbstractGeometry *geom, QString *errorMsg ) const
499 {
500  return relation( geom, RelationCrosses, errorMsg );
501 }
502 
503 bool QgsGeos::within( const QgsAbstractGeometry *geom, QString *errorMsg ) const
504 {
505  return relation( geom, RelationWithin, errorMsg );
506 }
507 
508 bool QgsGeos::overlaps( const QgsAbstractGeometry *geom, QString *errorMsg ) const
509 {
510  return relation( geom, RelationOverlaps, errorMsg );
511 }
512 
513 bool QgsGeos::contains( const QgsAbstractGeometry *geom, QString *errorMsg ) const
514 {
515  return relation( geom, RelationContains, errorMsg );
516 }
517 
518 bool QgsGeos::disjoint( const QgsAbstractGeometry *geom, QString *errorMsg ) const
519 {
520  return relation( geom, RelationDisjoint, errorMsg );
521 }
522 
523 QString QgsGeos::relate( const QgsAbstractGeometry *geom, QString *errorMsg ) const
524 {
525  if ( !mGeos )
526  {
527  return QString();
528  }
529 
530  geos::unique_ptr geosGeom( asGeos( geom, mPrecision ) );
531  if ( !geosGeom )
532  {
533  return QString();
534  }
535 
536  QString result;
537  try
538  {
539  char *r = GEOSRelate_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() );
540  if ( r )
541  {
542  result = QString( r );
543  GEOSFree_r( geosinit()->ctxt, r );
544  }
545  }
546  catch ( GEOSException &e )
547  {
548  if ( errorMsg )
549  {
550  *errorMsg = e.what();
551  }
552  }
553 
554  return result;
555 }
556 
557 bool QgsGeos::relatePattern( const QgsAbstractGeometry *geom, const QString &pattern, QString *errorMsg ) const
558 {
559  if ( !mGeos || !geom )
560  {
561  return false;
562  }
563 
564  geos::unique_ptr geosGeom( asGeos( geom, mPrecision ) );
565  if ( !geosGeom )
566  {
567  return false;
568  }
569 
570  bool result = false;
571  try
572  {
573  result = ( GEOSRelatePattern_r( geosinit()->ctxt, mGeos.get(), geosGeom.get(), pattern.toLocal8Bit().constData() ) == 1 );
574  }
575  catch ( GEOSException &e )
576  {
577  if ( errorMsg )
578  {
579  *errorMsg = e.what();
580  }
581  }
582 
583  return result;
584 }
585 
586 double QgsGeos::area( QString *errorMsg ) const
587 {
588  double area = -1.0;
589  if ( !mGeos )
590  {
591  return area;
592  }
593 
594  try
595  {
596  if ( GEOSArea_r( geosinit()->ctxt, mGeos.get(), &area ) != 1 )
597  return -1.0;
598  }
599  CATCH_GEOS_WITH_ERRMSG( -1.0 );
600  return area;
601 }
602 
603 double QgsGeos::length( QString *errorMsg ) const
604 {
605  double length = -1.0;
606  if ( !mGeos )
607  {
608  return length;
609  }
610  try
611  {
612  if ( GEOSLength_r( geosinit()->ctxt, mGeos.get(), &length ) != 1 )
613  return -1.0;
614  }
615  CATCH_GEOS_WITH_ERRMSG( -1.0 )
616  return length;
617 }
618 
620  QVector<QgsGeometry> &newGeometries,
621  bool topological,
622  QgsPointSequence &topologyTestPoints,
623  QString *errorMsg ) const
624 {
625 
626  EngineOperationResult returnCode = Success;
627  if ( !mGeos || !mGeometry )
628  {
629  return InvalidBaseGeometry;
630  }
631 
632  //return if this type is point/multipoint
633  if ( mGeometry->dimension() == 0 )
634  {
635  return SplitCannotSplitPoint; //cannot split points
636  }
637 
638  if ( !GEOSisValid_r( geosinit()->ctxt, mGeos.get() ) )
639  return InvalidBaseGeometry;
640 
641  //make sure splitLine is valid
642  if ( ( mGeometry->dimension() == 1 && splitLine.numPoints() < 1 ) ||
643  ( mGeometry->dimension() == 2 && splitLine.numPoints() < 2 ) )
644  return InvalidInput;
645 
646  newGeometries.clear();
647  geos::unique_ptr splitLineGeos;
648 
649  try
650  {
651  if ( splitLine.numPoints() > 1 )
652  {
653  splitLineGeos = createGeosLinestring( &splitLine, mPrecision );
654  }
655  else if ( splitLine.numPoints() == 1 )
656  {
657  splitLineGeos = createGeosPointXY( splitLine.xAt( 0 ), splitLine.yAt( 0 ), false, 0, false, 0, 2, mPrecision );
658  }
659  else
660  {
661  return InvalidInput;
662  }
663 
664  if ( !GEOSisValid_r( geosinit()->ctxt, splitLineGeos.get() ) || !GEOSisSimple_r( geosinit()->ctxt, splitLineGeos.get() ) )
665  {
666  return InvalidInput;
667  }
668 
669  if ( topological )
670  {
671  //find out candidate points for topological corrections
672  if ( !topologicalTestPointsSplit( splitLineGeos.get(), topologyTestPoints ) )
673  {
674  return InvalidInput; // TODO: is it really an invalid input?
675  }
676  }
677 
678  //call split function depending on geometry type
679  if ( mGeometry->dimension() == 1 )
680  {
681  returnCode = splitLinearGeometry( splitLineGeos.get(), newGeometries );
682  }
683  else if ( mGeometry->dimension() == 2 )
684  {
685  returnCode = splitPolygonGeometry( splitLineGeos.get(), newGeometries );
686  }
687  else
688  {
689  return InvalidInput;
690  }
691  }
693 
694  return returnCode;
695 }
696 
697 
698 
699 bool QgsGeos::topologicalTestPointsSplit( const GEOSGeometry *splitLine, QgsPointSequence &testPoints, QString *errorMsg ) const
700 {
701  //Find out the intersection points between splitLineGeos and this geometry.
702  //These points need to be tested for topological correctness by the calling function
703  //if topological editing is enabled
704 
705  if ( !mGeos )
706  {
707  return false;
708  }
709 
710  try
711  {
712  testPoints.clear();
713  geos::unique_ptr intersectionGeom( GEOSIntersection_r( geosinit()->ctxt, mGeos.get(), splitLine ) );
714  if ( !intersectionGeom )
715  return false;
716 
717  bool simple = false;
718  int nIntersectGeoms = 1;
719  if ( GEOSGeomTypeId_r( geosinit()->ctxt, intersectionGeom.get() ) == GEOS_LINESTRING
720  || GEOSGeomTypeId_r( geosinit()->ctxt, intersectionGeom.get() ) == GEOS_POINT )
721  simple = true;
722 
723  if ( !simple )
724  nIntersectGeoms = GEOSGetNumGeometries_r( geosinit()->ctxt, intersectionGeom.get() );
725 
726  for ( int i = 0; i < nIntersectGeoms; ++i )
727  {
728  const GEOSGeometry *currentIntersectGeom = nullptr;
729  if ( simple )
730  currentIntersectGeom = intersectionGeom.get();
731  else
732  currentIntersectGeom = GEOSGetGeometryN_r( geosinit()->ctxt, intersectionGeom.get(), i );
733 
734  const GEOSCoordSequence *lineSequence = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, currentIntersectGeom );
735  unsigned int sequenceSize = 0;
736  double x, y;
737  if ( GEOSCoordSeq_getSize_r( geosinit()->ctxt, lineSequence, &sequenceSize ) != 0 )
738  {
739  for ( unsigned int i = 0; i < sequenceSize; ++i )
740  {
741  if ( GEOSCoordSeq_getX_r( geosinit()->ctxt, lineSequence, i, &x ) != 0 )
742  {
743  if ( GEOSCoordSeq_getY_r( geosinit()->ctxt, lineSequence, i, &y ) != 0 )
744  {
745  testPoints.push_back( QgsPoint( x, y ) );
746  }
747  }
748  }
749  }
750  }
751  }
752  CATCH_GEOS_WITH_ERRMSG( true )
753 
754  return true;
755 }
756 
757 geos::unique_ptr QgsGeos::linePointDifference( GEOSGeometry *GEOSsplitPoint ) const
758 {
759  int type = GEOSGeomTypeId_r( geosinit()->ctxt, mGeos.get() );
760 
761  std::unique_ptr< QgsMultiCurve > multiCurve;
762  if ( type == GEOS_MULTILINESTRING )
763  {
764  multiCurve.reset( qgsgeometry_cast<QgsMultiCurve *>( mGeometry->clone() ) );
765  }
766  else if ( type == GEOS_LINESTRING )
767  {
768  multiCurve.reset( new QgsMultiCurve() );
769  multiCurve->addGeometry( mGeometry->clone() );
770  }
771  else
772  {
773  return nullptr;
774  }
775 
776  if ( !multiCurve )
777  {
778  return nullptr;
779  }
780 
781 
782  std::unique_ptr< QgsAbstractGeometry > splitGeom( fromGeos( GEOSsplitPoint ) );
783  QgsPoint *splitPoint = qgsgeometry_cast<QgsPoint *>( splitGeom.get() );
784  if ( !splitPoint )
785  {
786  return nullptr;
787  }
788 
789  QgsMultiCurve lines;
790 
791  //For each part
792  for ( int i = 0; i < multiCurve->numGeometries(); ++i )
793  {
794  const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( multiCurve->geometryN( i ) );
795  if ( line )
796  {
797  //For each segment
798  QgsLineString newLine;
799  newLine.addVertex( line->pointN( 0 ) );
800  int nVertices = line->numPoints();
801  for ( int j = 1; j < ( nVertices - 1 ); ++j )
802  {
803  QgsPoint currentPoint = line->pointN( j );
804  newLine.addVertex( currentPoint );
805  if ( currentPoint == *splitPoint )
806  {
807  lines.addGeometry( newLine.clone() );
808  newLine = QgsLineString();
809  newLine.addVertex( currentPoint );
810  }
811  }
812  newLine.addVertex( line->pointN( nVertices - 1 ) );
813  lines.addGeometry( newLine.clone() );
814  }
815  }
816 
817  return asGeos( &lines, mPrecision );
818 }
819 
820 QgsGeometryEngine::EngineOperationResult QgsGeos::splitLinearGeometry( GEOSGeometry *splitLine, QVector<QgsGeometry> &newGeometries ) const
821 {
822  if ( !splitLine )
823  return InvalidInput;
824 
825  if ( !mGeos )
826  return InvalidBaseGeometry;
827 
828  //first test if linestring intersects geometry. If not, return straight away
829  if ( !GEOSIntersects_r( geosinit()->ctxt, splitLine, mGeos.get() ) )
830  return NothingHappened;
831 
832  //check that split line has no linear intersection
833  int linearIntersect = GEOSRelatePattern_r( geosinit()->ctxt, mGeos.get(), splitLine, "1********" );
834  if ( linearIntersect > 0 )
835  return InvalidInput;
836 
837  int splitGeomType = GEOSGeomTypeId_r( geosinit()->ctxt, splitLine );
838 
839  geos::unique_ptr splitGeom;
840  if ( splitGeomType == GEOS_POINT )
841  {
842  splitGeom = linePointDifference( splitLine );
843  }
844  else
845  {
846  splitGeom.reset( GEOSDifference_r( geosinit()->ctxt, mGeos.get(), splitLine ) );
847  }
848  QVector<GEOSGeometry *> lineGeoms;
849 
850  int splitType = GEOSGeomTypeId_r( geosinit()->ctxt, splitGeom.get() );
851  if ( splitType == GEOS_MULTILINESTRING )
852  {
853  int nGeoms = GEOSGetNumGeometries_r( geosinit()->ctxt, splitGeom.get() );
854  lineGeoms.reserve( nGeoms );
855  for ( int i = 0; i < nGeoms; ++i )
856  lineGeoms << GEOSGeom_clone_r( geosinit()->ctxt, GEOSGetGeometryN_r( geosinit()->ctxt, splitGeom.get(), i ) );
857 
858  }
859  else
860  {
861  lineGeoms << GEOSGeom_clone_r( geosinit()->ctxt, splitGeom.get() );
862  }
863 
864  mergeGeometriesMultiTypeSplit( lineGeoms );
865 
866  for ( int i = 0; i < lineGeoms.size(); ++i )
867  {
868  newGeometries << QgsGeometry( fromGeos( lineGeoms[i] ) );
869  GEOSGeom_destroy_r( geosinit()->ctxt, lineGeoms[i] );
870  }
871 
872  return Success;
873 }
874 
875 QgsGeometryEngine::EngineOperationResult QgsGeos::splitPolygonGeometry( GEOSGeometry *splitLine, QVector<QgsGeometry> &newGeometries ) const
876 {
877  if ( !splitLine )
878  return InvalidInput;
879 
880  if ( !mGeos )
881  return InvalidBaseGeometry;
882 
883  //first test if linestring intersects geometry. If not, return straight away
884  if ( !GEOSIntersects_r( geosinit()->ctxt, splitLine, mGeos.get() ) )
885  return NothingHappened;
886 
887  //first union all the polygon rings together (to get them noded, see JTS developer guide)
888  geos::unique_ptr nodedGeometry = nodeGeometries( splitLine, mGeos.get() );
889  if ( !nodedGeometry )
890  return NodedGeometryError; //an error occurred during noding
891 
892  const GEOSGeometry *noded = nodedGeometry.get();
893  geos::unique_ptr polygons( GEOSPolygonize_r( geosinit()->ctxt, &noded, 1 ) );
894  if ( !polygons || numberOfGeometries( polygons.get() ) == 0 )
895  {
896  return InvalidBaseGeometry;
897  }
898 
899  //test every polygon if contained in original geometry
900  //include in result if yes
901  QVector<GEOSGeometry *> testedGeometries;
902  geos::unique_ptr intersectGeometry;
903 
904  //ratio intersect geometry / geometry. This should be close to 1
905  //if the polygon belongs to the input geometry
906 
907  for ( int i = 0; i < numberOfGeometries( polygons.get() ); i++ )
908  {
909  const GEOSGeometry *polygon = GEOSGetGeometryN_r( geosinit()->ctxt, polygons.get(), i );
910  intersectGeometry.reset( GEOSIntersection_r( geosinit()->ctxt, mGeos.get(), polygon ) );
911  if ( !intersectGeometry )
912  {
913  QgsDebugMsg( QStringLiteral( "intersectGeometry is nullptr" ) );
914  continue;
915  }
916 
917  double intersectionArea;
918  GEOSArea_r( geosinit()->ctxt, intersectGeometry.get(), &intersectionArea );
919 
920  double polygonArea;
921  GEOSArea_r( geosinit()->ctxt, polygon, &polygonArea );
922 
923  const double areaRatio = intersectionArea / polygonArea;
924  if ( areaRatio > 0.99 && areaRatio < 1.01 )
925  testedGeometries << GEOSGeom_clone_r( geosinit()->ctxt, polygon );
926  }
927 
928  int nGeometriesThis = numberOfGeometries( mGeos.get() ); //original number of geometries
929  if ( testedGeometries.empty() || testedGeometries.size() == nGeometriesThis )
930  {
931  //no split done, preserve original geometry
932  for ( int i = 0; i < testedGeometries.size(); ++i )
933  {
934  GEOSGeom_destroy_r( geosinit()->ctxt, testedGeometries[i] );
935  }
936  return NothingHappened;
937  }
938 
939  mergeGeometriesMultiTypeSplit( testedGeometries );
940 
941  int i;
942  for ( i = 0; i < testedGeometries.size() && GEOSisValid_r( geosinit()->ctxt, testedGeometries[i] ); ++i )
943  ;
944 
945  if ( i < testedGeometries.size() )
946  {
947  for ( i = 0; i < testedGeometries.size(); ++i )
948  GEOSGeom_destroy_r( geosinit()->ctxt, testedGeometries[i] );
949 
950  return InvalidBaseGeometry;
951  }
952 
953  for ( i = 0; i < testedGeometries.size(); ++i )
954  {
955  newGeometries << QgsGeometry( fromGeos( testedGeometries[i] ) );
956  GEOSGeom_destroy_r( geosinit()->ctxt, testedGeometries[i] );
957  }
958 
959  return Success;
960 }
961 
962 geos::unique_ptr QgsGeos::nodeGeometries( const GEOSGeometry *splitLine, const GEOSGeometry *geom )
963 {
964  if ( !splitLine || !geom )
965  return nullptr;
966 
967  geos::unique_ptr geometryBoundary;
968  if ( GEOSGeomTypeId_r( geosinit()->ctxt, geom ) == GEOS_POLYGON || GEOSGeomTypeId_r( geosinit()->ctxt, geom ) == GEOS_MULTIPOLYGON )
969  geometryBoundary.reset( GEOSBoundary_r( geosinit()->ctxt, geom ) );
970  else
971  geometryBoundary.reset( GEOSGeom_clone_r( geosinit()->ctxt, geom ) );
972 
973  geos::unique_ptr splitLineClone( GEOSGeom_clone_r( geosinit()->ctxt, splitLine ) );
974  geos::unique_ptr unionGeometry( GEOSUnion_r( geosinit()->ctxt, splitLineClone.get(), geometryBoundary.get() ) );
975 
976  return unionGeometry;
977 }
978 
979 int QgsGeos::mergeGeometriesMultiTypeSplit( QVector<GEOSGeometry *> &splitResult ) const
980 {
981  if ( !mGeos )
982  return 1;
983 
984  //convert mGeos to geometry collection
985  int type = GEOSGeomTypeId_r( geosinit()->ctxt, mGeos.get() );
986  if ( type != GEOS_GEOMETRYCOLLECTION &&
987  type != GEOS_MULTILINESTRING &&
988  type != GEOS_MULTIPOLYGON &&
989  type != GEOS_MULTIPOINT )
990  return 0;
991 
992  QVector<GEOSGeometry *> copyList = splitResult;
993  splitResult.clear();
994 
995  //collect all the geometries that belong to the initial multifeature
996  QVector<GEOSGeometry *> unionGeom;
997 
998  for ( int i = 0; i < copyList.size(); ++i )
999  {
1000  //is this geometry a part of the original multitype?
1001  bool isPart = false;
1002  for ( int j = 0; j < GEOSGetNumGeometries_r( geosinit()->ctxt, mGeos.get() ); j++ )
1003  {
1004  if ( GEOSEquals_r( geosinit()->ctxt, copyList[i], GEOSGetGeometryN_r( geosinit()->ctxt, mGeos.get(), j ) ) )
1005  {
1006  isPart = true;
1007  break;
1008  }
1009  }
1010 
1011  if ( isPart )
1012  {
1013  unionGeom << copyList[i];
1014  }
1015  else
1016  {
1017  QVector<GEOSGeometry *> geomVector;
1018  geomVector << copyList[i];
1019 
1020  if ( type == GEOS_MULTILINESTRING )
1021  splitResult << createGeosCollection( GEOS_MULTILINESTRING, geomVector ).release();
1022  else if ( type == GEOS_MULTIPOLYGON )
1023  splitResult << createGeosCollection( GEOS_MULTIPOLYGON, geomVector ).release();
1024  else
1025  GEOSGeom_destroy_r( geosinit()->ctxt, copyList[i] );
1026  }
1027  }
1028 
1029  //make multifeature out of unionGeom
1030  if ( !unionGeom.isEmpty() )
1031  {
1032  if ( type == GEOS_MULTILINESTRING )
1033  splitResult << createGeosCollection( GEOS_MULTILINESTRING, unionGeom ).release();
1034  else if ( type == GEOS_MULTIPOLYGON )
1035  splitResult << createGeosCollection( GEOS_MULTIPOLYGON, unionGeom ).release();
1036  }
1037  else
1038  {
1039  unionGeom.clear();
1040  }
1041 
1042  return 0;
1043 }
1044 
1045 geos::unique_ptr QgsGeos::createGeosCollection( int typeId, const QVector<GEOSGeometry *> &geoms )
1046 {
1047  int nNullGeoms = geoms.count( nullptr );
1048  int nNotNullGeoms = geoms.size() - nNullGeoms;
1049 
1050  GEOSGeometry **geomarr = new GEOSGeometry*[ nNotNullGeoms ];
1051  if ( !geomarr )
1052  {
1053  return nullptr;
1054  }
1055 
1056  int i = 0;
1057  QVector<GEOSGeometry *>::const_iterator geomIt = geoms.constBegin();
1058  for ( ; geomIt != geoms.constEnd(); ++geomIt )
1059  {
1060  if ( *geomIt )
1061  {
1062  geomarr[i] = *geomIt;
1063  ++i;
1064  }
1065  }
1066  geos::unique_ptr geom;
1067 
1068  try
1069  {
1070  geom.reset( GEOSGeom_createCollection_r( geosinit()->ctxt, typeId, geomarr, nNotNullGeoms ) );
1071  }
1072  catch ( GEOSException & )
1073  {
1074  }
1075 
1076  delete [] geomarr;
1077 
1078  return geom;
1079 }
1080 
1081 std::unique_ptr<QgsAbstractGeometry> QgsGeos::fromGeos( const GEOSGeometry *geos )
1082 {
1083  if ( !geos )
1084  {
1085  return nullptr;
1086  }
1087 
1088  int nCoordDims = GEOSGeom_getCoordinateDimension_r( geosinit()->ctxt, geos );
1089  int nDims = GEOSGeom_getDimensions_r( geosinit()->ctxt, geos );
1090  bool hasZ = ( nCoordDims == 3 );
1091  bool hasM = ( ( nDims - nCoordDims ) == 1 );
1092 
1093  switch ( GEOSGeomTypeId_r( geosinit()->ctxt, geos ) )
1094  {
1095  case GEOS_POINT: // a point
1096  {
1097  const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, geos );
1098  return std::unique_ptr<QgsAbstractGeometry>( coordSeqPoint( cs, 0, hasZ, hasM ).clone() );
1099  }
1100  case GEOS_LINESTRING:
1101  {
1102  return sequenceToLinestring( geos, hasZ, hasM );
1103  }
1104  case GEOS_POLYGON:
1105  {
1106  return fromGeosPolygon( geos );
1107  }
1108  case GEOS_MULTIPOINT:
1109  {
1110  std::unique_ptr< QgsMultiPoint > multiPoint( new QgsMultiPoint() );
1111  int nParts = GEOSGetNumGeometries_r( geosinit()->ctxt, geos );
1112  multiPoint->reserve( nParts );
1113  for ( int i = 0; i < nParts; ++i )
1114  {
1115  const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, GEOSGetGeometryN_r( geosinit()->ctxt, geos, i ) );
1116  if ( cs )
1117  {
1118  multiPoint->addGeometry( coordSeqPoint( cs, 0, hasZ, hasM ).clone() );
1119  }
1120  }
1121  return std::move( multiPoint );
1122  }
1123  case GEOS_MULTILINESTRING:
1124  {
1125  std::unique_ptr< QgsMultiLineString > multiLineString( new QgsMultiLineString() );
1126  int nParts = GEOSGetNumGeometries_r( geosinit()->ctxt, geos );
1127  multiLineString->reserve( nParts );
1128  for ( int i = 0; i < nParts; ++i )
1129  {
1130  std::unique_ptr< QgsLineString >line( sequenceToLinestring( GEOSGetGeometryN_r( geosinit()->ctxt, geos, i ), hasZ, hasM ) );
1131  if ( line )
1132  {
1133  multiLineString->addGeometry( line.release() );
1134  }
1135  }
1136  return std::move( multiLineString );
1137  }
1138  case GEOS_MULTIPOLYGON:
1139  {
1140  std::unique_ptr< QgsMultiPolygon > multiPolygon( new QgsMultiPolygon() );
1141 
1142  int nParts = GEOSGetNumGeometries_r( geosinit()->ctxt, geos );
1143  multiPolygon->reserve( nParts );
1144  for ( int i = 0; i < nParts; ++i )
1145  {
1146  std::unique_ptr< QgsPolygon > poly = fromGeosPolygon( GEOSGetGeometryN_r( geosinit()->ctxt, geos, i ) );
1147  if ( poly )
1148  {
1149  multiPolygon->addGeometry( poly.release() );
1150  }
1151  }
1152  return std::move( multiPolygon );
1153  }
1154  case GEOS_GEOMETRYCOLLECTION:
1155  {
1156  std::unique_ptr< QgsGeometryCollection > geomCollection( new QgsGeometryCollection() );
1157  int nParts = GEOSGetNumGeometries_r( geosinit()->ctxt, geos );
1158  geomCollection->reserve( nParts );
1159  for ( int i = 0; i < nParts; ++i )
1160  {
1161  std::unique_ptr< QgsAbstractGeometry > geom( fromGeos( GEOSGetGeometryN_r( geosinit()->ctxt, geos, i ) ) );
1162  if ( geom )
1163  {
1164  geomCollection->addGeometry( geom.release() );
1165  }
1166  }
1167  return std::move( geomCollection );
1168  }
1169  }
1170  return nullptr;
1171 }
1172 
1173 std::unique_ptr<QgsPolygon> QgsGeos::fromGeosPolygon( const GEOSGeometry *geos )
1174 {
1175  if ( GEOSGeomTypeId_r( geosinit()->ctxt, geos ) != GEOS_POLYGON )
1176  {
1177  return nullptr;
1178  }
1179 
1180  int nCoordDims = GEOSGeom_getCoordinateDimension_r( geosinit()->ctxt, geos );
1181  int nDims = GEOSGeom_getDimensions_r( geosinit()->ctxt, geos );
1182  bool hasZ = ( nCoordDims == 3 );
1183  bool hasM = ( ( nDims - nCoordDims ) == 1 );
1184 
1185  std::unique_ptr< QgsPolygon > polygon( new QgsPolygon() );
1186 
1187  const GEOSGeometry *ring = GEOSGetExteriorRing_r( geosinit()->ctxt, geos );
1188  if ( ring )
1189  {
1190  polygon->setExteriorRing( sequenceToLinestring( ring, hasZ, hasM ).release() );
1191  }
1192 
1193  QVector<QgsCurve *> interiorRings;
1194  const int ringCount = GEOSGetNumInteriorRings_r( geosinit()->ctxt, geos );
1195  interiorRings.reserve( ringCount );
1196  for ( int i = 0; i < ringCount; ++i )
1197  {
1198  ring = GEOSGetInteriorRingN_r( geosinit()->ctxt, geos, i );
1199  if ( ring )
1200  {
1201  interiorRings.push_back( sequenceToLinestring( ring, hasZ, hasM ).release() );
1202  }
1203  }
1204  polygon->setInteriorRings( interiorRings );
1205 
1206  return polygon;
1207 }
1208 
1209 std::unique_ptr<QgsLineString> QgsGeos::sequenceToLinestring( const GEOSGeometry *geos, bool hasZ, bool hasM )
1210 {
1211  const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, geos );
1212  unsigned int nPoints;
1213  GEOSCoordSeq_getSize_r( geosinit()->ctxt, cs, &nPoints );
1214  QVector< double > xOut( nPoints );
1215  QVector< double > yOut( nPoints );
1216  QVector< double > zOut;
1217  if ( hasZ )
1218  zOut.resize( nPoints );
1219  QVector< double > mOut;
1220  if ( hasM )
1221  mOut.resize( nPoints );
1222  double *x = xOut.data();
1223  double *y = yOut.data();
1224  double *z = zOut.data();
1225  double *m = mOut.data();
1226  for ( unsigned int i = 0; i < nPoints; ++i )
1227  {
1228 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
1229  if ( hasZ )
1230  GEOSCoordSeq_getXYZ_r( geosinit()->ctxt, cs, i, x++, y++, z++ );
1231  else
1232  GEOSCoordSeq_getXY_r( geosinit()->ctxt, cs, i, x++, y++ );
1233 #else
1234  GEOSCoordSeq_getX_r( geosinit()->ctxt, cs, i, x++ );
1235  GEOSCoordSeq_getY_r( geosinit()->ctxt, cs, i, y++ );
1236  if ( hasZ )
1237  {
1238  GEOSCoordSeq_getZ_r( geosinit()->ctxt, cs, i, z++ );
1239  }
1240 #endif
1241  if ( hasM )
1242  {
1243  GEOSCoordSeq_getOrdinate_r( geosinit()->ctxt, cs, i, 3, m++ );
1244  }
1245  }
1246  std::unique_ptr< QgsLineString > line( new QgsLineString( xOut, yOut, zOut, mOut ) );
1247  return line;
1248 }
1249 
1250 int QgsGeos::numberOfGeometries( GEOSGeometry *g )
1251 {
1252  if ( !g )
1253  return 0;
1254 
1255  int geometryType = GEOSGeomTypeId_r( geosinit()->ctxt, g );
1256  if ( geometryType == GEOS_POINT || geometryType == GEOS_LINESTRING || geometryType == GEOS_LINEARRING
1257  || geometryType == GEOS_POLYGON )
1258  return 1;
1259 
1260  //calling GEOSGetNumGeometries is save for multi types and collections also in geos2
1261  return GEOSGetNumGeometries_r( geosinit()->ctxt, g );
1262 }
1263 
1264 QgsPoint QgsGeos::coordSeqPoint( const GEOSCoordSequence *cs, int i, bool hasZ, bool hasM )
1265 {
1266  if ( !cs )
1267  {
1268  return QgsPoint();
1269  }
1270 
1271  double x, y;
1272  double z = 0;
1273  double m = 0;
1274 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
1275  if ( hasZ )
1276  GEOSCoordSeq_getXYZ_r( geosinit()->ctxt, cs, i, &x, &y, &z );
1277  else
1278  GEOSCoordSeq_getXY_r( geosinit()->ctxt, cs, i, &x, &y );
1279 #else
1280  GEOSCoordSeq_getX_r( geosinit()->ctxt, cs, i, &x );
1281  GEOSCoordSeq_getY_r( geosinit()->ctxt, cs, i, &y );
1282  if ( hasZ )
1283  {
1284  GEOSCoordSeq_getZ_r( geosinit()->ctxt, cs, i, &z );
1285  }
1286 #endif
1287  if ( hasM )
1288  {
1289  GEOSCoordSeq_getOrdinate_r( geosinit()->ctxt, cs, i, 3, &m );
1290  }
1291 
1293  if ( hasZ && hasM )
1294  {
1296  }
1297  else if ( hasZ )
1298  {
1299  t = QgsWkbTypes::PointZ;
1300  }
1301  else if ( hasM )
1302  {
1303  t = QgsWkbTypes::PointM;
1304  }
1305  return QgsPoint( t, x, y, z, m );
1306 }
1307 
1309 {
1310  if ( !geom )
1311  return nullptr;
1312 
1313  int coordDims = 2;
1314  if ( geom->is3D() )
1315  {
1316  ++coordDims;
1317  }
1318  if ( geom->isMeasure() )
1319  {
1320  ++coordDims;
1321  }
1322 
1324  {
1325  int geosType = GEOS_GEOMETRYCOLLECTION;
1326 
1328  {
1329  switch ( QgsWkbTypes::geometryType( geom->wkbType() ) )
1330  {
1332  geosType = GEOS_MULTIPOINT;
1333  break;
1334 
1336  geosType = GEOS_MULTILINESTRING;
1337  break;
1338 
1340  geosType = GEOS_MULTIPOLYGON;
1341  break;
1342 
1345  return nullptr;
1346  break;
1347  }
1348  }
1349 
1350 
1352 
1353  if ( !c )
1354  return nullptr;
1355 
1356  QVector< GEOSGeometry * > geomVector( c->numGeometries() );
1357  for ( int i = 0; i < c->numGeometries(); ++i )
1358  {
1359  geomVector[i] = asGeos( c->geometryN( i ), precision ).release();
1360  }
1361  return createGeosCollection( geosType, geomVector );
1362  }
1363  else
1364  {
1365  switch ( QgsWkbTypes::geometryType( geom->wkbType() ) )
1366  {
1368  return createGeosPoint( static_cast<const QgsPoint *>( geom ), coordDims, precision );
1369  break;
1370 
1372  return createGeosLinestring( static_cast<const QgsLineString *>( geom ), precision );
1373  break;
1374 
1376  return createGeosPolygon( static_cast<const QgsPolygon *>( geom ), precision );
1377  break;
1378 
1381  return nullptr;
1382  break;
1383  }
1384  }
1385  return nullptr;
1386 }
1387 
1388 std::unique_ptr<QgsAbstractGeometry> QgsGeos::overlay( const QgsAbstractGeometry *geom, Overlay op, QString *errorMsg ) const
1389 {
1390  if ( !mGeos || !geom )
1391  {
1392  return nullptr;
1393  }
1394 
1395  geos::unique_ptr geosGeom( asGeos( geom, mPrecision ) );
1396  if ( !geosGeom )
1397  {
1398  return nullptr;
1399  }
1400 
1401  try
1402  {
1403  geos::unique_ptr opGeom;
1404  switch ( op )
1405  {
1406  case OverlayIntersection:
1407  opGeom.reset( GEOSIntersection_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) );
1408  break;
1409  case OverlayDifference:
1410  opGeom.reset( GEOSDifference_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) );
1411  break;
1412  case OverlayUnion:
1413  {
1414  geos::unique_ptr unionGeometry( GEOSUnion_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) );
1415 
1416  if ( unionGeometry && GEOSGeomTypeId_r( geosinit()->ctxt, unionGeometry.get() ) == GEOS_MULTILINESTRING )
1417  {
1418  geos::unique_ptr mergedLines( GEOSLineMerge_r( geosinit()->ctxt, unionGeometry.get() ) );
1419  if ( mergedLines )
1420  {
1421  unionGeometry = std::move( mergedLines );
1422  }
1423  }
1424 
1425  opGeom = std::move( unionGeometry );
1426  }
1427  break;
1428  case OverlaySymDifference:
1429  opGeom.reset( GEOSSymDifference_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) );
1430  break;
1431  default: //unknown op
1432  return nullptr;
1433  }
1434  return fromGeos( opGeom.get() );
1435  }
1436  catch ( GEOSException &e )
1437  {
1438  if ( errorMsg )
1439  {
1440  *errorMsg = e.what();
1441  }
1442  return nullptr;
1443  }
1444 }
1445 
1446 bool QgsGeos::relation( const QgsAbstractGeometry *geom, Relation r, QString *errorMsg ) const
1447 {
1448  if ( !mGeos || !geom )
1449  {
1450  return false;
1451  }
1452 
1453  geos::unique_ptr geosGeom( asGeos( geom, mPrecision ) );
1454  if ( !geosGeom )
1455  {
1456  return false;
1457  }
1458 
1459  bool result = false;
1460  try
1461  {
1462  if ( mGeosPrepared ) //use faster version with prepared geometry
1463  {
1464  switch ( r )
1465  {
1466  case RelationIntersects:
1467  result = ( GEOSPreparedIntersects_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1468  break;
1469  case RelationTouches:
1470  result = ( GEOSPreparedTouches_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1471  break;
1472  case RelationCrosses:
1473  result = ( GEOSPreparedCrosses_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1474  break;
1475  case RelationWithin:
1476  result = ( GEOSPreparedWithin_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1477  break;
1478  case RelationContains:
1479  result = ( GEOSPreparedContains_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1480  break;
1481  case RelationDisjoint:
1482  result = ( GEOSPreparedDisjoint_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1483  break;
1484  case RelationOverlaps:
1485  result = ( GEOSPreparedOverlaps_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1486  break;
1487  default:
1488  return false;
1489  }
1490  return result;
1491  }
1492 
1493  switch ( r )
1494  {
1495  case RelationIntersects:
1496  result = ( GEOSIntersects_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1497  break;
1498  case RelationTouches:
1499  result = ( GEOSTouches_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1500  break;
1501  case RelationCrosses:
1502  result = ( GEOSCrosses_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1503  break;
1504  case RelationWithin:
1505  result = ( GEOSWithin_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1506  break;
1507  case RelationContains:
1508  result = ( GEOSContains_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1509  break;
1510  case RelationDisjoint:
1511  result = ( GEOSDisjoint_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1512  break;
1513  case RelationOverlaps:
1514  result = ( GEOSOverlaps_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1515  break;
1516  default:
1517  return false;
1518  }
1519  }
1520  catch ( GEOSException &e )
1521  {
1522  if ( errorMsg )
1523  {
1524  *errorMsg = e.what();
1525  }
1526  return false;
1527  }
1528 
1529  return result;
1530 }
1531 
1532 QgsAbstractGeometry *QgsGeos::buffer( double distance, int segments, QString *errorMsg ) const
1533 {
1534  if ( !mGeos )
1535  {
1536  return nullptr;
1537  }
1538 
1540  try
1541  {
1542  geos.reset( GEOSBuffer_r( geosinit()->ctxt, mGeos.get(), distance, segments ) );
1543  }
1544  CATCH_GEOS_WITH_ERRMSG( nullptr );
1545  return fromGeos( geos.get() ).release();
1546 }
1547 
1548 QgsAbstractGeometry *QgsGeos::buffer( double distance, int segments, int endCapStyle, int joinStyle, double miterLimit, QString *errorMsg ) const
1549 {
1550  if ( !mGeos )
1551  {
1552  return nullptr;
1553  }
1554 
1556  try
1557  {
1558  geos.reset( GEOSBufferWithStyle_r( geosinit()->ctxt, mGeos.get(), distance, segments, endCapStyle, joinStyle, miterLimit ) );
1559  }
1560  CATCH_GEOS_WITH_ERRMSG( nullptr );
1561  return fromGeos( geos.get() ).release();
1562 }
1563 
1564 QgsAbstractGeometry *QgsGeos::simplify( double tolerance, QString *errorMsg ) const
1565 {
1566  if ( !mGeos )
1567  {
1568  return nullptr;
1569  }
1571  try
1572  {
1573  geos.reset( GEOSTopologyPreserveSimplify_r( geosinit()->ctxt, mGeos.get(), tolerance ) );
1574  }
1575  CATCH_GEOS_WITH_ERRMSG( nullptr );
1576  return fromGeos( geos.get() ).release();
1577 }
1578 
1579 QgsAbstractGeometry *QgsGeos::interpolate( double distance, QString *errorMsg ) const
1580 {
1581  if ( !mGeos )
1582  {
1583  return nullptr;
1584  }
1586  try
1587  {
1588  geos.reset( GEOSInterpolate_r( geosinit()->ctxt, mGeos.get(), distance ) );
1589  }
1590  CATCH_GEOS_WITH_ERRMSG( nullptr );
1591  return fromGeos( geos.get() ).release();
1592 }
1593 
1594 QgsPoint *QgsGeos::centroid( QString *errorMsg ) const
1595 {
1596  if ( !mGeos )
1597  {
1598  return nullptr;
1599  }
1600 
1602  double x;
1603  double y;
1604 
1605  try
1606  {
1607  geos.reset( GEOSGetCentroid_r( geosinit()->ctxt, mGeos.get() ) );
1608 
1609  if ( !geos )
1610  return nullptr;
1611 
1612  GEOSGeomGetX_r( geosinit()->ctxt, geos.get(), &x );
1613  GEOSGeomGetY_r( geosinit()->ctxt, geos.get(), &y );
1614  }
1615  CATCH_GEOS_WITH_ERRMSG( nullptr );
1616 
1617  return new QgsPoint( x, y );
1618 }
1619 
1620 QgsAbstractGeometry *QgsGeos::envelope( QString *errorMsg ) const
1621 {
1622  if ( !mGeos )
1623  {
1624  return nullptr;
1625  }
1627  try
1628  {
1629  geos.reset( GEOSEnvelope_r( geosinit()->ctxt, mGeos.get() ) );
1630  }
1631  CATCH_GEOS_WITH_ERRMSG( nullptr );
1632  return fromGeos( geos.get() ).release();
1633 }
1634 
1635 QgsPoint *QgsGeos::pointOnSurface( QString *errorMsg ) const
1636 {
1637  if ( !mGeos )
1638  {
1639  return nullptr;
1640  }
1641 
1642  double x;
1643  double y;
1644 
1646  try
1647  {
1648  geos.reset( GEOSPointOnSurface_r( geosinit()->ctxt, mGeos.get() ) );
1649 
1650  if ( !geos || GEOSisEmpty_r( geosinit()->ctxt, geos.get() ) != 0 )
1651  {
1652  return nullptr;
1653  }
1654 
1655  GEOSGeomGetX_r( geosinit()->ctxt, geos.get(), &x );
1656  GEOSGeomGetY_r( geosinit()->ctxt, geos.get(), &y );
1657  }
1658  CATCH_GEOS_WITH_ERRMSG( nullptr );
1659 
1660  return new QgsPoint( x, y );
1661 }
1662 
1663 QgsAbstractGeometry *QgsGeos::convexHull( QString *errorMsg ) const
1664 {
1665  if ( !mGeos )
1666  {
1667  return nullptr;
1668  }
1669 
1670  try
1671  {
1672  geos::unique_ptr cHull( GEOSConvexHull_r( geosinit()->ctxt, mGeos.get() ) );
1673  std::unique_ptr< QgsAbstractGeometry > cHullGeom = fromGeos( cHull.get() );
1674  return cHullGeom.release();
1675  }
1676  CATCH_GEOS_WITH_ERRMSG( nullptr );
1677 }
1678 
1679 bool QgsGeos::isValid( QString *errorMsg, const bool allowSelfTouchingHoles, QgsGeometry *errorLoc ) const
1680 {
1681  if ( !mGeos )
1682  {
1683  return false;
1684  }
1685 
1686  try
1687  {
1688  GEOSGeometry *g1 = nullptr;
1689  char *r = nullptr;
1690  char res = GEOSisValidDetail_r( geosinit()->ctxt, mGeos.get(), allowSelfTouchingHoles ? GEOSVALID_ALLOW_SELFTOUCHING_RING_FORMING_HOLE : 0, &r, &g1 );
1691  const bool invalid = res != 1;
1692 
1693  QString error;
1694  if ( r )
1695  {
1696  error = QString( r );
1697  GEOSFree_r( geosinit()->ctxt, r );
1698  }
1699 
1700  if ( invalid && errorMsg )
1701  {
1702  static QgsStringMap translatedErrors;
1703 
1704  if ( translatedErrors.empty() )
1705  {
1706  // Copied from https://git.osgeo.org/gitea/geos/geos/src/branch/master/src/operation/valid/TopologyValidationError.cpp
1707  translatedErrors.insert( QStringLiteral( "topology validation error" ), QObject::tr( "Topology validation error", "GEOS Error" ) );
1708  translatedErrors.insert( QStringLiteral( "repeated point" ), QObject::tr( "Repeated point", "GEOS Error" ) );
1709  translatedErrors.insert( QStringLiteral( "hole lies outside shell" ), QObject::tr( "Hole lies outside shell", "GEOS Error" ) );
1710  translatedErrors.insert( QStringLiteral( "holes are nested" ), QObject::tr( "Holes are nested", "GEOS Error" ) );
1711  translatedErrors.insert( QStringLiteral( "interior is disconnected" ), QObject::tr( "Interior is disconnected", "GEOS Error" ) );
1712  translatedErrors.insert( QStringLiteral( "self-intersection" ), QObject::tr( "Self-intersection", "GEOS Error" ) );
1713  translatedErrors.insert( QStringLiteral( "ring self-intersection" ), QObject::tr( "Ring self-intersection", "GEOS Error" ) );
1714  translatedErrors.insert( QStringLiteral( "nested shells" ), QObject::tr( "Nested shells", "GEOS Error" ) );
1715  translatedErrors.insert( QStringLiteral( "duplicate rings" ), QObject::tr( "Duplicate rings", "GEOS Error" ) );
1716  translatedErrors.insert( QStringLiteral( "too few points in geometry component" ), QObject::tr( "Too few points in geometry component", "GEOS Error" ) );
1717  translatedErrors.insert( QStringLiteral( "invalid coordinate" ), QObject::tr( "Invalid coordinate", "GEOS Error" ) );
1718  translatedErrors.insert( QStringLiteral( "ring is not closed" ), QObject::tr( "Ring is not closed", "GEOS Error" ) );
1719  }
1720 
1721  *errorMsg = translatedErrors.value( error.toLower(), error );
1722 
1723  if ( g1 && errorLoc )
1724  {
1725  *errorLoc = geometryFromGeos( g1 );
1726  }
1727  else if ( g1 )
1728  {
1729  GEOSGeom_destroy_r( geosinit()->ctxt, g1 );
1730  }
1731  }
1732  return !invalid;
1733  }
1734  CATCH_GEOS_WITH_ERRMSG( false );
1735 }
1736 
1737 bool QgsGeos::isEqual( const QgsAbstractGeometry *geom, QString *errorMsg ) const
1738 {
1739  if ( !mGeos || !geom )
1740  {
1741  return false;
1742  }
1743 
1744  try
1745  {
1746  geos::unique_ptr geosGeom( asGeos( geom, mPrecision ) );
1747  if ( !geosGeom )
1748  {
1749  return false;
1750  }
1751  bool equal = GEOSEquals_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() );
1752  return equal;
1753  }
1754  CATCH_GEOS_WITH_ERRMSG( false );
1755 }
1756 
1757 bool QgsGeos::isEmpty( QString *errorMsg ) const
1758 {
1759  if ( !mGeos )
1760  {
1761  return false;
1762  }
1763 
1764  try
1765  {
1766  return GEOSisEmpty_r( geosinit()->ctxt, mGeos.get() );
1767  }
1768  CATCH_GEOS_WITH_ERRMSG( false );
1769 }
1770 
1771 bool QgsGeos::isSimple( QString *errorMsg ) const
1772 {
1773  if ( !mGeos )
1774  {
1775  return false;
1776  }
1777 
1778  try
1779  {
1780  return GEOSisSimple_r( geosinit()->ctxt, mGeos.get() );
1781  }
1782  CATCH_GEOS_WITH_ERRMSG( false );
1783 }
1784 
1785 GEOSCoordSequence *QgsGeos::createCoordinateSequence( const QgsCurve *curve, double precision, bool forceClose )
1786 {
1787  std::unique_ptr< QgsLineString > segmentized;
1788  const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( curve );
1789 
1790  if ( !line )
1791  {
1792  segmentized.reset( curve->curveToLine() );
1793  line = segmentized.get();
1794  }
1795 
1796  if ( !line )
1797  {
1798  return nullptr;
1799  }
1800 
1801  bool hasZ = line->is3D();
1802  bool hasM = false; //line->isMeasure(); //disabled until geos supports m-coordinates
1803  int coordDims = 2;
1804  if ( hasZ )
1805  {
1806  ++coordDims;
1807  }
1808  if ( hasM )
1809  {
1810  ++coordDims;
1811  }
1812 
1813  int numPoints = line->numPoints();
1814 
1815  int numOutPoints = numPoints;
1816  if ( forceClose && ( line->pointN( 0 ) != line->pointN( numPoints - 1 ) ) )
1817  {
1818  ++numOutPoints;
1819  }
1820 
1821  GEOSCoordSequence *coordSeq = nullptr;
1822  try
1823  {
1824  coordSeq = GEOSCoordSeq_create_r( geosinit()->ctxt, numOutPoints, coordDims );
1825  if ( !coordSeq )
1826  {
1827  QgsDebugMsg( QStringLiteral( "GEOS Exception: Could not create coordinate sequence for %1 points in %2 dimensions" ).arg( numPoints ).arg( coordDims ) );
1828  return nullptr;
1829  }
1830 
1831  const double *xData = line->xData();
1832  const double *yData = line->yData();
1833  const double *zData = hasZ ? line->zData() : nullptr;
1834  const double *mData = hasM ? line->mData() : nullptr;
1835 
1836  if ( precision > 0. )
1837  {
1838  for ( int i = 0; i < numOutPoints; ++i )
1839  {
1840  if ( i >= numPoints )
1841  {
1842  // start reading back from start of line
1843  xData = line->xData();
1844  yData = line->yData();
1845  zData = hasZ ? line->zData() : nullptr;
1846  mData = hasM ? line->mData() : nullptr;
1847  }
1848 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
1849  if ( hasZ )
1850  {
1851  GEOSCoordSeq_setXYZ_r( geosinit()->ctxt, coordSeq, i, std::round( *xData++ / precision ) * precision, std::round( *yData++ / precision ) * precision, std::round( *zData++ / precision ) * precision );
1852  }
1853  else
1854  {
1855  GEOSCoordSeq_setXY_r( geosinit()->ctxt, coordSeq, i, std::round( *xData++ / precision ) * precision, std::round( *yData++ / precision ) * precision );
1856  }
1857 #else
1858  GEOSCoordSeq_setX_r( geosinit()->ctxt, coordSeq, i, std::round( *xData++ / precision ) * precision );
1859  GEOSCoordSeq_setY_r( geosinit()->ctxt, coordSeq, i, std::round( *yData++ / precision ) * precision );
1860  if ( hasZ )
1861  {
1862  GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, i, 2, std::round( *zData++ / precision ) * precision );
1863  }
1864 #endif
1865  if ( hasM )
1866  {
1867  GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, i, 3, line->mAt( *mData++ ) );
1868  }
1869  }
1870  }
1871  else
1872  {
1873  for ( int i = 0; i < numOutPoints; ++i )
1874  {
1875  if ( i >= numPoints )
1876  {
1877  // start reading back from start of line
1878  xData = line->xData();
1879  yData = line->yData();
1880  zData = hasZ ? line->zData() : nullptr;
1881  mData = hasM ? line->mData() : nullptr;
1882  }
1883 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
1884  if ( hasZ )
1885  {
1886  GEOSCoordSeq_setXYZ_r( geosinit()->ctxt, coordSeq, i, *xData++, *yData++, *zData++ );
1887  }
1888  else
1889  {
1890  GEOSCoordSeq_setXY_r( geosinit()->ctxt, coordSeq, i, *xData++, *yData++ );
1891  }
1892 #else
1893  GEOSCoordSeq_setX_r( geosinit()->ctxt, coordSeq, i, *xData++ );
1894  GEOSCoordSeq_setY_r( geosinit()->ctxt, coordSeq, i, *yData++ );
1895  if ( hasZ )
1896  {
1897  GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, i, 2, *zData++ );
1898  }
1899 #endif
1900  if ( hasM )
1901  {
1902  GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, i, 3, *mData++ );
1903  }
1904  }
1905  }
1906  }
1907  CATCH_GEOS( nullptr )
1908 
1909  return coordSeq;
1910 }
1911 
1912 geos::unique_ptr QgsGeos::createGeosPoint( const QgsAbstractGeometry *point, int coordDims, double precision )
1913 {
1914  const QgsPoint *pt = qgsgeometry_cast<const QgsPoint *>( point );
1915  if ( !pt )
1916  return nullptr;
1917 
1918  return createGeosPointXY( pt->x(), pt->y(), pt->is3D(), pt->z(), pt->isMeasure(), pt->m(), coordDims, precision );
1919 }
1920 
1921 geos::unique_ptr QgsGeos::createGeosPointXY( double x, double y, bool hasZ, double z, bool hasM, double m, int coordDims, double precision )
1922 {
1923  Q_UNUSED( hasM )
1924  Q_UNUSED( m )
1925 
1926  geos::unique_ptr geosPoint;
1927  try
1928  {
1929 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
1930  if ( coordDims == 2 )
1931  {
1932  // optimised constructor
1933  if ( precision > 0. )
1934  geosPoint.reset( GEOSGeom_createPointFromXY_r( geosinit()->ctxt, std::round( x / precision ) * precision, std::round( y / precision ) * precision ) );
1935  else
1936  geosPoint.reset( GEOSGeom_createPointFromXY_r( geosinit()->ctxt, x, y ) );
1937  return geosPoint;
1938  }
1939 #endif
1940 
1941  GEOSCoordSequence *coordSeq = GEOSCoordSeq_create_r( geosinit()->ctxt, 1, coordDims );
1942  if ( !coordSeq )
1943  {
1944  QgsDebugMsg( QStringLiteral( "GEOS Exception: Could not create coordinate sequence for point with %1 dimensions" ).arg( coordDims ) );
1945  return nullptr;
1946  }
1947  if ( precision > 0. )
1948  {
1949  GEOSCoordSeq_setX_r( geosinit()->ctxt, coordSeq, 0, std::round( x / precision ) * precision );
1950  GEOSCoordSeq_setY_r( geosinit()->ctxt, coordSeq, 0, std::round( y / precision ) * precision );
1951  if ( hasZ )
1952  {
1953  GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, 0, 2, std::round( z / precision ) * precision );
1954  }
1955  }
1956  else
1957  {
1958  GEOSCoordSeq_setX_r( geosinit()->ctxt, coordSeq, 0, x );
1959  GEOSCoordSeq_setY_r( geosinit()->ctxt, coordSeq, 0, y );
1960  if ( hasZ )
1961  {
1962  GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, 0, 2, z );
1963  }
1964  }
1965 #if 0 //disabled until geos supports m-coordinates
1966  if ( hasM )
1967  {
1968  GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, 0, 3, m );
1969  }
1970 #endif
1971  geosPoint.reset( GEOSGeom_createPoint_r( geosinit()->ctxt, coordSeq ) );
1972  }
1973  CATCH_GEOS( nullptr )
1974  return geosPoint;
1975 }
1976 
1977 geos::unique_ptr QgsGeos::createGeosLinestring( const QgsAbstractGeometry *curve, double precision )
1978 {
1979  const QgsCurve *c = qgsgeometry_cast<const QgsCurve *>( curve );
1980  if ( !c )
1981  return nullptr;
1982 
1983  GEOSCoordSequence *coordSeq = createCoordinateSequence( c, precision );
1984  if ( !coordSeq )
1985  return nullptr;
1986 
1987  geos::unique_ptr geosGeom;
1988  try
1989  {
1990  geosGeom.reset( GEOSGeom_createLineString_r( geosinit()->ctxt, coordSeq ) );
1991  }
1992  CATCH_GEOS( nullptr )
1993  return geosGeom;
1994 }
1995 
1996 geos::unique_ptr QgsGeos::createGeosPolygon( const QgsAbstractGeometry *poly, double precision )
1997 {
1998  const QgsCurvePolygon *polygon = qgsgeometry_cast<const QgsCurvePolygon *>( poly );
1999  if ( !polygon )
2000  return nullptr;
2001 
2002  const QgsCurve *exteriorRing = polygon->exteriorRing();
2003  if ( !exteriorRing )
2004  {
2005  return nullptr;
2006  }
2007 
2008  geos::unique_ptr geosPolygon;
2009  try
2010  {
2011  geos::unique_ptr exteriorRingGeos( GEOSGeom_createLinearRing_r( geosinit()->ctxt, createCoordinateSequence( exteriorRing, precision, true ) ) );
2012 
2013  int nHoles = polygon->numInteriorRings();
2014  GEOSGeometry **holes = nullptr;
2015  if ( nHoles > 0 )
2016  {
2017  holes = new GEOSGeometry*[ nHoles ];
2018  }
2019 
2020  for ( int i = 0; i < nHoles; ++i )
2021  {
2022  const QgsCurve *interiorRing = polygon->interiorRing( i );
2023  holes[i] = GEOSGeom_createLinearRing_r( geosinit()->ctxt, createCoordinateSequence( interiorRing, precision, true ) );
2024  }
2025  geosPolygon.reset( GEOSGeom_createPolygon_r( geosinit()->ctxt, exteriorRingGeos.release(), holes, nHoles ) );
2026  delete[] holes;
2027  }
2028  CATCH_GEOS( nullptr )
2029 
2030  return geosPolygon;
2031 }
2032 
2033 QgsAbstractGeometry *QgsGeos::offsetCurve( double distance, int segments, int joinStyle, double miterLimit, QString *errorMsg ) const
2034 {
2035  if ( !mGeos )
2036  return nullptr;
2037 
2038  geos::unique_ptr offset;
2039  try
2040  {
2041  offset.reset( GEOSOffsetCurve_r( geosinit()->ctxt, mGeos.get(), distance, segments, joinStyle, miterLimit ) );
2042  }
2043  CATCH_GEOS_WITH_ERRMSG( nullptr )
2044  std::unique_ptr< QgsAbstractGeometry > offsetGeom = fromGeos( offset.get() );
2045  return offsetGeom.release();
2046 }
2047 
2048 std::unique_ptr<QgsAbstractGeometry> QgsGeos::singleSidedBuffer( double distance, int segments, int side, int joinStyle, double miterLimit, QString *errorMsg ) const
2049 {
2050  if ( !mGeos )
2051  {
2052  return nullptr;
2053  }
2054 
2056  try
2057  {
2058  geos::buffer_params_unique_ptr bp( GEOSBufferParams_create_r( geosinit()->ctxt ) );
2059  GEOSBufferParams_setSingleSided_r( geosinit()->ctxt, bp.get(), 1 );
2060  GEOSBufferParams_setQuadrantSegments_r( geosinit()->ctxt, bp.get(), segments );
2061  GEOSBufferParams_setJoinStyle_r( geosinit()->ctxt, bp.get(), joinStyle );
2062  GEOSBufferParams_setMitreLimit_r( geosinit()->ctxt, bp.get(), miterLimit ); //#spellok
2063 
2064  if ( side == 1 )
2065  {
2066  distance = -distance;
2067  }
2068  geos.reset( GEOSBufferWithParams_r( geosinit()->ctxt, mGeos.get(), bp.get(), distance ) );
2069  }
2070  CATCH_GEOS_WITH_ERRMSG( nullptr );
2071  return fromGeos( geos.get() );
2072 }
2073 
2074 std::unique_ptr<QgsAbstractGeometry> QgsGeos::reshapeGeometry( const QgsLineString &reshapeWithLine, EngineOperationResult *errorCode, QString *errorMsg ) const
2075 {
2076  if ( !mGeos || mGeometry->dimension() == 0 )
2077  {
2078  if ( errorCode ) { *errorCode = InvalidBaseGeometry; }
2079  return nullptr;
2080  }
2081 
2082  if ( reshapeWithLine.numPoints() < 2 )
2083  {
2084  if ( errorCode ) { *errorCode = InvalidInput; }
2085  return nullptr;
2086  }
2087 
2088  geos::unique_ptr reshapeLineGeos = createGeosLinestring( &reshapeWithLine, mPrecision );
2089 
2090  //single or multi?
2091  int numGeoms = GEOSGetNumGeometries_r( geosinit()->ctxt, mGeos.get() );
2092  if ( numGeoms == -1 )
2093  {
2094  if ( errorCode )
2095  {
2096  *errorCode = InvalidBaseGeometry;
2097  }
2098  return nullptr;
2099  }
2100 
2101  bool isMultiGeom = false;
2102  int geosTypeId = GEOSGeomTypeId_r( geosinit()->ctxt, mGeos.get() );
2103  if ( geosTypeId == GEOS_MULTILINESTRING || geosTypeId == GEOS_MULTIPOLYGON )
2104  isMultiGeom = true;
2105 
2106  bool isLine = ( mGeometry->dimension() == 1 );
2107 
2108  if ( !isMultiGeom )
2109  {
2110  geos::unique_ptr reshapedGeometry;
2111  if ( isLine )
2112  {
2113  reshapedGeometry = reshapeLine( mGeos.get(), reshapeLineGeos.get(), mPrecision );
2114  }
2115  else
2116  {
2117  reshapedGeometry = reshapePolygon( mGeos.get(), reshapeLineGeos.get(), mPrecision );
2118  }
2119 
2120  if ( errorCode )
2121  *errorCode = Success;
2122  std::unique_ptr< QgsAbstractGeometry > reshapeResult = fromGeos( reshapedGeometry.get() );
2123  return reshapeResult;
2124  }
2125  else
2126  {
2127  try
2128  {
2129  //call reshape for each geometry part and replace mGeos with new geometry if reshape took place
2130  bool reshapeTookPlace = false;
2131 
2132  geos::unique_ptr currentReshapeGeometry;
2133  GEOSGeometry **newGeoms = new GEOSGeometry*[numGeoms];
2134 
2135  for ( int i = 0; i < numGeoms; ++i )
2136  {
2137  if ( isLine )
2138  currentReshapeGeometry = reshapeLine( GEOSGetGeometryN_r( geosinit()->ctxt, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
2139  else
2140  currentReshapeGeometry = reshapePolygon( GEOSGetGeometryN_r( geosinit()->ctxt, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
2141 
2142  if ( currentReshapeGeometry )
2143  {
2144  newGeoms[i] = currentReshapeGeometry.release();
2145  reshapeTookPlace = true;
2146  }
2147  else
2148  {
2149  newGeoms[i] = GEOSGeom_clone_r( geosinit()->ctxt, GEOSGetGeometryN_r( geosinit()->ctxt, mGeos.get(), i ) );
2150  }
2151  }
2152 
2153  geos::unique_ptr newMultiGeom;
2154  if ( isLine )
2155  {
2156  newMultiGeom.reset( GEOSGeom_createCollection_r( geosinit()->ctxt, GEOS_MULTILINESTRING, newGeoms, numGeoms ) );
2157  }
2158  else //multipolygon
2159  {
2160  newMultiGeom.reset( GEOSGeom_createCollection_r( geosinit()->ctxt, GEOS_MULTIPOLYGON, newGeoms, numGeoms ) );
2161  }
2162 
2163  delete[] newGeoms;
2164  if ( !newMultiGeom )
2165  {
2166  if ( errorCode ) { *errorCode = EngineError; }
2167  return nullptr;
2168  }
2169 
2170  if ( reshapeTookPlace )
2171  {
2172  if ( errorCode )
2173  *errorCode = Success;
2174  std::unique_ptr< QgsAbstractGeometry > reshapedMultiGeom = fromGeos( newMultiGeom.get() );
2175  return reshapedMultiGeom;
2176  }
2177  else
2178  {
2179  if ( errorCode )
2180  {
2181  *errorCode = NothingHappened;
2182  }
2183  return nullptr;
2184  }
2185  }
2186  CATCH_GEOS_WITH_ERRMSG( nullptr )
2187  }
2188 }
2189 
2190 QgsGeometry QgsGeos::mergeLines( QString *errorMsg ) const
2191 {
2192  if ( !mGeos )
2193  {
2194  return QgsGeometry();
2195  }
2196 
2197  if ( GEOSGeomTypeId_r( geosinit()->ctxt, mGeos.get() ) != GEOS_MULTILINESTRING )
2198  return QgsGeometry();
2199 
2201  try
2202  {
2203  geos.reset( GEOSLineMerge_r( geosinit()->ctxt, mGeos.get() ) );
2204  }
2206  return QgsGeometry( fromGeos( geos.get() ) );
2207 }
2208 
2209 QgsGeometry QgsGeos::closestPoint( const QgsGeometry &other, QString *errorMsg ) const
2210 {
2211  if ( !mGeos || other.isNull() )
2212  {
2213  return QgsGeometry();
2214  }
2215 
2216  geos::unique_ptr otherGeom( asGeos( other.constGet(), mPrecision ) );
2217  if ( !otherGeom )
2218  {
2219  return QgsGeometry();
2220  }
2221 
2222  double nx = 0.0;
2223  double ny = 0.0;
2224  try
2225  {
2226  geos::coord_sequence_unique_ptr nearestCoord( GEOSNearestPoints_r( geosinit()->ctxt, mGeos.get(), otherGeom.get() ) );
2227 
2228  ( void )GEOSCoordSeq_getX_r( geosinit()->ctxt, nearestCoord.get(), 0, &nx );
2229  ( void )GEOSCoordSeq_getY_r( geosinit()->ctxt, nearestCoord.get(), 0, &ny );
2230  }
2231  catch ( GEOSException &e )
2232  {
2233  if ( errorMsg )
2234  {
2235  *errorMsg = e.what();
2236  }
2237  return QgsGeometry();
2238  }
2239 
2240  return QgsGeometry( new QgsPoint( nx, ny ) );
2241 }
2242 
2243 QgsGeometry QgsGeos::shortestLine( const QgsGeometry &other, QString *errorMsg ) const
2244 {
2245  if ( !mGeos || other.isNull() )
2246  {
2247  return QgsGeometry();
2248  }
2249 
2250  geos::unique_ptr otherGeom( asGeos( other.constGet(), mPrecision ) );
2251  if ( !otherGeom )
2252  {
2253  return QgsGeometry();
2254  }
2255 
2256  double nx1 = 0.0;
2257  double ny1 = 0.0;
2258  double nx2 = 0.0;
2259  double ny2 = 0.0;
2260  try
2261  {
2262  geos::coord_sequence_unique_ptr nearestCoord( GEOSNearestPoints_r( geosinit()->ctxt, mGeos.get(), otherGeom.get() ) );
2263 
2264  ( void )GEOSCoordSeq_getX_r( geosinit()->ctxt, nearestCoord.get(), 0, &nx1 );
2265  ( void )GEOSCoordSeq_getY_r( geosinit()->ctxt, nearestCoord.get(), 0, &ny1 );
2266  ( void )GEOSCoordSeq_getX_r( geosinit()->ctxt, nearestCoord.get(), 1, &nx2 );
2267  ( void )GEOSCoordSeq_getY_r( geosinit()->ctxt, nearestCoord.get(), 1, &ny2 );
2268  }
2269  catch ( GEOSException &e )
2270  {
2271  if ( errorMsg )
2272  {
2273  *errorMsg = e.what();
2274  }
2275  return QgsGeometry();
2276  }
2277 
2278  QgsLineString *line = new QgsLineString();
2279  line->addVertex( QgsPoint( nx1, ny1 ) );
2280  line->addVertex( QgsPoint( nx2, ny2 ) );
2281  return QgsGeometry( line );
2282 }
2283 
2284 double QgsGeos::lineLocatePoint( const QgsPoint &point, QString *errorMsg ) const
2285 {
2286  if ( !mGeos )
2287  {
2288  return -1;
2289  }
2290 
2291  geos::unique_ptr otherGeom( asGeos( &point, mPrecision ) );
2292  if ( !otherGeom )
2293  {
2294  return -1;
2295  }
2296 
2297  double distance = -1;
2298  try
2299  {
2300  distance = GEOSProject_r( geosinit()->ctxt, mGeos.get(), otherGeom.get() );
2301  }
2302  catch ( GEOSException &e )
2303  {
2304  if ( errorMsg )
2305  {
2306  *errorMsg = e.what();
2307  }
2308  return -1;
2309  }
2310 
2311  return distance;
2312 }
2313 
2314 QgsGeometry QgsGeos::polygonize( const QVector<const QgsAbstractGeometry *> &geometries, QString *errorMsg )
2315 {
2316  GEOSGeometry **const lineGeosGeometries = new GEOSGeometry*[ geometries.size()];
2317  int validLines = 0;
2318  for ( const QgsAbstractGeometry *g : geometries )
2319  {
2320  geos::unique_ptr l = asGeos( g );
2321  if ( l )
2322  {
2323  lineGeosGeometries[validLines] = l.release();
2324  validLines++;
2325  }
2326  }
2327 
2328  try
2329  {
2330  geos::unique_ptr result( GEOSPolygonize_r( geosinit()->ctxt, lineGeosGeometries, validLines ) );
2331  for ( int i = 0; i < validLines; ++i )
2332  {
2333  GEOSGeom_destroy_r( geosinit()->ctxt, lineGeosGeometries[i] );
2334  }
2335  delete[] lineGeosGeometries;
2336  return QgsGeometry( fromGeos( result.get() ) );
2337  }
2338  catch ( GEOSException &e )
2339  {
2340  if ( errorMsg )
2341  {
2342  *errorMsg = e.what();
2343  }
2344  for ( int i = 0; i < validLines; ++i )
2345  {
2346  GEOSGeom_destroy_r( geosinit()->ctxt, lineGeosGeometries[i] );
2347  }
2348  delete[] lineGeosGeometries;
2349  return QgsGeometry();
2350  }
2351 }
2352 
2353 QgsGeometry QgsGeos::voronoiDiagram( const QgsAbstractGeometry *extent, double tolerance, bool edgesOnly, QString *errorMsg ) const
2354 {
2355  if ( !mGeos )
2356  {
2357  return QgsGeometry();
2358  }
2359 
2360  geos::unique_ptr extentGeosGeom;
2361  if ( extent )
2362  {
2363  extentGeosGeom = asGeos( extent, mPrecision );
2364  if ( !extentGeosGeom )
2365  {
2366  return QgsGeometry();
2367  }
2368  }
2369 
2371  try
2372  {
2373  geos.reset( GEOSVoronoiDiagram_r( geosinit()->ctxt, mGeos.get(), extentGeosGeom.get(), tolerance, edgesOnly ) );
2374 
2375  if ( !geos || GEOSisEmpty_r( geosinit()->ctxt, geos.get() ) != 0 )
2376  {
2377  return QgsGeometry();
2378  }
2379 
2380  return QgsGeometry( fromGeos( geos.get() ) );
2381  }
2383 }
2384 
2385 QgsGeometry QgsGeos::delaunayTriangulation( double tolerance, bool edgesOnly, QString *errorMsg ) const
2386 {
2387  if ( !mGeos )
2388  {
2389  return QgsGeometry();
2390  }
2391 
2393  try
2394  {
2395  geos.reset( GEOSDelaunayTriangulation_r( geosinit()->ctxt, mGeos.get(), tolerance, edgesOnly ) );
2396 
2397  if ( !geos || GEOSisEmpty_r( geosinit()->ctxt, geos.get() ) != 0 )
2398  {
2399  return QgsGeometry();
2400  }
2401 
2402  return QgsGeometry( fromGeos( geos.get() ) );
2403  }
2405 }
2406 
2407 
2409 static bool _linestringEndpoints( const GEOSGeometry *linestring, double &x1, double &y1, double &x2, double &y2 )
2410 {
2411  const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, linestring );
2412  if ( !coordSeq )
2413  return false;
2414 
2415  unsigned int coordSeqSize;
2416  if ( GEOSCoordSeq_getSize_r( geosinit()->ctxt, coordSeq, &coordSeqSize ) == 0 )
2417  return false;
2418 
2419  if ( coordSeqSize < 2 )
2420  return false;
2421 
2422  GEOSCoordSeq_getX_r( geosinit()->ctxt, coordSeq, 0, &x1 );
2423  GEOSCoordSeq_getY_r( geosinit()->ctxt, coordSeq, 0, &y1 );
2424  GEOSCoordSeq_getX_r( geosinit()->ctxt, coordSeq, coordSeqSize - 1, &x2 );
2425  GEOSCoordSeq_getY_r( geosinit()->ctxt, coordSeq, coordSeqSize - 1, &y2 );
2426  return true;
2427 }
2428 
2429 
2431 static geos::unique_ptr _mergeLinestrings( const GEOSGeometry *line1, const GEOSGeometry *line2, const QgsPointXY &intersectionPoint )
2432 {
2433  double x1, y1, x2, y2;
2434  if ( !_linestringEndpoints( line1, x1, y1, x2, y2 ) )
2435  return nullptr;
2436 
2437  double rx1, ry1, rx2, ry2;
2438  if ( !_linestringEndpoints( line2, rx1, ry1, rx2, ry2 ) )
2439  return nullptr;
2440 
2441  bool intersectionAtOrigLineEndpoint =
2442  ( intersectionPoint.x() == x1 && intersectionPoint.y() == y1 ) !=
2443  ( intersectionPoint.x() == x2 && intersectionPoint.y() == y2 );
2444  bool intersectionAtReshapeLineEndpoint =
2445  ( intersectionPoint.x() == rx1 && intersectionPoint.y() == ry1 ) ||
2446  ( intersectionPoint.x() == rx2 && intersectionPoint.y() == ry2 );
2447 
2448  // the intersection must be at the begin/end of both lines
2449  if ( intersectionAtOrigLineEndpoint && intersectionAtReshapeLineEndpoint )
2450  {
2451  geos::unique_ptr g1( GEOSGeom_clone_r( geosinit()->ctxt, line1 ) );
2452  geos::unique_ptr g2( GEOSGeom_clone_r( geosinit()->ctxt, line2 ) );
2453  GEOSGeometry *geoms[2] = { g1.release(), g2.release() };
2454  geos::unique_ptr multiGeom( GEOSGeom_createCollection_r( geosinit()->ctxt, GEOS_MULTILINESTRING, geoms, 2 ) );
2455  geos::unique_ptr res( GEOSLineMerge_r( geosinit()->ctxt, multiGeom.get() ) );
2456  return res;
2457  }
2458  else
2459  return nullptr;
2460 }
2461 
2462 
2463 geos::unique_ptr QgsGeos::reshapeLine( const GEOSGeometry *line, const GEOSGeometry *reshapeLineGeos, double precision )
2464 {
2465  if ( !line || !reshapeLineGeos )
2466  return nullptr;
2467 
2468  bool atLeastTwoIntersections = false;
2469  bool oneIntersection = false;
2470  QgsPointXY oneIntersectionPoint;
2471 
2472  try
2473  {
2474  //make sure there are at least two intersection between line and reshape geometry
2475  geos::unique_ptr intersectGeom( GEOSIntersection_r( geosinit()->ctxt, line, reshapeLineGeos ) );
2476  if ( intersectGeom )
2477  {
2478  atLeastTwoIntersections = ( GEOSGeomTypeId_r( geosinit()->ctxt, intersectGeom.get() ) == GEOS_MULTIPOINT
2479  && GEOSGetNumGeometries_r( geosinit()->ctxt, intersectGeom.get() ) > 1 );
2480  // one point is enough when extending line at its endpoint
2481  if ( GEOSGeomTypeId_r( geosinit()->ctxt, intersectGeom.get() ) == GEOS_POINT )
2482  {
2483  const GEOSCoordSequence *intersectionCoordSeq = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, intersectGeom.get() );
2484  double xi, yi;
2485  GEOSCoordSeq_getX_r( geosinit()->ctxt, intersectionCoordSeq, 0, &xi );
2486  GEOSCoordSeq_getY_r( geosinit()->ctxt, intersectionCoordSeq, 0, &yi );
2487  oneIntersection = true;
2488  oneIntersectionPoint = QgsPointXY( xi, yi );
2489  }
2490  }
2491  }
2492  catch ( GEOSException & )
2493  {
2494  atLeastTwoIntersections = false;
2495  }
2496 
2497  // special case when extending line at its endpoint
2498  if ( oneIntersection )
2499  return _mergeLinestrings( line, reshapeLineGeos, oneIntersectionPoint );
2500 
2501  if ( !atLeastTwoIntersections )
2502  return nullptr;
2503 
2504  //begin and end point of original line
2505  double x1, y1, x2, y2;
2506  if ( !_linestringEndpoints( line, x1, y1, x2, y2 ) )
2507  return nullptr;
2508 
2509  geos::unique_ptr beginLineVertex = createGeosPointXY( x1, y1, false, 0, false, 0, 2, precision );
2510  geos::unique_ptr endLineVertex = createGeosPointXY( x2, y2, false, 0, false, 0, 2, precision );
2511 
2512  bool isRing = false;
2513  if ( GEOSGeomTypeId_r( geosinit()->ctxt, line ) == GEOS_LINEARRING
2514  || GEOSEquals_r( geosinit()->ctxt, beginLineVertex.get(), endLineVertex.get() ) == 1 )
2515  isRing = true;
2516 
2517  //node line and reshape line
2518  geos::unique_ptr nodedGeometry = nodeGeometries( reshapeLineGeos, line );
2519  if ( !nodedGeometry )
2520  {
2521  return nullptr;
2522  }
2523 
2524  //and merge them together
2525  geos::unique_ptr mergedLines( GEOSLineMerge_r( geosinit()->ctxt, nodedGeometry.get() ) );
2526  if ( !mergedLines )
2527  {
2528  return nullptr;
2529  }
2530 
2531  int numMergedLines = GEOSGetNumGeometries_r( geosinit()->ctxt, mergedLines.get() );
2532  if ( numMergedLines < 2 ) //some special cases. Normally it is >2
2533  {
2534  if ( numMergedLines == 1 ) //reshape line is from begin to endpoint. So we keep the reshapeline
2535  {
2536  geos::unique_ptr result( GEOSGeom_clone_r( geosinit()->ctxt, reshapeLineGeos ) );
2537  return result;
2538  }
2539  else
2540  return nullptr;
2541  }
2542 
2543  QVector<GEOSGeometry *> resultLineParts; //collection with the line segments that will be contained in result
2544  QVector<GEOSGeometry *> probableParts; //parts where we can decide on inclusion only after going through all the candidates
2545 
2546  for ( int i = 0; i < numMergedLines; ++i )
2547  {
2548  const GEOSGeometry *currentGeom = nullptr;
2549 
2550  currentGeom = GEOSGetGeometryN_r( geosinit()->ctxt, mergedLines.get(), i );
2551  const GEOSCoordSequence *currentCoordSeq = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, currentGeom );
2552  unsigned int currentCoordSeqSize;
2553  GEOSCoordSeq_getSize_r( geosinit()->ctxt, currentCoordSeq, &currentCoordSeqSize );
2554  if ( currentCoordSeqSize < 2 )
2555  continue;
2556 
2557  //get the two endpoints of the current line merge result
2558  double xBegin, xEnd, yBegin, yEnd;
2559  GEOSCoordSeq_getX_r( geosinit()->ctxt, currentCoordSeq, 0, &xBegin );
2560  GEOSCoordSeq_getY_r( geosinit()->ctxt, currentCoordSeq, 0, &yBegin );
2561  GEOSCoordSeq_getX_r( geosinit()->ctxt, currentCoordSeq, currentCoordSeqSize - 1, &xEnd );
2562  GEOSCoordSeq_getY_r( geosinit()->ctxt, currentCoordSeq, currentCoordSeqSize - 1, &yEnd );
2563  geos::unique_ptr beginCurrentGeomVertex = createGeosPointXY( xBegin, yBegin, false, 0, false, 0, 2, precision );
2564  geos::unique_ptr endCurrentGeomVertex = createGeosPointXY( xEnd, yEnd, false, 0, false, 0, 2, precision );
2565 
2566  //check how many endpoints of the line merge result are on the (original) line
2567  int nEndpointsOnOriginalLine = 0;
2568  if ( pointContainedInLine( beginCurrentGeomVertex.get(), line ) == 1 )
2569  nEndpointsOnOriginalLine += 1;
2570 
2571  if ( pointContainedInLine( endCurrentGeomVertex.get(), line ) == 1 )
2572  nEndpointsOnOriginalLine += 1;
2573 
2574  //check how many endpoints equal the endpoints of the original line
2575  int nEndpointsSameAsOriginalLine = 0;
2576  if ( GEOSEquals_r( geosinit()->ctxt, beginCurrentGeomVertex.get(), beginLineVertex.get() ) == 1
2577  || GEOSEquals_r( geosinit()->ctxt, beginCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
2578  nEndpointsSameAsOriginalLine += 1;
2579 
2580  if ( GEOSEquals_r( geosinit()->ctxt, endCurrentGeomVertex.get(), beginLineVertex.get() ) == 1
2581  || GEOSEquals_r( geosinit()->ctxt, endCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
2582  nEndpointsSameAsOriginalLine += 1;
2583 
2584  //check if the current geometry overlaps the original geometry (GEOSOverlap does not seem to work with linestrings)
2585  bool currentGeomOverlapsOriginalGeom = false;
2586  bool currentGeomOverlapsReshapeLine = false;
2587  if ( lineContainedInLine( currentGeom, line ) == 1 )
2588  currentGeomOverlapsOriginalGeom = true;
2589 
2590  if ( lineContainedInLine( currentGeom, reshapeLineGeos ) == 1 )
2591  currentGeomOverlapsReshapeLine = true;
2592 
2593  //logic to decide if this part belongs to the result
2594  if ( !isRing && nEndpointsSameAsOriginalLine == 1 && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
2595  {
2596  resultLineParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
2597  }
2598  //for closed rings, we take one segment from the candidate list
2599  else if ( isRing && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
2600  {
2601  probableParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
2602  }
2603  else if ( nEndpointsOnOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
2604  {
2605  resultLineParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
2606  }
2607  else if ( nEndpointsSameAsOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
2608  {
2609  resultLineParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
2610  }
2611  else if ( currentGeomOverlapsOriginalGeom && currentGeomOverlapsReshapeLine )
2612  {
2613  resultLineParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
2614  }
2615  }
2616 
2617  //add the longest segment from the probable list for rings (only used for polygon rings)
2618  if ( isRing && !probableParts.isEmpty() )
2619  {
2620  geos::unique_ptr maxGeom; //the longest geometry in the probabla list
2621  GEOSGeometry *currentGeom = nullptr;
2622  double maxLength = -std::numeric_limits<double>::max();
2623  double currentLength = 0;
2624  for ( int i = 0; i < probableParts.size(); ++i )
2625  {
2626  currentGeom = probableParts.at( i );
2627  GEOSLength_r( geosinit()->ctxt, currentGeom, &currentLength );
2628  if ( currentLength > maxLength )
2629  {
2630  maxLength = currentLength;
2631  maxGeom.reset( currentGeom );
2632  }
2633  else
2634  {
2635  GEOSGeom_destroy_r( geosinit()->ctxt, currentGeom );
2636  }
2637  }
2638  resultLineParts.push_back( maxGeom.release() );
2639  }
2640 
2641  geos::unique_ptr result;
2642  if ( resultLineParts.empty() )
2643  return nullptr;
2644 
2645  if ( resultLineParts.size() == 1 ) //the whole result was reshaped
2646  {
2647  result.reset( resultLineParts[0] );
2648  }
2649  else //>1
2650  {
2651  GEOSGeometry **lineArray = new GEOSGeometry*[resultLineParts.size()];
2652  for ( int i = 0; i < resultLineParts.size(); ++i )
2653  {
2654  lineArray[i] = resultLineParts[i];
2655  }
2656 
2657  //create multiline from resultLineParts
2658  geos::unique_ptr multiLineGeom( GEOSGeom_createCollection_r( geosinit()->ctxt, GEOS_MULTILINESTRING, lineArray, resultLineParts.size() ) );
2659  delete [] lineArray;
2660 
2661  //then do a linemerge with the newly combined partstrings
2662  result.reset( GEOSLineMerge_r( geosinit()->ctxt, multiLineGeom.get() ) );
2663  }
2664 
2665  //now test if the result is a linestring. Otherwise something went wrong
2666  if ( GEOSGeomTypeId_r( geosinit()->ctxt, result.get() ) != GEOS_LINESTRING )
2667  {
2668  return nullptr;
2669  }
2670 
2671  return result;
2672 }
2673 
2674 geos::unique_ptr QgsGeos::reshapePolygon( const GEOSGeometry *polygon, const GEOSGeometry *reshapeLineGeos, double precision )
2675 {
2676  //go through outer shell and all inner rings and check if there is exactly one intersection of a ring and the reshape line
2677  int nIntersections = 0;
2678  int lastIntersectingRing = -2;
2679  const GEOSGeometry *lastIntersectingGeom = nullptr;
2680 
2681  int nRings = GEOSGetNumInteriorRings_r( geosinit()->ctxt, polygon );
2682  if ( nRings < 0 )
2683  return nullptr;
2684 
2685  //does outer ring intersect?
2686  const GEOSGeometry *outerRing = GEOSGetExteriorRing_r( geosinit()->ctxt, polygon );
2687  if ( GEOSIntersects_r( geosinit()->ctxt, outerRing, reshapeLineGeos ) == 1 )
2688  {
2689  ++nIntersections;
2690  lastIntersectingRing = -1;
2691  lastIntersectingGeom = outerRing;
2692  }
2693 
2694  //do inner rings intersect?
2695  const GEOSGeometry **innerRings = new const GEOSGeometry*[nRings];
2696 
2697  try
2698  {
2699  for ( int i = 0; i < nRings; ++i )
2700  {
2701  innerRings[i] = GEOSGetInteriorRingN_r( geosinit()->ctxt, polygon, i );
2702  if ( GEOSIntersects_r( geosinit()->ctxt, innerRings[i], reshapeLineGeos ) == 1 )
2703  {
2704  ++nIntersections;
2705  lastIntersectingRing = i;
2706  lastIntersectingGeom = innerRings[i];
2707  }
2708  }
2709  }
2710  catch ( GEOSException & )
2711  {
2712  nIntersections = 0;
2713  }
2714 
2715  if ( nIntersections != 1 ) //reshape line is only allowed to intersect one ring
2716  {
2717  delete [] innerRings;
2718  return nullptr;
2719  }
2720 
2721  //we have one intersecting ring, let's try to reshape it
2722  geos::unique_ptr reshapeResult = reshapeLine( lastIntersectingGeom, reshapeLineGeos, precision );
2723  if ( !reshapeResult )
2724  {
2725  delete [] innerRings;
2726  return nullptr;
2727  }
2728 
2729  //if reshaping took place, we need to reassemble the polygon and its rings
2730  GEOSGeometry *newRing = nullptr;
2731  const GEOSCoordSequence *reshapeSequence = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, reshapeResult.get() );
2732  GEOSCoordSequence *newCoordSequence = GEOSCoordSeq_clone_r( geosinit()->ctxt, reshapeSequence );
2733 
2734  reshapeResult.reset();
2735 
2736  newRing = GEOSGeom_createLinearRing_r( geosinit()->ctxt, newCoordSequence );
2737  if ( !newRing )
2738  {
2739  delete [] innerRings;
2740  return nullptr;
2741  }
2742 
2743  GEOSGeometry *newOuterRing = nullptr;
2744  if ( lastIntersectingRing == -1 )
2745  newOuterRing = newRing;
2746  else
2747  newOuterRing = GEOSGeom_clone_r( geosinit()->ctxt, outerRing );
2748 
2749  //check if all the rings are still inside the outer boundary
2750  QVector<GEOSGeometry *> ringList;
2751  if ( nRings > 0 )
2752  {
2753  GEOSGeometry *outerRingPoly = GEOSGeom_createPolygon_r( geosinit()->ctxt, GEOSGeom_clone_r( geosinit()->ctxt, newOuterRing ), nullptr, 0 );
2754  if ( outerRingPoly )
2755  {
2756  GEOSGeometry *currentRing = nullptr;
2757  for ( int i = 0; i < nRings; ++i )
2758  {
2759  if ( lastIntersectingRing == i )
2760  currentRing = newRing;
2761  else
2762  currentRing = GEOSGeom_clone_r( geosinit()->ctxt, innerRings[i] );
2763 
2764  //possibly a ring is no longer contained in the result polygon after reshape
2765  if ( GEOSContains_r( geosinit()->ctxt, outerRingPoly, currentRing ) == 1 )
2766  ringList.push_back( currentRing );
2767  else
2768  GEOSGeom_destroy_r( geosinit()->ctxt, currentRing );
2769  }
2770  }
2771  GEOSGeom_destroy_r( geosinit()->ctxt, outerRingPoly );
2772  }
2773 
2774  GEOSGeometry **newInnerRings = new GEOSGeometry*[ringList.size()];
2775  for ( int i = 0; i < ringList.size(); ++i )
2776  newInnerRings[i] = ringList.at( i );
2777 
2778  delete [] innerRings;
2779 
2780  geos::unique_ptr reshapedPolygon( GEOSGeom_createPolygon_r( geosinit()->ctxt, newOuterRing, newInnerRings, ringList.size() ) );
2781  delete[] newInnerRings;
2782 
2783  return reshapedPolygon;
2784 }
2785 
2786 int QgsGeos::lineContainedInLine( const GEOSGeometry *line1, const GEOSGeometry *line2 )
2787 {
2788  if ( !line1 || !line2 )
2789  {
2790  return -1;
2791  }
2792 
2793  double bufferDistance = std::pow( 10.0L, geomDigits( line2 ) - 11 );
2794 
2795  geos::unique_ptr bufferGeom( GEOSBuffer_r( geosinit()->ctxt, line2, bufferDistance, DEFAULT_QUADRANT_SEGMENTS ) );
2796  if ( !bufferGeom )
2797  return -2;
2798 
2799  geos::unique_ptr intersectionGeom( GEOSIntersection_r( geosinit()->ctxt, bufferGeom.get(), line1 ) );
2800 
2801  //compare ratio between line1Length and intersectGeomLength (usually close to 1 if line1 is contained in line2)
2802  double intersectGeomLength;
2803  double line1Length;
2804 
2805  GEOSLength_r( geosinit()->ctxt, intersectionGeom.get(), &intersectGeomLength );
2806  GEOSLength_r( geosinit()->ctxt, line1, &line1Length );
2807 
2808  double intersectRatio = line1Length / intersectGeomLength;
2809  if ( intersectRatio > 0.9 && intersectRatio < 1.1 )
2810  return 1;
2811 
2812  return 0;
2813 }
2814 
2815 int QgsGeos::pointContainedInLine( const GEOSGeometry *point, const GEOSGeometry *line )
2816 {
2817  if ( !point || !line )
2818  return -1;
2819 
2820  double bufferDistance = std::pow( 10.0L, geomDigits( line ) - 11 );
2821 
2822  geos::unique_ptr lineBuffer( GEOSBuffer_r( geosinit()->ctxt, line, bufferDistance, 8 ) );
2823  if ( !lineBuffer )
2824  return -2;
2825 
2826  bool contained = false;
2827  if ( GEOSContains_r( geosinit()->ctxt, lineBuffer.get(), point ) == 1 )
2828  contained = true;
2829 
2830  return contained;
2831 }
2832 
2833 int QgsGeos::geomDigits( const GEOSGeometry *geom )
2834 {
2835  geos::unique_ptr bbox( GEOSEnvelope_r( geosinit()->ctxt, geom ) );
2836  if ( !bbox.get() )
2837  return -1;
2838 
2839  const GEOSGeometry *bBoxRing = GEOSGetExteriorRing_r( geosinit()->ctxt, bbox.get() );
2840  if ( !bBoxRing )
2841  return -1;
2842 
2843  const GEOSCoordSequence *bBoxCoordSeq = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, bBoxRing );
2844 
2845  if ( !bBoxCoordSeq )
2846  return -1;
2847 
2848  unsigned int nCoords = 0;
2849  if ( !GEOSCoordSeq_getSize_r( geosinit()->ctxt, bBoxCoordSeq, &nCoords ) )
2850  return -1;
2851 
2852  int maxDigits = -1;
2853  for ( unsigned int i = 0; i < nCoords - 1; ++i )
2854  {
2855  double t;
2856  GEOSCoordSeq_getX_r( geosinit()->ctxt, bBoxCoordSeq, i, &t );
2857 
2858  int digits;
2859  digits = std::ceil( std::log10( std::fabs( t ) ) );
2860  if ( digits > maxDigits )
2861  maxDigits = digits;
2862 
2863  GEOSCoordSeq_getY_r( geosinit()->ctxt, bBoxCoordSeq, i, &t );
2864  digits = std::ceil( std::log10( std::fabs( t ) ) );
2865  if ( digits > maxDigits )
2866  maxDigits = digits;
2867  }
2868 
2869  return maxDigits;
2870 }
2871 
2872 GEOSContextHandle_t QgsGeos::getGEOSHandler()
2873 {
2874  return geosinit()->ctxt;
2875 }
bool isMeasure() const
Returns true if the geometry contains m values.
int precision
A rectangle specified with double values.
Definition: qgsrectangle.h:41
double y
Definition: qgspoint.h:42
bool disjoint(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom is disjoint from this.
Definition: qgsgeos.cpp:518
QgsAbstractGeometry * intersection(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Calculate the intersection of this and geom.
Definition: qgsgeos.cpp:217
QgsGeometry mergeLines(QString *errorMsg=nullptr) const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
Definition: qgsgeos.cpp:2190
#define CATCH_GEOS(r)
Definition: qgsgeos.cpp:33
QgsGeos(const QgsAbstractGeometry *geometry, double precision=0)
GEOS geometry engine constructor.
Definition: qgsgeos.cpp:145
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:135
const double * mData() const
Returns a const pointer to the m vertex data, or nullptr if the linestring does not have m values...
Multi point geometry collection.
Definition: qgsmultipoint.h:29
static bool isMultiType(Type type)
Returns true if the WKB type is a multi type.
Definition: qgswkbtypes.h:706
The source geometry is not multi.
Definition: qgsgeometry.h:146
bool isEmpty(QString *errorMsg=nullptr) const override
Definition: qgsgeos.cpp:1757
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsPoint * pointOnSurface(QString *errorMsg=nullptr) const override
Calculate a point that is guaranteed to be on the surface of this.
Definition: qgsgeos.cpp:1635
double y
Definition: qgspointxy.h:48
QgsGeometry shortestLine(const QgsGeometry &other, QString *errorMsg=nullptr) const
Returns the shortest line joining this geometry to the other geometry.
Definition: qgsgeos.cpp:2243
double area(QString *errorMsg=nullptr) const override
Definition: qgsgeos.cpp:586
A class to represent a 2D point.
Definition: qgspointxy.h:43
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
const QgsAbstractGeometry * mGeometry
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
QgsAbstractGeometry * buffer(double distance, int segments, QString *errorMsg=nullptr) const override
Definition: qgsgeos.cpp:1532
bool isValid(QString *errorMsg=nullptr, bool allowSelfTouchingHoles=false, QgsGeometry *errorLoc=nullptr) const override
Returns true if the geometry is valid.
Definition: qgsgeos.cpp:1679
Multi line string geometry collection.
Curve polygon geometry type.
static std::unique_ptr< QgsGeometryCollection > createCollectionOfType(QgsWkbTypes::Type type)
Returns a new geometry collection matching a specified WKB type.
double length(QString *errorMsg=nullptr) const override
Definition: qgsgeos.cpp:603
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:122
bool isEqual(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if this is equal to geom.
Definition: qgsgeos.cpp:1737
QgsGeometry closestPoint(const QgsGeometry &other, QString *errorMsg=nullptr) const
Returns the closest point on the geometry to the other geometry.
Definition: qgsgeos.cpp:2209
virtual QgsRectangle boundingBox() const =0
Returns the minimal bounding box for the geometry.
static QgsGeometry::OperationResult addPart(QgsGeometry &geometry, GEOSGeometry *newPart)
Adds a new island polygon to a multipolygon feature.
Definition: qgsgeos.cpp:176
void CORE_EXPORT operator()(GEOSGeometry *geom)
Destroys the GEOS geometry geom, using the static QGIS geos context.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
QMap< QString, QString > QgsStringMap
Definition: qgis.h:694
OperationResult
Success or failure of a geometry operation.
Definition: qgsgeometry.h:134
std::unique_ptr< QgsAbstractGeometry > singleSidedBuffer(double distance, int segments, int side, int joinStyle, double miterLimit, QString *errorMsg=nullptr) const
Returns a single sided buffer for a geometry.
Definition: qgsgeos.cpp:2048
static GEOSContextHandle_t getGEOSHandler()
Definition: qgsgeos.cpp:2872
int numPoints() const override
Returns the number of points in the curve.
QgsAbstractGeometry * interpolate(double distance, QString *errorMsg=nullptr) const override
Definition: qgsgeos.cpp:1579
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
bool within(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom is within this.
Definition: qgsgeos.cpp:503
const double * xData() const
Returns a const pointer to the x vertex data.
#define CATCH_GEOS_WITH_ERRMSG(r)
Definition: qgsgeos.cpp:39
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
Operation succeeded.
Nothing happened, without any error.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:68
QgsAbstractGeometry * envelope(QString *errorMsg=nullptr) const override
Definition: qgsgeos.cpp:1620
std::unique_ptr< QgsAbstractGeometry > reshapeGeometry(const QgsLineString &reshapeWithLine, EngineOperationResult *errorCode, QString *errorMsg=nullptr) const
Reshapes the geometry using a line.
Definition: qgsgeos.cpp:2074
static std::unique_ptr< QgsAbstractGeometry > fromGeos(const GEOSGeometry *geos)
Create a geometry from a GEOSGeometry.
Definition: qgsgeos.cpp:1081
void addVertex(const QgsPoint &pt)
Adds a new vertex to the end of the line string.
bool touches(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom touches this.
Definition: qgsgeos.cpp:493
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:426
Geometry collection.
double lineLocatePoint(const QgsPoint &point, QString *errorMsg=nullptr) const
Returns a distance representing the location along this linestring of the closest point on this lines...
Definition: qgsgeos.cpp:2284
void prepareGeometry() override
Prepares the geometry, so that subsequent calls to spatial relation methods are much faster...
Definition: qgsgeos.cpp:198
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:202
double mAt(int index) const
Returns the m value of the specified node in the line string.
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:140
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
Definition: qgsgeos.h:79
double yAt(int index) const override
Returns the y-coordinate of the specified node in the line string.
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:812
double hausdorffDistanceDensify(const QgsAbstractGeometry *geom, double densifyFraction, QString *errorMsg=nullptr) const
Returns the Hausdorff distance between this geometry and geom.
Definition: qgsgeos.cpp:465
bool relatePattern(const QgsAbstractGeometry *geom, const QString &pattern, QString *errorMsg=nullptr) const override
Tests whether two geometries are related by a specified Dimensional Extended 9 Intersection Model (DE...
Definition: qgsgeos.cpp:557
QgsPoint * centroid(QString *errorMsg=nullptr) const override
Calculates the centroid of this.
Definition: qgsgeos.cpp:1594
T qgsgeometry_cast(const QgsAbstractGeometry *geom)
Multi curve geometry collection.
Definition: qgsmulticurve.h:29
double distance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Calculates the distance between this and geom.
Definition: qgsgeos.cpp:419
static QgsGeometry polygonize(const QVector< const QgsAbstractGeometry *> &geometries, QString *errorMsg=nullptr)
Creates a GeometryCollection geometry containing possible polygons formed from the constituent linewo...
Definition: qgsgeos.cpp:2314
QgsPoint * clone() const override
Clones the geometry by performing a deep copy.
Definition: qgspoint.cpp:97
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
Abstract base class for all geometries.
virtual int dimension() const =0
Returns the inherent dimension of the geometry.
std::unique_ptr< GEOSBufferParams, GeosDeleter > buffer_params_unique_ptr
Scoped GEOS buffer params pointer.
Definition: qgsgeos.h:89
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
QgsGeometry delaunayTriangulation(double tolerance=0.0, bool edgesOnly=false, QString *errorMsg=nullptr) const
Returns the Delaunay triangulation for the vertices of the geometry.
Definition: qgsgeos.cpp:2385
const double * yData() const
Returns a const pointer to the y vertex data.
Error occurred while creating a noded geometry.
double x
Definition: qgspointxy.h:47
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
Error occurred in the geometry engine.
const double * zData() const
Returns a const pointer to the z vertex data, or nullptr if the linestring does not have z values...
int numGeometries() const
Returns the number of geometries within the collection.
QgsAbstractGeometry * symDifference(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Calculate the symmetric difference of this and geom.
Definition: qgsgeos.cpp:414
Contains geos related utilities and functions.
Definition: qgsgeos.h:41
std::unique_ptr< QgsAbstractGeometry > clip(const QgsRectangle &rectangle, QString *errorMsg=nullptr) const
Performs a fast, non-robust intersection between the geometry and a rectangle.
Definition: qgsgeos.cpp:227
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
QgsAbstractGeometry * combine(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Calculate the combination of this and geom.
Definition: qgsgeos.cpp:361
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
void geometryChanged() override
Should be called whenever the geometry associated with the engine has been modified and the engine mu...
Definition: qgsgeos.cpp:191
QVector< QgsPoint > QgsPointSequence
static QgsPoint coordSeqPoint(const GEOSCoordSequence *cs, int i, bool hasZ, bool hasM)
Definition: qgsgeos.cpp:1264
static QgsGeometry::OperationResult addPart(QgsAbstractGeometry *geometry, std::unique_ptr< QgsAbstractGeometry > part)
Add a part to multi type geometry.
QgsAbstractGeometry * offsetCurve(double distance, int segments, int joinStyle, double miterLimit, QString *errorMsg=nullptr) const override
Definition: qgsgeos.cpp:2033
QgsAbstractGeometry * convexHull(QString *errorMsg=nullptr) const override
Calculate the convex hull of this.
Definition: qgsgeos.cpp:1663
static geos::unique_ptr asGeos(const QgsGeometry &geometry, double precision=0)
Returns a geos geometry - caller takes ownership of the object (should be deleted with GEOSGeom_destr...
Definition: qgsgeos.cpp:166
The base geometry on which the operation is done is invalid or empty.
Definition: qgsgeometry.h:138
Multi polygon geometry collection.
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
bool contains(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom contains this.
Definition: qgsgeos.cpp:513
void setYMaximum(double y)
Set the maximum y value.
Definition: qgsrectangle.h:145
std::unique_ptr< QgsAbstractGeometry > subdivide(int maxNodes, QString *errorMsg=nullptr) const
Subdivides the geometry.
Definition: qgsgeos.cpp:341
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:43
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...
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
QString relate(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Returns the Dimensional Extended 9 Intersection Model (DE-9IM) representation of the relationship bet...
Definition: qgsgeos.cpp:523
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:436
QgsPoint pointN(int i) const
Returns the specified point from inside the line string.
EngineOperationResult splitGeometry(const QgsLineString &splitLine, QVector< QgsGeometry > &newGeometries, bool topological, QgsPointSequence &topologyTestPoints, QString *errorMsg=nullptr) const override
Splits this geometry according to a given line.
Definition: qgsgeos.cpp:619
bool intersects(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom intersects this.
Definition: qgsgeos.cpp:488
The input is not valid.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
double hausdorffDistance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const
Returns the Hausdorff distance between this geometry and geom.
Definition: qgsgeos.cpp:442
double xAt(int index) const override
Returns the x-coordinate of the specified node in the line string.
QgsAbstractGeometry * difference(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Calculate the difference of this and geom.
Definition: qgsgeos.cpp:222
bool overlaps(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom overlaps this.
Definition: qgsgeos.cpp:508
double z
Definition: qgspoint.h:43
Contains geometry relation and modification algorithms.
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
static std::unique_ptr< QgsPolygon > fromGeosPolygon(const GEOSGeometry *geos)
Definition: qgsgeos.cpp:1173
std::unique_ptr< GEOSCoordSequence, GeosDeleter > coord_sequence_unique_ptr
Scoped GEOS coordinate sequence pointer.
Definition: qgsgeos.h:94
EngineOperationResult
Success or failure of a geometry operation.
QgsGeometry voronoiDiagram(const QgsAbstractGeometry *extent=nullptr, double tolerance=0.0, bool edgesOnly=false, QString *errorMsg=nullptr) const
Creates a Voronoi diagram for the nodes contained within the geometry.
Definition: qgsgeos.cpp:2353
static QgsGeometry geometryFromGeos(GEOSGeometry *geos)
Creates a new QgsGeometry object, feeding in a geometry in GEOS format.
Definition: qgsgeos.cpp:153
Polygon geometry type.
Definition: qgspolygon.h:31
const QgsCurve * exteriorRing() const
Returns the curve polygon&#39;s exterior ring.
static Type flatType(Type type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:576
The geometry on which the operation occurs is not valid.
#define DEFAULT_QUADRANT_SEGMENTS
Definition: qgsgeos.cpp:31
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
bool isSimple(QString *errorMsg=nullptr) const override
Determines whether the geometry is simple (according to OGC definition).
Definition: qgsgeos.cpp:1771
bool crosses(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom crosses this.
Definition: qgsgeos.cpp:498
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:130
QgsAbstractGeometry * simplify(double tolerance, QString *errorMsg=nullptr) const override
Definition: qgsgeos.cpp:1564
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
double m
Definition: qgspoint.h:44
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:209
double x
Definition: qgspoint.h:41