QGIS API Documentation  2.2.0-Valmiera
 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  , mMarkerMode( DefaultMarker )
30  , mArrowColor( QColor( 0, 0, 0 ) )
31 {
33 }
34 
35 QgsComposerArrow::QgsComposerArrow( const QPointF& startPoint, const QPointF& stopPoint, QgsComposition* c )
36  : QgsComposerItem( c )
37  , mStartPoint( startPoint )
38  , mStopPoint( stopPoint )
39  , mMarkerMode( DefaultMarker )
40  , mArrowColor( QColor( 0, 0, 0 ) )
41 {
44 }
45 
47 {
48 
49 }
50 
52 {
53  setArrowHeadWidth( 4 );
54  mPen.setColor( QColor( 0, 0, 0 ) );
55  mPen.setWidthF( 1 );
56 
57  //set composer item brush and pen to transparent white by default
58  setPen( QPen( QColor( 255, 255, 255, 0 ) ) );
59  setBrush( QBrush( QColor( 255, 255, 255, 0 ) ) );
60 }
61 
62 void QgsComposerArrow::paint( QPainter* painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget )
63 {
64  Q_UNUSED( itemStyle );
65  Q_UNUSED( pWidget );
66  if ( !painter )
67  {
68  return;
69  }
70 
71  drawBackground( painter );
72 
73  //draw arrow
74  QPen arrowPen = mPen;
75  arrowPen.setCapStyle( Qt::FlatCap );
76  arrowPen.setColor( mArrowColor );
77  painter->setPen( arrowPen );
78  painter->setBrush( QBrush( mArrowColor ) );
79  painter->drawLine( QPointF( mStartPoint.x() - pos().x(), mStartPoint.y() - pos().y() ), QPointF( mStopPoint.x() - pos().x(), mStopPoint.y() - pos().y() ) );
80 
81  if ( mMarkerMode == DefaultMarker )
82  {
83  drawHardcodedMarker( painter, EndMarker );
84  }
85  else if ( mMarkerMode == SVGMarker )
86  {
89  }
90 
91  drawFrame( painter );
92  if ( isSelected() )
93  {
94  drawSelectionBoxes( painter );
95  }
96 }
97 
98 void QgsComposerArrow::setSceneRect( const QRectF& rectangle )
99 {
100  //maintain the relative position of start and stop point in the rectangle
101  double startPointXPos = ( mStartPoint.x() - pos().x() ) / rect().width();
102  double startPointYPos = ( mStartPoint.y() - pos().y() ) / rect().height();
103  double stopPointXPos = ( mStopPoint.x() - pos().x() ) / rect().width();
104  double stopPointYPos = ( mStopPoint.y() - pos().y() ) / rect().height();
105 
106  mStartPoint.setX( rectangle.left() + startPointXPos * rectangle.width() );
107  mStartPoint.setY( rectangle.top() + startPointYPos * rectangle.height() );
108  mStopPoint.setX( rectangle.left() + stopPointXPos * rectangle.width() );
109  mStopPoint.setY( rectangle.top() + stopPointYPos * rectangle.height() );
110 
112 }
113 
115 {
116  Q_UNUSED( type );
117  QBrush arrowBrush = p->brush();
118  arrowBrush.setColor( mArrowColor );
119  p->setBrush( arrowBrush );
120  drawArrowHead( p, mStopPoint.x() - pos().x(), mStopPoint.y() - pos().y(), angle( mStartPoint, mStopPoint ), mArrowHeadWidth );
121 }
122 
123 void QgsComposerArrow::drawSVGMarker( QPainter* p, MarkerType type, const QString &markerPath )
124 {
125  Q_UNUSED( markerPath );
126  double ang = angle( mStartPoint, mStopPoint );
127 
128  double arrowHeadHeight;
129  if ( type == StartMarker )
130  {
131  arrowHeadHeight = mStartArrowHeadHeight;
132  }
133  else
134  {
135  arrowHeadHeight = mStopArrowHeadHeight;
136  }
137 
138  //prepare paint device
139  int dpi = ( p->device()->logicalDpiX() + p->device()->logicalDpiY() ) / 2;
140  double viewScaleFactor = horizontalViewScaleFactor();
141  int imageWidth = mArrowHeadWidth / 25.4 * dpi;
142  int imageHeight = arrowHeadHeight / 25.4 * dpi;
143 
144  //make nicer preview
146  {
147  imageWidth *= qMin( viewScaleFactor, 10.0 );
148  imageHeight *= qMin( viewScaleFactor, 10.0 );
149  }
150  QImage markerImage( imageWidth, imageHeight, QImage::Format_ARGB32 );
151  QColor markerBG( 255, 255, 255, 0 ); //transparent white background
152  markerImage.fill( markerBG.rgba() );
153 
154  QPointF imageFixPoint;
155  imageFixPoint.setX( mArrowHeadWidth / 2.0 );
156  QPointF canvasPoint;
157  if ( type == StartMarker )
158  {
159  canvasPoint = QPointF( mStartPoint.x() - pos().x(), mStartPoint.y() - pos().y() );
160  imageFixPoint.setY( mStartArrowHeadHeight );
161  }
162  else //end marker
163  {
164  canvasPoint = QPointF( mStopPoint.x() - pos().x(), mStopPoint.y() - pos().y() );
165  imageFixPoint.setY( 0 );
166  }
167 
168  //rasterize svg
169  QSvgRenderer r;
170  if ( type == StartMarker )
171  {
172  if ( !r.load( mStartMarkerFile ) )
173  {
174  return;
175  }
176  }
177  else //end marker
178  {
179  if ( !r.load( mEndMarkerFile ) )
180  {
181  return;
182  }
183  }
184 
185  //rotate image fix point for backtransform
186  QPointF fixPoint;
187  if ( type == StartMarker )
188  {
189  fixPoint.setX( 0 ); fixPoint.setY( arrowHeadHeight / 2.0 );
190  }
191  else
192  {
193  fixPoint.setX( 0 ); fixPoint.setY( -arrowHeadHeight / 2.0 );
194  }
195  QPointF rotatedFixPoint;
196  double angleRad = ang / 180 * M_PI;
197  rotatedFixPoint.setX( fixPoint.x() * cos( angleRad ) + fixPoint.y() * -sin( angleRad ) );
198  rotatedFixPoint.setY( fixPoint.x() * sin( angleRad ) + fixPoint.y() * cos( angleRad ) );
199 
200 
201  QPainter imagePainter( &markerImage );
202  r.render( &imagePainter );
203 
204  p->save();
205  p->translate( canvasPoint.x() - rotatedFixPoint.x() , canvasPoint.y() - rotatedFixPoint.y() );
206  p->rotate( ang );
207  p->translate( -mArrowHeadWidth / 2.0, -arrowHeadHeight / 2.0 );
208 
209  p->drawImage( QRectF( 0, 0, mArrowHeadWidth, arrowHeadHeight ), markerImage, QRectF( 0, 0, imageWidth, imageHeight ) );
210  p->restore();
211 
212  return;
213 }
214 
215 void QgsComposerArrow::setStartMarker( const QString& svgPath )
216 {
217  QSvgRenderer r;
218  if ( !r.load( svgPath ) )
219  {
220  return;
221  // mStartArrowHeadHeight = 0;
222  }
223  mStartMarkerFile = svgPath;
224 
225  //calculate mArrowHeadHeight from svg file and mArrowHeadWidth
226  QRect viewBox = r.viewBox();
227  mStartArrowHeadHeight = mArrowHeadWidth / viewBox.width() * viewBox.height();
229 }
230 
231 void QgsComposerArrow::setEndMarker( const QString& svgPath )
232 {
233  QSvgRenderer r;
234  if ( !r.load( svgPath ) )
235  {
236  return;
237  // mStopArrowHeadHeight = 0;
238  }
239  mEndMarkerFile = svgPath;
240 
241  //calculate mArrowHeadHeight from svg file and mArrowHeadWidth
242  QRect viewBox = r.viewBox();
243  mStopArrowHeadHeight = mArrowHeadWidth / viewBox.width() * viewBox.height();
245 }
246 
248 {
249  mPen.setWidthF( width );
251 }
252 
254 {
255  mArrowHeadWidth = width;
259 }
260 
262 {
263  //rectangle containing start and end point
264  QRectF rect = QRectF( qMin( mStartPoint.x(), mStopPoint.x() ), qMin( mStartPoint.y(), mStopPoint.y() ),
265  qAbs( mStopPoint.x() - mStartPoint.x() ), qAbs( mStopPoint.y() - mStartPoint.y() ) );
266  double enlarge = 0;
267  if ( mMarkerMode == DefaultMarker )
268  {
269  enlarge = mPen.widthF() / 2.0 + mArrowHeadWidth / 2.0;
270  }
271  else if ( mMarkerMode == NoMarker )
272  {
273  enlarge = mPen.widthF() / 2.0;
274  }
275  else if ( mMarkerMode == SVGMarker )
276  {
277  double maxArrowHeight = qMax( mStartArrowHeadHeight, mStopArrowHeadHeight );
278  enlarge = mPen.widthF() / 2 + qMax( mArrowHeadWidth / 2.0, maxArrowHeight / 2.0 );
279  }
280 
281  rect.adjust( -enlarge, -enlarge, enlarge, enlarge );
283 }
284 
285 bool QgsComposerArrow::writeXML( QDomElement& elem, QDomDocument & doc ) const
286 {
287  QDomElement composerArrowElem = doc.createElement( "ComposerArrow" );
288  composerArrowElem.setAttribute( "outlineWidth", QString::number( outlineWidth() ) );
289  composerArrowElem.setAttribute( "arrowHeadWidth", QString::number( mArrowHeadWidth ) );
290  composerArrowElem.setAttribute( "markerMode", mMarkerMode );
291  composerArrowElem.setAttribute( "startMarkerFile", mStartMarkerFile );
292  composerArrowElem.setAttribute( "endMarkerFile", mEndMarkerFile );
293 
294  //arrow color
295  QDomElement arrowColorElem = doc.createElement( "ArrowColor" );
296  arrowColorElem.setAttribute( "red", mArrowColor.red() );
297  arrowColorElem.setAttribute( "green", mArrowColor.green() );
298  arrowColorElem.setAttribute( "blue", mArrowColor.blue() );
299  arrowColorElem.setAttribute( "alpha", mArrowColor.alpha() );
300  composerArrowElem.appendChild( arrowColorElem );
301 
302  //start point
303  QDomElement startPointElem = doc.createElement( "StartPoint" );
304  startPointElem.setAttribute( "x", QString::number( mStartPoint.x() ) );
305  startPointElem.setAttribute( "y", QString::number( mStartPoint.y() ) );
306  composerArrowElem.appendChild( startPointElem );
307 
308  //stop point
309  QDomElement stopPointElem = doc.createElement( "StopPoint" );
310  stopPointElem.setAttribute( "x", QString::number( mStopPoint.x() ) );
311  stopPointElem.setAttribute( "y", QString::number( mStopPoint.y() ) );
312  composerArrowElem.appendChild( stopPointElem );
313 
314  elem.appendChild( composerArrowElem );
315  return _writeXML( composerArrowElem, doc );
316 }
317 
318 bool QgsComposerArrow::readXML( const QDomElement& itemElem, const QDomDocument& doc )
319 {
320  mArrowHeadWidth = itemElem.attribute( "arrowHeadWidth", "2.0" ).toDouble();
321  mPen.setWidthF( itemElem.attribute( "outlineWidth", "1.0" ).toDouble() );
322  setStartMarker( itemElem.attribute( "startMarkerFile", "" ) );
323  setEndMarker( itemElem.attribute( "endMarkerFile", "" ) );
324  mMarkerMode = QgsComposerArrow::MarkerMode( itemElem.attribute( "markerMode", "0" ).toInt() );
325 
326  //arrow color
327  QDomNodeList arrowColorList = itemElem.elementsByTagName( "ArrowColor" );
328  if ( arrowColorList.size() > 0 )
329  {
330  QDomElement arrowColorElem = arrowColorList.at( 0 ).toElement();
331  int red = arrowColorElem.attribute( "red", "0" ).toInt();
332  int green = arrowColorElem.attribute( "green", "0" ).toInt();
333  int blue = arrowColorElem.attribute( "blue", "0" ).toInt();
334  int alpha = arrowColorElem.attribute( "alpha", "255" ).toInt();
335  mArrowColor = QColor( red, green, blue, alpha );
336  }
337 
338  //restore general composer item properties
339  //needs to be before start point / stop point because setSceneRect()
340  QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
341  if ( composerItemList.size() > 0 )
342  {
343  QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
344  _readXML( composerItemElem, doc );
345  }
346 
347  //start point
348  QDomNodeList startPointList = itemElem.elementsByTagName( "StartPoint" );
349  if ( startPointList.size() > 0 )
350  {
351  QDomElement startPointElem = startPointList.at( 0 ).toElement();
352  mStartPoint.setX( startPointElem.attribute( "x", "0.0" ).toDouble() );
353  mStartPoint.setY( startPointElem.attribute( "y", "0.0" ).toDouble() );
354  }
355 
356  //stop point
357  QDomNodeList stopPointList = itemElem.elementsByTagName( "StopPoint" );
358  if ( stopPointList.size() > 0 )
359  {
360  QDomElement stopPointElem = stopPointList.at( 0 ).toElement();
361  mStopPoint.setX( stopPointElem.attribute( "x", "0.0" ).toDouble() );
362  mStopPoint.setY( stopPointElem.attribute( "y", "0.0" ).toDouble() );
363  }
364 
366  emit itemChanged();
367  return true;
368 }
QgsComposerArrow(QgsComposition *c)
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.
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.
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