Quantum GIS API Documentation  1.8
src/analysis/vector/qgsgeometryanalyzer.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002     qgsgeometryanalyzer.cpp - QGIS Tools for vector geometry analysis
00003                              -------------------
00004     begin                : 19 March 2009
00005     copyright            : (C) Carson Farmer
00006     email                : [email protected]
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00018 #include "qgsgeometryanalyzer.h"
00019 
00020 #include "qgsapplication.h"
00021 #include "qgsfield.h"
00022 #include "qgsfeature.h"
00023 #include "qgslogger.h"
00024 #include "qgscoordinatereferencesystem.h"
00025 #include "qgsvectorfilewriter.h"
00026 #include "qgsvectordataprovider.h"
00027 #include "qgsdistancearea.h"
00028 #include <QProgressDialog>
00029 
00030 bool QgsGeometryAnalyzer::simplify( QgsVectorLayer* layer,
00031                                     const QString& shapefileName,
00032                                     double tolerance,
00033                                     bool onlySelectedFeatures,
00034                                     QProgressDialog *p )
00035 {
00036   if ( !layer )
00037   {
00038     return false;
00039   }
00040 
00041   QgsVectorDataProvider* dp = layer->dataProvider();
00042   if ( !dp )
00043   {
00044     return false;
00045   }
00046 
00047   QGis::WkbType outputType = dp->geometryType();
00048   const QgsCoordinateReferenceSystem crs = layer->crs();
00049 
00050   QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), dp->fields(), outputType, &crs );
00051   QgsFeature currentFeature;
00052 
00053   //take only selection
00054   if ( onlySelectedFeatures )
00055   {
00056     //use QgsVectorLayer::featureAtId
00057     const QgsFeatureIds selection = layer->selectedFeaturesIds();
00058     if ( p )
00059     {
00060       p->setMaximum( selection.size() );
00061     }
00062 
00063     int processedFeatures = 0;
00064     QgsFeatureIds::const_iterator it = selection.constBegin();
00065     for ( ; it != selection.constEnd(); ++it )
00066     {
00067       if ( p )
00068       {
00069         p->setValue( processedFeatures );
00070       }
00071 
00072       if ( p && p->wasCanceled() )
00073       {
00074         break;
00075       }
00076       if ( !layer->featureAtId( *it, currentFeature, true, true ) )
00077       {
00078         continue;
00079       }
00080       simplifyFeature( currentFeature, &vWriter, tolerance );
00081       ++processedFeatures;
00082     }
00083 
00084     if ( p )
00085     {
00086       p->setValue( selection.size() );
00087     }
00088   }
00089   //take all features
00090   else
00091   {
00092     layer->select( layer->pendingAllAttributesList(), QgsRectangle(), true, false );
00093 
00094 
00095     int featureCount = layer->featureCount();
00096     if ( p )
00097     {
00098       p->setMaximum( featureCount );
00099     }
00100     int processedFeatures = 0;
00101 
00102     while ( layer->nextFeature( currentFeature ) )
00103     {
00104       if ( p )
00105       {
00106         p->setValue( processedFeatures );
00107       }
00108       if ( p && p->wasCanceled() )
00109       {
00110         break;
00111       }
00112       simplifyFeature( currentFeature, &vWriter, tolerance );
00113       ++processedFeatures;
00114     }
00115     if ( p )
00116     {
00117       p->setValue( featureCount );
00118     }
00119   }
00120 
00121   return true;
00122 }
00123 
00124 void QgsGeometryAnalyzer::simplifyFeature( QgsFeature& f, QgsVectorFileWriter* vfw, double tolerance )
00125 {
00126   QgsGeometry* featureGeometry = f.geometry();
00127   QgsGeometry* tmpGeometry = 0;
00128 
00129   if ( !featureGeometry )
00130   {
00131     return;
00132   }
00133   // simplify feature
00134   tmpGeometry = featureGeometry->simplify( tolerance );
00135 
00136   QgsFeature newFeature;
00137   newFeature.setGeometry( tmpGeometry );
00138   newFeature.setAttributeMap( f.attributeMap() );
00139 
00140   //add it to vector file writer
00141   if ( vfw )
00142   {
00143     vfw->addFeature( newFeature );
00144   }
00145 }
00146 
00147 bool QgsGeometryAnalyzer::centroids( QgsVectorLayer* layer, const QString& shapefileName,
00148                                      bool onlySelectedFeatures, QProgressDialog* p )
00149 {
00150   if ( !layer )
00151   {
00152     QgsDebugMsg( "No layer passed to centroids" );
00153     return false;
00154   }
00155 
00156   QgsVectorDataProvider* dp = layer->dataProvider();
00157   if ( !dp )
00158   {
00159     QgsDebugMsg( "No data provider for layer passed to centroids" );
00160     return false;
00161   }
00162 
00163   QGis::WkbType outputType = QGis::WKBPoint;
00164   const QgsCoordinateReferenceSystem crs = layer->crs();
00165 
00166   QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), dp->fields(), outputType, &crs );
00167   QgsFeature currentFeature;
00168 
00169   //take only selection
00170   if ( onlySelectedFeatures )
00171   {
00172     //use QgsVectorLayer::featureAtId
00173     const QgsFeatureIds selection = layer->selectedFeaturesIds();
00174     if ( p )
00175     {
00176       p->setMaximum( selection.size() );
00177     }
00178 
00179     int processedFeatures = 0;
00180     QgsFeatureIds::const_iterator it = selection.constBegin();
00181     for ( ; it != selection.constEnd(); ++it )
00182     {
00183       if ( p )
00184       {
00185         p->setValue( processedFeatures );
00186       }
00187 
00188       if ( p && p->wasCanceled() )
00189       {
00190         break;
00191       }
00192       if ( !layer->featureAtId( *it, currentFeature, true, true ) )
00193       {
00194         continue;
00195       }
00196       centroidFeature( currentFeature, &vWriter );
00197       ++processedFeatures;
00198     }
00199 
00200     if ( p )
00201     {
00202       p->setValue( selection.size() );
00203     }
00204   }
00205   //take all features
00206   else
00207   {
00208     layer->select( layer->pendingAllAttributesList(), QgsRectangle(), true, false );
00209 
00210     int featureCount = layer->featureCount();
00211     if ( p )
00212     {
00213       p->setMaximum( featureCount );
00214     }
00215     int processedFeatures = 0;
00216 
00217     while ( layer->nextFeature( currentFeature ) )
00218     {
00219       if ( p )
00220       {
00221         p->setValue( processedFeatures );
00222       }
00223       if ( p && p->wasCanceled() )
00224       {
00225         break;
00226       }
00227       centroidFeature( currentFeature, &vWriter );
00228       ++processedFeatures;
00229     }
00230     if ( p )
00231     {
00232       p->setValue( featureCount );
00233     }
00234   }
00235 
00236   return true;
00237 }
00238 
00239 
00240 void QgsGeometryAnalyzer::centroidFeature( QgsFeature& f, QgsVectorFileWriter* vfw )
00241 {
00242   QgsGeometry* featureGeometry = f.geometry();
00243   QgsGeometry* tmpGeometry = 0;
00244 
00245   if ( !featureGeometry )
00246   {
00247     return;
00248   }
00249 
00250   tmpGeometry = featureGeometry->centroid();
00251 
00252   QgsFeature newFeature;
00253   newFeature.setGeometry( tmpGeometry );
00254   newFeature.setAttributeMap( f.attributeMap() );
00255 
00256   //add it to vector file writer
00257   if ( vfw )
00258   {
00259     vfw->addFeature( newFeature );
00260   }
00261 }
00262 
00263 bool QgsGeometryAnalyzer::extent( QgsVectorLayer* layer,
00264                                   const QString& shapefileName,
00265                                   bool onlySelectedFeatures,
00266                                   QProgressDialog * )
00267 {
00268   if ( !layer )
00269   {
00270     return false;
00271   }
00272 
00273   QgsVectorDataProvider* dp = layer->dataProvider();
00274   if ( !dp )
00275   {
00276     return false;
00277   }
00278 
00279   QGis::WkbType outputType = QGis::WKBPolygon;
00280   const QgsCoordinateReferenceSystem crs = layer->crs();
00281 
00282   QgsFieldMap fields;
00283   fields.insert( 0 , QgsField( QString( "MINX" ), QVariant::Double ) );
00284   fields.insert( 1 , QgsField( QString( "MINY" ), QVariant::Double ) );
00285   fields.insert( 2 , QgsField( QString( "MAXX" ), QVariant::Double ) );
00286   fields.insert( 3 , QgsField( QString( "MAXY" ), QVariant::Double ) );
00287   fields.insert( 4 , QgsField( QString( "CNTX" ), QVariant::Double ) );
00288   fields.insert( 5 , QgsField( QString( "CNTY" ), QVariant::Double ) );
00289   fields.insert( 6 , QgsField( QString( "AREA" ), QVariant::Double ) );
00290   fields.insert( 7 , QgsField( QString( "PERIM" ), QVariant::Double ) );
00291   fields.insert( 8 , QgsField( QString( "HEIGHT" ), QVariant::Double ) );
00292   fields.insert( 9 , QgsField( QString( "WIDTH" ), QVariant::Double ) );
00293 
00294   QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), fields, outputType, &crs );
00295 
00296   QgsRectangle rect;
00297   if ( onlySelectedFeatures )  // take only selection
00298   {
00299     rect = layer->boundingBoxOfSelected();
00300   }
00301   else
00302   {
00303     rect = layer->extent();
00304   }
00305 
00306   double minx = rect.xMinimum();
00307   double miny = rect.yMinimum();
00308   double maxx = rect.xMaximum();
00309   double maxy = rect.yMaximum();
00310   double height = rect.height();
00311   double width = rect.width();
00312   double cntx = minx + ( width / 2.0 );
00313   double cnty = miny + ( height / 2.0 );
00314   double area = width * height;
00315   double perim = ( 2 * width ) + ( 2 * height );
00316 
00317   QgsFeature feat;
00318   QgsAttributeMap map;
00319   map.insert( 0 , QVariant( minx ) );
00320   map.insert( 1 , QVariant( miny ) );
00321   map.insert( 2 , QVariant( maxx ) );
00322   map.insert( 3 , QVariant( maxy ) );
00323   map.insert( 4 , QVariant( cntx ) );
00324   map.insert( 5 , QVariant( cnty ) );
00325   map.insert( 6 , QVariant( area ) );
00326   map.insert( 7 , QVariant( perim ) );
00327   map.insert( 8 , QVariant( height ) );
00328   map.insert( 9 , QVariant( width ) );
00329   feat.setAttributeMap( map );
00330   feat.setGeometry( QgsGeometry::fromRect( rect ) );
00331   vWriter.addFeature( feat );
00332   return true;
00333 }
00334 
00335 QList<double> QgsGeometryAnalyzer::simpleMeasure( QgsGeometry* mpGeometry )
00336 {
00337   QList<double> list;
00338   double perim;
00339   if ( mpGeometry->wkbType() == QGis::WKBPoint )
00340   {
00341     QgsPoint pt = mpGeometry->asPoint();
00342     list.append( pt.x() );
00343     list.append( pt.y() );
00344   }
00345   else
00346   {
00347     QgsDistanceArea measure;
00348     list.append( measure.measure( mpGeometry ) );
00349     if ( mpGeometry->type() == QGis::Polygon )
00350     {
00351       perim = perimeterMeasure( mpGeometry, measure );
00352       list.append( perim );
00353     }
00354   }
00355   return list;
00356 }
00357 
00358 double QgsGeometryAnalyzer::perimeterMeasure( QgsGeometry* geometry, QgsDistanceArea& measure )
00359 {
00360   double value = 0.00;
00361   if ( geometry->isMultipart() )
00362   {
00363     QgsMultiPolygon poly = geometry->asMultiPolygon();
00364     QgsMultiPolygon::iterator it;
00365     QgsPolygon::iterator jt;
00366     for ( it = poly.begin(); it != poly.end(); ++it )
00367     {
00368       for ( jt = it->begin(); jt != it->end(); ++jt )
00369       {
00370         value = value + measure.measure( QgsGeometry::fromPolyline( *jt ) );
00371       }
00372     }
00373   }
00374   else
00375   {
00376     QgsPolygon::iterator jt;
00377     QgsPolygon poly = geometry->asPolygon();
00378     for ( jt = poly.begin(); jt != poly.end(); ++jt )
00379     {
00380       value = value + measure.measure( QgsGeometry::fromPolyline( *jt ) );
00381     }
00382   }
00383   return value;
00384 }
00385 
00386 bool QgsGeometryAnalyzer::convexHull( QgsVectorLayer* layer, const QString& shapefileName,
00387                                       bool onlySelectedFeatures, int uniqueIdField, QProgressDialog* p )
00388 {
00389   if ( !layer )
00390   {
00391     return false;
00392   }
00393   QgsVectorDataProvider* dp = layer->dataProvider();
00394   if ( !dp )
00395   {
00396     return false;
00397   }
00398   bool useField = false;
00399   if ( uniqueIdField == -1 )
00400   {
00401     uniqueIdField = 0;
00402   }
00403   else
00404   {
00405     useField = true;
00406   }
00407   QgsFieldMap fields;
00408   fields.insert( 0 , QgsField( QString( "UID" ), QVariant::String ) );
00409   fields.insert( 1 , QgsField( QString( "AREA" ), QVariant::Double ) );
00410   fields.insert( 2 , QgsField( QString( "PERIM" ), QVariant::Double ) );
00411 
00412   QGis::WkbType outputType = QGis::WKBPolygon;
00413   const QgsCoordinateReferenceSystem crs = layer->crs();
00414 
00415   QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), fields, outputType, &crs );
00416   QgsFeature currentFeature;
00417   QgsGeometry* dissolveGeometry = 0; //dissolve geometry
00418   QMultiMap<QString, QgsFeatureId> map;
00419 
00420   if ( onlySelectedFeatures )
00421   {
00422     //use QgsVectorLayer::featureAtId
00423     const QgsFeatureIds selection = layer->selectedFeaturesIds();
00424     QgsFeatureIds::const_iterator it = selection.constBegin();
00425     for ( ; it != selection.constEnd(); ++it )
00426     {
00427 #if 0
00428       if ( p )
00429       {
00430         p->setValue( processedFeatures );
00431       }
00432       if ( p && p->wasCanceled() )
00433       {
00434         // break; // it may be better to do something else here?
00435         return false;
00436       }
00437 #endif
00438       if ( !layer->featureAtId( *it, currentFeature, true, true ) )
00439       {
00440         continue;
00441       }
00442       map.insert( currentFeature.attributeMap()[ uniqueIdField ].toString(), currentFeature.id() );
00443     }
00444   }
00445   else
00446   {
00447     layer->select( layer->pendingAllAttributesList(), QgsRectangle(), true, false );
00448     while ( layer->nextFeature( currentFeature ) )
00449     {
00450 #if 0
00451       if ( p )
00452       {
00453         p->setValue( processedFeatures );
00454       }
00455       if ( p && p->wasCanceled() )
00456       {
00457         // break; // it may be better to do something else here?
00458         return false;
00459       }
00460 #endif
00461       map.insert( currentFeature.attributeMap()[ uniqueIdField ].toString(), currentFeature.id() );
00462     }
00463   }
00464 
00465   QMultiMap<QString, QgsFeatureId>::const_iterator jt = map.constBegin();
00466   while ( jt != map.constEnd() )
00467   {
00468     QString currentKey = jt.key();
00469     int processedFeatures = 0;
00470     //take only selection
00471     if ( onlySelectedFeatures )
00472     {
00473       //use QgsVectorLayer::featureAtId
00474       const QgsFeatureIds selection = layer->selectedFeaturesIds();
00475       if ( p )
00476       {
00477         p->setMaximum( selection.size() );
00478       }
00479       processedFeatures = 0;
00480       while ( jt != map.constEnd() && ( jt.key() == currentKey || !useField ) )
00481       {
00482         if ( p && p->wasCanceled() )
00483         {
00484           break;
00485         }
00486         if ( selection.contains( jt.value() ) )
00487         {
00488           if ( p )
00489           {
00490             p->setValue( processedFeatures );
00491           }
00492           if ( !layer->featureAtId( jt.value(), currentFeature, true, true ) )
00493           {
00494             continue;
00495           }
00496           convexFeature( currentFeature, processedFeatures, &dissolveGeometry );
00497           ++processedFeatures;
00498         }
00499         ++jt;
00500       }
00501       QList<double> values;
00502       if ( !dissolveGeometry )
00503       {
00504         QgsDebugMsg( "no dissolved geometry - should not happen" );
00505         return false;
00506       }
00507       dissolveGeometry = dissolveGeometry->convexHull();
00508       values = simpleMeasure( dissolveGeometry );
00509       QgsAttributeMap attributeMap;
00510       attributeMap.insert( 0 , QVariant( currentKey ) );
00511       attributeMap.insert( 1 , values[ 0 ] );
00512       attributeMap.insert( 2 , values[ 1 ] );
00513       QgsFeature dissolveFeature;
00514       dissolveFeature.setAttributeMap( attributeMap );
00515       dissolveFeature.setGeometry( dissolveGeometry );
00516       vWriter.addFeature( dissolveFeature );
00517     }
00518     //take all features
00519     else
00520     {
00521       int featureCount = layer->featureCount();
00522       if ( p )
00523       {
00524         p->setMaximum( featureCount );
00525       }
00526       processedFeatures = 0;
00527       while ( jt != map.constEnd() && ( jt.key() == currentKey || !useField ) )
00528       {
00529         if ( p )
00530         {
00531           p->setValue( processedFeatures );
00532         }
00533 
00534         if ( p && p->wasCanceled() )
00535         {
00536           break;
00537         }
00538         if ( !layer->featureAtId( jt.value(), currentFeature, true, true ) )
00539         {
00540           continue;
00541         }
00542         convexFeature( currentFeature, processedFeatures, &dissolveGeometry );
00543         ++processedFeatures;
00544         ++jt;
00545       }
00546       QList<double> values;
00547       // QgsGeometry* tmpGeometry = 0;
00548       if ( !dissolveGeometry )
00549       {
00550         QgsDebugMsg( "no dissolved geometry - should not happen" );
00551         return false;
00552       }
00553       dissolveGeometry = dissolveGeometry->convexHull();
00554       // values = simpleMeasure( tmpGeometry );
00555       values = simpleMeasure( dissolveGeometry );
00556       QgsAttributeMap attributeMap;
00557       attributeMap.insert( 0 , QVariant( currentKey ) );
00558       attributeMap.insert( 1 , QVariant( values[ 0 ] ) );
00559       attributeMap.insert( 2 , QVariant( values[ 1 ] ) );
00560       QgsFeature dissolveFeature;
00561       dissolveFeature.setAttributeMap( attributeMap );
00562       dissolveFeature.setGeometry( dissolveGeometry );
00563       vWriter.addFeature( dissolveFeature );
00564     }
00565   }
00566   return true;
00567 }
00568 
00569 
00570 void QgsGeometryAnalyzer::convexFeature( QgsFeature& f, int nProcessedFeatures, QgsGeometry** dissolveGeometry )
00571 {
00572   QgsGeometry* featureGeometry = f.geometry();
00573   QgsGeometry* tmpGeometry = 0;
00574   QgsGeometry* convexGeometry = 0;
00575 
00576   if ( !featureGeometry )
00577   {
00578     return;
00579   }
00580 
00581   convexGeometry = featureGeometry->convexHull();
00582 
00583   if ( nProcessedFeatures == 0 )
00584   {
00585     *dissolveGeometry = convexGeometry;
00586   }
00587   else
00588   {
00589     tmpGeometry = *dissolveGeometry;
00590     *dissolveGeometry = ( *dissolveGeometry )->combine( convexGeometry );
00591     delete tmpGeometry;
00592     delete convexGeometry;
00593   }
00594 }
00595 
00596 bool QgsGeometryAnalyzer::dissolve( QgsVectorLayer* layer, const QString& shapefileName,
00597                                     bool onlySelectedFeatures, int uniqueIdField, QProgressDialog* p )
00598 {
00599   if ( !layer )
00600   {
00601     return false;
00602   }
00603   QgsVectorDataProvider* dp = layer->dataProvider();
00604   if ( !dp )
00605   {
00606     return false;
00607   }
00608   bool useField = false;
00609   if ( uniqueIdField == -1 )
00610   {
00611     uniqueIdField = 0;
00612   }
00613   else
00614   {
00615     useField = true;
00616   }
00617 
00618   QGis::WkbType outputType = dp->geometryType();
00619   const QgsCoordinateReferenceSystem crs = layer->crs();
00620 
00621   QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), dp->fields(), outputType, &crs );
00622   QgsFeature currentFeature;
00623   QMultiMap<QString, QgsFeatureId> map;
00624 
00625   if ( onlySelectedFeatures )
00626   {
00627     //use QgsVectorLayer::featureAtId
00628     const QgsFeatureIds selection = layer->selectedFeaturesIds();
00629     QgsFeatureIds::const_iterator it = selection.constBegin();
00630     for ( ; it != selection.constEnd(); ++it )
00631     {
00632       if ( !layer->featureAtId( *it, currentFeature, true, true ) )
00633       {
00634         continue;
00635       }
00636       map.insert( currentFeature.attributeMap()[ uniqueIdField ].toString(), currentFeature.id() );
00637     }
00638   }
00639   else
00640   {
00641     layer->select( layer->pendingAllAttributesList(), QgsRectangle(), true, false );
00642     while ( layer->nextFeature( currentFeature ) )
00643     {
00644       map.insert( currentFeature.attributeMap()[ uniqueIdField ].toString(), currentFeature.id() );
00645     }
00646   }
00647 
00648   QgsGeometry *dissolveGeometry = 0; //dissolve geometry
00649   QMultiMap<QString, QgsFeatureId>::const_iterator jt = map.constBegin();
00650   QgsFeature outputFeature;
00651   while ( jt != map.constEnd() )
00652   {
00653     QString currentKey = jt.key();
00654     int processedFeatures = 0;
00655     bool first = true;
00656     //take only selection
00657     if ( onlySelectedFeatures )
00658     {
00659       //use QgsVectorLayer::featureAtId
00660       const QgsFeatureIds selection = layer->selectedFeaturesIds();
00661       if ( p )
00662       {
00663         p->setMaximum( selection.size() );
00664       }
00665       while ( jt != map.constEnd() && ( jt.key() == currentKey || !useField ) )
00666       {
00667         if ( p && p->wasCanceled() )
00668         {
00669           break;
00670         }
00671         if ( selection.contains( jt.value() ) )
00672         {
00673           if ( p )
00674           {
00675             p->setValue( processedFeatures );
00676           }
00677           if ( !layer->featureAtId( jt.value(), currentFeature, true, true ) )
00678           {
00679             continue;
00680           }
00681           if ( first )
00682           {
00683             outputFeature.setAttributeMap( currentFeature.attributeMap() );
00684             first = false;
00685           }
00686           dissolveFeature( currentFeature, processedFeatures, &dissolveGeometry );
00687           ++processedFeatures;
00688         }
00689         ++jt;
00690       }
00691     }
00692     //take all features
00693     else
00694     {
00695       int featureCount = layer->featureCount();
00696       if ( p )
00697       {
00698         p->setMaximum( featureCount );
00699       }
00700       while ( jt != map.constEnd() && ( jt.key() == currentKey || !useField ) )
00701       {
00702         if ( p )
00703         {
00704           p->setValue( processedFeatures );
00705         }
00706 
00707         if ( p && p->wasCanceled() )
00708         {
00709           break;
00710         }
00711         if ( !layer->featureAtId( jt.value(), currentFeature, true, true ) )
00712         {
00713           continue;
00714         }
00715         {
00716           outputFeature.setAttributeMap( currentFeature.attributeMap() );
00717           first = false;
00718         }
00719         dissolveFeature( currentFeature, processedFeatures, &dissolveGeometry );
00720         ++processedFeatures;
00721         ++jt;
00722       }
00723     }
00724     outputFeature.setGeometry( dissolveGeometry );
00725     vWriter.addFeature( outputFeature );
00726   }
00727   return true;
00728 }
00729 
00730 void QgsGeometryAnalyzer::dissolveFeature( QgsFeature& f, int nProcessedFeatures, QgsGeometry** dissolveGeometry )
00731 {
00732   QgsGeometry* featureGeometry = f.geometry();
00733 
00734   if ( !featureGeometry )
00735   {
00736     return;
00737   }
00738 
00739   if ( nProcessedFeatures == 0 )
00740   {
00741     int geomSize = featureGeometry->wkbSize();
00742     *dissolveGeometry = new QgsGeometry();
00743     unsigned char* wkb = new unsigned char[geomSize];
00744     memcpy( wkb, featureGeometry->asWkb(), geomSize );
00745     ( *dissolveGeometry )->fromWkb( wkb, geomSize );
00746   }
00747   else
00748   {
00749     *dissolveGeometry = ( *dissolveGeometry )->combine( featureGeometry );
00750   }
00751 }
00752 
00753 bool QgsGeometryAnalyzer::buffer( QgsVectorLayer* layer, const QString& shapefileName, double bufferDistance,
00754                                   bool onlySelectedFeatures, bool dissolve, int bufferDistanceField, QProgressDialog* p )
00755 {
00756   if ( !layer )
00757   {
00758     return false;
00759   }
00760 
00761   QgsVectorDataProvider* dp = layer->dataProvider();
00762   if ( !dp )
00763   {
00764     return false;
00765   }
00766 
00767   QGis::WkbType outputType = QGis::WKBPolygon;
00768   if ( dissolve )
00769   {
00770     outputType = QGis::WKBMultiPolygon;
00771   }
00772   const QgsCoordinateReferenceSystem crs = layer->crs();
00773 
00774   QgsVectorFileWriter vWriter( shapefileName, dp->encoding(), dp->fields(), outputType, &crs );
00775   QgsFeature currentFeature;
00776   QgsGeometry *dissolveGeometry = 0; //dissolve geometry (if dissolve enabled)
00777 
00778   //take only selection
00779   if ( onlySelectedFeatures )
00780   {
00781     //use QgsVectorLayer::featureAtId
00782     const QgsFeatureIds selection = layer->selectedFeaturesIds();
00783     if ( p )
00784     {
00785       p->setMaximum( selection.size() );
00786     }
00787 
00788     int processedFeatures = 0;
00789     QgsFeatureIds::const_iterator it = selection.constBegin();
00790     for ( ; it != selection.constEnd(); ++it )
00791     {
00792       if ( p )
00793       {
00794         p->setValue( processedFeatures );
00795       }
00796 
00797       if ( p && p->wasCanceled() )
00798       {
00799         break;
00800       }
00801       if ( !layer->featureAtId( *it, currentFeature, true, true ) )
00802       {
00803         continue;
00804       }
00805       bufferFeature( currentFeature, processedFeatures, &vWriter, dissolve, &dissolveGeometry, bufferDistance, bufferDistanceField );
00806       ++processedFeatures;
00807     }
00808 
00809     if ( p )
00810     {
00811       p->setValue( selection.size() );
00812     }
00813   }
00814   //take all features
00815   else
00816   {
00817     layer->select( layer->pendingAllAttributesList(), QgsRectangle(), true, false );
00818 
00819 
00820     int featureCount = layer->featureCount();
00821     if ( p )
00822     {
00823       p->setMaximum( featureCount );
00824     }
00825     int processedFeatures = 0;
00826 
00827     while ( layer->nextFeature( currentFeature ) )
00828     {
00829       if ( p )
00830       {
00831         p->setValue( processedFeatures );
00832       }
00833       if ( p && p->wasCanceled() )
00834       {
00835         break;
00836       }
00837       bufferFeature( currentFeature, processedFeatures, &vWriter, dissolve, &dissolveGeometry, bufferDistance, bufferDistanceField );
00838       ++processedFeatures;
00839     }
00840     if ( p )
00841     {
00842       p->setValue( featureCount );
00843     }
00844   }
00845 
00846   if ( dissolve )
00847   {
00848     QgsFeature dissolveFeature;
00849     if ( !dissolveGeometry )
00850     {
00851       QgsDebugMsg( "no dissolved geometry - should not happen" );
00852       return false;
00853     }
00854     dissolveFeature.setGeometry( dissolveGeometry );
00855     vWriter.addFeature( dissolveFeature );
00856   }
00857   return true;
00858 }
00859 
00860 void QgsGeometryAnalyzer::bufferFeature( QgsFeature& f, int nProcessedFeatures, QgsVectorFileWriter* vfw, bool dissolve,
00861     QgsGeometry** dissolveGeometry, double bufferDistance, int bufferDistanceField )
00862 {
00863   double currentBufferDistance;
00864   QgsGeometry* featureGeometry = f.geometry();
00865   QgsGeometry* tmpGeometry = 0;
00866   QgsGeometry* bufferGeometry = 0;
00867 
00868   if ( !featureGeometry )
00869   {
00870     return;
00871   }
00872 
00873   //create buffer
00874   if ( bufferDistanceField == -1 )
00875   {
00876     currentBufferDistance = bufferDistance;
00877   }
00878   else
00879   {
00880     currentBufferDistance = f.attributeMap()[bufferDistanceField].toDouble();
00881   }
00882   bufferGeometry = featureGeometry->buffer( currentBufferDistance, 5 );
00883 
00884   if ( dissolve )
00885   {
00886     if ( nProcessedFeatures == 0 )
00887     {
00888       *dissolveGeometry = bufferGeometry;
00889     }
00890     else
00891     {
00892       tmpGeometry = *dissolveGeometry;
00893       *dissolveGeometry = ( *dissolveGeometry )->combine( bufferGeometry );
00894       delete tmpGeometry;
00895       delete bufferGeometry;
00896     }
00897   }
00898   else //dissolve
00899   {
00900     QgsFeature newFeature;
00901     newFeature.setGeometry( bufferGeometry );
00902     newFeature.setAttributeMap( f.attributeMap() );
00903 
00904     //add it to vector file writer
00905     if ( vfw )
00906     {
00907       vfw->addFeature( newFeature );
00908     }
00909   }
00910 }
00911 
00912 bool QgsGeometryAnalyzer::eventLayer( QgsVectorLayer* lineLayer, QgsVectorLayer* eventLayer, int lineField, int eventField, QList<int>& unlocatedFeatureIds, const QString& outputLayer,
00913                                       const QString& outputFormat, int locationField1, int locationField2, int offsetField, double offsetScale,
00914                                       bool forceSingleGeometry, QgsVectorDataProvider* memoryProvider, QProgressDialog* p )
00915 {
00916   if ( !lineLayer || !eventLayer || !lineLayer->isValid() || !eventLayer->isValid() )
00917   {
00918     return false;
00919   }
00920 
00921   //create line field / id map for line layer
00922   QMultiHash< QString, QgsFeatureId > lineLayerIdMap; //1:n possible (e.g. several linear reference geometries for one feature in the event layer)
00923   lineLayer->select( QgsAttributeList() << lineField,
00924                      QgsRectangle(), false, false );
00925   QgsFeature fet;
00926   while ( lineLayer->nextFeature( fet ) )
00927   {
00928     lineLayerIdMap.insert( fet.attributeMap()[lineField].toString(), fet.id() );
00929   }
00930 
00931   //create output datasource or attributes in memory provider
00932   QgsVectorFileWriter* fileWriter = 0;
00933   QgsFeatureList memoryProviderFeatures;
00934   if ( !memoryProvider )
00935   {
00936     QGis::WkbType memoryProviderType = QGis::WKBMultiLineString;
00937     if ( locationField2 == -1 )
00938     {
00939       memoryProviderType = forceSingleGeometry ? QGis::WKBPoint : QGis::WKBMultiPoint;
00940     }
00941     else
00942     {
00943       memoryProviderType = forceSingleGeometry ? QGis::WKBLineString : QGis::WKBMultiLineString;
00944     }
00945     fileWriter = new QgsVectorFileWriter( outputLayer,
00946                                           eventLayer->dataProvider()->encoding(),
00947                                           eventLayer->pendingFields(),
00948                                           memoryProviderType,
00949                                           &( lineLayer->crs() ),
00950                                           outputFormat );
00951   }
00952   else
00953   {
00954     memoryProvider->addAttributes( eventLayer->pendingFields().values() );
00955   }
00956 
00957   //iterate over eventLayer and write new features to output file or layer
00958   eventLayer->select( eventLayer->pendingAllAttributesList(), QgsRectangle(), false, false );
00959   QgsGeometry* lrsGeom = 0;
00960   QgsFeature lineFeature;
00961   double measure1, measure2;
00962 
00963   int nEventFeatures = eventLayer->pendingFeatureCount();
00964   int featureCounter = 0;
00965   int nOutputFeatures = 0; //number of output features for the current event feature
00966   if ( p )
00967   {
00968     p->setWindowModality( Qt::WindowModal );
00969     p->setMinimum( 0 );
00970     p->setMaximum( nEventFeatures );
00971     p->show();
00972   }
00973 
00974   while ( eventLayer->nextFeature( fet ) )
00975   {
00976     nOutputFeatures = 0;
00977 
00978     //update progress dialog
00979     if ( p )
00980     {
00981       if ( p->wasCanceled() )
00982       {
00983         break;
00984       }
00985       p->setValue( featureCounter );
00986       ++featureCounter;
00987     }
00988 
00989     measure1 = fet.attributeMap()[locationField1].toDouble();
00990     if ( locationField2 != -1 )
00991     {
00992       measure2 = fet.attributeMap()[locationField2].toDouble();
00993     }
00994 
00995     QList<QgsFeatureId> featureIdList = lineLayerIdMap.values( fet.attributeMap()[eventField].toString() );
00996     QList<QgsFeatureId>::const_iterator featureIdIt = featureIdList.constBegin();
00997     for ( ; featureIdIt != featureIdList.constEnd(); ++featureIdIt )
00998     {
00999       if ( !lineLayer->featureAtId( *featureIdIt, lineFeature, true, false ) )
01000       {
01001         continue;
01002       }
01003 
01004       if ( locationField2 == -1 )
01005       {
01006         lrsGeom = locateAlongMeasure( measure1, lineFeature.geometry() );
01007       }
01008       else
01009       {
01010         lrsGeom = locateBetweenMeasures( measure1, measure2, lineFeature.geometry() );
01011       }
01012 
01013       if ( lrsGeom )
01014       {
01015         ++nOutputFeatures;
01016         addEventLayerFeature( fet, lrsGeom, lineFeature.geometry(), fileWriter, memoryProviderFeatures, offsetField, offsetScale, forceSingleGeometry );
01017       }
01018     }
01019     if ( nOutputFeatures < 1 )
01020     {
01021       unlocatedFeatureIds.push_back( fet.id() );
01022     }
01023   }
01024 
01025   if ( p )
01026   {
01027     p->setValue( nEventFeatures );
01028   }
01029 
01030   if ( memoryProvider )
01031   {
01032     memoryProvider->addFeatures( memoryProviderFeatures );
01033   }
01034   delete fileWriter;
01035   return true;
01036 }
01037 
01038 void QgsGeometryAnalyzer::addEventLayerFeature( QgsFeature& feature, QgsGeometry* geom, QgsGeometry* lineGeom, QgsVectorFileWriter* fileWriter, QgsFeatureList& memoryFeatures,
01039     int offsetField, double offsetScale, bool forceSingleType )
01040 {
01041   if ( !geom )
01042   {
01043     return;
01044   }
01045 
01046   QList<QgsGeometry*> geomList;
01047   if ( forceSingleType )
01048   {
01049     geomList = geom->asGeometryCollection();
01050   }
01051   else
01052   {
01053     geomList.push_back( geom );
01054   }
01055 
01056   QList<QgsGeometry*>::iterator geomIt = geomList.begin();
01057   for ( ; geomIt != geomList.end(); ++geomIt )
01058   {
01059     //consider offset
01060     if ( offsetField >= 0 )
01061     {
01062       double offsetVal = feature.attributeMap()[offsetField].toDouble();
01063       offsetVal *= offsetScale;
01064       createOffsetGeometry( *geomIt, lineGeom, offsetVal );
01065     }
01066 
01067     feature.setGeometry( *geomIt );
01068     if ( fileWriter )
01069     {
01070       fileWriter->addFeature( feature );
01071     }
01072     else
01073     {
01074       memoryFeatures << feature;
01075     }
01076   }
01077 
01078   if ( forceSingleType )
01079   {
01080     delete geom;
01081   }
01082 }
01083 
01084 void QgsGeometryAnalyzer::createOffsetGeometry( QgsGeometry* geom, QgsGeometry* lineGeom, double offset )
01085 {
01086   if ( !geom || !lineGeom )
01087   {
01088     return;
01089   }
01090 
01091   QList<QgsGeometry*> inputGeomList;
01092 
01093   if ( geom->isMultipart() )
01094   {
01095     inputGeomList = geom->asGeometryCollection();
01096   }
01097   else
01098   {
01099     inputGeomList.push_back( geom );
01100   }
01101 
01102   QList<GEOSGeometry*> outputGeomList;
01103   QList<QgsGeometry*>::const_iterator inputGeomIt = inputGeomList.constBegin();
01104   for ( ; inputGeomIt != inputGeomList.constEnd(); ++inputGeomIt )
01105   {
01106     if ( geom->type() == QGis::Line )
01107     {
01108       //geos 3.3 needed for line offsets
01109 #if defined(GEOS_VERSION_MAJOR) && defined(GEOS_VERSION_MINOR) && \
01110       ((GEOS_VERSION_MAJOR>3) || ((GEOS_VERSION_MAJOR==3) && (GEOS_VERSION_MINOR>=3)))
01111       outputGeomList.push_back( GEOSOffsetCurve(( *inputGeomIt )->asGeos(), -offset, 8 /*quadSegments*/, 0 /*joinStyle*/, 5.0 /*mitreLimit*/ ) );
01112 #else
01113       outputGeomList.push_back( GEOSGeom_clone(( *inputGeomIt )->asGeos() ) );
01114 #endif
01115     }
01116     else if ( geom->type() == QGis::Point )
01117     {
01118       QgsPoint p = ( *inputGeomIt )->asPoint();
01119       p = createPointOffset( p.x(), p.y(), offset, lineGeom );
01120       GEOSCoordSequence* ptSeq = GEOSCoordSeq_create( 1, 2 );
01121       GEOSCoordSeq_setX( ptSeq, 0, p.x() );
01122       GEOSCoordSeq_setY( ptSeq, 0, p.y() );
01123       GEOSGeometry* geosPt = GEOSGeom_createPoint( ptSeq );
01124       outputGeomList.push_back( geosPt );
01125     }
01126   }
01127 
01128   if ( !geom->isMultipart() )
01129   {
01130     GEOSGeometry* outputGeom = outputGeomList.at( 0 );
01131     if ( outputGeom )
01132     {
01133       geom->fromGeos( outputGeom );
01134     }
01135   }
01136   else
01137   {
01138     GEOSGeometry** geomArray = new GEOSGeometry*[outputGeomList.size()];
01139     for ( int i = 0; i < outputGeomList.size(); ++i )
01140     {
01141       geomArray[i] = outputGeomList.at( i );
01142     }
01143     GEOSGeometry* collection = 0;
01144     if ( geom->type() == QGis::Point )
01145     {
01146       collection = GEOSGeom_createCollection( GEOS_MULTIPOINT, geomArray, outputGeomList.size() );
01147     }
01148     else if ( geom->type() == QGis::Line )
01149     {
01150       collection = GEOSGeom_createCollection( GEOS_MULTILINESTRING, geomArray, outputGeomList.size() );
01151     }
01152     geom->fromGeos( collection );
01153     delete[] geomArray;
01154   }
01155 }
01156 
01157 QgsPoint QgsGeometryAnalyzer::createPointOffset( double x, double y, double dist, QgsGeometry* lineGeom ) const
01158 {
01159   QgsPoint p( x, y );
01160   QgsPoint minDistPoint;
01161   int afterVertexNr;
01162   lineGeom->closestSegmentWithContext( p, minDistPoint, afterVertexNr );
01163 
01164   int beforeVertexNr = afterVertexNr - 1;
01165   QgsPoint beforeVertex = lineGeom->vertexAt( beforeVertexNr );
01166   QgsPoint afterVertex = lineGeom->vertexAt( afterVertexNr );
01167 
01168   //get normal vector
01169   double dx = afterVertex.x() - beforeVertex.x();
01170   double dy = afterVertex.y() - beforeVertex.y();
01171   double normalX = -dy;
01172   double normalY = dx;
01173   double normalLength = sqrt( normalX * normalX + normalY * normalY );
01174   normalX *= ( dist / normalLength );
01175   normalY *= ( dist / normalLength );
01176 
01177   double debugLength = sqrt( normalX * normalX + normalY * normalY ); //control
01178   Q_UNUSED( debugLength );
01179   return QgsPoint( x - normalX, y - normalY ); //negative values -> left side, positive values -> right side
01180 }
01181 
01182 QgsGeometry* QgsGeometryAnalyzer::locateBetweenMeasures( double fromMeasure, double toMeasure, QgsGeometry* lineGeom )
01183 {
01184   if ( !lineGeom )
01185   {
01186     return 0;
01187   }
01188 
01189   QgsMultiPolyline resultGeom;
01190 
01191   //need to go with WKB and z coordinate until QgsGeometry supports M values
01192   unsigned char* lineWkb = lineGeom->asWkb();
01193 
01194   unsigned char* ptr = lineWkb + 1;
01195   QGis::WkbType wkbType;
01196   memcpy( &wkbType, ptr, sizeof( wkbType ) );
01197   ptr += sizeof( wkbType );
01198 
01199   if ( wkbType != QGis::WKBLineString25D && wkbType != QGis::WKBMultiLineString25D )
01200   {
01201     return 0;
01202   }
01203 
01204   if ( wkbType == QGis::WKBLineString25D )
01205   {
01206     locateBetweenWkbString( ptr, resultGeom, fromMeasure, toMeasure );
01207   }
01208   else if ( wkbType == QGis::WKBMultiLineString25D )
01209   {
01210     int* nLines = ( int* )ptr;
01211     ptr += sizeof( int );
01212     for ( int i = 0; i < *nLines; ++i )
01213     {
01214       ptr += ( 1 + sizeof( wkbType ) );
01215       ptr = locateBetweenWkbString( ptr, resultGeom, fromMeasure, toMeasure );
01216     }
01217   }
01218 
01219   if ( resultGeom.size() < 1 )
01220   {
01221     return 0;
01222   }
01223   return QgsGeometry::fromMultiPolyline( resultGeom );
01224 }
01225 
01226 QgsGeometry* QgsGeometryAnalyzer::locateAlongMeasure( double measure, QgsGeometry* lineGeom )
01227 {
01228   if ( !lineGeom )
01229   {
01230     return 0;
01231   }
01232 
01233   QgsMultiPoint resultGeom;
01234 
01235   //need to go with WKB and z coordinate until QgsGeometry supports M values
01236   unsigned char* lineWkb = lineGeom->asWkb();
01237 
01238   unsigned char* ptr = lineWkb + 1;
01239   QGis::WkbType wkbType;
01240   memcpy( &wkbType, ptr, sizeof( wkbType ) );
01241   ptr += sizeof( wkbType );
01242 
01243   if ( wkbType != QGis::WKBLineString25D && wkbType != QGis::WKBMultiLineString25D )
01244   {
01245     return 0;
01246   }
01247 
01248   if ( wkbType == QGis::WKBLineString25D )
01249   {
01250     locateAlongWkbString( ptr, resultGeom, measure );
01251   }
01252   else if ( wkbType == QGis::WKBMultiLineString25D )
01253   {
01254     int* nLines = ( int* )ptr;
01255     ptr += sizeof( int );
01256     for ( int i = 0; i < *nLines; ++i )
01257     {
01258       ptr += ( 1 + sizeof( wkbType ) );
01259       ptr = locateAlongWkbString( ptr, resultGeom, measure );
01260     }
01261   }
01262 
01263   if ( resultGeom.size() < 1 )
01264   {
01265     return 0;
01266   }
01267   return QgsGeometry::fromMultiPoint( resultGeom );
01268 }
01269 
01270 unsigned char* QgsGeometryAnalyzer::locateBetweenWkbString( unsigned char* ptr, QgsMultiPolyline& result, double fromMeasure, double toMeasure )
01271 {
01272   int* nPoints = ( int* ) ptr;
01273   ptr += sizeof( int );
01274   double prevx, prevy, prevz;
01275   double *x, *y, *z;
01276   QgsPolyline currentLine;
01277 
01278   QgsPoint pt1, pt2;
01279   bool measureInSegment; //true if measure is contained in the segment
01280   bool secondPointClipped; //true if second point is != segment endpoint
01281 
01282 
01283   for ( int i = 0; i < *nPoints; ++i )
01284   {
01285     x = ( double* )ptr;
01286     ptr += sizeof( double );
01287     y = ( double* )ptr;
01288     ptr += sizeof( double );
01289     z = ( double* ) ptr;
01290     ptr += sizeof( double );
01291 
01292     if ( i > 0 )
01293     {
01294       measureInSegment = clipSegmentByRange( prevx, prevy, prevz, *x, *y, *z, fromMeasure, toMeasure, pt1, pt2, secondPointClipped );
01295       if ( measureInSegment )
01296       {
01297         if ( currentLine.size() < 1 ) //no points collected yet, so the first point needs to be added to the line
01298         {
01299           currentLine.append( pt1 );
01300         }
01301 
01302         if ( pt1 != pt2 ) //avoid duplicated entry if measure value equals m-value of vertex
01303         {
01304           currentLine.append( pt2 );
01305         }
01306 
01307         if ( secondPointClipped || i == *nPoints - 1 ) //close current segment
01308         {
01309           if ( currentLine.size() > 1 )
01310           {
01311             result.append( currentLine );
01312           }
01313           currentLine.clear();
01314         }
01315       }
01316     }
01317     prevx = *x; prevy = *y; prevz = *z;
01318   }
01319   return ptr;
01320 }
01321 
01322 unsigned char* QgsGeometryAnalyzer::locateAlongWkbString( unsigned char* ptr, QgsMultiPoint& result, double measure )
01323 {
01324   int* nPoints = ( int* ) ptr;
01325   ptr += sizeof( int );
01326   double prevx, prevy, prevz;
01327   double *x, *y, *z;
01328 
01329   QgsPoint pt1, pt2;
01330   bool pt1Ok, pt2Ok;
01331 
01332   for ( int i = 0; i < *nPoints; ++i )
01333   {
01334     x = ( double* )ptr;
01335     ptr += sizeof( double );
01336     y = ( double* )ptr;
01337     ptr += sizeof( double );
01338     z = ( double* ) ptr;
01339     ptr += sizeof( double );
01340 
01341     if ( i > 0 )
01342     {
01343       locateAlongSegment( prevx, prevy, prevz, *x, *y, *z, measure, pt1Ok, pt1, pt2Ok, pt2 );
01344       if ( pt1Ok )
01345       {
01346         result.append( pt1 );
01347       }
01348       if ( pt2Ok && ( i == ( *nPoints - 1 ) ) )
01349       {
01350         result.append( pt2 );
01351       }
01352     }
01353     prevx = *x; prevy = *y; prevz = *z;
01354   }
01355   return ptr;
01356 }
01357 
01358 bool QgsGeometryAnalyzer::clipSegmentByRange( double x1, double y1, double m1, double x2, double y2, double m2, double range1, double range2, QgsPoint& pt1,
01359     QgsPoint& pt2, bool& secondPointClipped )
01360 {
01361   bool reversed = m1 > m2;
01362   double tmp;
01363 
01364   //reverse m1, m2 if necessary (and consequently also x1,x2 / y1, y2)
01365   if ( reversed )
01366   {
01367     tmp = m1;
01368     m1 = m2;
01369     m2 = tmp;
01370 
01371     tmp = x1;
01372     x1 = x2;
01373     x2 = tmp;
01374 
01375     tmp = y1;
01376     y1 = y2;
01377     y2 = tmp;
01378   }
01379 
01380   //reverse range1, range2 if necessary
01381   if ( range1 > range2 )
01382   {
01383     tmp = range1;
01384     range1 = range2;
01385     range2 = tmp;
01386   }
01387 
01388   //segment completely outside of range
01389   if ( m2 < range1 || m1 > range2 )
01390   {
01391     return false;
01392   }
01393 
01394   //segment completely inside of range
01395   if ( m2 <= range2 && m1 >= range1 )
01396   {
01397     if ( reversed )
01398     {
01399       pt1.setX( x2 ); pt1.setY( y2 );
01400       pt2.setX( x1 ); pt2.setY( y1 );
01401     }
01402     else
01403     {
01404       pt1.setX( x1 ); pt1.setY( y1 );
01405       pt2.setX( x2 ); pt2.setY( y2 );
01406     }
01407     secondPointClipped = false;
01408     return true;
01409   }
01410 
01411   //m1 inside and m2 not
01412   if ( m1 >= range1 && m1 <= range2 )
01413   {
01414     pt1.setX( x1 ); pt1.setY( y1 );
01415     double dist = ( range2 - m1 ) / ( m2 - m1 );
01416     pt2.setX( x1 + ( x2 - x1 ) * dist );
01417     pt2.setY( y1 + ( y2 - y1 ) * dist );
01418     secondPointClipped = !reversed;
01419   }
01420 
01421   //m2 inside and m1 not
01422   if ( m2 >= range1 && m2 <= range2 )
01423   {
01424     pt2.setX( x2 ); pt2.setY( y2 );
01425     double dist = ( m2 - range1 ) / ( m2 - m1 );
01426     pt1.setX( x2 - ( x2 - x1 ) * dist );
01427     pt1.setY( y2 - ( y2 - y1 ) * dist );
01428     secondPointClipped = reversed;
01429   }
01430 
01431   //range1 and range 2 both inside the segment
01432   if ( range1 >= m1 && range2 <= m2 )
01433   {
01434     double dist1 = ( range1 - m1 ) / ( m2 - m1 );
01435     double dist2 = ( range2 - m1 ) / ( m2 - m1 );
01436     pt1.setX( x1 + ( x2 - x1 ) * dist1 );
01437     pt1.setY( y1 + ( y2 - y1 ) * dist1 );
01438     pt2.setX( x1 + ( x2 - x1 ) * dist2 );
01439     pt2.setY( y1 + ( y2 - y1 ) * dist2 );
01440     secondPointClipped = true;
01441   }
01442 
01443   if ( reversed ) //switch p1 and p2
01444   {
01445     QgsPoint tmpPt = pt1;
01446     pt1 = pt2;
01447     pt2 = tmpPt;
01448   }
01449 
01450   return true;
01451 }
01452 
01453 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 )
01454 {
01455   bool reversed = false;
01456   pt1Ok = false;
01457   pt2Ok = false;
01458   double tolerance = 0.000001; //work with a small tolerance to catch e.g. locations at endpoints
01459 
01460   if ( m1 > m2 )
01461   {
01462     double tmp = m1;
01463     m1 = m2;
01464     m2 = tmp;
01465     reversed = true;
01466   }
01467 
01468   //segment does not match
01469   if (( m1 - measure ) > tolerance || ( measure - m2 ) > tolerance )
01470   {
01471     pt1Ok = false;
01472     pt2Ok = false;
01473     return;
01474   }
01475 
01476   //match with vertex1
01477   if ( doubleNear( m1, measure, tolerance ) )
01478   {
01479     if ( reversed )
01480     {
01481       pt2Ok = true;
01482       pt2.setX( x2 ); pt2.setY( y2 );
01483     }
01484     else
01485     {
01486       pt1Ok = true;
01487       pt1.setX( x1 ); pt1.setY( y1 );
01488     }
01489   }
01490 
01491   //match with vertex2
01492   if ( doubleNear( m2, measure, tolerance ) )
01493   {
01494     if ( reversed )
01495     {
01496       pt1Ok = true;
01497       pt1.setX( x1 ); pt1.setY( y1 );
01498     }
01499     else
01500     {
01501       pt2Ok = true;
01502       pt2.setX( x2 ); pt2.setY( y2 );
01503     }
01504   }
01505 
01506 
01507   if ( pt1Ok || pt2Ok )
01508   {
01509     return;
01510   }
01511 
01512   //match between the vertices
01513   if ( doubleNear( m1, m2 ) )
01514   {
01515     pt1.setX( x1 );
01516     pt1.setY( y1 );
01517     pt1Ok = true;
01518     return;
01519   }
01520   double dist = ( measure - m1 ) / ( m2 - m1 );
01521   if ( reversed )
01522   {
01523     dist = 1 - dist;
01524   }
01525 
01526   pt1.setX( x1 + dist * ( x2 - x1 ) );
01527   pt1.setY( y1 + dist * ( y2 - y1 ) );
01528   pt1Ok = true;
01529 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines