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