QGIS API Documentation  2.13.0-Master
qgsgeometryanalyzer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsgeometryanalyzer.cpp - QGIS Tools for vector geometry analysis
3  -------------------
4  begin : 19 March 2009
5  copyright : (C) Carson Farmer
6  email : [email protected]
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgsgeometryanalyzer.h"
19 
20 #include "qgsapplication.h"
21 #include "qgsfield.h"
22 #include "qgsfeature.h"
23 #include "qgslogger.h"
25 #include "qgsvectorfilewriter.h"
26 #include "qgsvectordataprovider.h"
27 #include "qgsdistancearea.h"
28 #include <QProgressDialog>
29 
31  const QString& shapefileName,
32  double tolerance,
33  bool onlySelectedFeatures,
34  QProgressDialog *p )
35 {
36  if ( !layer )
37  {
38  return false;
39  }
40 
41  QgsVectorDataProvider* dp = layer->dataProvider();
42  if ( !dp )
43  {
44  return false;
45  }
46 
47  QGis::WkbType outputType = dp->geometryType();
48  const QgsCoordinateReferenceSystem crs = layer->crs();
49 
50  QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), layer->fields(), outputType, &crs );
51  QgsFeature currentFeature;
52 
53  //take only selection
54  if ( onlySelectedFeatures )
55  {
56  //use QgsVectorLayer::featureAtId
57  const QgsFeatureIds selection = layer->selectedFeaturesIds();
58  if ( p )
59  {
60  p->setMaximum( selection.size() );
61  }
62 
63  int processedFeatures = 0;
65  for ( ; it != selection.constEnd(); ++it )
66  {
67  if ( p )
68  {
69  p->setValue( processedFeatures );
70  }
71 
72  if ( p && p->wasCanceled() )
73  {
74  break;
75  }
76  if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( *it ) ).nextFeature( currentFeature ) )
77  {
78  continue;
79  }
80  simplifyFeature( currentFeature, &vWriter, tolerance );
81  ++processedFeatures;
82  }
83 
84  if ( p )
85  {
86  p->setValue( selection.size() );
87  }
88  }
89  //take all features
90  else
91  {
92  QgsFeatureIterator fit = layer->getFeatures();
93 
94  int featureCount = layer->featureCount();
95  if ( p )
96  {
97  p->setMaximum( featureCount );
98  }
99  int processedFeatures = 0;
100 
101  while ( fit.nextFeature( currentFeature ) )
102  {
103  if ( p )
104  {
105  p->setValue( processedFeatures );
106  }
107  if ( p && p->wasCanceled() )
108  {
109  break;
110  }
111  simplifyFeature( currentFeature, &vWriter, tolerance );
112  ++processedFeatures;
113  }
114  if ( p )
115  {
116  p->setValue( featureCount );
117  }
118  }
119 
120  return true;
121 }
122 
123 void QgsGeometryAnalyzer::simplifyFeature( QgsFeature& f, QgsVectorFileWriter* vfw, double tolerance )
124 {
125  if ( !f.constGeometry() )
126  {
127  return;
128  }
129 
130  const QgsGeometry* featureGeometry = f.constGeometry();
131  QgsGeometry* tmpGeometry = nullptr;
132 
133  // simplify feature
134  tmpGeometry = featureGeometry->simplify( tolerance );
135 
136  QgsFeature newFeature;
137  newFeature.setGeometry( tmpGeometry );
138  newFeature.setAttributes( f.attributes() );
139 
140  //add it to vector file writer
141  if ( vfw )
142  {
143  vfw->addFeature( newFeature );
144  }
145 }
146 
147 bool QgsGeometryAnalyzer::centroids( QgsVectorLayer* layer, const QString& shapefileName,
148  bool onlySelectedFeatures, QProgressDialog* p )
149 {
150  if ( !layer )
151  {
152  QgsDebugMsg( "No layer passed to centroids" );
153  return false;
154  }
155 
156  QgsVectorDataProvider* dp = layer->dataProvider();
157  if ( !dp )
158  {
159  QgsDebugMsg( "No data provider for layer passed to centroids" );
160  return false;
161  }
162 
163  QGis::WkbType outputType = QGis::WKBPoint;
164  const QgsCoordinateReferenceSystem crs = layer->crs();
165 
166  QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), layer->fields(), outputType, &crs );
167  QgsFeature currentFeature;
168 
169  //take only selection
170  if ( onlySelectedFeatures )
171  {
172  //use QgsVectorLayer::featureAtId
173  const QgsFeatureIds selection = layer->selectedFeaturesIds();
174  if ( p )
175  {
176  p->setMaximum( selection.size() );
177  }
178 
179  int processedFeatures = 0;
180  QgsFeatureIds::const_iterator it = selection.constBegin();
181  for ( ; it != selection.constEnd(); ++it )
182  {
183  if ( p )
184  {
185  p->setValue( processedFeatures );
186  }
187 
188  if ( p && p->wasCanceled() )
189  {
190  break;
191  }
192  if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( *it ) ).nextFeature( currentFeature ) )
193  {
194  continue;
195  }
196  centroidFeature( currentFeature, &vWriter );
197  ++processedFeatures;
198  }
199 
200  if ( p )
201  {
202  p->setValue( selection.size() );
203  }
204  }
205  //take all features
206  else
207  {
208  QgsFeatureIterator fit = layer->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() ) );
209 
210  int featureCount = layer->featureCount();
211  if ( p )
212  {
213  p->setMaximum( featureCount );
214  }
215  int processedFeatures = 0;
216 
217  while ( fit.nextFeature( currentFeature ) )
218  {
219  if ( p )
220  {
221  p->setValue( processedFeatures );
222  }
223  if ( p && p->wasCanceled() )
224  {
225  break;
226  }
227  centroidFeature( currentFeature, &vWriter );
228  ++processedFeatures;
229  }
230  if ( p )
231  {
232  p->setValue( featureCount );
233  }
234  }
235 
236  return true;
237 }
238 
239 
240 void QgsGeometryAnalyzer::centroidFeature( QgsFeature& f, QgsVectorFileWriter* vfw )
241 {
242  if ( !f.constGeometry() )
243  {
244  return;
245  }
246 
247  const QgsGeometry* featureGeometry = f.constGeometry();
248  QgsGeometry* tmpGeometry = nullptr;
249 
250  tmpGeometry = featureGeometry->centroid();
251 
252  QgsFeature newFeature;
253  newFeature.setGeometry( tmpGeometry );
254  newFeature.setAttributes( f.attributes() );
255 
256  //add it to vector file writer
257  if ( vfw )
258  {
259  vfw->addFeature( newFeature );
260  }
261 }
262 
264  const QString& shapefileName,
265  bool onlySelectedFeatures,
266  QProgressDialog * )
267 {
268  if ( !layer )
269  {
270  return false;
271  }
272 
273  QgsVectorDataProvider* dp = layer->dataProvider();
274  if ( !dp )
275  {
276  return false;
277  }
278 
279  QGis::WkbType outputType = QGis::WKBPolygon;
280  const QgsCoordinateReferenceSystem crs = layer->crs();
281 
282  QgsFields fields;
283  fields.append( QgsField( QString( "MINX" ), QVariant::Double ) );
284  fields.append( QgsField( QString( "MINY" ), QVariant::Double ) );
285  fields.append( QgsField( QString( "MAXX" ), QVariant::Double ) );
286  fields.append( QgsField( QString( "MAXY" ), QVariant::Double ) );
287  fields.append( QgsField( QString( "CNTX" ), QVariant::Double ) );
288  fields.append( QgsField( QString( "CNTY" ), QVariant::Double ) );
289  fields.append( QgsField( QString( "AREA" ), QVariant::Double ) );
290  fields.append( QgsField( QString( "PERIM" ), QVariant::Double ) );
291  fields.append( QgsField( QString( "HEIGHT" ), QVariant::Double ) );
292  fields.append( QgsField( QString( "WIDTH" ), QVariant::Double ) );
293 
294  QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), fields, outputType, &crs );
295 
296  QgsRectangle rect;
297  if ( onlySelectedFeatures ) // take only selection
298  {
299  rect = layer->boundingBoxOfSelected();
300  }
301  else
302  {
303  rect = layer->extent();
304  }
305 
306  double minx = rect.xMinimum();
307  double miny = rect.yMinimum();
308  double maxx = rect.xMaximum();
309  double maxy = rect.yMaximum();
310  double height = rect.height();
311  double width = rect.width();
312  double cntx = minx + ( width / 2.0 );
313  double cnty = miny + ( height / 2.0 );
314  double area = width * height;
315  double perim = ( 2 * width ) + ( 2 * height );
316 
317  QgsFeature feat;
318  QgsAttributes attrs( 10 );
319  attrs[0] = QVariant( minx );
320  attrs[1] = QVariant( miny );
321  attrs[2] = QVariant( maxx );
322  attrs[3] = QVariant( maxy );
323  attrs[4] = QVariant( cntx );
324  attrs[5] = QVariant( cnty );
325  attrs[6] = QVariant( area );
326  attrs[7] = QVariant( perim );
327  attrs[8] = QVariant( height );
328  attrs[9] = QVariant( width );
329  feat.setAttributes( attrs );
330  feat.setGeometry( QgsGeometry::fromRect( rect ) );
331  vWriter.addFeature( feat );
332  return true;
333 }
334 
335 QList<double> QgsGeometryAnalyzer::simpleMeasure( QgsGeometry* mpGeometry )
336 {
337  QList<double> list;
338  double perim;
339  if ( mpGeometry->wkbType() == QGis::WKBPoint )
340  {
341  QgsPoint pt = mpGeometry->asPoint();
342  list.append( pt.x() );
343  list.append( pt.y() );
344  }
345  else
346  {
347  QgsDistanceArea measure;
348  list.append( measure.measureArea( mpGeometry ) );
349  if ( mpGeometry->type() == QGis::Polygon )
350  {
351  perim = perimeterMeasure( mpGeometry, measure );
352  list.append( perim );
353  }
354  }
355  return list;
356 }
357 
358 double QgsGeometryAnalyzer::perimeterMeasure( QgsGeometry* geometry, QgsDistanceArea& measure )
359 {
360  return measure.measurePerimeter( geometry );
361 }
362 
363 bool QgsGeometryAnalyzer::convexHull( QgsVectorLayer* layer, const QString& shapefileName,
364  bool onlySelectedFeatures, int uniqueIdField, QProgressDialog* p )
365 {
366  if ( !layer )
367  {
368  return false;
369  }
370  QgsVectorDataProvider* dp = layer->dataProvider();
371  if ( !dp )
372  {
373  return false;
374  }
375  bool useField = false;
376  if ( uniqueIdField == -1 )
377  {
378  uniqueIdField = 0;
379  }
380  else
381  {
382  useField = true;
383  }
384  QgsFields fields;
385  fields.append( QgsField( QString( "UID" ), QVariant::String ) );
386  fields.append( QgsField( QString( "AREA" ), QVariant::Double ) );
387  fields.append( QgsField( QString( "PERIM" ), QVariant::Double ) );
388 
389  QGis::WkbType outputType = QGis::WKBPolygon;
390  const QgsCoordinateReferenceSystem crs = layer->crs();
391 
392  QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), fields, outputType, &crs );
393  QgsFeature currentFeature;
394  QgsGeometry* dissolveGeometry = nullptr; //dissolve geometry
396 
397  if ( onlySelectedFeatures )
398  {
399  //use QgsVectorLayer::featureAtId
400  const QgsFeatureIds selection = layer->selectedFeaturesIds();
401  QgsFeatureIds::const_iterator it = selection.constBegin();
402  for ( ; it != selection.constEnd(); ++it )
403  {
404 #if 0
405  if ( p )
406  {
407  p->setValue( processedFeatures );
408  }
409  if ( p && p->wasCanceled() )
410  {
411  // break; // it may be better to do something else here?
412  return false;
413  }
414 #endif
415  if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( *it ) ).nextFeature( currentFeature ) )
416  {
417  continue;
418  }
419  map.insert( currentFeature.attribute( uniqueIdField ).toString(), currentFeature.id() );
420  }
421  }
422  else
423  {
424  QgsFeatureIterator fit = layer->getFeatures();
425  while ( fit.nextFeature( currentFeature ) )
426  {
427 #if 0
428  if ( p )
429  {
430  p->setValue( processedFeatures );
431  }
432  if ( p && p->wasCanceled() )
433  {
434  // break; // it may be better to do something else here?
435  return false;
436  }
437 #endif
438  map.insert( currentFeature.attribute( uniqueIdField ).toString(), currentFeature.id() );
439  }
440  }
441 
443  while ( jt != map.constEnd() )
444  {
445  QString currentKey = jt.key();
446  int processedFeatures = 0;
447  //take only selection
448  if ( onlySelectedFeatures )
449  {
450  //use QgsVectorLayer::featureAtId
451  const QgsFeatureIds selection = layer->selectedFeaturesIds();
452  if ( p )
453  {
454  p->setMaximum( selection.size() );
455  }
456  processedFeatures = 0;
457  while ( jt != map.constEnd() && ( jt.key() == currentKey || !useField ) )
458  {
459  if ( p && p->wasCanceled() )
460  {
461  break;
462  }
463  if ( selection.contains( jt.value() ) )
464  {
465  if ( p )
466  {
467  p->setValue( processedFeatures );
468  }
469  if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( jt.value() ) ).nextFeature( currentFeature ) )
470  {
471  continue;
472  }
473  convexFeature( currentFeature, processedFeatures, &dissolveGeometry );
474  ++processedFeatures;
475  }
476  ++jt;
477  }
478  QList<double> values;
479  if ( !dissolveGeometry )
480  {
481  QgsDebugMsg( "no dissolved geometry - should not happen" );
482  return false;
483  }
484  dissolveGeometry = dissolveGeometry->convexHull();
485  values = simpleMeasure( dissolveGeometry );
486  QgsAttributes attributes( 3 );
487  attributes[0] = QVariant( currentKey );
488  attributes[1] = values.at( 0 );
489  attributes[2] = values.at( 1 );
490  QgsFeature dissolveFeature;
491  dissolveFeature.setAttributes( attributes );
492  dissolveFeature.setGeometry( dissolveGeometry );
493  vWriter.addFeature( dissolveFeature );
494  }
495  //take all features
496  else
497  {
498  int featureCount = layer->featureCount();
499  if ( p )
500  {
501  p->setMaximum( featureCount );
502  }
503  processedFeatures = 0;
504  while ( jt != map.constEnd() && ( jt.key() == currentKey || !useField ) )
505  {
506  if ( p )
507  {
508  p->setValue( processedFeatures );
509  }
510 
511  if ( p && p->wasCanceled() )
512  {
513  break;
514  }
515  if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( jt.value() ) ).nextFeature( currentFeature ) )
516  {
517  continue;
518  }
519  convexFeature( currentFeature, processedFeatures, &dissolveGeometry );
520  ++processedFeatures;
521  ++jt;
522  }
523  QList<double> values;
524  // QgsGeometry* tmpGeometry = 0;
525  if ( !dissolveGeometry )
526  {
527  QgsDebugMsg( "no dissolved geometry - should not happen" );
528  return false;
529  }
530  dissolveGeometry = dissolveGeometry->convexHull();
531  // values = simpleMeasure( tmpGeometry );
532  values = simpleMeasure( dissolveGeometry );
533  QgsAttributes attributes;
534  attributes[0] = QVariant( currentKey );
535  attributes[1] = QVariant( values[ 0 ] );
536  attributes[2] = QVariant( values[ 1 ] );
537  QgsFeature dissolveFeature;
538  dissolveFeature.setAttributes( attributes );
539  dissolveFeature.setGeometry( dissolveGeometry );
540  vWriter.addFeature( dissolveFeature );
541  }
542  }
543  return true;
544 }
545 
546 
547 void QgsGeometryAnalyzer::convexFeature( QgsFeature& f, int nProcessedFeatures, QgsGeometry** dissolveGeometry )
548 {
549  if ( !f.constGeometry() )
550  {
551  return;
552  }
553 
554  const QgsGeometry* featureGeometry = f.constGeometry();
555  QgsGeometry* tmpGeometry = nullptr;
556  QgsGeometry* convexGeometry = nullptr;
557 
558  convexGeometry = featureGeometry->convexHull();
559 
560  if ( nProcessedFeatures == 0 )
561  {
562  *dissolveGeometry = convexGeometry;
563  }
564  else
565  {
566  tmpGeometry = *dissolveGeometry;
567  *dissolveGeometry = ( *dissolveGeometry )->combine( convexGeometry );
568  delete tmpGeometry;
569  delete convexGeometry;
570  }
571 }
572 
573 bool QgsGeometryAnalyzer::dissolve( QgsVectorLayer* layer, const QString& shapefileName,
574  bool onlySelectedFeatures, int uniqueIdField, QProgressDialog* p )
575 {
576  if ( !layer )
577  {
578  return false;
579  }
580  QgsVectorDataProvider* dp = layer->dataProvider();
581  if ( !dp )
582  {
583  return false;
584  }
585  bool useField = false;
586  if ( uniqueIdField == -1 )
587  {
588  uniqueIdField = 0;
589  }
590  else
591  {
592  useField = true;
593  }
594 
595  QGis::WkbType outputType = dp->geometryType();
596  const QgsCoordinateReferenceSystem crs = layer->crs();
597 
598  QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), layer->fields(), outputType, &crs );
599  QgsFeature currentFeature;
601 
602  if ( onlySelectedFeatures )
603  {
604  //use QgsVectorLayer::featureAtId
605  const QgsFeatureIds selection = layer->selectedFeaturesIds();
606  QgsFeatureIds::const_iterator it = selection.constBegin();
607  for ( ; it != selection.constEnd(); ++it )
608  {
609  if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( *it ) ).nextFeature( currentFeature ) )
610  {
611  continue;
612  }
613  map.insert( currentFeature.attribute( uniqueIdField ).toString(), currentFeature.id() );
614  }
615  }
616  else
617  {
618  QgsFeatureIterator fit = layer->getFeatures();
619  while ( fit.nextFeature( currentFeature ) )
620  {
621  map.insert( currentFeature.attribute( uniqueIdField ).toString(), currentFeature.id() );
622  }
623  }
624 
625  QgsGeometry *dissolveGeometry = nullptr; //dissolve geometry
627  QgsFeature outputFeature;
628  while ( jt != map.constEnd() )
629  {
630  QString currentKey = jt.key();
631  int processedFeatures = 0;
632  bool first = true;
633  //take only selection
634  if ( onlySelectedFeatures )
635  {
636  //use QgsVectorLayer::featureAtId
637  const QgsFeatureIds selection = layer->selectedFeaturesIds();
638  if ( p )
639  {
640  p->setMaximum( selection.size() );
641  }
642  while ( jt != map.constEnd() && ( jt.key() == currentKey || !useField ) )
643  {
644  if ( p && p->wasCanceled() )
645  {
646  break;
647  }
648  if ( selection.contains( jt.value() ) )
649  {
650  if ( p )
651  {
652  p->setValue( processedFeatures );
653  }
654  if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( jt.value() ) ).nextFeature( currentFeature ) )
655  {
656  continue;
657  }
658  if ( first )
659  {
660  outputFeature.setAttributes( currentFeature.attributes() );
661  first = false;
662  }
663  dissolveFeature( currentFeature, processedFeatures, &dissolveGeometry );
664  ++processedFeatures;
665  }
666  ++jt;
667  }
668  }
669  //take all features
670  else
671  {
672  int featureCount = layer->featureCount();
673  if ( p )
674  {
675  p->setMaximum( featureCount );
676  }
677  while ( jt != map.constEnd() && ( jt.key() == currentKey || !useField ) )
678  {
679  if ( p )
680  {
681  p->setValue( processedFeatures );
682  }
683 
684  if ( p && p->wasCanceled() )
685  {
686  break;
687  }
688  if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( jt.value() ) ).nextFeature( currentFeature ) )
689  {
690  continue;
691  }
692  {
693  outputFeature.setAttributes( currentFeature.attributes() );
694  first = false;
695  }
696  dissolveFeature( currentFeature, processedFeatures, &dissolveGeometry );
697  ++processedFeatures;
698  ++jt;
699  }
700  }
701  outputFeature.setGeometry( dissolveGeometry );
702  vWriter.addFeature( outputFeature );
703  }
704  return true;
705 }
706 
707 void QgsGeometryAnalyzer::dissolveFeature( QgsFeature& f, int nProcessedFeatures, QgsGeometry** dissolveGeometry )
708 {
709  if ( !f.constGeometry() )
710  {
711  return;
712  }
713 
714  const QgsGeometry* featureGeometry = f.constGeometry();
715 
716  if ( nProcessedFeatures == 0 )
717  {
718  int geomSize = featureGeometry->wkbSize();
719  *dissolveGeometry = new QgsGeometry();
720  unsigned char* wkb = new unsigned char[geomSize];
721  memcpy( wkb, featureGeometry->asWkb(), geomSize );
722  ( *dissolveGeometry )->fromWkb( wkb, geomSize );
723  }
724  else
725  {
726  *dissolveGeometry = ( *dissolveGeometry )->combine( featureGeometry );
727  }
728 }
729 
730 bool QgsGeometryAnalyzer::buffer( QgsVectorLayer* layer, const QString& shapefileName, double bufferDistance,
731  bool onlySelectedFeatures, bool dissolve, int bufferDistanceField, QProgressDialog* p )
732 {
733  if ( !layer )
734  {
735  return false;
736  }
737 
738  QgsVectorDataProvider* dp = layer->dataProvider();
739  if ( !dp )
740  {
741  return false;
742  }
743 
744  QGis::WkbType outputType = QGis::WKBPolygon;
745  if ( dissolve )
746  {
747  outputType = QGis::WKBMultiPolygon;
748  }
749  const QgsCoordinateReferenceSystem crs = layer->crs();
750 
751  QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), layer->fields(), outputType, &crs );
752  QgsFeature currentFeature;
753  QgsGeometry *dissolveGeometry = nullptr; //dissolve geometry (if dissolve enabled)
754 
755  //take only selection
756  if ( onlySelectedFeatures )
757  {
758  //use QgsVectorLayer::featureAtId
759  const QgsFeatureIds selection = layer->selectedFeaturesIds();
760  if ( p )
761  {
762  p->setMaximum( selection.size() );
763  }
764 
765  int processedFeatures = 0;
766  QgsFeatureIds::const_iterator it = selection.constBegin();
767  for ( ; it != selection.constEnd(); ++it )
768  {
769  if ( p )
770  {
771  p->setValue( processedFeatures );
772  }
773 
774  if ( p && p->wasCanceled() )
775  {
776  break;
777  }
778  if ( !layer->getFeatures( QgsFeatureRequest().setFilterFid( *it ) ).nextFeature( currentFeature ) )
779  {
780  continue;
781  }
782  bufferFeature( currentFeature, processedFeatures, &vWriter, dissolve, &dissolveGeometry, bufferDistance, bufferDistanceField );
783  ++processedFeatures;
784  }
785 
786  if ( p )
787  {
788  p->setValue( selection.size() );
789  }
790  }
791  //take all features
792  else
793  {
794  QgsFeatureIterator fit = layer->getFeatures();
795 
796  int featureCount = layer->featureCount();
797  if ( p )
798  {
799  p->setMaximum( featureCount );
800  }
801  int processedFeatures = 0;
802 
803  while ( fit.nextFeature( currentFeature ) )
804  {
805  if ( p )
806  {
807  p->setValue( processedFeatures );
808  }
809  if ( p && p->wasCanceled() )
810  {
811  break;
812  }
813  bufferFeature( currentFeature, processedFeatures, &vWriter, dissolve, &dissolveGeometry, bufferDistance, bufferDistanceField );
814  ++processedFeatures;
815  }
816  if ( p )
817  {
818  p->setValue( featureCount );
819  }
820  }
821 
822  if ( dissolve )
823  {
824  QgsFeature dissolveFeature;
825  if ( !dissolveGeometry )
826  {
827  QgsDebugMsg( "no dissolved geometry - should not happen" );
828  return false;
829  }
830  dissolveFeature.setGeometry( dissolveGeometry );
831  vWriter.addFeature( dissolveFeature );
832  }
833  return true;
834 }
835 
836 void QgsGeometryAnalyzer::bufferFeature( QgsFeature& f, int nProcessedFeatures, QgsVectorFileWriter* vfw, bool dissolve,
837  QgsGeometry** dissolveGeometry, double bufferDistance, int bufferDistanceField )
838 {
839  if ( !f.constGeometry() )
840  {
841  return;
842  }
843 
844  double currentBufferDistance;
845  const QgsGeometry* featureGeometry = f.constGeometry();
846  QgsGeometry* tmpGeometry = nullptr;
847  QgsGeometry* bufferGeometry = nullptr;
848 
849  //create buffer
850  if ( bufferDistanceField == -1 )
851  {
852  currentBufferDistance = bufferDistance;
853  }
854  else
855  {
856  currentBufferDistance = f.attribute( bufferDistanceField ).toDouble();
857  }
858  bufferGeometry = featureGeometry->buffer( currentBufferDistance, 5 );
859 
860  if ( dissolve )
861  {
862  if ( nProcessedFeatures == 0 )
863  {
864  *dissolveGeometry = bufferGeometry;
865  }
866  else
867  {
868  tmpGeometry = *dissolveGeometry;
869  *dissolveGeometry = ( *dissolveGeometry )->combine( bufferGeometry );
870  delete tmpGeometry;
871  delete bufferGeometry;
872  }
873  }
874  else //dissolve
875  {
876  QgsFeature newFeature;
877  newFeature.setGeometry( bufferGeometry );
878  newFeature.setAttributes( f.attributes() );
879 
880  //add it to vector file writer
881  if ( vfw )
882  {
883  vfw->addFeature( newFeature );
884  }
885  }
886 }
887 
888 bool QgsGeometryAnalyzer::eventLayer( QgsVectorLayer* lineLayer, QgsVectorLayer* eventLayer, int lineField, int eventField, QgsFeatureIds &unlocatedFeatureIds, const QString& outputLayer,
889  const QString& outputFormat, int locationField1, int locationField2, int offsetField, double offsetScale,
890  bool forceSingleGeometry, QgsVectorDataProvider* memoryProvider, QProgressDialog* p )
891 {
892  if ( !lineLayer || !eventLayer || !lineLayer->isValid() || !eventLayer->isValid() )
893  {
894  return false;
895  }
896 
897  //create line field / id map for line layer
898  QMultiHash< QString, QgsFeature > lineLayerIdMap; //1:n possible (e.g. several linear reference geometries for one feature in the event layer)
899  QgsFeatureIterator fit = lineLayer->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() << lineField ) );
900  QgsFeature fet;
901  while ( fit.nextFeature( fet ) )
902  {
903  lineLayerIdMap.insert( fet.attribute( lineField ).toString(), fet );
904  }
905 
906  //create output datasource or attributes in memory provider
907  QgsVectorFileWriter* fileWriter = nullptr;
908  QgsFeatureList memoryProviderFeatures;
909  if ( !memoryProvider )
910  {
911  QGis::WkbType memoryProviderType = QGis::WKBMultiLineString;
912  if ( locationField2 == -1 )
913  {
914  memoryProviderType = forceSingleGeometry ? QGis::WKBPoint : QGis::WKBMultiPoint;
915  }
916  else
917  {
918  memoryProviderType = forceSingleGeometry ? QGis::WKBLineString : QGis::WKBMultiLineString;
919  }
920  fileWriter = new QgsVectorFileWriter( outputLayer,
921  eventLayer->dataProvider()->encoding(),
922  eventLayer->fields(),
923  memoryProviderType,
924  &( lineLayer->crs() ),
925  outputFormat );
926  }
927  else
928  {
929  memoryProvider->addAttributes( eventLayer->fields().toList() );
930  }
931 
932  //iterate over eventLayer and write new features to output file or layer
933  fit = eventLayer->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ) );
934  QgsGeometry* lrsGeom = nullptr;
935  double measure1, measure2 = 0.0;
936 
937  int nEventFeatures = eventLayer->featureCount();
938  int featureCounter = 0;
939  int nOutputFeatures = 0; //number of output features for the current event feature
940  if ( p )
941  {
942  p->setWindowModality( Qt::WindowModal );
943  p->setMinimum( 0 );
944  p->setMaximum( nEventFeatures );
945  p->show();
946  }
947 
948  while ( fit.nextFeature( fet ) )
949  {
950  nOutputFeatures = 0;
951 
952  //update progress dialog
953  if ( p )
954  {
955  if ( p->wasCanceled() )
956  {
957  break;
958  }
959  p->setValue( featureCounter );
960  ++featureCounter;
961  }
962 
963  measure1 = fet.attribute( locationField1 ).toDouble();
964  if ( locationField2 != -1 )
965  {
966  measure2 = fet.attribute( locationField2 ).toDouble();
967  if ( qgsDoubleNear(( measure2 - measure1 ), 0.0 ) )
968  {
969  continue;
970  }
971  }
972 
973  QList<QgsFeature> featureIdList = lineLayerIdMap.values( fet.attribute( eventField ).toString() );
974  QList<QgsFeature>::iterator featureIdIt = featureIdList.begin();
975  for ( ; featureIdIt != featureIdList.end(); ++featureIdIt )
976  {
977  if ( locationField2 == -1 )
978  {
979  lrsGeom = locateAlongMeasure( measure1, featureIdIt->constGeometry() );
980  }
981  else
982  {
983  lrsGeom = locateBetweenMeasures( measure1, measure2, featureIdIt->constGeometry() );
984  }
985 
986  if ( lrsGeom )
987  {
988  ++nOutputFeatures;
989  addEventLayerFeature( fet, lrsGeom, featureIdIt->geometry(), fileWriter, memoryProviderFeatures, offsetField, offsetScale, forceSingleGeometry );
990  }
991  }
992  if ( nOutputFeatures < 1 )
993  {
994  unlocatedFeatureIds.insert( fet.id() );
995  }
996  }
997 
998  if ( p )
999  {
1000  p->setValue( nEventFeatures );
1001  }
1002 
1003  if ( memoryProvider )
1004  {
1005  memoryProvider->addFeatures( memoryProviderFeatures );
1006  }
1007  delete fileWriter;
1008  return true;
1009 }
1010 
1011 void QgsGeometryAnalyzer::addEventLayerFeature( QgsFeature& feature, QgsGeometry* geom, QgsGeometry* lineGeom, QgsVectorFileWriter* fileWriter, QgsFeatureList& memoryFeatures,
1012  int offsetField, double offsetScale, bool forceSingleType )
1013 {
1014  if ( !geom )
1015  {
1016  return;
1017  }
1018 
1019  QList<QgsGeometry*> geomList;
1020  if ( forceSingleType )
1021  {
1022  geomList = geom->asGeometryCollection();
1023  }
1024  else
1025  {
1026  geomList.push_back( geom );
1027  }
1028 
1029  QList<QgsGeometry*>::iterator geomIt = geomList.begin();
1030  for ( ; geomIt != geomList.end(); ++geomIt )
1031  {
1032  //consider offset
1033  if ( offsetField >= 0 )
1034  {
1035  double offsetVal = feature.attribute( offsetField ).toDouble();
1036  offsetVal *= offsetScale;
1037  if ( !createOffsetGeometry( *geomIt, lineGeom, offsetVal ) )
1038  {
1039  delete *geomIt;
1040  continue;
1041  }
1042  }
1043 
1044  feature.setGeometry( *geomIt );
1045  if ( fileWriter )
1046  {
1047  fileWriter->addFeature( feature );
1048  }
1049  else
1050  {
1051  memoryFeatures << feature;
1052  }
1053  }
1054 
1055  if ( forceSingleType )
1056  {
1057  delete geom;
1058  }
1059 }
1060 
1061 bool QgsGeometryAnalyzer::createOffsetGeometry( QgsGeometry* geom, QgsGeometry* lineGeom, double offset )
1062 {
1063  if ( !geom || !lineGeom )
1064  {
1065  return false;
1066  }
1067 
1068  QList<QgsGeometry*> inputGeomList;
1069 
1070  if ( geom->isMultipart() )
1071  {
1072  inputGeomList = geom->asGeometryCollection();
1073  }
1074  else
1075  {
1076  inputGeomList.push_back( geom );
1077  }
1078 
1079  QList<GEOSGeometry*> outputGeomList;
1080  QList<QgsGeometry*>::const_iterator inputGeomIt = inputGeomList.constBegin();
1081  GEOSContextHandle_t geosctxt = QgsGeometry::getGEOSHandler();
1082  for ( ; inputGeomIt != inputGeomList.constEnd(); ++inputGeomIt )
1083  {
1084  if ( geom->type() == QGis::Line )
1085  {
1086  //geos 3.3 needed for line offsets
1087 #if defined(GEOS_VERSION_MAJOR) && defined(GEOS_VERSION_MINOR) && \
1088  ((GEOS_VERSION_MAJOR>3) || ((GEOS_VERSION_MAJOR==3) && (GEOS_VERSION_MINOR>=3)))
1089  GEOSGeometry* offsetGeom = GEOSOffsetCurve_r( geosctxt, ( *inputGeomIt )->asGeos(), -offset, 8 /*quadSegments*/, 0 /*joinStyle*/, 5.0 /*mitreLimit*/ );
1090  if ( !offsetGeom || !GEOSisValid_r( geosctxt, offsetGeom ) )
1091  {
1092  return false;
1093  }
1094  if ( !GEOSisValid_r( geosctxt, offsetGeom ) || GEOSGeomTypeId_r( geosctxt, offsetGeom ) != GEOS_LINESTRING || GEOSGeomGetNumPoints_r( geosctxt, offsetGeom ) < 1 )
1095  {
1096  GEOSGeom_destroy_r( geosctxt, offsetGeom );
1097  return false;
1098  }
1099  outputGeomList.push_back( offsetGeom );
1100 #else
1101  outputGeomList.push_back( GEOSGeom_clone_r( geosctxt, ( *inputGeomIt )->asGeos() ) );
1102 #endif
1103  }
1104  else if ( geom->type() == QGis::Point )
1105  {
1106  QgsPoint p = ( *inputGeomIt )->asPoint();
1107  p = createPointOffset( p.x(), p.y(), offset, lineGeom );
1108  GEOSCoordSequence* ptSeq = GEOSCoordSeq_create_r( geosctxt, 1, 2 );
1109  GEOSCoordSeq_setX_r( geosctxt, ptSeq, 0, p.x() );
1110  GEOSCoordSeq_setY_r( geosctxt, ptSeq, 0, p.y() );
1111  GEOSGeometry* geosPt = GEOSGeom_createPoint_r( geosctxt, ptSeq );
1112  outputGeomList.push_back( geosPt );
1113  }
1114  }
1115 
1116  if ( !geom->isMultipart() )
1117  {
1118  GEOSGeometry* outputGeom = outputGeomList.at( 0 );
1119  if ( outputGeom )
1120  {
1121  geom->fromGeos( outputGeom );
1122  }
1123  }
1124  else
1125  {
1126  GEOSGeometry** geomArray = new GEOSGeometry*[outputGeomList.size()];
1127  for ( int i = 0; i < outputGeomList.size(); ++i )
1128  {
1129  geomArray[i] = outputGeomList.at( i );
1130  }
1131  GEOSGeometry* collection = nullptr;
1132  if ( geom->type() == QGis::Point )
1133  {
1134  collection = GEOSGeom_createCollection_r( geosctxt, GEOS_MULTIPOINT, geomArray, outputGeomList.size() );
1135  }
1136  else if ( geom->type() == QGis::Line )
1137  {
1138  collection = GEOSGeom_createCollection_r( geosctxt, GEOS_MULTILINESTRING, geomArray, outputGeomList.size() );
1139  }
1140  geom->fromGeos( collection );
1141  delete[] geomArray;
1142  }
1143  return true;
1144 }
1145 
1146 QgsPoint QgsGeometryAnalyzer::createPointOffset( double x, double y, double dist, QgsGeometry* lineGeom ) const
1147 {
1148  QgsPoint p( x, y );
1149  QgsPoint minDistPoint;
1150  int afterVertexNr;
1151  lineGeom->closestSegmentWithContext( p, minDistPoint, afterVertexNr );
1152 
1153  int beforeVertexNr = afterVertexNr - 1;
1154  QgsPoint beforeVertex = lineGeom->vertexAt( beforeVertexNr );
1155  QgsPoint afterVertex = lineGeom->vertexAt( afterVertexNr );
1156 
1157  //get normal vector
1158  double dx = afterVertex.x() - beforeVertex.x();
1159  double dy = afterVertex.y() - beforeVertex.y();
1160  double normalX = -dy;
1161  double normalY = dx;
1162  double normalLength = sqrt( normalX * normalX + normalY * normalY );
1163  normalX *= ( dist / normalLength );
1164  normalY *= ( dist / normalLength );
1165 
1166  double debugLength = sqrt( normalX * normalX + normalY * normalY ); //control
1167  Q_UNUSED( debugLength );
1168  return QgsPoint( x - normalX, y - normalY ); //negative values -> left side, positive values -> right side
1169 }
1170 
1171 QgsGeometry* QgsGeometryAnalyzer::locateBetweenMeasures( double fromMeasure, double toMeasure, const QgsGeometry* lineGeom )
1172 {
1173  if ( !lineGeom )
1174  {
1175  return nullptr;
1176  }
1177 
1178  QgsMultiPolyline resultGeom;
1179 
1180  //need to go with WKB and z coordinate until QgsGeometry supports M values
1181  const unsigned char* lineWkb = lineGeom->asWkb();
1182 
1183  const unsigned char* ptr = lineWkb + 1;
1184  QGis::WkbType wkbType;
1185  memcpy( &wkbType, ptr, sizeof( wkbType ) );
1186  ptr += sizeof( wkbType );
1187 
1188  if ( wkbType != QGis::WKBLineString25D && wkbType != QGis::WKBMultiLineString25D )
1189  {
1190  return nullptr;
1191  }
1192 
1193  if ( wkbType == QGis::WKBLineString25D )
1194  {
1195  locateBetweenWkbString( ptr, resultGeom, fromMeasure, toMeasure );
1196  }
1197  else if ( wkbType == QGis::WKBMultiLineString25D )
1198  {
1199  int* nLines = ( int* )ptr;
1200  ptr += sizeof( int );
1201  for ( int i = 0; i < *nLines; ++i )
1202  {
1203  ptr += ( 1 + sizeof( wkbType ) );
1204  ptr = locateBetweenWkbString( ptr, resultGeom, fromMeasure, toMeasure );
1205  }
1206  }
1207 
1208  if ( resultGeom.size() < 1 )
1209  {
1210  return nullptr;
1211  }
1212  return QgsGeometry::fromMultiPolyline( resultGeom );
1213 }
1214 
1216 {
1217  if ( !lineGeom )
1218  {
1219  return nullptr;
1220  }
1221 
1222  QgsMultiPoint resultGeom;
1223 
1224  //need to go with WKB and z coordinate until QgsGeometry supports M values
1225  const unsigned char* lineWkb = lineGeom->asWkb();
1226 
1227  const unsigned char* ptr = lineWkb + 1;
1228  QGis::WkbType wkbType;
1229  memcpy( &wkbType, ptr, sizeof( wkbType ) );
1230  ptr += sizeof( wkbType );
1231 
1232  if ( wkbType != QGis::WKBLineString25D && wkbType != QGis::WKBMultiLineString25D )
1233  {
1234  return nullptr;
1235  }
1236 
1237  if ( wkbType == QGis::WKBLineString25D )
1238  {
1239  locateAlongWkbString( ptr, resultGeom, measure );
1240  }
1241  else if ( wkbType == QGis::WKBMultiLineString25D )
1242  {
1243  int* nLines = ( int* )ptr;
1244  ptr += sizeof( int );
1245  for ( int i = 0; i < *nLines; ++i )
1246  {
1247  ptr += ( 1 + sizeof( wkbType ) );
1248  ptr = locateAlongWkbString( ptr, resultGeom, measure );
1249  }
1250  }
1251 
1252  if ( resultGeom.size() < 1 )
1253  {
1254  return nullptr;
1255  }
1256  return QgsGeometry::fromMultiPoint( resultGeom );
1257 }
1258 
1259 const unsigned char* QgsGeometryAnalyzer::locateBetweenWkbString( const unsigned char* ptr, QgsMultiPolyline& result, double fromMeasure, double toMeasure )
1260 {
1261  int* nPoints = ( int* ) ptr;
1262  ptr += sizeof( int );
1263  double prevx = 0.0, prevy = 0.0, prevz = 0.0;
1264  double *x, *y, *z;
1265  QgsPolyline currentLine;
1266 
1267  QgsPoint pt1, pt2;
1268  bool measureInSegment; //true if measure is contained in the segment
1269  bool secondPointClipped; //true if second point is != segment endpoint
1270 
1271 
1272  for ( int i = 0; i < *nPoints; ++i )
1273  {
1274  x = ( double* )ptr;
1275  ptr += sizeof( double );
1276  y = ( double* )ptr;
1277  ptr += sizeof( double );
1278  z = ( double* ) ptr;
1279  ptr += sizeof( double );
1280 
1281  if ( i > 0 )
1282  {
1283  measureInSegment = clipSegmentByRange( prevx, prevy, prevz, *x, *y, *z, fromMeasure, toMeasure, pt1, pt2, secondPointClipped );
1284  if ( measureInSegment )
1285  {
1286  if ( currentLine.size() < 1 ) //no points collected yet, so the first point needs to be added to the line
1287  {
1288  currentLine.append( pt1 );
1289  }
1290 
1291  if ( pt1 != pt2 ) //avoid duplicated entry if measure value equals m-value of vertex
1292  {
1293  currentLine.append( pt2 );
1294  }
1295 
1296  if ( secondPointClipped || i == *nPoints - 1 ) //close current segment
1297  {
1298  if ( currentLine.size() > 1 )
1299  {
1300  result.append( currentLine );
1301  }
1302  currentLine.clear();
1303  }
1304  }
1305  }
1306  prevx = *x;
1307  prevy = *y;
1308  prevz = *z;
1309  }
1310  return ptr;
1311 }
1312 
1313 const unsigned char* QgsGeometryAnalyzer::locateAlongWkbString( const unsigned char* ptr, QgsMultiPoint& result, double measure )
1314 {
1315  int* nPoints = ( int* ) ptr;
1316  ptr += sizeof( int );
1317  double prevx = 0.0, prevy = 0.0, prevz = 0.0;
1318  double *x, *y, *z;
1319 
1320  QgsPoint pt1, pt2;
1321  bool pt1Ok, pt2Ok;
1322 
1323  for ( int i = 0; i < *nPoints; ++i )
1324  {
1325  x = ( double* )ptr;
1326  ptr += sizeof( double );
1327  y = ( double* )ptr;
1328  ptr += sizeof( double );
1329  z = ( double* ) ptr;
1330  ptr += sizeof( double );
1331 
1332  if ( i > 0 )
1333  {
1334  locateAlongSegment( prevx, prevy, prevz, *x, *y, *z, measure, pt1Ok, pt1, pt2Ok, pt2 );
1335  if ( pt1Ok )
1336  {
1337  result.append( pt1 );
1338  }
1339  if ( pt2Ok && ( i == ( *nPoints - 1 ) ) )
1340  {
1341  result.append( pt2 );
1342  }
1343  }
1344  prevx = *x;
1345  prevy = *y;
1346  prevz = *z;
1347  }
1348  return ptr;
1349 }
1350 
1351 bool QgsGeometryAnalyzer::clipSegmentByRange( double x1, double y1, double m1, double x2, double y2, double m2, double range1, double range2, QgsPoint& pt1,
1352  QgsPoint& pt2, bool& secondPointClipped )
1353 {
1354  bool reversed = m1 > m2;
1355  double tmp;
1356 
1357  //reverse m1, m2 if necessary (and consequently also x1,x2 / y1, y2)
1358  if ( reversed )
1359  {
1360  tmp = m1;
1361  m1 = m2;
1362  m2 = tmp;
1363 
1364  tmp = x1;
1365  x1 = x2;
1366  x2 = tmp;
1367 
1368  tmp = y1;
1369  y1 = y2;
1370  y2 = tmp;
1371  }
1372 
1373  //reverse range1, range2 if necessary
1374  if ( range1 > range2 )
1375  {
1376  tmp = range1;
1377  range1 = range2;
1378  range2 = tmp;
1379  }
1380 
1381  //segment completely outside of range
1382  if ( m2 < range1 || m1 > range2 )
1383  {
1384  return false;
1385  }
1386 
1387  //segment completely inside of range
1388  if ( m2 <= range2 && m1 >= range1 )
1389  {
1390  if ( reversed )
1391  {
1392  pt1.setX( x2 );
1393  pt1.setY( y2 );
1394  pt2.setX( x1 );
1395  pt2.setY( y1 );
1396  }
1397  else
1398  {
1399  pt1.setX( x1 );
1400  pt1.setY( y1 );
1401  pt2.setX( x2 );
1402  pt2.setY( y2 );
1403  }
1404  secondPointClipped = false;
1405  return true;
1406  }
1407 
1408  //m1 inside and m2 not
1409  if ( m1 >= range1 && m1 <= range2 )
1410  {
1411  pt1.setX( x1 );
1412  pt1.setY( y1 );
1413  double dist = ( range2 - m1 ) / ( m2 - m1 );
1414  pt2.setX( x1 + ( x2 - x1 ) * dist );
1415  pt2.setY( y1 + ( y2 - y1 ) * dist );
1416  secondPointClipped = !reversed;
1417  }
1418 
1419  //m2 inside and m1 not
1420  if ( m2 >= range1 && m2 <= range2 )
1421  {
1422  pt2.setX( x2 );
1423  pt2.setY( y2 );
1424  double dist = ( m2 - range1 ) / ( m2 - m1 );
1425  pt1.setX( x2 - ( x2 - x1 ) * dist );
1426  pt1.setY( y2 - ( y2 - y1 ) * dist );
1427  secondPointClipped = reversed;
1428  }
1429 
1430  //range1 and range 2 both inside the segment
1431  if ( range1 >= m1 && range2 <= m2 )
1432  {
1433  double dist1 = ( range1 - m1 ) / ( m2 - m1 );
1434  double dist2 = ( range2 - m1 ) / ( m2 - m1 );
1435  pt1.setX( x1 + ( x2 - x1 ) * dist1 );
1436  pt1.setY( y1 + ( y2 - y1 ) * dist1 );
1437  pt2.setX( x1 + ( x2 - x1 ) * dist2 );
1438  pt2.setY( y1 + ( y2 - y1 ) * dist2 );
1439  secondPointClipped = true;
1440  }
1441 
1442  if ( reversed ) //switch p1 and p2
1443  {
1444  QgsPoint tmpPt = pt1;
1445  pt1 = pt2;
1446  pt2 = tmpPt;
1447  }
1448 
1449  return true;
1450 }
1451 
1452 void QgsGeometryAnalyzer::locateAlongSegment( double x1, double y1, double m1, double x2, double y2, double m2, double measure, bool& pt1Ok, QgsPoint& pt1, bool& pt2Ok, QgsPoint& pt2 )
1453 {
1454  bool reversed = false;
1455  pt1Ok = false;
1456  pt2Ok = false;
1457  double tolerance = 0.000001; //work with a small tolerance to catch e.g. locations at endpoints
1458 
1459  if ( m1 > m2 )
1460  {
1461  double tmp = m1;
1462  m1 = m2;
1463  m2 = tmp;
1464  reversed = true;
1465  }
1466 
1467  //segment does not match
1468  if (( m1 - measure ) > tolerance || ( measure - m2 ) > tolerance )
1469  {
1470  pt1Ok = false;
1471  pt2Ok = false;
1472  return;
1473  }
1474 
1475  //match with vertex1
1476  if ( qgsDoubleNear( m1, measure, tolerance ) )
1477  {
1478  if ( reversed )
1479  {
1480  pt2Ok = true;
1481  pt2.setX( x2 );
1482  pt2.setY( y2 );
1483  }
1484  else
1485  {
1486  pt1Ok = true;
1487  pt1.setX( x1 );
1488  pt1.setY( y1 );
1489  }
1490  }
1491 
1492  //match with vertex2
1493  if ( qgsDoubleNear( m2, measure, tolerance ) )
1494  {
1495  if ( reversed )
1496  {
1497  pt1Ok = true;
1498  pt1.setX( x1 );
1499  pt1.setY( y1 );
1500  }
1501  else
1502  {
1503  pt2Ok = true;
1504  pt2.setX( x2 );
1505  pt2.setY( y2 );
1506  }
1507  }
1508 
1509 
1510  if ( pt1Ok || pt2Ok )
1511  {
1512  return;
1513  }
1514 
1515  //match between the vertices
1516  if ( qgsDoubleNear( m1, m2 ) )
1517  {
1518  pt1.setX( x1 );
1519  pt1.setY( y1 );
1520  pt1Ok = true;
1521  return;
1522  }
1523  double dist = ( measure - m1 ) / ( m2 - m1 );
1524  if ( reversed )
1525  {
1526  dist = 1 - dist;
1527  }
1528 
1529  pt1.setX( x1 + dist * ( x2 - x1 ) );
1530  pt1.setY( y1 + dist * ( y2 - y1 ) );
1531  pt1Ok = true;
1532 }
QList< QgsField > toList() const
Utility function to return a list of QgsField instances.
Definition: qgsfield.cpp:427
QgsFeatureId id() const
Get the feature ID for this feature.
Definition: qgsfeature.cpp:65
bool eventLayer(QgsVectorLayer *lineLayer, QgsVectorLayer *eventLayer, int lineField, int eventField, QgsFeatureIds &unlocatedFeatureIds, const QString &outputLayer, const QString &outputFormat, int locationField1, int locationField2=-1, int offsetField=-1, double offsetScale=1.0, bool forceSingleGeometry=false, QgsVectorDataProvider *memoryProvider=nullptr, QProgressDialog *p=nullptr)
Creates an event layer (multipoint or multiline) by locating features from a (non-spatial) event tabl...
QgsGeometry * simplify(double tolerance) const
Returns a simplified version of this geometry using a specified tolerance value.
Wrapper for iterator of features from vector data provider or vector layer.
A rectangle specified with double values.
Definition: qgsrectangle.h:35
QgsGeometry * locateBetweenMeasures(double fromMeasure, double toMeasure, const QgsGeometry *lineGeom)
Returns linear reference geometry as a multiline (or 0 if no match).
QgsGeometry * convexHull() const
Returns the smallest convex polygon that contains all the points in the geometry. ...
virtual bool addAttributes(const QList< QgsField > &attributes)
Adds new attributes.
bool simplify(QgsVectorLayer *layer, const QString &shapefileName, double tolerance, bool onlySelectedFeatures=false, QProgressDialog *p=nullptr)
Simplify vector layer using (a modified) Douglas-Peucker algorithm and write it to a new shape file...
void append(const T &value)
void setMaximum(int maximum)
void push_back(const T &value)
void setWindowModality(Qt::WindowModality windowModality)
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
QgsFields fields() const
Returns the list of fields of this layer.
int size() const
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest())
Query the provider for features specified in request.
double closestSegmentWithContext(const QgsPoint &point, QgsPoint &minDistPoint, int &afterVertex, double *leftOf=nullptr, double epsilon=DEFAULT_SEGMENT_EPSILON) const
Searches for the closest segment of geometry to the given point.
const_iterator constBegin() const
QList< QgsGeometry * > asGeometryCollection() const
Return contents of the geometry as a list of geometries.
const T & at(int i) const
QGis::GeometryType type() const
Returns type of the geometry as a QGis::GeometryType.
Container of fields for a vector layer.
Definition: qgsfield.h:189
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:76
void setAttributes(const QgsAttributes &attrs)
Sets the feature&#39;s attributes.
Definition: qgsfeature.cpp:115
bool addFeature(QgsFeature &feature, QgsFeatureRendererV2 *renderer=nullptr, QGis::UnitType outputUnit=QGis::Meters)
Add feature to the currently opened data source.
WkbType
Used for symbology operations.
Definition: qgis.h:57
bool centroids(QgsVectorLayer *layer, const QString &shapefileName, bool onlySelectedFeatures=false, QProgressDialog *p=nullptr)
Calculate the true centroids, or &#39;center of mass&#39; for a vector layer and write it to a new shape file...
A convenience class for writing vector files to disk.
const_iterator insert(const T &value)
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:187
virtual bool addFeatures(QgsFeatureList &flist)
Adds a list of features.
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:285
double x() const
Get the x value of the point.
Definition: qgspoint.h:128
int size() const
long featureCount(QgsSymbolV2 *symbol)
Number of features rendered with specified symbol.
QString encoding() const
Get encoding which is used for accessing data.
void setValue(int progress)
void clear()
void setGeometry(const QgsGeometry &geom)
Set this feature&#39;s geometry from another QgsGeometry object.
Definition: qgsfeature.cpp:124
void append(const T &value)
QgsRectangle extent() override
Return the extent of the layer.
const Key & key() const
const QgsFeatureIds & selectedFeaturesIds() const
Return reference to identifiers of selected features.
QgsAttributes attributes() const
Returns the feature&#39;s attributes.
Definition: qgsfeature.cpp:110
const_iterator constEnd() const
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QList< int > QgsAttributeList
void fromGeos(GEOSGeometry *geos)
Set the geometry, feeding in a geometry in GEOS format.
double measurePerimeter(const QgsGeometry *geometry) const
Measures the perimeter of a polygon geometry.
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Append a field. The field must have unique name, otherwise it is rejected (returns false) ...
Definition: qgsfield.cpp:307
const T & value() const
QMap< Key, T >::iterator insert(const Key &key, const T &value)
const_iterator constEnd() const
QGis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:44
QgsGeometry * centroid() const
Returns the center of mass of a geometry.
QHash< Key, T >::iterator insert(const Key &key, const T &value)
virtual QGis::WkbType geometryType() const =0
Get feature type.
bool isValid()
Return the status of the layer.
QgsGeometry * buffer(double distance, int segments) const
Returns a buffer region around this geometry having the given width and with a specified number of se...
A class to represent a point.
Definition: qgspoint.h:65
iterator end()
double measureArea(const QgsGeometry *geometry) const
Measures the area of a geometry.
void setX(double x)
Sets the x value of the point.
Definition: qgspoint.h:105
void setY(double y)
Sets the y value of the point.
Definition: qgspoint.h:113
const_iterator constBegin() const
bool contains(const T &value) const
static QgsGeometry * fromMultiPolyline(const QgsMultiPolyline &multiline)
Creates a new geometry from a QgsMultiPolyline object.
General purpose distance and area calculator.
QgsGeometry * combine(const QgsGeometry *geometry) const
Returns a geometry representing all the points in this geometry and other (a union geometry operation...
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:271
int wkbSize() const
Returns the size of the WKB in asWkb().
static GEOSContextHandle_t getGEOSHandler()
Return GEOS context handle.
bool buffer(QgsVectorLayer *layer, const QString &shapefileName, double bufferDistance, bool onlySelectedFeatures=false, bool dissolve=false, int bufferDistanceField=-1, QProgressDialog *p=nullptr)
Create buffers for a vector layer and write it to a new shape file.
bool isMultipart() const
Returns true if WKB of the geometry is of WKBMulti* type.
Class for storing a coordinate reference system (CRS)
const QgsGeometry * constGeometry() const
Gets a const pointer to the geometry object associated with this feature.
Definition: qgsfeature.cpp:82
QList< T > values() const
static QgsGeometry * fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
QgsGeometry * locateAlongMeasure(double measure, const QgsGeometry *lineGeom)
Returns linear reference geometry.
static QgsGeometry * fromMultiPoint(const QgsMultiPoint &multipoint)
Creates a new geometry from a QgsMultiPoint object.
double y() const
Get the y value of the point.
Definition: qgspoint.h:136
const QgsCoordinateReferenceSystem & crs() const
Returns layer&#39;s spatial reference system.
double toDouble(bool *ok) const
bool extent(QgsVectorLayer *layer, const QString &shapefileName, bool onlySelectedFeatures=false, QProgressDialog *p=nullptr)
Create a polygon based on the extent of all (selected) features and write it to a new shape file...
void show()
void setMinimum(int minimum)
QgsVectorDataProvider * dataProvider()
Returns the data provider.
const_iterator constEnd() const
bool nextFeature(QgsFeature &f)
const_iterator constBegin() const
This is the base class for vector data providers.
Geometry is not required. It may still be returned if e.g. required for a filter condition.
QgsPoint asPoint() const
Return contents of the geometry as a point if wkbType is WKBPoint, otherwise returns [0...
A vector of attributes.
Definition: qgsfeature.h:115
int size() const
Represents a vector layer which manages a vector based data sets.
const unsigned char * asWkb() const
Returns the buffer containing this geometry in WKB format.
double xMinimum() const
Get the x minimum value (left side of rectangle)
Definition: qgsrectangle.h:191
QString toString() const
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
iterator begin()
bool dissolve(QgsVectorLayer *layer, const QString &shapefileName, bool onlySelectedFeatures=false, int uniqueIdField=-1, QProgressDialog *p=nullptr)
Dissolve a vector layer and write it to a new shape file.
bool convexHull(QgsVectorLayer *layer, const QString &shapefileName, bool onlySelectedFeatures=false, int uniqueIdField=-1, QProgressDialog *p=nullptr)
Create convex hull(s) of a vector layer and write it to a new shape file.
QgsRectangle boundingBoxOfSelected()
Returns the bounding box of the selected features.