Quantum GIS API Documentation
1.8
|
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 }