QGIS API Documentation  2.8.2-Wien
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
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 
263 {
264  if ( !geom )
265  {
266  reset( mGeometryType );
267  return;
268  }
269 
270  reset( geom->type() );
271  addGeometry( geom, layer );
272 }
273 
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 }