|
QGIS API Documentation
master-59fd5e0
|
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 }