1 /***************************************************************************
2 qgsgrassregion.h - Edit region
3 -------------------
4 begin : August, 2004
5 copyright : (C) 2004 by Radim Blazek
6 email : blazek@itc.it
7 ***************************************************************************/
8 /***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17 #include "qgis.h"
18 #include "qgsgrassregion.h"
19 #include "qgsgrassplugin.h"
20 #include "qgsgrass.h"
21
22 #include "qgisinterface.h"
23 #include "qgslogger.h"
24 #include "qgsmapcanvas.h"
25 #include "qgsmaptool.h"
26 #include "qgsexception.h"
27 #include "qgsmapmouseevent.h"
28
29 #include <QButtonGroup>
30 #include <QColorDialog>
31 #include <QMessageBox>
32 #include <QRubberBand>
33 #include <QSettings>
34 #include <QDoubleValidator>
35
36
37 //! Map tool which uses rubber band for changing grass region
QgsGrassRegionEdit(QgsMapCanvas * canvas)38 QgsGrassRegionEdit::QgsGrassRegionEdit( QgsMapCanvas *canvas )
39 : QgsMapTool( canvas )
40 {
41 mDraw = false;
42 mRubberBand = new QgsRubberBand( mCanvas, QgsWkbTypes::PolygonGeometry );
43 mSrcRubberBand = new QgsRubberBand( mCanvas, QgsWkbTypes::PolygonGeometry );
44 QString error;
45 mCrs = QgsGrass::crs( QgsGrass::getDefaultGisdbase(), QgsGrass::getDefaultLocation(), error );
46 QgsDebugMsg( "mCrs: " + mCrs.toWkt() );
47 setTransform();
48 connect( canvas, &QgsMapCanvas::destinationCrsChanged, this, &QgsGrassRegionEdit::setTransform );
49 }
50
~QgsGrassRegionEdit()51 QgsGrassRegionEdit::~QgsGrassRegionEdit()
52 {
53 delete mRubberBand;
54 delete mSrcRubberBand;
55 }
56
57 //! mouse pressed in map canvas
canvasPressEvent(QgsMapMouseEvent * event)58 void QgsGrassRegionEdit::canvasPressEvent( QgsMapMouseEvent *event )
59 {
60 mDraw = true;
61 mRubberBand->reset( QgsWkbTypes::PolygonGeometry );
62 mSrcRubberBand->reset( QgsWkbTypes::PolygonGeometry );
63 emit captureStarted();
64
65 mStartPoint = toMapCoordinates( event->pos() );
66 mEndPoint = mStartPoint;
67 setRegion( mStartPoint, mEndPoint );
68 }
69
70 //! mouse movement in map canvas
canvasMoveEvent(QgsMapMouseEvent * event)71 void QgsGrassRegionEdit::canvasMoveEvent( QgsMapMouseEvent *event )
72 {
73 if ( !mDraw )
74 return;
75
76 mEndPoint = toMapCoordinates( event->pos() );
77 setRegion( mStartPoint, mEndPoint );
78 }
79
80 //! mouse button released
canvasReleaseEvent(QgsMapMouseEvent * event)81 void QgsGrassRegionEdit::canvasReleaseEvent( QgsMapMouseEvent *event )
82 {
83 if ( !mDraw )
84 return;
85
86 mEndPoint = toMapCoordinates( event->pos() );
87 setRegion( mStartPoint, mEndPoint );
88 mDraw = false;
89 emit captureEnded();
90 }
91
92 //! called when map tool is about to get inactive
deactivate()93 void QgsGrassRegionEdit::deactivate()
94 {
95 mRubberBand->reset( QgsWkbTypes::PolygonGeometry );
96 mSrcRubberBand->reset( QgsWkbTypes::PolygonGeometry );
97 QgsMapTool::deactivate();
98 }
99
setRegion(const QgsPointXY & ul,const QgsPointXY & lr)100 void QgsGrassRegionEdit::setRegion( const QgsPointXY &ul, const QgsPointXY &lr )
101 {
102 mStartPoint = ul;
103 mEndPoint = lr;
104 calcSrcRegion();
105 drawRegion( canvas(), mRubberBand, mSrcRectangle, mCoordinateTransform, true );
106 drawRegion( canvas(), mSrcRubberBand, QgsRectangle( mStartPoint, mEndPoint ), QgsCoordinateTransform(), true );
107 }
108
calcSrcRegion()109 void QgsGrassRegionEdit::calcSrcRegion()
110 {
111 mSrcRectangle.set( mStartPoint, mEndPoint );
112
113 if ( mCrs.isValid() && mCanvas->mapSettings().destinationCrs().isValid() )
114 {
115 QgsCoordinateTransform coordinateTransform;
116 coordinateTransform.setSourceCrs( mCanvas->mapSettings().destinationCrs() );
117 coordinateTransform.setDestinationCrs( mCrs );
118 mSrcRectangle = coordinateTransform.transformBoundingBox( mSrcRectangle );
119 }
120 }
121
setTransform()122 void QgsGrassRegionEdit::setTransform()
123 {
124 if ( mCrs.isValid() && canvas()->mapSettings().destinationCrs().isValid() )
125 {
126 mCoordinateTransform.setSourceCrs( mCrs );
127 mCoordinateTransform.setDestinationCrs( canvas()->mapSettings().destinationCrs() );
128 }
129 }
130
transform(QgsMapCanvas *,QVector<QgsPointXY> & points,const QgsCoordinateTransform & coordinateTransform,Qgis::TransformDirection direction)131 void QgsGrassRegionEdit::transform( QgsMapCanvas *, QVector<QgsPointXY> &points, const QgsCoordinateTransform &coordinateTransform, Qgis::TransformDirection direction )
132 {
133 //! Coordinate transform
134 //QgsDebugMsg ( "srcCrs = " + coordinateTransform->sourceCrs().toWkt() );
135 //QgsDebugMsg ( "destCrs = " + coordinateTransform->destCRS().toWkt() );
136 try
137 {
138 for ( int i = 0; i < points.size(); i++ )
139 {
140 points[i] = coordinateTransform.transform( points[i], direction );
141 }
142 }
143 catch ( QgsCsException &cse )
144 {
145 Q_UNUSED( cse )
146 QgsDebugMsg( QString( "transformation failed: %1" ).arg( cse.what() ) );
147 }
148 }
149
drawRegion(QgsMapCanvas * canvas,QgsRubberBand * rubberBand,const QgsRectangle & rect,const QgsCoordinateTransform & coordinateTransform,bool isPolygon)150 void QgsGrassRegionEdit::drawRegion( QgsMapCanvas *canvas, QgsRubberBand *rubberBand, const QgsRectangle &rect, const QgsCoordinateTransform &coordinateTransform, bool isPolygon )
151 {
152 QVector<QgsPointXY> points;
153 points.append( QgsPointXY( rect.xMinimum(), rect.yMinimum() ) );
154 points.append( QgsPointXY( rect.xMaximum(), rect.yMinimum() ) );
155 points.append( QgsPointXY( rect.xMaximum(), rect.yMaximum() ) );
156 points.append( QgsPointXY( rect.xMinimum(), rect.yMaximum() ) );
157 if ( !isPolygon )
158 {
159 points.append( QgsPointXY( rect.xMinimum(), rect.yMinimum() ) );
160 }
161
162 if ( coordinateTransform.isValid() )
163 {
164 transform( canvas, points, coordinateTransform );
165 }
166 rubberBand->reset( isPolygon ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::LineGeometry );
167 for ( int i = 0; i < points.size(); i++ )
168 {
169 bool update = false; // true to update canvas
170 if ( i == points.size() - 1 )
171 update = true;
172 rubberBand->addPoint( points[i], update );
173 }
174 rubberBand->show();
175 }
176
getRegion()177 QgsRectangle QgsGrassRegionEdit::getRegion()
178 {
179 //return QgsRectangle( mStartPoint, mEndPoint );
180 return mSrcRectangle;
181 }
182
setSrcRegion(const QgsRectangle & rect)183 void QgsGrassRegionEdit::setSrcRegion( const QgsRectangle &rect )
184 {
185 mSrcRectangle = rect;
186 }
187
QgsGrassRegion(QgisInterface * iface,QWidget * parent,Qt::WindowFlags f)188 QgsGrassRegion::QgsGrassRegion( QgisInterface *iface,
189 QWidget *parent, Qt::WindowFlags f )
190 : QWidget( parent, f )
191 , QgsGrassRegionBase()
192 , mX( 0 )
193 , mY( 0 )
194 , mUpdatingGui( false )
195 {
196 QgsDebugMsg( "QgsGrassRegion()" );
197 QgsGrass::initRegion( &mWindow );
198
199 setupUi( this );
200 connect( mDrawButton, &QPushButton::clicked, this, &QgsGrassRegion::mDrawButton_clicked );
201 setAttribute( Qt::WA_DeleteOnClose );
202
203 connect( mButtonBox, &QDialogButtonBox::clicked, this, &QgsGrassRegion::buttonClicked );
204
205 //mPlugin = plugin;
206 mInterface = iface;
207 mCanvas = mInterface->mapCanvas();
208 mUpdatingGui = false;
209
210 // Set input validators
211 QDoubleValidator *dv = new QDoubleValidator( nullptr );
212 QIntValidator *iv = new QIntValidator( nullptr );
213
214 mNorth->setValidator( dv );
215 mSouth->setValidator( dv );
216 mEast->setValidator( dv );
217 mWest->setValidator( dv );
218 mNSRes->setValidator( dv );
219 mEWRes->setValidator( dv );
220 mRows->setValidator( iv );
221 mCols->setValidator( iv );
222
223 // Group radio buttons
224 mRadioGroup = new QButtonGroup();
225 mRadioGroup->addButton( mResRadio );
226 mRadioGroup->addButton( mRowsColsRadio );
227 mResRadio->setChecked( true );
228 radioChanged();
229
230 connect( mRadioGroup, static_cast<void ( QButtonGroup::* )( int )>( &QButtonGroup::buttonClicked ), this, &QgsGrassRegion::radioChanged );
231
232 // Connect entries
233 connect( mNorth, &QLineEdit::editingFinished, this, &QgsGrassRegion::northChanged );
234 connect( mSouth, &QLineEdit::editingFinished, this, &QgsGrassRegion::southChanged );
235 connect( mEast, &QLineEdit::editingFinished, this, &QgsGrassRegion::eastChanged );
236 connect( mWest, &QLineEdit::editingFinished, this, &QgsGrassRegion::westChanged );
237 connect( mNSRes, &QLineEdit::editingFinished, this, &QgsGrassRegion::NSResChanged );
238 connect( mEWRes, &QLineEdit::editingFinished, this, &QgsGrassRegion::EWResChanged );
239 connect( mRows, &QLineEdit::editingFinished, this, &QgsGrassRegion::rowsChanged );
240 connect( mCols, &QLineEdit::editingFinished, this, &QgsGrassRegion::colsChanged );
241
242 connect( QgsGrass::instance(), &QgsGrass::regionChanged, this, &QgsGrassRegion::reloadRegion );
243 connect( mCanvas, &QgsMapCanvas::mapToolSet, this, &QgsGrassRegion::canvasMapToolSet );
244 }
245
~QgsGrassRegion()246 QgsGrassRegion::~QgsGrassRegion()
247 {
248 delete mRegionEdit;
249 }
250
formatExtent(double v)251 QString QgsGrassRegion::formatExtent( double v )
252 {
253 // format with precision approximately to meters
254 // max length of degree of latitude on pole is 111694 m
255 return qgsDoubleToString( v, mCrs.mapUnits() == QgsUnitTypes::DistanceDegrees ? 6 : 1 );
256 }
257
formatResolution(double v)258 QString QgsGrassRegion::formatResolution( double v )
259 {
260 return qgsDoubleToString( v, mCrs.mapUnits() == QgsUnitTypes::DistanceDegrees ? 10 : 4 );
261 }
262
readRegion()263 void QgsGrassRegion::readRegion()
264 {
265 // Read current region
266 try
267 {
268 QgsGrass::region( &mWindow );
269 }
270 catch ( QgsGrass::Exception &e )
271 {
272 QgsGrass::warning( e );
273 return;
274 }
275 }
276
refreshGui()277 void QgsGrassRegion::refreshGui()
278 {
279 if ( mUpdatingGui )
280 {
281 return;
282 }
283
284 mUpdatingGui = true;
285
286
287 mNorth->setText( formatExtent( mWindow.north ) );
288 mSouth->setText( formatExtent( mWindow.south ) );
289 mEast->setText( formatExtent( mWindow.east ) );
290 mWest->setText( formatExtent( mWindow.west ) );
291 mNSRes->setText( formatResolution( mWindow.ns_res ) );
292 mEWRes->setText( formatResolution( mWindow.ew_res ) );
293 mRows->setText( QString::number( mWindow.rows ) );
294 mCols->setText( QString::number( mWindow.cols ) );
295
296 displayRegion();
297 mUpdatingGui = false;
298 }
299
reloadRegion()300 void QgsGrassRegion::reloadRegion()
301 {
302 readRegion();
303 refreshGui();
304 }
305
mapsetChanged()306 void QgsGrassRegion::mapsetChanged()
307 {
308 delete mRegionEdit;
309 mRegionEdit = nullptr;
310 if ( QgsGrass::activeMode() )
311 {
312 mRegionEdit = new QgsGrassRegionEdit( mCanvas );
313 connect( mRegionEdit, &QgsGrassRegionEdit::captureEnded, this, &QgsGrassRegion::onCaptureFinished );
314
315 QString error;
316 mCrs = QgsGrass::crs( QgsGrass::getDefaultGisdbase(), QgsGrass::getDefaultLocation(), error );
317 reloadRegion();
318 }
319 }
320
northChanged()321 void QgsGrassRegion::northChanged()
322 {
323 if ( mUpdatingGui )
324 return;
325
326 mWindow.north = mNorth->text().toDouble();
327 if ( mWindow.north < mWindow.south )
328 mWindow.north = mWindow.south;
329
330 adjust();
331 refreshGui();
332 }
333
southChanged()334 void QgsGrassRegion::southChanged()
335 {
336 if ( mUpdatingGui )
337 return;
338
339 mWindow.south = mSouth->text().toDouble();
340 if ( mWindow.south > mWindow.north )
341 mWindow.south = mWindow.north;
342
343 adjust();
344 refreshGui();
345 }
346
eastChanged()347 void QgsGrassRegion::eastChanged()
348 {
349 if ( mUpdatingGui )
350 return;
351
352 mWindow.east = mEast->text().toDouble();
353 if ( mWindow.east < mWindow.west )
354 mWindow.east = mWindow.west;
355
356 adjust();
357 refreshGui();
358 }
359
westChanged()360 void QgsGrassRegion::westChanged()
361 {
362 if ( mUpdatingGui )
363 return;
364
365 mWindow.west = mWest->text().toDouble();
366 if ( mWindow.west > mWindow.east )
367 mWindow.west = mWindow.east;
368
369 adjust();
370 refreshGui();
371 }
372
NSResChanged()373 void QgsGrassRegion::NSResChanged()
374 {
375 if ( mUpdatingGui )
376 return;
377
378 mWindow.ns_res = mNSRes->text().toDouble();
379 if ( mWindow.ns_res <= 0 )
380 mWindow.ns_res = 1;
381
382 adjust();
383 refreshGui();
384 }
385
EWResChanged()386 void QgsGrassRegion::EWResChanged()
387 {
388 if ( mUpdatingGui )
389 return;
390
391 mWindow.ew_res = mEWRes->text().toDouble();
392 if ( mWindow.ew_res <= 0 )
393 mWindow.ew_res = 1;
394
395 adjust();
396 refreshGui();
397 }
398
rowsChanged()399 void QgsGrassRegion::rowsChanged()
400 {
401 if ( mUpdatingGui )
402 return;
403
404 mWindow.rows = mRows->text().toInt();
405 if ( mWindow.rows < 1 )
406 mWindow.rows = 1;
407
408 adjust();
409 refreshGui();
410 }
411
colsChanged()412 void QgsGrassRegion::colsChanged()
413 {
414 if ( mUpdatingGui )
415 return;
416
417 mWindow.cols = mCols->text().toInt();
418 if ( mWindow.cols < 1 )
419 mWindow.cols = 1;
420
421 adjust();
422 refreshGui();
423 }
424
adjust()425 void QgsGrassRegion::adjust()
426 {
427 mButtonBox->button( QDialogButtonBox::Apply )->setDisabled( false );
428 int rc = 0;
429 if ( mRowsColsRadio->isChecked() )
430 {
431 rc = 1;
432 }
433 G_TRY
434 {
435 G_adjust_Cell_head( &mWindow, rc, rc );
436 }
437 G_CATCH( QgsGrass::Exception & e )
438 {
439 QgsGrass::warning( e );
440 mButtonBox->button( QDialogButtonBox::Apply )->setDisabled( true );
441 }
442 }
443
radioChanged()444 void QgsGrassRegion::radioChanged()
445 {
446
447 bool res = !mRowsColsRadio->isChecked();
448
449 mEWResLabel->setEnabled( res );
450 mEWRes->setEnabled( res );
451 mNSResLabel->setEnabled( res );
452 mNSRes->setEnabled( res );
453
454 mColsLabel->setEnabled( !res );
455 mCols->setEnabled( !res );
456 mRowsLabel->setEnabled( !res );
457 mRows->setEnabled( !res );
458 }
459
onCaptureFinished()460 void QgsGrassRegion::onCaptureFinished()
461 {
462 if ( !mRegionEdit )
463 {
464 return;
465 }
466 QgsRectangle rect = mRegionEdit->getRegion();
467
468 mWindow.west = rect.xMinimum();
469 mWindow.east = rect.xMaximum();
470 mWindow.south = rect.yMinimum();
471 mWindow.north = rect.yMaximum();
472 adjust();
473
474 refreshGui();
475 }
476
canvasMapToolSet(QgsMapTool * tool)477 void QgsGrassRegion::canvasMapToolSet( QgsMapTool *tool )
478 {
479 mDrawButton->setChecked( tool == mRegionEdit );
480 }
481
displayRegion()482 void QgsGrassRegion::displayRegion()
483 {
484 if ( !mRegionEdit )
485 {
486 return;
487 }
488 QgsPointXY ul( mWindow.west, mWindow.north );
489 QgsPointXY lr( mWindow.east, mWindow.south );
490
491 mRegionEdit->setSrcRegion( QgsRectangle( ul, lr ) );
492 }
493
mDrawButton_clicked()494 void QgsGrassRegion::mDrawButton_clicked()
495 {
496 mCanvas->setMapTool( mRegionEdit );
497 }
498
buttonClicked(QAbstractButton * button)499 void QgsGrassRegion::buttonClicked( QAbstractButton *button )
500 {
501 if ( mButtonBox->buttonRole( button ) == QDialogButtonBox::ApplyRole )
502 {
503 try
504 {
505 QgsGrass::instance()->writeRegion( &mWindow );
506 }
507 catch ( QgsGrass::Exception &e )
508 {
509 QgsGrass::warning( e );
510 return;
511 }
512 }
513 else if ( mButtonBox->buttonRole( button ) == QDialogButtonBox::ResetRole )
514 {
515 reloadRegion();
516 }
517 // Better to keep the tool selected until another tool is chosen?
518 mCanvas->unsetMapTool( mRegionEdit );
519 }
520
521
522