QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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 {
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  // we will need prepared geometry for intersection tests
884  const_cast<QgsGeos *>( this )->prepareGeometry();
885  if ( !mGeosPrepared )
886  return EngineError;
887 
888  //first test if linestring intersects geometry. If not, return straight away
889  if ( !GEOSPreparedIntersects_r( geosinit()->ctxt, mGeosPrepared.get(), splitLine ) )
890  return NothingHappened;
891 
892  //first union all the polygon rings together (to get them noded, see JTS developer guide)
893  geos::unique_ptr nodedGeometry = nodeGeometries( splitLine, mGeos.get() );
894  if ( !nodedGeometry )
895  return NodedGeometryError; //an error occurred during noding
896 
897  const GEOSGeometry *noded = nodedGeometry.get();
898  geos::unique_ptr polygons( GEOSPolygonize_r( geosinit()->ctxt, &noded, 1 ) );
899  if ( !polygons || numberOfGeometries( polygons.get() ) == 0 )
900  {
901  return InvalidBaseGeometry;
902  }
903 
904  //test every polygon is contained in original geometry
905  //include in result if yes
906  QVector<GEOSGeometry *> testedGeometries;
907 
908  // test whether the polygon parts returned from polygonize algorithm actually
909  // belong to the source polygon geometry (if the source polygon contains some holes,
910  // those would be also returned by polygonize and we need to skip them)
911  for ( int i = 0; i < numberOfGeometries( polygons.get() ); i++ )
912  {
913  const GEOSGeometry *polygon = GEOSGetGeometryN_r( geosinit()->ctxt, polygons.get(), i );
914 
915  geos::unique_ptr pointOnSurface( GEOSPointOnSurface_r( geosinit()->ctxt, polygon ) );
916  if ( pointOnSurface && GEOSPreparedIntersects_r( geosinit()->ctxt, mGeosPrepared.get(), pointOnSurface.get() ) )
917  testedGeometries << GEOSGeom_clone_r( geosinit()->ctxt, polygon );
918  }
919 
920  int nGeometriesThis = numberOfGeometries( mGeos.get() ); //original number of geometries
921  if ( testedGeometries.empty() || testedGeometries.size() == nGeometriesThis )
922  {
923  //no split done, preserve original geometry
924  for ( int i = 0; i < testedGeometries.size(); ++i )
925  {
926  GEOSGeom_destroy_r( geosinit()->ctxt, testedGeometries[i] );
927  }
928  return NothingHappened;
929  }
930 
931  // For multi-part geometries, try to identify parts that have been unchanged and try to merge them back
932  // to a single multi-part geometry. For example, if there's a multi-polygon with three parts, but only
933  // one part is being split, this function makes sure that the other two parts will be kept in a multi-part
934  // geometry rather than being separated into two single-part geometries.
935  mergeGeometriesMultiTypeSplit( testedGeometries );
936 
937  int i;
938  for ( i = 0; i < testedGeometries.size() && GEOSisValid_r( geosinit()->ctxt, testedGeometries[i] ); ++i )
939  ;
940 
941  if ( i < testedGeometries.size() )
942  {
943  for ( i = 0; i < testedGeometries.size(); ++i )
944  GEOSGeom_destroy_r( geosinit()->ctxt, testedGeometries[i] );
945 
946  return InvalidBaseGeometry;
947  }
948 
949  for ( i = 0; i < testedGeometries.size(); ++i )
950  {
951  newGeometries << QgsGeometry( fromGeos( testedGeometries[i] ) );
952  GEOSGeom_destroy_r( geosinit()->ctxt, testedGeometries[i] );
953  }
954 
955  return Success;
956 }
957 
958 geos::unique_ptr QgsGeos::nodeGeometries( const GEOSGeometry *splitLine, const GEOSGeometry *geom )
959 {
960  if ( !splitLine || !geom )
961  return nullptr;
962 
963  geos::unique_ptr geometryBoundary;
964  if ( GEOSGeomTypeId_r( geosinit()->ctxt, geom ) == GEOS_POLYGON || GEOSGeomTypeId_r( geosinit()->ctxt, geom ) == GEOS_MULTIPOLYGON )
965  geometryBoundary.reset( GEOSBoundary_r( geosinit()->ctxt, geom ) );
966  else
967  geometryBoundary.reset( GEOSGeom_clone_r( geosinit()->ctxt, geom ) );
968 
969  geos::unique_ptr splitLineClone( GEOSGeom_clone_r( geosinit()->ctxt, splitLine ) );
970  geos::unique_ptr unionGeometry( GEOSUnion_r( geosinit()->ctxt, splitLineClone.get(), geometryBoundary.get() ) );
971 
972  return unionGeometry;
973 }
974 
975 int QgsGeos::mergeGeometriesMultiTypeSplit( QVector<GEOSGeometry *> &splitResult ) const
976 {
977  if ( !mGeos )
978  return 1;
979 
980  //convert mGeos to geometry collection
981  int type = GEOSGeomTypeId_r( geosinit()->ctxt, mGeos.get() );
982  if ( type != GEOS_GEOMETRYCOLLECTION &&
983  type != GEOS_MULTILINESTRING &&
984  type != GEOS_MULTIPOLYGON &&
985  type != GEOS_MULTIPOINT )
986  return 0;
987 
988  QVector<GEOSGeometry *> copyList = splitResult;
989  splitResult.clear();
990 
991  //collect all the geometries that belong to the initial multifeature
992  QVector<GEOSGeometry *> unionGeom;
993 
994  for ( int i = 0; i < copyList.size(); ++i )
995  {
996  //is this geometry a part of the original multitype?
997  bool isPart = false;
998  for ( int j = 0; j < GEOSGetNumGeometries_r( geosinit()->ctxt, mGeos.get() ); j++ )
999  {
1000  if ( GEOSEquals_r( geosinit()->ctxt, copyList[i], GEOSGetGeometryN_r( geosinit()->ctxt, mGeos.get(), j ) ) )
1001  {
1002  isPart = true;
1003  break;
1004  }
1005  }
1006 
1007  if ( isPart )
1008  {
1009  unionGeom << copyList[i];
1010  }
1011  else
1012  {
1013  QVector<GEOSGeometry *> geomVector;
1014  geomVector << copyList[i];
1015 
1016  if ( type == GEOS_MULTILINESTRING )
1017  splitResult << createGeosCollection( GEOS_MULTILINESTRING, geomVector ).release();
1018  else if ( type == GEOS_MULTIPOLYGON )
1019  splitResult << createGeosCollection( GEOS_MULTIPOLYGON, geomVector ).release();
1020  else
1021  GEOSGeom_destroy_r( geosinit()->ctxt, copyList[i] );
1022  }
1023  }
1024 
1025  //make multifeature out of unionGeom
1026  if ( !unionGeom.isEmpty() )
1027  {
1028  if ( type == GEOS_MULTILINESTRING )
1029  splitResult << createGeosCollection( GEOS_MULTILINESTRING, unionGeom ).release();
1030  else if ( type == GEOS_MULTIPOLYGON )
1031  splitResult << createGeosCollection( GEOS_MULTIPOLYGON, unionGeom ).release();
1032  }
1033  else
1034  {
1035  unionGeom.clear();
1036  }
1037 
1038  return 0;
1039 }
1040 
1041 geos::unique_ptr QgsGeos::createGeosCollection( int typeId, const QVector<GEOSGeometry *> &geoms )
1042 {
1043  int nNullGeoms = geoms.count( nullptr );
1044  int nNotNullGeoms = geoms.size() - nNullGeoms;
1045 
1046  GEOSGeometry **geomarr = new GEOSGeometry*[ nNotNullGeoms ];
1047  if ( !geomarr )
1048  {
1049  return nullptr;
1050  }
1051 
1052  int i = 0;
1053  QVector<GEOSGeometry *>::const_iterator geomIt = geoms.constBegin();
1054  for ( ; geomIt != geoms.constEnd(); ++geomIt )
1055  {
1056  if ( *geomIt )
1057  {
1058  geomarr[i] = *geomIt;
1059  ++i;
1060  }
1061  }
1062  geos::unique_ptr geom;
1063 
1064  try
1065  {
1066  geom.reset( GEOSGeom_createCollection_r( geosinit()->ctxt, typeId, geomarr, nNotNullGeoms ) );
1067  }
1068  catch ( GEOSException & )
1069  {
1070  }
1071 
1072  delete [] geomarr;
1073 
1074  return geom;
1075 }
1076 
1077 std::unique_ptr<QgsAbstractGeometry> QgsGeos::fromGeos( const GEOSGeometry *geos )
1078 {
1079  if ( !geos )
1080  {
1081  return nullptr;
1082  }
1083 
1084  int nCoordDims = GEOSGeom_getCoordinateDimension_r( geosinit()->ctxt, geos );
1085  int nDims = GEOSGeom_getDimensions_r( geosinit()->ctxt, geos );
1086  bool hasZ = ( nCoordDims == 3 );
1087  bool hasM = ( ( nDims - nCoordDims ) == 1 );
1088 
1089  switch ( GEOSGeomTypeId_r( geosinit()->ctxt, geos ) )
1090  {
1091  case GEOS_POINT: // a point
1092  {
1093  const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, geos );
1094  unsigned int nPoints = 0;
1095  GEOSCoordSeq_getSize_r( geosinit()->ctxt, cs, &nPoints );
1096  return nPoints > 0 ? std::unique_ptr<QgsAbstractGeometry>( coordSeqPoint( cs, 0, hasZ, hasM ).clone() ) : qgis::make_unique< QgsPoint >();
1097  }
1098  case GEOS_LINESTRING:
1099  {
1100  return sequenceToLinestring( geos, hasZ, hasM );
1101  }
1102  case GEOS_POLYGON:
1103  {
1104  return fromGeosPolygon( geos );
1105  }
1106  case GEOS_MULTIPOINT:
1107  {
1108  std::unique_ptr< QgsMultiPoint > multiPoint( new QgsMultiPoint() );
1109  int nParts = GEOSGetNumGeometries_r( geosinit()->ctxt, geos );
1110  multiPoint->reserve( nParts );
1111  for ( int i = 0; i < nParts; ++i )
1112  {
1113  const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, GEOSGetGeometryN_r( geosinit()->ctxt, geos, i ) );
1114  if ( cs )
1115  {
1116  unsigned int nPoints = 0;
1117  GEOSCoordSeq_getSize_r( geosinit()->ctxt, cs, &nPoints );
1118  if ( nPoints > 0 )
1119  multiPoint->addGeometry( coordSeqPoint( cs, 0, hasZ, hasM ).clone() );
1120  }
1121  }
1122  return std::move( multiPoint );
1123  }
1124  case GEOS_MULTILINESTRING:
1125  {
1126  std::unique_ptr< QgsMultiLineString > multiLineString( new QgsMultiLineString() );
1127  int nParts = GEOSGetNumGeometries_r( geosinit()->ctxt, geos );
1128  multiLineString->reserve( nParts );
1129  for ( int i = 0; i < nParts; ++i )
1130  {
1131  std::unique_ptr< QgsLineString >line( sequenceToLinestring( GEOSGetGeometryN_r( geosinit()->ctxt, geos, i ), hasZ, hasM ) );
1132  if ( line )
1133  {
1134  multiLineString->addGeometry( line.release() );
1135  }
1136  }
1137  return std::move( multiLineString );
1138  }
1139  case GEOS_MULTIPOLYGON:
1140  {
1141  std::unique_ptr< QgsMultiPolygon > multiPolygon( new QgsMultiPolygon() );
1142 
1143  int nParts = GEOSGetNumGeometries_r( geosinit()->ctxt, geos );
1144  multiPolygon->reserve( nParts );
1145  for ( int i = 0; i < nParts; ++i )
1146  {
1147  std::unique_ptr< QgsPolygon > poly = fromGeosPolygon( GEOSGetGeometryN_r( geosinit()->ctxt, geos, i ) );
1148  if ( poly )
1149  {
1150  multiPolygon->addGeometry( poly.release() );
1151  }
1152  }
1153  return std::move( multiPolygon );
1154  }
1155  case GEOS_GEOMETRYCOLLECTION:
1156  {
1157  std::unique_ptr< QgsGeometryCollection > geomCollection( new QgsGeometryCollection() );
1158  int nParts = GEOSGetNumGeometries_r( geosinit()->ctxt, geos );
1159  geomCollection->reserve( nParts );
1160  for ( int i = 0; i < nParts; ++i )
1161  {
1162  std::unique_ptr< QgsAbstractGeometry > geom( fromGeos( GEOSGetGeometryN_r( geosinit()->ctxt, geos, i ) ) );
1163  if ( geom )
1164  {
1165  geomCollection->addGeometry( geom.release() );
1166  }
1167  }
1168  return std::move( geomCollection );
1169  }
1170  }
1171  return nullptr;
1172 }
1173 
1174 std::unique_ptr<QgsPolygon> QgsGeos::fromGeosPolygon( const GEOSGeometry *geos )
1175 {
1176  if ( GEOSGeomTypeId_r( geosinit()->ctxt, geos ) != GEOS_POLYGON )
1177  {
1178  return nullptr;
1179  }
1180 
1181  int nCoordDims = GEOSGeom_getCoordinateDimension_r( geosinit()->ctxt, geos );
1182  int nDims = GEOSGeom_getDimensions_r( geosinit()->ctxt, geos );
1183  bool hasZ = ( nCoordDims == 3 );
1184  bool hasM = ( ( nDims - nCoordDims ) == 1 );
1185 
1186  std::unique_ptr< QgsPolygon > polygon( new QgsPolygon() );
1187 
1188  const GEOSGeometry *ring = GEOSGetExteriorRing_r( geosinit()->ctxt, geos );
1189  if ( ring )
1190  {
1191  polygon->setExteriorRing( sequenceToLinestring( ring, hasZ, hasM ).release() );
1192  }
1193 
1194  QVector<QgsCurve *> interiorRings;
1195  const int ringCount = GEOSGetNumInteriorRings_r( geosinit()->ctxt, geos );
1196  interiorRings.reserve( ringCount );
1197  for ( int i = 0; i < ringCount; ++i )
1198  {
1199  ring = GEOSGetInteriorRingN_r( geosinit()->ctxt, geos, i );
1200  if ( ring )
1201  {
1202  interiorRings.push_back( sequenceToLinestring( ring, hasZ, hasM ).release() );
1203  }
1204  }
1205  polygon->setInteriorRings( interiorRings );
1206 
1207  return polygon;
1208 }
1209 
1210 std::unique_ptr<QgsLineString> QgsGeos::sequenceToLinestring( const GEOSGeometry *geos, bool hasZ, bool hasM )
1211 {
1212  const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, geos );
1213  unsigned int nPoints;
1214  GEOSCoordSeq_getSize_r( geosinit()->ctxt, cs, &nPoints );
1215  QVector< double > xOut( nPoints );
1216  QVector< double > yOut( nPoints );
1217  QVector< double > zOut;
1218  if ( hasZ )
1219  zOut.resize( nPoints );
1220  QVector< double > mOut;
1221  if ( hasM )
1222  mOut.resize( nPoints );
1223  double *x = xOut.data();
1224  double *y = yOut.data();
1225  double *z = zOut.data();
1226  double *m = mOut.data();
1227  for ( unsigned int i = 0; i < nPoints; ++i )
1228  {
1229 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
1230  if ( hasZ )
1231  GEOSCoordSeq_getXYZ_r( geosinit()->ctxt, cs, i, x++, y++, z++ );
1232  else
1233  GEOSCoordSeq_getXY_r( geosinit()->ctxt, cs, i, x++, y++ );
1234 #else
1235  GEOSCoordSeq_getX_r( geosinit()->ctxt, cs, i, x++ );
1236  GEOSCoordSeq_getY_r( geosinit()->ctxt, cs, i, y++ );
1237  if ( hasZ )
1238  {
1239  GEOSCoordSeq_getZ_r( geosinit()->ctxt, cs, i, z++ );
1240  }
1241 #endif
1242  if ( hasM )
1243  {
1244  GEOSCoordSeq_getOrdinate_r( geosinit()->ctxt, cs, i, 3, m++ );
1245  }
1246  }
1247  std::unique_ptr< QgsLineString > line( new QgsLineString( xOut, yOut, zOut, mOut ) );
1248  return line;
1249 }
1250 
1251 int QgsGeos::numberOfGeometries( GEOSGeometry *g )
1252 {
1253  if ( !g )
1254  return 0;
1255 
1256  int geometryType = GEOSGeomTypeId_r( geosinit()->ctxt, g );
1257  if ( geometryType == GEOS_POINT || geometryType == GEOS_LINESTRING || geometryType == GEOS_LINEARRING
1258  || geometryType == GEOS_POLYGON )
1259  return 1;
1260 
1261  //calling GEOSGetNumGeometries is save for multi types and collections also in geos2
1262  return GEOSGetNumGeometries_r( geosinit()->ctxt, g );
1263 }
1264 
1265 QgsPoint QgsGeos::coordSeqPoint( const GEOSCoordSequence *cs, int i, bool hasZ, bool hasM )
1266 {
1267  if ( !cs )
1268  {
1269  return QgsPoint();
1270  }
1271 
1272  double x, y;
1273  double z = 0;
1274  double m = 0;
1275 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
1276  if ( hasZ )
1277  GEOSCoordSeq_getXYZ_r( geosinit()->ctxt, cs, i, &x, &y, &z );
1278  else
1279  GEOSCoordSeq_getXY_r( geosinit()->ctxt, cs, i, &x, &y );
1280 #else
1281  GEOSCoordSeq_getX_r( geosinit()->ctxt, cs, i, &x );
1282  GEOSCoordSeq_getY_r( geosinit()->ctxt, cs, i, &y );
1283  if ( hasZ )
1284  {
1285  GEOSCoordSeq_getZ_r( geosinit()->ctxt, cs, i, &z );
1286  }
1287 #endif
1288  if ( hasM )
1289  {
1290  GEOSCoordSeq_getOrdinate_r( geosinit()->ctxt, cs, i, 3, &m );
1291  }
1292 
1294  if ( hasZ && hasM )
1295  {
1297  }
1298  else if ( hasZ )
1299  {
1300  t = QgsWkbTypes::PointZ;
1301  }
1302  else if ( hasM )
1303  {
1304  t = QgsWkbTypes::PointM;
1305  }
1306  return QgsPoint( t, x, y, z, m );
1307 }
1308 
1310 {
1311  if ( !geom )
1312  return nullptr;
1313 
1314  int coordDims = 2;
1315  if ( geom->is3D() )
1316  {
1317  ++coordDims;
1318  }
1319  if ( geom->isMeasure() )
1320  {
1321  ++coordDims;
1322  }
1323 
1325  {
1326  int geosType = GEOS_GEOMETRYCOLLECTION;
1327 
1329  {
1330  switch ( QgsWkbTypes::geometryType( geom->wkbType() ) )
1331  {
1333  geosType = GEOS_MULTIPOINT;
1334  break;
1335 
1337  geosType = GEOS_MULTILINESTRING;
1338  break;
1339 
1341  geosType = GEOS_MULTIPOLYGON;
1342  break;
1343 
1346  return nullptr;
1347  break;
1348  }
1349  }
1350 
1351 
1352  const QgsGeometryCollection *c = qgsgeometry_cast<const QgsGeometryCollection *>( geom );
1353 
1354  if ( !c )
1355  return nullptr;
1356 
1357  QVector< GEOSGeometry * > geomVector( c->numGeometries() );
1358  for ( int i = 0; i < c->numGeometries(); ++i )
1359  {
1360  geomVector[i] = asGeos( c->geometryN( i ), precision ).release();
1361  }
1362  return createGeosCollection( geosType, geomVector );
1363  }
1364  else
1365  {
1366  switch ( QgsWkbTypes::geometryType( geom->wkbType() ) )
1367  {
1369  return createGeosPoint( static_cast<const QgsPoint *>( geom ), coordDims, precision );
1370  break;
1371 
1373  return createGeosLinestring( static_cast<const QgsLineString *>( geom ), precision );
1374  break;
1375 
1377  return createGeosPolygon( static_cast<const QgsPolygon *>( geom ), precision );
1378  break;
1379 
1382  return nullptr;
1383  break;
1384  }
1385  }
1386  return nullptr;
1387 }
1388 
1389 std::unique_ptr<QgsAbstractGeometry> QgsGeos::overlay( const QgsAbstractGeometry *geom, Overlay op, QString *errorMsg ) const
1390 {
1391  if ( !mGeos || !geom )
1392  {
1393  return nullptr;
1394  }
1395 
1396  geos::unique_ptr geosGeom( asGeos( geom, mPrecision ) );
1397  if ( !geosGeom )
1398  {
1399  return nullptr;
1400  }
1401 
1402  try
1403  {
1404  geos::unique_ptr opGeom;
1405  switch ( op )
1406  {
1407  case OverlayIntersection:
1408  opGeom.reset( GEOSIntersection_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) );
1409  break;
1410  case OverlayDifference:
1411  opGeom.reset( GEOSDifference_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) );
1412  break;
1413  case OverlayUnion:
1414  {
1415  geos::unique_ptr unionGeometry( GEOSUnion_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) );
1416 
1417  if ( unionGeometry && GEOSGeomTypeId_r( geosinit()->ctxt, unionGeometry.get() ) == GEOS_MULTILINESTRING )
1418  {
1419  geos::unique_ptr mergedLines( GEOSLineMerge_r( geosinit()->ctxt, unionGeometry.get() ) );
1420  if ( mergedLines )
1421  {
1422  unionGeometry = std::move( mergedLines );
1423  }
1424  }
1425 
1426  opGeom = std::move( unionGeometry );
1427  }
1428  break;
1429  case OverlaySymDifference:
1430  opGeom.reset( GEOSSymDifference_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) );
1431  break;
1432  default: //unknown op
1433  return nullptr;
1434  }
1435  return fromGeos( opGeom.get() );
1436  }
1437  catch ( GEOSException &e )
1438  {
1439  if ( errorMsg )
1440  {
1441  *errorMsg = e.what();
1442  }
1443  return nullptr;
1444  }
1445 }
1446 
1447 bool QgsGeos::relation( const QgsAbstractGeometry *geom, Relation r, QString *errorMsg ) const
1448 {
1449  if ( !mGeos || !geom )
1450  {
1451  return false;
1452  }
1453 
1454  geos::unique_ptr geosGeom( asGeos( geom, mPrecision ) );
1455  if ( !geosGeom )
1456  {
1457  return false;
1458  }
1459 
1460  bool result = false;
1461  try
1462  {
1463  if ( mGeosPrepared ) //use faster version with prepared geometry
1464  {
1465  switch ( r )
1466  {
1467  case RelationIntersects:
1468  result = ( GEOSPreparedIntersects_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1469  break;
1470  case RelationTouches:
1471  result = ( GEOSPreparedTouches_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1472  break;
1473  case RelationCrosses:
1474  result = ( GEOSPreparedCrosses_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1475  break;
1476  case RelationWithin:
1477  result = ( GEOSPreparedWithin_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1478  break;
1479  case RelationContains:
1480  result = ( GEOSPreparedContains_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1481  break;
1482  case RelationDisjoint:
1483  result = ( GEOSPreparedDisjoint_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1484  break;
1485  case RelationOverlaps:
1486  result = ( GEOSPreparedOverlaps_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1487  break;
1488  default:
1489  return false;
1490  }
1491  return result;
1492  }
1493 
1494  switch ( r )
1495  {
1496  case RelationIntersects:
1497  result = ( GEOSIntersects_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1498  break;
1499  case RelationTouches:
1500  result = ( GEOSTouches_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1501  break;
1502  case RelationCrosses:
1503  result = ( GEOSCrosses_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1504  break;
1505  case RelationWithin:
1506  result = ( GEOSWithin_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1507  break;
1508  case RelationContains:
1509  result = ( GEOSContains_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1510  break;
1511  case RelationDisjoint:
1512  result = ( GEOSDisjoint_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1513  break;
1514  case RelationOverlaps:
1515  result = ( GEOSOverlaps_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1516  break;
1517  default:
1518  return false;
1519  }
1520  }
1521  catch ( GEOSException &e )
1522  {
1523  if ( errorMsg )
1524  {
1525  *errorMsg = e.what();
1526  }
1527  return false;
1528  }
1529 
1530  return result;
1531 }
1532 
1533 QgsAbstractGeometry *QgsGeos::buffer( double distance, int segments, QString *errorMsg ) const
1534 {
1535  if ( !mGeos )
1536  {
1537  return nullptr;
1538  }
1539 
1541  try
1542  {
1543  geos.reset( GEOSBuffer_r( geosinit()->ctxt, mGeos.get(), distance, segments ) );
1544  }
1545  CATCH_GEOS_WITH_ERRMSG( nullptr );
1546  return fromGeos( geos.get() ).release();
1547 }
1548 
1549 QgsAbstractGeometry *QgsGeos::buffer( double distance, int segments, int endCapStyle, int joinStyle, double miterLimit, QString *errorMsg ) const
1550 {
1551  if ( !mGeos )
1552  {
1553  return nullptr;
1554  }
1555 
1557  try
1558  {
1559  geos.reset( GEOSBufferWithStyle_r( geosinit()->ctxt, mGeos.get(), distance, segments, endCapStyle, joinStyle, miterLimit ) );
1560  }
1561  CATCH_GEOS_WITH_ERRMSG( nullptr );
1562  return fromGeos( geos.get() ).release();
1563 }
1564 
1565 QgsAbstractGeometry *QgsGeos::simplify( double tolerance, QString *errorMsg ) const
1566 {
1567  if ( !mGeos )
1568  {
1569  return nullptr;
1570  }
1572  try
1573  {
1574  geos.reset( GEOSTopologyPreserveSimplify_r( geosinit()->ctxt, mGeos.get(), tolerance ) );
1575  }
1576  CATCH_GEOS_WITH_ERRMSG( nullptr );
1577  return fromGeos( geos.get() ).release();
1578 }
1579 
1580 QgsAbstractGeometry *QgsGeos::interpolate( double distance, QString *errorMsg ) const
1581 {
1582  if ( !mGeos )
1583  {
1584  return nullptr;
1585  }
1587  try
1588  {
1589  geos.reset( GEOSInterpolate_r( geosinit()->ctxt, mGeos.get(), distance ) );
1590  }
1591  CATCH_GEOS_WITH_ERRMSG( nullptr );
1592  return fromGeos( geos.get() ).release();
1593 }
1594 
1595 QgsPoint *QgsGeos::centroid( QString *errorMsg ) const
1596 {
1597  if ( !mGeos )
1598  {
1599  return nullptr;
1600  }
1601 
1603  double x;
1604  double y;
1605 
1606  try
1607  {
1608  geos.reset( GEOSGetCentroid_r( geosinit()->ctxt, mGeos.get() ) );
1609 
1610  if ( !geos )
1611  return nullptr;
1612 
1613  GEOSGeomGetX_r( geosinit()->ctxt, geos.get(), &x );
1614  GEOSGeomGetY_r( geosinit()->ctxt, geos.get(), &y );
1615  }
1616  CATCH_GEOS_WITH_ERRMSG( nullptr );
1617 
1618  return new QgsPoint( x, y );
1619 }
1620 
1621 QgsAbstractGeometry *QgsGeos::envelope( QString *errorMsg ) const
1622 {
1623  if ( !mGeos )
1624  {
1625  return nullptr;
1626  }
1628  try
1629  {
1630  geos.reset( GEOSEnvelope_r( geosinit()->ctxt, mGeos.get() ) );
1631  }
1632  CATCH_GEOS_WITH_ERRMSG( nullptr );
1633  return fromGeos( geos.get() ).release();
1634 }
1635 
1636 QgsPoint *QgsGeos::pointOnSurface( QString *errorMsg ) const
1637 {
1638  if ( !mGeos )
1639  {
1640  return nullptr;
1641  }
1642 
1643  double x;
1644  double y;
1645 
1647  try
1648  {
1649  geos.reset( GEOSPointOnSurface_r( geosinit()->ctxt, mGeos.get() ) );
1650 
1651  if ( !geos || GEOSisEmpty_r( geosinit()->ctxt, geos.get() ) != 0 )
1652  {
1653  return nullptr;
1654  }
1655 
1656  GEOSGeomGetX_r( geosinit()->ctxt, geos.get(), &x );
1657  GEOSGeomGetY_r( geosinit()->ctxt, geos.get(), &y );
1658  }
1659  CATCH_GEOS_WITH_ERRMSG( nullptr );
1660 
1661  return new QgsPoint( x, y );
1662 }
1663 
1664 QgsAbstractGeometry *QgsGeos::convexHull( QString *errorMsg ) const
1665 {
1666  if ( !mGeos )
1667  {
1668  return nullptr;
1669  }
1670 
1671  try
1672  {
1673  geos::unique_ptr cHull( GEOSConvexHull_r( geosinit()->ctxt, mGeos.get() ) );
1674  std::unique_ptr< QgsAbstractGeometry > cHullGeom = fromGeos( cHull.get() );
1675  return cHullGeom.release();
1676  }
1677  CATCH_GEOS_WITH_ERRMSG( nullptr );
1678 }
1679 
1680 bool QgsGeos::isValid( QString *errorMsg, const bool allowSelfTouchingHoles, QgsGeometry *errorLoc ) const
1681 {
1682  if ( !mGeos )
1683  {
1684  return false;
1685  }
1686 
1687  try
1688  {
1689  GEOSGeometry *g1 = nullptr;
1690  char *r = nullptr;
1691  char res = GEOSisValidDetail_r( geosinit()->ctxt, mGeos.get(), allowSelfTouchingHoles ? GEOSVALID_ALLOW_SELFTOUCHING_RING_FORMING_HOLE : 0, &r, &g1 );
1692  const bool invalid = res != 1;
1693 
1694  QString error;
1695  if ( r )
1696  {
1697  error = QString( r );
1698  GEOSFree_r( geosinit()->ctxt, r );
1699  }
1700 
1701  if ( invalid && errorMsg )
1702  {
1703  static QgsStringMap translatedErrors;
1704 
1705  if ( translatedErrors.empty() )
1706  {
1707  // Copied from https://git.osgeo.org/gitea/geos/geos/src/branch/master/src/operation/valid/TopologyValidationError.cpp
1708  translatedErrors.insert( QStringLiteral( "topology validation error" ), QObject::tr( "Topology validation error", "GEOS Error" ) );
1709  translatedErrors.insert( QStringLiteral( "repeated point" ), QObject::tr( "Repeated point", "GEOS Error" ) );
1710  translatedErrors.insert( QStringLiteral( "hole lies outside shell" ), QObject::tr( "Hole lies outside shell", "GEOS Error" ) );
1711  translatedErrors.insert( QStringLiteral( "holes are nested" ), QObject::tr( "Holes are nested", "GEOS Error" ) );
1712  translatedErrors.insert( QStringLiteral( "interior is disconnected" ), QObject::tr( "Interior is disconnected", "GEOS Error" ) );
1713  translatedErrors.insert( QStringLiteral( "self-intersection" ), QObject::tr( "Self-intersection", "GEOS Error" ) );
1714  translatedErrors.insert( QStringLiteral( "ring self-intersection" ), QObject::tr( "Ring self-intersection", "GEOS Error" ) );
1715  translatedErrors.insert( QStringLiteral( "nested shells" ), QObject::tr( "Nested shells", "GEOS Error" ) );
1716  translatedErrors.insert( QStringLiteral( "duplicate rings" ), QObject::tr( "Duplicate rings", "GEOS Error" ) );
1717  translatedErrors.insert( QStringLiteral( "too few points in geometry component" ), QObject::tr( "Too few points in geometry component", "GEOS Error" ) );
1718  translatedErrors.insert( QStringLiteral( "invalid coordinate" ), QObject::tr( "Invalid coordinate", "GEOS Error" ) );
1719  translatedErrors.insert( QStringLiteral( "ring is not closed" ), QObject::tr( "Ring is not closed", "GEOS Error" ) );
1720  }
1721 
1722  *errorMsg = translatedErrors.value( error.toLower(), error );
1723 
1724  if ( g1 && errorLoc )
1725  {
1726  *errorLoc = geometryFromGeos( g1 );
1727  }
1728  else if ( g1 )
1729  {
1730  GEOSGeom_destroy_r( geosinit()->ctxt, g1 );
1731  }
1732  }
1733  return !invalid;
1734  }
1735  CATCH_GEOS_WITH_ERRMSG( false );
1736 }
1737 
1738 bool QgsGeos::isEqual( const QgsAbstractGeometry *geom, QString *errorMsg ) const
1739 {
1740  if ( !mGeos || !geom )
1741  {
1742  return false;
1743  }
1744 
1745  try
1746  {
1747  geos::unique_ptr geosGeom( asGeos( geom, mPrecision ) );
1748  if ( !geosGeom )
1749  {
1750  return false;
1751  }
1752  bool equal = GEOSEquals_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() );
1753  return equal;
1754  }
1755  CATCH_GEOS_WITH_ERRMSG( false );
1756 }
1757 
1758 bool QgsGeos::isEmpty( QString *errorMsg ) const
1759 {
1760  if ( !mGeos )
1761  {
1762  return false;
1763  }
1764 
1765  try
1766  {
1767  return GEOSisEmpty_r( geosinit()->ctxt, mGeos.get() );
1768  }
1769  CATCH_GEOS_WITH_ERRMSG( false );
1770 }
1771 
1772 bool QgsGeos::isSimple( QString *errorMsg ) const
1773 {
1774  if ( !mGeos )
1775  {
1776  return false;
1777  }
1778 
1779  try
1780  {
1781  return GEOSisSimple_r( geosinit()->ctxt, mGeos.get() );
1782  }
1783  CATCH_GEOS_WITH_ERRMSG( false );
1784 }
1785 
1786 GEOSCoordSequence *QgsGeos::createCoordinateSequence( const QgsCurve *curve, double precision, bool forceClose )
1787 {
1788  std::unique_ptr< QgsLineString > segmentized;
1789  const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( curve );
1790 
1791  if ( !line )
1792  {
1793  segmentized.reset( curve->curveToLine() );
1794  line = segmentized.get();
1795  }
1796 
1797  if ( !line )
1798  {
1799  return nullptr;
1800  }
1801 
1802  bool hasZ = line->is3D();
1803  bool hasM = false; //line->isMeasure(); //disabled until geos supports m-coordinates
1804  int coordDims = 2;
1805  if ( hasZ )
1806  {
1807  ++coordDims;
1808  }
1809  if ( hasM )
1810  {
1811  ++coordDims;
1812  }
1813 
1814  int numPoints = line->numPoints();
1815 
1816  int numOutPoints = numPoints;
1817  if ( forceClose && ( line->pointN( 0 ) != line->pointN( numPoints - 1 ) ) )
1818  {
1819  ++numOutPoints;
1820  }
1821 
1822  GEOSCoordSequence *coordSeq = nullptr;
1823  try
1824  {
1825  coordSeq = GEOSCoordSeq_create_r( geosinit()->ctxt, numOutPoints, coordDims );
1826  if ( !coordSeq )
1827  {
1828  QgsDebugMsg( QStringLiteral( "GEOS Exception: Could not create coordinate sequence for %1 points in %2 dimensions" ).arg( numPoints ).arg( coordDims ) );
1829  return nullptr;
1830  }
1831 
1832  const double *xData = line->xData();
1833  const double *yData = line->yData();
1834  const double *zData = hasZ ? line->zData() : nullptr;
1835  const double *mData = hasM ? line->mData() : nullptr;
1836 
1837  if ( precision > 0. )
1838  {
1839  for ( int i = 0; i < numOutPoints; ++i )
1840  {
1841  if ( i >= numPoints )
1842  {
1843  // start reading back from start of line
1844  xData = line->xData();
1845  yData = line->yData();
1846  zData = hasZ ? line->zData() : nullptr;
1847  mData = hasM ? line->mData() : nullptr;
1848  }
1849 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
1850  if ( hasZ )
1851  {
1852  GEOSCoordSeq_setXYZ_r( geosinit()->ctxt, coordSeq, i, std::round( *xData++ / precision ) * precision, std::round( *yData++ / precision ) * precision, std::round( *zData++ / precision ) * precision );
1853  }
1854  else
1855  {
1856  GEOSCoordSeq_setXY_r( geosinit()->ctxt, coordSeq, i, std::round( *xData++ / precision ) * precision, std::round( *yData++ / precision ) * precision );
1857  }
1858 #else
1859  GEOSCoordSeq_setX_r( geosinit()->ctxt, coordSeq, i, std::round( *xData++ / precision ) * precision );
1860  GEOSCoordSeq_setY_r( geosinit()->ctxt, coordSeq, i, std::round( *yData++ / precision ) * precision );
1861  if ( hasZ )
1862  {
1863  GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, i, 2, std::round( *zData++ / precision ) * precision );
1864  }
1865 #endif
1866  if ( hasM )
1867  {
1868  GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, i, 3, line->mAt( *mData++ ) );
1869  }
1870  }
1871  }
1872  else
1873  {
1874  for ( int i = 0; i < numOutPoints; ++i )
1875  {
1876  if ( i >= numPoints )
1877  {
1878  // start reading back from start of line
1879  xData = line->xData();
1880  yData = line->yData();
1881  zData = hasZ ? line->zData() : nullptr;
1882  mData = hasM ? line->mData() : nullptr;
1883  }
1884 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
1885  if ( hasZ )
1886  {
1887  GEOSCoordSeq_setXYZ_r( geosinit()->ctxt, coordSeq, i, *xData++, *yData++, *zData++ );
1888  }
1889  else
1890  {
1891  GEOSCoordSeq_setXY_r( geosinit()->ctxt, coordSeq, i, *xData++, *yData++ );
1892  }
1893 #else
1894  GEOSCoordSeq_setX_r( geosinit()->ctxt, coordSeq, i, *xData++ );
1895  GEOSCoordSeq_setY_r( geosinit()->ctxt, coordSeq, i, *yData++ );
1896  if ( hasZ )
1897  {
1898  GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, i, 2, *zData++ );
1899  }
1900 #endif
1901  if ( hasM )
1902  {
1903  GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, i, 3, *mData++ );
1904  }
1905  }
1906  }
1907  }
1908  CATCH_GEOS( nullptr )
1909 
1910  return coordSeq;
1911 }
1912 
1913 geos::unique_ptr QgsGeos::createGeosPoint( const QgsAbstractGeometry *point, int coordDims, double precision )
1914 {
1915  const QgsPoint *pt = qgsgeometry_cast<const QgsPoint *>( point );
1916  if ( !pt )
1917  return nullptr;
1918 
1919  return createGeosPointXY( pt->x(), pt->y(), pt->is3D(), pt->z(), pt->isMeasure(), pt->m(), coordDims, precision );
1920 }
1921 
1922 geos::unique_ptr QgsGeos::createGeosPointXY( double x, double y, bool hasZ, double z, bool hasM, double m, int coordDims, double precision )
1923 {
1924  Q_UNUSED( hasM )
1925  Q_UNUSED( m )
1926 
1927  geos::unique_ptr geosPoint;
1928  try
1929  {
1930 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
1931  if ( coordDims == 2 )
1932  {
1933  // optimised constructor
1934  if ( precision > 0. )
1935  geosPoint.reset( GEOSGeom_createPointFromXY_r( geosinit()->ctxt, std::round( x / precision ) * precision, std::round( y / precision ) * precision ) );
1936  else
1937  geosPoint.reset( GEOSGeom_createPointFromXY_r( geosinit()->ctxt, x, y ) );
1938  return geosPoint;
1939  }
1940 #endif
1941 
1942  GEOSCoordSequence *coordSeq = GEOSCoordSeq_create_r( geosinit()->ctxt, 1, coordDims );
1943  if ( !coordSeq )
1944  {
1945  QgsDebugMsg( QStringLiteral( "GEOS Exception: Could not create coordinate sequence for point with %1 dimensions" ).arg( coordDims ) );
1946  return nullptr;
1947  }
1948  if ( precision > 0. )
1949  {
1950  GEOSCoordSeq_setX_r( geosinit()->ctxt, coordSeq, 0, std::round( x / precision ) * precision );
1951  GEOSCoordSeq_setY_r( geosinit()->ctxt, coordSeq, 0, std::round( y / precision ) * precision );
1952  if ( hasZ )
1953  {
1954  GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, 0, 2, std::round( z / precision ) * precision );
1955  }
1956  }
1957  else
1958  {
1959  GEOSCoordSeq_setX_r( geosinit()->ctxt, coordSeq, 0, x );
1960  GEOSCoordSeq_setY_r( geosinit()->ctxt, coordSeq, 0, y );
1961  if ( hasZ )
1962  {
1963  GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, 0, 2, z );
1964  }
1965  }
1966 #if 0 //disabled until geos supports m-coordinates
1967  if ( hasM )
1968  {
1969  GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, 0, 3, m );
1970  }
1971 #endif
1972  geosPoint.reset( GEOSGeom_createPoint_r( geosinit()->ctxt, coordSeq ) );
1973  }
1974  CATCH_GEOS( nullptr )
1975  return geosPoint;
1976 }
1977 
1978 geos::unique_ptr QgsGeos::createGeosLinestring( const QgsAbstractGeometry *curve, double precision )
1979 {
1980  const QgsCurve *c = qgsgeometry_cast<const QgsCurve *>( curve );
1981  if ( !c )
1982  return nullptr;
1983 
1984  GEOSCoordSequence *coordSeq = createCoordinateSequence( c, precision );
1985  if ( !coordSeq )
1986  return nullptr;
1987 
1988  geos::unique_ptr geosGeom;
1989  try
1990  {
1991  geosGeom.reset( GEOSGeom_createLineString_r( geosinit()->ctxt, coordSeq ) );
1992  }
1993  CATCH_GEOS( nullptr )
1994  return geosGeom;
1995 }
1996 
1997 geos::unique_ptr QgsGeos::createGeosPolygon( const QgsAbstractGeometry *poly, double precision )
1998 {
1999  const QgsCurvePolygon *polygon = qgsgeometry_cast<const QgsCurvePolygon *>( poly );
2000  if ( !polygon )
2001  return nullptr;
2002 
2003  const QgsCurve *exteriorRing = polygon->exteriorRing();
2004  if ( !exteriorRing )
2005  {
2006  return nullptr;
2007  }
2008 
2009  geos::unique_ptr geosPolygon;
2010  try
2011  {
2012  geos::unique_ptr exteriorRingGeos( GEOSGeom_createLinearRing_r( geosinit()->ctxt, createCoordinateSequence( exteriorRing, precision, true ) ) );
2013 
2014  int nHoles = polygon->numInteriorRings();
2015  GEOSGeometry **holes = nullptr;
2016  if ( nHoles > 0 )
2017  {
2018  holes = new GEOSGeometry*[ nHoles ];
2019  }
2020 
2021  for ( int i = 0; i < nHoles; ++i )
2022  {
2023  const QgsCurve *interiorRing = polygon->interiorRing( i );
2024  holes[i] = GEOSGeom_createLinearRing_r( geosinit()->ctxt, createCoordinateSequence( interiorRing, precision, true ) );
2025  }
2026  geosPolygon.reset( GEOSGeom_createPolygon_r( geosinit()->ctxt, exteriorRingGeos.release(), holes, nHoles ) );
2027  delete[] holes;
2028  }
2029  CATCH_GEOS( nullptr )
2030 
2031  return geosPolygon;
2032 }
2033 
2034 QgsAbstractGeometry *QgsGeos::offsetCurve( double distance, int segments, int joinStyle, double miterLimit, QString *errorMsg ) const
2035 {
2036  if ( !mGeos )
2037  return nullptr;
2038 
2039  geos::unique_ptr offset;
2040  try
2041  {
2042  offset.reset( GEOSOffsetCurve_r( geosinit()->ctxt, mGeos.get(), distance, segments, joinStyle, miterLimit ) );
2043  }
2044  CATCH_GEOS_WITH_ERRMSG( nullptr )
2045  std::unique_ptr< QgsAbstractGeometry > offsetGeom = fromGeos( offset.get() );
2046  return offsetGeom.release();
2047 }
2048 
2049 std::unique_ptr<QgsAbstractGeometry> QgsGeos::singleSidedBuffer( double distance, int segments, int side, int joinStyle, double miterLimit, QString *errorMsg ) const
2050 {
2051  if ( !mGeos )
2052  {
2053  return nullptr;
2054  }
2055 
2057  try
2058  {
2059  geos::buffer_params_unique_ptr bp( GEOSBufferParams_create_r( geosinit()->ctxt ) );
2060  GEOSBufferParams_setSingleSided_r( geosinit()->ctxt, bp.get(), 1 );
2061  GEOSBufferParams_setQuadrantSegments_r( geosinit()->ctxt, bp.get(), segments );
2062  GEOSBufferParams_setJoinStyle_r( geosinit()->ctxt, bp.get(), joinStyle );
2063  GEOSBufferParams_setMitreLimit_r( geosinit()->ctxt, bp.get(), miterLimit ); //#spellok
2064 
2065  if ( side == 1 )
2066  {
2067  distance = -distance;
2068  }
2069  geos.reset( GEOSBufferWithParams_r( geosinit()->ctxt, mGeos.get(), bp.get(), distance ) );
2070  }
2071  CATCH_GEOS_WITH_ERRMSG( nullptr );
2072  return fromGeos( geos.get() );
2073 }
2074 
2075 std::unique_ptr<QgsAbstractGeometry> QgsGeos::reshapeGeometry( const QgsLineString &reshapeWithLine, EngineOperationResult *errorCode, QString *errorMsg ) const
2076 {
2077  if ( !mGeos || mGeometry->dimension() == 0 )
2078  {
2079  if ( errorCode ) { *errorCode = InvalidBaseGeometry; }
2080  return nullptr;
2081  }
2082 
2083  if ( reshapeWithLine.numPoints() < 2 )
2084  {
2085  if ( errorCode ) { *errorCode = InvalidInput; }
2086  return nullptr;
2087  }
2088 
2089  geos::unique_ptr reshapeLineGeos = createGeosLinestring( &reshapeWithLine, mPrecision );
2090 
2091  //single or multi?
2092  int numGeoms = GEOSGetNumGeometries_r( geosinit()->ctxt, mGeos.get() );
2093  if ( numGeoms == -1 )
2094  {
2095  if ( errorCode )
2096  {
2097  *errorCode = InvalidBaseGeometry;
2098  }
2099  return nullptr;
2100  }
2101 
2102  bool isMultiGeom = false;
2103  int geosTypeId = GEOSGeomTypeId_r( geosinit()->ctxt, mGeos.get() );
2104  if ( geosTypeId == GEOS_MULTILINESTRING || geosTypeId == GEOS_MULTIPOLYGON )
2105  isMultiGeom = true;
2106 
2107  bool isLine = ( mGeometry->dimension() == 1 );
2108 
2109  if ( !isMultiGeom )
2110  {
2111  geos::unique_ptr reshapedGeometry;
2112  if ( isLine )
2113  {
2114  reshapedGeometry = reshapeLine( mGeos.get(), reshapeLineGeos.get(), mPrecision );
2115  }
2116  else
2117  {
2118  reshapedGeometry = reshapePolygon( mGeos.get(), reshapeLineGeos.get(), mPrecision );
2119  }
2120 
2121  if ( errorCode )
2122  *errorCode = Success;
2123  std::unique_ptr< QgsAbstractGeometry > reshapeResult = fromGeos( reshapedGeometry.get() );
2124  return reshapeResult;
2125  }
2126  else
2127  {
2128  try
2129  {
2130  //call reshape for each geometry part and replace mGeos with new geometry if reshape took place
2131  bool reshapeTookPlace = false;
2132 
2133  geos::unique_ptr currentReshapeGeometry;
2134  GEOSGeometry **newGeoms = new GEOSGeometry*[numGeoms];
2135 
2136  for ( int i = 0; i < numGeoms; ++i )
2137  {
2138  if ( isLine )
2139  currentReshapeGeometry = reshapeLine( GEOSGetGeometryN_r( geosinit()->ctxt, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
2140  else
2141  currentReshapeGeometry = reshapePolygon( GEOSGetGeometryN_r( geosinit()->ctxt, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
2142 
2143  if ( currentReshapeGeometry )
2144  {
2145  newGeoms[i] = currentReshapeGeometry.release();
2146  reshapeTookPlace = true;
2147  }
2148  else
2149  {
2150  newGeoms[i] = GEOSGeom_clone_r( geosinit()->ctxt, GEOSGetGeometryN_r( geosinit()->ctxt, mGeos.get(), i ) );
2151  }
2152  }
2153 
2154  geos::unique_ptr newMultiGeom;
2155  if ( isLine )
2156  {
2157  newMultiGeom.reset( GEOSGeom_createCollection_r( geosinit()->ctxt, GEOS_MULTILINESTRING, newGeoms, numGeoms ) );
2158  }
2159  else //multipolygon
2160  {
2161  newMultiGeom.reset( GEOSGeom_createCollection_r( geosinit()->ctxt, GEOS_MULTIPOLYGON, newGeoms, numGeoms ) );
2162  }
2163 
2164  delete[] newGeoms;
2165  if ( !newMultiGeom )
2166  {
2167  if ( errorCode ) { *errorCode = EngineError; }
2168  return nullptr;
2169  }
2170 
2171  if ( reshapeTookPlace )
2172  {
2173  if ( errorCode )
2174  *errorCode = Success;
2175  std::unique_ptr< QgsAbstractGeometry > reshapedMultiGeom = fromGeos( newMultiGeom.get() );
2176  return reshapedMultiGeom;
2177  }
2178  else
2179  {
2180  if ( errorCode )
2181  {
2182  *errorCode = NothingHappened;
2183  }
2184  return nullptr;
2185  }
2186  }
2187  CATCH_GEOS_WITH_ERRMSG( nullptr )
2188  }
2189 }
2190 
2191 QgsGeometry QgsGeos::mergeLines( QString *errorMsg ) const
2192 {
2193  if ( !mGeos )
2194  {
2195  return QgsGeometry();
2196  }
2197 
2198  if ( GEOSGeomTypeId_r( geosinit()->ctxt, mGeos.get() ) != GEOS_MULTILINESTRING )
2199  return QgsGeometry();
2200 
2202  try
2203  {
2204  geos.reset( GEOSLineMerge_r( geosinit()->ctxt, mGeos.get() ) );
2205  }
2207  return QgsGeometry( fromGeos( geos.get() ) );
2208 }
2209 
2210 QgsGeometry QgsGeos::closestPoint( const QgsGeometry &other, QString *errorMsg ) const
2211 {
2212  if ( !mGeos || isEmpty() || other.isNull() )
2213  {
2214  return QgsGeometry();
2215  }
2216 
2217  geos::unique_ptr otherGeom( asGeos( other.constGet(), mPrecision ) );
2218  if ( !otherGeom )
2219  {
2220  return QgsGeometry();
2221  }
2222 
2223  double nx = 0.0;
2224  double ny = 0.0;
2225  try
2226  {
2227  geos::coord_sequence_unique_ptr nearestCoord( GEOSNearestPoints_r( geosinit()->ctxt, mGeos.get(), otherGeom.get() ) );
2228 
2229  ( void )GEOSCoordSeq_getX_r( geosinit()->ctxt, nearestCoord.get(), 0, &nx );
2230  ( void )GEOSCoordSeq_getY_r( geosinit()->ctxt, nearestCoord.get(), 0, &ny );
2231  }
2232  catch ( GEOSException &e )
2233  {
2234  if ( errorMsg )
2235  {
2236  *errorMsg = e.what();
2237  }
2238  return QgsGeometry();
2239  }
2240 
2241  return QgsGeometry( new QgsPoint( nx, ny ) );
2242 }
2243 
2244 QgsGeometry QgsGeos::shortestLine( const QgsGeometry &other, QString *errorMsg ) const
2245 {
2246  if ( !mGeos || other.isNull() )
2247  {
2248  return QgsGeometry();
2249  }
2250 
2251  geos::unique_ptr otherGeom( asGeos( other.constGet(), mPrecision ) );
2252  if ( !otherGeom )
2253  {
2254  return QgsGeometry();
2255  }
2256 
2257  double nx1 = 0.0;
2258  double ny1 = 0.0;
2259  double nx2 = 0.0;
2260  double ny2 = 0.0;
2261  try
2262  {
2263  geos::coord_sequence_unique_ptr nearestCoord( GEOSNearestPoints_r( geosinit()->ctxt, mGeos.get(), otherGeom.get() ) );
2264 
2265  ( void )GEOSCoordSeq_getX_r( geosinit()->ctxt, nearestCoord.get(), 0, &nx1 );
2266  ( void )GEOSCoordSeq_getY_r( geosinit()->ctxt, nearestCoord.get(), 0, &ny1 );
2267  ( void )GEOSCoordSeq_getX_r( geosinit()->ctxt, nearestCoord.get(), 1, &nx2 );
2268  ( void )GEOSCoordSeq_getY_r( geosinit()->ctxt, nearestCoord.get(), 1, &ny2 );
2269  }
2270  catch ( GEOSException &e )
2271  {
2272  if ( errorMsg )
2273  {
2274  *errorMsg = e.what();
2275  }
2276  return QgsGeometry();
2277  }
2278 
2279  QgsLineString *line = new QgsLineString();
2280  line->addVertex( QgsPoint( nx1, ny1 ) );
2281  line->addVertex( QgsPoint( nx2, ny2 ) );
2282  return QgsGeometry( line );
2283 }
2284 
2285 double QgsGeos::lineLocatePoint( const QgsPoint &point, QString *errorMsg ) const
2286 {
2287  if ( !mGeos )
2288  {
2289  return -1;
2290  }
2291 
2292  geos::unique_ptr otherGeom( asGeos( &point, mPrecision ) );
2293  if ( !otherGeom )
2294  {
2295  return -1;
2296  }
2297 
2298  double distance = -1;
2299  try
2300  {
2301  distance = GEOSProject_r( geosinit()->ctxt, mGeos.get(), otherGeom.get() );
2302  }
2303  catch ( GEOSException &e )
2304  {
2305  if ( errorMsg )
2306  {
2307  *errorMsg = e.what();
2308  }
2309  return -1;
2310  }
2311 
2312  return distance;
2313 }
2314 
2315 QgsGeometry QgsGeos::polygonize( const QVector<const QgsAbstractGeometry *> &geometries, QString *errorMsg )
2316 {
2317  GEOSGeometry **const lineGeosGeometries = new GEOSGeometry*[ geometries.size()];
2318  int validLines = 0;
2319  for ( const QgsAbstractGeometry *g : geometries )
2320  {
2321  geos::unique_ptr l = asGeos( g );
2322  if ( l )
2323  {
2324  lineGeosGeometries[validLines] = l.release();
2325  validLines++;
2326  }
2327  }
2328 
2329  try
2330  {
2331  geos::unique_ptr result( GEOSPolygonize_r( geosinit()->ctxt, lineGeosGeometries, validLines ) );
2332  for ( int i = 0; i < validLines; ++i )
2333  {
2334  GEOSGeom_destroy_r( geosinit()->ctxt, lineGeosGeometries[i] );
2335  }
2336  delete[] lineGeosGeometries;
2337  return QgsGeometry( fromGeos( result.get() ) );
2338  }
2339  catch ( GEOSException &e )
2340  {
2341  if ( errorMsg )
2342  {
2343  *errorMsg = e.what();
2344  }
2345  for ( int i = 0; i < validLines; ++i )
2346  {
2347  GEOSGeom_destroy_r( geosinit()->ctxt, lineGeosGeometries[i] );
2348  }
2349  delete[] lineGeosGeometries;
2350  return QgsGeometry();
2351  }
2352 }
2353 
2354 QgsGeometry QgsGeos::voronoiDiagram( const QgsAbstractGeometry *extent, double tolerance, bool edgesOnly, QString *errorMsg ) const
2355 {
2356  if ( !mGeos )
2357  {
2358  return QgsGeometry();
2359  }
2360 
2361  geos::unique_ptr extentGeosGeom;
2362  if ( extent )
2363  {
2364  extentGeosGeom = asGeos( extent, mPrecision );
2365  if ( !extentGeosGeom )
2366  {
2367  return QgsGeometry();
2368  }
2369  }
2370 
2372  try
2373  {
2374  geos.reset( GEOSVoronoiDiagram_r( geosinit()->ctxt, mGeos.get(), extentGeosGeom.get(), tolerance, edgesOnly ) );
2375 
2376  if ( !geos || GEOSisEmpty_r( geosinit()->ctxt, geos.get() ) != 0 )
2377  {
2378  return QgsGeometry();
2379  }
2380 
2381  return QgsGeometry( fromGeos( geos.get() ) );
2382  }
2384 }
2385 
2386 QgsGeometry QgsGeos::delaunayTriangulation( double tolerance, bool edgesOnly, QString *errorMsg ) const
2387 {
2388  if ( !mGeos )
2389  {
2390  return QgsGeometry();
2391  }
2392 
2394  try
2395  {
2396  geos.reset( GEOSDelaunayTriangulation_r( geosinit()->ctxt, mGeos.get(), tolerance, edgesOnly ) );
2397 
2398  if ( !geos || GEOSisEmpty_r( geosinit()->ctxt, geos.get() ) != 0 )
2399  {
2400  return QgsGeometry();
2401  }
2402 
2403  return QgsGeometry( fromGeos( geos.get() ) );
2404  }
2406 }
2407 
2408 
2410 static bool _linestringEndpoints( const GEOSGeometry *linestring, double &x1, double &y1, double &x2, double &y2 )
2411 {
2412  const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, linestring );
2413  if ( !coordSeq )
2414  return false;
2415 
2416  unsigned int coordSeqSize;
2417  if ( GEOSCoordSeq_getSize_r( geosinit()->ctxt, coordSeq, &coordSeqSize ) == 0 )
2418  return false;
2419 
2420  if ( coordSeqSize < 2 )
2421  return false;
2422 
2423  GEOSCoordSeq_getX_r( geosinit()->ctxt, coordSeq, 0, &x1 );
2424  GEOSCoordSeq_getY_r( geosinit()->ctxt, coordSeq, 0, &y1 );
2425  GEOSCoordSeq_getX_r( geosinit()->ctxt, coordSeq, coordSeqSize - 1, &x2 );
2426  GEOSCoordSeq_getY_r( geosinit()->ctxt, coordSeq, coordSeqSize - 1, &y2 );
2427  return true;
2428 }
2429 
2430 
2432 static geos::unique_ptr _mergeLinestrings( const GEOSGeometry *line1, const GEOSGeometry *line2, const QgsPointXY &intersectionPoint )
2433 {
2434  double x1, y1, x2, y2;
2435  if ( !_linestringEndpoints( line1, x1, y1, x2, y2 ) )
2436  return nullptr;
2437 
2438  double rx1, ry1, rx2, ry2;
2439  if ( !_linestringEndpoints( line2, rx1, ry1, rx2, ry2 ) )
2440  return nullptr;
2441 
2442  bool intersectionAtOrigLineEndpoint =
2443  ( intersectionPoint.x() == x1 && intersectionPoint.y() == y1 ) !=
2444  ( intersectionPoint.x() == x2 && intersectionPoint.y() == y2 );
2445  bool intersectionAtReshapeLineEndpoint =
2446  ( intersectionPoint.x() == rx1 && intersectionPoint.y() == ry1 ) ||
2447  ( intersectionPoint.x() == rx2 && intersectionPoint.y() == ry2 );
2448 
2449  // the intersection must be at the begin/end of both lines
2450  if ( intersectionAtOrigLineEndpoint && intersectionAtReshapeLineEndpoint )
2451  {
2452  geos::unique_ptr g1( GEOSGeom_clone_r( geosinit()->ctxt, line1 ) );
2453  geos::unique_ptr g2( GEOSGeom_clone_r( geosinit()->ctxt, line2 ) );
2454  GEOSGeometry *geoms[2] = { g1.release(), g2.release() };
2455  geos::unique_ptr multiGeom( GEOSGeom_createCollection_r( geosinit()->ctxt, GEOS_MULTILINESTRING, geoms, 2 ) );
2456  geos::unique_ptr res( GEOSLineMerge_r( geosinit()->ctxt, multiGeom.get() ) );
2457  return res;
2458  }
2459  else
2460  return nullptr;
2461 }
2462 
2463 
2464 geos::unique_ptr QgsGeos::reshapeLine( const GEOSGeometry *line, const GEOSGeometry *reshapeLineGeos, double precision )
2465 {
2466  if ( !line || !reshapeLineGeos )
2467  return nullptr;
2468 
2469  bool atLeastTwoIntersections = false;
2470  bool oneIntersection = false;
2471  QgsPointXY oneIntersectionPoint;
2472 
2473  try
2474  {
2475  //make sure there are at least two intersection between line and reshape geometry
2476  geos::unique_ptr intersectGeom( GEOSIntersection_r( geosinit()->ctxt, line, reshapeLineGeos ) );
2477  if ( intersectGeom )
2478  {
2479  atLeastTwoIntersections = ( GEOSGeomTypeId_r( geosinit()->ctxt, intersectGeom.get() ) == GEOS_MULTIPOINT
2480  && GEOSGetNumGeometries_r( geosinit()->ctxt, intersectGeom.get() ) > 1 );
2481  // one point is enough when extending line at its endpoint
2482  if ( GEOSGeomTypeId_r( geosinit()->ctxt, intersectGeom.get() ) == GEOS_POINT )
2483  {
2484  const GEOSCoordSequence *intersectionCoordSeq = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, intersectGeom.get() );
2485  double xi, yi;
2486  GEOSCoordSeq_getX_r( geosinit()->ctxt, intersectionCoordSeq, 0, &xi );
2487  GEOSCoordSeq_getY_r( geosinit()->ctxt, intersectionCoordSeq, 0, &yi );
2488  oneIntersection = true;
2489  oneIntersectionPoint = QgsPointXY( xi, yi );
2490  }
2491  }
2492  }
2493  catch ( GEOSException & )
2494  {
2495  atLeastTwoIntersections = false;
2496  }
2497 
2498  // special case when extending line at its endpoint
2499  if ( oneIntersection )
2500  return _mergeLinestrings( line, reshapeLineGeos, oneIntersectionPoint );
2501 
2502  if ( !atLeastTwoIntersections )
2503  return nullptr;
2504 
2505  //begin and end point of original line
2506  double x1, y1, x2, y2;
2507  if ( !_linestringEndpoints( line, x1, y1, x2, y2 ) )
2508  return nullptr;
2509 
2510  geos::unique_ptr beginLineVertex = createGeosPointXY( x1, y1, false, 0, false, 0, 2, precision );
2511  geos::unique_ptr endLineVertex = createGeosPointXY( x2, y2, false, 0, false, 0, 2, precision );
2512 
2513  bool isRing = false;
2514  if ( GEOSGeomTypeId_r( geosinit()->ctxt, line ) == GEOS_LINEARRING
2515  || GEOSEquals_r( geosinit()->ctxt, beginLineVertex.get(), endLineVertex.get() ) == 1 )
2516  isRing = true;
2517 
2518  //node line and reshape line
2519  geos::unique_ptr nodedGeometry = nodeGeometries( reshapeLineGeos, line );
2520  if ( !nodedGeometry )
2521  {
2522  return nullptr;
2523  }
2524 
2525  //and merge them together
2526  geos::unique_ptr mergedLines( GEOSLineMerge_r( geosinit()->ctxt, nodedGeometry.get() ) );
2527  if ( !mergedLines )
2528  {
2529  return nullptr;
2530  }
2531 
2532  int numMergedLines = GEOSGetNumGeometries_r( geosinit()->ctxt, mergedLines.get() );
2533  if ( numMergedLines < 2 ) //some special cases. Normally it is >2
2534  {
2535  if ( numMergedLines == 1 ) //reshape line is from begin to endpoint. So we keep the reshapeline
2536  {
2537  geos::unique_ptr result( GEOSGeom_clone_r( geosinit()->ctxt, reshapeLineGeos ) );
2538  return result;
2539  }
2540  else
2541  return nullptr;
2542  }
2543 
2544  QVector<GEOSGeometry *> resultLineParts; //collection with the line segments that will be contained in result
2545  QVector<GEOSGeometry *> probableParts; //parts where we can decide on inclusion only after going through all the candidates
2546 
2547  for ( int i = 0; i < numMergedLines; ++i )
2548  {
2549  const GEOSGeometry *currentGeom = nullptr;
2550 
2551  currentGeom = GEOSGetGeometryN_r( geosinit()->ctxt, mergedLines.get(), i );
2552  const GEOSCoordSequence *currentCoordSeq = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, currentGeom );
2553  unsigned int currentCoordSeqSize;
2554  GEOSCoordSeq_getSize_r( geosinit()->ctxt, currentCoordSeq, &currentCoordSeqSize );
2555  if ( currentCoordSeqSize < 2 )
2556  continue;
2557 
2558  //get the two endpoints of the current line merge result
2559  double xBegin, xEnd, yBegin, yEnd;
2560  GEOSCoordSeq_getX_r( geosinit()->ctxt, currentCoordSeq, 0, &xBegin );
2561  GEOSCoordSeq_getY_r( geosinit()->ctxt, currentCoordSeq, 0, &yBegin );
2562  GEOSCoordSeq_getX_r( geosinit()->ctxt, currentCoordSeq, currentCoordSeqSize - 1, &xEnd );
2563  GEOSCoordSeq_getY_r( geosinit()->ctxt, currentCoordSeq, currentCoordSeqSize - 1, &yEnd );
2564  geos::unique_ptr beginCurrentGeomVertex = createGeosPointXY( xBegin, yBegin, false, 0, false, 0, 2, precision );
2565  geos::unique_ptr endCurrentGeomVertex = createGeosPointXY( xEnd, yEnd, false, 0, false, 0, 2, precision );
2566 
2567  //check how many endpoints of the line merge result are on the (original) line
2568  int nEndpointsOnOriginalLine = 0;
2569  if ( pointContainedInLine( beginCurrentGeomVertex.get(), line ) == 1 )
2570  nEndpointsOnOriginalLine += 1;
2571 
2572  if ( pointContainedInLine( endCurrentGeomVertex.get(), line ) == 1 )
2573  nEndpointsOnOriginalLine += 1;
2574 
2575  //check how many endpoints equal the endpoints of the original line
2576  int nEndpointsSameAsOriginalLine = 0;
2577  if ( GEOSEquals_r( geosinit()->ctxt, beginCurrentGeomVertex.get(), beginLineVertex.get() ) == 1
2578  || GEOSEquals_r( geosinit()->ctxt, beginCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
2579  nEndpointsSameAsOriginalLine += 1;
2580 
2581  if ( GEOSEquals_r( geosinit()->ctxt, endCurrentGeomVertex.get(), beginLineVertex.get() ) == 1
2582  || GEOSEquals_r( geosinit()->ctxt, endCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
2583  nEndpointsSameAsOriginalLine += 1;
2584 
2585  //check if the current geometry overlaps the original geometry (GEOSOverlap does not seem to work with linestrings)
2586  bool currentGeomOverlapsOriginalGeom = false;
2587  bool currentGeomOverlapsReshapeLine = false;
2588  if ( lineContainedInLine( currentGeom, line ) == 1 )
2589  currentGeomOverlapsOriginalGeom = true;
2590 
2591  if ( lineContainedInLine( currentGeom, reshapeLineGeos ) == 1 )
2592  currentGeomOverlapsReshapeLine = true;
2593 
2594  //logic to decide if this part belongs to the result
2595  if ( !isRing && nEndpointsSameAsOriginalLine == 1 && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
2596  {
2597  resultLineParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
2598  }
2599  //for closed rings, we take one segment from the candidate list
2600  else if ( isRing && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
2601  {
2602  probableParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
2603  }
2604  else if ( nEndpointsOnOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
2605  {
2606  resultLineParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
2607  }
2608  else if ( nEndpointsSameAsOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
2609  {
2610  resultLineParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
2611  }
2612  else if ( currentGeomOverlapsOriginalGeom && currentGeomOverlapsReshapeLine )
2613  {
2614  resultLineParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
2615  }
2616  }
2617 
2618  //add the longest segment from the probable list for rings (only used for polygon rings)
2619  if ( isRing && !probableParts.isEmpty() )
2620  {
2621  geos::unique_ptr maxGeom; //the longest geometry in the probabla list
2622  GEOSGeometry *currentGeom = nullptr;
2623  double maxLength = -std::numeric_limits<double>::max();
2624  double currentLength = 0;
2625  for ( int i = 0; i < probableParts.size(); ++i )
2626  {
2627  currentGeom = probableParts.at( i );
2628  GEOSLength_r( geosinit()->ctxt, currentGeom, &currentLength );
2629  if ( currentLength > maxLength )
2630  {
2631  maxLength = currentLength;
2632  maxGeom.reset( currentGeom );
2633  }
2634  else
2635  {
2636  GEOSGeom_destroy_r( geosinit()->ctxt, currentGeom );
2637  }
2638  }
2639  resultLineParts.push_back( maxGeom.release() );
2640  }
2641 
2642  geos::unique_ptr result;
2643  if ( resultLineParts.empty() )
2644  return nullptr;
2645 
2646  if ( resultLineParts.size() == 1 ) //the whole result was reshaped
2647  {
2648  result.reset( resultLineParts[0] );
2649  }
2650  else //>1
2651  {
2652  GEOSGeometry **lineArray = new GEOSGeometry*[resultLineParts.size()];
2653  for ( int i = 0; i < resultLineParts.size(); ++i )
2654  {
2655  lineArray[i] = resultLineParts[i];
2656  }
2657 
2658  //create multiline from resultLineParts
2659  geos::unique_ptr multiLineGeom( GEOSGeom_createCollection_r( geosinit()->ctxt, GEOS_MULTILINESTRING, lineArray, resultLineParts.size() ) );
2660  delete [] lineArray;
2661 
2662  //then do a linemerge with the newly combined partstrings
2663  result.reset( GEOSLineMerge_r( geosinit()->ctxt, multiLineGeom.get() ) );
2664  }
2665 
2666  //now test if the result is a linestring. Otherwise something went wrong
2667  if ( GEOSGeomTypeId_r( geosinit()->ctxt, result.get() ) != GEOS_LINESTRING )
2668  {
2669  return nullptr;
2670  }
2671 
2672  return result;
2673 }
2674 
2675 geos::unique_ptr QgsGeos::reshapePolygon( const GEOSGeometry *polygon, const GEOSGeometry *reshapeLineGeos, double precision )
2676 {
2677  //go through outer shell and all inner rings and check if there is exactly one intersection of a ring and the reshape line
2678  int nIntersections = 0;
2679  int lastIntersectingRing = -2;
2680  const GEOSGeometry *lastIntersectingGeom = nullptr;
2681 
2682  int nRings = GEOSGetNumInteriorRings_r( geosinit()->ctxt, polygon );
2683  if ( nRings < 0 )
2684  return nullptr;
2685 
2686  //does outer ring intersect?
2687  const GEOSGeometry *outerRing = GEOSGetExteriorRing_r( geosinit()->ctxt, polygon );
2688  if ( GEOSIntersects_r( geosinit()->ctxt, outerRing, reshapeLineGeos ) == 1 )
2689  {
2690  ++nIntersections;
2691  lastIntersectingRing = -1;
2692  lastIntersectingGeom = outerRing;
2693  }
2694 
2695  //do inner rings intersect?
2696  const GEOSGeometry **innerRings = new const GEOSGeometry*[nRings];
2697 
2698  try
2699  {
2700  for ( int i = 0; i < nRings; ++i )
2701  {
2702  innerRings[i] = GEOSGetInteriorRingN_r( geosinit()->ctxt, polygon, i );
2703  if ( GEOSIntersects_r( geosinit()->ctxt, innerRings[i], reshapeLineGeos ) == 1 )
2704  {
2705  ++nIntersections;
2706  lastIntersectingRing = i;
2707  lastIntersectingGeom = innerRings[i];
2708  }
2709  }
2710  }
2711  catch ( GEOSException & )
2712  {
2713  nIntersections = 0;
2714  }
2715 
2716  if ( nIntersections != 1 ) //reshape line is only allowed to intersect one ring
2717  {
2718  delete [] innerRings;
2719  return nullptr;
2720  }
2721 
2722  //we have one intersecting ring, let's try to reshape it
2723  geos::unique_ptr reshapeResult = reshapeLine( lastIntersectingGeom, reshapeLineGeos, precision );
2724  if ( !reshapeResult )
2725  {
2726  delete [] innerRings;
2727  return nullptr;
2728  }
2729 
2730  //if reshaping took place, we need to reassemble the polygon and its rings
2731  GEOSGeometry *newRing = nullptr;
2732  const GEOSCoordSequence *reshapeSequence = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, reshapeResult.get() );
2733  GEOSCoordSequence *newCoordSequence = GEOSCoordSeq_clone_r( geosinit()->ctxt, reshapeSequence );
2734 
2735  reshapeResult.reset();
2736 
2737  newRing = GEOSGeom_createLinearRing_r( geosinit()->ctxt, newCoordSequence );
2738  if ( !newRing )
2739  {
2740  delete [] innerRings;
2741  return nullptr;
2742  }
2743 
2744  GEOSGeometry *newOuterRing = nullptr;
2745  if ( lastIntersectingRing == -1 )
2746  newOuterRing = newRing;
2747  else
2748  newOuterRing = GEOSGeom_clone_r( geosinit()->ctxt, outerRing );
2749 
2750  //check if all the rings are still inside the outer boundary
2751  QVector<GEOSGeometry *> ringList;
2752  if ( nRings > 0 )
2753  {
2754  GEOSGeometry *outerRingPoly = GEOSGeom_createPolygon_r( geosinit()->ctxt, GEOSGeom_clone_r( geosinit()->ctxt, newOuterRing ), nullptr, 0 );
2755  if ( outerRingPoly )
2756  {
2757  GEOSGeometry *currentRing = nullptr;
2758  for ( int i = 0; i < nRings; ++i )
2759  {
2760  if ( lastIntersectingRing == i )
2761  currentRing = newRing;
2762  else
2763  currentRing = GEOSGeom_clone_r( geosinit()->ctxt, innerRings[i] );
2764 
2765  //possibly a ring is no longer contained in the result polygon after reshape
2766  if ( GEOSContains_r( geosinit()->ctxt, outerRingPoly, currentRing ) == 1 )
2767  ringList.push_back( currentRing );
2768  else
2769  GEOSGeom_destroy_r( geosinit()->ctxt, currentRing );
2770  }
2771  }
2772  GEOSGeom_destroy_r( geosinit()->ctxt, outerRingPoly );
2773  }
2774 
2775  GEOSGeometry **newInnerRings = new GEOSGeometry*[ringList.size()];
2776  for ( int i = 0; i < ringList.size(); ++i )
2777  newInnerRings[i] = ringList.at( i );
2778 
2779  delete [] innerRings;
2780 
2781  geos::unique_ptr reshapedPolygon( GEOSGeom_createPolygon_r( geosinit()->ctxt, newOuterRing, newInnerRings, ringList.size() ) );
2782  delete[] newInnerRings;
2783 
2784  return reshapedPolygon;
2785 }
2786 
2787 int QgsGeos::lineContainedInLine( const GEOSGeometry *line1, const GEOSGeometry *line2 )
2788 {
2789  if ( !line1 || !line2 )
2790  {
2791  return -1;
2792  }
2793 
2794  double bufferDistance = std::pow( 10.0L, geomDigits( line2 ) - 11 );
2795 
2796  geos::unique_ptr bufferGeom( GEOSBuffer_r( geosinit()->ctxt, line2, bufferDistance, DEFAULT_QUADRANT_SEGMENTS ) );
2797  if ( !bufferGeom )
2798  return -2;
2799 
2800  geos::unique_ptr intersectionGeom( GEOSIntersection_r( geosinit()->ctxt, bufferGeom.get(), line1 ) );
2801 
2802  //compare ratio between line1Length and intersectGeomLength (usually close to 1 if line1 is contained in line2)
2803  double intersectGeomLength;
2804  double line1Length;
2805 
2806  GEOSLength_r( geosinit()->ctxt, intersectionGeom.get(), &intersectGeomLength );
2807  GEOSLength_r( geosinit()->ctxt, line1, &line1Length );
2808 
2809  double intersectRatio = line1Length / intersectGeomLength;
2810  if ( intersectRatio > 0.9 && intersectRatio < 1.1 )
2811  return 1;
2812 
2813  return 0;
2814 }
2815 
2816 int QgsGeos::pointContainedInLine( const GEOSGeometry *point, const GEOSGeometry *line )
2817 {
2818  if ( !point || !line )
2819  return -1;
2820 
2821  double bufferDistance = std::pow( 10.0L, geomDigits( line ) - 11 );
2822 
2823  geos::unique_ptr lineBuffer( GEOSBuffer_r( geosinit()->ctxt, line, bufferDistance, 8 ) );
2824  if ( !lineBuffer )
2825  return -2;
2826 
2827  bool contained = false;
2828  if ( GEOSContains_r( geosinit()->ctxt, lineBuffer.get(), point ) == 1 )
2829  contained = true;
2830 
2831  return contained;
2832 }
2833 
2834 int QgsGeos::geomDigits( const GEOSGeometry *geom )
2835 {
2836  geos::unique_ptr bbox( GEOSEnvelope_r( geosinit()->ctxt, geom ) );
2837  if ( !bbox.get() )
2838  return -1;
2839 
2840  const GEOSGeometry *bBoxRing = GEOSGetExteriorRing_r( geosinit()->ctxt, bbox.get() );
2841  if ( !bBoxRing )
2842  return -1;
2843 
2844  const GEOSCoordSequence *bBoxCoordSeq = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, bBoxRing );
2845 
2846  if ( !bBoxCoordSeq )
2847  return -1;
2848 
2849  unsigned int nCoords = 0;
2850  if ( !GEOSCoordSeq_getSize_r( geosinit()->ctxt, bBoxCoordSeq, &nCoords ) )
2851  return -1;
2852 
2853  int maxDigits = -1;
2854  for ( unsigned int i = 0; i < nCoords - 1; ++i )
2855  {
2856  double t;
2857  GEOSCoordSeq_getX_r( geosinit()->ctxt, bBoxCoordSeq, i, &t );
2858 
2859  int digits;
2860  digits = std::ceil( std::log10( std::fabs( t ) ) );
2861  if ( digits > maxDigits )
2862  maxDigits = digits;
2863 
2864  GEOSCoordSeq_getY_r( geosinit()->ctxt, bBoxCoordSeq, i, &t );
2865  digits = std::ceil( std::log10( std::fabs( t ) ) );
2866  if ( digits > maxDigits )
2867  maxDigits = digits;
2868  }
2869 
2870  return maxDigits;
2871 }
2872 
2873 GEOSContextHandle_t QgsGeos::getGEOSHandler()
2874 {
2875  return geosinit()->ctxt;
2876 }
QgsCurve
Abstract base class for curved geometry type.
Definition: qgscurve.h:35
qgspolygon.h
QgsGeometry::AddPartNotMultiGeometry
@ AddPartNotMultiGeometry
The source geometry is not multi.
Definition: qgsgeometry.h:146
QgsGeos::hausdorffDistanceDensify
double hausdorffDistanceDensify(const QgsAbstractGeometry *geom, double densifyFraction, QString *errorMsg=nullptr) const
Returns the Hausdorff distance between this geometry and geom.
Definition: qgsgeos.cpp:465
QgsGeos::distance
double distance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Calculates the distance between this and geom.
Definition: qgsgeos.cpp:419
QgsGeometryEngine::mGeometry
const QgsAbstractGeometry * mGeometry
Definition: qgsgeometryengine.h:274
QgsGeos::convexHull
QgsAbstractGeometry * convexHull(QString *errorMsg=nullptr) const override
Calculate the convex hull of this.
Definition: qgsgeos.cpp:1664
QgsLineString::pointN
QgsPoint pointN(int i) const
Returns the specified point from inside the line string.
Definition: qgslinestring.cpp:660
QgsLineString::xAt
double xAt(int index) const override
Returns the x-coordinate of the specified node in the line string.
Definition: qgslinestring.cpp:709
CATCH_GEOS_WITH_ERRMSG
#define CATCH_GEOS_WITH_ERRMSG(r)
Definition: qgsgeos.cpp:39
QgsPointXY::y
double y
Definition: qgspointxy.h:48
QgsMultiCurve::addGeometry
bool addGeometry(QgsAbstractGeometry *g) override
Adds a geometry and takes ownership. Returns true in case of success.
Definition: qgsmulticurve.cpp:134
QgsLineString::zData
const double * zData() const
Returns a const pointer to the z vertex data, or nullptr if the linestring does not have z values.
Definition: qgslinestring.h:241
QgsAbstractGeometry::wkbType
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
Definition: qgsabstractgeometry.h:189
QgsGeometryEngine::InvalidInput
@ InvalidInput
The input is not valid.
Definition: qgsgeometryengine.h:49
QgsGeos::isEmpty
bool isEmpty(QString *errorMsg=nullptr) const override
Definition: qgsgeos.cpp:1758
QgsWkbTypes::Point
@ Point
Definition: qgswkbtypes.h:71
geos::buffer_params_unique_ptr
std::unique_ptr< GEOSBufferParams, GeosDeleter > buffer_params_unique_ptr
Scoped GEOS buffer params pointer.
Definition: qgsgeos.h:89
QgsGeos::crosses
bool crosses(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom crosses this.
Definition: qgsgeos.cpp:498
QgsPolygon
Polygon geometry type.
Definition: qgspolygon.h:33
QgsLineString::mAt
double mAt(int index) const
Returns the m value of the specified node in the line string.
Definition: qgslinestring.h:320
qgslinestring.h
QgsWkbTypes::NullGeometry
@ NullGeometry
Definition: qgswkbtypes.h:145
QgsGeos::clip
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
QgsPoint
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
QgsGeos::shortestLine
QgsGeometry shortestLine(const QgsGeometry &other, QString *errorMsg=nullptr) const
Returns the shortest line joining this geometry to the other geometry.
Definition: qgsgeos.cpp:2244
QgsRectangle::setXMinimum
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:130
qgsgeometryfactory.h
QgsLineString::numPoints
int numPoints() const override
Returns the number of points in the curve.
Definition: qgslinestring.cpp:650
QgsGeos::geometryFromGeos
static QgsGeometry geometryFromGeos(GEOSGeometry *geos)
Creates a new QgsGeometry object, feeding in a geometry in GEOS format.
Definition: qgsgeos.cpp:153
QgsLineString::yAt
double yAt(int index) const override
Returns the y-coordinate of the specified node in the line string.
Definition: qgslinestring.cpp:717
QgsGeos::disjoint
bool disjoint(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom is disjoint from this.
Definition: qgsgeos.cpp:518
QgsCurvePolygon
Curve polygon geometry type.
Definition: qgscurvepolygon.h:34
QgsRectangle::xMaximum
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
QgsWkbTypes::isMultiType
static bool isMultiType(Type type)
Returns true if the WKB type is a multi type.
Definition: qgswkbtypes.h:831
geos
Contains geos related utilities and functions.
Definition: qgsgeos.h:41
QgsGeometryEngine::EngineError
@ EngineError
Error occurred in the geometry engine.
Definition: qgsgeometryengine.h:46
QgsMultiLineString
Multi line string geometry collection.
Definition: qgsmultilinestring.h:29
qgsmultipoint.h
QgsCurvePolygon::exteriorRing
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
Definition: qgscurvepolygon.h:86
QgsPoint::z
double z
Definition: qgspoint.h:60
QgsGeos::area
double area(QString *errorMsg=nullptr) const override
Definition: qgsgeos.cpp:586
QgsLineString::yData
const double * yData() const
Returns a const pointer to the y vertex data.
Definition: qgslinestring.h:228
QgsWkbTypes::Type
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:68
DEFAULT_QUADRANT_SEGMENTS
#define DEFAULT_QUADRANT_SEGMENTS
Definition: qgsgeos.cpp:31
QgsGeometry::OperationResult
OperationResult
Success or failure of a geometry operation.
Definition: qgsgeometry.h:134
QgsGeos::buffer
QgsAbstractGeometry * buffer(double distance, int segments, QString *errorMsg=nullptr) const override
Definition: qgsgeos.cpp:1533
QgsGeos::envelope
QgsAbstractGeometry * envelope(QString *errorMsg=nullptr) const override
Definition: qgsgeos.cpp:1621
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsLineString
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:43
QgsGeos::splitGeometry
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
QgsGeos::relate
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
Q_GLOBAL_STATIC
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
QgsRectangle
Definition: qgsrectangle.h:41
QgsWkbTypes::PolygonGeometry
@ PolygonGeometry
Definition: qgswkbtypes.h:143
QgsCurvePolygon::interiorRing
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
Definition: qgscurvepolygon.h:99
QgsGeos::hausdorffDistance
double hausdorffDistance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const
Returns the Hausdorff distance between this geometry and geom.
Definition: qgsgeos.cpp:442
QgsLineString::xData
const double * xData() const
Returns a const pointer to the x vertex data.
Definition: qgslinestring.h:217
QgsGeos::fromGeosPolygon
static std::unique_ptr< QgsPolygon > fromGeosPolygon(const GEOSGeometry *geos)
Definition: qgsgeos.cpp:1174
geos::unique_ptr
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
Definition: qgsgeos.h:79
QgsGeos::addPart
static QgsGeometry::OperationResult addPart(QgsGeometry &geometry, GEOSGeometry *newPart)
Adds a new island polygon to a multipolygon feature.
Definition: qgsgeos.cpp:176
QgsWkbTypes::PointM
@ PointM
Definition: qgswkbtypes.h:98
QgsGeos::voronoiDiagram
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:2354
QgsPoint::y
double y
Definition: qgspoint.h:59
precision
int precision
Definition: qgswfsgetfeature.cpp:103
QgsGeometryCollection
Geometry collection.
Definition: qgsgeometrycollection.h:35
QgsAbstractGeometry::dimension
virtual int dimension() const =0
Returns the inherent dimension of the geometry.
QgsGeos::asGeos
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
QgsWkbTypes::PointZM
@ PointZM
Definition: qgswkbtypes.h:111
QgsLineString::clone
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
Definition: qgslinestring.cpp:293
QgsGeometryEngine::NodedGeometryError
@ NodedGeometryError
Error occurred while creating a noded geometry.
Definition: qgsgeometryengine.h:47
QgsGeometryEngine::InvalidBaseGeometry
@ InvalidBaseGeometry
The geometry on which the operation occurs is not valid.
Definition: qgsgeometryengine.h:48
QgsGeometryFactory::createCollectionOfType
static std::unique_ptr< QgsGeometryCollection > createCollectionOfType(QgsWkbTypes::Type type)
Returns a new geometry collection matching a specified WKB type.
Definition: qgsgeometryfactory.cpp:264
qgsmultipolygon.h
QgsGeos::closestPoint
QgsGeometry closestPoint(const QgsGeometry &other, QString *errorMsg=nullptr) const
Returns the closest point on the geometry to the other geometry.
Definition: qgsgeos.cpp:2210
QgsMultiCurve
Multi curve geometry collection.
Definition: qgsmulticurve.h:29
QgsGeos::interpolate
QgsAbstractGeometry * interpolate(double distance, QString *errorMsg=nullptr) const override
Definition: qgsgeos.cpp:1580
QgsGeos::isSimple
bool isSimple(QString *errorMsg=nullptr) const override
Determines whether the geometry is simple (according to OGC definition).
Definition: qgsgeos.cpp:1772
qgsDoubleNear
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:315
QgsGeos::singleSidedBuffer
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:2049
QgsGeos::delaunayTriangulation
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:2386
QgsWkbTypes::GeometryCollection
@ GeometryCollection
Definition: qgswkbtypes.h:78
QgsGeometryCollection::addGeometry
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
Definition: qgsgeometrycollection.cpp:226
QgsAbstractGeometry::clone
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
QgsGeos
Definition: qgsgeos.h:103
QgsGeos::polygonize
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:2315
QgsGeos::within
bool within(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom is within this.
Definition: qgsgeos.cpp:503
QgsGeos::overlaps
bool overlaps(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom overlaps this.
Definition: qgsgeos.cpp:508
QgsGeos::combine
QgsAbstractGeometry * combine(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Calculate the combination of this and geom.
Definition: qgsgeos.cpp:361
QgsWkbTypes::PointZ
@ PointZ
Definition: qgswkbtypes.h:85
QgsWkbTypes::geometryType
static GeometryType geometryType(Type type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:937
QgsRectangle::setXMaximum
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:135
QgsPoint::m
double m
Definition: qgspoint.h:61
QgsGeos::offsetCurve
QgsAbstractGeometry * offsetCurve(double distance, int segments, int joinStyle, double miterLimit, QString *errorMsg=nullptr) const override
Definition: qgsgeos.cpp:2034
QgsAbstractGeometry::is3D
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
Definition: qgsabstractgeometry.h:202
QgsGeos::centroid
QgsPoint * centroid(QString *errorMsg=nullptr) const override
Calculates the centroid of this.
Definition: qgsgeos.cpp:1595
QgsMultiPolygon
Multi polygon geometry collection.
Definition: qgsmultipolygon.h:29
QgsGeometry::isNull
bool isNull
Definition: qgsgeometry.h:125
QgsGeometry::constGet
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
Definition: qgsgeometry.cpp:128
CATCH_GEOS
#define CATCH_GEOS(r)
Definition: qgsgeos.cpp:33
QgsGeos::QgsGeos
QgsGeos(const QgsAbstractGeometry *geometry, double precision=0)
GEOS geometry engine constructor.
Definition: qgsgeos.cpp:145
QgsMultiPoint
Multi point geometry collection.
Definition: qgsmultipoint.h:29
QgsLineString::mData
const double * mData() const
Returns a const pointer to the m vertex data, or nullptr if the linestring does not have m values.
Definition: qgslinestring.h:257
QgsAbstractGeometry
Abstract base class for all geometries.
Definition: qgsabstractgeometry.h:71
QgsRectangle::yMaximum
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
QgsGeos::getGEOSHandler
static GEOSContextHandle_t getGEOSHandler()
Definition: qgsgeos.cpp:2873
QgsGeos::relatePattern
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
QgsPointXY
Definition: qgspointxy.h:43
QgsGeos::pointOnSurface
QgsPoint * pointOnSurface(QString *errorMsg=nullptr) const override
Calculate a point that is guaranteed to be on the surface of this.
Definition: qgsgeos.cpp:1636
QgsAbstractGeometry::boundingBox
virtual QgsRectangle boundingBox() const =0
Returns the minimal bounding box for the geometry.
QgsGeometryEngine::NothingHappened
@ NothingHappened
Nothing happened, without any error.
Definition: qgsgeometryengine.h:44
QgsGeometry::get
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
Definition: qgsgeometry.cpp:133
QgsWkbTypes::LineGeometry
@ LineGeometry
Definition: qgswkbtypes.h:142
QgsCurvePolygon::numInteriorRings
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
Definition: qgscurvepolygon.h:76
QgsStringMap
QMap< QString, QString > QgsStringMap
Definition: qgis.h:714
QgsWkbTypes::PointGeometry
@ PointGeometry
Definition: qgswkbtypes.h:141
QgsGeos::geometryChanged
void geometryChanged() override
Should be called whenever the geometry associated with the engine has been modified and the engine mu...
Definition: qgsgeos.cpp:191
QgsGeos::symDifference
QgsAbstractGeometry * symDifference(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Calculate the symmetric difference of this and geom.
Definition: qgsgeos.cpp:414
QgsGeometryEngine::SplitCannotSplitPoint
@ SplitCannotSplitPoint
Points cannot be split.
Definition: qgsgeometryengine.h:51
QgsGeos::isEqual
bool isEqual(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if this is equal to geom.
Definition: qgsgeos.cpp:1738
QgsGeos::reshapeGeometry
std::unique_ptr< QgsAbstractGeometry > reshapeGeometry(const QgsLineString &reshapeWithLine, EngineOperationResult *errorCode, QString *errorMsg=nullptr) const
Reshapes the geometry using a line.
Definition: qgsgeos.cpp:2075
QgsPointSequence
QVector< QgsPoint > QgsPointSequence
Definition: qgsabstractgeometry.h:44
QgsGeos::length
double length(QString *errorMsg=nullptr) const override
Definition: qgsgeos.cpp:603
QgsGeos::intersection
QgsAbstractGeometry * intersection(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Calculate the intersection of this and geom.
Definition: qgsgeos.cpp:217
c
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
Definition: porting_processing.dox:1
QgsGeometry
Definition: qgsgeometry.h:122
QgsGeos::touches
bool touches(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom touches this.
Definition: qgsgeos.cpp:493
QgsGeometry::InvalidBaseGeometry
@ InvalidBaseGeometry
The base geometry on which the operation is done is invalid or empty.
Definition: qgsgeometry.h:138
QgsLineString::addVertex
void addVertex(const QgsPoint &pt)
Adds a new vertex to the end of the line string.
Definition: qgslinestring.cpp:1290
QgsGeometryEditUtils::addPart
static QgsGeometry::OperationResult addPart(QgsAbstractGeometry *geometry, std::unique_ptr< QgsAbstractGeometry > part)
Add a part to multi type geometry.
Definition: qgsgeometryeditutils.cpp:98
qgsgeometryeditutils.h
QgsPointXY::x
double x
Definition: qgspointxy.h:47
QgsGeometryEngine::Success
@ Success
Operation succeeded.
Definition: qgsgeometryengine.h:43
QgsRectangle::height
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:209
QgsGeos::mergeLines
QgsGeometry mergeLines(QString *errorMsg=nullptr) const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
Definition: qgsgeos.cpp:2191
QgsGeos::lineLocatePoint
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:2285
QgsWkbTypes::UnknownGeometry
@ UnknownGeometry
Definition: qgswkbtypes.h:144
QgsRectangle::yMinimum
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
QgsAbstractGeometry::isMeasure
bool isMeasure() const
Returns true if the geometry contains m values.
Definition: qgsabstractgeometry.h:211
QgsGeos::contains
bool contains(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom contains this.
Definition: qgsgeos.cpp:513
geos::coord_sequence_unique_ptr
std::unique_ptr< GEOSCoordSequence, GeosDeleter > coord_sequence_unique_ptr
Scoped GEOS coordinate sequence pointer.
Definition: qgsgeos.h:94
QgsGeometryEngine::EngineOperationResult
EngineOperationResult
Success or failure of a geometry operation.
Definition: qgsgeometryengine.h:41
QgsGeos::subdivide
std::unique_ptr< QgsAbstractGeometry > subdivide(int maxNodes, QString *errorMsg=nullptr) const
Subdivides the geometry.
Definition: qgsgeos.cpp:341
qgsgeometrycollection.h
qgsmulticurve.h
QgsGeos::simplify
QgsAbstractGeometry * simplify(double tolerance, QString *errorMsg=nullptr) const override
Definition: qgsgeos.cpp:1565
geos::GeosDeleter::operator()
void CORE_EXPORT operator()(GEOSGeometry *geom)
Destroys the GEOS geometry geom, using the static QGIS geos context.
qgslogger.h
QgsGeos::fromGeos
static std::unique_ptr< QgsAbstractGeometry > fromGeos(const GEOSGeometry *geos)
Create a geometry from a GEOSGeometry.
Definition: qgsgeos.cpp:1077
QgsGeos::intersects
bool intersects(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom intersects this.
Definition: qgsgeos.cpp:488
QgsGeos::isValid
bool isValid(QString *errorMsg=nullptr, bool allowSelfTouchingHoles=false, QgsGeometry *errorLoc=nullptr) const override
Returns true if the geometry is valid.
Definition: qgsgeos.cpp:1680
QgsGeometryEngine
Contains geometry relation and modification algorithms.
Definition: qgsgeometryengine.h:33
QgsRectangle::isEmpty
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:437
QgsGeos::difference
QgsAbstractGeometry * difference(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Calculate the difference of this and geom.
Definition: qgsgeos.cpp:222
QgsCurve::curveToLine
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.
qgsabstractgeometry.h
QgsPoint::x
double x
Definition: qgspoint.h:58
QgsRectangle::isNull
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:447
QgsRectangle::width
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:202
QgsRectangle::setYMinimum
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:140
QgsGeos::prepareGeometry
void prepareGeometry() override
Prepares the geometry, so that subsequent calls to spatial relation methods are much faster.
Definition: qgsgeos.cpp:198
QgsRectangle::setYMaximum
void setYMaximum(double y)
Set the maximum y value.
Definition: qgsrectangle.h:145
QgsRectangle::xMinimum
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
QgsWkbTypes::flatType
static Type flatType(Type type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:701
qgsmultilinestring.h
qgsgeos.h
QgsGeos::coordSeqPoint
static QgsPoint coordSeqPoint(const GEOSCoordSequence *cs, int i, bool hasZ, bool hasM)
Definition: qgsgeos.cpp:1265