QGIS API Documentation  2.3.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgscomposerarrow.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscomposerarrow.cpp
3  ----------------------
4  begin : November 2009
5  copyright : (C) 2009 by Marco Hugentobler
6  email : marco@hugis.net
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgscomposerarrow.h"
19 #include "qgscomposition.h"
20 #include <QPainter>
21 #include <QSvgRenderer>
22 
23 #include <cmath>
24 
26  : QgsComposerItem( c )
27  , mStartPoint( 0, 0 )
28  , mStopPoint( 0, 0 )
29  , mStartXIdx( 0 )
30  , mStartYIdx( 0 )
31  , mMarkerMode( DefaultMarker )
32  , mArrowColor( QColor( 0, 0, 0 ) )
33 {
35 }
36 
37 QgsComposerArrow::QgsComposerArrow( const QPointF& startPoint, const QPointF& stopPoint, QgsComposition* c )
38  : QgsComposerItem( c )
39  , mStartPoint( startPoint )
40  , mStopPoint( stopPoint )
41  , mMarkerMode( DefaultMarker )
42  , mArrowColor( QColor( 0, 0, 0 ) )
43 {
44  mStartXIdx = mStopPoint.x() < mStartPoint.x();
45  mStartYIdx = mStopPoint.y() < mStartPoint.y();
48 }
49 
51 {
52 
53 }
54 
56 {
57  setArrowHeadWidth( 4 );
58  mPen.setColor( QColor( 0, 0, 0 ) );
59  mPen.setWidthF( 1 );
60 
61  //set composer item brush and pen to transparent white by default
62  setPen( QPen( QColor( 255, 255, 255, 0 ) ) );
63  setBrush( QBrush( QColor( 255, 255, 255, 0 ) ) );
64 }
65 
66 void QgsComposerArrow::paint( QPainter* painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget )
67 {
68  Q_UNUSED( itemStyle );
69  Q_UNUSED( pWidget );
70  if ( !painter )
71  {
72  return;
73  }
74 
75  drawBackground( painter );
76 
77  //draw arrow
78  QPen arrowPen = mPen;
79  arrowPen.setCapStyle( Qt::FlatCap );
80  arrowPen.setColor( mArrowColor );
81  painter->setPen( arrowPen );
82  painter->setBrush( QBrush( mArrowColor ) );
83  painter->drawLine( QPointF( mStartPoint.x() - pos().x(), mStartPoint.y() - pos().y() ), QPointF( mStopPoint.x() - pos().x(), mStopPoint.y() - pos().y() ) );
84 
85  if ( mMarkerMode == DefaultMarker )
86  {
87  drawHardcodedMarker( painter, EndMarker );
88  }
89  else if ( mMarkerMode == SVGMarker )
90  {
93  }
94 
95  drawFrame( painter );
96  if ( isSelected() )
97  {
98  drawSelectionBoxes( painter );
99  }
100 }
101 
102 void QgsComposerArrow::setSceneRect( const QRectF& rectangle )
103 {
104  if ( rectangle.width() < 0 )
105  {
106  mStartXIdx = 1 - mStartXIdx;
107  }
108  if ( rectangle.height() < 0 )
109  {
110  mStartYIdx = 1 - mStartYIdx;
111  }
112 
113  double margin = computeMarkerMargin();
114 
115  // Ensure the rectangle is at least as large as needed to include the markers
116  QRectF rect = rectangle.unite( QRectF( rectangle.x(), rectangle.y(), 2. * margin, 2. * margin ) );
117 
118  // Compute new start and stop positions
119  double x[2] = {rect.x(), rect.x() + rect.width()};
120  double y[2] = {rect.y(), rect.y() + rect.height()};
121 
122  double xsign = x[mStartXIdx] < x[1 - mStartXIdx] ? 1.0 : -1.0;
123  double ysign = y[mStartYIdx] < y[1 - mStartYIdx] ? 1.0 : -1.0;
124 
125  mStartPoint = QPointF( x[mStartXIdx] + xsign * margin, y[mStartYIdx] + ysign * margin );
126  mStopPoint = QPointF( x[1 - mStartXIdx] - xsign * margin, y[1 - mStartYIdx] - ysign * margin );
127 
129 }
130 
132 {
133  Q_UNUSED( type );
134  QBrush arrowBrush = p->brush();
135  arrowBrush.setColor( mArrowColor );
136  p->setBrush( arrowBrush );
137  drawArrowHead( p, mStopPoint.x() - pos().x(), mStopPoint.y() - pos().y(), angle( mStartPoint, mStopPoint ), mArrowHeadWidth );
138 }
139 
140 void QgsComposerArrow::drawSVGMarker( QPainter* p, MarkerType type, const QString &markerPath )
141 {
142  Q_UNUSED( markerPath );
143  double ang = angle( mStartPoint, mStopPoint );
144 
145  double arrowHeadHeight;
146  if ( type == StartMarker )
147  {
148  arrowHeadHeight = mStartArrowHeadHeight;
149  }
150  else
151  {
152  arrowHeadHeight = mStopArrowHeadHeight;
153  }
154 
155  //prepare paint device
156  int dpi = ( p->device()->logicalDpiX() + p->device()->logicalDpiY() ) / 2;
157  double viewScaleFactor = horizontalViewScaleFactor();
158  int imageWidth = mArrowHeadWidth / 25.4 * dpi;
159  int imageHeight = arrowHeadHeight / 25.4 * dpi;
160 
161  //make nicer preview
163  {
164  imageWidth *= qMin( viewScaleFactor, 10.0 );
165  imageHeight *= qMin( viewScaleFactor, 10.0 );
166  }
167  QImage markerImage( imageWidth, imageHeight, QImage::Format_ARGB32 );
168  QColor markerBG( 255, 255, 255, 0 ); //transparent white background
169  markerImage.fill( markerBG.rgba() );
170 
171  QPointF imageFixPoint;
172  imageFixPoint.setX( mArrowHeadWidth / 2.0 );
173  QPointF canvasPoint;
174  if ( type == StartMarker )
175  {
176  canvasPoint = QPointF( mStartPoint.x() - pos().x(), mStartPoint.y() - pos().y() );
177  imageFixPoint.setY( mStartArrowHeadHeight );
178  }
179  else //end marker
180  {
181  canvasPoint = QPointF( mStopPoint.x() - pos().x(), mStopPoint.y() - pos().y() );
182  imageFixPoint.setY( 0 );
183  }
184 
185  //rasterize svg
186  QSvgRenderer r;
187  if ( type == StartMarker )
188  {
189  if ( mStartMarkerFile.isEmpty() || !r.load( mStartMarkerFile ) )
190  {
191  return;
192  }
193  }
194  else //end marker
195  {
196  if ( mEndMarkerFile.isEmpty() || !r.load( mEndMarkerFile ) )
197  {
198  return;
199  }
200  }
201 
202  //rotate image fix point for backtransform
203  QPointF fixPoint;
204  if ( type == StartMarker )
205  {
206  fixPoint.setX( 0 ); fixPoint.setY( arrowHeadHeight / 2.0 );
207  }
208  else
209  {
210  fixPoint.setX( 0 ); fixPoint.setY( -arrowHeadHeight / 2.0 );
211  }
212  QPointF rotatedFixPoint;
213  double angleRad = ang / 180 * M_PI;
214  rotatedFixPoint.setX( fixPoint.x() * cos( angleRad ) + fixPoint.y() * -sin( angleRad ) );
215  rotatedFixPoint.setY( fixPoint.x() * sin( angleRad ) + fixPoint.y() * cos( angleRad ) );
216 
217 
218  QPainter imagePainter( &markerImage );
219  r.render( &imagePainter );
220 
221  p->save();
222  p->translate( canvasPoint.x() - rotatedFixPoint.x() , canvasPoint.y() - rotatedFixPoint.y() );
223  p->rotate( ang );
224  p->translate( -mArrowHeadWidth / 2.0, -arrowHeadHeight / 2.0 );
225 
226  p->drawImage( QRectF( 0, 0, mArrowHeadWidth, arrowHeadHeight ), markerImage, QRectF( 0, 0, imageWidth, imageHeight ) );
227  p->restore();
228 
229  return;
230 }
231 
232 void QgsComposerArrow::setStartMarker( const QString& svgPath )
233 {
234  QSvgRenderer r;
235  if ( svgPath.isEmpty() || !r.load( svgPath ) )
236  {
237  return;
238  // mStartArrowHeadHeight = 0;
239  }
240  mStartMarkerFile = svgPath;
241 
242  //calculate mArrowHeadHeight from svg file and mArrowHeadWidth
243  QRect viewBox = r.viewBox();
244  mStartArrowHeadHeight = mArrowHeadWidth / viewBox.width() * viewBox.height();
246 }
247 
248 void QgsComposerArrow::setEndMarker( const QString& svgPath )
249 {
250  QSvgRenderer r;
251  if ( svgPath.isEmpty() || !r.load( svgPath ) )
252  {
253  return;
254  // mStopArrowHeadHeight = 0;
255  }
256  mEndMarkerFile = svgPath;
257 
258  //calculate mArrowHeadHeight from svg file and mArrowHeadWidth
259  QRect viewBox = r.viewBox();
260  mStopArrowHeadHeight = mArrowHeadWidth / viewBox.width() * viewBox.height();
262 }
263 
265 {
266  mPen.setWidthF( width );
268 }
269 
271 {
272  mArrowHeadWidth = width;
276 }
277 
279 {
280  double margin = 0;
281  if ( mMarkerMode == DefaultMarker )
282  {
283  margin = mPen.widthF() / 2.0 + mArrowHeadWidth / 2.0;
284  }
285  else if ( mMarkerMode == NoMarker )
286  {
287  margin = mPen.widthF() / 2.0;
288  }
289  else if ( mMarkerMode == SVGMarker )
290  {
291  double maxArrowHeight = qMax( mStartArrowHeadHeight, mStopArrowHeadHeight );
292  margin = mPen.widthF() / 2 + qMax( mArrowHeadWidth / 2.0, maxArrowHeight / 2.0 );
293  }
294  return margin;
295 }
296 
298 {
299  //rectangle containing start and end point
300  QRectF rect = QRectF( qMin( mStartPoint.x(), mStopPoint.x() ), qMin( mStartPoint.y(), mStopPoint.y() ),
301  qAbs( mStopPoint.x() - mStartPoint.x() ), qAbs( mStopPoint.y() - mStartPoint.y() ) );
302  double enlarge = computeMarkerMargin();
303  rect.adjust( -enlarge, -enlarge, enlarge, enlarge );
305 }
306 
308 {
309  mMarkerMode = mode;
311 }
312 
313 bool QgsComposerArrow::writeXML( QDomElement& elem, QDomDocument & doc ) const
314 {
315  QDomElement composerArrowElem = doc.createElement( "ComposerArrow" );
316  composerArrowElem.setAttribute( "outlineWidth", QString::number( outlineWidth() ) );
317  composerArrowElem.setAttribute( "arrowHeadWidth", QString::number( mArrowHeadWidth ) );
318  composerArrowElem.setAttribute( "markerMode", mMarkerMode );
319  composerArrowElem.setAttribute( "startMarkerFile", mStartMarkerFile );
320  composerArrowElem.setAttribute( "endMarkerFile", mEndMarkerFile );
321 
322  //arrow color
323  QDomElement arrowColorElem = doc.createElement( "ArrowColor" );
324  arrowColorElem.setAttribute( "red", mArrowColor.red() );
325  arrowColorElem.setAttribute( "green", mArrowColor.green() );
326  arrowColorElem.setAttribute( "blue", mArrowColor.blue() );
327  arrowColorElem.setAttribute( "alpha", mArrowColor.alpha() );
328  composerArrowElem.appendChild( arrowColorElem );
329 
330  //start point
331  QDomElement startPointElem = doc.createElement( "StartPoint" );
332  startPointElem.setAttribute( "x", QString::number( mStartPoint.x() ) );
333  startPointElem.setAttribute( "y", QString::number( mStartPoint.y() ) );
334  composerArrowElem.appendChild( startPointElem );
335 
336  //stop point
337  QDomElement stopPointElem = doc.createElement( "StopPoint" );
338  stopPointElem.setAttribute( "x", QString::number( mStopPoint.x() ) );
339  stopPointElem.setAttribute( "y", QString::number( mStopPoint.y() ) );
340  composerArrowElem.appendChild( stopPointElem );
341 
342  elem.appendChild( composerArrowElem );
343  return _writeXML( composerArrowElem, doc );
344 }
345 
346 bool QgsComposerArrow::readXML( const QDomElement& itemElem, const QDomDocument& doc )
347 {
348  mArrowHeadWidth = itemElem.attribute( "arrowHeadWidth", "2.0" ).toDouble();
349  mPen.setWidthF( itemElem.attribute( "outlineWidth", "1.0" ).toDouble() );
350  setStartMarker( itemElem.attribute( "startMarkerFile", "" ) );
351  setEndMarker( itemElem.attribute( "endMarkerFile", "" ) );
352  mMarkerMode = QgsComposerArrow::MarkerMode( itemElem.attribute( "markerMode", "0" ).toInt() );
353 
354  //arrow color
355  QDomNodeList arrowColorList = itemElem.elementsByTagName( "ArrowColor" );
356  if ( arrowColorList.size() > 0 )
357  {
358  QDomElement arrowColorElem = arrowColorList.at( 0 ).toElement();
359  int red = arrowColorElem.attribute( "red", "0" ).toInt();
360  int green = arrowColorElem.attribute( "green", "0" ).toInt();
361  int blue = arrowColorElem.attribute( "blue", "0" ).toInt();
362  int alpha = arrowColorElem.attribute( "alpha", "255" ).toInt();
363  mArrowColor = QColor( red, green, blue, alpha );
364  }
365 
366  //restore general composer item properties
367  //needs to be before start point / stop point because setSceneRect()
368  QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
369  if ( composerItemList.size() > 0 )
370  {
371  QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
372  _readXML( composerItemElem, doc );
373  }
374 
375  //start point
376  QDomNodeList startPointList = itemElem.elementsByTagName( "StartPoint" );
377  if ( startPointList.size() > 0 )
378  {
379  QDomElement startPointElem = startPointList.at( 0 ).toElement();
380  mStartPoint.setX( startPointElem.attribute( "x", "0.0" ).toDouble() );
381  mStartPoint.setY( startPointElem.attribute( "y", "0.0" ).toDouble() );
382  }
383 
384  //stop point
385  QDomNodeList stopPointList = itemElem.elementsByTagName( "StopPoint" );
386  if ( stopPointList.size() > 0 )
387  {
388  QDomElement stopPointElem = stopPointList.at( 0 ).toElement();
389  mStopPoint.setX( stopPointElem.attribute( "x", "0.0" ).toDouble() );
390  mStopPoint.setY( stopPointElem.attribute( "y", "0.0" ).toDouble() );
391  }
392 
393  mStartXIdx = mStopPoint.x() < mStartPoint.x();
394  mStartYIdx = mStopPoint.y() < mStartPoint.y();
395 
397  emit itemChanged();
398  return true;
399 }
400 
401 
QgsComposerArrow(QgsComposition *c)
int mStartXIdx
Considering the rectangle as spanning [x[0], x[1]] x [y[0], y[1]], these indices specify which index ...
void setOutlineWidth(double width)
A item that forms part of a map composition.
void setStartMarker(const QString &svgPath)
virtual void drawFrame(QPainter *p)
Draw black frame around item.
QString mStartMarkerFile
Path to the start marker file.
void setArrowHeadWidth(double width)
Sets the width of the arrow head in mm.
void adaptItemSceneRect()
Adapts the item scene rect to contain the start point, the stop point including the arrow marker and ...
double outlineWidth() const
void itemChanged()
Used e.g.
bool _readXML(const QDomElement &itemElem, const QDomDocument &doc)
Reads parameter that are not subclass specific in document.
double horizontalViewScaleFactor() const
Returns the zoom factor of the graphics view.
MarkerMode mMarkerMode
Default marker, no marker or svg marker.
virtual void drawSelectionBoxes(QPainter *p)
Draw selection boxes around item.
double computeMarkerMargin() const
Computes the margin around the line necessary to include the markers.
void drawSVGMarker(QPainter *p, MarkerType type, const QString &markerPath)
Draws a user-defined marker (must be an svg file)
#define M_PI
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget)
Reimplementation of QCanvasItem::paint - draw on canvas.
bool readXML(const QDomElement &itemElem, const QDomDocument &doc)
sets state from Dom document
void drawHardcodedMarker(QPainter *p, MarkerType type)
Draws the default marker at the line end.
void setMarkerMode(MarkerMode mode)
QgsComposition * mComposition
Graphics scene for map printing.
void setSceneRect(const QRectF &rectangle)
Modifies position of start and endpoint and calls QgsComposerItem::setSceneRect.
double mArrowHeadWidth
Width of the arrow marker in mm.
bool _writeXML(QDomElement &itemElem, QDomDocument &doc) const
Writes parameter that are not subclass specific in document.
QString mEndMarkerFile
Path to the end marker file.
virtual void drawBackground(QPainter *p)
Draw background.
void initGraphicsSettings()
Apply default graphics settings.
virtual void setSceneRect(const QRectF &rectangle)
Sets this items bound in scene coordinates such that 1 item size units corresponds to 1 scene size un...
double angle(const QPointF &p1, const QPointF &p2) const
Returns angle of the line from p1 to p2 (clockwise, starting at N)
void drawArrowHead(QPainter *p, double x, double y, double angle, double arrowHeadWidth) const
Draws arrowhead.
QgsComposition::PlotStyle plotStyle() const
double mStartArrowHeadHeight
Height of the arrow marker in mm.
void setEndMarker(const QString &svgPath)
bool writeXML(QDomElement &elem, QDomDocument &doc) const
stores state in Dom element