QGIS API Documentation  2.4.0-Chugiak
 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  {
92  if ( ct )
93  {
94  if ( mGeometry )
95  {
96  mGeometry->transform( *ct );
97  }
98  else if ( mFeature.geometry() )
99  {
100  mFeature.geometry()->transform( *ct );
101  }
102  }
103  }
104  updateRect();
105  update();
106  setColor( QColor( Qt::lightGray ) );
107 }
108 
110 {
111  delete mGeometry;
112 }
113 
117 void QgsHighlight::setColor( const QColor & color )
118 {
119  mPen.setColor( color );
120  QColor fillColor( color.red(), color.green(), color.blue(), 63 );
121  mBrush.setColor( fillColor );
122  mBrush.setStyle( Qt::SolidPattern );
123 }
124 
125 void QgsHighlight::setFillColor( const QColor & fillColor )
126 {
127  mBrush.setColor( fillColor );
128  mBrush.setStyle( Qt::SolidPattern );
129 }
130 
131 QgsFeatureRendererV2 * QgsHighlight::getRenderer( const QgsRenderContext & context, const QColor & color, const QColor & fillColor )
132 {
133  QgsFeatureRendererV2 *renderer = 0;
134  QgsVectorLayer *layer = qobject_cast<QgsVectorLayer*>( mLayer );
135  if ( layer && layer->rendererV2() )
136  {
137  renderer = layer->rendererV2()->clone();
138  }
139  if ( renderer )
140  {
141  foreach ( QgsSymbolV2* symbol, renderer->symbols() )
142  {
143  if ( !symbol ) continue;
144  setSymbol( symbol, context, color, fillColor );
145  }
146  }
147  return renderer;
148 }
149 
150 void QgsHighlight::setSymbol( QgsSymbolV2* symbol, const QgsRenderContext & context, const QColor & color, const QColor & fillColor )
151 {
152  if ( !symbol ) return;
153 
154 
155  for ( int i = symbol->symbolLayerCount() - 1; i >= 0; i-- )
156  {
157  QgsSymbolLayerV2* symbolLayer = symbol->symbolLayer( i );
158  if ( !symbolLayer ) continue;
159 
160  if ( symbolLayer->subSymbol() )
161  {
162  setSymbol( symbolLayer->subSymbol(), context, color, fillColor );
163  }
164  else
165  {
166  symbolLayer->setColor( color ); // line symbology layers
167  symbolLayer->setOutlineColor( color ); // marker and fill symbology layers
168  symbolLayer->setFillColor( fillColor ); // marker and fill symbology layers
169 
170  // Data defined widths overwrite what we set here (widths do not work with data defined)
171  QgsSimpleMarkerSymbolLayerV2 * simpleMarker = dynamic_cast<QgsSimpleMarkerSymbolLayerV2*>( symbolLayer );
172  if ( simpleMarker )
173  {
174  simpleMarker->setOutlineWidth( getSymbolWidth( context, simpleMarker->outlineWidth(), simpleMarker->outlineWidthUnit() ) );
175  }
176  QgsSimpleLineSymbolLayerV2 * simpleLine = dynamic_cast<QgsSimpleLineSymbolLayerV2*>( symbolLayer );
177  if ( simpleLine )
178  {
179  simpleLine->setWidth( getSymbolWidth( context, simpleLine->width(), simpleLine->widthUnit() ) );
180  }
181  QgsSimpleFillSymbolLayerV2 * simpleFill = dynamic_cast<QgsSimpleFillSymbolLayerV2*>( symbolLayer );
182  if ( simpleFill )
183  {
184  simpleFill->setBorderWidth( getSymbolWidth( context, simpleFill->borderWidth(), simpleFill->outputUnit() ) );
185  }
186  symbolLayer->removeDataDefinedProperty( "color" );
187  symbolLayer->removeDataDefinedProperty( "color_border" );
188  }
189  }
190 }
191 
192 double QgsHighlight::getSymbolWidth( const QgsRenderContext & context, double width, QgsSymbolV2::OutputUnit unit )
193 {
194  // if necessary scale mm to map units
195  double scale = 1.;
196  if ( unit == QgsSymbolV2::MapUnit )
197  {
199  }
200  width = qMax( width + 2 * mBuffer * scale, mMinWidth * scale );
201  return width;
202 }
203 
207 void QgsHighlight::setWidth( int width )
208 {
209  mPen.setWidth( width );
210 }
211 
212 void QgsHighlight::paintPoint( QPainter *p, QgsPoint point )
213 {
214  QPolygonF r( 5 );
215 
216  double d = mMapCanvas->extent().width() * 0.005;
217  r[0] = toCanvasCoordinates( point + QgsVector( -d, -d ) ) - pos();
218  r[1] = toCanvasCoordinates( point + QgsVector( d, -d ) ) - pos();
219  r[2] = toCanvasCoordinates( point + QgsVector( d, d ) ) - pos();
220  r[3] = toCanvasCoordinates( point + QgsVector( -d, d ) ) - pos();
221  r[4] = r[0];
222 
223  p->drawPolygon( r );
224 }
225 
226 void QgsHighlight::paintLine( QPainter *p, QgsPolyline line )
227 {
228  QPolygonF polygon( line.size() );
229 
230  for ( int i = 0; i < line.size(); i++ )
231  {
232  polygon[i] = toCanvasCoordinates( line[i] ) - pos();
233  }
234 
235  p->drawPolyline( polygon );
236 }
237 
238 void QgsHighlight::paintPolygon( QPainter *p, QgsPolygon polygon )
239 {
240  // OddEven fill rule by default
241  QPainterPath path;
242 
243  p->setPen( mPen );
244  p->setBrush( mBrush );
245 
246  for ( int i = 0; i < polygon.size(); i++ )
247  {
248  if ( polygon[i].empty() ) continue;
249 
250  QPolygonF ring;
251  ring.reserve( polygon[i].size() + 1 );
252 
253  for ( int j = 0; j < polygon[i].size(); j++ )
254  {
255  //adding point only if it is more than a pixel appart from the previous one
256  const QPointF cur = toCanvasCoordinates( polygon[i][j] ) - pos();
257  if ( 0 == j || std::abs( ring.back().x() - cur.x() ) > 1 || std::abs( ring.back().y() - cur.y() ) > 1 )
258  {
259  ring.push_back( cur );
260  }
261  }
262 
263  ring.push_back( ring[ 0 ] );
264 
265  path.addPolygon( ring );
266  }
267 
268  p->drawPath( path );
269 }
270 
274 void QgsHighlight::paint( QPainter* p )
275 {
276  if ( mGeometry )
277  {
278  p->setPen( mPen );
279  p->setBrush( mBrush );
280 
281  switch ( mGeometry->wkbType() )
282  {
283  case QGis::WKBPoint:
284  case QGis::WKBPoint25D:
285  {
286  paintPoint( p, mGeometry->asPoint() );
287  }
288  break;
289 
290  case QGis::WKBMultiPoint:
292  {
294  for ( int i = 0; i < m.size(); i++ )
295  {
296  paintPoint( p, m[i] );
297  }
298  }
299  break;
300 
301  case QGis::WKBLineString:
303  {
304  paintLine( p, mGeometry->asPolyline() );
305  }
306  break;
307 
310  {
312 
313  for ( int i = 0; i < m.size(); i++ )
314  {
315  paintLine( p, m[i] );
316  }
317  }
318  break;
319 
320  case QGis::WKBPolygon:
321  case QGis::WKBPolygon25D:
322  {
324  }
325  break;
326 
329  {
331  for ( int i = 0; i < m.size(); i++ )
332  {
333  paintPolygon( p, m[i] );
334  }
335  }
336  break;
337 
338  case QGis::WKBUnknown:
339  default:
340  return;
341  }
342  }
343  else if ( mFeature.geometry() )
344  {
345  QgsVectorLayer *layer = qobject_cast<QgsVectorLayer*>( mLayer );
346  if ( !layer )
347  return;
348  QgsMapSettings mapSettings = mMapCanvas->mapSettings();
349  QgsRenderContext context = QgsRenderContext::fromMapSettings( mapSettings );
350 
351  // Because lower level outlines must be covered by upper level fill color
352  // we render first with temporary opaque color, which is then replaced
353  // by final transparent fill color.
354  QColor tmpColor( 255, 0, 0, 255 );
355  QColor tmpFillColor( 0, 255, 0, 255 );
356 
357  QgsFeatureRendererV2 *renderer = getRenderer( context, tmpColor, tmpFillColor );
358  if ( layer && renderer )
359  {
360  QgsRectangle extent = mMapCanvas->extent();
361  if ( extent != rect() ) // catches also canvas resize as it is causing extent change
362  {
363  updateRect();
364  return; // it will be repainted after updateRect()
365  }
366 
367  QSize imageSize( mMapCanvas->mapSettings().outputSize() );
368  QImage image = QImage( imageSize.width(), imageSize.height(), QImage::Format_ARGB32 );
369  image.fill( 0 );
370  QPainter *imagePainter = new QPainter( &image );
371  imagePainter->setRenderHint( QPainter::Antialiasing, true );
372 
373  context.setPainter( imagePainter );
374 
375  renderer->startRender( context, layer->pendingFields() );
376  renderer->renderFeature( mFeature, context );
377  renderer->stopRender( context );
378 
379  imagePainter->end();
380 
381  QColor color( mPen.color() ); // true output color
382  // coefficient to subtract alpha using green (temporary fill)
383  double k = ( 255. - mBrush.color().alpha() ) / 255.;
384  for ( int r = 0; r < image.height(); r++ )
385  {
386  for ( int c = 0; c < image.width(); c++ )
387  {
388  QRgb rgba = image.pixel( c, r );
389  int alpha = qAlpha( rgba );
390  if ( alpha > 0 )
391  {
392  int green = qGreen( rgba );
393  color.setAlpha( qBound<int>( 0, alpha - ( green * k ), 255 ) );
394 
395  image.setPixel( c, r, color.rgba() );
396  }
397  }
398  }
399 
400  p->drawImage( 0, 0, image );
401 
402  delete imagePainter;
403  delete renderer;
404  }
405  }
406 }
407 
409 {
410  if ( mGeometry )
411  {
413 
414  if ( r.isEmpty() )
415  {
416  double d = mMapCanvas->extent().width() * 0.005;
417  r.setXMinimum( r.xMinimum() - d );
418  r.setYMinimum( r.yMinimum() - d );
419  r.setXMaximum( r.xMaximum() + d );
420  r.setYMaximum( r.yMaximum() + d );
421  }
422 
423  setRect( r );
424  setVisible( mGeometry );
425  }
426  else if ( mFeature.geometry() )
427  {
428  // We are currently using full map canvas extent for two reasons:
429  // 1) currently there is no method in QgsFeatureRendererV2 to get rendered feature
430  // bounding box
431  // 2) using different extent would result in shifted fill patterns
432  setRect( mMapCanvas->extent() );
433  setVisible( true );
434  }
435  else
436  {
437  setRect( QgsRectangle() );
438  }
439 }
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:169
double yMaximum() const
Get the y maximum value (top side of rectangle)
Definition: qgsrectangle.h:194
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:113
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:104
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 ...
The QgsMapSettings class contains configuration for rendering of the map.
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:199
QSize outputSize() const
Return the size of the resulting map image.
double xMaximum() const
Get the x maximum value (right side of rectangle)
Definition: qgsrectangle.h:184
QgsGeometry * mGeometry
Definition: qgshighlight.h:89
void setYMinimum(double y)
Set the minimum y value.
Definition: qgsrectangle.h:174
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:85
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)
static double lineWidthScaleFactor(const QgsRenderContext &c, QgsSymbolV2::OutputUnit u, const QgsMapUnitScale &scale=QgsMapUnitScale())
Returns the line width scale factor depending on the unit and the paint device.
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:179
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
double mMinWidth
Definition: qgshighlight.h:93
Class for doing transforms between two map coordinate systems.
void setRect(const QgsRectangle &r)
sets canvas item rectangle
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:204
QgsPoint asPoint() const
return contents of the geometry as a point if wkbType is WKBPoint, otherwise returns [0...
const QgsCoordinateTransform * layerTransfrom(QgsMapLayer *layer) const
Return coordinate transform from layer's CRS to destination CRS.
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:189
QgsSymbolV2::OutputUnit widthUnit() const
void setXMinimum(double x)
Set the minimum x value.
Definition: qgsrectangle.h:164
void setWidth(int width)
Set width.