QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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  QString message = QString::fromUtf8( buffer );
61 
62 #ifdef _MSC_VER
63  // stupid stupid MSVC, *SOMETIMES* raises it's own exception if we throw GEOSException, resulting in a crash!
64  // see https://github.com/qgis/QGIS/issues/22709
65  // if you want to test alternative fixes for this, run the testqgsexpression.cpp test suite - that will crash
66  // and burn on the "line_interpolate_point point" test if a GEOSException is thrown.
67  // TODO - find a real fix for the underlying issue
68  try
69  {
70  throw GEOSException( message );
71  }
72  catch ( ... )
73  {
74  // oops, msvc threw an exception when we tried to throw the exception!
75  // just throw nothing instead (except your mouse at your monitor)
76  }
77 #else
78  throw GEOSException( message );
79 #endif
80 }
81 
82 
83 static void printGEOSNotice( const char *fmt, ... )
84 {
85 #if defined(QGISDEBUG)
86  va_list ap;
87  char buffer[1024];
88 
89  va_start( ap, fmt );
90  vsnprintf( buffer, sizeof buffer, fmt, ap );
91  va_end( ap );
92 #else
93  Q_UNUSED( fmt )
94 #endif
95 }
96 
97 class GEOSInit
98 {
99  public:
100  GEOSContextHandle_t ctxt;
101 
102  GEOSInit()
103  {
104  ctxt = initGEOS_r( printGEOSNotice, throwGEOSException );
105  }
106 
107  ~GEOSInit()
108  {
109  finishGEOS_r( ctxt );
110  }
111 
112  GEOSInit( const GEOSInit &rh ) = delete;
113  GEOSInit &operator=( const GEOSInit &rh ) = delete;
114 };
115 
116 Q_GLOBAL_STATIC( GEOSInit, geosinit )
117 
118 void geos::GeosDeleter::operator()( GEOSGeometry *geom )
119 {
120  GEOSGeom_destroy_r( geosinit()->ctxt, geom );
121 }
122 
123 void geos::GeosDeleter::operator()( const GEOSPreparedGeometry *geom )
124 {
125  GEOSPreparedGeom_destroy_r( geosinit()->ctxt, geom );
126 }
127 
128 void geos::GeosDeleter::operator()( GEOSBufferParams *params )
129 {
130  GEOSBufferParams_destroy_r( geosinit()->ctxt, params );
131 }
132 
133 void geos::GeosDeleter::operator()( GEOSCoordSequence *sequence )
134 {
135  GEOSCoordSeq_destroy_r( geosinit()->ctxt, sequence );
136 }
137 
138 
140 
141 
143  : QgsGeometryEngine( geometry )
144  , mGeos( nullptr )
145  , mPrecision( precision )
146 {
147  cacheGeos();
148 }
149 
151 {
153  GEOSGeom_destroy_r( QgsGeos::getGEOSHandler(), geos );
154  return g;
155 }
156 
158 {
159  QgsGeometry g( QgsGeos::fromGeos( geos.get() ) );
160  return g;
161 }
162 
164 {
165  if ( geometry.isNull() )
166  {
167  return nullptr;
168  }
169 
170  return asGeos( geometry.constGet(), precision );
171 }
172 
173 QgsGeometry::OperationResult QgsGeos::addPart( QgsGeometry &geometry, GEOSGeometry *newPart )
174 {
175  if ( geometry.isNull() )
176  {
178  }
179  if ( !newPart )
180  {
182  }
183 
184  std::unique_ptr< QgsAbstractGeometry > geom = fromGeos( newPart );
185  return QgsGeometryEditUtils::addPart( geometry.get(), std::move( geom ) );
186 }
187 
189 {
190  mGeos.reset();
191  mGeosPrepared.reset();
192  cacheGeos();
193 }
194 
196 {
197  mGeosPrepared.reset();
198  if ( mGeos )
199  {
200  mGeosPrepared.reset( GEOSPrepare_r( geosinit()->ctxt, mGeos.get() ) );
201  }
202 }
203 
204 void QgsGeos::cacheGeos() const
205 {
206  if ( !mGeometry )
207  {
208  return;
209  }
210 
211  mGeos = asGeos( mGeometry, mPrecision );
212 }
213 
214 QgsAbstractGeometry *QgsGeos::intersection( const QgsAbstractGeometry *geom, QString *errorMsg ) const
215 {
216  return overlay( geom, OverlayIntersection, errorMsg ).release();
217 }
218 
219 QgsAbstractGeometry *QgsGeos::difference( const QgsAbstractGeometry *geom, QString *errorMsg ) const
220 {
221  return overlay( geom, OverlayDifference, errorMsg ).release();
222 }
223 
224 std::unique_ptr<QgsAbstractGeometry> QgsGeos::clip( const QgsRectangle &rect, QString *errorMsg ) const
225 {
226  if ( !mGeos || rect.isNull() || rect.isEmpty() )
227  {
228  return nullptr;
229  }
230 
231  try
232  {
233  geos::unique_ptr opGeom( GEOSClipByRect_r( geosinit()->ctxt, mGeos.get(), rect.xMinimum(), rect.yMinimum(), rect.xMaximum(), rect.yMaximum() ) );
234  return fromGeos( opGeom.get() );
235  }
236  catch ( GEOSException &e )
237  {
238  logError( QStringLiteral( "GEOS" ), e.what() );
239  if ( errorMsg )
240  {
241  *errorMsg = e.what();
242  }
243  return nullptr;
244  }
245 }
246 
247 
248 
249 
250 void QgsGeos::subdivideRecursive( const GEOSGeometry *currentPart, int maxNodes, int depth, QgsGeometryCollection *parts, const QgsRectangle &clipRect ) const
251 {
252  int partType = GEOSGeomTypeId_r( geosinit()->ctxt, currentPart );
253  if ( qgsDoubleNear( clipRect.width(), 0.0 ) && qgsDoubleNear( clipRect.height(), 0.0 ) )
254  {
255  if ( partType == GEOS_POINT )
256  {
257  parts->addGeometry( fromGeos( currentPart ).release() );
258  return;
259  }
260  else
261  {
262  return;
263  }
264  }
265 
266  if ( partType == GEOS_MULTILINESTRING || partType == GEOS_MULTIPOLYGON || partType == GEOS_GEOMETRYCOLLECTION )
267  {
268  int partCount = GEOSGetNumGeometries_r( geosinit()->ctxt, currentPart );
269  for ( int i = 0; i < partCount; ++i )
270  {
271  subdivideRecursive( GEOSGetGeometryN_r( geosinit()->ctxt, currentPart, i ), maxNodes, depth, parts, clipRect );
272  }
273  return;
274  }
275 
276  if ( depth > 50 )
277  {
278  parts->addGeometry( fromGeos( currentPart ).release() );
279  return;
280  }
281 
282  int vertexCount = GEOSGetNumCoordinates_r( geosinit()->ctxt, currentPart );
283  if ( vertexCount == 0 )
284  {
285  return;
286  }
287  else if ( vertexCount < maxNodes )
288  {
289  parts->addGeometry( fromGeos( currentPart ).release() );
290  return;
291  }
292 
293  // chop clipping rect in half by longest side
294  double width = clipRect.width();
295  double height = clipRect.height();
296  QgsRectangle halfClipRect1 = clipRect;
297  QgsRectangle halfClipRect2 = clipRect;
298  if ( width > height )
299  {
300  halfClipRect1.setXMaximum( clipRect.xMinimum() + width / 2.0 );
301  halfClipRect2.setXMinimum( halfClipRect1.xMaximum() );
302  }
303  else
304  {
305  halfClipRect1.setYMaximum( clipRect.yMinimum() + height / 2.0 );
306  halfClipRect2.setYMinimum( halfClipRect1.yMaximum() );
307  }
308 
309  if ( height <= 0 )
310  {
311  halfClipRect1.setYMinimum( halfClipRect1.yMinimum() - std::numeric_limits<double>::epsilon() );
312  halfClipRect2.setYMinimum( halfClipRect2.yMinimum() - std::numeric_limits<double>::epsilon() );
313  halfClipRect1.setYMaximum( halfClipRect1.yMaximum() + std::numeric_limits<double>::epsilon() );
314  halfClipRect2.setYMaximum( halfClipRect2.yMaximum() + std::numeric_limits<double>::epsilon() );
315  }
316  if ( width <= 0 )
317  {
318  halfClipRect1.setXMinimum( halfClipRect1.xMinimum() - std::numeric_limits<double>::epsilon() );
319  halfClipRect2.setXMinimum( halfClipRect2.xMinimum() - std::numeric_limits<double>::epsilon() );
320  halfClipRect1.setXMaximum( halfClipRect1.xMaximum() + std::numeric_limits<double>::epsilon() );
321  halfClipRect2.setXMaximum( halfClipRect2.xMaximum() + std::numeric_limits<double>::epsilon() );
322  }
323 
324  geos::unique_ptr clipPart1( GEOSClipByRect_r( geosinit()->ctxt, currentPart, halfClipRect1.xMinimum(), halfClipRect1.yMinimum(), halfClipRect1.xMaximum(), halfClipRect1.yMaximum() ) );
325  geos::unique_ptr clipPart2( GEOSClipByRect_r( geosinit()->ctxt, currentPart, halfClipRect2.xMinimum(), halfClipRect2.yMinimum(), halfClipRect2.xMaximum(), halfClipRect2.yMaximum() ) );
326 
327  ++depth;
328 
329  if ( clipPart1 )
330  {
331  subdivideRecursive( clipPart1.get(), maxNodes, depth, parts, halfClipRect1 );
332  }
333  if ( clipPart2 )
334  {
335  subdivideRecursive( clipPart2.get(), maxNodes, depth, parts, halfClipRect2 );
336  }
337 }
338 
339 std::unique_ptr<QgsAbstractGeometry> QgsGeos::subdivide( int maxNodes, QString *errorMsg ) const
340 {
341  if ( !mGeos )
342  {
343  return nullptr;
344  }
345 
346  // minimum allowed max is 8
347  maxNodes = std::max( maxNodes, 8 );
348 
349  std::unique_ptr< QgsGeometryCollection > parts = QgsGeometryFactory::createCollectionOfType( mGeometry->wkbType() );
350  try
351  {
352  subdivideRecursive( mGeos.get(), maxNodes, 0, parts.get(), mGeometry->boundingBox() );
353  }
354  CATCH_GEOS_WITH_ERRMSG( nullptr )
355 
356  return std::move( parts );
357 }
358 
359 QgsAbstractGeometry *QgsGeos::combine( const QgsAbstractGeometry *geom, QString *errorMsg ) const
360 {
361  return overlay( geom, OverlayUnion, errorMsg ).release();
362 }
363 
364 QgsAbstractGeometry *QgsGeos::combine( const QVector<QgsAbstractGeometry *> &geomList, QString *errorMsg ) const
365 {
366  QVector< GEOSGeometry * > geosGeometries;
367  geosGeometries.reserve( geomList.size() );
368  for ( const QgsAbstractGeometry *g : geomList )
369  {
370  if ( !g )
371  continue;
372 
373  geosGeometries << asGeos( g, mPrecision ).release();
374  }
375 
376  geos::unique_ptr geomUnion;
377  try
378  {
379  geos::unique_ptr geomCollection = createGeosCollection( GEOS_GEOMETRYCOLLECTION, geosGeometries );
380  geomUnion.reset( GEOSUnaryUnion_r( geosinit()->ctxt, geomCollection.get() ) );
381  }
382  CATCH_GEOS_WITH_ERRMSG( nullptr )
383 
384  std::unique_ptr< QgsAbstractGeometry > result = fromGeos( geomUnion.get() );
385  return result.release();
386 }
387 
388 QgsAbstractGeometry *QgsGeos::combine( const QVector<QgsGeometry> &geomList, QString *errorMsg ) const
389 {
390  QVector< GEOSGeometry * > geosGeometries;
391  geosGeometries.reserve( geomList.size() );
392  for ( const QgsGeometry &g : geomList )
393  {
394  if ( g.isNull() )
395  continue;
396 
397  geosGeometries << asGeos( g.constGet(), mPrecision ).release();
398  }
399 
400  geos::unique_ptr geomUnion;
401  try
402  {
403  geos::unique_ptr geomCollection = createGeosCollection( GEOS_GEOMETRYCOLLECTION, geosGeometries );
404  geomUnion.reset( GEOSUnaryUnion_r( geosinit()->ctxt, geomCollection.get() ) );
405  }
406  CATCH_GEOS_WITH_ERRMSG( nullptr )
407 
408  std::unique_ptr< QgsAbstractGeometry > result = fromGeos( geomUnion.get() );
409  return result.release();
410 }
411 
412 QgsAbstractGeometry *QgsGeos::symDifference( const QgsAbstractGeometry *geom, QString *errorMsg ) const
413 {
414  return overlay( geom, OverlaySymDifference, errorMsg ).release();
415 }
416 
417 double QgsGeos::distance( const QgsAbstractGeometry *geom, QString *errorMsg ) const
418 {
419  double distance = -1.0;
420  if ( !mGeos )
421  {
422  return distance;
423  }
424 
425  geos::unique_ptr otherGeosGeom( asGeos( geom, mPrecision ) );
426  if ( !otherGeosGeom )
427  {
428  return distance;
429  }
430 
431  try
432  {
433  GEOSDistance_r( geosinit()->ctxt, mGeos.get(), otherGeosGeom.get(), &distance );
434  }
435  CATCH_GEOS_WITH_ERRMSG( -1.0 )
436 
437  return distance;
438 }
439 
440 double QgsGeos::hausdorffDistance( const QgsAbstractGeometry *geom, QString *errorMsg ) const
441 {
442  double distance = -1.0;
443  if ( !mGeos )
444  {
445  return distance;
446  }
447 
448  geos::unique_ptr otherGeosGeom( asGeos( geom, mPrecision ) );
449  if ( !otherGeosGeom )
450  {
451  return distance;
452  }
453 
454  try
455  {
456  GEOSHausdorffDistance_r( geosinit()->ctxt, mGeos.get(), otherGeosGeom.get(), &distance );
457  }
458  CATCH_GEOS_WITH_ERRMSG( -1.0 )
459 
460  return distance;
461 }
462 
463 double QgsGeos::hausdorffDistanceDensify( const QgsAbstractGeometry *geom, double densifyFraction, QString *errorMsg ) const
464 {
465  double distance = -1.0;
466  if ( !mGeos )
467  {
468  return distance;
469  }
470 
471  geos::unique_ptr otherGeosGeom( asGeos( geom, mPrecision ) );
472  if ( !otherGeosGeom )
473  {
474  return distance;
475  }
476 
477  try
478  {
479  GEOSHausdorffDistanceDensify_r( geosinit()->ctxt, mGeos.get(), otherGeosGeom.get(), densifyFraction, &distance );
480  }
481  CATCH_GEOS_WITH_ERRMSG( -1.0 )
482 
483  return distance;
484 }
485 
486 bool QgsGeos::intersects( const QgsAbstractGeometry *geom, QString *errorMsg ) const
487 {
488  return relation( geom, RelationIntersects, errorMsg );
489 }
490 
491 bool QgsGeos::touches( const QgsAbstractGeometry *geom, QString *errorMsg ) const
492 {
493  return relation( geom, RelationTouches, errorMsg );
494 }
495 
496 bool QgsGeos::crosses( const QgsAbstractGeometry *geom, QString *errorMsg ) const
497 {
498  return relation( geom, RelationCrosses, errorMsg );
499 }
500 
501 bool QgsGeos::within( const QgsAbstractGeometry *geom, QString *errorMsg ) const
502 {
503  return relation( geom, RelationWithin, errorMsg );
504 }
505 
506 bool QgsGeos::overlaps( const QgsAbstractGeometry *geom, QString *errorMsg ) const
507 {
508  return relation( geom, RelationOverlaps, errorMsg );
509 }
510 
511 bool QgsGeos::contains( const QgsAbstractGeometry *geom, QString *errorMsg ) const
512 {
513  return relation( geom, RelationContains, errorMsg );
514 }
515 
516 bool QgsGeos::disjoint( const QgsAbstractGeometry *geom, QString *errorMsg ) const
517 {
518  return relation( geom, RelationDisjoint, errorMsg );
519 }
520 
521 QString QgsGeos::relate( const QgsAbstractGeometry *geom, QString *errorMsg ) const
522 {
523  if ( !mGeos )
524  {
525  return QString();
526  }
527 
528  geos::unique_ptr geosGeom( asGeos( geom, mPrecision ) );
529  if ( !geosGeom )
530  {
531  return QString();
532  }
533 
534  QString result;
535  try
536  {
537  char *r = GEOSRelate_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() );
538  if ( r )
539  {
540  result = QString( r );
541  GEOSFree_r( geosinit()->ctxt, r );
542  }
543  }
544  catch ( GEOSException &e )
545  {
546  logError( QStringLiteral( "GEOS" ), e.what() );
547  if ( errorMsg )
548  {
549  *errorMsg = e.what();
550  }
551  }
552 
553  return result;
554 }
555 
556 bool QgsGeos::relatePattern( const QgsAbstractGeometry *geom, const QString &pattern, QString *errorMsg ) const
557 {
558  if ( !mGeos || !geom )
559  {
560  return false;
561  }
562 
563  geos::unique_ptr geosGeom( asGeos( geom, mPrecision ) );
564  if ( !geosGeom )
565  {
566  return false;
567  }
568 
569  bool result = false;
570  try
571  {
572  result = ( GEOSRelatePattern_r( geosinit()->ctxt, mGeos.get(), geosGeom.get(), pattern.toLocal8Bit().constData() ) == 1 );
573  }
574  catch ( GEOSException &e )
575  {
576  logError( QStringLiteral( "GEOS" ), e.what() );
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, bool skipIntersectionCheck ) 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, skipIntersectionCheck );
682  }
683  else if ( mGeometry->dimension() == 2 )
684  {
685  returnCode = splitPolygonGeometry( splitLineGeos.get(), newGeometries, skipIntersectionCheck );
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, bool skipIntersectionCheck ) 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 ( !skipIntersectionCheck && !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, bool skipIntersectionCheck ) 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 ( !skipIntersectionCheck && !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  if ( GEOSisEmpty_r( geosinit()->ctxt, *geomIt ) )
1059  {
1060  // don't add empty parts to a geos collection, it can cause crashes in GEOS
1061  nNullGeoms++;
1062  nNotNullGeoms--;
1063  GEOSGeom_destroy_r( geosinit()->ctxt, *geomIt );
1064  }
1065  else
1066  {
1067  geomarr[i] = *geomIt;
1068  ++i;
1069  }
1070  }
1071  }
1072  geos::unique_ptr geom;
1073 
1074  try
1075  {
1076  geom.reset( GEOSGeom_createCollection_r( geosinit()->ctxt, typeId, geomarr, nNotNullGeoms ) );
1077  }
1078  catch ( GEOSException & )
1079  {
1080  }
1081 
1082  delete [] geomarr;
1083 
1084  return geom;
1085 }
1086 
1087 std::unique_ptr<QgsAbstractGeometry> QgsGeos::fromGeos( const GEOSGeometry *geos )
1088 {
1089  if ( !geos )
1090  {
1091  return nullptr;
1092  }
1093 
1094  int nCoordDims = GEOSGeom_getCoordinateDimension_r( geosinit()->ctxt, geos );
1095  int nDims = GEOSGeom_getDimensions_r( geosinit()->ctxt, geos );
1096  bool hasZ = ( nCoordDims == 3 );
1097  bool hasM = ( ( nDims - nCoordDims ) == 1 );
1098 
1099  switch ( GEOSGeomTypeId_r( geosinit()->ctxt, geos ) )
1100  {
1101  case GEOS_POINT: // a point
1102  {
1103  const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, geos );
1104  unsigned int nPoints = 0;
1105  GEOSCoordSeq_getSize_r( geosinit()->ctxt, cs, &nPoints );
1106  return nPoints > 0 ? std::unique_ptr<QgsAbstractGeometry>( coordSeqPoint( cs, 0, hasZ, hasM ).clone() ) : qgis::make_unique< QgsPoint >();
1107  }
1108  case GEOS_LINESTRING:
1109  {
1110  return sequenceToLinestring( geos, hasZ, hasM );
1111  }
1112  case GEOS_POLYGON:
1113  {
1114  return fromGeosPolygon( geos );
1115  }
1116  case GEOS_MULTIPOINT:
1117  {
1118  std::unique_ptr< QgsMultiPoint > multiPoint( new QgsMultiPoint() );
1119  int nParts = GEOSGetNumGeometries_r( geosinit()->ctxt, geos );
1120  multiPoint->reserve( nParts );
1121  for ( int i = 0; i < nParts; ++i )
1122  {
1123  const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, GEOSGetGeometryN_r( geosinit()->ctxt, geos, i ) );
1124  if ( cs )
1125  {
1126  unsigned int nPoints = 0;
1127  GEOSCoordSeq_getSize_r( geosinit()->ctxt, cs, &nPoints );
1128  if ( nPoints > 0 )
1129  multiPoint->addGeometry( coordSeqPoint( cs, 0, hasZ, hasM ).clone() );
1130  }
1131  }
1132  return std::move( multiPoint );
1133  }
1134  case GEOS_MULTILINESTRING:
1135  {
1136  std::unique_ptr< QgsMultiLineString > multiLineString( new QgsMultiLineString() );
1137  int nParts = GEOSGetNumGeometries_r( geosinit()->ctxt, geos );
1138  multiLineString->reserve( nParts );
1139  for ( int i = 0; i < nParts; ++i )
1140  {
1141  std::unique_ptr< QgsLineString >line( sequenceToLinestring( GEOSGetGeometryN_r( geosinit()->ctxt, geos, i ), hasZ, hasM ) );
1142  if ( line )
1143  {
1144  multiLineString->addGeometry( line.release() );
1145  }
1146  }
1147  return std::move( multiLineString );
1148  }
1149  case GEOS_MULTIPOLYGON:
1150  {
1151  std::unique_ptr< QgsMultiPolygon > multiPolygon( new QgsMultiPolygon() );
1152 
1153  int nParts = GEOSGetNumGeometries_r( geosinit()->ctxt, geos );
1154  multiPolygon->reserve( nParts );
1155  for ( int i = 0; i < nParts; ++i )
1156  {
1157  std::unique_ptr< QgsPolygon > poly = fromGeosPolygon( GEOSGetGeometryN_r( geosinit()->ctxt, geos, i ) );
1158  if ( poly )
1159  {
1160  multiPolygon->addGeometry( poly.release() );
1161  }
1162  }
1163  return std::move( multiPolygon );
1164  }
1165  case GEOS_GEOMETRYCOLLECTION:
1166  {
1167  std::unique_ptr< QgsGeometryCollection > geomCollection( new QgsGeometryCollection() );
1168  int nParts = GEOSGetNumGeometries_r( geosinit()->ctxt, geos );
1169  geomCollection->reserve( nParts );
1170  for ( int i = 0; i < nParts; ++i )
1171  {
1172  std::unique_ptr< QgsAbstractGeometry > geom( fromGeos( GEOSGetGeometryN_r( geosinit()->ctxt, geos, i ) ) );
1173  if ( geom )
1174  {
1175  geomCollection->addGeometry( geom.release() );
1176  }
1177  }
1178  return std::move( geomCollection );
1179  }
1180  }
1181  return nullptr;
1182 }
1183 
1184 std::unique_ptr<QgsPolygon> QgsGeos::fromGeosPolygon( const GEOSGeometry *geos )
1185 {
1186  if ( GEOSGeomTypeId_r( geosinit()->ctxt, geos ) != GEOS_POLYGON )
1187  {
1188  return nullptr;
1189  }
1190 
1191  int nCoordDims = GEOSGeom_getCoordinateDimension_r( geosinit()->ctxt, geos );
1192  int nDims = GEOSGeom_getDimensions_r( geosinit()->ctxt, geos );
1193  bool hasZ = ( nCoordDims == 3 );
1194  bool hasM = ( ( nDims - nCoordDims ) == 1 );
1195 
1196  std::unique_ptr< QgsPolygon > polygon( new QgsPolygon() );
1197 
1198  const GEOSGeometry *ring = GEOSGetExteriorRing_r( geosinit()->ctxt, geos );
1199  if ( ring )
1200  {
1201  polygon->setExteriorRing( sequenceToLinestring( ring, hasZ, hasM ).release() );
1202  }
1203 
1204  QVector<QgsCurve *> interiorRings;
1205  const int ringCount = GEOSGetNumInteriorRings_r( geosinit()->ctxt, geos );
1206  interiorRings.reserve( ringCount );
1207  for ( int i = 0; i < ringCount; ++i )
1208  {
1209  ring = GEOSGetInteriorRingN_r( geosinit()->ctxt, geos, i );
1210  if ( ring )
1211  {
1212  interiorRings.push_back( sequenceToLinestring( ring, hasZ, hasM ).release() );
1213  }
1214  }
1215  polygon->setInteriorRings( interiorRings );
1216 
1217  return polygon;
1218 }
1219 
1220 std::unique_ptr<QgsLineString> QgsGeos::sequenceToLinestring( const GEOSGeometry *geos, bool hasZ, bool hasM )
1221 {
1222  const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, geos );
1223  unsigned int nPoints;
1224  GEOSCoordSeq_getSize_r( geosinit()->ctxt, cs, &nPoints );
1225  QVector< double > xOut( nPoints );
1226  QVector< double > yOut( nPoints );
1227  QVector< double > zOut;
1228  if ( hasZ )
1229  zOut.resize( nPoints );
1230  QVector< double > mOut;
1231  if ( hasM )
1232  mOut.resize( nPoints );
1233  double *x = xOut.data();
1234  double *y = yOut.data();
1235  double *z = zOut.data();
1236  double *m = mOut.data();
1237  for ( unsigned int i = 0; i < nPoints; ++i )
1238  {
1239 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
1240  if ( hasZ )
1241  GEOSCoordSeq_getXYZ_r( geosinit()->ctxt, cs, i, x++, y++, z++ );
1242  else
1243  GEOSCoordSeq_getXY_r( geosinit()->ctxt, cs, i, x++, y++ );
1244 #else
1245  GEOSCoordSeq_getX_r( geosinit()->ctxt, cs, i, x++ );
1246  GEOSCoordSeq_getY_r( geosinit()->ctxt, cs, i, y++ );
1247  if ( hasZ )
1248  {
1249  GEOSCoordSeq_getZ_r( geosinit()->ctxt, cs, i, z++ );
1250  }
1251 #endif
1252  if ( hasM )
1253  {
1254  GEOSCoordSeq_getOrdinate_r( geosinit()->ctxt, cs, i, 3, m++ );
1255  }
1256  }
1257  std::unique_ptr< QgsLineString > line( new QgsLineString( xOut, yOut, zOut, mOut ) );
1258  return line;
1259 }
1260 
1261 int QgsGeos::numberOfGeometries( GEOSGeometry *g )
1262 {
1263  if ( !g )
1264  return 0;
1265 
1266  int geometryType = GEOSGeomTypeId_r( geosinit()->ctxt, g );
1267  if ( geometryType == GEOS_POINT || geometryType == GEOS_LINESTRING || geometryType == GEOS_LINEARRING
1268  || geometryType == GEOS_POLYGON )
1269  return 1;
1270 
1271  //calling GEOSGetNumGeometries is save for multi types and collections also in geos2
1272  return GEOSGetNumGeometries_r( geosinit()->ctxt, g );
1273 }
1274 
1275 QgsPoint QgsGeos::coordSeqPoint( const GEOSCoordSequence *cs, int i, bool hasZ, bool hasM )
1276 {
1277  if ( !cs )
1278  {
1279  return QgsPoint();
1280  }
1281 
1282  double x, y;
1283  double z = 0;
1284  double m = 0;
1285 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
1286  if ( hasZ )
1287  GEOSCoordSeq_getXYZ_r( geosinit()->ctxt, cs, i, &x, &y, &z );
1288  else
1289  GEOSCoordSeq_getXY_r( geosinit()->ctxt, cs, i, &x, &y );
1290 #else
1291  GEOSCoordSeq_getX_r( geosinit()->ctxt, cs, i, &x );
1292  GEOSCoordSeq_getY_r( geosinit()->ctxt, cs, i, &y );
1293  if ( hasZ )
1294  {
1295  GEOSCoordSeq_getZ_r( geosinit()->ctxt, cs, i, &z );
1296  }
1297 #endif
1298  if ( hasM )
1299  {
1300  GEOSCoordSeq_getOrdinate_r( geosinit()->ctxt, cs, i, 3, &m );
1301  }
1302 
1304  if ( hasZ && hasM )
1305  {
1307  }
1308  else if ( hasZ )
1309  {
1310  t = QgsWkbTypes::PointZ;
1311  }
1312  else if ( hasM )
1313  {
1314  t = QgsWkbTypes::PointM;
1315  }
1316  return QgsPoint( t, x, y, z, m );
1317 }
1318 
1320 {
1321  if ( !geom )
1322  return nullptr;
1323 
1324  int coordDims = 2;
1325  if ( geom->is3D() )
1326  {
1327  ++coordDims;
1328  }
1329  if ( geom->isMeasure() )
1330  {
1331  ++coordDims;
1332  }
1333 
1335  {
1336  int geosType = GEOS_GEOMETRYCOLLECTION;
1337 
1339  {
1340  switch ( QgsWkbTypes::geometryType( geom->wkbType() ) )
1341  {
1343  geosType = GEOS_MULTIPOINT;
1344  break;
1345 
1347  geosType = GEOS_MULTILINESTRING;
1348  break;
1349 
1351  geosType = GEOS_MULTIPOLYGON;
1352  break;
1353 
1356  return nullptr;
1357  break;
1358  }
1359  }
1360 
1361 
1362  const QgsGeometryCollection *c = qgsgeometry_cast<const QgsGeometryCollection *>( geom );
1363 
1364  if ( !c )
1365  return nullptr;
1366 
1367  QVector< GEOSGeometry * > geomVector( c->numGeometries() );
1368  for ( int i = 0; i < c->numGeometries(); ++i )
1369  {
1370  geomVector[i] = asGeos( c->geometryN( i ), precision ).release();
1371  }
1372  return createGeosCollection( geosType, geomVector );
1373  }
1374  else
1375  {
1376  switch ( QgsWkbTypes::geometryType( geom->wkbType() ) )
1377  {
1379  return createGeosPoint( static_cast<const QgsPoint *>( geom ), coordDims, precision );
1380  break;
1381 
1383  return createGeosLinestring( static_cast<const QgsLineString *>( geom ), precision );
1384  break;
1385 
1387  return createGeosPolygon( static_cast<const QgsPolygon *>( geom ), precision );
1388  break;
1389 
1392  return nullptr;
1393  break;
1394  }
1395  }
1396  return nullptr;
1397 }
1398 
1399 std::unique_ptr<QgsAbstractGeometry> QgsGeos::overlay( const QgsAbstractGeometry *geom, Overlay op, QString *errorMsg ) const
1400 {
1401  if ( !mGeos || !geom )
1402  {
1403  return nullptr;
1404  }
1405 
1406  geos::unique_ptr geosGeom( asGeos( geom, mPrecision ) );
1407  if ( !geosGeom )
1408  {
1409  return nullptr;
1410  }
1411 
1412  try
1413  {
1414  geos::unique_ptr opGeom;
1415  switch ( op )
1416  {
1417  case OverlayIntersection:
1418  opGeom.reset( GEOSIntersection_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) );
1419  break;
1420  case OverlayDifference:
1421  opGeom.reset( GEOSDifference_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) );
1422  break;
1423  case OverlayUnion:
1424  {
1425  geos::unique_ptr unionGeometry( GEOSUnion_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) );
1426 
1427  if ( unionGeometry && GEOSGeomTypeId_r( geosinit()->ctxt, unionGeometry.get() ) == GEOS_MULTILINESTRING )
1428  {
1429  geos::unique_ptr mergedLines( GEOSLineMerge_r( geosinit()->ctxt, unionGeometry.get() ) );
1430  if ( mergedLines )
1431  {
1432  unionGeometry = std::move( mergedLines );
1433  }
1434  }
1435 
1436  opGeom = std::move( unionGeometry );
1437  }
1438  break;
1439  case OverlaySymDifference:
1440  opGeom.reset( GEOSSymDifference_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) );
1441  break;
1442  default: //unknown op
1443  return nullptr;
1444  }
1445  return fromGeos( opGeom.get() );
1446  }
1447  catch ( GEOSException &e )
1448  {
1449  logError( QStringLiteral( "GEOS" ), e.what() );
1450  if ( errorMsg )
1451  {
1452  *errorMsg = e.what();
1453  }
1454  return nullptr;
1455  }
1456 }
1457 
1458 bool QgsGeos::relation( const QgsAbstractGeometry *geom, Relation r, QString *errorMsg ) const
1459 {
1460  if ( !mGeos || !geom )
1461  {
1462  return false;
1463  }
1464 
1465  geos::unique_ptr geosGeom( asGeos( geom, mPrecision ) );
1466  if ( !geosGeom )
1467  {
1468  return false;
1469  }
1470 
1471  bool result = false;
1472  try
1473  {
1474  if ( mGeosPrepared ) //use faster version with prepared geometry
1475  {
1476  switch ( r )
1477  {
1478  case RelationIntersects:
1479  result = ( GEOSPreparedIntersects_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1480  break;
1481  case RelationTouches:
1482  result = ( GEOSPreparedTouches_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1483  break;
1484  case RelationCrosses:
1485  result = ( GEOSPreparedCrosses_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1486  break;
1487  case RelationWithin:
1488  result = ( GEOSPreparedWithin_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1489  break;
1490  case RelationContains:
1491  result = ( GEOSPreparedContains_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1492  break;
1493  case RelationDisjoint:
1494  result = ( GEOSPreparedDisjoint_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1495  break;
1496  case RelationOverlaps:
1497  result = ( GEOSPreparedOverlaps_r( geosinit()->ctxt, mGeosPrepared.get(), geosGeom.get() ) == 1 );
1498  break;
1499  default:
1500  return false;
1501  }
1502  return result;
1503  }
1504 
1505  switch ( r )
1506  {
1507  case RelationIntersects:
1508  result = ( GEOSIntersects_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1509  break;
1510  case RelationTouches:
1511  result = ( GEOSTouches_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1512  break;
1513  case RelationCrosses:
1514  result = ( GEOSCrosses_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1515  break;
1516  case RelationWithin:
1517  result = ( GEOSWithin_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1518  break;
1519  case RelationContains:
1520  result = ( GEOSContains_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1521  break;
1522  case RelationDisjoint:
1523  result = ( GEOSDisjoint_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1524  break;
1525  case RelationOverlaps:
1526  result = ( GEOSOverlaps_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() ) == 1 );
1527  break;
1528  default:
1529  return false;
1530  }
1531  }
1532  catch ( GEOSException &e )
1533  {
1534  logError( QStringLiteral( "GEOS" ), e.what() );
1535  if ( errorMsg )
1536  {
1537  *errorMsg = e.what();
1538  }
1539  return false;
1540  }
1541 
1542  return result;
1543 }
1544 
1545 QgsAbstractGeometry *QgsGeos::buffer( double distance, int segments, QString *errorMsg ) const
1546 {
1547  if ( !mGeos )
1548  {
1549  return nullptr;
1550  }
1551 
1553  try
1554  {
1555  geos.reset( GEOSBuffer_r( geosinit()->ctxt, mGeos.get(), distance, segments ) );
1556  }
1557  CATCH_GEOS_WITH_ERRMSG( nullptr );
1558  return fromGeos( geos.get() ).release();
1559 }
1560 
1561 QgsAbstractGeometry *QgsGeos::buffer( double distance, int segments, int endCapStyle, int joinStyle, double miterLimit, QString *errorMsg ) const
1562 {
1563  if ( !mGeos )
1564  {
1565  return nullptr;
1566  }
1567 
1569  try
1570  {
1571  geos.reset( GEOSBufferWithStyle_r( geosinit()->ctxt, mGeos.get(), distance, segments, endCapStyle, joinStyle, miterLimit ) );
1572  }
1573  CATCH_GEOS_WITH_ERRMSG( nullptr );
1574  return fromGeos( geos.get() ).release();
1575 }
1576 
1577 QgsAbstractGeometry *QgsGeos::simplify( double tolerance, QString *errorMsg ) const
1578 {
1579  if ( !mGeos )
1580  {
1581  return nullptr;
1582  }
1584  try
1585  {
1586  geos.reset( GEOSTopologyPreserveSimplify_r( geosinit()->ctxt, mGeos.get(), tolerance ) );
1587  }
1588  CATCH_GEOS_WITH_ERRMSG( nullptr );
1589  return fromGeos( geos.get() ).release();
1590 }
1591 
1592 QgsAbstractGeometry *QgsGeos::interpolate( double distance, QString *errorMsg ) const
1593 {
1594  if ( !mGeos )
1595  {
1596  return nullptr;
1597  }
1599  try
1600  {
1601  geos.reset( GEOSInterpolate_r( geosinit()->ctxt, mGeos.get(), distance ) );
1602  }
1603  CATCH_GEOS_WITH_ERRMSG( nullptr );
1604  return fromGeos( geos.get() ).release();
1605 }
1606 
1607 QgsPoint *QgsGeos::centroid( QString *errorMsg ) const
1608 {
1609  if ( !mGeos )
1610  {
1611  return nullptr;
1612  }
1613 
1615  double x;
1616  double y;
1617 
1618  try
1619  {
1620  geos.reset( GEOSGetCentroid_r( geosinit()->ctxt, mGeos.get() ) );
1621 
1622  if ( !geos )
1623  return nullptr;
1624 
1625  GEOSGeomGetX_r( geosinit()->ctxt, geos.get(), &x );
1626  GEOSGeomGetY_r( geosinit()->ctxt, geos.get(), &y );
1627  }
1628  CATCH_GEOS_WITH_ERRMSG( nullptr );
1629 
1630  return new QgsPoint( x, y );
1631 }
1632 
1633 QgsAbstractGeometry *QgsGeos::envelope( QString *errorMsg ) const
1634 {
1635  if ( !mGeos )
1636  {
1637  return nullptr;
1638  }
1640  try
1641  {
1642  geos.reset( GEOSEnvelope_r( geosinit()->ctxt, mGeos.get() ) );
1643  }
1644  CATCH_GEOS_WITH_ERRMSG( nullptr );
1645  return fromGeos( geos.get() ).release();
1646 }
1647 
1648 QgsPoint *QgsGeos::pointOnSurface( QString *errorMsg ) const
1649 {
1650  if ( !mGeos )
1651  {
1652  return nullptr;
1653  }
1654 
1655  double x;
1656  double y;
1657 
1659  try
1660  {
1661  geos.reset( GEOSPointOnSurface_r( geosinit()->ctxt, mGeos.get() ) );
1662 
1663  if ( !geos || GEOSisEmpty_r( geosinit()->ctxt, geos.get() ) != 0 )
1664  {
1665  return nullptr;
1666  }
1667 
1668  GEOSGeomGetX_r( geosinit()->ctxt, geos.get(), &x );
1669  GEOSGeomGetY_r( geosinit()->ctxt, geos.get(), &y );
1670  }
1671  CATCH_GEOS_WITH_ERRMSG( nullptr );
1672 
1673  return new QgsPoint( x, y );
1674 }
1675 
1676 QgsAbstractGeometry *QgsGeos::convexHull( QString *errorMsg ) const
1677 {
1678  if ( !mGeos )
1679  {
1680  return nullptr;
1681  }
1682 
1683  try
1684  {
1685  geos::unique_ptr cHull( GEOSConvexHull_r( geosinit()->ctxt, mGeos.get() ) );
1686  std::unique_ptr< QgsAbstractGeometry > cHullGeom = fromGeos( cHull.get() );
1687  return cHullGeom.release();
1688  }
1689  CATCH_GEOS_WITH_ERRMSG( nullptr );
1690 }
1691 
1692 bool QgsGeos::isValid( QString *errorMsg, const bool allowSelfTouchingHoles, QgsGeometry *errorLoc ) const
1693 {
1694  if ( !mGeos )
1695  {
1696  return false;
1697  }
1698 
1699  try
1700  {
1701  GEOSGeometry *g1 = nullptr;
1702  char *r = nullptr;
1703  char res = GEOSisValidDetail_r( geosinit()->ctxt, mGeos.get(), allowSelfTouchingHoles ? GEOSVALID_ALLOW_SELFTOUCHING_RING_FORMING_HOLE : 0, &r, &g1 );
1704  const bool invalid = res != 1;
1705 
1706  QString error;
1707  if ( r )
1708  {
1709  error = QString( r );
1710  GEOSFree_r( geosinit()->ctxt, r );
1711  }
1712 
1713  if ( invalid && errorMsg )
1714  {
1715  static QgsStringMap translatedErrors;
1716 
1717  if ( translatedErrors.empty() )
1718  {
1719  // Copied from https://git.osgeo.org/gitea/geos/geos/src/branch/master/src/operation/valid/TopologyValidationError.cpp
1720  translatedErrors.insert( QStringLiteral( "topology validation error" ), QObject::tr( "Topology validation error", "GEOS Error" ) );
1721  translatedErrors.insert( QStringLiteral( "repeated point" ), QObject::tr( "Repeated point", "GEOS Error" ) );
1722  translatedErrors.insert( QStringLiteral( "hole lies outside shell" ), QObject::tr( "Hole lies outside shell", "GEOS Error" ) );
1723  translatedErrors.insert( QStringLiteral( "holes are nested" ), QObject::tr( "Holes are nested", "GEOS Error" ) );
1724  translatedErrors.insert( QStringLiteral( "interior is disconnected" ), QObject::tr( "Interior is disconnected", "GEOS Error" ) );
1725  translatedErrors.insert( QStringLiteral( "self-intersection" ), QObject::tr( "Self-intersection", "GEOS Error" ) );
1726  translatedErrors.insert( QStringLiteral( "ring self-intersection" ), QObject::tr( "Ring self-intersection", "GEOS Error" ) );
1727  translatedErrors.insert( QStringLiteral( "nested shells" ), QObject::tr( "Nested shells", "GEOS Error" ) );
1728  translatedErrors.insert( QStringLiteral( "duplicate rings" ), QObject::tr( "Duplicate rings", "GEOS Error" ) );
1729  translatedErrors.insert( QStringLiteral( "too few points in geometry component" ), QObject::tr( "Too few points in geometry component", "GEOS Error" ) );
1730  translatedErrors.insert( QStringLiteral( "invalid coordinate" ), QObject::tr( "Invalid coordinate", "GEOS Error" ) );
1731  translatedErrors.insert( QStringLiteral( "ring is not closed" ), QObject::tr( "Ring is not closed", "GEOS Error" ) );
1732  }
1733 
1734  *errorMsg = translatedErrors.value( error.toLower(), error );
1735 
1736  if ( g1 && errorLoc )
1737  {
1738  *errorLoc = geometryFromGeos( g1 );
1739  }
1740  else if ( g1 )
1741  {
1742  GEOSGeom_destroy_r( geosinit()->ctxt, g1 );
1743  }
1744  }
1745  return !invalid;
1746  }
1747  CATCH_GEOS_WITH_ERRMSG( false );
1748 }
1749 
1750 bool QgsGeos::isEqual( const QgsAbstractGeometry *geom, QString *errorMsg ) const
1751 {
1752  if ( !mGeos || !geom )
1753  {
1754  return false;
1755  }
1756 
1757  try
1758  {
1759  geos::unique_ptr geosGeom( asGeos( geom, mPrecision ) );
1760  if ( !geosGeom )
1761  {
1762  return false;
1763  }
1764  bool equal = GEOSEquals_r( geosinit()->ctxt, mGeos.get(), geosGeom.get() );
1765  return equal;
1766  }
1767  CATCH_GEOS_WITH_ERRMSG( false );
1768 }
1769 
1770 bool QgsGeos::isEmpty( QString *errorMsg ) const
1771 {
1772  if ( !mGeos )
1773  {
1774  return false;
1775  }
1776 
1777  try
1778  {
1779  return GEOSisEmpty_r( geosinit()->ctxt, mGeos.get() );
1780  }
1781  CATCH_GEOS_WITH_ERRMSG( false );
1782 }
1783 
1784 bool QgsGeos::isSimple( QString *errorMsg ) const
1785 {
1786  if ( !mGeos )
1787  {
1788  return false;
1789  }
1790 
1791  try
1792  {
1793  return GEOSisSimple_r( geosinit()->ctxt, mGeos.get() );
1794  }
1795  CATCH_GEOS_WITH_ERRMSG( false );
1796 }
1797 
1798 GEOSCoordSequence *QgsGeos::createCoordinateSequence( const QgsCurve *curve, double precision, bool forceClose )
1799 {
1800  std::unique_ptr< QgsLineString > segmentized;
1801  const QgsLineString *line = qgsgeometry_cast<const QgsLineString *>( curve );
1802 
1803  if ( !line )
1804  {
1805  segmentized.reset( curve->curveToLine() );
1806  line = segmentized.get();
1807  }
1808 
1809  if ( !line )
1810  {
1811  return nullptr;
1812  }
1813 
1814  bool hasZ = line->is3D();
1815  bool hasM = false; //line->isMeasure(); //disabled until geos supports m-coordinates
1816  int coordDims = 2;
1817  if ( hasZ )
1818  {
1819  ++coordDims;
1820  }
1821  if ( hasM )
1822  {
1823  ++coordDims;
1824  }
1825 
1826  int numPoints = line->numPoints();
1827 
1828  int numOutPoints = numPoints;
1829  if ( forceClose && ( line->pointN( 0 ) != line->pointN( numPoints - 1 ) ) )
1830  {
1831  ++numOutPoints;
1832  }
1833 
1834  GEOSCoordSequence *coordSeq = nullptr;
1835  try
1836  {
1837  coordSeq = GEOSCoordSeq_create_r( geosinit()->ctxt, numOutPoints, coordDims );
1838  if ( !coordSeq )
1839  {
1840  QgsDebugMsg( QStringLiteral( "GEOS Exception: Could not create coordinate sequence for %1 points in %2 dimensions" ).arg( numPoints ).arg( coordDims ) );
1841  return nullptr;
1842  }
1843 
1844  const double *xData = line->xData();
1845  const double *yData = line->yData();
1846  const double *zData = hasZ ? line->zData() : nullptr;
1847  const double *mData = hasM ? line->mData() : nullptr;
1848 
1849  if ( precision > 0. )
1850  {
1851  for ( int i = 0; i < numOutPoints; ++i )
1852  {
1853  if ( i >= numPoints )
1854  {
1855  // start reading back from start of line
1856  xData = line->xData();
1857  yData = line->yData();
1858  zData = hasZ ? line->zData() : nullptr;
1859  mData = hasM ? line->mData() : nullptr;
1860  }
1861 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
1862  if ( hasZ )
1863  {
1864  GEOSCoordSeq_setXYZ_r( geosinit()->ctxt, coordSeq, i, std::round( *xData++ / precision ) * precision, std::round( *yData++ / precision ) * precision, std::round( *zData++ / precision ) * precision );
1865  }
1866  else
1867  {
1868  GEOSCoordSeq_setXY_r( geosinit()->ctxt, coordSeq, i, std::round( *xData++ / precision ) * precision, std::round( *yData++ / precision ) * precision );
1869  }
1870 #else
1871  GEOSCoordSeq_setX_r( geosinit()->ctxt, coordSeq, i, std::round( *xData++ / precision ) * precision );
1872  GEOSCoordSeq_setY_r( geosinit()->ctxt, coordSeq, i, std::round( *yData++ / precision ) * precision );
1873  if ( hasZ )
1874  {
1875  GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, i, 2, std::round( *zData++ / precision ) * precision );
1876  }
1877 #endif
1878  if ( hasM )
1879  {
1880  GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, i, 3, line->mAt( *mData++ ) );
1881  }
1882  }
1883  }
1884  else
1885  {
1886  for ( int i = 0; i < numOutPoints; ++i )
1887  {
1888  if ( i >= numPoints )
1889  {
1890  // start reading back from start of line
1891  xData = line->xData();
1892  yData = line->yData();
1893  zData = hasZ ? line->zData() : nullptr;
1894  mData = hasM ? line->mData() : nullptr;
1895  }
1896 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
1897  if ( hasZ )
1898  {
1899  GEOSCoordSeq_setXYZ_r( geosinit()->ctxt, coordSeq, i, *xData++, *yData++, *zData++ );
1900  }
1901  else
1902  {
1903  GEOSCoordSeq_setXY_r( geosinit()->ctxt, coordSeq, i, *xData++, *yData++ );
1904  }
1905 #else
1906  GEOSCoordSeq_setX_r( geosinit()->ctxt, coordSeq, i, *xData++ );
1907  GEOSCoordSeq_setY_r( geosinit()->ctxt, coordSeq, i, *yData++ );
1908  if ( hasZ )
1909  {
1910  GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, i, 2, *zData++ );
1911  }
1912 #endif
1913  if ( hasM )
1914  {
1915  GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, i, 3, *mData++ );
1916  }
1917  }
1918  }
1919  }
1920  CATCH_GEOS( nullptr )
1921 
1922  return coordSeq;
1923 }
1924 
1925 geos::unique_ptr QgsGeos::createGeosPoint( const QgsAbstractGeometry *point, int coordDims, double precision )
1926 {
1927  const QgsPoint *pt = qgsgeometry_cast<const QgsPoint *>( point );
1928  if ( !pt )
1929  return nullptr;
1930 
1931  return createGeosPointXY( pt->x(), pt->y(), pt->is3D(), pt->z(), pt->isMeasure(), pt->m(), coordDims, precision );
1932 }
1933 
1934 geos::unique_ptr QgsGeos::createGeosPointXY( double x, double y, bool hasZ, double z, bool hasM, double m, int coordDims, double precision )
1935 {
1936  Q_UNUSED( hasM )
1937  Q_UNUSED( m )
1938 
1939  geos::unique_ptr geosPoint;
1940  try
1941  {
1942 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
1943  if ( coordDims == 2 )
1944  {
1945  // optimised constructor
1946  if ( precision > 0. )
1947  geosPoint.reset( GEOSGeom_createPointFromXY_r( geosinit()->ctxt, std::round( x / precision ) * precision, std::round( y / precision ) * precision ) );
1948  else
1949  geosPoint.reset( GEOSGeom_createPointFromXY_r( geosinit()->ctxt, x, y ) );
1950  return geosPoint;
1951  }
1952 #endif
1953 
1954  GEOSCoordSequence *coordSeq = GEOSCoordSeq_create_r( geosinit()->ctxt, 1, coordDims );
1955  if ( !coordSeq )
1956  {
1957  QgsDebugMsg( QStringLiteral( "GEOS Exception: Could not create coordinate sequence for point with %1 dimensions" ).arg( coordDims ) );
1958  return nullptr;
1959  }
1960  if ( precision > 0. )
1961  {
1962  GEOSCoordSeq_setX_r( geosinit()->ctxt, coordSeq, 0, std::round( x / precision ) * precision );
1963  GEOSCoordSeq_setY_r( geosinit()->ctxt, coordSeq, 0, std::round( y / precision ) * precision );
1964  if ( hasZ )
1965  {
1966  GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, 0, 2, std::round( z / precision ) * precision );
1967  }
1968  }
1969  else
1970  {
1971  GEOSCoordSeq_setX_r( geosinit()->ctxt, coordSeq, 0, x );
1972  GEOSCoordSeq_setY_r( geosinit()->ctxt, coordSeq, 0, y );
1973  if ( hasZ )
1974  {
1975  GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, 0, 2, z );
1976  }
1977  }
1978 #if 0 //disabled until geos supports m-coordinates
1979  if ( hasM )
1980  {
1981  GEOSCoordSeq_setOrdinate_r( geosinit()->ctxt, coordSeq, 0, 3, m );
1982  }
1983 #endif
1984  geosPoint.reset( GEOSGeom_createPoint_r( geosinit()->ctxt, coordSeq ) );
1985  }
1986  CATCH_GEOS( nullptr )
1987  return geosPoint;
1988 }
1989 
1990 geos::unique_ptr QgsGeos::createGeosLinestring( const QgsAbstractGeometry *curve, double precision )
1991 {
1992  const QgsCurve *c = qgsgeometry_cast<const QgsCurve *>( curve );
1993  if ( !c )
1994  return nullptr;
1995 
1996  GEOSCoordSequence *coordSeq = createCoordinateSequence( c, precision );
1997  if ( !coordSeq )
1998  return nullptr;
1999 
2000  geos::unique_ptr geosGeom;
2001  try
2002  {
2003  geosGeom.reset( GEOSGeom_createLineString_r( geosinit()->ctxt, coordSeq ) );
2004  }
2005  CATCH_GEOS( nullptr )
2006  return geosGeom;
2007 }
2008 
2009 geos::unique_ptr QgsGeos::createGeosPolygon( const QgsAbstractGeometry *poly, double precision )
2010 {
2011  const QgsCurvePolygon *polygon = qgsgeometry_cast<const QgsCurvePolygon *>( poly );
2012  if ( !polygon )
2013  return nullptr;
2014 
2015  const QgsCurve *exteriorRing = polygon->exteriorRing();
2016  if ( !exteriorRing )
2017  {
2018  return nullptr;
2019  }
2020 
2021  geos::unique_ptr geosPolygon;
2022  try
2023  {
2024  geos::unique_ptr exteriorRingGeos( GEOSGeom_createLinearRing_r( geosinit()->ctxt, createCoordinateSequence( exteriorRing, precision, true ) ) );
2025 
2026  int nHoles = polygon->numInteriorRings();
2027  GEOSGeometry **holes = nullptr;
2028  if ( nHoles > 0 )
2029  {
2030  holes = new GEOSGeometry*[ nHoles ];
2031  }
2032 
2033  for ( int i = 0; i < nHoles; ++i )
2034  {
2035  const QgsCurve *interiorRing = polygon->interiorRing( i );
2036  holes[i] = GEOSGeom_createLinearRing_r( geosinit()->ctxt, createCoordinateSequence( interiorRing, precision, true ) );
2037  }
2038  geosPolygon.reset( GEOSGeom_createPolygon_r( geosinit()->ctxt, exteriorRingGeos.release(), holes, nHoles ) );
2039  delete[] holes;
2040  }
2041  CATCH_GEOS( nullptr )
2042 
2043  return geosPolygon;
2044 }
2045 
2046 QgsAbstractGeometry *QgsGeos::offsetCurve( double distance, int segments, int joinStyle, double miterLimit, QString *errorMsg ) const
2047 {
2048  if ( !mGeos )
2049  return nullptr;
2050 
2051  geos::unique_ptr offset;
2052  try
2053  {
2054  offset.reset( GEOSOffsetCurve_r( geosinit()->ctxt, mGeos.get(), distance, segments, joinStyle, miterLimit ) );
2055  }
2056  CATCH_GEOS_WITH_ERRMSG( nullptr )
2057  std::unique_ptr< QgsAbstractGeometry > offsetGeom = fromGeos( offset.get() );
2058  return offsetGeom.release();
2059 }
2060 
2061 std::unique_ptr<QgsAbstractGeometry> QgsGeos::singleSidedBuffer( double distance, int segments, int side, int joinStyle, double miterLimit, QString *errorMsg ) const
2062 {
2063  if ( !mGeos )
2064  {
2065  return nullptr;
2066  }
2067 
2069  try
2070  {
2071  geos::buffer_params_unique_ptr bp( GEOSBufferParams_create_r( geosinit()->ctxt ) );
2072  GEOSBufferParams_setSingleSided_r( geosinit()->ctxt, bp.get(), 1 );
2073  GEOSBufferParams_setQuadrantSegments_r( geosinit()->ctxt, bp.get(), segments );
2074  GEOSBufferParams_setJoinStyle_r( geosinit()->ctxt, bp.get(), joinStyle );
2075  GEOSBufferParams_setMitreLimit_r( geosinit()->ctxt, bp.get(), miterLimit ); //#spellok
2076 
2077  if ( side == 1 )
2078  {
2079  distance = -distance;
2080  }
2081  geos.reset( GEOSBufferWithParams_r( geosinit()->ctxt, mGeos.get(), bp.get(), distance ) );
2082  }
2083  CATCH_GEOS_WITH_ERRMSG( nullptr );
2084  return fromGeos( geos.get() );
2085 }
2086 
2087 std::unique_ptr<QgsAbstractGeometry> QgsGeos::reshapeGeometry( const QgsLineString &reshapeWithLine, EngineOperationResult *errorCode, QString *errorMsg ) const
2088 {
2089  if ( !mGeos || mGeometry->dimension() == 0 )
2090  {
2091  if ( errorCode ) { *errorCode = InvalidBaseGeometry; }
2092  return nullptr;
2093  }
2094 
2095  if ( reshapeWithLine.numPoints() < 2 )
2096  {
2097  if ( errorCode ) { *errorCode = InvalidInput; }
2098  return nullptr;
2099  }
2100 
2101  geos::unique_ptr reshapeLineGeos = createGeosLinestring( &reshapeWithLine, mPrecision );
2102 
2103  //single or multi?
2104  int numGeoms = GEOSGetNumGeometries_r( geosinit()->ctxt, mGeos.get() );
2105  if ( numGeoms == -1 )
2106  {
2107  if ( errorCode )
2108  {
2109  *errorCode = InvalidBaseGeometry;
2110  }
2111  return nullptr;
2112  }
2113 
2114  bool isMultiGeom = false;
2115  int geosTypeId = GEOSGeomTypeId_r( geosinit()->ctxt, mGeos.get() );
2116  if ( geosTypeId == GEOS_MULTILINESTRING || geosTypeId == GEOS_MULTIPOLYGON )
2117  isMultiGeom = true;
2118 
2119  bool isLine = ( mGeometry->dimension() == 1 );
2120 
2121  if ( !isMultiGeom )
2122  {
2123  geos::unique_ptr reshapedGeometry;
2124  if ( isLine )
2125  {
2126  reshapedGeometry = reshapeLine( mGeos.get(), reshapeLineGeos.get(), mPrecision );
2127  }
2128  else
2129  {
2130  reshapedGeometry = reshapePolygon( mGeos.get(), reshapeLineGeos.get(), mPrecision );
2131  }
2132 
2133  if ( errorCode )
2134  *errorCode = Success;
2135  std::unique_ptr< QgsAbstractGeometry > reshapeResult = fromGeos( reshapedGeometry.get() );
2136  return reshapeResult;
2137  }
2138  else
2139  {
2140  try
2141  {
2142  //call reshape for each geometry part and replace mGeos with new geometry if reshape took place
2143  bool reshapeTookPlace = false;
2144 
2145  geos::unique_ptr currentReshapeGeometry;
2146  GEOSGeometry **newGeoms = new GEOSGeometry*[numGeoms];
2147 
2148  for ( int i = 0; i < numGeoms; ++i )
2149  {
2150  if ( isLine )
2151  currentReshapeGeometry = reshapeLine( GEOSGetGeometryN_r( geosinit()->ctxt, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
2152  else
2153  currentReshapeGeometry = reshapePolygon( GEOSGetGeometryN_r( geosinit()->ctxt, mGeos.get(), i ), reshapeLineGeos.get(), mPrecision );
2154 
2155  if ( currentReshapeGeometry )
2156  {
2157  newGeoms[i] = currentReshapeGeometry.release();
2158  reshapeTookPlace = true;
2159  }
2160  else
2161  {
2162  newGeoms[i] = GEOSGeom_clone_r( geosinit()->ctxt, GEOSGetGeometryN_r( geosinit()->ctxt, mGeos.get(), i ) );
2163  }
2164  }
2165 
2166  geos::unique_ptr newMultiGeom;
2167  if ( isLine )
2168  {
2169  newMultiGeom.reset( GEOSGeom_createCollection_r( geosinit()->ctxt, GEOS_MULTILINESTRING, newGeoms, numGeoms ) );
2170  }
2171  else //multipolygon
2172  {
2173  newMultiGeom.reset( GEOSGeom_createCollection_r( geosinit()->ctxt, GEOS_MULTIPOLYGON, newGeoms, numGeoms ) );
2174  }
2175 
2176  delete[] newGeoms;
2177  if ( !newMultiGeom )
2178  {
2179  if ( errorCode ) { *errorCode = EngineError; }
2180  return nullptr;
2181  }
2182 
2183  if ( reshapeTookPlace )
2184  {
2185  if ( errorCode )
2186  *errorCode = Success;
2187  std::unique_ptr< QgsAbstractGeometry > reshapedMultiGeom = fromGeos( newMultiGeom.get() );
2188  return reshapedMultiGeom;
2189  }
2190  else
2191  {
2192  if ( errorCode )
2193  {
2194  *errorCode = NothingHappened;
2195  }
2196  return nullptr;
2197  }
2198  }
2199  CATCH_GEOS_WITH_ERRMSG( nullptr )
2200  }
2201 }
2202 
2203 QgsGeometry QgsGeos::mergeLines( QString *errorMsg ) const
2204 {
2205  if ( !mGeos )
2206  {
2207  return QgsGeometry();
2208  }
2209 
2210  if ( GEOSGeomTypeId_r( geosinit()->ctxt, mGeos.get() ) != GEOS_MULTILINESTRING )
2211  return QgsGeometry();
2212 
2214  try
2215  {
2216  geos.reset( GEOSLineMerge_r( geosinit()->ctxt, mGeos.get() ) );
2217  }
2219  return QgsGeometry( fromGeos( geos.get() ) );
2220 }
2221 
2222 QgsGeometry QgsGeos::closestPoint( const QgsGeometry &other, QString *errorMsg ) const
2223 {
2224  if ( !mGeos || isEmpty() || other.isNull() )
2225  {
2226  return QgsGeometry();
2227  }
2228 
2229  geos::unique_ptr otherGeom( asGeos( other.constGet(), mPrecision ) );
2230  if ( !otherGeom )
2231  {
2232  return QgsGeometry();
2233  }
2234 
2235  double nx = 0.0;
2236  double ny = 0.0;
2237  try
2238  {
2239  geos::coord_sequence_unique_ptr nearestCoord( GEOSNearestPoints_r( geosinit()->ctxt, mGeos.get(), otherGeom.get() ) );
2240 
2241  ( void )GEOSCoordSeq_getX_r( geosinit()->ctxt, nearestCoord.get(), 0, &nx );
2242  ( void )GEOSCoordSeq_getY_r( geosinit()->ctxt, nearestCoord.get(), 0, &ny );
2243  }
2244  catch ( GEOSException &e )
2245  {
2246  logError( QStringLiteral( "GEOS" ), e.what() );
2247  if ( errorMsg )
2248  {
2249  *errorMsg = e.what();
2250  }
2251  return QgsGeometry();
2252  }
2253 
2254  return QgsGeometry( new QgsPoint( nx, ny ) );
2255 }
2256 
2257 QgsGeometry QgsGeos::shortestLine( const QgsGeometry &other, QString *errorMsg ) const
2258 {
2259  if ( !mGeos || other.isNull() )
2260  {
2261  return QgsGeometry();
2262  }
2263 
2264  geos::unique_ptr otherGeom( asGeos( other.constGet(), mPrecision ) );
2265  if ( !otherGeom )
2266  {
2267  return QgsGeometry();
2268  }
2269 
2270  double nx1 = 0.0;
2271  double ny1 = 0.0;
2272  double nx2 = 0.0;
2273  double ny2 = 0.0;
2274  try
2275  {
2276  geos::coord_sequence_unique_ptr nearestCoord( GEOSNearestPoints_r( geosinit()->ctxt, mGeos.get(), otherGeom.get() ) );
2277 
2278  ( void )GEOSCoordSeq_getX_r( geosinit()->ctxt, nearestCoord.get(), 0, &nx1 );
2279  ( void )GEOSCoordSeq_getY_r( geosinit()->ctxt, nearestCoord.get(), 0, &ny1 );
2280  ( void )GEOSCoordSeq_getX_r( geosinit()->ctxt, nearestCoord.get(), 1, &nx2 );
2281  ( void )GEOSCoordSeq_getY_r( geosinit()->ctxt, nearestCoord.get(), 1, &ny2 );
2282  }
2283  catch ( GEOSException &e )
2284  {
2285  logError( QStringLiteral( "GEOS" ), e.what() );
2286  if ( errorMsg )
2287  {
2288  *errorMsg = e.what();
2289  }
2290  return QgsGeometry();
2291  }
2292 
2293  QgsLineString *line = new QgsLineString();
2294  line->addVertex( QgsPoint( nx1, ny1 ) );
2295  line->addVertex( QgsPoint( nx2, ny2 ) );
2296  return QgsGeometry( line );
2297 }
2298 
2299 double QgsGeos::lineLocatePoint( const QgsPoint &point, QString *errorMsg ) const
2300 {
2301  if ( !mGeos )
2302  {
2303  return -1;
2304  }
2305 
2306  geos::unique_ptr otherGeom( asGeos( &point, mPrecision ) );
2307  if ( !otherGeom )
2308  {
2309  return -1;
2310  }
2311 
2312  double distance = -1;
2313  try
2314  {
2315  distance = GEOSProject_r( geosinit()->ctxt, mGeos.get(), otherGeom.get() );
2316  }
2317  catch ( GEOSException &e )
2318  {
2319  logError( QStringLiteral( "GEOS" ), e.what() );
2320  if ( errorMsg )
2321  {
2322  *errorMsg = e.what();
2323  }
2324  return -1;
2325  }
2326 
2327  return distance;
2328 }
2329 
2330 QgsGeometry QgsGeos::polygonize( const QVector<const QgsAbstractGeometry *> &geometries, QString *errorMsg )
2331 {
2332  GEOSGeometry **const lineGeosGeometries = new GEOSGeometry*[ geometries.size()];
2333  int validLines = 0;
2334  for ( const QgsAbstractGeometry *g : geometries )
2335  {
2336  geos::unique_ptr l = asGeos( g );
2337  if ( l )
2338  {
2339  lineGeosGeometries[validLines] = l.release();
2340  validLines++;
2341  }
2342  }
2343 
2344  try
2345  {
2346  geos::unique_ptr result( GEOSPolygonize_r( geosinit()->ctxt, lineGeosGeometries, validLines ) );
2347  for ( int i = 0; i < validLines; ++i )
2348  {
2349  GEOSGeom_destroy_r( geosinit()->ctxt, lineGeosGeometries[i] );
2350  }
2351  delete[] lineGeosGeometries;
2352  return QgsGeometry( fromGeos( result.get() ) );
2353  }
2354  catch ( GEOSException &e )
2355  {
2356  if ( errorMsg )
2357  {
2358  *errorMsg = e.what();
2359  }
2360  for ( int i = 0; i < validLines; ++i )
2361  {
2362  GEOSGeom_destroy_r( geosinit()->ctxt, lineGeosGeometries[i] );
2363  }
2364  delete[] lineGeosGeometries;
2365  return QgsGeometry();
2366  }
2367 }
2368 
2369 QgsGeometry QgsGeos::voronoiDiagram( const QgsAbstractGeometry *extent, double tolerance, bool edgesOnly, QString *errorMsg ) const
2370 {
2371  if ( !mGeos )
2372  {
2373  return QgsGeometry();
2374  }
2375 
2376  geos::unique_ptr extentGeosGeom;
2377  if ( extent )
2378  {
2379  extentGeosGeom = asGeos( extent, mPrecision );
2380  if ( !extentGeosGeom )
2381  {
2382  return QgsGeometry();
2383  }
2384  }
2385 
2387  try
2388  {
2389  geos.reset( GEOSVoronoiDiagram_r( geosinit()->ctxt, mGeos.get(), extentGeosGeom.get(), tolerance, edgesOnly ) );
2390 
2391  if ( !geos || GEOSisEmpty_r( geosinit()->ctxt, geos.get() ) != 0 )
2392  {
2393  return QgsGeometry();
2394  }
2395 
2396  return QgsGeometry( fromGeos( geos.get() ) );
2397  }
2399 }
2400 
2401 QgsGeometry QgsGeos::delaunayTriangulation( double tolerance, bool edgesOnly, QString *errorMsg ) const
2402 {
2403  if ( !mGeos )
2404  {
2405  return QgsGeometry();
2406  }
2407 
2409  try
2410  {
2411  geos.reset( GEOSDelaunayTriangulation_r( geosinit()->ctxt, mGeos.get(), tolerance, edgesOnly ) );
2412 
2413  if ( !geos || GEOSisEmpty_r( geosinit()->ctxt, geos.get() ) != 0 )
2414  {
2415  return QgsGeometry();
2416  }
2417 
2418  return QgsGeometry( fromGeos( geos.get() ) );
2419  }
2421 }
2422 
2423 
2425 static bool _linestringEndpoints( const GEOSGeometry *linestring, double &x1, double &y1, double &x2, double &y2 )
2426 {
2427  const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, linestring );
2428  if ( !coordSeq )
2429  return false;
2430 
2431  unsigned int coordSeqSize;
2432  if ( GEOSCoordSeq_getSize_r( geosinit()->ctxt, coordSeq, &coordSeqSize ) == 0 )
2433  return false;
2434 
2435  if ( coordSeqSize < 2 )
2436  return false;
2437 
2438  GEOSCoordSeq_getX_r( geosinit()->ctxt, coordSeq, 0, &x1 );
2439  GEOSCoordSeq_getY_r( geosinit()->ctxt, coordSeq, 0, &y1 );
2440  GEOSCoordSeq_getX_r( geosinit()->ctxt, coordSeq, coordSeqSize - 1, &x2 );
2441  GEOSCoordSeq_getY_r( geosinit()->ctxt, coordSeq, coordSeqSize - 1, &y2 );
2442  return true;
2443 }
2444 
2445 
2447 static geos::unique_ptr _mergeLinestrings( const GEOSGeometry *line1, const GEOSGeometry *line2, const QgsPointXY &intersectionPoint )
2448 {
2449  double x1, y1, x2, y2;
2450  if ( !_linestringEndpoints( line1, x1, y1, x2, y2 ) )
2451  return nullptr;
2452 
2453  double rx1, ry1, rx2, ry2;
2454  if ( !_linestringEndpoints( line2, rx1, ry1, rx2, ry2 ) )
2455  return nullptr;
2456 
2457  bool intersectionAtOrigLineEndpoint =
2458  ( intersectionPoint.x() == x1 && intersectionPoint.y() == y1 ) !=
2459  ( intersectionPoint.x() == x2 && intersectionPoint.y() == y2 );
2460  bool intersectionAtReshapeLineEndpoint =
2461  ( intersectionPoint.x() == rx1 && intersectionPoint.y() == ry1 ) ||
2462  ( intersectionPoint.x() == rx2 && intersectionPoint.y() == ry2 );
2463 
2464  // the intersection must be at the begin/end of both lines
2465  if ( intersectionAtOrigLineEndpoint && intersectionAtReshapeLineEndpoint )
2466  {
2467  geos::unique_ptr g1( GEOSGeom_clone_r( geosinit()->ctxt, line1 ) );
2468  geos::unique_ptr g2( GEOSGeom_clone_r( geosinit()->ctxt, line2 ) );
2469  GEOSGeometry *geoms[2] = { g1.release(), g2.release() };
2470  geos::unique_ptr multiGeom( GEOSGeom_createCollection_r( geosinit()->ctxt, GEOS_MULTILINESTRING, geoms, 2 ) );
2471  geos::unique_ptr res( GEOSLineMerge_r( geosinit()->ctxt, multiGeom.get() ) );
2472  return res;
2473  }
2474  else
2475  return nullptr;
2476 }
2477 
2478 
2479 geos::unique_ptr QgsGeos::reshapeLine( const GEOSGeometry *line, const GEOSGeometry *reshapeLineGeos, double precision )
2480 {
2481  if ( !line || !reshapeLineGeos )
2482  return nullptr;
2483 
2484  bool atLeastTwoIntersections = false;
2485  bool oneIntersection = false;
2486  QgsPointXY oneIntersectionPoint;
2487 
2488  try
2489  {
2490  //make sure there are at least two intersection between line and reshape geometry
2491  geos::unique_ptr intersectGeom( GEOSIntersection_r( geosinit()->ctxt, line, reshapeLineGeos ) );
2492  if ( intersectGeom )
2493  {
2494  atLeastTwoIntersections = ( GEOSGeomTypeId_r( geosinit()->ctxt, intersectGeom.get() ) == GEOS_MULTIPOINT
2495  && GEOSGetNumGeometries_r( geosinit()->ctxt, intersectGeom.get() ) > 1 );
2496  // one point is enough when extending line at its endpoint
2497  if ( GEOSGeomTypeId_r( geosinit()->ctxt, intersectGeom.get() ) == GEOS_POINT )
2498  {
2499  const GEOSCoordSequence *intersectionCoordSeq = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, intersectGeom.get() );
2500  double xi, yi;
2501  GEOSCoordSeq_getX_r( geosinit()->ctxt, intersectionCoordSeq, 0, &xi );
2502  GEOSCoordSeq_getY_r( geosinit()->ctxt, intersectionCoordSeq, 0, &yi );
2503  oneIntersection = true;
2504  oneIntersectionPoint = QgsPointXY( xi, yi );
2505  }
2506  }
2507  }
2508  catch ( GEOSException & )
2509  {
2510  atLeastTwoIntersections = false;
2511  }
2512 
2513  // special case when extending line at its endpoint
2514  if ( oneIntersection )
2515  return _mergeLinestrings( line, reshapeLineGeos, oneIntersectionPoint );
2516 
2517  if ( !atLeastTwoIntersections )
2518  return nullptr;
2519 
2520  //begin and end point of original line
2521  double x1, y1, x2, y2;
2522  if ( !_linestringEndpoints( line, x1, y1, x2, y2 ) )
2523  return nullptr;
2524 
2525  geos::unique_ptr beginLineVertex = createGeosPointXY( x1, y1, false, 0, false, 0, 2, precision );
2526  geos::unique_ptr endLineVertex = createGeosPointXY( x2, y2, false, 0, false, 0, 2, precision );
2527 
2528  bool isRing = false;
2529  if ( GEOSGeomTypeId_r( geosinit()->ctxt, line ) == GEOS_LINEARRING
2530  || GEOSEquals_r( geosinit()->ctxt, beginLineVertex.get(), endLineVertex.get() ) == 1 )
2531  isRing = true;
2532 
2533  //node line and reshape line
2534  geos::unique_ptr nodedGeometry = nodeGeometries( reshapeLineGeos, line );
2535  if ( !nodedGeometry )
2536  {
2537  return nullptr;
2538  }
2539 
2540  //and merge them together
2541  geos::unique_ptr mergedLines( GEOSLineMerge_r( geosinit()->ctxt, nodedGeometry.get() ) );
2542  if ( !mergedLines )
2543  {
2544  return nullptr;
2545  }
2546 
2547  int numMergedLines = GEOSGetNumGeometries_r( geosinit()->ctxt, mergedLines.get() );
2548  if ( numMergedLines < 2 ) //some special cases. Normally it is >2
2549  {
2550  if ( numMergedLines == 1 ) //reshape line is from begin to endpoint. So we keep the reshapeline
2551  {
2552  geos::unique_ptr result( GEOSGeom_clone_r( geosinit()->ctxt, reshapeLineGeos ) );
2553  return result;
2554  }
2555  else
2556  return nullptr;
2557  }
2558 
2559  QVector<GEOSGeometry *> resultLineParts; //collection with the line segments that will be contained in result
2560  QVector<GEOSGeometry *> probableParts; //parts where we can decide on inclusion only after going through all the candidates
2561 
2562  for ( int i = 0; i < numMergedLines; ++i )
2563  {
2564  const GEOSGeometry *currentGeom = nullptr;
2565 
2566  currentGeom = GEOSGetGeometryN_r( geosinit()->ctxt, mergedLines.get(), i );
2567  const GEOSCoordSequence *currentCoordSeq = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, currentGeom );
2568  unsigned int currentCoordSeqSize;
2569  GEOSCoordSeq_getSize_r( geosinit()->ctxt, currentCoordSeq, &currentCoordSeqSize );
2570  if ( currentCoordSeqSize < 2 )
2571  continue;
2572 
2573  //get the two endpoints of the current line merge result
2574  double xBegin, xEnd, yBegin, yEnd;
2575  GEOSCoordSeq_getX_r( geosinit()->ctxt, currentCoordSeq, 0, &xBegin );
2576  GEOSCoordSeq_getY_r( geosinit()->ctxt, currentCoordSeq, 0, &yBegin );
2577  GEOSCoordSeq_getX_r( geosinit()->ctxt, currentCoordSeq, currentCoordSeqSize - 1, &xEnd );
2578  GEOSCoordSeq_getY_r( geosinit()->ctxt, currentCoordSeq, currentCoordSeqSize - 1, &yEnd );
2579  geos::unique_ptr beginCurrentGeomVertex = createGeosPointXY( xBegin, yBegin, false, 0, false, 0, 2, precision );
2580  geos::unique_ptr endCurrentGeomVertex = createGeosPointXY( xEnd, yEnd, false, 0, false, 0, 2, precision );
2581 
2582  //check how many endpoints of the line merge result are on the (original) line
2583  int nEndpointsOnOriginalLine = 0;
2584  if ( pointContainedInLine( beginCurrentGeomVertex.get(), line ) == 1 )
2585  nEndpointsOnOriginalLine += 1;
2586 
2587  if ( pointContainedInLine( endCurrentGeomVertex.get(), line ) == 1 )
2588  nEndpointsOnOriginalLine += 1;
2589 
2590  //check how many endpoints equal the endpoints of the original line
2591  int nEndpointsSameAsOriginalLine = 0;
2592  if ( GEOSEquals_r( geosinit()->ctxt, beginCurrentGeomVertex.get(), beginLineVertex.get() ) == 1
2593  || GEOSEquals_r( geosinit()->ctxt, beginCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
2594  nEndpointsSameAsOriginalLine += 1;
2595 
2596  if ( GEOSEquals_r( geosinit()->ctxt, endCurrentGeomVertex.get(), beginLineVertex.get() ) == 1
2597  || GEOSEquals_r( geosinit()->ctxt, endCurrentGeomVertex.get(), endLineVertex.get() ) == 1 )
2598  nEndpointsSameAsOriginalLine += 1;
2599 
2600  //check if the current geometry overlaps the original geometry (GEOSOverlap does not seem to work with linestrings)
2601  bool currentGeomOverlapsOriginalGeom = false;
2602  bool currentGeomOverlapsReshapeLine = false;
2603  if ( lineContainedInLine( currentGeom, line ) == 1 )
2604  currentGeomOverlapsOriginalGeom = true;
2605 
2606  if ( lineContainedInLine( currentGeom, reshapeLineGeos ) == 1 )
2607  currentGeomOverlapsReshapeLine = true;
2608 
2609  //logic to decide if this part belongs to the result
2610  if ( !isRing && nEndpointsSameAsOriginalLine == 1 && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
2611  {
2612  resultLineParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
2613  }
2614  //for closed rings, we take one segment from the candidate list
2615  else if ( isRing && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
2616  {
2617  probableParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
2618  }
2619  else if ( nEndpointsOnOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
2620  {
2621  resultLineParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
2622  }
2623  else if ( nEndpointsSameAsOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
2624  {
2625  resultLineParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
2626  }
2627  else if ( currentGeomOverlapsOriginalGeom && currentGeomOverlapsReshapeLine )
2628  {
2629  resultLineParts.push_back( GEOSGeom_clone_r( geosinit()->ctxt, currentGeom ) );
2630  }
2631  }
2632 
2633  //add the longest segment from the probable list for rings (only used for polygon rings)
2634  if ( isRing && !probableParts.isEmpty() )
2635  {
2636  geos::unique_ptr maxGeom; //the longest geometry in the probabla list
2637  GEOSGeometry *currentGeom = nullptr;
2638  double maxLength = -std::numeric_limits<double>::max();
2639  double currentLength = 0;
2640  for ( int i = 0; i < probableParts.size(); ++i )
2641  {
2642  currentGeom = probableParts.at( i );
2643  GEOSLength_r( geosinit()->ctxt, currentGeom, &currentLength );
2644  if ( currentLength > maxLength )
2645  {
2646  maxLength = currentLength;
2647  maxGeom.reset( currentGeom );
2648  }
2649  else
2650  {
2651  GEOSGeom_destroy_r( geosinit()->ctxt, currentGeom );
2652  }
2653  }
2654  resultLineParts.push_back( maxGeom.release() );
2655  }
2656 
2657  geos::unique_ptr result;
2658  if ( resultLineParts.empty() )
2659  return nullptr;
2660 
2661  if ( resultLineParts.size() == 1 ) //the whole result was reshaped
2662  {
2663  result.reset( resultLineParts[0] );
2664  }
2665  else //>1
2666  {
2667  GEOSGeometry **lineArray = new GEOSGeometry*[resultLineParts.size()];
2668  for ( int i = 0; i < resultLineParts.size(); ++i )
2669  {
2670  lineArray[i] = resultLineParts[i];
2671  }
2672 
2673  //create multiline from resultLineParts
2674  geos::unique_ptr multiLineGeom( GEOSGeom_createCollection_r( geosinit()->ctxt, GEOS_MULTILINESTRING, lineArray, resultLineParts.size() ) );
2675  delete [] lineArray;
2676 
2677  //then do a linemerge with the newly combined partstrings
2678  result.reset( GEOSLineMerge_r( geosinit()->ctxt, multiLineGeom.get() ) );
2679  }
2680 
2681  //now test if the result is a linestring. Otherwise something went wrong
2682  if ( GEOSGeomTypeId_r( geosinit()->ctxt, result.get() ) != GEOS_LINESTRING )
2683  {
2684  return nullptr;
2685  }
2686 
2687  return result;
2688 }
2689 
2690 geos::unique_ptr QgsGeos::reshapePolygon( const GEOSGeometry *polygon, const GEOSGeometry *reshapeLineGeos, double precision )
2691 {
2692  //go through outer shell and all inner rings and check if there is exactly one intersection of a ring and the reshape line
2693  int nIntersections = 0;
2694  int lastIntersectingRing = -2;
2695  const GEOSGeometry *lastIntersectingGeom = nullptr;
2696 
2697  int nRings = GEOSGetNumInteriorRings_r( geosinit()->ctxt, polygon );
2698  if ( nRings < 0 )
2699  return nullptr;
2700 
2701  //does outer ring intersect?
2702  const GEOSGeometry *outerRing = GEOSGetExteriorRing_r( geosinit()->ctxt, polygon );
2703  if ( GEOSIntersects_r( geosinit()->ctxt, outerRing, reshapeLineGeos ) == 1 )
2704  {
2705  ++nIntersections;
2706  lastIntersectingRing = -1;
2707  lastIntersectingGeom = outerRing;
2708  }
2709 
2710  //do inner rings intersect?
2711  const GEOSGeometry **innerRings = new const GEOSGeometry*[nRings];
2712 
2713  try
2714  {
2715  for ( int i = 0; i < nRings; ++i )
2716  {
2717  innerRings[i] = GEOSGetInteriorRingN_r( geosinit()->ctxt, polygon, i );
2718  if ( GEOSIntersects_r( geosinit()->ctxt, innerRings[i], reshapeLineGeos ) == 1 )
2719  {
2720  ++nIntersections;
2721  lastIntersectingRing = i;
2722  lastIntersectingGeom = innerRings[i];
2723  }
2724  }
2725  }
2726  catch ( GEOSException & )
2727  {
2728  nIntersections = 0;
2729  }
2730 
2731  if ( nIntersections != 1 ) //reshape line is only allowed to intersect one ring
2732  {
2733  delete [] innerRings;
2734  return nullptr;
2735  }
2736 
2737  //we have one intersecting ring, let's try to reshape it
2738  geos::unique_ptr reshapeResult = reshapeLine( lastIntersectingGeom, reshapeLineGeos, precision );
2739  if ( !reshapeResult )
2740  {
2741  delete [] innerRings;
2742  return nullptr;
2743  }
2744 
2745  //if reshaping took place, we need to reassemble the polygon and its rings
2746  GEOSGeometry *newRing = nullptr;
2747  const GEOSCoordSequence *reshapeSequence = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, reshapeResult.get() );
2748  GEOSCoordSequence *newCoordSequence = GEOSCoordSeq_clone_r( geosinit()->ctxt, reshapeSequence );
2749 
2750  reshapeResult.reset();
2751 
2752  newRing = GEOSGeom_createLinearRing_r( geosinit()->ctxt, newCoordSequence );
2753  if ( !newRing )
2754  {
2755  delete [] innerRings;
2756  return nullptr;
2757  }
2758 
2759  GEOSGeometry *newOuterRing = nullptr;
2760  if ( lastIntersectingRing == -1 )
2761  newOuterRing = newRing;
2762  else
2763  newOuterRing = GEOSGeom_clone_r( geosinit()->ctxt, outerRing );
2764 
2765  //check if all the rings are still inside the outer boundary
2766  QVector<GEOSGeometry *> ringList;
2767  if ( nRings > 0 )
2768  {
2769  GEOSGeometry *outerRingPoly = GEOSGeom_createPolygon_r( geosinit()->ctxt, GEOSGeom_clone_r( geosinit()->ctxt, newOuterRing ), nullptr, 0 );
2770  if ( outerRingPoly )
2771  {
2772  GEOSGeometry *currentRing = nullptr;
2773  for ( int i = 0; i < nRings; ++i )
2774  {
2775  if ( lastIntersectingRing == i )
2776  currentRing = newRing;
2777  else
2778  currentRing = GEOSGeom_clone_r( geosinit()->ctxt, innerRings[i] );
2779 
2780  //possibly a ring is no longer contained in the result polygon after reshape
2781  if ( GEOSContains_r( geosinit()->ctxt, outerRingPoly, currentRing ) == 1 )
2782  ringList.push_back( currentRing );
2783  else
2784  GEOSGeom_destroy_r( geosinit()->ctxt, currentRing );
2785  }
2786  }
2787  GEOSGeom_destroy_r( geosinit()->ctxt, outerRingPoly );
2788  }
2789 
2790  GEOSGeometry **newInnerRings = new GEOSGeometry*[ringList.size()];
2791  for ( int i = 0; i < ringList.size(); ++i )
2792  newInnerRings[i] = ringList.at( i );
2793 
2794  delete [] innerRings;
2795 
2796  geos::unique_ptr reshapedPolygon( GEOSGeom_createPolygon_r( geosinit()->ctxt, newOuterRing, newInnerRings, ringList.size() ) );
2797  delete[] newInnerRings;
2798 
2799  return reshapedPolygon;
2800 }
2801 
2802 int QgsGeos::lineContainedInLine( const GEOSGeometry *line1, const GEOSGeometry *line2 )
2803 {
2804  if ( !line1 || !line2 )
2805  {
2806  return -1;
2807  }
2808 
2809  double bufferDistance = std::pow( 10.0L, geomDigits( line2 ) - 11 );
2810 
2811  geos::unique_ptr bufferGeom( GEOSBuffer_r( geosinit()->ctxt, line2, bufferDistance, DEFAULT_QUADRANT_SEGMENTS ) );
2812  if ( !bufferGeom )
2813  return -2;
2814 
2815  geos::unique_ptr intersectionGeom( GEOSIntersection_r( geosinit()->ctxt, bufferGeom.get(), line1 ) );
2816 
2817  //compare ratio between line1Length and intersectGeomLength (usually close to 1 if line1 is contained in line2)
2818  double intersectGeomLength;
2819  double line1Length;
2820 
2821  GEOSLength_r( geosinit()->ctxt, intersectionGeom.get(), &intersectGeomLength );
2822  GEOSLength_r( geosinit()->ctxt, line1, &line1Length );
2823 
2824  double intersectRatio = line1Length / intersectGeomLength;
2825  if ( intersectRatio > 0.9 && intersectRatio < 1.1 )
2826  return 1;
2827 
2828  return 0;
2829 }
2830 
2831 int QgsGeos::pointContainedInLine( const GEOSGeometry *point, const GEOSGeometry *line )
2832 {
2833  if ( !point || !line )
2834  return -1;
2835 
2836  double bufferDistance = std::pow( 10.0L, geomDigits( line ) - 11 );
2837 
2838  geos::unique_ptr lineBuffer( GEOSBuffer_r( geosinit()->ctxt, line, bufferDistance, 8 ) );
2839  if ( !lineBuffer )
2840  return -2;
2841 
2842  bool contained = false;
2843  if ( GEOSContains_r( geosinit()->ctxt, lineBuffer.get(), point ) == 1 )
2844  contained = true;
2845 
2846  return contained;
2847 }
2848 
2849 int QgsGeos::geomDigits( const GEOSGeometry *geom )
2850 {
2851  geos::unique_ptr bbox( GEOSEnvelope_r( geosinit()->ctxt, geom ) );
2852  if ( !bbox.get() )
2853  return -1;
2854 
2855  const GEOSGeometry *bBoxRing = GEOSGetExteriorRing_r( geosinit()->ctxt, bbox.get() );
2856  if ( !bBoxRing )
2857  return -1;
2858 
2859  const GEOSCoordSequence *bBoxCoordSeq = GEOSGeom_getCoordSeq_r( geosinit()->ctxt, bBoxRing );
2860 
2861  if ( !bBoxCoordSeq )
2862  return -1;
2863 
2864  unsigned int nCoords = 0;
2865  if ( !GEOSCoordSeq_getSize_r( geosinit()->ctxt, bBoxCoordSeq, &nCoords ) )
2866  return -1;
2867 
2868  int maxDigits = -1;
2869  for ( unsigned int i = 0; i < nCoords - 1; ++i )
2870  {
2871  double t;
2872  GEOSCoordSeq_getX_r( geosinit()->ctxt, bBoxCoordSeq, i, &t );
2873 
2874  int digits;
2875  digits = std::ceil( std::log10( std::fabs( t ) ) );
2876  if ( digits > maxDigits )
2877  maxDigits = digits;
2878 
2879  GEOSCoordSeq_getY_r( geosinit()->ctxt, bBoxCoordSeq, i, &t );
2880  digits = std::ceil( std::log10( std::fabs( t ) ) );
2881  if ( digits > maxDigits )
2882  maxDigits = digits;
2883  }
2884 
2885  return maxDigits;
2886 }
2887 
2888 GEOSContextHandle_t QgsGeos::getGEOSHandler()
2889 {
2890  return geosinit()->ctxt;
2891 }
QgsCurve
Abstract base class for curved geometry type.
Definition: qgscurve.h:36
qgspolygon.h
QgsGeometry::AddPartNotMultiGeometry
@ AddPartNotMultiGeometry
The source geometry is not multi.
Definition: qgsgeometry.h:147
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:463
QgsGeos::distance
double distance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Calculates the distance between this and geom.
Definition: qgsgeos.cpp:417
QgsGeometryEngine::mGeometry
const QgsAbstractGeometry * mGeometry
Definition: qgsgeometryengine.h:288
QgsGeos::convexHull
QgsAbstractGeometry * convexHull(QString *errorMsg=nullptr) const override
Calculate the convex hull of this.
Definition: qgsgeos.cpp:1676
QgsLineString::pointN
QgsPoint pointN(int i) const
Returns the specified point from inside the line string.
Definition: qgslinestring.cpp:713
QgsRectangle::height
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:209
QgsLineString::xAt
double xAt(int index) const override
Returns the x-coordinate of the specified node in the line string.
Definition: qgslinestring.cpp:762
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:144
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:245
QgsGeometryEngine::InvalidInput
@ InvalidInput
The input is not valid.
Definition: qgsgeometryengine.h:50
QgsGeos::isEmpty
bool isEmpty(QString *errorMsg=nullptr) const override
Definition: qgsgeos.cpp:1770
QgsWkbTypes::Point
@ Point
Definition: qgswkbtypes.h:72
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:496
QgsPolygon
Polygon geometry type.
Definition: qgspolygon.h:34
QgsLineString::mAt
double mAt(int index) const
Returns the m value of the specified node in the line string.
Definition: qgslinestring.h:324
qgslinestring.h
QgsWkbTypes::NullGeometry
@ NullGeometry
Definition: qgswkbtypes.h:146
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:224
QgsPoint
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:38
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:2257
qgsgeometryfactory.h
QgsCurvePolygon::exteriorRing
const QgsCurve * exteriorRing() const SIP_HOLDGIL
Returns the curve polygon's exterior ring.
Definition: qgscurvepolygon.h:87
QgsGeos::geometryFromGeos
static QgsGeometry geometryFromGeos(GEOSGeometry *geos)
Creates a new QgsGeometry object, feeding in a geometry in GEOS format.
Definition: qgsgeos.cpp:150
QgsGeometry::isNull
Q_GADGET bool isNull
Definition: qgsgeometry.h:126
QgsPointXY::x
Q_GADGET double x
Definition: qgspointxy.h:47
QgsLineString::yAt
double yAt(int index) const override
Returns the y-coordinate of the specified node in the line string.
Definition: qgslinestring.cpp:770
QgsGeos::disjoint
bool disjoint(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom is disjoint from this.
Definition: qgsgeos.cpp:516
QgsWkbTypes::flatType
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:702
QgsCurvePolygon
Curve polygon geometry type.
Definition: qgscurvepolygon.h:35
QgsRectangle::yMinimum
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
geos
Contains geos related utilities and functions.
Definition: qgsgeos.h:42
QgsGeometryEngine::EngineError
@ EngineError
Error occurred in the geometry engine.
Definition: qgsgeometryengine.h:47
QgsMultiLineString
Multi line string geometry collection.
Definition: qgsmultilinestring.h:32
qgsmultipoint.h
QgsPoint::z
double z
Definition: qgspoint.h:43
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:232
QgsWkbTypes::Type
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
DEFAULT_QUADRANT_SEGMENTS
#define DEFAULT_QUADRANT_SEGMENTS
Definition: qgsgeos.cpp:31
QgsGeometry::OperationResult
OperationResult
Success or failure of a geometry operation.
Definition: qgsgeometry.h:136
QgsGeos::buffer
QgsAbstractGeometry * buffer(double distance, int segments, QString *errorMsg=nullptr) const override
Definition: qgsgeos.cpp:1545
QgsGeos::envelope
QgsAbstractGeometry * envelope(QString *errorMsg=nullptr) const override
Definition: qgsgeos.cpp:1633
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsLineString
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:44
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:521
Q_GLOBAL_STATIC
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
QgsRectangle
A rectangle specified with double values.
Definition: qgsrectangle.h:42
QgsWkbTypes::PolygonGeometry
@ PolygonGeometry
Definition: qgswkbtypes.h:144
QgsGeos::hausdorffDistance
double hausdorffDistance(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const
Returns the Hausdorff distance between this geometry and geom.
Definition: qgsgeos.cpp:440
QgsLineString::xData
const double * xData() const
Returns a const pointer to the x vertex data.
Definition: qgslinestring.h:221
QgsGeos::fromGeosPolygon
static std::unique_ptr< QgsPolygon > fromGeosPolygon(const GEOSGeometry *geos)
Definition: qgsgeos.cpp:1184
QgsAbstractGeometry::isMeasure
bool isMeasure() const SIP_HOLDGIL
Returns true if the geometry contains m values.
Definition: qgsabstractgeometry.h:215
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:173
QgsWkbTypes::PointM
@ PointM
Definition: qgswkbtypes.h:99
QgsCurvePolygon::numInteriorRings
int numInteriorRings() const SIP_HOLDGIL
Returns the number of interior rings contained with the curve polygon.
Definition: qgscurvepolygon.h:77
QgsRectangle::xMaximum
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
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:2369
QgsPoint::y
double y
Definition: qgspoint.h:42
precision
int precision
Definition: qgswfsgetfeature.cpp:49
QgsGeometryCollection
Geometry collection.
Definition: qgsgeometrycollection.h:36
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:163
QgsWkbTypes::PointZM
@ PointZM
Definition: qgswkbtypes.h:112
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:48
QgsGeometryEngine::InvalidBaseGeometry
@ InvalidBaseGeometry
The geometry on which the operation occurs is not valid.
Definition: qgsgeometryengine.h:49
QgsAbstractGeometry::wkbType
QgsWkbTypes::Type wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
Definition: qgsabstractgeometry.h:193
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:2222
QgsMultiCurve
Multi curve geometry collection.
Definition: qgsmulticurve.h:30
QgsGeos::interpolate
QgsAbstractGeometry * interpolate(double distance, QString *errorMsg=nullptr) const override
Definition: qgsgeos.cpp:1592
QgsGeos::isSimple
bool isSimple(QString *errorMsg=nullptr) const override
Determines whether the geometry is simple (according to OGC definition).
Definition: qgsgeos.cpp:1784
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:2061
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:2401
QgsWkbTypes::GeometryCollection
@ GeometryCollection
Definition: qgswkbtypes.h:79
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
Does vector analysis using the geos library and handles import, export, exception handling*.
Definition: qgsgeos.h:104
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:2330
QgsGeos::within
bool within(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom is within this.
Definition: qgsgeos.cpp:501
QgsGeos::overlaps
bool overlaps(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom overlaps this.
Definition: qgsgeos.cpp:506
QgsGeos::combine
QgsAbstractGeometry * combine(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Calculate the combination of this and geom.
Definition: qgsgeos.cpp:359
QgsWkbTypes::PointZ
@ PointZ
Definition: qgswkbtypes.h:86
QgsRectangle::setXMinimum
void setXMinimum(double x) SIP_HOLDGIL
Set the minimum x value.
Definition: qgsrectangle.h:130
QgsPoint::x
Q_GADGET double x
Definition: qgspoint.h:41
QgsGeos::splitGeometry
EngineOperationResult splitGeometry(const QgsLineString &splitLine, QVector< QgsGeometry > &newGeometries, bool topological, QgsPointSequence &topologyTestPoints, QString *errorMsg=nullptr, bool skipIntersectionCheck=false) const override
Splits this geometry according to a given line.
Definition: qgsgeos.cpp:619
QgsPoint::m
double m
Definition: qgspoint.h:44
QgsGeos::offsetCurve
QgsAbstractGeometry * offsetCurve(double distance, int segments, int joinStyle, double miterLimit, QString *errorMsg=nullptr) const override
Definition: qgsgeos.cpp:2046
QgsGeos::centroid
QgsPoint * centroid(QString *errorMsg=nullptr) const override
Calculates the centroid of this.
Definition: qgsgeos.cpp:1607
QgsMultiPolygon
Multi polygon geometry collection.
Definition: qgsmultipolygon.h:32
QgsRectangle::xMinimum
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
CATCH_GEOS
#define CATCH_GEOS(r)
Definition: qgsgeos.cpp:33
QgsCurvePolygon::interiorRing
const QgsCurve * interiorRing(int i) const SIP_HOLDGIL
Retrieves an interior ring from the curve polygon.
Definition: qgscurvepolygon.h:100
QgsGeos::QgsGeos
QgsGeos(const QgsAbstractGeometry *geometry, double precision=0)
GEOS geometry engine constructor.
Definition: qgsgeos.cpp:142
QgsGeometry::constGet
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
Definition: qgsgeometry.cpp:128
QgsMultiPoint
Multi point geometry collection.
Definition: qgsmultipoint.h:30
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:261
QgsRectangle::setXMaximum
void setXMaximum(double x) SIP_HOLDGIL
Set the maximum x value.
Definition: qgsrectangle.h:135
QgsAbstractGeometry
Abstract base class for all geometries.
Definition: qgsabstractgeometry.h:74
QgsGeos::getGEOSHandler
static GEOSContextHandle_t getGEOSHandler()
Definition: qgsgeos.cpp:2888
QgsAbstractGeometry::is3D
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
Definition: qgsabstractgeometry.h:206
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:556
QgsPointXY
A class to represent a 2D point.
Definition: qgspointxy.h:44
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:1648
QgsRectangle::setYMaximum
void setYMaximum(double y) SIP_HOLDGIL
Set the maximum y value.
Definition: qgsrectangle.h:145
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:45
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:143
QgsStringMap
QMap< QString, QString > QgsStringMap
Definition: qgis.h:758
QgsWkbTypes::PointGeometry
@ PointGeometry
Definition: qgswkbtypes.h:142
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:188
QgsGeos::symDifference
QgsAbstractGeometry * symDifference(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Calculate the symmetric difference of this and geom.
Definition: qgsgeos.cpp:412
QgsGeometryEngine::SplitCannotSplitPoint
@ SplitCannotSplitPoint
Points cannot be split.
Definition: qgsgeometryengine.h:52
QgsRectangle::yMaximum
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
QgsGeos::isEqual
bool isEqual(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if this is equal to geom.
Definition: qgsgeos.cpp:1750
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:2087
QgsRectangle::setYMinimum
void setYMinimum(double y) SIP_HOLDGIL
Set the minimum y value.
Definition: qgsrectangle.h:140
QgsPointSequence
QVector< QgsPoint > QgsPointSequence
Definition: qgsabstractgeometry.h:46
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:214
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
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
QgsGeos::touches
bool touches(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom touches this.
Definition: qgsgeos.cpp:491
QgsGeometry::InvalidBaseGeometry
@ InvalidBaseGeometry
The base geometry on which the operation is done is invalid or empty.
Definition: qgsgeometry.h:139
QgsRectangle::width
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:202
QgsLineString::addVertex
void addVertex(const QgsPoint &pt)
Adds a new vertex to the end of the line string.
Definition: qgslinestring.cpp:1343
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
QgsGeometryEngine::Success
@ Success
Operation succeeded.
Definition: qgsgeometryengine.h:44
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:2203
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:2299
QgsWkbTypes::UnknownGeometry
@ UnknownGeometry
Definition: qgswkbtypes.h:145
QgsWkbTypes::geometryType
static GeometryType geometryType(Type type) SIP_HOLDGIL
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:938
QgsGeos::contains
bool contains(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom contains this.
Definition: qgsgeos.cpp:511
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:43
QgsGeos::subdivide
std::unique_ptr< QgsAbstractGeometry > subdivide(int maxNodes, QString *errorMsg=nullptr) const
Subdivides the geometry.
Definition: qgsgeos.cpp:339
qgsgeometrycollection.h
qgsmulticurve.h
QgsGeos::simplify
QgsAbstractGeometry * simplify(double tolerance, QString *errorMsg=nullptr) const override
Definition: qgsgeos.cpp:1577
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:1087
QgsLineString::numPoints
int numPoints() const override SIP_HOLDGIL
Returns the number of points in the curve.
Definition: qgslinestring.cpp:703
QgsWkbTypes::isMultiType
static bool isMultiType(Type type) SIP_HOLDGIL
Returns true if the WKB type is a multi type.
Definition: qgswkbtypes.h:832
QgsGeos::intersects
bool intersects(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom intersects this.
Definition: qgsgeos.cpp:486
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:1692
QgsGeometryEngine
Contains geometry relation and modification algorithms.
Definition: qgsgeometryengine.h:35
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:219
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
QgsGeometryEngine::logError
void logError(const QString &engineName, const QString &message) const
Logs an error message encountered during an operation.
Definition: qgsgeometryengine.h:298
QgsRectangle::isNull
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:447
QgsGeos::prepareGeometry
void prepareGeometry() override
Prepares the geometry, so that subsequent calls to spatial relation methods are much faster.
Definition: qgsgeos.cpp:195
qgsmultilinestring.h
qgsgeos.h
QgsGeos::coordSeqPoint
static QgsPoint coordSeqPoint(const GEOSCoordSequence *cs, int i, bool hasZ, bool hasM)
Definition: qgsgeos.cpp:1275