QGIS API Documentation  2.3.0-Master
 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 <QImage>
17 
18 #include "qgsmarkersymbollayerv2.h"
19 #include "qgslinesymbollayerv2.h"
20 
21 #include "qgscoordinatetransform.h"
22 #include "qgsfillsymbollayerv2.h"
23 #include "qgsgeometry.h"
24 #include "qgshighlight.h"
25 #include "qgslinesymbollayerv2.h"
26 #include "qgslinesymbollayerv2.h"
27 #include "qgsmapcanvas.h"
28 #include "qgsmaplayer.h"
29 #include "qgsmaprenderer.h"
30 #include "qgsmarkersymbollayerv2.h"
31 #include "qgsrendercontext.h"
32 #include "qgssymbollayerv2.h"
33 #include "qgssymbolv2.h"
34 #include "qgsvectorlayer.h"
35 
36 /* Few notes about highligting (RB):
37  - The highlight fill must always be partially transparent because above highlighted layer
38  may be another layer which must remain partially visible.
39  - Because single highlight color does not work well with layers using similar layer color
40  there were considered various possibilities but no optimal solution was found.
41  What does not work:
42  - lighter/darker color: it would work more or less for fully opaque highlight, but
43  overlaying transparent lighter color over original has small visual efect.
44  - complemetary color: mixing transparent (128) complement color with original color
45  results in grey for all colors
46  - contrast line style/ fill pattern: impression is not highligh but just different style
47  - line buffer with contrast (or 2 contrast) color: the same as with patterns, no highlight impression
48  - fill with highlight or contrast color but opaque and using pattern
49  (e.g. Qt::Dense7Pattern): again no highlight impression
50 */
57  : QgsMapCanvasItem( mapCanvas )
58  , mLayer( layer )
59  , mBuffer( 0 )
60  , mMinWidth( 0 )
61 {
62  mGeometry = geom ? new QgsGeometry( *geom ) : 0;
63  init();
64 }
65 
67  : QgsMapCanvasItem( mapCanvas )
68  , mLayer( static_cast<QgsMapLayer *>( layer ) )
69  , mBuffer( 0 )
70  , mMinWidth( 0 )
71 {
72  mGeometry = geom ? new QgsGeometry( *geom ) : 0;
73  init();
74 }
75 
77  : QgsMapCanvasItem( mapCanvas )
78  , mGeometry( 0 )
79  , mLayer( static_cast<QgsMapLayer *>( layer ) )
80  , mFeature( feature )
81  , mBuffer( 0 )
82  , mMinWidth( 0 )
83 {
84  init();
85 }
86 
88 {
90  {
91  // TODO[MD]: after merge - should not use mapRenderer()
93  if ( ct )
94  {
95  if ( mGeometry )
96  {
97  mGeometry->transform( *ct );
98  }
99  else if ( mFeature.geometry() )
100  {
101  mFeature.geometry()->transform( *ct );
102  }
103  }
104  }
105  updateRect();
106  update();
107  setColor( QColor( Qt::lightGray ) );
108 }
109 
111 {
112  delete mGeometry;
113 }
114 
118 void QgsHighlight::setColor( const QColor & color )
119 {
120  mPen.setColor( color );
121  QColor fillColor( color.red(), color.green(), color.blue(), 63 );
122  mBrush.setColor( fillColor );
123  mBrush.setStyle( Qt::SolidPattern );
124 }
125 
126 void QgsHighlight::setFillColor( const QColor & fillColor )
127 {
128  mBrush.setColor( fillColor );
129  mBrush.setStyle( Qt::SolidPattern );
130 }
131 
132 QgsFeatureRendererV2 * QgsHighlight::getRenderer( const QgsRenderContext & context, const QColor & color, const QColor & fillColor )
133 {
134  QgsFeatureRendererV2 *renderer = 0;
135  QgsVectorLayer *layer = qobject_cast<QgsVectorLayer*>( mLayer );
136  if ( layer && layer->rendererV2() )
137  {
138  renderer = layer->rendererV2()->clone();
139  }
140  if ( renderer )
141  {
142  foreach ( QgsSymbolV2* symbol, renderer->symbols() )
143  {
144  if ( !symbol ) continue;
145  setSymbol( symbol, context, color, fillColor );
146  }
147  }
148  return renderer;
149 }
150 
151 void QgsHighlight::setSymbol( QgsSymbolV2* symbol, const QgsRenderContext & context, const QColor & color, const QColor & fillColor )
152 {
153  if ( !symbol ) return;
154 
155 
156  for ( int i = symbol->symbolLayerCount() - 1; i >= 0; i-- )
157  {
158  QgsSymbolLayerV2* symbolLayer = symbol->symbolLayer( i );
159  if ( !symbolLayer ) continue;
160 
161  if ( symbolLayer->subSymbol() )
162  {
163  setSymbol( symbolLayer->subSymbol(), context, color, fillColor );
164  }
165  else
166  {
167  symbolLayer->setColor( color ); // line symbology layers
168  symbolLayer->setOutlineColor( color ); // marker and fill symbology layers
169  symbolLayer->setFillColor( fillColor ); // marker and fill symbology layers
170 
171  // Data defined widths overwrite what we set here (widths do not work with data defined)
172  QgsSimpleMarkerSymbolLayerV2 * simpleMarker = dynamic_cast<QgsSimpleMarkerSymbolLayerV2*>( symbolLayer );
173  if ( simpleMarker )
174  {
175  simpleMarker->setOutlineWidth( getSymbolWidth( context, simpleMarker->outlineWidth(), simpleMarker->outlineWidthUnit() ) );
176  }
177  QgsSimpleLineSymbolLayerV2 * simpleLine = dynamic_cast<QgsSimpleLineSymbolLayerV2*>( symbolLayer );
178  if ( simpleLine )
179  {
180  simpleLine->setWidth( getSymbolWidth( context, simpleLine->width(), simpleLine->widthUnit() ) );
181  }
182  QgsSimpleFillSymbolLayerV2 * simpleFill = dynamic_cast<QgsSimpleFillSymbolLayerV2*>( symbolLayer );
183  if ( simpleFill )
184  {
185  simpleFill->setBorderWidth( getSymbolWidth( context, simpleFill->borderWidth(), simpleFill->outputUnit() ) );
186  }
187  symbolLayer->removeDataDefinedProperty( "color" );
188  symbolLayer->removeDataDefinedProperty( "color_border" );
189  }
190  }
191 }
192 
193 double QgsHighlight::getSymbolWidth( const QgsRenderContext & context, double width, QgsSymbolV2::OutputUnit unit )
194 {
195  // if necessary scale mm to map units
196  double scale = 1.;
197  if ( unit == QgsSymbolV2::MapUnit )
198  {
200  }
201  width = qMax( width + 2 * mBuffer * scale, mMinWidth * scale );
202  return width;
203 }
204 
208 void QgsHighlight::setWidth( int width )
209 {
210  mPen.setWidth( width );
211 }
212 
213 void QgsHighlight::paintPoint( QPainter *p, QgsPoint point )
214 {
215  QPolygonF r( 5 );
216 
217  double d = mMapCanvas->extent().width() * 0.005;
218  r[0] = toCanvasCoordinates( point + QgsVector( -d, -d ) ) - pos();
219  r[1] = toCanvasCoordinates( point + QgsVector( d, -d ) ) - pos();
220  r[2] = toCanvasCoordinates( point + QgsVector( d, d ) ) - pos();
221  r[3] = toCanvasCoordinates( point + QgsVector( -d, d ) ) - pos();
222  r[4] = r[0];
223 
224  p->drawPolygon( r );
225 }
226 
227 void QgsHighlight::paintLine( QPainter *p, QgsPolyline line )
228 {
229  QPolygonF polygon( line.size() );
230 
231  for ( int i = 0; i < line.size(); i++ )
232  {
233  polygon[i] = toCanvasCoordinates( line[i] ) - pos();
234  }
235 
236  p->drawPolyline( polygon );
237 }
238 
239 void QgsHighlight::paintPolygon( QPainter *p, QgsPolygon polygon )
240 {
241  // OddEven fill rule by default
242  QPainterPath path;
243 
244  p->setPen( mPen );
245  p->setBrush( mBrush );
246 
247  for ( int i = 0; i < polygon.size(); i++ )
248  {
249  if ( polygon[i].empty() ) continue;
250 
251  QPolygonF ring;
252  ring.reserve( polygon[i].size() + 1 );
253 
254  for ( int j = 0; j < polygon[i].size(); j++ )
255  {
256  //adding point only if it is more than a pixel appart from the previous one
257  const QPointF cur = toCanvasCoordinates( polygon[i][j] ) - pos();
258  if ( 0 == j || std::abs( ring.back().x() - cur.x() ) > 1 || std::abs( ring.back().y() - cur.y() ) > 1 )
259  {
260  ring.push_back( cur );
261  }
262  }
263 
264  ring.push_back( ring[ 0 ] );
265 
266  path.addPolygon( ring );
267  }
268 
269  p->drawPath( path );
270 }
271 
275 void QgsHighlight::paint( QPainter* p )
276 {
277  if ( mGeometry )
278  {
279  p->setPen( mPen );
280  p->setBrush( mBrush );
281 
282  switch ( mGeometry->wkbType() )
283  {
284  case QGis::WKBPoint:
285  case QGis::WKBPoint25D:
286  {
287  paintPoint( p, mGeometry->asPoint() );
288  }
289  break;
290 
291  case QGis::WKBMultiPoint:
293  {
295  for ( int i = 0; i < m.size(); i++ )
296  {
297  paintPoint( p, m[i] );
298  }
299  }
300  break;
301 
302  case QGis::WKBLineString:
304  {
305  paintLine( p, mGeometry->asPolyline() );
306  }
307  break;
308 
311  {
313 
314  for ( int i = 0; i < m.size(); i++ )
315  {
316  paintLine( p, m[i] );
317  }
318  }
319  break;
320 
321  case QGis::WKBPolygon:
322  case QGis::WKBPolygon25D:
323  {
325  }
326  break;
327 
330  {
332  for ( int i = 0; i < m.size(); i++ )
333  {
334  paintPolygon( p, m[i] );
335  }
336  }
337  break;
338 
339  case QGis::WKBUnknown:
340  default:
341  return;
342  }
343  }
344  else if ( mFeature.geometry() )
345  {
346  QgsVectorLayer *layer = qobject_cast<QgsVectorLayer*>( mLayer );
347  if( !layer )
348  return;
349  QgsMapSettings mapSettings = mMapCanvas->mapSettings();
350  QgsRenderContext context = QgsRenderContext::fromMapSettings( mapSettings );
351 
352  // Because lower level outlines must be covered by upper level fill color
353  // we render first with temporary opaque color, which is then replaced
354  // by final transparent fill color.
355  QColor tmpColor( 255, 0, 0, 255 );
356  QColor tmpFillColor( 0, 255, 0, 255 );
357 
358  QgsFeatureRendererV2 *renderer = getRenderer( context, tmpColor, tmpFillColor );
359  if ( layer && renderer )
360  {
361  QgsRectangle extent = mMapCanvas->extent();
362  if ( extent != rect() ) // catches also canvas resize as it is causing extent change
363  {
364  updateRect();
365  return; // it will be repainted after updateRect()
366  }
367 
368  QSize imageSize( mMapCanvas->mapSettings().outputSize() );
369  QImage image = QImage( imageSize.width(), imageSize.height(), QImage::Format_ARGB32 );
370  image.fill( 0 );
371  QPainter *imagePainter = new QPainter( &image );
372  imagePainter->setRenderHint( QPainter::Antialiasing, true );
373 
374  context.setPainter( imagePainter );
375 
376  renderer->startRender( context, layer->pendingFields() );
377  renderer->renderFeature( mFeature, context );
378  renderer->stopRender( context );
379 
380  imagePainter->end();
381 
382  QColor color( mPen.color() ); // true output color
383  // coefficient to subtract alpha using green (temporary fill)
384  double k = ( 255. - mBrush.color().alpha() ) / 255.;
385  for ( int r = 0; r < image.height(); r++ )
386  {
387  for ( int c = 0; c < image.width(); c++ )
388  {
389  QRgb rgba = image.pixel( c, r );
390  int alpha = qAlpha( rgba );
391  if ( alpha > 0 )
392  {
393  int green = qGreen( rgba );
394  color.setAlpha( qBound<int>( 0, alpha - ( green * k ), 255 ) );
395 
396  image.setPixel( c, r, color.rgba() );
397  }
398  }
399  }
400 
401  p->drawImage( 0, 0, image );
402 
403  delete imagePainter;
404  delete renderer;
405  }
406  }
407 }
408 
410 {
411  if ( mGeometry )
412  {
414 
415  if ( r.isEmpty() )
416  {
417  double d = mMapCanvas->extent().width() * 0.005;
418  r.setXMinimum( r.xMinimum() - d );
419  r.setYMinimum( r.yMinimum() - d );
420  r.setXMaximum( r.xMaximum() + d );
421  r.setYMaximum( r.yMaximum() + d );
422  }
423 
424  setRect( r );
425  setVisible( mGeometry );
426  }
427  else if ( mFeature.geometry() )
428  {
429  // We are currently using full map canvas extent for two reasons:
430  // 1) currently there is no method in QgsFeatureRendererV2 to get rendered feature
431  // bounding box
432  // 2) using different extent would result in shifted fill patterns
433  setRect( mMapCanvas->extent() );
434  setVisible( true );
435  }
436  else
437  {
438  setRect( QgsRectangle() );
439  }
440 }
QgsFeatureRendererV2 * rendererV2()
Return renderer V2.
A rectangle specified with double values.
Definition: qgsrectangle.h:35
Base class for all map layer types.
Definition: qgsmaplayer.h:47
bool isEmpty() const
test if rectangle is empty
QgsFeatureRendererV2 * getRenderer(const QgsRenderContext &context, const QColor &color, const QColor &fillColor)
Get renderer for current color mode and colors.
virtual double width() const
virtual void setOutlineColor(const QColor &color)
Set outline color.
void setXMaximum(double x)
Set the maximum x value.
Definition: qgsrectangle.h:164
double yMaximum() const
Get the y maximum value (top side of rectangle)
Definition: qgsrectangle.h:189
QgsMultiPolyline asMultiPolyline() const
return contents of the geometry as a multi linestring if wkbType is WKBMultiLineString, otherwise an empty list
QVector< QgsPoint > QgsPolyline
polyline is represented as a vector of points
Definition: qgsgeometry.h:38
QgsGeometry * geometry() const
Get the geometry object associated with this feature.
Definition: qgsfeature.cpp:112
QgsPolygon asPolygon() const
return contents of the geometry as a polygon if wkbType is WKBPolygon, otherwise an empty list ...
void setFillColor(const QColor &fillColor)
Set polygons fill color.
An abstract class for items that can be placed on the map canvas.
QgsMapLayer * mLayer
Definition: qgshighlight.h:90
void paintPolygon(QPainter *p, QgsPolygon polygon)
const QgsMapSettings & mapSettings() const
Get access to properties used for map rendering.
bool hasCrsTransformEnabled() const
returns true if projections are enabled for this layer set
QgsSymbolV2::OutputUnit outputUnit() const
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:114
QgsRectangle rect() const
returns canvas item rectangle
virtual void removeDataDefinedProperty(const QString &property)
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:105
const QgsCoordinateTransform * transformation(const QgsMapLayer *layer) const
virtual void startRender(QgsRenderContext &context, const QgsFields &fields)=0
QgsMultiPolygon asMultiPolygon() const
return contents of the geometry as a multi polygon if wkbType is WKBMultiPolygon, otherwise an empty ...
virtual void stopRender(QgsRenderContext &context)=0
virtual QgsSymbolV2List symbols()=0
for symbol levels
virtual bool renderFeature(QgsFeature &feature, QgsRenderContext &context, int layer=-1, bool selected=false, bool drawVertexMarker=false)
QgsHighlight(QgsMapCanvas *mapCanvas, QgsGeometry *geom, QgsMapLayer *layer)
virtual QgsFeatureRendererV2 * clone()=0
QgsFeature mFeature
Definition: qgshighlight.h:91
double yMinimum() const
Get the y minimum value (bottom side of rectangle)
Definition: qgsrectangle.h:194
QSize outputSize() const
double xMaximum() const
Get the x maximum value (right side of rectangle)
Definition: qgsrectangle.h:179
QgsGeometry * mGeometry
Definition: qgshighlight.h:89
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:169
virtual void setWidth(double width)
void updateRect()
recalculates needed rectangle
void paintPoint(QPainter *p, QgsPoint point)
QVector< QgsPolygon > QgsMultiPolygon
a collection of QgsPolygons that share a common collection of attributes
Definition: qgsgeometry.h:53
QVector< QgsPoint > QgsMultiPoint
a collection of QgsPoints that share a common collection of attributes
Definition: qgsgeometry.h:47
int symbolLayerCount()
Definition: qgssymbolv2.h:84
void setPainter(QPainter *p)
void setSymbol(QgsSymbolV2 *symbol, const QgsRenderContext &context, const QColor &color, const QColor &fillColor)
QGis::WkbType wkbType() const
Returns type of wkb (point / linestring / polygon etc.)
virtual void paint(QPainter *p)
QVector< QgsPolyline > QgsPolygon
polygon: first item of the list is outer ring, inner rings (if any) start from second item ...
Definition: qgsgeometry.h:44
const QgsMapLayer * layer() const
Definition: qgshighlight.h:69
A class to represent a point geometry.
Definition: qgspoint.h:63
virtual void setFillColor(const QColor &color)
Set fill color.
QPointF toCanvasCoordinates(const QgsPoint &point)
transformation from map coordinates to screen coordinates
A class to represent a vector.
Definition: qgspoint.h:32
QVector< QgsPolyline > QgsMultiPolyline
a collection of QgsPolylines that share a common collection of attributes
Definition: qgsgeometry.h:50
double mBuffer
Definition: qgshighlight.h:92
virtual QgsSymbolV2 * subSymbol()
double getSymbolWidth(const QgsRenderContext &context, double width, QgsSymbolV2::OutputUnit unit)
QgsPolyline asPolyline() const
return contents of the geometry as a polyline if wkbType is WKBLineString, otherwise an empty list ...
QgsSymbolV2::OutputUnit outlineWidthUnit() const
QgsRectangle boundingBox()
Returns the bounding box of this feature.
Contains information about the context of a rendering operation.
void paintLine(QPainter *p, QgsPolyline line)
void setColor(const QColor &color)
Set line/outline to color, polygon fill to color with alpha = 63.
void setYMaximum(double y)
Set the maximum y value.
Definition: qgsrectangle.h:174
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
QgsMultiPoint asMultiPoint() const
return contents of the geometry as a multi point if wkbType is WKBMultiPoint, otherwise an empty list...
QgsMapCanvas * mMapCanvas
pointer to map canvas
Q_DECL_DEPRECATED QgsMapRenderer * mapRenderer()
double mMinWidth
Definition: qgshighlight.h:93
Class for doing transforms between two map coordinate systems.
void setRect(const QgsRectangle &r)
sets canvas item rectangle
static double lineWidthScaleFactor(const QgsRenderContext &c, QgsSymbolV2::OutputUnit u)
Returns the line width scale factor depending on the unit and the paint device.
int transform(const QgsCoordinateTransform &ct)
Transform this geometry as described by CoordinateTranasform ct.
virtual void setColor(const QColor &color)
QgsRectangle extent() const
Returns the current zoom exent of the map canvas.
const QgsFields & pendingFields() const
returns field list in the to-be-committed state
QgsSymbolLayerV2 * symbolLayer(int layer)
QBrush mBrush
Definition: qgshighlight.h:87
double width() const
Width of the rectangle.
Definition: qgsrectangle.h:199
QgsPoint asPoint() const
return contents of the geometry as a point if wkbType is WKBPoint, otherwise returns [0...
Represents a vector layer which manages a vector based data sets.
double size
Definition: qgssvgcache.cpp:77
void setBorderWidth(double borderWidth)
double xMinimum() const
Get the x minimum value (left side of rectangle)
Definition: qgsrectangle.h:184
QgsSymbolV2::OutputUnit widthUnit() const
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:159
void setWidth(int width)
Set width.