Skip to content

Commit

Permalink
Add centimeter and millimeter based units to Project Properties and M…
Browse files Browse the repository at this point in the history
…ap Tools Options

Fix the "map units" option in distance measurements when using geographic coordinate systems
  • Loading branch information
jdugge authored and nyalldawson committed Jan 7, 2019
1 parent 0cc809b commit 3e05c99
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 42 deletions.
6 changes: 4 additions & 2 deletions python/core/auto_generated/qgsdistancearea.sip.in
Expand Up @@ -189,23 +189,25 @@ Measures the perimeter of a polygon geometry.
.. versionadded:: 2.12
%End

double measureLine( const QVector<QgsPointXY> &points ) const;
double measureLine( const QVector<QgsPointXY> &points, bool forceCartesian = false ) const;
%Docstring
Measures the length of a line with multiple segments.

:param points: list of points in line
:param forceCartesian: calculate distances in cartesian coordinates

:return: length of line. The units for the returned length can be retrieved by calling lengthUnits().

.. seealso:: :py:func:`lengthUnits`
%End

double measureLine( const QgsPointXY &p1, const QgsPointXY &p2 ) const;
double measureLine( const QgsPointXY &p1, const QgsPointXY &p2, bool forceCartesian = false ) const;
%Docstring
Measures the distance between two points.

:param p1: start of line
:param p2: end of line
:param forceCartesian: calculate distances in cartesian coordinates

:return: distance between points. The units for the returned distance can be retrieved by calling lengthUnits().

Expand Down
107 changes: 78 additions & 29 deletions src/app/qgsmeasuredialog.cpp
Expand Up @@ -51,9 +51,19 @@ QgsMeasureDialog::QgsMeasureDialog( QgsMeasureTool *tool, Qt::WindowFlags f )

repopulateComboBoxUnits( mMeasureArea );
if ( mMeasureArea )
mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( QgsProject::instance()->areaUnits() ) );
{
if ( useMapUnits )
mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( QgsUnitTypes::AreaUnknownUnit ) );
else
mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( QgsProject::instance()->areaUnits() ) );
}
else
mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( QgsProject::instance()->distanceUnits() ) );
{
if ( useMapUnits )
mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( QgsUnitTypes::DistanceUnknownUnit ) );
else
mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( QgsProject::instance()->distanceUnits() ) );
}

if ( !mCanvas->mapSettings().destinationCrs().isValid() )
{
Expand Down Expand Up @@ -103,21 +113,51 @@ void QgsMeasureDialog::updateSettings()
mCanvasUnits = mCanvas->mapUnits();
// Configure QgsDistanceArea
mDistanceUnits = QgsProject::instance()->distanceUnits();
mapDistanceUnits = QgsProject::instance()->crs().mapUnits();
mAreaUnits = QgsProject::instance()->areaUnits();
mDa.setSourceCrs( mCanvas->mapSettings().destinationCrs(), QgsProject::instance()->transformContext() );
mDa.setEllipsoid( QgsProject::instance()->ellipsoid() );

mTable->clear();
mTotal = 0;
updateUi();

if ( !mCanvas->mapSettings().destinationCrs().isValid() ||
( mCanvas->mapSettings().destinationCrs().mapUnits() == QgsUnitTypes::DistanceDegrees
&& mDistanceUnits == QgsUnitTypes::DistanceDegrees ) )
forceCartesian = true;
else
forceCartesian = false;
}

void QgsMeasureDialog::unitsChanged( int index )
{
if ( mMeasureArea )
{
mAreaUnits = static_cast< QgsUnitTypes::AreaUnit >( mUnitsCombo->itemData( index ).toInt() );
if ( mAreaUnits == QgsUnitTypes::AreaUnknownUnit )
{
useMapUnits = true;
mAreaUnits = QgsUnitTypes::distanceToAreaUnit( mapDistanceUnits );
}
else
{
useMapUnits = false;
}
}
else
{
mDistanceUnits = static_cast< QgsUnitTypes::DistanceUnit >( mUnitsCombo->itemData( index ).toInt() );
if ( mDistanceUnits == QgsUnitTypes::DistanceUnknownUnit )
{
useMapUnits = true;
mDistanceUnits = mapDistanceUnits;
}
else
{
useMapUnits = false;
}
}
mTable->clear();
mTotal = 0.;
updateUi();
Expand All @@ -142,6 +182,13 @@ void QgsMeasureDialog::restart()

void QgsMeasureDialog::mouseMove( const QgsPointXY &point )
{
if ( !mCanvas->mapSettings().destinationCrs().isValid() ||
( mCanvas->mapSettings().destinationCrs().mapUnits() == QgsUnitTypes::DistanceDegrees
&& mDistanceUnits == QgsUnitTypes::DistanceDegrees ) )
forceCartesian = true;
else
forceCartesian = false;

mLastMousePoint = point;
// show current distance/area while moving the point
// by creating a temporary copy of point array
Expand All @@ -157,11 +204,10 @@ void QgsMeasureDialog::mouseMove( const QgsPointXY &point )
{
QVector< QgsPointXY > tmpPoints = mTool->points();
QgsPointXY p1( tmpPoints.at( tmpPoints.size() - 1 ) ), p2( point );
double d = mDa.measureLine( p1, p2 );

editTotal->setText( formatDistance( mTotal + d ) );

d = convertLength( d, mDistanceUnits );
double d = mDa.measureLine( p1, p2, forceCartesian );
editTotal->setText( formatDistance( mTotal + d, !forceCartesian ) );
if ( !forceCartesian )
d = convertLength( d, mDistanceUnits );

// Set moving
QTreeWidgetItem *item = mTable->topLevelItem( mTable->topLevelItemCount() - 1 );
Expand Down Expand Up @@ -191,8 +237,8 @@ void QgsMeasureDialog::addPoint()
}
if ( numPoints > 1 )
{
mTotal = mDa.measureLine( mTool->points() );
editTotal->setText( formatDistance( mTotal ) );
mTotal = mDa.measureLine( mTool->points(), forceCartesian );
editTotal->setText( formatDistance( mTotal, !forceCartesian ) );
}
}
}
Expand All @@ -219,14 +265,14 @@ void QgsMeasureDialog::removeLastPoint()
//remove final row
delete mTable->takeTopLevelItem( mTable->topLevelItemCount() - 1 );

mTotal = mDa.measureLine( mTool->points() );
mTotal = mDa.measureLine( mTool->points(), forceCartesian );

if ( !mTool->done() )
{
// need to add the distance for the temporary mouse cursor point
QVector< QgsPointXY > tmpPoints = mTool->points();
QgsPointXY p1( tmpPoints.at( tmpPoints.size() - 1 ) );
double d = mDa.measureLine( p1, mLastMousePoint );
double d = mDa.measureLine( p1, mLastMousePoint, forceCartesian );

d = convertLength( d, mDistanceUnits );

Expand All @@ -236,7 +282,7 @@ void QgsMeasureDialog::removeLastPoint()
}
else
{
editTotal->setText( formatDistance( mTotal ) );
editTotal->setText( formatDistance( mTotal, !forceCartesian ) );
}
}
}
Expand Down Expand Up @@ -297,7 +343,7 @@ QString QgsMeasureDialog::formatArea( double area, bool convertUnits ) const

void QgsMeasureDialog::updateUi()
{
// Set tooltip to indicate how we calculate measurments
// Set tooltip to indicate how we calculate measurements
QString toolTip = tr( "The calculations are based on:" );

bool forceCartesian = false;
Expand Down Expand Up @@ -450,18 +496,25 @@ void QgsMeasureDialog::updateUi()

if ( mMeasureArea )
{
mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( mAreaUnits ) );
if ( useMapUnits )
mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( QgsUnitTypes::AreaUnknownUnit ) );
else
mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( mAreaUnits ) );
}
else
{
mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( mDistanceUnits ) );
if ( mDistanceUnits != QgsUnitTypes::DistanceUnknownUnit )
if ( useMapUnits )
{
mTable->setHeaderLabels( QStringList( tr( "Segments [%1]" ).arg( QgsUnitTypes::toString( mDistanceUnits ) ) ) );
mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( QgsUnitTypes::DistanceUnknownUnit ) );
mTable->setHeaderLabels( QStringList( tr( "Segments [%1]" ).arg( QgsUnitTypes::toString( mapDistanceUnits ) ) ) );
}
else
{
mTable->setHeaderLabels( QStringList( tr( "Segments" ) ) );
mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( mDistanceUnits ) );
if ( mDistanceUnits != QgsUnitTypes::DistanceUnknownUnit )
mTable->setHeaderLabels( QStringList( tr( "Segments [%1]" ).arg( QgsUnitTypes::toString( mDistanceUnits ) ) ) );
else
mTable->setHeaderLabels( QStringList( tr( "Segments" ) ) );
}
}

Expand Down Expand Up @@ -489,16 +542,13 @@ void QgsMeasureDialog::updateUi()
if ( !b )
{
double d = -1;
if ( forceCartesian )
d = mDa.measureLine( p1, p2, forceCartesian );
if ( !forceCartesian )
{
//Cartesian calculation forced
d = std::sqrt( p2.sqrDist( p1 ) );
mTotal += d;
}
else
{
d = mDa.measureLine( p1, p2 );
d = convertLength( d, mDistanceUnits );
if ( mDistanceUnits == QgsUnitTypes::DistanceUnknownUnit && mapDistanceUnits != QgsUnitTypes::DistanceUnknownUnit )
d = convertLength( d, mapDistanceUnits );
else
d = convertLength( d, mDistanceUnits );
}

QTreeWidgetItem *item = new QTreeWidgetItem( QStringList( QLocale().toString( d, 'f', mDecimalPlaces ) ) );
Expand All @@ -510,8 +560,7 @@ void QgsMeasureDialog::updateUi()
b = false;
}

if ( !forceCartesian )
mTotal = mDa.measureLine( mTool->points() );
mTotal = mDa.measureLine( mTool->points(), forceCartesian );
mTable->show(); // Show the table with items
editTotal->setText( formatDistance( mTotal, convertToDisplayUnits ) );
}
Expand Down
12 changes: 12 additions & 0 deletions src/app/qgsmeasuredialog.h
Expand Up @@ -98,6 +98,15 @@ class APP_EXPORT QgsMeasureDialog : public QDialog, private Ui::QgsMeasureBase
//! indicates whether we're measuring distances or areas
bool mMeasureArea = false;

//! Indicates whether the user chose "Map units" instead of directly selecting a unit
bool useMapUnits = false;

/**
* Indicates whether we need to measure distances in cartesian instead of
* spherical coordinates, such as when measuring in degrees in a geographic CRS
*/
bool forceCartesian = true;

//! Number of decimal places we want.
int mDecimalPlaces = 3;

Expand All @@ -107,6 +116,9 @@ class APP_EXPORT QgsMeasureDialog : public QDialog, private Ui::QgsMeasureBase
//! Current unit for distance values
QgsUnitTypes::DistanceUnit mDistanceUnits = QgsUnitTypes::DistanceUnknownUnit;

//! Current map unit for distance values
QgsUnitTypes::DistanceUnit mapDistanceUnits = QgsUnitTypes::DistanceUnknownUnit;

//! Current unit for area values
QgsUnitTypes::AreaUnit mAreaUnits = QgsUnitTypes::AreaUnknownUnit;

Expand Down
4 changes: 4 additions & 0 deletions src/app/qgsoptions.cpp
Expand Up @@ -477,6 +477,8 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl, const QList<QgsOpti
mDistanceUnitsComboBox->addItem( tr( "Yards" ), QgsUnitTypes::DistanceYards );
mDistanceUnitsComboBox->addItem( tr( "Miles" ), QgsUnitTypes::DistanceMiles );
mDistanceUnitsComboBox->addItem( tr( "Nautical miles" ), QgsUnitTypes::DistanceNauticalMiles );
mDistanceUnitsComboBox->addItem( tr( "Centimeters" ), QgsUnitTypes::DistanceCentimeters );
mDistanceUnitsComboBox->addItem( tr( "Millimeters" ), QgsUnitTypes::DistanceMillimeters );
mDistanceUnitsComboBox->addItem( tr( "Degrees" ), QgsUnitTypes::DistanceDegrees );
mDistanceUnitsComboBox->addItem( tr( "Map units" ), QgsUnitTypes::DistanceUnknownUnit );

Expand All @@ -494,6 +496,8 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl, const QList<QgsOpti
mAreaUnitsComboBox->addItem( tr( "Hectares" ), QgsUnitTypes::AreaHectares );
mAreaUnitsComboBox->addItem( tr( "Acres" ), QgsUnitTypes::AreaAcres );
mAreaUnitsComboBox->addItem( tr( "Square nautical miles" ), QgsUnitTypes::AreaSquareNauticalMiles );
mAreaUnitsComboBox->addItem( tr( "Square centimeters" ), QgsUnitTypes::AreaSquareCentimeters );
mAreaUnitsComboBox->addItem( tr( "Square millimeters" ), QgsUnitTypes::AreaSquareMillimeters );
mAreaUnitsComboBox->addItem( tr( "Square degrees" ), QgsUnitTypes::AreaSquareDegrees );
mAreaUnitsComboBox->addItem( tr( "Map units" ), QgsUnitTypes::AreaUnknownUnit );

Expand Down
4 changes: 4 additions & 0 deletions src/app/qgsprojectproperties.cpp
Expand Up @@ -127,6 +127,8 @@ QgsProjectProperties::QgsProjectProperties( QgsMapCanvas *mapCanvas, QWidget *pa
mDistanceUnitsCombo->addItem( tr( "Yards" ), QgsUnitTypes::DistanceYards );
mDistanceUnitsCombo->addItem( tr( "Miles" ), QgsUnitTypes::DistanceMiles );
mDistanceUnitsCombo->addItem( tr( "Nautical miles" ), QgsUnitTypes::DistanceNauticalMiles );
mDistanceUnitsCombo->addItem( tr( "Centimeters" ), QgsUnitTypes::DistanceCentimeters );
mDistanceUnitsCombo->addItem( tr( "Millimeters" ), QgsUnitTypes::DistanceMillimeters );
mDistanceUnitsCombo->addItem( tr( "Degrees" ), QgsUnitTypes::DistanceDegrees );
mDistanceUnitsCombo->addItem( tr( "Map units" ), QgsUnitTypes::DistanceUnknownUnit );

Expand All @@ -138,6 +140,8 @@ QgsProjectProperties::QgsProjectProperties( QgsMapCanvas *mapCanvas, QWidget *pa
mAreaUnitsCombo->addItem( tr( "Hectares" ), QgsUnitTypes::AreaHectares );
mAreaUnitsCombo->addItem( tr( "Acres" ), QgsUnitTypes::AreaAcres );
mAreaUnitsCombo->addItem( tr( "Square nautical miles" ), QgsUnitTypes::AreaSquareNauticalMiles );
mAreaUnitsCombo->addItem( tr( "Square centimeters" ), QgsUnitTypes::AreaSquareCentimeters );
mAreaUnitsCombo->addItem( tr( "Square millimeters" ), QgsUnitTypes::AreaSquareMillimeters );
mAreaUnitsCombo->addItem( tr( "Square degrees" ), QgsUnitTypes::AreaSquareDegrees );
mAreaUnitsCombo->addItem( tr( "Map units" ), QgsUnitTypes::AreaUnknownUnit );

Expand Down
16 changes: 8 additions & 8 deletions src/core/qgsdistancearea.cpp
Expand Up @@ -253,7 +253,7 @@ double QgsDistanceArea::measurePerimeter( const QgsGeometry &geometry ) const
return length;
}

double QgsDistanceArea::measureLine( const QgsCurve *curve ) const
double QgsDistanceArea::measureLine( const QgsCurve *curve, bool forceCartesian ) const
{
if ( !curve )
{
Expand All @@ -264,10 +264,10 @@ double QgsDistanceArea::measureLine( const QgsCurve *curve ) const
QVector<QgsPointXY> linePoints;
curve->points( linePointsV2 );
QgsGeometry::convertPointList( linePointsV2, linePoints );
return measureLine( linePoints );
return measureLine( linePoints, forceCartesian );
}

double QgsDistanceArea::measureLine( const QVector<QgsPointXY> &points ) const
double QgsDistanceArea::measureLine( const QVector<QgsPointXY> &points, bool forceCartesian ) const
{
if ( points.size() < 2 )
return 0;
Expand All @@ -277,22 +277,22 @@ double QgsDistanceArea::measureLine( const QVector<QgsPointXY> &points ) const

try
{
if ( willUseEllipsoid() )
if ( willUseEllipsoid() && !forceCartesian )
p1 = mCoordTransform.transform( points[0] );
else
p1 = points[0];

for ( QVector<QgsPointXY>::const_iterator i = points.constBegin(); i != points.constEnd(); ++i )
{
if ( willUseEllipsoid() )
if ( willUseEllipsoid() && !forceCartesian )
{
p2 = mCoordTransform.transform( *i );
total += computeDistanceBearing( p1, p2 );
}
else
{
p2 = *i;
total += measureLine( p1, p2 );
total += measureLine( p1, p2, forceCartesian );
}

p1 = p2;
Expand All @@ -309,7 +309,7 @@ double QgsDistanceArea::measureLine( const QVector<QgsPointXY> &points ) const

}

double QgsDistanceArea::measureLine( const QgsPointXY &p1, const QgsPointXY &p2 ) const
double QgsDistanceArea::measureLine( const QgsPointXY &p1, const QgsPointXY &p2, bool forceCartesian ) const
{
double result;

Expand All @@ -318,7 +318,7 @@ double QgsDistanceArea::measureLine( const QgsPointXY &p1, const QgsPointXY &p2
QgsPointXY pp1 = p1, pp2 = p2;

QgsDebugMsgLevel( QStringLiteral( "Measuring from %1 to %2" ).arg( p1.toString( 4 ), p2.toString( 4 ) ), 3 );
if ( willUseEllipsoid() )
if ( willUseEllipsoid() && !forceCartesian )
{
QgsDebugMsgLevel( QStringLiteral( "Ellipsoidal calculations is enabled, using ellipsoid %1" ).arg( mEllipsoid ), 4 );
QgsDebugMsgLevel( QStringLiteral( "From proj4 : %1" ).arg( mCoordTransform.sourceCrs().toProj4() ), 4 );
Expand Down

0 comments on commit 3e05c99

Please sign in to comment.