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