QGIS API Documentation  2.2.0-Valmiera
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgshighlight.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgshighlight.cpp - widget to highlight features on the map
3  --------------------------------------
4  Date : 02-03-2011
5  Copyright : (C) 2011 by Juergen E. Fischer, norBIT GmbH
6  Email : jef at norbit dot de
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 <typeinfo>
17 
18 #include <QImage>
19 
20 #include "qgsmarkersymbollayerv2.h"
21 #include "qgslinesymbollayerv2.h"
22 
23 #include "qgscoordinatetransform.h"
24 #include "qgsgeometry.h"
25 #include "qgshighlight.h"
26 #include "qgsmapcanvas.h"
27 #include "qgsmaplayer.h"
28 #include "qgsmaprenderer.h"
29 #include "qgsrendercontext.h"
30 #include "qgssymbollayerv2.h"
31 #include "qgssymbolv2.h"
32 #include "qgsvectorlayer.h"
33 
40  : QgsMapCanvasItem( mapCanvas )
41  , mLayer( layer )
42  , mRenderer( 0 )
43 {
44  mGeometry = geom ? new QgsGeometry( *geom ) : 0;
45  init();
46 }
47 
49  : QgsMapCanvasItem( mapCanvas )
50  , mLayer( static_cast<QgsMapLayer *>( layer ) )
51  , mRenderer( 0 )
52 {
53  mGeometry = geom ? new QgsGeometry( *geom ) : 0;
54  init();
55 }
56 
58  : QgsMapCanvasItem( mapCanvas )
59  , mGeometry( 0 )
60  , mLayer( static_cast<QgsMapLayer *>( layer ) )
61  , mFeature( feature )
62  , mRenderer( 0 )
63 {
64  init();
65 }
66 
68 {
70  {
72  if ( ct )
73  {
74  if ( mGeometry )
75  {
76  mGeometry->transform( *ct );
77  }
78  else if ( mFeature.geometry() )
79  {
80  mFeature.geometry()->transform( *ct );
81  }
82  }
83  }
84  updateRect();
85  update();
86  setColor( QColor( Qt::lightGray ) );
87 }
88 
90 {
91  delete mGeometry;
92  delete mRenderer;
93 }
94 
98 void QgsHighlight::setColor( const QColor & color )
99 {
100  mPen.setColor( color );
101  QColor fillColor( color.red(), color.green(), color.blue(), 63 );
102  mBrush.setColor( fillColor );
103  mBrush.setStyle( Qt::SolidPattern );
104 
105  delete mRenderer;
106  mRenderer = 0;
107  QgsVectorLayer *layer = vectorLayer();
108  if ( layer && layer->rendererV2() )
109  {
110  mRenderer = layer->rendererV2()->clone();
111  }
112  if ( mRenderer )
113  {
114  foreach ( QgsSymbolV2* symbol, mRenderer->symbols() )
115  {
116  if ( !symbol ) continue;
117  setSymbolColor( symbol, color );
118  }
119  }
120 }
121 
122 void QgsHighlight::setSymbolColor( QgsSymbolV2* symbol, const QColor & color )
123 {
124  if ( !symbol ) return;
125 
126  // Temporary fill color must be similar to outline color (antialiasing between
127  // outline and temporary fill) but also different enough to be distinguishable
128  // so that it can be replaced by transparent fill.
129  // Unfortunately the result of the transparent fill rendered over the original
130  // (not highlighted) feature color may be either lighter or darker.
131  if ( color.lightness() < 200 )
132  {
133  mTemporaryFillColor = color.lighter( 120 );
134  }
135  else
136  {
137  mTemporaryFillColor = color.darker( 120 );
138  }
139  mTemporaryFillColor.setAlpha( 255 );
140 
141  for ( int i = symbol->symbolLayerCount() - 1; i >= 0; i-- )
142  {
143  QgsSymbolLayerV2* symbolLayer = symbol->symbolLayer( i );
144  if ( !symbolLayer ) continue;
145 
146  if ( symbolLayer->subSymbol() )
147  {
148  setSymbolColor( symbolLayer->subSymbol(), color );
149  }
150  else
151  {
152  symbolLayer->setColor( color ); // line symbology layers
153  symbolLayer->setOutlineColor( color ); // marker and fill symbology layers
154  symbolLayer->setFillColor( mTemporaryFillColor ); // marker and fill symbology layers
155 
156  symbolLayer->removeDataDefinedProperty( "color" );
157  symbolLayer->removeDataDefinedProperty( "color_border" );
158  }
159  }
160 }
161 
165 void QgsHighlight::setWidth( int width )
166 {
167  mPen.setWidth( width );
168 }
169 
170 void QgsHighlight::paintPoint( QPainter *p, QgsPoint point )
171 {
172  QPolygonF r( 5 );
173 
174  double d = mMapCanvas->extent().width() * 0.005;
175  r[0] = toCanvasCoordinates( point + QgsVector( -d, -d ) ) - pos();
176  r[1] = toCanvasCoordinates( point + QgsVector( d, -d ) ) - pos();
177  r[2] = toCanvasCoordinates( point + QgsVector( d, d ) ) - pos();
178  r[3] = toCanvasCoordinates( point + QgsVector( -d, d ) ) - pos();
179  r[4] = r[0];
180 
181  p->drawPolygon( r );
182 }
183 
184 void QgsHighlight::paintLine( QPainter *p, QgsPolyline line )
185 {
186  QPolygonF polygon( line.size() );
187 
188  for ( int i = 0; i < line.size(); i++ )
189  {
190  polygon[i] = toCanvasCoordinates( line[i] ) - pos();
191  }
192 
193  p->drawPolyline( polygon );
194 }
195 
196 void QgsHighlight::paintPolygon( QPainter *p, QgsPolygon polygon )
197 {
198  // OddEven fill rule by default
199  QPainterPath path;
200 
201  p->setPen( mPen );
202  p->setBrush( mBrush );
203 
204  for ( int i = 0; i < polygon.size(); i++ )
205  {
206  if ( polygon[i].empty() ) continue;
207 
208  QPolygonF ring;
209  ring.reserve( polygon[i].size() + 1 );
210 
211  for ( int j = 0; j < polygon[i].size(); j++ )
212  {
213  //adding point only if it is more than a pixel appart from the previous one
214  const QPointF cur = toCanvasCoordinates( polygon[i][j] ) - pos();
215  if ( 0 == j || std::abs( ring.back().x() - cur.x() ) > 1 || std::abs( ring.back().y() - cur.y() ) > 1 )
216  {
217  ring.push_back( cur );
218  }
219  }
220 
221  ring.push_back( ring[ 0 ] );
222 
223  path.addPolygon( ring );
224  }
225 
226  p->drawPath( path );
227 }
228 
232 void QgsHighlight::paint( QPainter* p )
233 {
234  if ( mGeometry )
235  {
236  p->setPen( mPen );
237  p->setBrush( mBrush );
238 
239  switch ( mGeometry->wkbType() )
240  {
241  case QGis::WKBPoint:
242  case QGis::WKBPoint25D:
243  {
244  paintPoint( p, mGeometry->asPoint() );
245  }
246  break;
247 
248  case QGis::WKBMultiPoint:
250  {
252  for ( int i = 0; i < m.size(); i++ )
253  {
254  paintPoint( p, m[i] );
255  }
256  }
257  break;
258 
259  case QGis::WKBLineString:
261  {
262  paintLine( p, mGeometry->asPolyline() );
263  }
264  break;
265 
268  {
270 
271  for ( int i = 0; i < m.size(); i++ )
272  {
273  paintLine( p, m[i] );
274  }
275  }
276  break;
277 
278  case QGis::WKBPolygon:
279  case QGis::WKBPolygon25D:
280  {
282  }
283  break;
284 
287  {
289  for ( int i = 0; i < m.size(); i++ )
290  {
291  paintPolygon( p, m[i] );
292  }
293  }
294  break;
295 
296  case QGis::WKBUnknown:
297  default:
298  return;
299  }
300  }
301  else if ( mFeature.geometry() && mRenderer )
302  {
303  QgsVectorLayer *layer = vectorLayer();
304  if ( layer )
305  {
307 
308  // The context is local rectangle of QgsHighlight we previously set.
309  // Because QgsMapCanvasItem::setRect() adds 1 pixel on border we cannot simply
310  // use boundingRect().height() for QgsMapToPixel height.
311  QgsRectangle extent = mMapCanvas->extent();
312  if ( extent != rect() ) // catches also canvas resize as it is causing extent change
313  {
314  updateRect();
315  return; // it will be repainted after updateRect()
316  }
317 
318 
319  QPointF ll = toCanvasCoordinates( QgsPoint( extent.xMinimum(), extent.yMinimum() ) );
320  QPointF ur = toCanvasCoordinates( QgsPoint( extent.xMaximum(), extent.yMaximum() ) );
321  double height = ll.y() - ur.y();
322  double width = ur.x() - ll.x();
323 
324  // Because lower level outlines must be covered by upper level fill color
325  // we render first with temporary opaque color, which is then replaced
326  // by final transparent fill color.
327  QImage image = QImage(( int )width, ( int )height, QImage::Format_ARGB32 );
328  image.fill( 0 );
329  QPainter *imagePainter = new QPainter( &image );
330  imagePainter->setRenderHint( QPainter::Antialiasing, true );
331 
333  height, extent.yMinimum(), extent.xMinimum() );
334  context.setMapToPixel( mapToPixel );
335  context.setExtent( extent );
336  context.setCoordinateTransform( 0 ); // we reprojected geometry in init()
337  context.setPainter( imagePainter );
338 
339  mRenderer->startRender( context, layer );
340  mRenderer->renderFeature( mFeature, context );
341  mRenderer->stopRender( context );
342 
343  imagePainter->end();
344 
345  QRgb temporaryRgb = mTemporaryFillColor.rgba();
346  QColor color = QColor( mBrush.color() );
347  color.setAlpha( 63 );
348  QRgb colorRgb = color.rgba();
349 
350  for ( int r = 0; r < image.height(); r++ )
351  {
352  QRgb *line = ( QRgb * ) image.scanLine( r );
353  for ( int c = 0; c < image.width(); c++ )
354  {
355  if ( line[c] == temporaryRgb )
356  {
357  line[c] = colorRgb;
358  }
359  }
360  }
361 
362  p->drawImage( 0, 0, image );
363 
364  delete imagePainter;
365  }
366  }
367 }
368 
370 {
371  if ( mGeometry )
372  {
374 
375  if ( r.isEmpty() )
376  {
377  double d = mMapCanvas->extent().width() * 0.005;
378  r.setXMinimum( r.xMinimum() - d );
379  r.setYMinimum( r.yMinimum() - d );
380  r.setXMaximum( r.xMaximum() + d );
381  r.setYMaximum( r.yMaximum() + d );
382  }
383 
384  setRect( r );
385  setVisible( mGeometry );
386  }
387  else if ( mFeature.geometry() )
388  {
389  // We are currently using full map canvas extent for two reasons:
390  // 1) currently there is no method in QgsFeatureRendererV2 to get rendered feature
391  // bounding box
392  // 2) using different extent would result in shifted fill patterns
393  setRect( mMapCanvas->extent() );
394  setVisible( true );
395  }
396  else
397  {
398  setRect( QgsRectangle() );
399  }
400 }
401 
403 {
404  return dynamic_cast<QgsVectorLayer *>( mLayer );
405 }