00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
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
00054 if ( onlySelectedFeatures )
00055 {
00056
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
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
00134 tmpGeometry = featureGeometry->simplify( tolerance );
00135
00136 QgsFeature newFeature;
00137 newFeature.setGeometry( tmpGeometry );
00138 newFeature.setAttributeMap( f.attributeMap() );
00139
00140
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
00170 if ( onlySelectedFeatures )
00171 {
00172
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
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
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 )
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;
00418 QMultiMap<QString, QgsFeatureId> map;
00419
00420 if ( onlySelectedFeatures )
00421 {
00422
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
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
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
00471 if ( onlySelectedFeatures )
00472 {
00473
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
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
00548 if ( !dissolveGeometry )
00549 {
00550 QgsDebugMsg( "no dissolved geometry - should not happen" );
00551 return false;
00552 }
00553 dissolveGeometry = dissolveGeometry->convexHull();
00554
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
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;
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
00657 if ( onlySelectedFeatures )
00658 {
00659
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
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;
00777
00778
00779 if ( onlySelectedFeatures )
00780 {
00781
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
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
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
00899 {
00900 QgsFeature newFeature;
00901 newFeature.setGeometry( bufferGeometry );
00902 newFeature.setAttributeMap( f.attributeMap() );
00903
00904
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
00922 QMultiHash< QString, QgsFeatureId > lineLayerIdMap;
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
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
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;
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
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
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
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 , 0 , 5.0 ) );
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
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 );
01178 Q_UNUSED( debugLength );
01179 return QgsPoint( x - normalX, y - normalY );
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
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
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;
01280 bool secondPointClipped;
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 )
01298 {
01299 currentLine.append( pt1 );
01300 }
01301
01302 if ( pt1 != pt2 )
01303 {
01304 currentLine.append( pt2 );
01305 }
01306
01307 if ( secondPointClipped || i == *nPoints - 1 )
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
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
01381 if ( range1 > range2 )
01382 {
01383 tmp = range1;
01384 range1 = range2;
01385 range2 = tmp;
01386 }
01387
01388
01389 if ( m2 < range1 || m1 > range2 )
01390 {
01391 return false;
01392 }
01393
01394
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
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
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
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 )
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;
01459
01460 if ( m1 > m2 )
01461 {
01462 double tmp = m1;
01463 m1 = m2;
01464 m2 = tmp;
01465 reversed = true;
01466 }
01467
01468
01469 if (( m1 - measure ) > tolerance || ( measure - m2 ) > tolerance )
01470 {
01471 pt1Ok = false;
01472 pt2Ok = false;
01473 return;
01474 }
01475
01476
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
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
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 }