QGIS API Documentation  2.12.0-Lyon
qgsmaptoolcapture.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmaptoolcapture.cpp - map tool for capturing points, lines, polygons
3  ---------------------
4  begin : January 2006
5  copyright : (C) 2006 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 "qgsmaptoolcapture.h"
17 
18 #include "qgscursors.h"
19 #include "qgsgeometryvalidator.h"
20 #include "qgslayertreeview.h"
21 #include "qgslinestringv2.h"
22 #include "qgslogger.h"
23 #include "qgsmapcanvas.h"
24 #include "qgsmapmouseevent.h"
25 #include "qgsmaprenderer.h"
26 #include "qgspolygonv2.h"
27 #include "qgsrubberband.h"
28 #include "qgsvectorlayer.h"
29 #include "qgsvertexmarker.h"
30 
31 #include <QCursor>
32 #include <QPixmap>
33 #include <QMouseEvent>
34 #include <QStatusBar>
35 
36 
38  : QgsMapToolAdvancedDigitizing( canvas, cadDockWidget )
39  , mRubberBand( 0 )
40  , mTempRubberBand( 0 )
41  , mValidator( 0 )
42  , mSnappingMarker( 0 )
43 #ifdef Q_OS_WIN
44  , mSkipNextContextMenuEvent( 0 )
45 #endif
46 {
48 
49  // enable the snapping on mouse move / release
50  mSnapOnMove = true;
51  mSnapOnRelease = true;
52  mSnapOnDoubleClick = false;
53  mSnapOnPress = false;
54 
55  mCaptureModeFromLayer = mode == CaptureNone;
56  mCapturing = false;
57 
58  QPixmap mySelectQPixmap = QPixmap(( const char ** ) capture_point_cursor );
59  setCursor( QCursor( mySelectQPixmap, 8, 8 ) );
60 
61  connect( canvas, SIGNAL( currentLayerChanged( QgsMapLayer * ) ),
62  this, SLOT( currentLayerChanged( QgsMapLayer * ) ) );
63 }
64 
66 {
67  delete mSnappingMarker;
68 
69  stopCapturing();
70 
71  if ( mValidator )
72  {
73  mValidator->deleteLater();
74  mValidator = 0;
75  }
76 }
77 
79 {
80  delete mSnappingMarker;
81  mSnappingMarker = 0;
82 
84 }
85 
86 void QgsMapToolCapture::validationFinished()
87 {
88  emit messageDiscarded();
89  QString msgFinished = tr( "Validation finished" );
90  if ( mValidationWarnings.count() )
91  {
92  emit messageEmitted( mValidationWarnings.join( "\n" ).append( "\n" ).append( msgFinished ), QgsMessageBar::WARNING );
93  }
94 }
95 
96 void QgsMapToolCapture::currentLayerChanged( QgsMapLayer *layer )
97 {
98  if ( !mCaptureModeFromLayer )
99  return;
100 
102 
103  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
104  if ( !vlayer )
105  {
106  return;
107  }
108 
109  switch ( vlayer->geometryType() )
110  {
111  case QGis::Point:
113  break;
114  case QGis::Line:
116  break;
117  case QGis::Polygon:
119  break;
120  default:
122  break;
123  }
124 }
125 
127 {
129  bool snapped = e->isSnapped();
130  QgsPoint point = e->mapPoint();
131 
132  if ( !snapped )
133  {
134  delete mSnappingMarker;
135  mSnappingMarker = 0;
136  }
137  else
138  {
139  if ( !mSnappingMarker )
140  {
141  mSnappingMarker = new QgsVertexMarker( mCanvas );
142  mSnappingMarker->setIconType( QgsVertexMarker::ICON_CROSS );
143  mSnappingMarker->setColor( Qt::magenta );
144  mSnappingMarker->setPenWidth( 3 );
145  }
146  mSnappingMarker->setCenter( point );
147  }
148 
149  if ( !mTempRubberBand && mCaptureCurve.numPoints() > 0 )
150  {
151  mTempRubberBand = createRubberBand( mCaptureMode == CapturePolygon ? QGis::Polygon : QGis::Line, true );
152  QgsPointV2 pt = mCaptureCurve.endPoint();
153  mTempRubberBand->addPoint( QgsPoint( pt.x(), pt.y() ) );
154  mTempRubberBand->addPoint( point );
155  }
156 
157  if ( mCaptureMode != CapturePoint && mTempRubberBand && mCapturing )
158  {
159  mTempRubberBand->movePoint( point );
160  }
161 } // mouseMoveEvent
162 
163 int QgsMapToolCapture::nextPoint( const QgsPoint& mapPoint, QgsPoint& layerPoint )
164 {
165  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
166  if ( !vlayer )
167  {
168  QgsDebugMsg( "no vector layer" );
169  return 1;
170  }
171  try
172  {
173  layerPoint = toLayerCoordinates( vlayer, mapPoint ); //transform snapped point back to layer crs
174  }
175  catch ( QgsCsException &cse )
176  {
177  Q_UNUSED( cse );
178  QgsDebugMsg( "transformation to layer coordinate failed" );
179  return 2;
180  }
181 
182  return 0;
183 }
184 
185 int QgsMapToolCapture::nextPoint( const QPoint &p, QgsPoint &layerPoint, QgsPoint &mapPoint )
186 {
187 
188  mapPoint = toMapCoordinates( p );
189  return nextPoint( mapPoint, layerPoint );
190 }
191 
193 {
194  if ( mode() == CaptureNone )
195  {
196  QgsDebugMsg( "invalid capture mode" );
197  return 2;
198  }
199 
200  int res;
201  QgsPoint layerPoint;
202  res = nextPoint( point, layerPoint );
203  if ( res != 0 )
204  {
205  return res;
206  }
207 
208  if ( !mRubberBand )
209  {
211  }
212  mRubberBand->addPoint( point );
213  mCaptureCurve.addVertex( QgsPointV2( layerPoint.x(), layerPoint.y() ) );
214 
215  if ( !mTempRubberBand )
216  {
217  mTempRubberBand = createRubberBand( mCaptureMode == CapturePolygon ? QGis::Polygon : QGis::Line, true );
218  }
219  else
220  {
221  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? true : false );
222  }
223  if ( mCaptureMode == CaptureLine )
224  {
225  mTempRubberBand->addPoint( point );
226  }
227  else if ( mCaptureMode == CapturePolygon )
228  {
229  const QgsPoint *firstPoint = mRubberBand->getPoint( 0, 0 );
230  mTempRubberBand->addPoint( *firstPoint );
231  mTempRubberBand->movePoint( point );
232  mTempRubberBand->addPoint( point );
233  }
234 
235  validateGeometry();
236 
237  return 0;
238 }
239 
241 {
242  if ( !c )
243  {
244  return 1;
245  }
246 
247  if ( !mRubberBand )
248  {
250  }
251 
252  QgsLineStringV2* lineString = c->curveToLine();
253  QList<QgsPointV2> linePoints;
254  lineString->points( linePoints );
255  delete lineString;
256  QList<QgsPointV2>::const_iterator ptIt = linePoints.constBegin();
257  for ( ; ptIt != linePoints.constEnd(); ++ptIt )
258  {
259  mRubberBand->addPoint( QgsPoint( ptIt->x(), ptIt->y() ) );
260  }
261 
262  if ( !mTempRubberBand )
263  {
264  mTempRubberBand = createRubberBand( mCaptureMode == CapturePolygon ? QGis::Polygon : QGis::Line, true );
265  }
266  else
267  {
268  mTempRubberBand->reset();
269  }
270  QgsPointV2 endPt = c->endPoint();
271  mTempRubberBand->addPoint( QgsPoint( endPt.x(), endPt.y() ) ); //add last point of c
272 
273  //transform back to layer CRS in case map CRS and layer CRS are different
274  QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
275  const QgsCoordinateTransform* ct = mCanvas->mapSettings().layerTransform( vlayer );
276  if ( ct )
277  {
279  }
280  mCaptureCurve.addCurve( c );
281 
282  return 0;
283 }
284 
285 
287 {
288  if ( mRubberBand )
289  {
290  int rubberBandSize = mRubberBand->numberOfVertices();
291  int tempRubberBandSize = mTempRubberBand->numberOfVertices();
292  int captureListSize = size();
293 
294  if ( rubberBandSize < 1 || captureListSize < 1 )
295  {
296  return;
297  }
298 
299  mRubberBand->removePoint( -1 );
300 
301  if ( rubberBandSize > 1 )
302  {
303  if ( tempRubberBandSize > 1 )
304  {
305  const QgsPoint *point = mRubberBand->getPoint( 0, rubberBandSize - 2 );
306  mTempRubberBand->movePoint( tempRubberBandSize - 2, *point );
307  }
308  }
309  else
310  {
311  mTempRubberBand->reset( mCaptureMode == CapturePolygon ? true : false );
312  }
313 
314  QgsVertexId vertexToRemove;
315  vertexToRemove.part = 0; vertexToRemove.ring = 0; vertexToRemove.vertex = size() - 1;
316  mCaptureCurve.deleteVertex( vertexToRemove );
317 
318  validateGeometry();
319  }
320 }
321 
323 {
324  if ( e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete )
325  {
326  undo();
327 
328  // Override default shortcut management in MapCanvas
329  e->ignore();
330  }
331  else if ( e->key() == Qt::Key_Escape )
332  {
333  stopCapturing();
334 
335  // Override default shortcut management in MapCanvas
336  e->ignore();
337  }
338 }
339 
341 {
342  mCapturing = true;
343 }
344 
346 {
347  return mCapturing;
348 }
349 
351 {
352  if ( mRubberBand )
353  {
354  delete mRubberBand;
355  mRubberBand = 0;
356  }
357 
358  if ( mTempRubberBand )
359  {
360  delete mTempRubberBand;
361  mTempRubberBand = 0;
362  }
363 
364  while ( !mGeomErrorMarkers.isEmpty() )
365  {
366  delete mGeomErrorMarkers.takeFirst();
367  }
368 
369  mGeomErrors.clear();
370 
371 #ifdef Q_OS_WIN
372  Q_FOREACH ( QWidget *w, qApp->topLevelWidgets() )
373  {
374  if ( w->objectName() == "QgisApp" )
375  {
376  if ( mSkipNextContextMenuEvent++ == 0 )
377  w->installEventFilter( this );
378  break;
379  }
380  }
381 #endif
382 
383  mCapturing = false;
384  mCaptureCurve.clear();
385  if ( currentVectorLayer() )
387 }
388 
390 {
391  if ( mTempRubberBand )
392  {
393  delete mTempRubberBand;
394  mTempRubberBand = 0;
395  }
396 }
397 
399 {
400  mCaptureCurve.close();
401 }
402 
403 void QgsMapToolCapture::validateGeometry()
404 {
405  QSettings settings;
406  if ( settings.value( "/qgis/digitizing/validate_geometries", 1 ).toInt() == 0 )
407  return;
408 
409  if ( mValidator )
410  {
411  mValidator->deleteLater();
412  mValidator = 0;
413  }
414 
415  mValidationWarnings.clear();
416  mGeomErrors.clear();
417  while ( !mGeomErrorMarkers.isEmpty() )
418  {
419  delete mGeomErrorMarkers.takeFirst();
420  }
421 
423 
424  switch ( mCaptureMode )
425  {
426  case CaptureNone:
427  case CapturePoint:
428  return;
429  case CaptureLine:
430  if ( size() < 2 )
431  return;
432  g.reset( new QgsGeometry( mCaptureCurve.curveToLine() ) );
433  break;
434  case CapturePolygon:
435  if ( size() < 3 )
436  return;
437  QgsLineStringV2* exteriorRing = mCaptureCurve.curveToLine();
438  exteriorRing->close();
439  QgsPolygonV2* polygon = new QgsPolygonV2();
440  polygon->setExteriorRing( exteriorRing );
441  g.reset( new QgsGeometry( polygon ) );
442  break;
443  }
444 
445  if ( !g.data() )
446  return;
447 
448  mValidator = new QgsGeometryValidator( g.data() );
449  connect( mValidator, SIGNAL( errorFound( QgsGeometry::Error ) ), this, SLOT( addError( QgsGeometry::Error ) ) );
450  connect( mValidator, SIGNAL( finished() ), this, SLOT( validationFinished() ) );
451  mValidator->start();
452  messageEmitted( tr( "Validation started" ) );
453 }
454 
455 void QgsMapToolCapture::addError( QgsGeometry::Error e )
456 {
457  mGeomErrors << e;
458  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() );
459  if ( !vlayer )
460  return;
461 
462  mValidationWarnings << e.what();
463 
464  if ( e.hasWhere() )
465  {
467  vm->setCenter( mCanvas->mapSettings().layerToMapCoordinates( vlayer, e.where() ) );
469  vm->setPenWidth( 2 );
470  vm->setToolTip( e.what() );
471  vm->setColor( Qt::green );
472  vm->setZValue( vm->zValue() + 1 );
473  mGeomErrorMarkers << vm;
474  }
475 
476  emit messageDiscarded();
477  emit messageEmitted( mValidationWarnings.join( "\n" ), QgsMessageBar::WARNING );
478 }
479 
481 {
482  return mCaptureCurve.numPoints();
483 }
484 
486 {
487  QList<QgsPointV2> pts;
489  mCaptureCurve.points( pts );
490  QgsGeometry::convertPointList( pts, points );
491  return points;
492 }
493 
495 {
496  QList<QgsPointV2> pts;
497  QgsGeometry::convertPointList( pointList, pts );
498 
499  QgsLineStringV2* line = new QgsLineStringV2();
500  line->setPoints( pts );
501 
502  mCaptureCurve.clear();
503  mCaptureCurve.addCurve( line );
504 }
505 
506 #ifdef Q_OS_WIN
507 bool QgsMapToolCapture::eventFilter( QObject *obj, QEvent *event )
508 {
509  if ( event->type() != QEvent::ContextMenu )
510  return false;
511 
512  if ( --mSkipNextContextMenuEvent == 0 )
513  obj->removeEventFilter( this );
514 
515  return mSkipNextContextMenuEvent >= 0;
516 }
517 #endif
void clear()
int addCurve(QgsCurveV2 *c)
Adds a whole curve (e.g.
void close()
Appends first point if not already closed.
Base class for all map layer types.
Definition: qgsmaplayer.h:49
QgsPoint layerToMapCoordinates(QgsMapLayer *theLayer, QgsPoint point) const
transform point coordinates from layer's CRS to output CRS
QString & append(QChar ch)
Type type() const
virtual QgsLineStringV2 * curveToLine() const override
Returns a new line string geometry corresponding to a segmentized approximation of the curve...
QgsPoint toMapCoordinates(const QPoint &point)
transformation from screen coordinates to map coordinates
Definition: qgsmaptool.cpp:42
QgsPoint mapPoint() const
mapPoint returns the point in coordinates
void close()
Appends first point if not already closed.
int size()
Number of points digitized.
double x() const
Definition: qgspointv2.h:42
const QgsCoordinateTransform * layerTransform(QgsMapLayer *layer) const
Return coordinate transform from layer's CRS to destination CRS.
void points(QList< QgsPointV2 > &pt) const override
Returns a list of points within the curve.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
virtual QgsPointV2 endPoint() const override
Returns the end point of the curve.
virtual bool deleteVertex(const QgsVertexId &position) override
Deletes a vertex within the geometry.
void movePoint(const QgsPoint &p, int geometryIndex=0)
Moves the rubber band point specified by index.
const QgsPoint * getPoint(int i, int j=0) const
Return vertex.
void setPenWidth(int width)
A QgsMapMouseEvent is the result of a user interaction with the mouse on a QgsMapCanvas.
const QgsMapSettings & mapSettings() const
Get access to properties used for map rendering.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:76
int numberOfVertices() const
Returns count of vertices in all lists of mPoint.
QString join(const QString &separator) const
The QgsMapToolAdvancedDigitizing class is a QgsMapTool whcih gives event directly in map coordinates ...
bool mSnapOnDoubleClick
snap on double click
CaptureMode mode() const
The capture mode.
QString tr(const char *sourceText, const char *disambiguation, int n)
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:107
double x() const
Get the x value of the point.
Definition: qgspoint.h:126
void reset(T *other)
double y() const
Definition: qgspointv2.h:43
void start(Priority priority)
QgsMapCanvas * mCanvas
pointer to map canvas
Definition: qgsmaptool.h:193
Polygon geometry type.
Definition: qgspolygonv2.h:29
qreal zValue() const
int count(const T &value) const
void ignore()
void installEventFilter(QObject *filterObj)
int toInt(bool *ok) const
Utility class for identifying a unique vertex within a geometry.
Line string geometry type.
void addVertex(const QgsPointV2 &pt)
Adds a vertex to the end of the geometry.
static void convertPointList(const QList< QgsPoint > &input, QList< QgsPointV2 > &output)
Upgrades a point list from QgsPoint to QgsPointV2.
void removePoint(int index=0, bool doUpdate=true, int geometryIndex=0)
Remove a vertex from the rubberband and (optionally) update canvas.
Point geometry type.
Definition: qgspointv2.h:29
bool isEmpty() const
void setToolTip(const QString &toolTip)
void setCenter(const QgsPoint &point)
QgsPoint toLayerCoordinates(QgsMapLayer *layer, const QPoint &point)
transformation from screen coordinates to layer's coordinates
Definition: qgsmaptool.cpp:54
void triggerRepaint()
Will advice the map canvas (and any other interested party) that this layer requires to be repainted...
void addPoint(const QgsPoint &p, bool doUpdate=true, int geometryIndex=0)
Add a vertex to the rubberband and update canvas.
void reset(QGis::GeometryType geometryType=QGis::Line)
Clears all the geometries in this rubberband.
QgsRubberBand * createRubberBand(QGis::GeometryType geometryType=QGis::Line, bool alternativeBand=false)
Creates a rubber band with the color/line width from the QGIS settings.
virtual void transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d=QgsCoordinateTransform::ForwardTransform)=0
Transforms the geometry using a coordinate transform.
A class for marking vertices of features using e.g.
virtual bool eventFilter(QObject *watched, QEvent *event)
void messageEmitted(QString message, QgsMessageBar::MessageLevel=QgsMessageBar::INFO)
emit a message
QGis::GeometryType geometryType() const
Returns point, line or polygon.
void deleteLater()
CaptureMode mCaptureMode
The capture mode in which this tool operates.
A class to represent a point.
Definition: qgspoint.h:63
void undo()
Removes the last vertex from mRubberBand and mCaptureList.
T * data() const
int key() const
QgsMapLayer * currentLayer()
returns current layer (set by legend widget)
const char * capture_point_cursor[]
Definition: qgscursors.cpp:87
QgsVectorLayer * currentVectorLayer()
Returns the current vector layer of the map canvas or 0.
void addCurve(QgsCurveV2 *c)
Adds a curve to the geometr (takes ownership)
void closePolygon()
Close an open polygon.
QVariant value(const QString &key, const QVariant &defaultValue) const
void deleteTempRubberBand()
Clean a temporary rubberband.
virtual QgsLineStringV2 * curveToLine() const =0
Returns a new line string geometry corresponding to a segmentized approximation of the curve...
virtual QgsPointV2 endPoint() const =0
Returns the end point of the curve.
virtual void deactivate() override
deactive the tool
void startCapturing()
Start capturing.
virtual void keyPressEvent(QKeyEvent *e) override
Intercept key events like Esc or Del to delete the last point.
QgsMapToolCapture(QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget, CaptureMode mode=CaptureNone)
constructor
void setPoints(const QList< QgsPointV2 > &points)
void setExteriorRing(QgsCurveV2 *ring)
Sets exterior ring (takes ownership)
T takeFirst()
bool isSnapped() const
Returns true if there is a snapped point cached.
virtual void points(QList< QgsPointV2 > &pts) const override
Returns a list of points within the curve.
virtual ~QgsMapToolCapture()
destructor
Class for doing transforms between two map coordinate systems.
QList< QgsPoint > points()
List of digitized points.
int addVertex(const QgsPoint &point)
Adds a point to the rubber band (in map coordinates) and to the capture list (in layer coordinates) ...
void messageDiscarded()
emit signal to clear previous message
int nextPoint(const QgsPoint &mapPoint, QgsPoint &layerPoint)
double y() const
Get the y value of the point.
Definition: qgspoint.h:134
virtual void cadCanvasMoveEvent(QgsMapMouseEvent *e)
Override this method when subclassing this class.
virtual void setCursor(const QCursor &cursor)
Set a user defined cursor.
Definition: qgsmaptool.cpp:139
void stopCapturing()
Stop capturing.
Custom exception class for Coordinate Reference System related exceptions.
const_iterator constEnd() const
const_iterator constBegin() const
virtual void cadCanvasMoveEvent(QgsMapMouseEvent *e) override
Update the rubberband according to mouse position.
Abstract base class for curved geometry type.
Definition: qgscurvev2.h:32
void setPoints(const QList< QgsPoint > &pointList)
Set the points on which to work.
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
The QgsAdvancedDigitizingDock class is a dockable widget used to handle the CAD tools on top of a sel...
void setColor(const QColor &color)
Represents a vector layer which manages a vector based data sets.
bool isCapturing() const
Are we currently capturing?
virtual int numPoints() const override
Returns the number of points in the curve.
void setZValue(qreal z)
void setIconType(int iconType)
void removeEventFilter(QObject *obj)
virtual void clear() override
Clears the geometry, ie reset it to a null geometry.
virtual void deactivate() override
Unregisters this maptool from the cad dock widget.