1 /***************************************************************************
2 qgsmeasuretool.cpp - map tool for measuring distances and areas
3 ---------------------
4 begin : April 2007
5 copyright : (C) 2007 by Martin Dobias
6 email : wonder.sk at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16 #include "qgsdistancearea.h"
17 #include "qgslogger.h"
18 #include "qgsmapcanvas.h"
19 #include "qgsmaptopixel.h"
20 #include "qgsrubberband.h"
21 #include "qgsvectorlayer.h"
22 #include "qgssnappingutils.h"
23 #include "qgstolerance.h"
24 #include "qgsexception.h"
25 #include "qgsmeasuredialog.h"
26 #include "qgsmeasuretool.h"
27 #include "qgsmessagelog.h"
28 #include "qgssettings.h"
29 #include "qgsproject.h"
30 #include "qgssnapindicator.h"
31 #include "qgsmapmouseevent.h"
32
33 #include <QMessageBox>
34
35
QgsMeasureTool(QgsMapCanvas * canvas,bool measureArea)36 QgsMeasureTool::QgsMeasureTool( QgsMapCanvas *canvas, bool measureArea )
37 : QgsMapTool( canvas )
38 , mMeasureArea( measureArea )
39 , mSnapIndicator( new QgsSnapIndicator( canvas ) )
40 {
41 mRubberBand = new QgsRubberBand( canvas, mMeasureArea ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry );
42 mRubberBandPoints = new QgsRubberBand( canvas, QgsWkbTypes::PointGeometry );
43
44 // Append point we will move
45 mPoints.append( QgsPointXY( 0, 0 ) );
46 mDestinationCrs = canvas->mapSettings().destinationCrs();
47
48 mDialog = new QgsMeasureDialog( this );
49 mDialog->setWindowFlags( mDialog->windowFlags() | Qt::Tool );
50 mDialog->restorePosition();
51
52 connect( canvas, &QgsMapCanvas::destinationCrsChanged, this, &QgsMeasureTool::updateSettings );
53 }
54
~QgsMeasureTool()55 QgsMeasureTool::~QgsMeasureTool()
56 {
57 // important - dialog is not parented to this tool (it's parented to the main window)
58 // but we want to clean it up now
59 delete mDialog;
60 }
61
points() const62 QVector<QgsPointXY> QgsMeasureTool::points() const
63 {
64 return mPoints;
65 }
66
67
activate()68 void QgsMeasureTool::activate()
69 {
70 mDialog->show();
71 mRubberBand->show();
72 mRubberBandPoints->show();
73 QgsMapTool::activate();
74
75 // ensure that we have correct settings
76 updateSettings();
77
78 // If we suspect that they have data that is projected, yet the
79 // map CRS is set to a geographic one, warn them.
80 if ( mCanvas->mapSettings().destinationCrs().isValid() &&
81 mCanvas->mapSettings().destinationCrs().isGeographic() &&
82 ( mCanvas->extent().height() > 360 ||
83 mCanvas->extent().width() > 720 ) )
84 {
85 QMessageBox::warning( nullptr, tr( "Incorrect Measure Results" ),
86 tr( "<p>This map is defined with a geographic coordinate system "
87 "(latitude/longitude) "
88 "but the map extents suggests that it is actually a projected "
89 "coordinate system (e.g., Mercator). "
90 "If so, the results from line or area measurements will be "
91 "incorrect.</p>"
92 "<p>To fix this, explicitly set an appropriate map coordinate "
93 "system using the <tt>Settings:Project Properties</tt> menu." ) );
94 mWrongProjectProjection = true;
95 }
96 }
97
deactivate()98 void QgsMeasureTool::deactivate()
99 {
100 mSnapIndicator->setMatch( QgsPointLocator::Match() );
101
102 mDialog->hide();
103 mRubberBand->hide();
104 mRubberBandPoints->hide();
105 QgsMapTool::deactivate();
106 }
107
restart()108 void QgsMeasureTool::restart()
109 {
110 mPoints.clear();
111
112 mRubberBand->reset( mMeasureArea ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry );
113 mRubberBandPoints->reset( QgsWkbTypes::PointGeometry );
114
115 mDone = true;
116 mWrongProjectProjection = false;
117 }
118
updateSettings()119 void QgsMeasureTool::updateSettings()
120 {
121 QgsSettings settings;
122
123 int myRed = settings.value( QStringLiteral( "qgis/default_measure_color_red" ), 222 ).toInt();
124 int myGreen = settings.value( QStringLiteral( "qgis/default_measure_color_green" ), 155 ).toInt();
125 int myBlue = settings.value( QStringLiteral( "qgis/default_measure_color_blue" ), 67 ).toInt();
126 mRubberBand->setColor( QColor( myRed, myGreen, myBlue, 100 ) );
127 mRubberBand->setWidth( 3 );
128 mRubberBandPoints->setIcon( QgsRubberBand::ICON_CIRCLE );
129 mRubberBandPoints->setIconSize( 10 );
130 mRubberBandPoints->setColor( QColor( myRed, myGreen, myBlue, 150 ) );
131
132 // Reproject the points to the new destination CoordinateReferenceSystem
133 if ( mRubberBand->size() > 0 && mDestinationCrs != mCanvas->mapSettings().destinationCrs() && mCanvas->mapSettings().destinationCrs().isValid() )
134 {
135 QVector<QgsPointXY> points = mPoints;
136 bool lastDone = mDone;
137
138 mDialog->restart();
139 mDone = lastDone;
140 QgsCoordinateTransform ct( mDestinationCrs, mCanvas->mapSettings().destinationCrs(), QgsProject::instance() );
141
142 const auto constPoints = points;
143 for ( const QgsPointXY &previousPoint : constPoints )
144 {
145 try
146 {
147 QgsPointXY point = ct.transform( previousPoint );
148
149 mPoints.append( point );
150 mRubberBand->addPoint( point, false );
151 mRubberBandPoints->addPoint( point, false );
152 }
153 catch ( QgsCsException &cse )
154 {
155 QgsMessageLog::logMessage( tr( "Transform error caught at the MeasureTool: %1" ).arg( cse.what() ) );
156 }
157 }
158
159 mRubberBand->updatePosition();
160 mRubberBand->update();
161 mRubberBandPoints->updatePosition();
162 mRubberBandPoints->update();
163 }
164 mDestinationCrs = mCanvas->mapSettings().destinationCrs();
165
166 mDialog->updateSettings();
167
168 if ( !mDone && mRubberBand->size() > 0 )
169 {
170 mRubberBand->addPoint( mPoints.last() );
171 mDialog->addPoint();
172 }
173 if ( mRubberBand->size() > 0 )
174 {
175 mRubberBand->setVisible( true );
176 mRubberBandPoints->setVisible( true );
177 }
178 }
179
180 //////////////////////////
181
canvasPressEvent(QgsMapMouseEvent * e)182 void QgsMeasureTool::canvasPressEvent( QgsMapMouseEvent *e )
183 {
184 Q_UNUSED( e )
185 }
186
canvasMoveEvent(QgsMapMouseEvent * e)187 void QgsMeasureTool::canvasMoveEvent( QgsMapMouseEvent *e )
188 {
189 QgsPointXY point = e->snapPoint();
190 mSnapIndicator->setMatch( e->mapPointMatch() );
191
192 if ( ! mDone )
193 {
194 mRubberBand->movePoint( point );
195 mDialog->mouseMove( point );
196 }
197 }
198
199
canvasReleaseEvent(QgsMapMouseEvent * e)200 void QgsMeasureTool::canvasReleaseEvent( QgsMapMouseEvent *e )
201 {
202 QgsPointXY point = e->snapPoint();
203
204 if ( mDone ) // if we have stopped measuring any mouse click restart measuring
205 {
206 mDialog->restart();
207 }
208
209 if ( e->button() == Qt::RightButton ) // if we clicked the right button we stop measuring
210 {
211 mDone = true;
212 mRubberBand->removeLastPoint();
213 mDialog->removeLastPoint();
214 }
215 else if ( e->button() == Qt::LeftButton )
216 {
217 mDone = false;
218 addPoint( point );
219 }
220
221 mDialog->show();
222
223 }
224
undo()225 void QgsMeasureTool::undo()
226 {
227 if ( mRubberBand )
228 {
229 if ( mPoints.empty() )
230 {
231 return;
232 }
233
234 if ( mPoints.size() == 1 )
235 {
236 //removing first point, so restart everything
237 restart();
238 mDialog->restart();
239 }
240 else
241 {
242 //remove second last point from line band, and last point from points band
243 mRubberBand->removePoint( -2, true );
244 mRubberBandPoints->removePoint( -1, true );
245 mPoints.removeLast();
246
247 mDialog->removeLastPoint();
248 }
249
250 }
251 }
252
keyPressEvent(QKeyEvent * e)253 void QgsMeasureTool::keyPressEvent( QKeyEvent *e )
254 {
255 if ( ( e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete ) )
256 {
257 if ( !mDone )
258 {
259 undo();
260 }
261
262 // Override default shortcut management in MapCanvas
263 e->ignore();
264 }
265 }
266
267
addPoint(const QgsPointXY & point)268 void QgsMeasureTool::addPoint( const QgsPointXY &point )
269 {
270 QgsDebugMsg( "point=" + point.toString() );
271
272 // don't add points with the same coordinates
273 if ( !mPoints.isEmpty() && mPoints.last() == point )
274 {
275 return;
276 }
277
278 QgsPointXY pnt( point );
279 // Append point that we will be moving.
280 mPoints.append( pnt );
281
282 mRubberBand->addPoint( point );
283 mRubberBandPoints->addPoint( point );
284 if ( ! mDone ) // Prevent the insertion of a new item in segments measure table
285 {
286 mDialog->addPoint();
287 }
288 }
289