Skip to content
Permalink
Browse files

Add centimeter and millimeter based units to Project Properties and M…

…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 3e05c99c11f73f131a7382a65c3d430da39bc535
@@ -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().

@@ -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() )
{
@@ -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();
@@ -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
@@ -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 );
@@ -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 ) );
}
}
}
@@ -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 );

@@ -236,7 +282,7 @@ void QgsMeasureDialog::removeLastPoint()
}
else
{
editTotal->setText( formatDistance( mTotal ) );
editTotal->setText( formatDistance( mTotal, !forceCartesian ) );
}
}
}
@@ -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;
@@ -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" ) ) );
}
}

@@ -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 ) ) );
@@ -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 ) );
}
@@ -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;

@@ -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;

@@ -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 );

@@ -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 );

@@ -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 );

@@ -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 );

@@ -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 )
{
@@ -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;
@@ -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;
@@ -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;

@@ -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 );

0 comments on commit 3e05c99

Please sign in to comment.
You can’t perform that action at this time.