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