QGIS API Documentation  2.12.0-Lyon
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 "qgsabstractgeometryv2.h"
19 #include "qgsgeometryfactory.h"
20 #include "qgslinestringv2.h"
21 #include "qgsmessagelog.h"
22 #include "qgsmulticurvev2.h"
23 #include "qgsmultilinestringv2.h"
24 #include "qgsmultipointv2.h"
25 #include "qgsmultipolygonv2.h"
26 #include "qgslogger.h"
27 #include "qgspolygonv2.h"
28 #include <limits>
29 #include <cstdio>
30 #include <QtCore/qmath.h>
31 
32 #define DEFAULT_QUADRANT_SEGMENTS 8
33 
34 #define CATCH_GEOS(r) \
35  catch (GEOSException &e) \
36  { \
37  QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr("GEOS") ); \
38  return r; \
39  }
40 
41 #define CATCH_GEOS_WITH_ERRMSG(r) \
42  catch (GEOSException &e) \
43  { \
44  QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr("GEOS") ); \
45  if ( errorMsg ) \
46  { \
47  *errorMsg = e.what(); \
48  } \
49  return r; \
50  }
51 
52 
53 static void throwGEOSException( const char *fmt, ... )
54 {
55  va_list ap;
56  char buffer[1024];
57 
58  va_start( ap, fmt );
59  vsnprintf( buffer, sizeof buffer, fmt, ap );
60  va_end( ap );
61 
62  QgsDebugMsg( QString( "GEOS exception: %1" ).arg( buffer ) );
63 
64  throw GEOSException( QString::fromUtf8( buffer ) );
65 }
66 
67 static void printGEOSNotice( const char *fmt, ... )
68 {
69 #if defined(QGISDEBUG)
70  va_list ap;
71  char buffer[1024];
72 
73  va_start( ap, fmt );
74  vsnprintf( buffer, sizeof buffer, fmt, ap );
75  va_end( ap );
76 
77  QgsDebugMsg( QString( "GEOS notice: %1" ).arg( QString::fromUtf8( buffer ) ) );
78 #else
79  Q_UNUSED( fmt );
80 #endif
81 }
82 
83 class GEOSInit
84 {
85  public:
86  GEOSContextHandle_t ctxt;
87 
89  {
90  ctxt = initGEOS_r( printGEOSNotice, throwGEOSException );
91  }
92 
94  {
95  finishGEOS_r( ctxt );
96  }
97 };
98 
100 
102 {
103  public:
104  explicit GEOSGeomScopedPtr( GEOSGeometry* geom = 0 ) : mGeom( geom ) {}
105  ~GEOSGeomScopedPtr() { GEOSGeom_destroy_r( geosinit.ctxt, mGeom ); }
106  GEOSGeometry* get() const { return mGeom; }
107  operator bool() const { return mGeom != 0; }
108  void reset( GEOSGeometry* geom )
109  {
110  GEOSGeom_destroy_r( geosinit.ctxt, mGeom );
111  mGeom = geom;
112  }
113 
114  private:
115  GEOSGeometry* mGeom;
116 };
117 
118 QgsGeos::QgsGeos( const QgsAbstractGeometryV2* geometry, double precision )
119  : QgsGeometryEngine( geometry ), mGeos( 0 ), mGeosPrepared( 0 ), mPrecision( precision )
120 {
121  cacheGeos();
122 }
123 
125 {
126  GEOSGeom_destroy_r( geosinit.ctxt, mGeos );
127  mGeos = 0;
128  GEOSPreparedGeom_destroy_r( geosinit.ctxt, mGeosPrepared );
129  mGeosPrepared = 0;
130 }
131 
133 {
134  GEOSGeom_destroy_r( geosinit.ctxt, mGeos );
135  mGeos = 0;
136  GEOSPreparedGeom_destroy_r( geosinit.ctxt, mGeosPrepared );
137  mGeosPrepared = 0;
138  cacheGeos();
139 }
140 
142 {
143  GEOSPreparedGeom_destroy_r( geosinit.ctxt, mGeosPrepared );
144  mGeosPrepared = 0;
145  if ( mGeos )
146  {
147  mGeosPrepared = GEOSPrepare_r( geosinit.ctxt, mGeos );
148  }
149 }
150 
151 void QgsGeos::cacheGeos() const
152 {
153  if ( !mGeometry || mGeos )
154  {
155  return;
156  }
157 
158  mGeos = asGeos( mGeometry, mPrecision );
159 }
160 
162 {
163  return overlay( geom, INTERSECTION, errorMsg );
164 }
165 
167 {
168  return overlay( geom, DIFFERENCE, errorMsg );
169 }
170 
172 {
173  return overlay( geom, UNION, errorMsg );
174 }
175 
177 {
178 
179  QVector< GEOSGeometry* > geosGeometries;
180  geosGeometries.resize( geomList.size() );
181  for ( int i = 0; i < geomList.size(); ++i )
182  {
183  geosGeometries[i] = asGeos( geomList.at( i ), mPrecision );
184  }
185 
186  GEOSGeometry* geomUnion = 0;
187  try
188  {
189  GEOSGeometry* geomCollection = createGeosCollection( GEOS_GEOMETRYCOLLECTION, geosGeometries );
190  geomUnion = GEOSUnaryUnion_r( geosinit.ctxt, geomCollection );
191  GEOSGeom_destroy_r( geosinit.ctxt, geomCollection );
192  }
194 
195  QgsAbstractGeometryV2* result = fromGeos( geomUnion );
196  GEOSGeom_destroy_r( geosinit.ctxt, geomUnion );
197  return result;
198 }
199 
201 {
202  return overlay( geom, SYMDIFFERENCE, errorMsg );
203 }
204 
205 double QgsGeos::distance( const QgsAbstractGeometryV2& geom, QString* errorMsg ) const
206 {
207  double distance = -1.0;
208  if ( !mGeos )
209  {
210  return distance;
211  }
212 
213  GEOSGeometry* otherGeosGeom = asGeos( &geom, mPrecision );
214  if ( !otherGeosGeom )
215  {
216  return distance;
217  }
218 
219  try
220  {
221  GEOSDistance_r( geosinit.ctxt, mGeos, otherGeosGeom, &distance );
222  }
223  CATCH_GEOS_WITH_ERRMSG( -1.0 )
224 
225  GEOSGeom_destroy_r( geosinit.ctxt, otherGeosGeom );
226 
227  return distance;
228 }
229 
230 bool QgsGeos::intersects( const QgsAbstractGeometryV2& geom, QString* errorMsg ) const
231 {
232  return relation( geom, INTERSECTS, errorMsg );
233 }
234 
235 bool QgsGeos::touches( const QgsAbstractGeometryV2& geom, QString* errorMsg ) const
236 {
237  return relation( geom, TOUCHES, errorMsg );
238 }
239 
240 bool QgsGeos::crosses( const QgsAbstractGeometryV2& geom, QString* errorMsg ) const
241 {
242  return relation( geom, CROSSES, errorMsg );
243 }
244 
245 bool QgsGeos::within( const QgsAbstractGeometryV2& geom, QString* errorMsg ) const
246 {
247  return relation( geom, WITHIN, errorMsg );
248 }
249 
250 bool QgsGeos::overlaps( const QgsAbstractGeometryV2& geom, QString* errorMsg ) const
251 {
252  return relation( geom, OVERLAPS, errorMsg );
253 }
254 
255 bool QgsGeos::contains( const QgsAbstractGeometryV2& geom, QString* errorMsg ) const
256 {
257  return relation( geom, CONTAINS, errorMsg );
258 }
259 
260 bool QgsGeos::disjoint( const QgsAbstractGeometryV2& geom, QString* errorMsg ) const
261 {
262  return relation( geom, DISJOINT, errorMsg );
263 }
264 
265 QString QgsGeos::relate( const QgsAbstractGeometryV2& geom, QString* errorMsg ) const
266 {
267  if ( !mGeos )
268  {
269  return QString();
270  }
271 
272  GEOSGeomScopedPtr geosGeom( asGeos( &geom, mPrecision ) );
273  if ( !geosGeom )
274  {
275  return QString();
276  }
277 
278  QString result;
279  try
280  {
281  char* r = GEOSRelate_r( geosinit.ctxt, mGeos, geosGeom.get() );
282  if ( r )
283  {
284  result = QString( r );
285  GEOSFree_r( geosinit.ctxt, r );
286  }
287  }
288  catch ( GEOSException &e )
289  {
290  if ( errorMsg )
291  {
292  *errorMsg = e.what();
293  }
294  }
295 
296  return result;
297 }
298 
299 double QgsGeos::area( QString* errorMsg ) const
300 {
301  double area = -1.0;
302  if ( !mGeos )
303  {
304  return area;
305  }
306 
307  try
308  {
309  if ( GEOSArea_r( geosinit.ctxt, mGeos, &area ) != 1 )
310  return -1.0;
311  }
312  CATCH_GEOS_WITH_ERRMSG( -1.0 );
313  return area;
314 }
315 
316 double QgsGeos::length( QString* errorMsg ) const
317 {
318  double length = -1.0;
319  if ( !mGeos )
320  {
321  return length;
322  }
323  try
324  {
325  if ( GEOSLength_r( geosinit.ctxt, mGeos, &length ) != 1 )
326  return -1.0;
327  }
328  CATCH_GEOS_WITH_ERRMSG( -1.0 )
329  return length;
330 }
331 
333  QList<QgsAbstractGeometryV2*>&newGeometries,
334  bool topological,
335  QList<QgsPointV2> &topologyTestPoints,
336  QString* errorMsg ) const
337 {
338 
339  int returnCode = 0;
340  if ( !mGeometry || !mGeos )
341  {
342  return 1;
343  }
344 
345  //return if this type is point/multipoint
346  if ( mGeometry->dimension() == 0 )
347  {
348  return 1; //cannot split points
349  }
350 
351  if ( !GEOSisValid_r( geosinit.ctxt, mGeos ) )
352  return 7;
353 
354  //make sure splitLine is valid
355  if (( mGeometry->dimension() == 1 && splitLine.numPoints() < 1 ) ||
356  ( mGeometry->dimension() == 2 && splitLine.numPoints() < 2 ) )
357  return 1;
358 
359  newGeometries.clear();
360  GEOSGeometry* splitLineGeos = 0;
361 
362  try
363  {
364  if ( splitLine.numPoints() > 1 )
365  {
366  splitLineGeos = createGeosLinestring( &splitLine, mPrecision );
367  }
368  else if ( splitLine.numPoints() == 1 )
369  {
370  QgsPointV2 pt = splitLine.pointN( 0 );
371  splitLineGeos = createGeosPoint( &pt, 2, mPrecision );
372  }
373  else
374  {
375  return 1;
376  }
377 
378  if ( !GEOSisValid_r( geosinit.ctxt, splitLineGeos ) || !GEOSisSimple_r( geosinit.ctxt, splitLineGeos ) )
379  {
380  GEOSGeom_destroy_r( geosinit.ctxt, splitLineGeos );
381  return 1;
382  }
383 
384  if ( topological )
385  {
386  //find out candidate points for topological corrections
387  if ( topologicalTestPointsSplit( splitLineGeos, topologyTestPoints ) != 0 )
388  return 1;
389  }
390 
391  //call split function depending on geometry type
392  if ( mGeometry->dimension() == 1 )
393  {
394  returnCode = splitLinearGeometry( splitLineGeos, newGeometries );
395  GEOSGeom_destroy_r( geosinit.ctxt, splitLineGeos );
396  }
397  else if ( mGeometry->dimension() == 2 )
398  {
399  returnCode = splitPolygonGeometry( splitLineGeos, newGeometries );
400  GEOSGeom_destroy_r( geosinit.ctxt, splitLineGeos );
401  }
402  else
403  {
404  return 1;
405  }
406  }
408 
409  return returnCode;
410 }
411 
412 
413 
414 int QgsGeos::topologicalTestPointsSplit( const GEOSGeometry* splitLine, QList<QgsPointV2>& testPoints, QString* errorMsg ) const
415 {
416  //Find out the intersection points between splitLineGeos and this geometry.
417  //These points need to be tested for topological correctness by the calling function
418  //if topological editing is enabled
419 
420  if ( !mGeos )
421  {
422  return 1;
423  }
424 
425  try
426  {
427  testPoints.clear();
428  GEOSGeometry* intersectionGeom = GEOSIntersection_r( geosinit.ctxt, mGeos, splitLine );
429  if ( !intersectionGeom )
430  return 1;
431 
432  bool simple = false;
433  int nIntersectGeoms = 1;
434  if ( GEOSGeomTypeId_r( geosinit.ctxt, intersectionGeom ) == GEOS_LINESTRING
435  || GEOSGeomTypeId_r( geosinit.ctxt, intersectionGeom ) == GEOS_POINT )
436  simple = true;
437 
438  if ( !simple )
439  nIntersectGeoms = GEOSGetNumGeometries_r( geosinit.ctxt, intersectionGeom );
440 
441  for ( int i = 0; i < nIntersectGeoms; ++i )
442  {
443  const GEOSGeometry* currentIntersectGeom;
444  if ( simple )
445  currentIntersectGeom = intersectionGeom;
446  else
447  currentIntersectGeom = GEOSGetGeometryN_r( geosinit.ctxt, intersectionGeom, i );
448 
449  const GEOSCoordSequence* lineSequence = GEOSGeom_getCoordSeq_r( geosinit.ctxt, currentIntersectGeom );
450  unsigned int sequenceSize = 0;
451  double x, y;
452  if ( GEOSCoordSeq_getSize_r( geosinit.ctxt, lineSequence, &sequenceSize ) != 0 )
453  {
454  for ( unsigned int i = 0; i < sequenceSize; ++i )
455  {
456  if ( GEOSCoordSeq_getX_r( geosinit.ctxt, lineSequence, i, &x ) != 0 )
457  {
458  if ( GEOSCoordSeq_getY_r( geosinit.ctxt, lineSequence, i, &y ) != 0 )
459  {
460  testPoints.push_back( QgsPointV2( x, y ) );
461  }
462  }
463  }
464  }
465  }
466  GEOSGeom_destroy_r( geosinit.ctxt, intersectionGeom );
467  }
469 
470  return 0;
471 }
472 
473 GEOSGeometry* QgsGeos::linePointDifference( GEOSGeometry* GEOSsplitPoint ) const
474 {
475  int type = GEOSGeomTypeId_r( geosinit.ctxt, mGeos );
476 
477  QgsMultiCurveV2* multiCurve = 0;
478  if ( type == GEOS_MULTILINESTRING )
479  {
480  multiCurve = dynamic_cast<QgsMultiCurveV2*>( mGeometry->clone() );
481  }
482  else if ( type == GEOS_LINESTRING )
483  {
484  multiCurve = new QgsMultiCurveV2();
485  multiCurve->addGeometry( mGeometry->clone() );
486  }
487  else
488  {
489  return 0;
490  }
491 
492  if ( !multiCurve )
493  {
494  return 0;
495  }
496 
497 
498  QgsAbstractGeometryV2* splitGeom = fromGeos( GEOSsplitPoint );
499  QgsPointV2* splitPoint = dynamic_cast<QgsPointV2*>( splitGeom );
500  if ( !splitPoint )
501  {
502  delete splitGeom; return 0;
503  }
504 
505  QgsMultiCurveV2 lines;
506 
507  //For each part
508  for ( int i = 0; i < multiCurve->numGeometries(); ++i )
509  {
510  const QgsLineStringV2* line = dynamic_cast<const QgsLineStringV2*>( multiCurve->geometryN( i ) );
511  if ( line )
512  {
513  //For each segment
514  QgsLineStringV2 newLine;
515  newLine.addVertex( line->pointN( 0 ) );
516  int nVertices = newLine.numPoints();
517  for ( int j = 1; j < ( nVertices - 1 ); ++j )
518  {
519  QgsPointV2 currentPoint = line->pointN( j );
520  newLine.addVertex( currentPoint );
521  if ( currentPoint == *splitPoint )
522  {
523  lines.addGeometry( newLine.clone() );
524  newLine = QgsLineStringV2();
525  newLine.addVertex( currentPoint );
526  }
527  }
528  newLine.addVertex( line->pointN( nVertices - 1 ) );
529  lines.addGeometry( newLine.clone() );
530  }
531  }
532 
533  delete splitGeom;
534  delete multiCurve;
535  return asGeos( &lines, mPrecision );
536 }
537 
538 int QgsGeos::splitLinearGeometry( GEOSGeometry* splitLine, QList<QgsAbstractGeometryV2*>& newGeometries ) const
539 {
540  if ( !splitLine )
541  return 2;
542 
543  if ( !mGeos )
544  return 5;
545 
546  //first test if linestring intersects geometry. If not, return straight away
547  if ( !GEOSIntersects_r( geosinit.ctxt, splitLine, mGeos ) )
548  return 1;
549 
550  //check that split line has no linear intersection
551  int linearIntersect = GEOSRelatePattern_r( geosinit.ctxt, mGeos, splitLine, "1********" );
552  if ( linearIntersect > 0 )
553  return 3;
554 
555  int splitGeomType = GEOSGeomTypeId_r( geosinit.ctxt, splitLine );
556 
557  GEOSGeometry* splitGeom;
558  if ( splitGeomType == GEOS_POINT )
559  {
560  splitGeom = linePointDifference( splitLine );
561  }
562  else
563  {
564  splitGeom = GEOSDifference_r( geosinit.ctxt, mGeos, splitLine );
565  }
566  QVector<GEOSGeometry*> lineGeoms;
567 
568  int splitType = GEOSGeomTypeId_r( geosinit.ctxt, splitGeom );
569  if ( splitType == GEOS_MULTILINESTRING )
570  {
571  int nGeoms = GEOSGetNumGeometries_r( geosinit.ctxt, splitGeom );
572  lineGeoms.reserve( nGeoms );
573  for ( int i = 0; i < nGeoms; ++i )
574  lineGeoms << GEOSGeom_clone_r( geosinit.ctxt, GEOSGetGeometryN_r( geosinit.ctxt, splitGeom, i ) );
575 
576  }
577  else
578  {
579  lineGeoms << GEOSGeom_clone_r( geosinit.ctxt, splitGeom );
580  }
581 
582  mergeGeometriesMultiTypeSplit( lineGeoms );
583 
584  for ( int i = 0; i < lineGeoms.size(); ++i )
585  {
586  newGeometries << fromGeos( lineGeoms[i] );
587  GEOSGeom_destroy_r( geosinit.ctxt, lineGeoms[i] );
588  }
589 
590  GEOSGeom_destroy_r( geosinit.ctxt, splitGeom );
591  return 0;
592 }
593 
594 int QgsGeos::splitPolygonGeometry( GEOSGeometry* splitLine, QList<QgsAbstractGeometryV2*>& newGeometries ) const
595 {
596  if ( !splitLine )
597  return 2;
598 
599  if ( !mGeos )
600  return 5;
601 
602  //first test if linestring intersects geometry. If not, return straight away
603  if ( !GEOSIntersects_r( geosinit.ctxt, splitLine, mGeos ) )
604  return 1;
605 
606  //first union all the polygon rings together (to get them noded, see JTS developer guide)
607  GEOSGeometry *nodedGeometry = nodeGeometries( splitLine, mGeos );
608  if ( !nodedGeometry )
609  return 2; //an error occured during noding
610 
611  GEOSGeometry *polygons = GEOSPolygonize_r( geosinit.ctxt, &nodedGeometry, 1 );
612  if ( !polygons || numberOfGeometries( polygons ) == 0 )
613  {
614  if ( polygons )
615  GEOSGeom_destroy_r( geosinit.ctxt, polygons );
616 
617  GEOSGeom_destroy_r( geosinit.ctxt, nodedGeometry );
618 
619  return 4;
620  }
621 
622  GEOSGeom_destroy_r( geosinit.ctxt, nodedGeometry );
623 
624  //test every polygon if contained in original geometry
625  //include in result if yes
626  QVector<GEOSGeometry*> testedGeometries;
627  GEOSGeometry *intersectGeometry = 0;
628 
629  //ratio intersect geometry / geometry. This should be close to 1
630  //if the polygon belongs to the input geometry
631 
632  for ( int i = 0; i < numberOfGeometries( polygons ); i++ )
633  {
634  const GEOSGeometry *polygon = GEOSGetGeometryN_r( geosinit.ctxt, polygons, i );
635  intersectGeometry = GEOSIntersection_r( geosinit.ctxt, mGeos, polygon );
636  if ( !intersectGeometry )
637  {
638  QgsDebugMsg( "intersectGeometry is NULL" );
639  continue;
640  }
641 
642  double intersectionArea;
643  GEOSArea_r( geosinit.ctxt, intersectGeometry, &intersectionArea );
644 
645  double polygonArea;
646  GEOSArea_r( geosinit.ctxt, polygon, &polygonArea );
647 
648  const double areaRatio = intersectionArea / polygonArea;
649  if ( areaRatio > 0.99 && areaRatio < 1.01 )
650  testedGeometries << GEOSGeom_clone_r( geosinit.ctxt, polygon );
651 
652  GEOSGeom_destroy_r( geosinit.ctxt, intersectGeometry );
653  }
654  GEOSGeom_destroy_r( geosinit.ctxt, polygons );
655 
656  bool splitDone = true;
657  int nGeometriesThis = numberOfGeometries( mGeos ); //original number of geometries
658  if ( testedGeometries.size() == nGeometriesThis )
659  {
660  splitDone = false;
661  }
662 
663  mergeGeometriesMultiTypeSplit( testedGeometries );
664 
665  //no split done, preserve original geometry
666  if ( !splitDone )
667  {
668  for ( int i = 0; i < testedGeometries.size(); ++i )
669  {
670  GEOSGeom_destroy_r( geosinit.ctxt, testedGeometries[i] );
671  }
672  return 1;
673  }
674 
675  int i;
676  for ( i = 0; i < testedGeometries.size() && GEOSisValid_r( geosinit.ctxt, testedGeometries[i] ); ++i )
677  ;
678 
679  if ( i < testedGeometries.size() )
680  {
681  for ( i = 0; i < testedGeometries.size(); ++i )
682  GEOSGeom_destroy_r( geosinit.ctxt, testedGeometries[i] );
683 
684  return 3;
685  }
686 
687  for ( i = 0; i < testedGeometries.size(); ++i )
688  newGeometries << fromGeos( testedGeometries[i] );
689 
690  return 0;
691 }
692 
693 GEOSGeometry* QgsGeos::nodeGeometries( const GEOSGeometry *splitLine, const GEOSGeometry *geom )
694 {
695  if ( !splitLine || !geom )
696  return 0;
697 
698  GEOSGeometry *geometryBoundary = 0;
699  if ( GEOSGeomTypeId_r( geosinit.ctxt, geom ) == GEOS_POLYGON || GEOSGeomTypeId_r( geosinit.ctxt, geom ) == GEOS_MULTIPOLYGON )
700  geometryBoundary = GEOSBoundary_r( geosinit.ctxt, geom );
701  else
702  geometryBoundary = GEOSGeom_clone_r( geosinit.ctxt, geom );
703 
704  GEOSGeometry *splitLineClone = GEOSGeom_clone_r( geosinit.ctxt, splitLine );
705  GEOSGeometry *unionGeometry = GEOSUnion_r( geosinit.ctxt, splitLineClone, geometryBoundary );
706  GEOSGeom_destroy_r( geosinit.ctxt, splitLineClone );
707 
708  GEOSGeom_destroy_r( geosinit.ctxt, geometryBoundary );
709  return unionGeometry;
710 }
711 
712 int QgsGeos::mergeGeometriesMultiTypeSplit( QVector<GEOSGeometry*>& splitResult ) const
713 {
714  if ( !mGeos )
715  return 1;
716 
717  //convert mGeos to geometry collection
718  int type = GEOSGeomTypeId_r( geosinit.ctxt, mGeos );
719  if ( type != GEOS_GEOMETRYCOLLECTION &&
720  type != GEOS_MULTILINESTRING &&
721  type != GEOS_MULTIPOLYGON &&
722  type != GEOS_MULTIPOINT )
723  return 0;
724 
725  QVector<GEOSGeometry*> copyList = splitResult;
726  splitResult.clear();
727 
728  //collect all the geometries that belong to the initial multifeature
729  QVector<GEOSGeometry*> unionGeom;
730 
731  for ( int i = 0; i < copyList.size(); ++i )
732  {
733  //is this geometry a part of the original multitype?
734  bool isPart = false;
735  for ( int j = 0; j < GEOSGetNumGeometries_r( geosinit.ctxt, mGeos ); j++ )
736  {
737  if ( GEOSEquals_r( geosinit.ctxt, copyList[i], GEOSGetGeometryN_r( geosinit.ctxt, mGeos, j ) ) )
738  {
739  isPart = true;
740  break;
741  }
742  }
743 
744  if ( isPart )
745  {
746  unionGeom << copyList[i];
747  }
748  else
749  {
750  QVector<GEOSGeometry*> geomVector;
751  geomVector << copyList[i];
752 
753  if ( type == GEOS_MULTILINESTRING )
754  splitResult << createGeosCollection( GEOS_MULTILINESTRING, geomVector );
755  else if ( type == GEOS_MULTIPOLYGON )
756  splitResult << createGeosCollection( GEOS_MULTIPOLYGON, geomVector );
757  else
758  GEOSGeom_destroy_r( geosinit.ctxt, copyList[i] );
759  }
760  }
761 
762  //make multifeature out of unionGeom
763  if ( unionGeom.size() > 0 )
764  {
765  if ( type == GEOS_MULTILINESTRING )
766  splitResult << createGeosCollection( GEOS_MULTILINESTRING, unionGeom );
767  else if ( type == GEOS_MULTIPOLYGON )
768  splitResult << createGeosCollection( GEOS_MULTIPOLYGON, unionGeom );
769  }
770  else
771  {
772  unionGeom.clear();
773  }
774 
775  return 0;
776 }
777 
778 GEOSGeometry* QgsGeos::createGeosCollection( int typeId, const QVector<GEOSGeometry*>& geoms )
779 {
780  int nNullGeoms = geoms.count( 0 );
781  int nNotNullGeoms = geoms.size() - nNullGeoms;
782 
783  GEOSGeometry **geomarr = new GEOSGeometry*[ nNotNullGeoms ];
784  if ( !geomarr )
785  {
786  return 0;
787  }
788 
789  int i = 0;
791  for ( ; geomIt != geoms.constEnd(); ++geomIt )
792  {
793  if ( *geomIt )
794  {
795  geomarr[i] = *geomIt;
796  ++i;
797  }
798  }
799  GEOSGeometry *geom = 0;
800 
801  try
802  {
803  geom = GEOSGeom_createCollection_r( geosinit.ctxt, typeId, geomarr, nNotNullGeoms );
804  }
805  catch ( GEOSException &e )
806  {
807  QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
808  }
809 
810  delete [] geomarr;
811 
812  return geom;
813 }
814 
815 QgsAbstractGeometryV2* QgsGeos::fromGeos( const GEOSGeometry* geos )
816 {
817  if ( !geos )
818  {
819  return 0;
820  }
821 
822  int nCoordDims = GEOSGeom_getCoordinateDimension_r( geosinit.ctxt, geos );
823  int nDims = GEOSGeom_getDimensions_r( geosinit.ctxt, geos );
824  bool hasZ = ( nCoordDims == 3 );
825  bool hasM = (( nDims - nCoordDims ) == 1 );
826 
827  switch ( GEOSGeomTypeId_r( geosinit.ctxt, geos ) )
828  {
829  case GEOS_POINT: // a point
830  {
831  const GEOSCoordSequence* cs = GEOSGeom_getCoordSeq_r( geosinit.ctxt, geos );
832  return ( coordSeqPoint( cs, 0, hasZ, hasM ).clone() );
833  }
834  case GEOS_LINESTRING:
835  {
836  return sequenceToLinestring( geos, hasZ, hasM );
837  }
838  case GEOS_POLYGON:
839  {
840  return fromGeosPolygon( geos );
841  }
842  case GEOS_MULTIPOINT:
843  {
844  QgsMultiPointV2* multiPoint = new QgsMultiPointV2();
845  int nParts = GEOSGetNumGeometries_r( geosinit.ctxt, geos );
846  for ( int i = 0; i < nParts; ++i )
847  {
848  const GEOSCoordSequence* cs = GEOSGeom_getCoordSeq_r( geosinit.ctxt, GEOSGetGeometryN_r( geosinit.ctxt, geos, i ) );
849  if ( cs )
850  {
851  multiPoint->addGeometry( coordSeqPoint( cs, 0, hasZ, hasM ).clone() );
852  }
853  }
854  return multiPoint;
855  }
856  case GEOS_MULTILINESTRING:
857  {
858  QgsMultiLineStringV2* multiLineString = new QgsMultiLineStringV2();
859  int nParts = GEOSGetNumGeometries_r( geosinit.ctxt, geos );
860  for ( int i = 0; i < nParts; ++i )
861  {
862  QgsLineStringV2* line = sequenceToLinestring( GEOSGetGeometryN_r( geosinit.ctxt, geos, i ), hasZ, hasM );
863  if ( line )
864  {
865  multiLineString->addGeometry( line );
866  }
867  }
868  return multiLineString;
869  }
870  case GEOS_MULTIPOLYGON:
871  {
872  QgsMultiPolygonV2* multiPolygon = new QgsMultiPolygonV2();
873 
874  int nParts = GEOSGetNumGeometries_r( geosinit.ctxt, geos );
875  for ( int i = 0; i < nParts; ++i )
876  {
877  QgsPolygonV2* poly = fromGeosPolygon( GEOSGetGeometryN_r( geosinit.ctxt, geos, i ) );
878  if ( poly )
879  {
880  multiPolygon->addGeometry( poly );
881  }
882  }
883  return multiPolygon;
884  }
885  case GEOS_GEOMETRYCOLLECTION:
886  {
887  QgsGeometryCollectionV2* geomCollection = new QgsGeometryCollectionV2();
888  int nParts = GEOSGetNumGeometries_r( geosinit.ctxt, geos );
889  for ( int i = 0; i < nParts; ++i )
890  {
891  QgsAbstractGeometryV2* geom = fromGeos( GEOSGetGeometryN_r( geosinit.ctxt, geos, i ) );
892  if ( geom )
893  {
894  geomCollection->addGeometry( geom );
895  }
896  }
897  return geomCollection;
898  }
899  }
900  return 0;
901 }
902 
903 QgsPolygonV2* QgsGeos::fromGeosPolygon( const GEOSGeometry* geos )
904 {
905  if ( GEOSGeomTypeId_r( geosinit.ctxt, geos ) != GEOS_POLYGON )
906  {
907  return 0;
908  }
909 
910  int nCoordDims = GEOSGeom_getCoordinateDimension_r( geosinit.ctxt, geos );
911  int nDims = GEOSGeom_getDimensions_r( geosinit.ctxt, geos );
912  bool hasZ = ( nCoordDims == 3 );
913  bool hasM = (( nDims - nCoordDims ) == 1 );
914 
915  QgsPolygonV2* polygon = new QgsPolygonV2();
916 
917  const GEOSGeometry* ring = GEOSGetExteriorRing_r( geosinit.ctxt, geos );
918  if ( ring )
919  {
920  polygon->setExteriorRing( sequenceToLinestring( ring, hasZ, hasM ) );
921  }
922 
923  QList<QgsCurveV2*> interiorRings;
924  for ( int i = 0; i < GEOSGetNumInteriorRings_r( geosinit.ctxt, geos ); ++i )
925  {
926  ring = GEOSGetInteriorRingN_r( geosinit.ctxt, geos, i );
927  if ( ring )
928  {
929  interiorRings.push_back( sequenceToLinestring( ring, hasZ, hasM ) );
930  }
931  }
932  polygon->setInteriorRings( interiorRings );
933 
934  return polygon;
935 }
936 
937 QgsLineStringV2* QgsGeos::sequenceToLinestring( const GEOSGeometry* geos, bool hasZ, bool hasM )
938 {
939  QList<QgsPointV2> pts;
940  const GEOSCoordSequence* cs = GEOSGeom_getCoordSeq_r( geosinit.ctxt, geos );
941  unsigned int nPoints;
942  GEOSCoordSeq_getSize_r( geosinit.ctxt, cs, &nPoints );
943  pts.reserve( nPoints );
944  for ( unsigned int i = 0; i < nPoints; ++i )
945  {
946  pts.push_back( coordSeqPoint( cs, i, hasZ, hasM ) );
947  }
948  QgsLineStringV2* line = new QgsLineStringV2();
949  line->setPoints( pts );
950  return line;
951 }
952 
953 int QgsGeos::numberOfGeometries( GEOSGeometry* g )
954 {
955  if ( !g )
956  return 0;
957 
958  int geometryType = GEOSGeomTypeId_r( geosinit.ctxt, g );
959  if ( geometryType == GEOS_POINT || geometryType == GEOS_LINESTRING || geometryType == GEOS_LINEARRING
960  || geometryType == GEOS_POLYGON )
961  return 1;
962 
963  //calling GEOSGetNumGeometries is save for multi types and collections also in geos2
964  return GEOSGetNumGeometries_r( geosinit.ctxt, g );
965 }
966 
967 QgsPointV2 QgsGeos::coordSeqPoint( const GEOSCoordSequence* cs, int i, bool hasZ, bool hasM )
968 {
969  if ( !cs )
970  {
971  return QgsPointV2();
972  }
973 
974  double x, y;
975  double z = 0;
976  double m = 0;
977  GEOSCoordSeq_getX_r( geosinit.ctxt, cs, i, &x );
978  GEOSCoordSeq_getY_r( geosinit.ctxt, cs, i, &y );
979  if ( hasZ )
980  {
981  GEOSCoordSeq_getZ_r( geosinit.ctxt, cs, i, &z );
982  }
983  if ( hasM )
984  {
985  GEOSCoordSeq_getOrdinate_r( geosinit.ctxt, cs, i, 3, &m );
986  }
987 
989  if ( hasZ && hasM )
990  {
992  }
993  else if ( hasZ )
994  {
996  }
997  else if ( hasM )
998  {
1000  }
1001  return QgsPointV2( t, x, y, z, m );
1002 }
1003 
1004 GEOSGeometry* QgsGeos::asGeos( const QgsAbstractGeometryV2* geom, double precision )
1005 {
1006  int coordDims = 2;
1007  if ( geom->is3D() )
1008  {
1009  ++coordDims;
1010  }
1011  if ( geom->isMeasure() )
1012  {
1013  ++coordDims;
1014  }
1015 
1016  //curve
1017  const QgsCurveV2* curve = dynamic_cast< const QgsCurveV2* >( geom );
1018  const QgsCurvePolygonV2* curvePolygon = dynamic_cast< const QgsCurvePolygonV2* >( geom );
1019  if ( curve )
1020  {
1021  QScopedPointer< QgsLineStringV2> lineString( curve->curveToLine() );
1022  return createGeosLinestring( lineString.data(), precision );
1023  }
1024  else if ( curvePolygon )
1025  {
1026  QScopedPointer<QgsPolygonV2> polygon( curvePolygon->toPolygon() );
1027  return createGeosPolygon( polygon.data(), precision );
1028  }
1029  else if ( geom->geometryType() == "Point" )
1030  {
1031  return createGeosPoint( geom, coordDims, precision );
1032  }
1033  else if ( QgsWKBTypes::isMultiType( geom->wkbType() ) )
1034  {
1035  int geosType = GEOS_MULTIPOINT;
1036  if ( geom->geometryType() == "MultiPoint" )
1037  {
1038  geosType = GEOS_MULTIPOINT;
1039  }
1040  if ( geom->geometryType() == "MultiCurve" || geom->geometryType() == "MultiLineString" )
1041  {
1042  geosType = GEOS_MULTILINESTRING;
1043  }
1044  else if ( geom->geometryType() == "MultiSurface" || geom->geometryType() == "MultiPolygon" )
1045  {
1046  geosType = GEOS_MULTIPOLYGON;
1047  }
1048  else if ( geom->geometryType() == "GeometryCollection" )
1049  {
1050  geosType = GEOS_GEOMETRYCOLLECTION;
1051  }
1052 
1053  const QgsGeometryCollectionV2* c = dynamic_cast<const QgsGeometryCollectionV2*>( geom );
1054  if ( !c )
1055  {
1056  return 0;
1057  }
1058 
1059  QVector< GEOSGeometry* > geomVector( c->numGeometries() );
1060  for ( int i = 0; i < c->numGeometries(); ++i )
1061  {
1062  geomVector[i] = asGeos( c->geometryN( i ), precision );
1063  }
1064  return createGeosCollection( geosType, geomVector );
1065  }
1066 
1067  return 0;
1068 }
1069 
1070 QgsAbstractGeometryV2* QgsGeos::overlay( const QgsAbstractGeometryV2& geom, Overlay op, QString* errorMsg ) const
1071 {
1072  if ( !mGeos )
1073  {
1074  return 0;
1075  }
1076 
1077  GEOSGeomScopedPtr geosGeom( asGeos( &geom, mPrecision ) );
1078  if ( !geosGeom )
1079  {
1080  return 0;
1081  }
1082 
1083  try
1084  {
1085  GEOSGeomScopedPtr opGeom;
1086  switch ( op )
1087  {
1088  case INTERSECTION:
1089  opGeom.reset( GEOSIntersection_r( geosinit.ctxt, mGeos, geosGeom.get() ) );
1090  break;
1091  case DIFFERENCE:
1092  opGeom.reset( GEOSDifference_r( geosinit.ctxt, mGeos, geosGeom.get() ) );
1093  break;
1094  case UNION:
1095  {
1096  GEOSGeometry *unionGeometry = GEOSUnion_r( geosinit.ctxt, mGeos, geosGeom.get() );
1097 
1098  if ( unionGeometry && GEOSGeomTypeId_r( geosinit.ctxt, unionGeometry ) == GEOS_MULTILINESTRING )
1099  {
1100  GEOSGeometry *mergedLines = GEOSLineMerge_r( geosinit.ctxt, unionGeometry );
1101  if ( mergedLines )
1102  {
1103  GEOSGeom_destroy_r( geosinit.ctxt, unionGeometry );
1104  unionGeometry = mergedLines;
1105  }
1106  }
1107 
1108  opGeom.reset( unionGeometry );
1109  }
1110  break;
1111  case SYMDIFFERENCE:
1112  opGeom.reset( GEOSSymDifference_r( geosinit.ctxt, mGeos, geosGeom.get() ) );
1113  break;
1114  default: //unknown op
1115  return 0;
1116  }
1117  QgsAbstractGeometryV2* opResult = fromGeos( opGeom.get() );
1118  return opResult;
1119  }
1120  catch ( GEOSException &e )
1121  {
1122  if ( errorMsg )
1123  {
1124  *errorMsg = e.what();
1125  }
1126  return 0;
1127  }
1128 }
1129 
1130 bool QgsGeos::relation( const QgsAbstractGeometryV2& geom, Relation r, QString* errorMsg ) const
1131 {
1132  if ( !mGeos )
1133  {
1134  return false;
1135  }
1136 
1137  GEOSGeomScopedPtr geosGeom( asGeos( &geom, mPrecision ) );
1138  if ( !geosGeom )
1139  {
1140  return false;
1141  }
1142 
1143  bool result = false;
1144  try
1145  {
1146  if ( mGeosPrepared ) //use faster version with prepared geometry
1147  {
1148  switch ( r )
1149  {
1150  case INTERSECTS:
1151  result = ( GEOSPreparedIntersects_r( geosinit.ctxt, mGeosPrepared, geosGeom.get() ) == 1 );
1152  break;
1153  case TOUCHES:
1154  result = ( GEOSPreparedTouches_r( geosinit.ctxt, mGeosPrepared, geosGeom.get() ) == 1 );
1155  break;
1156  case CROSSES:
1157  result = ( GEOSPreparedCrosses_r( geosinit.ctxt, mGeosPrepared, geosGeom.get() ) == 1 );
1158  break;
1159  case WITHIN:
1160  result = ( GEOSPreparedWithin_r( geosinit.ctxt, mGeosPrepared, geosGeom.get() ) == 1 );
1161  break;
1162  case CONTAINS:
1163  result = ( GEOSPreparedContains_r( geosinit.ctxt, mGeosPrepared, geosGeom.get() ) == 1 );
1164  break;
1165  case DISJOINT:
1166  result = ( GEOSPreparedDisjoint_r( geosinit.ctxt, mGeosPrepared, geosGeom.get() ) == 1 );
1167  break;
1168  case OVERLAPS:
1169  result = ( GEOSPreparedOverlaps_r( geosinit.ctxt, mGeosPrepared, geosGeom.get() ) == 1 );
1170  break;
1171  default:
1172  return false;
1173  }
1174  return result;
1175  }
1176 
1177  switch ( r )
1178  {
1179  case INTERSECTS:
1180  result = ( GEOSIntersects_r( geosinit.ctxt, mGeos, geosGeom.get() ) == 1 );
1181  break;
1182  case TOUCHES:
1183  result = ( GEOSTouches_r( geosinit.ctxt, mGeos, geosGeom.get() ) == 1 );
1184  break;
1185  case CROSSES:
1186  result = ( GEOSCrosses_r( geosinit.ctxt, mGeos, geosGeom.get() ) == 1 );
1187  break;
1188  case WITHIN:
1189  result = ( GEOSWithin_r( geosinit.ctxt, mGeos, geosGeom.get() ) == 1 );
1190  break;
1191  case CONTAINS:
1192  result = ( GEOSContains_r( geosinit.ctxt, mGeos, geosGeom.get() ) == 1 );
1193  break;
1194  case DISJOINT:
1195  result = ( GEOSDisjoint_r( geosinit.ctxt, mGeos, geosGeom.get() ) == 1 );
1196  break;
1197  case OVERLAPS:
1198  result = ( GEOSOverlaps_r( geosinit.ctxt, mGeos, geosGeom.get() ) == 1 );
1199  break;
1200  default:
1201  return false;
1202  }
1203  }
1204  catch ( GEOSException &e )
1205  {
1206  if ( errorMsg )
1207  {
1208  *errorMsg = e.what();
1209  }
1210  return 0;
1211  }
1212 
1213  return result;
1214 }
1215 
1216 QgsAbstractGeometryV2* QgsGeos::buffer( double distance, int segments, QString* errorMsg ) const
1217 {
1218  if ( !mGeos )
1219  {
1220  return 0;
1221  }
1222 
1223  GEOSGeomScopedPtr geos;
1224  try
1225  {
1226  geos.reset( GEOSBuffer_r( geosinit.ctxt, mGeos, distance, segments ) );
1227  }
1229  return fromGeos( geos.get() );
1230 }
1231 
1232 QgsAbstractGeometryV2 *QgsGeos::buffer( double distance, int segments, int endCapStyle, int joinStyle, double mitreLimit, QString* errorMsg ) const
1233 {
1234  if ( !mGeos )
1235  {
1236  return 0;
1237  }
1238 
1239 #if defined(GEOS_VERSION_MAJOR) && defined(GEOS_VERSION_MINOR) && \
1240  ((GEOS_VERSION_MAJOR>3) || ((GEOS_VERSION_MAJOR==3) && (GEOS_VERSION_MINOR>=3)))
1241 
1242  GEOSGeomScopedPtr geos;
1243  try
1244  {
1245  geos.reset( GEOSBufferWithStyle_r( geosinit.ctxt, mGeos, distance, segments, endCapStyle, joinStyle, mitreLimit ) );
1246  }
1248  return fromGeos( geos.get() );
1249 #else
1250  return 0;
1251 #endif //0
1252 }
1253 
1254 QgsAbstractGeometryV2* QgsGeos::simplify( double tolerance, QString* errorMsg ) const
1255 {
1256  if ( !mGeos )
1257  {
1258  return 0;
1259  }
1260  GEOSGeomScopedPtr geos;
1261  try
1262  {
1263  geos.reset( GEOSTopologyPreserveSimplify_r( geosinit.ctxt, mGeos, tolerance ) );
1264  }
1266  return fromGeos( geos.get() );
1267 }
1268 
1270 {
1271  if ( !mGeos )
1272  {
1273  return 0;
1274  }
1275  GEOSGeomScopedPtr geos;
1276  try
1277  {
1278  geos.reset( GEOSInterpolate_r( geosinit.ctxt, mGeos, distance ) );
1279  }
1281  return fromGeos( geos.get() );
1282 }
1283 
1284 bool QgsGeos::centroid( QgsPointV2& pt, QString* errorMsg ) const
1285 {
1286  if ( !mGeos )
1287  {
1288  return false;
1289  }
1290 
1291  GEOSGeomScopedPtr geos;
1292  try
1293  {
1294  geos.reset( GEOSGetCentroid_r( geosinit.ctxt, mGeos ) );
1295  }
1296  CATCH_GEOS_WITH_ERRMSG( false );
1297 
1298  if ( !geos )
1299  {
1300  return false;
1301  }
1302 
1303  double x, y;
1304  GEOSGeomGetX_r( geosinit.ctxt, geos.get(), &x );
1305  GEOSGeomGetY_r( geosinit.ctxt, geos.get(), &y );
1306  pt.setX( x ); pt.setY( y );
1307  return true;
1308 }
1309 
1311 {
1312  if ( !mGeos )
1313  {
1314  return 0;
1315  }
1316  GEOSGeomScopedPtr geos;
1317  try
1318  {
1319  geos.reset( GEOSEnvelope_r( geosinit.ctxt, mGeos ) );
1320  }
1322  return fromGeos( geos.get() );
1323 }
1324 
1325 bool QgsGeos::pointOnSurface( QgsPointV2& pt, QString* errorMsg ) const
1326 {
1327  if ( !mGeos )
1328  {
1329  return false;
1330  }
1331 
1332  GEOSGeomScopedPtr geos;
1333  try
1334  {
1335  geos.reset( GEOSPointOnSurface_r( geosinit.ctxt, mGeos ) );
1336  }
1337  CATCH_GEOS_WITH_ERRMSG( false );
1338 
1339  if ( !geos )
1340  {
1341  return false;
1342  }
1343 
1344  double x, y;
1345  GEOSGeomGetX_r( geosinit.ctxt, geos.get(), &x );
1346  GEOSGeomGetY_r( geosinit.ctxt, geos.get(), &y );
1347 
1348  pt.setX( x );
1349  pt.setY( y );
1350 
1351  return true;
1352 }
1353 
1355 {
1356  if ( !mGeos )
1357  {
1358  return 0;
1359  }
1360 
1361  try
1362  {
1363  GEOSGeometry* cHull = GEOSConvexHull_r( geosinit.ctxt, mGeos );
1364  QgsAbstractGeometryV2* cHullGeom = fromGeos( cHull );
1365  GEOSGeom_destroy_r( geosinit.ctxt, cHull );
1366  return cHullGeom;
1367  }
1369 }
1370 
1371 bool QgsGeos::isValid( QString* errorMsg ) const
1372 {
1373  if ( !mGeos )
1374  {
1375  return false;
1376  }
1377 
1378  try
1379  {
1380  return GEOSisValid_r( geosinit.ctxt, mGeos );
1381  }
1382  CATCH_GEOS_WITH_ERRMSG( false );
1383 }
1384 
1385 bool QgsGeos::isEqual( const QgsAbstractGeometryV2& geom, QString* errorMsg ) const
1386 {
1387  if ( !mGeos )
1388  {
1389  return false;
1390  }
1391 
1392  try
1393  {
1394  GEOSGeomScopedPtr geosGeom( asGeos( &geom, mPrecision ) );
1395  if ( !geosGeom )
1396  {
1397  return false;
1398  }
1399  bool equal = GEOSEquals_r( geosinit.ctxt, mGeos, geosGeom.get() );
1400  return equal;
1401  }
1402  CATCH_GEOS_WITH_ERRMSG( false );
1403 }
1404 
1405 bool QgsGeos::isEmpty( QString* errorMsg ) const
1406 {
1407  if ( !mGeos )
1408  {
1409  return false;
1410  }
1411 
1412  try
1413  {
1414  return GEOSisEmpty_r( geosinit.ctxt, mGeos );
1415  }
1416  CATCH_GEOS_WITH_ERRMSG( false );
1417 }
1418 
1419 GEOSCoordSequence* QgsGeos::createCoordinateSequence( const QgsCurveV2* curve, double precision )
1420 {
1421  bool segmentize = false;
1422  const QgsLineStringV2* line = dynamic_cast<const QgsLineStringV2*>( curve );
1423 
1424  if ( !line )
1425  {
1426  line = curve->curveToLine();
1427  segmentize = true;
1428  }
1429 
1430  if ( !line )
1431  {
1432  return 0;
1433  }
1434 
1435  bool hasZ = line->is3D();
1436  bool hasM = false; //line->isMeasure(); //disabled until geos supports m-coordinates
1437  int coordDims = 2;
1438  if ( hasZ )
1439  {
1440  ++coordDims;
1441  }
1442  if ( hasM )
1443  {
1444  ++coordDims;
1445  }
1446 
1447  int numPoints = line->numPoints();
1448  GEOSCoordSequence* coordSeq = 0;
1449  try
1450  {
1451  coordSeq = GEOSCoordSeq_create_r( geosinit.ctxt, numPoints, coordDims );
1452  if ( precision > 0. )
1453  {
1454  for ( int i = 0; i < numPoints; ++i )
1455  {
1456  QgsPointV2 pt = line->pointN( i ); //todo: create method to get const point reference
1457  GEOSCoordSeq_setX_r( geosinit.ctxt, coordSeq, i, qgsRound( pt.x() / precision ) * precision );
1458  GEOSCoordSeq_setY_r( geosinit.ctxt, coordSeq, i, qgsRound( pt.y() / precision ) * precision );
1459  if ( hasZ )
1460  {
1461  GEOSCoordSeq_setOrdinate_r( geosinit.ctxt, coordSeq, i, 2, qgsRound( pt.z() / precision ) * precision );
1462  }
1463  if ( hasM )
1464  {
1465  GEOSCoordSeq_setOrdinate_r( geosinit.ctxt, coordSeq, i, 3, pt.m() );
1466  }
1467  }
1468  }
1469  else
1470  {
1471  for ( int i = 0; i < numPoints; ++i )
1472  {
1473  QgsPointV2 pt = line->pointN( i ); //todo: create method to get const point reference
1474  GEOSCoordSeq_setX_r( geosinit.ctxt, coordSeq, i, pt.x() );
1475  GEOSCoordSeq_setY_r( geosinit.ctxt, coordSeq, i, pt.y() );
1476  if ( hasZ )
1477  {
1478  GEOSCoordSeq_setOrdinate_r( geosinit.ctxt, coordSeq, i, 2, pt.z() );
1479  }
1480  if ( hasM )
1481  {
1482  GEOSCoordSeq_setOrdinate_r( geosinit.ctxt, coordSeq, i, 3, pt.m() );
1483  }
1484  }
1485  }
1486  }
1487  CATCH_GEOS( 0 )
1488 
1489  if ( segmentize )
1490  {
1491  delete line;
1492  }
1493  return coordSeq;
1494 }
1495 
1496 GEOSGeometry* QgsGeos::createGeosPoint( const QgsAbstractGeometryV2* point, int coordDims, double precision )
1497 {
1498  const QgsPointV2* pt = dynamic_cast<const QgsPointV2*>( point );
1499  if ( !pt )
1500  return 0;
1501 
1502  GEOSGeometry* geosPoint = 0;
1503 
1504  try
1505  {
1506  GEOSCoordSequence* coordSeq = GEOSCoordSeq_create_r( geosinit.ctxt, 1, coordDims );
1507  if ( precision > 0. )
1508  {
1509  GEOSCoordSeq_setX_r( geosinit.ctxt, coordSeq, 0, qgsRound( pt->x() / precision ) * precision );
1510  GEOSCoordSeq_setY_r( geosinit.ctxt, coordSeq, 0, qgsRound( pt->y() / precision ) * precision );
1511  if ( pt->is3D() )
1512  {
1513  GEOSCoordSeq_setOrdinate_r( geosinit.ctxt, coordSeq, 0, 2, qgsRound( pt->z() / precision ) * precision );
1514  }
1515  }
1516  else
1517  {
1518  GEOSCoordSeq_setX_r( geosinit.ctxt, coordSeq, 0, pt->x() );
1519  GEOSCoordSeq_setY_r( geosinit.ctxt, coordSeq, 0, pt->y() );
1520  if ( pt->is3D() )
1521  {
1522  GEOSCoordSeq_setOrdinate_r( geosinit.ctxt, coordSeq, 0, 2, pt->z() );
1523  }
1524  }
1525  if ( 0 /*pt->isMeasure()*/ ) //disabled until geos supports m-coordinates
1526  {
1527  GEOSCoordSeq_setOrdinate_r( geosinit.ctxt, coordSeq, 0, 3, pt->m() );
1528  }
1529  geosPoint = GEOSGeom_createPoint_r( geosinit.ctxt, coordSeq );
1530  }
1531  CATCH_GEOS( 0 )
1532  return geosPoint;
1533 }
1534 
1535 GEOSGeometry* QgsGeos::createGeosLinestring( const QgsAbstractGeometryV2* curve , double precision )
1536 {
1537  const QgsCurveV2* c = dynamic_cast<const QgsCurveV2*>( curve );
1538  if ( !c )
1539  return 0;
1540 
1541  GEOSCoordSequence* coordSeq = createCoordinateSequence( c, precision );
1542  if ( !coordSeq )
1543  return 0;
1544 
1545  GEOSGeometry* geosGeom = 0;
1546  try
1547  {
1548  geosGeom = GEOSGeom_createLineString_r( geosinit.ctxt, coordSeq );
1549  }
1550  CATCH_GEOS( 0 )
1551  return geosGeom;
1552 }
1553 
1554 GEOSGeometry* QgsGeos::createGeosPolygon( const QgsAbstractGeometryV2* poly , double precision )
1555 {
1556  const QgsCurvePolygonV2* polygon = dynamic_cast<const QgsCurvePolygonV2*>( poly );
1557  if ( !polygon )
1558  return 0;
1559 
1560  const QgsCurveV2* exteriorRing = polygon->exteriorRing();
1561  if ( !exteriorRing )
1562  {
1563  return 0;
1564  }
1565 
1566  GEOSGeometry* geosPolygon = 0;
1567  try
1568  {
1569  GEOSGeometry* exteriorRingGeos = GEOSGeom_createLinearRing_r( geosinit.ctxt, createCoordinateSequence( exteriorRing, precision ) );
1570 
1571 
1572  int nHoles = polygon->numInteriorRings();
1573  GEOSGeometry** holes = 0;
1574  if ( nHoles > 0 )
1575  {
1576  holes = new GEOSGeometry*[ nHoles ];
1577  }
1578 
1579  for ( int i = 0; i < nHoles; ++i )
1580  {
1581  const QgsCurveV2* interiorRing = polygon->interiorRing( i );
1582  holes[i] = GEOSGeom_createLinearRing_r( geosinit.ctxt, createCoordinateSequence( interiorRing, precision ) );
1583  }
1584  geosPolygon = GEOSGeom_createPolygon_r( geosinit.ctxt, exteriorRingGeos, holes, nHoles );
1585  delete[] holes;
1586  }
1587  CATCH_GEOS( 0 )
1588 
1589  return geosPolygon;
1590 }
1591 
1592 QgsAbstractGeometryV2* QgsGeos::offsetCurve( double distance, int segments, int joinStyle, double mitreLimit, QString* errorMsg ) const
1593 {
1594  if ( !mGeos )
1595  return 0;
1596 
1597  GEOSGeometry* offset = 0;
1598  try
1599  {
1600  offset = GEOSOffsetCurve_r( geosinit.ctxt, mGeos, distance, segments, joinStyle, mitreLimit );
1601  }
1603  QgsAbstractGeometryV2* offsetGeom = fromGeos( offset );
1604  GEOSGeom_destroy_r( geosinit.ctxt, offset );
1605  return offsetGeom;
1606 }
1607 
1608 QgsAbstractGeometryV2* QgsGeos::reshapeGeometry( const QgsLineStringV2& reshapeWithLine, int* errorCode, QString* errorMsg ) const
1609 {
1610  if ( !mGeos || reshapeWithLine.numPoints() < 2 || mGeometry->dimension() == 0 )
1611  {
1612  if ( errorCode ) { *errorCode = 1; }
1613  return 0;
1614  }
1615 
1616  GEOSGeometry* reshapeLineGeos = createGeosLinestring( &reshapeWithLine, mPrecision );
1617 
1618  //single or multi?
1619  int numGeoms = GEOSGetNumGeometries_r( geosinit.ctxt, mGeos );
1620  if ( numGeoms == -1 )
1621  {
1622  if ( errorCode ) { *errorCode = 1; }
1623  GEOSGeom_destroy_r( geosinit.ctxt, reshapeLineGeos );
1624  return 0;
1625  }
1626 
1627  bool isMultiGeom = false;
1628  int geosTypeId = GEOSGeomTypeId_r( geosinit.ctxt, mGeos );
1629  if ( geosTypeId == GEOS_MULTILINESTRING || geosTypeId == GEOS_MULTIPOLYGON )
1630  isMultiGeom = true;
1631 
1632  bool isLine = ( mGeometry->dimension() == 1 );
1633 
1634  if ( !isMultiGeom )
1635  {
1636  GEOSGeometry* reshapedGeometry;
1637  if ( isLine )
1638  {
1639  reshapedGeometry = reshapeLine( mGeos, reshapeLineGeos, mPrecision );
1640  }
1641  else
1642  {
1643  reshapedGeometry = reshapePolygon( mGeos, reshapeLineGeos, mPrecision );
1644  }
1645 
1646  if ( errorCode ) { *errorCode = 0; }
1647  QgsAbstractGeometryV2* reshapeResult = fromGeos( reshapedGeometry );
1648  GEOSGeom_destroy_r( geosinit.ctxt, reshapedGeometry );
1649  GEOSGeom_destroy_r( geosinit.ctxt, reshapeLineGeos );
1650  return reshapeResult;
1651  }
1652  else
1653  {
1654  try
1655  {
1656  //call reshape for each geometry part and replace mGeos with new geometry if reshape took place
1657  bool reshapeTookPlace = false;
1658 
1659  GEOSGeometry* currentReshapeGeometry = 0;
1660  GEOSGeometry** newGeoms = new GEOSGeometry*[numGeoms];
1661 
1662  for ( int i = 0; i < numGeoms; ++i )
1663  {
1664  if ( isLine )
1665  currentReshapeGeometry = reshapeLine( GEOSGetGeometryN_r( geosinit.ctxt, mGeos, i ), reshapeLineGeos, mPrecision );
1666  else
1667  currentReshapeGeometry = reshapePolygon( GEOSGetGeometryN_r( geosinit.ctxt, mGeos, i ), reshapeLineGeos, mPrecision );
1668 
1669  if ( currentReshapeGeometry )
1670  {
1671  newGeoms[i] = currentReshapeGeometry;
1672  reshapeTookPlace = true;
1673  }
1674  else
1675  {
1676  newGeoms[i] = GEOSGeom_clone_r( geosinit.ctxt, GEOSGetGeometryN_r( geosinit.ctxt, mGeos, i ) );
1677  }
1678  }
1679  GEOSGeom_destroy_r( geosinit.ctxt, reshapeLineGeos );
1680 
1681  GEOSGeometry* newMultiGeom = 0;
1682  if ( isLine )
1683  {
1684  newMultiGeom = GEOSGeom_createCollection_r( geosinit.ctxt, GEOS_MULTILINESTRING, newGeoms, numGeoms );
1685  }
1686  else //multipolygon
1687  {
1688  newMultiGeom = GEOSGeom_createCollection_r( geosinit.ctxt, GEOS_MULTIPOLYGON, newGeoms, numGeoms );
1689  }
1690 
1691  delete[] newGeoms;
1692  if ( !newMultiGeom )
1693  {
1694  if ( errorCode ) { *errorCode = 3; }
1695  return 0;
1696  }
1697 
1698  if ( reshapeTookPlace )
1699  {
1700  if ( errorCode ) { *errorCode = 0; }
1701  QgsAbstractGeometryV2* reshapedMultiGeom = fromGeos( newMultiGeom );
1702  GEOSGeom_destroy_r( geosinit.ctxt, newMultiGeom );
1703  return reshapedMultiGeom;
1704  }
1705  else
1706  {
1707  GEOSGeom_destroy_r( geosinit.ctxt, newMultiGeom );
1708  if ( errorCode ) { *errorCode = 1; }
1709  return 0;
1710  }
1711  }
1713  }
1714 }
1715 
1716 GEOSGeometry* QgsGeos::reshapeLine( const GEOSGeometry* line, const GEOSGeometry* reshapeLineGeos , double precision )
1717 {
1718  if ( !line || !reshapeLineGeos )
1719  return 0;
1720 
1721  bool atLeastTwoIntersections = false;
1722 
1723  try
1724  {
1725  //make sure there are at least two intersection between line and reshape geometry
1726  GEOSGeometry* intersectGeom = GEOSIntersection_r( geosinit.ctxt, line, reshapeLineGeos );
1727  if ( intersectGeom )
1728  {
1729  atLeastTwoIntersections = ( GEOSGeomTypeId_r( geosinit.ctxt, intersectGeom ) == GEOS_MULTIPOINT
1730  && GEOSGetNumGeometries_r( geosinit.ctxt, intersectGeom ) > 1 );
1731  GEOSGeom_destroy_r( geosinit.ctxt, intersectGeom );
1732  }
1733  }
1734  catch ( GEOSException &e )
1735  {
1736  QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
1737  atLeastTwoIntersections = false;
1738  }
1739 
1740  if ( !atLeastTwoIntersections )
1741  return 0;
1742 
1743  //begin and end point of original line
1744  const GEOSCoordSequence* lineCoordSeq = GEOSGeom_getCoordSeq_r( geosinit.ctxt, line );
1745  if ( !lineCoordSeq )
1746  return 0;
1747 
1748  unsigned int lineCoordSeqSize;
1749  if ( GEOSCoordSeq_getSize_r( geosinit.ctxt, lineCoordSeq, &lineCoordSeqSize ) == 0 )
1750  return 0;
1751 
1752  if ( lineCoordSeqSize < 2 )
1753  return 0;
1754 
1755  //first and last vertex of line
1756  double x1, y1, x2, y2;
1757  GEOSCoordSeq_getX_r( geosinit.ctxt, lineCoordSeq, 0, &x1 );
1758  GEOSCoordSeq_getY_r( geosinit.ctxt, lineCoordSeq, 0, &y1 );
1759  GEOSCoordSeq_getX_r( geosinit.ctxt, lineCoordSeq, lineCoordSeqSize - 1, &x2 );
1760  GEOSCoordSeq_getY_r( geosinit.ctxt, lineCoordSeq, lineCoordSeqSize - 1, &y2 );
1761  QgsPointV2 beginPoint( x1, y1 );
1762  GEOSGeometry* beginLineVertex = createGeosPoint( &beginPoint, 2, precision );
1763  QgsPointV2 endPoint( x2, y2 );
1764  GEOSGeometry* endLineVertex = createGeosPoint( &endPoint, 2, precision );
1765 
1766  bool isRing = false;
1767  if ( GEOSGeomTypeId_r( geosinit.ctxt, line ) == GEOS_LINEARRING
1768  || GEOSEquals_r( geosinit.ctxt, beginLineVertex, endLineVertex ) == 1 )
1769  isRing = true;
1770 
1771  //node line and reshape line
1772  GEOSGeometry* nodedGeometry = nodeGeometries( reshapeLineGeos, line );
1773  if ( !nodedGeometry )
1774  {
1775  GEOSGeom_destroy_r( geosinit.ctxt, beginLineVertex );
1776  GEOSGeom_destroy_r( geosinit.ctxt, endLineVertex );
1777  return 0;
1778  }
1779 
1780  //and merge them together
1781  GEOSGeometry *mergedLines = GEOSLineMerge_r( geosinit.ctxt, nodedGeometry );
1782  GEOSGeom_destroy_r( geosinit.ctxt, nodedGeometry );
1783  if ( !mergedLines )
1784  {
1785  GEOSGeom_destroy_r( geosinit.ctxt, beginLineVertex );
1786  GEOSGeom_destroy_r( geosinit.ctxt, endLineVertex );
1787  return 0;
1788  }
1789 
1790  int numMergedLines = GEOSGetNumGeometries_r( geosinit.ctxt, mergedLines );
1791  if ( numMergedLines < 2 ) //some special cases. Normally it is >2
1792  {
1793  GEOSGeom_destroy_r( geosinit.ctxt, beginLineVertex );
1794  GEOSGeom_destroy_r( geosinit.ctxt, endLineVertex );
1795  if ( numMergedLines == 1 ) //reshape line is from begin to endpoint. So we keep the reshapeline
1796  return GEOSGeom_clone_r( geosinit.ctxt, reshapeLineGeos );
1797  else
1798  return 0;
1799  }
1800 
1801  QList<GEOSGeometry*> resultLineParts; //collection with the line segments that will be contained in result
1802  QList<GEOSGeometry*> probableParts; //parts where we can decide on inclusion only after going through all the candidates
1803 
1804  for ( int i = 0; i < numMergedLines; ++i )
1805  {
1806  const GEOSGeometry* currentGeom;
1807 
1808  currentGeom = GEOSGetGeometryN_r( geosinit.ctxt, mergedLines, i );
1809  const GEOSCoordSequence* currentCoordSeq = GEOSGeom_getCoordSeq_r( geosinit.ctxt, currentGeom );
1810  unsigned int currentCoordSeqSize;
1811  GEOSCoordSeq_getSize_r( geosinit.ctxt, currentCoordSeq, &currentCoordSeqSize );
1812  if ( currentCoordSeqSize < 2 )
1813  continue;
1814 
1815  //get the two endpoints of the current line merge result
1816  double xBegin, xEnd, yBegin, yEnd;
1817  GEOSCoordSeq_getX_r( geosinit.ctxt, currentCoordSeq, 0, &xBegin );
1818  GEOSCoordSeq_getY_r( geosinit.ctxt, currentCoordSeq, 0, &yBegin );
1819  GEOSCoordSeq_getX_r( geosinit.ctxt, currentCoordSeq, currentCoordSeqSize - 1, &xEnd );
1820  GEOSCoordSeq_getY_r( geosinit.ctxt, currentCoordSeq, currentCoordSeqSize - 1, &yEnd );
1821  QgsPointV2 beginPoint( xBegin, yBegin );
1822  GEOSGeometry* beginCurrentGeomVertex = createGeosPoint( &beginPoint, 2, precision );
1823  QgsPointV2 endPoint( xEnd, yEnd );
1824  GEOSGeometry* endCurrentGeomVertex = createGeosPoint( &endPoint, 2, precision );
1825 
1826  //check how many endpoints of the line merge result are on the (original) line
1827  int nEndpointsOnOriginalLine = 0;
1828  if ( pointContainedInLine( beginCurrentGeomVertex, line ) == 1 )
1829  nEndpointsOnOriginalLine += 1;
1830 
1831  if ( pointContainedInLine( endCurrentGeomVertex, line ) == 1 )
1832  nEndpointsOnOriginalLine += 1;
1833 
1834  //check how many endpoints equal the endpoints of the original line
1835  int nEndpointsSameAsOriginalLine = 0;
1836  if ( GEOSEquals_r( geosinit.ctxt, beginCurrentGeomVertex, beginLineVertex ) == 1
1837  || GEOSEquals_r( geosinit.ctxt, beginCurrentGeomVertex, endLineVertex ) == 1 )
1838  nEndpointsSameAsOriginalLine += 1;
1839 
1840  if ( GEOSEquals_r( geosinit.ctxt, endCurrentGeomVertex, beginLineVertex ) == 1
1841  || GEOSEquals_r( geosinit.ctxt, endCurrentGeomVertex, endLineVertex ) == 1 )
1842  nEndpointsSameAsOriginalLine += 1;
1843 
1844  //check if the current geometry overlaps the original geometry (GEOSOverlap does not seem to work with linestrings)
1845  bool currentGeomOverlapsOriginalGeom = false;
1846  bool currentGeomOverlapsReshapeLine = false;
1847  if ( lineContainedInLine( currentGeom, line ) == 1 )
1848  currentGeomOverlapsOriginalGeom = true;
1849 
1850  if ( lineContainedInLine( currentGeom, reshapeLineGeos ) == 1 )
1851  currentGeomOverlapsReshapeLine = true;
1852 
1853  //logic to decide if this part belongs to the result
1854  if ( nEndpointsSameAsOriginalLine == 1 && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
1855  {
1856  resultLineParts.push_back( GEOSGeom_clone_r( geosinit.ctxt, currentGeom ) );
1857  }
1858  //for closed rings, we take one segment from the candidate list
1859  else if ( isRing && nEndpointsOnOriginalLine == 2 && currentGeomOverlapsOriginalGeom )
1860  {
1861  probableParts.push_back( GEOSGeom_clone_r( geosinit.ctxt, currentGeom ) );
1862  }
1863  else if ( nEndpointsOnOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
1864  {
1865  resultLineParts.push_back( GEOSGeom_clone_r( geosinit.ctxt, currentGeom ) );
1866  }
1867  else if ( nEndpointsSameAsOriginalLine == 2 && !currentGeomOverlapsOriginalGeom )
1868  {
1869  resultLineParts.push_back( GEOSGeom_clone_r( geosinit.ctxt, currentGeom ) );
1870  }
1871  else if ( currentGeomOverlapsOriginalGeom && currentGeomOverlapsReshapeLine )
1872  {
1873  resultLineParts.push_back( GEOSGeom_clone_r( geosinit.ctxt, currentGeom ) );
1874  }
1875 
1876  GEOSGeom_destroy_r( geosinit.ctxt, beginCurrentGeomVertex );
1877  GEOSGeom_destroy_r( geosinit.ctxt, endCurrentGeomVertex );
1878  }
1879 
1880  //add the longest segment from the probable list for rings (only used for polygon rings)
1881  if ( isRing && probableParts.size() > 0 )
1882  {
1883  GEOSGeometry* maxGeom = 0; //the longest geometry in the probabla list
1884  GEOSGeometry* currentGeom = 0;
1885  double maxLength = -std::numeric_limits<double>::max();
1886  double currentLength = 0;
1887  for ( int i = 0; i < probableParts.size(); ++i )
1888  {
1889  currentGeom = probableParts.at( i );
1890  GEOSLength_r( geosinit.ctxt, currentGeom, &currentLength );
1891  if ( currentLength > maxLength )
1892  {
1893  maxLength = currentLength;
1894  GEOSGeom_destroy_r( geosinit.ctxt, maxGeom );
1895  maxGeom = currentGeom;
1896  }
1897  else
1898  {
1899  GEOSGeom_destroy_r( geosinit.ctxt, currentGeom );
1900  }
1901  }
1902  resultLineParts.push_back( maxGeom );
1903  }
1904 
1905  GEOSGeom_destroy_r( geosinit.ctxt, beginLineVertex );
1906  GEOSGeom_destroy_r( geosinit.ctxt, endLineVertex );
1907  GEOSGeom_destroy_r( geosinit.ctxt, mergedLines );
1908 
1909  GEOSGeometry* result = 0;
1910  if ( resultLineParts.size() < 1 )
1911  return 0;
1912 
1913  if ( resultLineParts.size() == 1 ) //the whole result was reshaped
1914  {
1915  result = resultLineParts[0];
1916  }
1917  else //>1
1918  {
1919  GEOSGeometry **lineArray = new GEOSGeometry*[resultLineParts.size()];
1920  for ( int i = 0; i < resultLineParts.size(); ++i )
1921  {
1922  lineArray[i] = resultLineParts[i];
1923  }
1924 
1925  //create multiline from resultLineParts
1926  GEOSGeometry* multiLineGeom = GEOSGeom_createCollection_r( geosinit.ctxt, GEOS_MULTILINESTRING, lineArray, resultLineParts.size() );
1927  delete [] lineArray;
1928 
1929  //then do a linemerge with the newly combined partstrings
1930  result = GEOSLineMerge_r( geosinit.ctxt, multiLineGeom );
1931  GEOSGeom_destroy_r( geosinit.ctxt, multiLineGeom );
1932  }
1933 
1934  //now test if the result is a linestring. Otherwise something went wrong
1935  if ( GEOSGeomTypeId_r( geosinit.ctxt, result ) != GEOS_LINESTRING )
1936  {
1937  GEOSGeom_destroy_r( geosinit.ctxt, result );
1938  return 0;
1939  }
1940 
1941  return result;
1942 }
1943 
1944 GEOSGeometry* QgsGeos::reshapePolygon( const GEOSGeometry* polygon, const GEOSGeometry* reshapeLineGeos, double precision )
1945 {
1946  //go through outer shell and all inner rings and check if there is exactly one intersection of a ring and the reshape line
1947  int nIntersections = 0;
1948  int lastIntersectingRing = -2;
1949  const GEOSGeometry* lastIntersectingGeom = 0;
1950 
1951  int nRings = GEOSGetNumInteriorRings_r( geosinit.ctxt, polygon );
1952  if ( nRings < 0 )
1953  return 0;
1954 
1955  //does outer ring intersect?
1956  const GEOSGeometry* outerRing = GEOSGetExteriorRing_r( geosinit.ctxt, polygon );
1957  if ( GEOSIntersects_r( geosinit.ctxt, outerRing, reshapeLineGeos ) == 1 )
1958  {
1959  ++nIntersections;
1960  lastIntersectingRing = -1;
1961  lastIntersectingGeom = outerRing;
1962  }
1963 
1964  //do inner rings intersect?
1965  const GEOSGeometry **innerRings = new const GEOSGeometry*[nRings];
1966 
1967  try
1968  {
1969  for ( int i = 0; i < nRings; ++i )
1970  {
1971  innerRings[i] = GEOSGetInteriorRingN_r( geosinit.ctxt, polygon, i );
1972  if ( GEOSIntersects_r( geosinit.ctxt, innerRings[i], reshapeLineGeos ) == 1 )
1973  {
1974  ++nIntersections;
1975  lastIntersectingRing = i;
1976  lastIntersectingGeom = innerRings[i];
1977  }
1978  }
1979  }
1980  catch ( GEOSException &e )
1981  {
1982  QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
1983  nIntersections = 0;
1984  }
1985 
1986  if ( nIntersections != 1 ) //reshape line is only allowed to intersect one ring
1987  {
1988  delete [] innerRings;
1989  return 0;
1990  }
1991 
1992  //we have one intersecting ring, let's try to reshape it
1993  GEOSGeometry* reshapeResult = reshapeLine( lastIntersectingGeom, reshapeLineGeos, precision );
1994  if ( !reshapeResult )
1995  {
1996  delete [] innerRings;
1997  return 0;
1998  }
1999 
2000  //if reshaping took place, we need to reassemble the polygon and its rings
2001  GEOSGeometry* newRing = 0;
2002  const GEOSCoordSequence* reshapeSequence = GEOSGeom_getCoordSeq_r( geosinit.ctxt, reshapeResult );
2003  GEOSCoordSequence* newCoordSequence = GEOSCoordSeq_clone_r( geosinit.ctxt, reshapeSequence );
2004 
2005  GEOSGeom_destroy_r( geosinit.ctxt, reshapeResult );
2006 
2007  newRing = GEOSGeom_createLinearRing_r( geosinit.ctxt, newCoordSequence );
2008  if ( !newRing )
2009  {
2010  delete [] innerRings;
2011  return 0;
2012  }
2013 
2014  GEOSGeometry* newOuterRing = 0;
2015  if ( lastIntersectingRing == -1 )
2016  newOuterRing = newRing;
2017  else
2018  newOuterRing = GEOSGeom_clone_r( geosinit.ctxt, outerRing );
2019 
2020  //check if all the rings are still inside the outer boundary
2021  QList<GEOSGeometry*> ringList;
2022  if ( nRings > 0 )
2023  {
2024  GEOSGeometry* outerRingPoly = GEOSGeom_createPolygon_r( geosinit.ctxt, GEOSGeom_clone_r( geosinit.ctxt, newOuterRing ), 0, 0 );
2025  if ( outerRingPoly )
2026  {
2027  GEOSGeometry* currentRing = 0;
2028  for ( int i = 0; i < nRings; ++i )
2029  {
2030  if ( lastIntersectingRing == i )
2031  currentRing = newRing;
2032  else
2033  currentRing = GEOSGeom_clone_r( geosinit.ctxt, innerRings[i] );
2034 
2035  //possibly a ring is no longer contained in the result polygon after reshape
2036  if ( GEOSContains_r( geosinit.ctxt, outerRingPoly, currentRing ) == 1 )
2037  ringList.push_back( currentRing );
2038  else
2039  GEOSGeom_destroy_r( geosinit.ctxt, currentRing );
2040  }
2041  }
2042  GEOSGeom_destroy_r( geosinit.ctxt, outerRingPoly );
2043  }
2044 
2045  GEOSGeometry** newInnerRings = new GEOSGeometry*[ringList.size()];
2046  for ( int i = 0; i < ringList.size(); ++i )
2047  newInnerRings[i] = ringList.at( i );
2048 
2049  delete [] innerRings;
2050 
2051  GEOSGeometry* reshapedPolygon = GEOSGeom_createPolygon_r( geosinit.ctxt, newOuterRing, newInnerRings, ringList.size() );
2052  delete[] newInnerRings;
2053 
2054  return reshapedPolygon;
2055 }
2056 
2057 int QgsGeos::lineContainedInLine( const GEOSGeometry* line1, const GEOSGeometry* line2 )
2058 {
2059  if ( !line1 || !line2 )
2060  {
2061  return -1;
2062  }
2063 
2064  double bufferDistance = pow( 10.0L, geomDigits( line2 ) - 11 );
2065 
2066  GEOSGeometry* bufferGeom = GEOSBuffer_r( geosinit.ctxt, line2, bufferDistance, DEFAULT_QUADRANT_SEGMENTS );
2067  if ( !bufferGeom )
2068  return -2;
2069 
2070  GEOSGeometry* intersectionGeom = GEOSIntersection_r( geosinit.ctxt, bufferGeom, line1 );
2071 
2072  //compare ratio between line1Length and intersectGeomLength (usually close to 1 if line1 is contained in line2)
2073  double intersectGeomLength;
2074  double line1Length;
2075 
2076  GEOSLength_r( geosinit.ctxt, intersectionGeom, &intersectGeomLength );
2077  GEOSLength_r( geosinit.ctxt, line1, &line1Length );
2078 
2079  GEOSGeom_destroy_r( geosinit.ctxt, bufferGeom );
2080  GEOSGeom_destroy_r( geosinit.ctxt, intersectionGeom );
2081 
2082  double intersectRatio = line1Length / intersectGeomLength;
2083  if ( intersectRatio > 0.9 && intersectRatio < 1.1 )
2084  return 1;
2085 
2086  return 0;
2087 }
2088 
2089 int QgsGeos::pointContainedInLine( const GEOSGeometry* point, const GEOSGeometry* line )
2090 {
2091  if ( !point || !line )
2092  return -1;
2093 
2094  double bufferDistance = pow( 10.0L, geomDigits( line ) - 11 );
2095 
2096  GEOSGeometry* lineBuffer = GEOSBuffer_r( geosinit.ctxt, line, bufferDistance, 8 );
2097  if ( !lineBuffer )
2098  return -2;
2099 
2100  bool contained = false;
2101  if ( GEOSContains_r( geosinit.ctxt, lineBuffer, point ) == 1 )
2102  contained = true;
2103 
2104  GEOSGeom_destroy_r( geosinit.ctxt, lineBuffer );
2105  return contained;
2106 }
2107 
2108 int QgsGeos::geomDigits( const GEOSGeometry* geom )
2109 {
2110  GEOSGeomScopedPtr bbox( GEOSEnvelope_r( geosinit.ctxt, geom ) );
2111  if ( !bbox.get() )
2112  return -1;
2113 
2114  const GEOSGeometry* bBoxRing = GEOSGetExteriorRing_r( geosinit.ctxt, bbox.get() );
2115  if ( !bBoxRing )
2116  return -1;
2117 
2118  const GEOSCoordSequence* bBoxCoordSeq = GEOSGeom_getCoordSeq_r( geosinit.ctxt, bBoxRing );
2119 
2120  if ( !bBoxCoordSeq )
2121  return -1;
2122 
2123  unsigned int nCoords = 0;
2124  if ( !GEOSCoordSeq_getSize_r( geosinit.ctxt, bBoxCoordSeq, &nCoords ) )
2125  return -1;
2126 
2127  int maxDigits = -1;
2128  for ( unsigned int i = 0; i < nCoords - 1; ++i )
2129  {
2130  double t;
2131  GEOSCoordSeq_getX_r( geosinit.ctxt, bBoxCoordSeq, i, &t );
2132 
2133  int digits;
2134  digits = ceil( log10( fabs( t ) ) );
2135  if ( digits > maxDigits )
2136  maxDigits = digits;
2137 
2138  GEOSCoordSeq_getY_r( geosinit.ctxt, bBoxCoordSeq, i, &t );
2139  digits = ceil( log10( fabs( t ) ) );
2140  if ( digits > maxDigits )
2141  maxDigits = digits;
2142  }
2143 
2144  return maxDigits;
2145 }
2146 
2147 GEOSContextHandle_t QgsGeos::getGEOSHandler()
2148 {
2149  return geosinit.ctxt;
2150 }
virtual bool addGeometry(QgsAbstractGeometryV2 *g) override
Adds a geometry and takes ownership.
void clear()
void reset(GEOSGeometry *geom)
Definition: qgsgeos.cpp:108
QgsWKBTypes::Type wkbType() const
Returns the WKB type of the geometry.
double length(QString *errorMsg=0) const override
Definition: qgsgeos.cpp:316
static bool isMultiType(Type type)
Definition: qgswkbtypes.cpp:75
QgsPointV2 pointN(int i) const
bool intersects(const QgsAbstractGeometryV2 &geom, QString *errorMsg=0) const override
Definition: qgsgeos.cpp:230
#define CATCH_GEOS(r)
Definition: qgsgeos.cpp:34
QgsAbstractGeometryV2 * interpolate(double distance, QString *errorMsg=0) const override
Definition: qgsgeos.cpp:1269
bool crosses(const QgsAbstractGeometryV2 &geom, QString *errorMsg=0) const override
Definition: qgsgeos.cpp:240
static QgsPointV2 coordSeqPoint(const GEOSCoordSequence *cs, int i, bool hasZ, bool hasM)
Definition: qgsgeos.cpp:967
double x() const
Definition: qgspointv2.h:42
void push_back(const T &value)
virtual bool addGeometry(QgsAbstractGeometryV2 *g) override
Adds a geometry and takes ownership.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
Multi curve geometry collection.
void reserve(int alloc)
const_iterator constEnd() const
bool centroid(QgsPointV2 &pt, QString *errorMsg=0) const override
Definition: qgsgeos.cpp:1284
const T & at(int i) const
QgsCurveV2 * exteriorRing() const
Abstract base class for all geometries.
static QgsAbstractGeometryV2 * fromGeos(const GEOSGeometry *geos)
Create a geometry from a GEOSGeometry.
Definition: qgsgeos.cpp:815
void setX(double x)
Definition: qgspointv2.h:47
bool pointOnSurface(QgsPointV2 &pt, QString *errorMsg=0) const override
Definition: qgsgeos.cpp:1325
~QgsGeos()
Definition: qgsgeos.cpp:124
QgsGeos(const QgsAbstractGeometryV2 *geometry, double precision=0)
GEOS geometry engine constructor.
Definition: qgsgeos.cpp:118
Multi point geometry collection.
bool disjoint(const QgsAbstractGeometryV2 &geom, QString *errorMsg=0) const override
Definition: qgsgeos.cpp:260
virtual bool addGeometry(QgsAbstractGeometryV2 *g) override
Adds a geometry and takes ownership.
bool contains(const QgsAbstractGeometryV2 &geom, QString *errorMsg=0) const override
Definition: qgsgeos.cpp:255
QgsAbstractGeometryV2 * symDifference(const QgsAbstractGeometryV2 &geom, QString *errorMsg=0) const override
Definition: qgsgeos.cpp:200
double area(QString *errorMsg=0) const override
Definition: qgsgeos.cpp:299
Multi line string geometry collection.
QString tr(const char *sourceText, const char *disambiguation, int n)
void setY(double y)
Definition: qgspointv2.h:48
static GEOSContextHandle_t getGEOSHandler()
Definition: qgsgeos.cpp:2147
int size() const
GEOSGeometry * get() const
Definition: qgsgeos.cpp:106
double y() const
Definition: qgspointv2.h:43
double ANALYSIS_EXPORT max(double x, double y)
Returns the maximum of two doubles or the first argument if both are equal.
Polygon geometry type.
Definition: qgspolygonv2.h:29
#define CATCH_GEOS_WITH_ERRMSG(r)
Definition: qgsgeos.cpp:41
double qgsRound(double x)
Definition: qgis.h:295
void clear()
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
void setInteriorRings(const QList< QgsCurveV2 * > &rings)
Sets all interior rings (takes ownership)
GEOSGeomScopedPtr(GEOSGeometry *geom=0)
Definition: qgsgeos.cpp:104
QString fromUtf8(const char *str, int size)
void resize(int size)
bool overlaps(const QgsAbstractGeometryV2 &geom, QString *errorMsg=0) const override
Definition: qgsgeos.cpp:250
static QgsPolygonV2 * fromGeosPolygon(const GEOSGeometry *geos)
Definition: qgsgeos.cpp:903
bool isMeasure() const
Returns true if the geometry contains m values.
double z() const
Definition: qgspointv2.h:44
Line string geometry type.
QgsAbstractGeometryV2 * combine(const QgsAbstractGeometryV2 &geom, QString *errorMsg=0) const override
Definition: qgsgeos.cpp:171
void prepareGeometry() override
Definition: qgsgeos.cpp:141
int splitGeometry(const QgsLineStringV2 &splitLine, QList< QgsAbstractGeometryV2 * > &newGeometries, bool topological, QList< QgsPointV2 > &topologyTestPoints, QString *errorMsg=0) const override
Splits this geometry according to a given line.
Definition: qgsgeos.cpp:332
Point geometry type.
Definition: qgspointv2.h:29
static void logMessage(const QString &message, const QString &tag=QString::null, MessageLevel level=WARNING)
add a message to the instance (and create it if necessary)
virtual bool addGeometry(QgsAbstractGeometryV2 *g) override
Adds a geometry and takes ownership.
virtual QgsLineStringV2 * clone() const override
Clones the geometry by performing a deep copy.
virtual QgsPolygonV2 * toPolygon() const
GEOSContextHandle_t ctxt
Definition: qgsgeos.cpp:86
GEOSInit()
Definition: qgsgeos.cpp:88
const QgsAbstractGeometryV2 * mGeometry
Does vector analysis using the geos library and handles import, export, exception handling*...
Definition: qgsgeos.h:29
void reserve(int size)
QgsAbstractGeometryV2 * reshapeGeometry(const QgsLineStringV2 &reshapeWithLine, int *errorCode, QString *errorMsg=0) const
Definition: qgsgeos.cpp:1608
static GEOSGeometry * asGeos(const QgsAbstractGeometryV2 *geom, double precision=0)
Definition: qgsgeos.cpp:1004
virtual QString geometryType() const =0
Returns a unique string representing the geometry type.
void geometryChanged() override
Removes caches.
Definition: qgsgeos.cpp:132
QgsAbstractGeometryV2 * difference(const QgsAbstractGeometryV2 &geom, QString *errorMsg=0) const override
Definition: qgsgeos.cpp:166
static void printGEOSNotice(const char *fmt,...)
Definition: qgsgeos.cpp:67
~GEOSInit()
Definition: qgsgeos.cpp:93
QgsAbstractGeometryV2 * convexHull(QString *errorMsg=0) const override
Definition: qgsgeos.cpp:1354
QgsAbstractGeometryV2 * simplify(double tolerance, QString *errorMsg=0) const override
Definition: qgsgeos.cpp:1254
int numGeometries() const
Returns the number of geometries within the collection.
void addVertex(const QgsPointV2 &pt)
QgsAbstractGeometryV2 * buffer(double distance, int segments, QString *errorMsg=0) const override
Definition: qgsgeos.cpp:1216
virtual QgsLineStringV2 * curveToLine() const =0
Returns a new line string geometry corresponding to a segmentized approximation of the curve...
const_iterator constBegin() const
QgsAbstractGeometryV2 * intersection(const QgsAbstractGeometryV2 &geom, QString *errorMsg=0) const override
Definition: qgsgeos.cpp:161
void setPoints(const QList< QgsPointV2 > &points)
void setExteriorRing(QgsCurveV2 *ring)
Sets exterior ring (takes ownership)
static GEOSInit geosinit
Definition: qgsgeos.cpp:99
bool within(const QgsAbstractGeometryV2 &geom, QString *errorMsg=0) const override
Definition: qgsgeos.cpp:245
virtual bool addGeometry(QgsAbstractGeometryV2 *g)
Adds a geometry and takes ownership.
virtual int dimension() const =0
Returns the inherent dimension of the geometry.
static void throwGEOSException(const char *fmt,...)
Definition: qgsgeos.cpp:53
int count(const T &value) const
bool isEqual(const QgsAbstractGeometryV2 &geom, QString *errorMsg=0) const override
Definition: qgsgeos.cpp:1385
const QgsAbstractGeometryV2 * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
double m() const
Definition: qgspointv2.h:45
bool isEmpty(QString *errorMsg=0) const override
Definition: qgsgeos.cpp:1405
QgsAbstractGeometryV2 * envelope(QString *errorMsg=0) const override
Definition: qgsgeos.cpp:1310
QgsAbstractGeometryV2 * offsetCurve(double distance, int segments, int joinStyle, double mitreLimit, QString *errorMsg=0) const override
Definition: qgsgeos.cpp:1592
bool touches(const QgsAbstractGeometryV2 &geom, QString *errorMsg=0) const override
Definition: qgsgeos.cpp:235
double distance(const QgsAbstractGeometryV2 &geom, QString *errorMsg=0) const override
Definition: qgsgeos.cpp:205
Multi polygon geometry collection.
Contains geometry relation and modification algorithms.
QString relate(const QgsAbstractGeometryV2 &geom, QString *errorMsg=0) const override
Returns the Dimensional Extended 9 Intersection Model (DE-9IM) representation of the relationship bet...
Definition: qgsgeos.cpp:265
Curve polygon geometry type.
int numInteriorRings() const
virtual QgsAbstractGeometryV2 * clone() const =0
Clones the geometry by performing a deep copy.
Abstract base class for curved geometry type.
Definition: qgscurvev2.h:32
int size() const
bool isValid(QString *errorMsg=0) const override
Definition: qgsgeos.cpp:1371
#define DEFAULT_QUADRANT_SEGMENTS
Definition: qgsgeos.cpp:32
QgsCurveV2 * interiorRing(int i) const
int numPoints() const override
Returns the number of points in the curve.