Measure tool fixes:

- Make unit selection more robust (doesn't rely on translations)
- Make calculation of lengths in degrees use a cartesian
calculation when project CRS is in degrees
- Fix display of calculation details, was misleading and
inaccurate for many CRS/unit combinations
This commit is contained in:
Nyall Dawson 2016-02-07 22:07:50 +11:00
parent 33c37d0cb2
commit b712f3cf4b
3 changed files with 98 additions and 43 deletions

View File

@ -50,18 +50,17 @@ QgsMeasureDialog::QgsMeasureDialog( QgsMeasureTool* tool, Qt::WindowFlags f )
mMeasureArea = tool->measureArea(); mMeasureArea = tool->measureArea();
mTotal = 0.; mTotal = 0.;
mUnitsCombo->addItem( QgsUnitTypes::toString( QGis::Meters ) ); mUnitsCombo->addItem( QgsUnitTypes::toString( QGis::Meters ), QGis::Meters );
mUnitsCombo->addItem( QgsUnitTypes::toString( QGis::Feet ) ); mUnitsCombo->addItem( QgsUnitTypes::toString( QGis::Feet ), QGis::Feet );
mUnitsCombo->addItem( QgsUnitTypes::toString( QGis::Degrees ) ); mUnitsCombo->addItem( QgsUnitTypes::toString( QGis::Degrees ), QGis::Degrees );
mUnitsCombo->addItem( QgsUnitTypes::toString( QGis::NauticalMiles ) ); mUnitsCombo->addItem( QgsUnitTypes::toString( QGis::NauticalMiles ), QGis::NauticalMiles );
QSettings settings; QSettings settings;
QString units = settings.value( "/qgis/measure/displayunits", QgsUnitTypes::encodeUnit( QGis::Meters ) ).toString(); QString units = settings.value( "/qgis/measure/displayunits", QgsUnitTypes::encodeUnit( QGis::Meters ) ).toString();
mUnitsCombo->setCurrentIndex( mUnitsCombo->findText( QgsUnitTypes::toString( QgsUnitTypes::decodeDistanceUnit( units ) ), Qt::MatchFixedString ) ); mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( QgsUnitTypes::decodeDistanceUnit( units ) ) );
updateSettings(); updateSettings();
connect( mUnitsCombo, SIGNAL( currentIndexChanged( const QString & ) ), this, SLOT( unitsChanged( const QString & ) ) ); connect( mUnitsCombo, SIGNAL( currentIndexChanged( int ) ), this, SLOT( unitsChanged( int ) ) );
connect( buttonBox, SIGNAL( rejected() ), this, SLOT( reject() ) ); connect( buttonBox, SIGNAL( rejected() ), this, SLOT( reject() ) );
groupBox->setCollapsed( true ); groupBox->setCollapsed( true );
@ -79,7 +78,7 @@ void QgsMeasureDialog::updateSettings()
mDecimalPlaces = settings.value( "/qgis/measure/decimalplaces", "3" ).toInt(); mDecimalPlaces = settings.value( "/qgis/measure/decimalplaces", "3" ).toInt();
mCanvasUnits = mTool->canvas()->mapUnits(); mCanvasUnits = mTool->canvas()->mapUnits();
// Configure QgsDistanceArea // Configure QgsDistanceArea
mDisplayUnits = QgsUnitTypes::stringToDistanceUnit( mUnitsCombo->currentText() ); mDisplayUnits = static_cast< QGis::UnitType >( mUnitsCombo->itemData( mUnitsCombo->currentIndex() ).toInt() );
mDa.setSourceCrs( mTool->canvas()->mapSettings().destinationCrs().srsid() ); mDa.setSourceCrs( mTool->canvas()->mapSettings().destinationCrs().srsid() );
mDa.setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) ); mDa.setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) );
// Only use ellipsoidal calculation when project wide transformation is enabled. // Only use ellipsoidal calculation when project wide transformation is enabled.
@ -103,9 +102,9 @@ void QgsMeasureDialog::updateSettings()
updateUi(); updateUi();
} }
void QgsMeasureDialog::unitsChanged( const QString &units ) void QgsMeasureDialog::unitsChanged( int index )
{ {
mDisplayUnits = QgsUnitTypes::stringToDistanceUnit( units ); mDisplayUnits = static_cast< QGis::UnitType >( mUnitsCombo->itemData( index ).toInt() );
mTable->clear(); mTable->clear();
mTotal = 0.; mTotal = 0.;
updateUi(); updateUi();
@ -243,11 +242,12 @@ void QgsMeasureDialog::saveWindowLocation()
settings.setValue( key, height() ); settings.setValue( key, height() );
} }
QString QgsMeasureDialog::formatDistance( double distance ) QString QgsMeasureDialog::formatDistance( double distance, bool convertUnits )
{ {
QSettings settings; QSettings settings;
bool baseUnit = settings.value( "/qgis/measure/keepbaseunit", false ).toBool(); bool baseUnit = settings.value( "/qgis/measure/keepbaseunit", false ).toBool();
if ( convertUnits )
distance = convertLength( distance, mDisplayUnits ); distance = convertLength( distance, mDisplayUnits );
return QgsDistanceArea::textUnit( distance, mDecimalPlaces, mDisplayUnits, false, baseUnit ); return QgsDistanceArea::textUnit( distance, mDecimalPlaces, mDisplayUnits, false, baseUnit );
} }
@ -266,41 +266,88 @@ void QgsMeasureDialog::updateUi()
{ {
// Set tooltip to indicate how we calculate measurments // Set tooltip to indicate how we calculate measurments
QString toolTip = tr( "The calculations are based on:" ); QString toolTip = tr( "The calculations are based on:" );
bool forceCartesian = false;
bool convertToDisplayUnits = true;
if ( mTool->canvas()->mapSettings().destinationCrs().mapUnits() == QGis::Degrees
&& mDisplayUnits == QGis::Degrees )
{
//both source and destination units are degrees
toolTip += "<br> * " + tr( "Both project CRS (%1) and measured length are in degrees, so measurement is calculated using cartesian calculations in degrees." ).arg(
mTool->canvas()->mapSettings().destinationCrs().description() );
forceCartesian = true;
convertToDisplayUnits = false; //not required since we will be measuring in degrees
}
else
{
QGis::UnitType resultUnit = QGis::UnknownUnit;
if ( ! mTool->canvas()->hasCrsTransformEnabled() ) if ( ! mTool->canvas()->hasCrsTransformEnabled() )
{ {
resultUnit = mTool->canvas()->mapSettings().destinationCrs().mapUnits();
toolTip += "<br> * " + tr( "Project CRS transformation is turned off." ) + ' '; toolTip += "<br> * " + tr( "Project CRS transformation is turned off." ) + ' ';
toolTip += tr( "Canvas units setting is taken from project properties setting (%1)." ).arg( QgsUnitTypes::toString( mCanvasUnits ) ); toolTip += tr( "Distance is calculated in %1, based on project CRS (%2)." ).arg( QgsUnitTypes::toString( resultUnit ),
toolTip += "<br> * " + tr( "Ellipsoidal calculation is not possible, as project CRS is undefined." ); mTool->canvas()->mapSettings().destinationCrs().description() );
toolTip += "<br> * " + tr( "Ellipsoidal calculation is not possible with CRS transformation disabled." );
setWindowTitle( tr( "Measure (OTF off)" ) ); setWindowTitle( tr( "Measure (OTF off)" ) );
} }
else else
{ {
if ( mDa.ellipsoidalEnabled() ) if ( mDa.willUseEllipsoid() )
{ {
resultUnit = QGis::Meters;
toolTip += "<br> * " + tr( "Project CRS transformation is turned on and ellipsoidal calculation is selected." ) + ' '; toolTip += "<br> * " + tr( "Project CRS transformation is turned on and ellipsoidal calculation is selected." ) + ' ';
toolTip += "<br> * " + tr( "The coordinates are transformed to the chosen ellipsoid (%1), and the result is in meters" ).arg( mDa.ellipsoid() ); toolTip += "<br> * " + tr( "The coordinates are transformed to the chosen ellipsoid (%1), and the measurement is calculated in %2." ).arg( mDa.ellipsoid(),
QgsUnitTypes::toString( resultUnit ) );
} }
else else
{ {
toolTip += "<br> * " + tr( "Project CRS transformation is turned on but ellipsoidal calculation is not selected." ); resultUnit = mTool->canvas()->mapSettings().destinationCrs().mapUnits();
toolTip += "<br> * " + tr( "The canvas units setting is taken from the project CRS (%1)." ).arg( QgsUnitTypes::toString( mCanvasUnits ) ); toolTip += "<br> * " + tr( "Project CRS transformation is turned on but ellipsoidal calculation is not selected." ) + ' ';
toolTip += tr( "Distance is calculated in %1, based on project CRS (%2)." ).arg( QgsUnitTypes::toString( resultUnit ),
mTool->canvas()->mapSettings().destinationCrs().description() );
} }
setWindowTitle( tr( "Measure (OTF on)" ) ); setWindowTitle( tr( "Measure (OTF on)" ) );
} }
if (( mCanvasUnits == QGis::Meters && mDisplayUnits == QGis::Feet ) || ( mCanvasUnits == QGis::Feet && mDisplayUnits == QGis::Meters ) ) if ( QgsUnitTypes::unitType( resultUnit ) == QgsUnitTypes::Geographic &&
QgsUnitTypes::unitType( mDisplayUnits ) == QgsUnitTypes::Standard )
{ {
toolTip += "<br> * " + tr( "Finally, the value is converted from %1 to %2." ).arg( QgsUnitTypes::toString( mCanvasUnits ), QgsUnitTypes::toString( mDisplayUnits ) ); toolTip += "<br> * Distance is roughly converted to meters by using scale at equator (1 degree = 111319.49 meters).";
resultUnit = QGis::Meters;
}
else if ( QgsUnitTypes::unitType( resultUnit ) == QgsUnitTypes::Standard &&
QgsUnitTypes::unitType( mDisplayUnits ) == QgsUnitTypes::Geographic )
{
toolTip += "<br> * Distance is roughly converted to degrees by using scale at equator (1 degree = 111319.49 meters).";
resultUnit = QGis::Degrees;
}
if ( resultUnit != mDisplayUnits )
{
if ( QgsUnitTypes::unitType( resultUnit ) == QgsUnitTypes::Standard &&
QgsUnitTypes::unitType( mDisplayUnits ) == QgsUnitTypes::Standard )
{
// only shown if both conditions are true:
// - the display unit is a standard distance measurement (eg feet)
// - either the canvas units is also a standard distance OR we are using an ellipsoid (in which case the
// value will be in meters)
toolTip += "<br> * " + tr( "The value is converted from %1 to %2." ).arg( QgsUnitTypes::toString( resultUnit ),
QgsUnitTypes::toString( mDisplayUnits ) );
}
else
{
//should not be possible!
}
}
} }
editTotal->setToolTip( toolTip ); editTotal->setToolTip( toolTip );
mTable->setToolTip( toolTip ); mTable->setToolTip( toolTip );
mNotesLabel->setText( toolTip ); mNotesLabel->setText( toolTip );
QGis::UnitType newDisplayUnits; mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( mDisplayUnits ) );
double dummy = 1.0; mTable->setHeaderLabels( QStringList( tr( "Segments [%1]" ).arg( QgsUnitTypes::toString( mDisplayUnits ) ) ) );
convertMeasurement( dummy, newDisplayUnits, true );
mTable->setHeaderLabels( QStringList( tr( "Segments [%1]" ).arg( QgsUnitTypes::toString( newDisplayUnits ) ) ) );
if ( mMeasureArea ) if ( mMeasureArea )
{ {
@ -318,14 +365,24 @@ void QgsMeasureDialog::updateUi()
bool b = true; // first point bool b = true; // first point
QgsPoint p1, p2; QgsPoint p1, p2;
mTotal = 0;
for ( it = mTool->points().constBegin(); it != mTool->points().constEnd(); ++it ) for ( it = mTool->points().constBegin(); it != mTool->points().constEnd(); ++it )
{ {
p2 = *it; p2 = *it;
if ( !b ) if ( !b )
{ {
double d = mDa.measureLine( p1, p2 ); double d = -1;
if ( forceCartesian )
{
//cartesian calculation forced
d = sqrt( p2.sqrDist( p1 ) );
mTotal += d;
}
else
{
d = mDa.measureLine( p1, p2 );
d = convertLength( d, mDisplayUnits ); d = convertLength( d, mDisplayUnits );
}
QTreeWidgetItem *item = new QTreeWidgetItem( QStringList( QLocale::system().toString( d, 'f', mDecimalPlaces ) ) ); QTreeWidgetItem *item = new QTreeWidgetItem( QStringList( QLocale::system().toString( d, 'f', mDecimalPlaces ) ) );
item->setTextAlignment( 0, Qt::AlignRight ); item->setTextAlignment( 0, Qt::AlignRight );
@ -335,15 +392,17 @@ void QgsMeasureDialog::updateUi()
p1 = p2; p1 = p2;
b = false; b = false;
} }
if ( !forceCartesian )
mTotal = mDa.measureLine( mTool->points() ); mTotal = mDa.measureLine( mTool->points() );
mTable->show(); // Show the table with items mTable->show(); // Show the table with items
editTotal->setText( formatDistance( mTotal ) ); editTotal->setText( formatDistance( mTotal, convertToDisplayUnits ) );
} }
} }
void QgsMeasureDialog::convertMeasurement( double &measure, QGis::UnitType &u, bool isArea ) void QgsMeasureDialog::convertMeasurement( double &measure, QGis::UnitType &u, bool isArea )
{ {
// Helper for converting between meters and feet // Helper for converting between units
// The parameter &u is out only... // The parameter &u is out only...
// Get the canvas units // Get the canvas units

View File

@ -66,7 +66,7 @@ class APP_EXPORT QgsMeasureDialog : public QDialog, private Ui::QgsMeasureBase
void updateSettings(); void updateSettings();
private slots: private slots:
void unitsChanged( const QString &units ); void unitsChanged( int index );
//! Open configuration tab //! Open configuration tab
void openConfigTab(); void openConfigTab();
@ -74,7 +74,7 @@ class APP_EXPORT QgsMeasureDialog : public QDialog, private Ui::QgsMeasureBase
private: private:
//! formats distance to most appropriate units //! formats distance to most appropriate units
QString formatDistance( double distance ); QString formatDistance( double distance, bool convertUnits = true );
//! formats area to most appropriate units //! formats area to most appropriate units
QString formatArea( double area ); QString formatArea( double area );

View File

@ -108,12 +108,8 @@ void QgsMeasureTool::restart()
mRubberBand->reset( mMeasureArea ? QGis::Polygon : QGis::Line ); mRubberBand->reset( mMeasureArea ? QGis::Polygon : QGis::Line );
mRubberBandPoints->reset( QGis::Point ); mRubberBandPoints->reset( QGis::Point );
// re-read settings
updateSettings();
mDone = true; mDone = true;
mWrongProjectProjection = false; mWrongProjectProjection = false;
} }
void QgsMeasureTool::updateSettings() void QgsMeasureTool::updateSettings()