Skip to content

Commit 3e05c99

Browse files
jduggenyalldawson
authored andcommittedJan 7, 2019
Add centimeter and millimeter based units to Project Properties and Map Tools Options
Fix the "map units" option in distance measurements when using geographic coordinate systems
1 parent 0cc809b commit 3e05c99

File tree

7 files changed

+115
-42
lines changed

7 files changed

+115
-42
lines changed
 

‎python/core/auto_generated/qgsdistancearea.sip.in

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,23 +189,25 @@ Measures the perimeter of a polygon geometry.
189189
.. versionadded:: 2.12
190190
%End
191191

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

196196
:param points: list of points in line
197+
:param forceCartesian: calculate distances in cartesian coordinates
197198

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

200201
.. seealso:: :py:func:`lengthUnits`
201202
%End
202203

203-
double measureLine( const QgsPointXY &p1, const QgsPointXY &p2 ) const;
204+
double measureLine( const QgsPointXY &p1, const QgsPointXY &p2, bool forceCartesian = false ) const;
204205
%Docstring
205206
Measures the distance between two points.
206207

207208
:param p1: start of line
208209
:param p2: end of line
210+
:param forceCartesian: calculate distances in cartesian coordinates
209211

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

‎src/app/qgsmeasuredialog.cpp

Lines changed: 78 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,19 @@ QgsMeasureDialog::QgsMeasureDialog( QgsMeasureTool *tool, Qt::WindowFlags f )
5151

5252
repopulateComboBoxUnits( mMeasureArea );
5353
if ( mMeasureArea )
54-
mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( QgsProject::instance()->areaUnits() ) );
54+
{
55+
if ( useMapUnits )
56+
mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( QgsUnitTypes::AreaUnknownUnit ) );
57+
else
58+
mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( QgsProject::instance()->areaUnits() ) );
59+
}
5560
else
56-
mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( QgsProject::instance()->distanceUnits() ) );
61+
{
62+
if ( useMapUnits )
63+
mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( QgsUnitTypes::DistanceUnknownUnit ) );
64+
else
65+
mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( QgsProject::instance()->distanceUnits() ) );
66+
}
5767

5868
if ( !mCanvas->mapSettings().destinationCrs().isValid() )
5969
{
@@ -103,21 +113,51 @@ void QgsMeasureDialog::updateSettings()
103113
mCanvasUnits = mCanvas->mapUnits();
104114
// Configure QgsDistanceArea
105115
mDistanceUnits = QgsProject::instance()->distanceUnits();
116+
mapDistanceUnits = QgsProject::instance()->crs().mapUnits();
106117
mAreaUnits = QgsProject::instance()->areaUnits();
107118
mDa.setSourceCrs( mCanvas->mapSettings().destinationCrs(), QgsProject::instance()->transformContext() );
108119
mDa.setEllipsoid( QgsProject::instance()->ellipsoid() );
109120

110121
mTable->clear();
111122
mTotal = 0;
112123
updateUi();
124+
125+
if ( !mCanvas->mapSettings().destinationCrs().isValid() ||
126+
( mCanvas->mapSettings().destinationCrs().mapUnits() == QgsUnitTypes::DistanceDegrees
127+
&& mDistanceUnits == QgsUnitTypes::DistanceDegrees ) )
128+
forceCartesian = true;
129+
else
130+
forceCartesian = false;
113131
}
114132

115133
void QgsMeasureDialog::unitsChanged( int index )
116134
{
117135
if ( mMeasureArea )
136+
{
118137
mAreaUnits = static_cast< QgsUnitTypes::AreaUnit >( mUnitsCombo->itemData( index ).toInt() );
138+
if ( mAreaUnits == QgsUnitTypes::AreaUnknownUnit )
139+
{
140+
useMapUnits = true;
141+
mAreaUnits = QgsUnitTypes::distanceToAreaUnit( mapDistanceUnits );
142+
}
143+
else
144+
{
145+
useMapUnits = false;
146+
}
147+
}
119148
else
149+
{
120150
mDistanceUnits = static_cast< QgsUnitTypes::DistanceUnit >( mUnitsCombo->itemData( index ).toInt() );
151+
if ( mDistanceUnits == QgsUnitTypes::DistanceUnknownUnit )
152+
{
153+
useMapUnits = true;
154+
mDistanceUnits = mapDistanceUnits;
155+
}
156+
else
157+
{
158+
useMapUnits = false;
159+
}
160+
}
121161
mTable->clear();
122162
mTotal = 0.;
123163
updateUi();
@@ -142,6 +182,13 @@ void QgsMeasureDialog::restart()
142182

143183
void QgsMeasureDialog::mouseMove( const QgsPointXY &point )
144184
{
185+
if ( !mCanvas->mapSettings().destinationCrs().isValid() ||
186+
( mCanvas->mapSettings().destinationCrs().mapUnits() == QgsUnitTypes::DistanceDegrees
187+
&& mDistanceUnits == QgsUnitTypes::DistanceDegrees ) )
188+
forceCartesian = true;
189+
else
190+
forceCartesian = false;
191+
145192
mLastMousePoint = point;
146193
// show current distance/area while moving the point
147194
// by creating a temporary copy of point array
@@ -157,11 +204,10 @@ void QgsMeasureDialog::mouseMove( const QgsPointXY &point )
157204
{
158205
QVector< QgsPointXY > tmpPoints = mTool->points();
159206
QgsPointXY p1( tmpPoints.at( tmpPoints.size() - 1 ) ), p2( point );
160-
double d = mDa.measureLine( p1, p2 );
161-
162-
editTotal->setText( formatDistance( mTotal + d ) );
163-
164-
d = convertLength( d, mDistanceUnits );
207+
double d = mDa.measureLine( p1, p2, forceCartesian );
208+
editTotal->setText( formatDistance( mTotal + d, !forceCartesian ) );
209+
if ( !forceCartesian )
210+
d = convertLength( d, mDistanceUnits );
165211

166212
// Set moving
167213
QTreeWidgetItem *item = mTable->topLevelItem( mTable->topLevelItemCount() - 1 );
@@ -191,8 +237,8 @@ void QgsMeasureDialog::addPoint()
191237
}
192238
if ( numPoints > 1 )
193239
{
194-
mTotal = mDa.measureLine( mTool->points() );
195-
editTotal->setText( formatDistance( mTotal ) );
240+
mTotal = mDa.measureLine( mTool->points(), forceCartesian );
241+
editTotal->setText( formatDistance( mTotal, !forceCartesian ) );
196242
}
197243
}
198244
}
@@ -219,14 +265,14 @@ void QgsMeasureDialog::removeLastPoint()
219265
//remove final row
220266
delete mTable->takeTopLevelItem( mTable->topLevelItemCount() - 1 );
221267

222-
mTotal = mDa.measureLine( mTool->points() );
268+
mTotal = mDa.measureLine( mTool->points(), forceCartesian );
223269

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

231277
d = convertLength( d, mDistanceUnits );
232278

@@ -236,7 +282,7 @@ void QgsMeasureDialog::removeLastPoint()
236282
}
237283
else
238284
{
239-
editTotal->setText( formatDistance( mTotal ) );
285+
editTotal->setText( formatDistance( mTotal, !forceCartesian ) );
240286
}
241287
}
242288
}
@@ -297,7 +343,7 @@ QString QgsMeasureDialog::formatArea( double area, bool convertUnits ) const
297343

298344
void QgsMeasureDialog::updateUi()
299345
{
300-
// Set tooltip to indicate how we calculate measurments
346+
// Set tooltip to indicate how we calculate measurements
301347
QString toolTip = tr( "The calculations are based on:" );
302348

303349
bool forceCartesian = false;
@@ -450,18 +496,25 @@ void QgsMeasureDialog::updateUi()
450496

451497
if ( mMeasureArea )
452498
{
453-
mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( mAreaUnits ) );
499+
if ( useMapUnits )
500+
mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( QgsUnitTypes::AreaUnknownUnit ) );
501+
else
502+
mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( mAreaUnits ) );
454503
}
455504
else
456505
{
457-
mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( mDistanceUnits ) );
458-
if ( mDistanceUnits != QgsUnitTypes::DistanceUnknownUnit )
506+
if ( useMapUnits )
459507
{
460-
mTable->setHeaderLabels( QStringList( tr( "Segments [%1]" ).arg( QgsUnitTypes::toString( mDistanceUnits ) ) ) );
508+
mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( QgsUnitTypes::DistanceUnknownUnit ) );
509+
mTable->setHeaderLabels( QStringList( tr( "Segments [%1]" ).arg( QgsUnitTypes::toString( mapDistanceUnits ) ) ) );
461510
}
462511
else
463512
{
464-
mTable->setHeaderLabels( QStringList( tr( "Segments" ) ) );
513+
mUnitsCombo->setCurrentIndex( mUnitsCombo->findData( mDistanceUnits ) );
514+
if ( mDistanceUnits != QgsUnitTypes::DistanceUnknownUnit )
515+
mTable->setHeaderLabels( QStringList( tr( "Segments [%1]" ).arg( QgsUnitTypes::toString( mDistanceUnits ) ) ) );
516+
else
517+
mTable->setHeaderLabels( QStringList( tr( "Segments" ) ) );
465518
}
466519
}
467520

@@ -489,16 +542,13 @@ void QgsMeasureDialog::updateUi()
489542
if ( !b )
490543
{
491544
double d = -1;
492-
if ( forceCartesian )
545+
d = mDa.measureLine( p1, p2, forceCartesian );
546+
if ( !forceCartesian )
493547
{
494-
//Cartesian calculation forced
495-
d = std::sqrt( p2.sqrDist( p1 ) );
496-
mTotal += d;
497-
}
498-
else
499-
{
500-
d = mDa.measureLine( p1, p2 );
501-
d = convertLength( d, mDistanceUnits );
548+
if ( mDistanceUnits == QgsUnitTypes::DistanceUnknownUnit && mapDistanceUnits != QgsUnitTypes::DistanceUnknownUnit )
549+
d = convertLength( d, mapDistanceUnits );
550+
else
551+
d = convertLength( d, mDistanceUnits );
502552
}
503553

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

513-
if ( !forceCartesian )
514-
mTotal = mDa.measureLine( mTool->points() );
563+
mTotal = mDa.measureLine( mTool->points(), forceCartesian );
515564
mTable->show(); // Show the table with items
516565
editTotal->setText( formatDistance( mTotal, convertToDisplayUnits ) );
517566
}

‎src/app/qgsmeasuredialog.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,15 @@ class APP_EXPORT QgsMeasureDialog : public QDialog, private Ui::QgsMeasureBase
9898
//! indicates whether we're measuring distances or areas
9999
bool mMeasureArea = false;
100100

101+
//! Indicates whether the user chose "Map units" instead of directly selecting a unit
102+
bool useMapUnits = false;
103+
104+
/**
105+
* Indicates whether we need to measure distances in cartesian instead of
106+
* spherical coordinates, such as when measuring in degrees in a geographic CRS
107+
*/
108+
bool forceCartesian = true;
109+
101110
//! Number of decimal places we want.
102111
int mDecimalPlaces = 3;
103112

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

119+
//! Current map unit for distance values
120+
QgsUnitTypes::DistanceUnit mapDistanceUnits = QgsUnitTypes::DistanceUnknownUnit;
121+
110122
//! Current unit for area values
111123
QgsUnitTypes::AreaUnit mAreaUnits = QgsUnitTypes::AreaUnknownUnit;
112124

‎src/app/qgsoptions.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,8 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl, const QList<QgsOpti
477477
mDistanceUnitsComboBox->addItem( tr( "Yards" ), QgsUnitTypes::DistanceYards );
478478
mDistanceUnitsComboBox->addItem( tr( "Miles" ), QgsUnitTypes::DistanceMiles );
479479
mDistanceUnitsComboBox->addItem( tr( "Nautical miles" ), QgsUnitTypes::DistanceNauticalMiles );
480+
mDistanceUnitsComboBox->addItem( tr( "Centimeters" ), QgsUnitTypes::DistanceCentimeters );
481+
mDistanceUnitsComboBox->addItem( tr( "Millimeters" ), QgsUnitTypes::DistanceMillimeters );
480482
mDistanceUnitsComboBox->addItem( tr( "Degrees" ), QgsUnitTypes::DistanceDegrees );
481483
mDistanceUnitsComboBox->addItem( tr( "Map units" ), QgsUnitTypes::DistanceUnknownUnit );
482484

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

‎src/app/qgsprojectproperties.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ QgsProjectProperties::QgsProjectProperties( QgsMapCanvas *mapCanvas, QWidget *pa
127127
mDistanceUnitsCombo->addItem( tr( "Yards" ), QgsUnitTypes::DistanceYards );
128128
mDistanceUnitsCombo->addItem( tr( "Miles" ), QgsUnitTypes::DistanceMiles );
129129
mDistanceUnitsCombo->addItem( tr( "Nautical miles" ), QgsUnitTypes::DistanceNauticalMiles );
130+
mDistanceUnitsCombo->addItem( tr( "Centimeters" ), QgsUnitTypes::DistanceCentimeters );
131+
mDistanceUnitsCombo->addItem( tr( "Millimeters" ), QgsUnitTypes::DistanceMillimeters );
130132
mDistanceUnitsCombo->addItem( tr( "Degrees" ), QgsUnitTypes::DistanceDegrees );
131133
mDistanceUnitsCombo->addItem( tr( "Map units" ), QgsUnitTypes::DistanceUnknownUnit );
132134

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

‎src/core/qgsdistancearea.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ double QgsDistanceArea::measurePerimeter( const QgsGeometry &geometry ) const
253253
return length;
254254
}
255255

256-
double QgsDistanceArea::measureLine( const QgsCurve *curve ) const
256+
double QgsDistanceArea::measureLine( const QgsCurve *curve, bool forceCartesian ) const
257257
{
258258
if ( !curve )
259259
{
@@ -264,10 +264,10 @@ double QgsDistanceArea::measureLine( const QgsCurve *curve ) const
264264
QVector<QgsPointXY> linePoints;
265265
curve->points( linePointsV2 );
266266
QgsGeometry::convertPointList( linePointsV2, linePoints );
267-
return measureLine( linePoints );
267+
return measureLine( linePoints, forceCartesian );
268268
}
269269

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

278278
try
279279
{
280-
if ( willUseEllipsoid() )
280+
if ( willUseEllipsoid() && !forceCartesian )
281281
p1 = mCoordTransform.transform( points[0] );
282282
else
283283
p1 = points[0];
284284

285285
for ( QVector<QgsPointXY>::const_iterator i = points.constBegin(); i != points.constEnd(); ++i )
286286
{
287-
if ( willUseEllipsoid() )
287+
if ( willUseEllipsoid() && !forceCartesian )
288288
{
289289
p2 = mCoordTransform.transform( *i );
290290
total += computeDistanceBearing( p1, p2 );
291291
}
292292
else
293293
{
294294
p2 = *i;
295-
total += measureLine( p1, p2 );
295+
total += measureLine( p1, p2, forceCartesian );
296296
}
297297

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

310310
}
311311

312-
double QgsDistanceArea::measureLine( const QgsPointXY &p1, const QgsPointXY &p2 ) const
312+
double QgsDistanceArea::measureLine( const QgsPointXY &p1, const QgsPointXY &p2, bool forceCartesian ) const
313313
{
314314
double result;
315315

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

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

‎src/core/qgsdistancearea.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,19 +165,21 @@ class CORE_EXPORT QgsDistanceArea
165165
/**
166166
* Measures the length of a line with multiple segments.
167167
* \param points list of points in line
168+
* \param forceCartesian calculate distances in cartesian coordinates
168169
* \returns length of line. The units for the returned length can be retrieved by calling lengthUnits().
169170
* \see lengthUnits()
170171
*/
171-
double measureLine( const QVector<QgsPointXY> &points ) const;
172+
double measureLine( const QVector<QgsPointXY> &points, bool forceCartesian = false ) const;
172173

173174
/**
174175
* Measures the distance between two points.
175176
* \param p1 start of line
176177
* \param p2 end of line
178+
* \param forceCartesian calculate distances in cartesian coordinates
177179
* \returns distance between points. The units for the returned distance can be retrieved by calling lengthUnits().
178180
* \see lengthUnits()
179181
*/
180-
double measureLine( const QgsPointXY &p1, const QgsPointXY &p2 ) const;
182+
double measureLine( const QgsPointXY &p1, const QgsPointXY &p2, bool forceCartesian = false ) const;
181183

182184
/**
183185
* Calculates the distance from one point with distance in meters and azimuth (direction)
@@ -341,7 +343,7 @@ class CORE_EXPORT QgsDistanceArea
341343
double getQbar( double x ) const;
342344

343345
double measure( const QgsAbstractGeometry *geomV2, MeasureType type = Default ) const;
344-
double measureLine( const QgsCurve *curve ) const;
346+
double measureLine( const QgsCurve *curve, bool forceCartesian = false ) const;
345347
double measurePolygon( const QgsCurve *curve ) const;
346348

347349
// temporary area measurement stuff

0 commit comments

Comments
 (0)
Please sign in to comment.