QGIS API Documentation  2.9.0-Master
qgsrubberband.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrubberband.cpp - Rubberband widget for drawing multilines and polygons
3  --------------------------------------
4  Date : 07-Jan-2006
5  Copyright : (C) 2006 by Tom Elwertowski
6  Email : telwertowski at users dot sourceforge dot net
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 "qgsrubberband.h"
17 #include "qgsfeature.h"
18 #include "qgsgeometry.h"
19 #include "qgslogger.h"
20 #include "qgsmapcanvas.h"
21 #include "qgsmaprenderer.h"
22 #include "qgsvectorlayer.h"
23 #include <QPainter>
24 
31  : QgsMapCanvasItem( mapCanvas )
32  , mIconSize( 5 )
33  , mIconType( ICON_CIRCLE )
34  , mGeometryType( geometryType )
35  , mTranslationOffsetX( 0.0 )
36  , mTranslationOffsetY( 0.0 )
37 {
38  reset( geometryType );
39  QColor color( Qt::lightGray );
40  color.setAlpha( 63 );
41  setColor( color );
42  setWidth( 1 );
43  setLineStyle( Qt::SolidLine );
44  setBrushStyle( Qt::SolidPattern );
45 }
46 
47 QgsRubberBand::QgsRubberBand( QgsMapCanvas* mapCanvas, bool isPolygon )
48  : QgsMapCanvasItem( mapCanvas )
49  , mIconSize( 5 )
50  , mIconType( ICON_CIRCLE )
51  , mTranslationOffsetX( 0.0 )
52  , mTranslationOffsetY( 0.0 )
53 {
54  reset( isPolygon ? QGis::Polygon : QGis::Line );
55  QColor color( Qt::lightGray );
56  color.setAlpha( 63 );
57  setColor( color );
58  setWidth( 1 );
59  setLineStyle( Qt::SolidLine );
60  setBrushStyle( Qt::SolidPattern );
61 }
62 
63 QgsRubberBand::QgsRubberBand()
64  : QgsMapCanvasItem( 0 )
65  , mIconSize( 5 )
66  , mIconType( ICON_CIRCLE )
67  , mGeometryType( QGis::Polygon )
68  , mTranslationOffsetX( 0.0 )
69  , mTranslationOffsetY( 0.0 )
70 {
71 }
72 
74 {
75 }
76 
80 void QgsRubberBand::setColor( const QColor & color )
81 {
82  setBorderColor( color );
83  setFillColor( color );
84 }
85 
89 void QgsRubberBand::setFillColor( const QColor & color )
90 {
91  QColor fillColor( color.red(), color.green(), color.blue(), color.alpha() );
92  mBrush.setColor( fillColor );
93 }
94 
98 void QgsRubberBand::setBorderColor( const QColor & color )
99 {
100  QColor penColor( color.red(), color.green(), color.blue(), color.alpha() );
101  mPen.setColor( penColor );
102 }
103 
104 
108 void QgsRubberBand::setWidth( int width )
109 {
110  mPen.setWidth( width );
111 }
112 
114 {
115  mIconType = icon;
116 }
117 
118 void QgsRubberBand::setIconSize( int iconSize )
119 {
120  mIconSize = iconSize;
121 }
122 
123 void QgsRubberBand::setLineStyle( Qt::PenStyle penStyle )
124 {
125  mPen.setStyle( penStyle );
126 }
127 
128 void QgsRubberBand::setBrushStyle( Qt::BrushStyle brushStyle )
129 {
130  mBrush.setStyle( brushStyle );
131 }
132 
137 {
138  mPoints.clear();
139  mGeometryType = geometryType;
140  updateRect();
141  update();
142 }
143 
144 void QgsRubberBand::reset( bool isPolygon )
145 {
146  mPoints.clear();
147  mGeometryType = isPolygon ? QGis::Polygon : QGis::Line;
148  updateRect();
149  update();
150 }
151 
155 void QgsRubberBand::addPoint( const QgsPoint & p, bool doUpdate /* = true */, int geometryIndex )
156 {
157  if ( geometryIndex < 0 )
158  {
159  geometryIndex = mPoints.size() - 1;
160  }
161 
162  if ( geometryIndex < 0 || geometryIndex > mPoints.size() )
163  {
164  return;
165  }
166 
167  if ( geometryIndex == mPoints.size() )
168  {
169  mPoints.push_back( QList<QgsPoint>() << p );
170  }
171 
172  if ( mPoints[geometryIndex].size() == 2 &&
173  mPoints[geometryIndex][0] == mPoints[geometryIndex][1] )
174  {
175  mPoints[geometryIndex].last() = p;
176  }
177  else
178  {
179  mPoints[geometryIndex] << p;
180  }
181 
182 
183  if ( doUpdate )
184  {
185  setVisible( true );
186  updateRect();
187  update();
188  }
189 }
190 
191 
192 void QgsRubberBand::removePoint( int index, bool doUpdate/* = true*/, int geometryIndex/* = 0*/ )
193 {
194 
195  if ( mPoints.size() < geometryIndex + 1 )
196  {
197  return;
198  }
199 
200 
201  if ( mPoints[geometryIndex].size() > 0 )
202  {
203  // negative index removes from end, eg -1 removes last one
204  if ( index < 0 )
205  {
206  index = mPoints[geometryIndex].size() + index;
207  }
208  mPoints[geometryIndex].removeAt( index );
209  }
210 
211  if ( doUpdate )
212  {
213  updateRect();
214  update();
215  }
216 }
217 
218 void QgsRubberBand::removeLastPoint( int geometryIndex, bool doUpdate/* = true*/ )
219 {
220  removePoint( -1, doUpdate, geometryIndex );
221 }
222 
226 void QgsRubberBand::movePoint( const QgsPoint & p, int geometryIndex )
227 {
228  if ( mPoints.size() < geometryIndex + 1 )
229  {
230  return;
231  }
232 
233  if ( mPoints.at( geometryIndex ).size() < 1 )
234  {
235  return;
236  }
237 
238  mPoints[geometryIndex].last() = p;
239 
240  updateRect();
241  update();
242 }
243 
244 void QgsRubberBand::movePoint( int index, const QgsPoint& p, int geometryIndex )
245 {
246  if ( mPoints.size() < geometryIndex + 1 )
247  {
248  return;
249  }
250 
251  if ( mPoints.at( geometryIndex ).size() < index )
252  {
253  return;
254  }
255 
256  mPoints[geometryIndex][index] = p;
257 
258  updateRect();
259  update();
260 }
261 
262 void QgsRubberBand::setToGeometry( const QgsGeometry* geom, QgsVectorLayer* layer )
263 {
264  if ( !geom )
265  {
266  reset( mGeometryType );
267  return;
268  }
269 
270  reset( geom->type() );
271  addGeometry( geom, layer );
272 }
273 
274 void QgsRubberBand::addGeometry( const QgsGeometry* geom, QgsVectorLayer* layer )
275 {
276  if ( !geom )
277  {
278  return;
279  }
280 
281  //maprender object of canvas
282  const QgsMapSettings& ms = mMapCanvas->mapSettings();
283 
284  int idx = mPoints.size();
285 
286  switch ( geom->wkbType() )
287  {
288 
289  case QGis::WKBPoint:
290  case QGis::WKBPoint25D:
291  {
292  QgsPoint pt;
293  if ( layer )
294  {
295  pt = ms.layerToMapCoordinates( layer, geom->asPoint() );
296  }
297  else
298  {
299  pt = geom->asPoint();
300  }
301  addPoint( pt, false, idx );
302  removeLastPoint( idx, false );
303  }
304  break;
305 
306  case QGis::WKBMultiPoint:
308  {
309  QgsMultiPoint mpt = geom->asMultiPoint();
310  for ( int i = 0; i < mpt.size(); ++i, ++idx )
311  {
312  QgsPoint pt = mpt[i];
313  if ( layer )
314  {
315  addPoint( ms.layerToMapCoordinates( layer, pt ), false, idx );
316  removeLastPoint( idx, false );
317  }
318  else
319  {
320  addPoint( pt, false, idx );
321  removeLastPoint( idx, false );
322  }
323  }
324  }
325  break;
326 
327  case QGis::WKBLineString:
329  {
330  QgsPolyline line = geom->asPolyline();
331  for ( int i = 0; i < line.count(); i++ )
332  {
333  if ( layer )
334  {
335  addPoint( ms.layerToMapCoordinates( layer, line[i] ), false, idx );
336  }
337  else
338  {
339  addPoint( line[i], false, idx );
340  }
341  }
342  }
343  break;
344 
347  {
348 
349  QgsMultiPolyline mline = geom->asMultiPolyline();
350  for ( int i = 0; i < mline.size(); ++i, ++idx )
351  {
352  QgsPolyline line = mline[i];
353 
354  if ( line.size() == 0 )
355  {
356  --idx;
357  }
358 
359  for ( int j = 0; j < line.size(); ++j )
360  {
361  if ( layer )
362  {
363  addPoint( ms.layerToMapCoordinates( layer, line[j] ), false, idx );
364  }
365  else
366  {
367  addPoint( line[j], false, idx );
368  }
369  }
370  }
371  }
372  break;
373 
374  case QGis::WKBPolygon:
375  case QGis::WKBPolygon25D:
376  {
377  QgsPolygon poly = geom->asPolygon();
378  QgsPolyline line = poly[0];
379  for ( int i = 0; i < line.count(); i++ )
380  {
381  if ( layer )
382  {
383  addPoint( ms.layerToMapCoordinates( layer, line[i] ), false, idx );
384  }
385  else
386  {
387  addPoint( line[i], false, idx );
388  }
389  }
390  }
391  break;
392 
395  {
396 
397  QgsMultiPolygon multipoly = geom->asMultiPolygon();
398  for ( int i = 0; i < multipoly.size(); ++i, ++idx )
399  {
400  QgsPolygon poly = multipoly[i];
401  QgsPolyline line = poly[0];
402  for ( int j = 0; j < line.count(); ++j )
403  {
404  if ( layer )
405  {
406  addPoint( ms.layerToMapCoordinates( layer, line[j] ), false, idx );
407  }
408  else
409  {
410  addPoint( line[j], false, idx );
411  }
412  }
413  }
414  }
415  break;
416 
417  case QGis::WKBUnknown:
418  default:
419  return;
420  }
421 
422  setVisible( true );
423  updateRect();
424  update();
425 }
426 
427 void QgsRubberBand::setToCanvasRectangle( const QRect& rect )
428 {
429  if ( !mMapCanvas )
430  {
431  return;
432  }
433 
434  const QgsMapToPixel* transform = mMapCanvas->getCoordinateTransform();
435  QgsPoint ll = transform->toMapCoordinates( rect.left(), rect.bottom() );
436  QgsPoint lr = transform->toMapCoordinates( rect.right(), rect.bottom() );
437  QgsPoint ul = transform->toMapCoordinates( rect.left(), rect.top() );
438  QgsPoint ur = transform->toMapCoordinates( rect.right(), rect.top() );
439 
440  reset( QGis::Polygon );
441  addPoint( ll, false );
442  addPoint( lr, false );
443  addPoint( ur, false );
444  addPoint( ul, true );
445 }
446 
450 void QgsRubberBand::paint( QPainter* p )
451 {
452  if ( mPoints.size() > 0 )
453  {
454  p->setBrush( mBrush );
455  p->setPen( mPen );
456 
457  Q_FOREACH ( const QList<QgsPoint>& line, mPoints )
458  {
459  QVector<QPointF> pts;
460  Q_FOREACH ( const QgsPoint& pt, line )
461  {
462  const QPointF cur = toCanvasCoordinates( QgsPoint( pt.x() + mTranslationOffsetX, pt.y() + mTranslationOffsetY ) ) - pos();
463  if ( pts.empty() || std::abs( pts.back().x() - cur.x() ) > 1 || std::abs( pts.back().y() - cur.y() ) > 1 )
464  pts.append( cur );
465  }
466 
467  switch ( mGeometryType )
468  {
469  case QGis::Polygon:
470  {
471  p->drawPolygon( pts );
472  }
473  break;
474 
475  case QGis::Point:
476  {
477  Q_FOREACH ( const QPointF& pt, pts )
478  {
479  double x = pt.x();
480  double y = pt.y();
481 
482  qreal s = ( mIconSize - 1 ) / 2.0;
483 
484  switch ( mIconType )
485  {
486  case ICON_NONE:
487  break;
488 
489  case ICON_CROSS:
490  p->drawLine( QLineF( x - s, y, x + s, y ) );
491  p->drawLine( QLineF( x, y - s, x, y + s ) );
492  break;
493 
494  case ICON_X:
495  p->drawLine( QLineF( x - s, y - s, x + s, y + s ) );
496  p->drawLine( QLineF( x - s, y + s, x + s, y - s ) );
497  break;
498 
499  case ICON_BOX:
500  p->drawLine( QLineF( x - s, y - s, x + s, y - s ) );
501  p->drawLine( QLineF( x + s, y - s, x + s, y + s ) );
502  p->drawLine( QLineF( x + s, y + s, x - s, y + s ) );
503  p->drawLine( QLineF( x - s, y + s, x - s, y - s ) );
504  break;
505 
506  case ICON_FULL_BOX:
507  p->drawRect( x - s, y - s, mIconSize, mIconSize );
508  break;
509 
510  case ICON_CIRCLE:
511  p->drawEllipse( x - s, y - s, mIconSize, mIconSize );
512  break;
513  }
514  }
515  }
516  break;
517 
518  case QGis::Line:
519  default:
520  {
521  p->drawPolyline( pts );
522  }
523  break;
524  }
525  }
526  }
527 }
528 
530 {
531  if ( mPoints.empty() )
532  {
533  setRect( QgsRectangle() );
534  setVisible( false );
535  return;
536  }
537 
538  const QgsMapToPixel& m2p = *( mMapCanvas->getCoordinateTransform() );
539 
540  qreal res = m2p.mapUnitsPerPixel();
541  qreal w = (( mIconSize - 1 ) / 2 + mPen.width() ) / res;
542 
543  QgsRectangle r;
544  for ( int i = 0; i < mPoints.size(); ++i )
545  {
546  QList<QgsPoint>::const_iterator it = mPoints.at( i ).constBegin(),
547  itE = mPoints.at( i ).constEnd();
548  for ( ; it != itE; ++it )
549  {
550  QgsPoint p( it->x() + mTranslationOffsetX, it->y() + mTranslationOffsetY );
551  p = m2p.transform( p );
552  QgsRectangle rect( p.x() - w, p.y() - w, p.x() + w, p.y() + w );
553 
554  if ( r.isEmpty() )
555  {
556  // Get rectangle of the first point
557  r = rect;
558  }
559  else
560  {
561  r.combineExtentWith( &rect );
562  }
563  }
564  }
565 
566  // This is an hack to pass QgsMapCanvasItem::setRect what it
567  // expects (encoding of position and size of the item)
568  QgsPoint topLeft = m2p.toMapPoint( r.xMinimum(), r.yMinimum() );
569  QgsRectangle rect( topLeft.x(), topLeft.y(), topLeft.x() + r.width()*res, topLeft.y() - r.height()*res );
570 
571  setRect( rect );
572 }
573 
575 {
576  // re-compute rectangle
577  // See http://hub.qgis.org/issues/12392
578  // NOTE: could be optimized by saving map-extent
579  // of rubberband and simply re-projecting
580  // that to device-rectange on "updatePosition"
581  updateRect();
582 }
583 
584 void QgsRubberBand::setTranslationOffset( double dx, double dy )
585 {
586  mTranslationOffsetX = dx;
587  mTranslationOffsetY = dy;
588  updateRect();
589 }
590 
592 {
593  return mPoints.size();
594 }
595 
596 int QgsRubberBand::partSize( int geometryIndex ) const
597 {
598  if ( geometryIndex < 0 || geometryIndex >= mPoints.size() ) return 0;
599  return mPoints[geometryIndex].size();
600 }
601 
603 {
604  int count = 0;
605  QList<QList<QgsPoint> >::const_iterator it = mPoints.constBegin();
606  for ( ; it != mPoints.constEnd(); ++it )
607  {
608  QList<QgsPoint>::const_iterator iter = it->constBegin();
609  for ( ; iter != it->constEnd(); ++iter )
610  {
611  ++count;
612  }
613  }
614  return count;
615 }
616 
617 const QgsPoint *QgsRubberBand::getPoint( int i, int j ) const
618 {
619  if ( i < mPoints.size() && j < mPoints[i].size() )
620  return &mPoints[i][j];
621  else
622  return 0;
623 }
624 
626 {
627  QgsGeometry *geom = NULL;
628 
629  switch ( mGeometryType )
630  {
631  case QGis::Polygon:
632  {
633  QgsPolygon polygon;
634  QList< QList<QgsPoint> >::const_iterator it = mPoints.constBegin();
635  for ( ; it != mPoints.constEnd(); ++it )
636  {
637  polygon.append( getPolyline( *it ) );
638  }
639  geom = QgsGeometry::fromPolygon( polygon );
640  break;
641  }
642 
643  case QGis::Point:
644  {
645  QgsMultiPoint multiPoint;
646 
647  QList< QList<QgsPoint> >::const_iterator it = mPoints.constBegin();
648  for ( ; it != mPoints.constEnd(); ++it )
649  {
650  multiPoint += getPolyline( *it );
651  }
652  geom = QgsGeometry::fromMultiPoint( multiPoint );
653  break;
654  }
655 
656  case QGis::Line:
657  default:
658  {
659  if ( mPoints.size() > 0 )
660  {
661  if ( mPoints.size() > 1 )
662  {
663  QgsMultiPolyline multiPolyline;
664  QList< QList<QgsPoint> >::const_iterator it = mPoints.constBegin();
665  for ( ; it != mPoints.constEnd(); ++it )
666  {
667  multiPolyline.append( getPolyline( *it ) );
668  }
669  geom = QgsGeometry::fromMultiPolyline( multiPolyline );
670  }
671  else
672  {
673  geom = QgsGeometry::fromPolyline( getPolyline( mPoints[0] ) );
674  }
675  }
676  break;
677  }
678  }
679  return geom;
680 }
681 
682 QgsPolyline QgsRubberBand::getPolyline( const QList<QgsPoint> & points )
683 {
684  QgsPolyline polyline;
685  QList<QgsPoint>::const_iterator iter = points.constBegin();
686  for ( ; iter != points.constEnd(); ++iter )
687  {
688  polyline.append( *iter );
689  }
690  return polyline;
691 }
void setIconSize(int iconSize)
Set the size of the point icons.
void setWidth(int width)
Set the width of the line.
static unsigned index
A rectangle specified with double values.
Definition: qgsrectangle.h:35
QgsPoint layerToMapCoordinates(QgsMapLayer *theLayer, QgsPoint point) const
transform point coordinates from layer's CRS to output CRS
bool isEmpty() const
test if rectangle is empty.
void setBorderColor(const QColor &color)
Set the border color for the rubberband.
QPointF toCanvasCoordinates(const QgsPoint &point) const
transformation from map coordinates to screen coordinates
void setTranslationOffset(double dx, double dy)
Adds translation to original coordinates (all in map coordinates)
void setLineStyle(Qt::PenStyle penStyle)
Set the style of the line.
void movePoint(const QgsPoint &p, int geometryIndex=0)
Moves the rubber band point specified by index.
A box is used to highlight points (□)
Definition: qgsrubberband.h:54
QgsPoint transform(const QgsPoint &p) const
const QgsPoint * getPoint(int i, int j=0) const
Return vertex.
An abstract class for items that can be placed on the map canvas.
const QgsMapSettings & mapSettings() const
Get access to properties used for map rendering.
GeometryType
Definition: qgis.h:155
virtual void updatePosition() override
called on changed extent or resize event to update position of the item
The QGis class provides global constants for use throughout the application.
Definition: qgis.h:34
int numberOfVertices() const
Returns count of vertices in all lists of mPoint.
QgsRectangle rect() const
returns canvas item rectangle in map units
QgsGeometry * asGeometry()
Returns the rubberband as a Geometry.
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:105
double x() const
Definition: qgspoint.h:126
virtual void paint(QPainter *p) override
The QgsMapSettings class contains configuration for rendering of the map.
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:34
void combineExtentWith(QgsRectangle *rect)
expand the rectangle so that covers both the original rectangle and the given rectangle ...
double yMinimum() const
Get the y minimum value (bottom side of rectangle)
Definition: qgsrectangle.h:197
A circle is used to highlight points (○)
Definition: qgsrubberband.h:58
void removePoint(int index=0, bool doUpdate=true, int geometryIndex=0)
Remove a vertex from the rubberband and (optionally) update canvas.
void setRect(const QgsRectangle &r, bool resetRotation=true)
sets canvas item rectangle in map units
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.
void addGeometry(const QgsGeometry *geom, QgsVectorLayer *layer)
Add the geometry of an existing feature to a rubberband This is useful for multi feature highlighting...
double mapUnitsPerPixel() const
Return current map units per pixel.
void setFillColor(const QColor &color)
Set the fill color for the rubberband.
A cross is used to highlight points (x)
Definition: qgsrubberband.h:50
A class to represent a point.
Definition: qgspoint.h:63
A full box is used to highlight points (■)
Definition: qgsrubberband.h:62
void setBrushStyle(Qt::BrushStyle brushStyle)
Set the style of the brush.
QgsPoint toMapCoordinates(int x, int y) const
void setIcon(IconType icon)
Set the icon type to highlight point geometries.
int size() const
Returns number of geometries.
void setToCanvasRectangle(const QRect &rect)
Sets this rubber band to a map canvas rectangle.
QgsRubberBand(QgsMapCanvas *mapCanvas, QGis::GeometryType geometryType=QGis::Line)
Creates a new RubberBand.
QgsMapCanvas * mMapCanvas
pointer to map canvas
int partSize(int geometryIndex) const
Returns number of vertices in feature part.
A cross is used to highlight points (+)
Definition: qgsrubberband.h:46
const QgsMapToPixel * getCoordinateTransform()
Get the current coordinate transform.
double y() const
Definition: qgspoint.h:134
No icon is used.
Definition: qgsrubberband.h:42
void setColor(const QColor &color)
Set the color for the rubberband.
double width() const
Width of the rectangle.
Definition: qgsrectangle.h:202
Represents a vector layer which manages a vector based data sets.
void removeLastPoint(int geometryIndex=0, bool doUpdate=true)
Removes the last point.
double xMinimum() const
Get the x minimum value (left side of rectangle)
Definition: qgsrectangle.h:187
QgsPoint toMapPoint(qreal x, qreal y) const
void updateRect()
recalculates needed rectangle
void setToGeometry(const QgsGeometry *geom, QgsVectorLayer *layer)
Sets this rubber band to the geometry of an existing feature.
double height() const
Height of the rectangle.
Definition: qgsrectangle.h:207