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