QGIS API Documentation  2.2.0-Valmiera
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgsmarkersymbollayerv2.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmarkersymbollayerv2.cpp
3  ---------------------
4  begin : November 2009
5  copyright : (C) 2009 by Martin Dobias
6  email : wonder dot sk at gmail dot com
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 "qgsmarkersymbollayerv2.h"
17 #include "qgssymbollayerv2utils.h"
18 
19 #include "qgsdxfexport.h"
20 #include "qgsdxfpaintdevice.h"
21 #include "qgsexpression.h"
22 #include "qgsrendercontext.h"
23 #include "qgslogger.h"
24 #include "qgssvgcache.h"
25 
26 #include <QPainter>
27 #include <QSvgRenderer>
28 #include <QFileInfo>
29 #include <QDir>
30 #include <QDomDocument>
31 #include <QDomElement>
32 
33 #include <cmath>
34 
36 
37 QgsSimpleMarkerSymbolLayerV2::QgsSimpleMarkerSymbolLayerV2( QString name, QColor color, QColor borderColor, double size, double angle, QgsSymbolV2::ScaleMethod scaleMethod )
38  : mOutlineStyle( Qt::SolidLine ), mOutlineWidth( 0 ), mOutlineWidthUnit( QgsSymbolV2::MM )
39 {
40  mName = name;
41  mColor = color;
43  mSize = size;
44  mAngle = angle;
45  mOffset = QPointF( 0, 0 );
49  mAngleExpression = NULL;
50  mNameExpression = NULL;
51 }
52 
54 {
61 
62  if ( props.contains( "name" ) )
63  name = props["name"];
64  if ( props.contains( "color" ) )
65  color = QgsSymbolLayerV2Utils::decodeColor( props["color"] );
66  if ( props.contains( "color_border" ) )
67  borderColor = QgsSymbolLayerV2Utils::decodeColor( props["color_border"] );
68  if ( props.contains( "size" ) )
69  size = props["size"].toDouble();
70  if ( props.contains( "angle" ) )
71  angle = props["angle"].toDouble();
72  if ( props.contains( "scale_method" ) )
73  scaleMethod = QgsSymbolLayerV2Utils::decodeScaleMethod( props["scale_method"] );
74 
75  QgsSimpleMarkerSymbolLayerV2* m = new QgsSimpleMarkerSymbolLayerV2( name, color, borderColor, size, angle, scaleMethod );
76  if ( props.contains( "offset" ) )
77  m->setOffset( QgsSymbolLayerV2Utils::decodePoint( props["offset"] ) );
78  if ( props.contains( "offset_unit" ) )
79  m->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
80  if ( props.contains( "size_unit" ) )
81  m->setSizeUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["size_unit"] ) );
82 
83  if ( props.contains( "outline_style" ) )
84  {
85  m->setOutlineStyle( QgsSymbolLayerV2Utils::decodePenStyle( props["outline_style"] ) );
86  }
87  if ( props.contains( "outline_width" ) )
88  {
89  m->setOutlineWidth( props["outline_width"].toDouble() );
90  }
91  if ( props.contains( "outline_width_unit" ) )
92  {
93  m->setOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["outline_width_unit"] ) );
94  }
95 
96  if ( props.contains( "horizontal_anchor_point" ) )
97  {
98  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayerV2::HorizontalAnchorPoint( props[ "horizontal_anchor_point" ].toInt() ) );
99  }
100  if ( props.contains( "vertical_anchor_point" ) )
101  {
102  m->setVerticalAnchorPoint( QgsMarkerSymbolLayerV2::VerticalAnchorPoint( props[ "vertical_anchor_point" ].toInt() ) );
103  }
104 
105  //data defined properties
106  if ( props.contains( "name_expression" ) )
107  {
108  m->setDataDefinedProperty( "name", props["name_expression"] );
109  }
110  if ( props.contains( "color_expression" ) )
111  {
112  m->setDataDefinedProperty( "color", props["color_expression"] );
113  }
114  if ( props.contains( "color_border_expression" ) )
115  {
116  m->setDataDefinedProperty( "color_border", props["color_border_expression"] );
117  }
118  if ( props.contains( "outline_width_expression" ) )
119  {
120  m->setDataDefinedProperty( "outline_width", props["outline_width_expression"] );
121  }
122  if ( props.contains( "size_expression" ) )
123  {
124  m->setDataDefinedProperty( "size", props["size_expression"] );
125  }
126  if ( props.contains( "angle_expression" ) )
127  {
128  m->setDataDefinedProperty( "angle", props["angle_expression"] );
129  }
130  if ( props.contains( "offset_expression" ) )
131  {
132  m->setDataDefinedProperty( "offset", props["offset_expression"] );
133  }
134  if ( props.contains( "horizontal_anchor_point_expression" ) )
135  {
136  m->setDataDefinedProperty( "horizontal_anchor_point", props[ "horizontal_anchor_point_expression" ] );
137  }
138  if ( props.contains( "vertical_anchor_point_expression" ) )
139  {
140  m->setDataDefinedProperty( "vertical_anchor_point", props[ "vertical_anchor_point_expression" ] );
141  }
142  return m;
143 }
144 
145 
147 {
148  return "SimpleMarker";
149 }
150 
152 {
153  QColor brushColor = mColor;
154  QColor penColor = mBorderColor;
155 
156  brushColor.setAlphaF( mColor.alphaF() * context.alpha() );
157  penColor.setAlphaF( mBorderColor.alphaF() * context.alpha() );
158 
159  mBrush = QBrush( brushColor );
160  mPen = QPen( penColor );
161  mPen.setStyle( mOutlineStyle );
163 
164  QColor selBrushColor = context.renderContext().selectionColor();
165  QColor selPenColor = selBrushColor == mColor ? selBrushColor : mBorderColor;
166  if ( context.alpha() < 1 )
167  {
168  selBrushColor.setAlphaF( context.alpha() );
169  selPenColor.setAlphaF( context.alpha() );
170  }
171  mSelBrush = QBrush( selBrushColor );
172  mSelPen = QPen( selPenColor );
173  mSelPen.setStyle( mOutlineStyle );
175 
176  bool hasDataDefinedRotation = context.renderHints() & QgsSymbolV2::DataDefinedRotation || dataDefinedProperty( "angle" );
177  bool hasDataDefinedSize = context.renderHints() & QgsSymbolV2::DataDefinedSizeScale || dataDefinedProperty( "size" );
178 
179  // use caching only when:
180  // - size, rotation, shape, color, border color is not data-defined
181  // - drawing to screen (not printer)
182  mUsingCache = !hasDataDefinedRotation && !hasDataDefinedSize && !context.renderContext().forceVectorOutput()
183  && !dataDefinedProperty( "name" ) && !dataDefinedProperty( "color" ) && !dataDefinedProperty( "color_border" ) && !dataDefinedProperty( "outline_width" ) &&
184  !dataDefinedProperty( "size" );
185 
186  // use either QPolygonF or QPainterPath for drawing
187  // TODO: find out whether drawing directly doesn't bring overhead - if not, use it for all shapes
188  if ( !prepareShape() ) // drawing as a polygon
189  {
190  if ( preparePath() ) // drawing as a painter path
191  {
192  // some markers can't be drawn as a polygon (circle, cross)
193  // For these set the selected border color to the selected color
194 
195  if ( mName != "circle" )
196  mSelPen.setColor( selBrushColor );
197  }
198  else
199  {
200  QgsDebugMsg( "unknown symbol" );
201  return;
202  }
203  }
204 
205  QMatrix transform;
206 
207  // scale the shape (if the size is not going to be modified)
208  if ( !hasDataDefinedSize )
209  {
210  double scaledSize = mSize * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mSizeUnit );
211  if ( mUsingCache )
212  scaledSize *= context.renderContext().rasterScaleFactor();
213  double half = scaledSize / 2.0;
214  transform.scale( half, half );
215  }
216 
217  // rotate if the rotation is not going to be changed during the rendering
218  if ( !hasDataDefinedRotation && mAngle != 0 )
219  {
220  transform.rotate( mAngle );
221  }
222 
223  if ( !mPolygon.isEmpty() )
224  mPolygon = transform.map( mPolygon );
225  else
226  mPath = transform.map( mPath );
227 
228  if ( mUsingCache )
229  {
230  if ( !prepareCache( context ) )
231  {
232  mUsingCache = false;
233  }
234  }
235  else
236  {
237  mCache = QImage();
238  mSelCache = QImage();
239  }
240 
241  prepareExpressions( context.layer(), context.renderContext().rendererScale() );
242  mAngleExpression = expression( "angle" );
243  mNameExpression = expression( "name" );
244 
246 }
247 
248 
250 {
251  double scaledSize = mSize * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mSizeUnit );
252 
253  // calculate necessary image size for the cache
254  double pw = (( mPen.widthF() == 0 ? 1 : mPen.widthF() ) + 1 ) / 2 * 2; // make even (round up); handle cosmetic pen
255  int imageSize = (( int ) scaledSize + pw ) / 2 * 2 + 1; // make image width, height odd; account for pen width
256  double center = imageSize / 2.0;
257 
258  if ( imageSize > mMaximumCacheWidth )
259  {
260  return false;
261  }
262 
263  mCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
264  mCache.fill( 0 );
265 
266  QPainter p;
267  p.begin( &mCache );
268  p.setRenderHint( QPainter::Antialiasing );
269  p.setBrush( mBrush );
270  p.setPen( mPen );
271  p.translate( QPointF( center, center ) );
272  drawMarker( &p, context );
273  p.end();
274 
275  // Construct the selected version of the Cache
276 
277  QColor selColor = context.renderContext().selectionColor();
278 
279  mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
280  mSelCache.fill( 0 );
281 
282  p.begin( &mSelCache );
283  p.setRenderHint( QPainter::Antialiasing );
284  p.setBrush( mSelBrush );
285  p.setPen( mSelPen );
286  p.translate( QPointF( center, center ) );
287  drawMarker( &p, context );
288  p.end();
289 
290  // Check that the selected version is different. If not, then re-render,
291  // filling the background with the selection color and using the normal
292  // colors for the symbol .. could be ugly!
293 
294  if ( mSelCache == mCache )
295  {
296  p.begin( &mSelCache );
297  p.setRenderHint( QPainter::Antialiasing );
298  p.fillRect( 0, 0, imageSize, imageSize, selColor );
299  p.setBrush( mBrush );
300  p.setPen( mPen );
301  p.translate( QPointF( center, center ) );
302  drawMarker( &p, context );
303  p.end();
304  }
305 
306  return true;
307 }
308 
310 {
311  Q_UNUSED( context );
312 }
313 
315 {
316  mPolygon.clear();
317 
318  if ( name.isNull() )
319  {
320  name = mName;
321  }
322 
323  if ( name == "square" || name == "rectangle" )
324  {
325  mPolygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 1, 1 ) ) );
326  return true;
327  }
328  else if ( name == "diamond" )
329  {
330  mPolygon << QPointF( -1, 0 ) << QPointF( 0, 1 )
331  << QPointF( 1, 0 ) << QPointF( 0, -1 );
332  return true;
333  }
334  else if ( name == "pentagon" )
335  {
336  mPolygon << QPointF( sin( DEG2RAD( 288.0 ) ), - cos( DEG2RAD( 288.0 ) ) )
337  << QPointF( sin( DEG2RAD( 216.0 ) ), - cos( DEG2RAD( 216.0 ) ) )
338  << QPointF( sin( DEG2RAD( 144.0 ) ), - cos( DEG2RAD( 144.0 ) ) )
339  << QPointF( sin( DEG2RAD( 72.0 ) ), - cos( DEG2RAD( 72.0 ) ) )
340  << QPointF( 0, -1 );
341  return true;
342  }
343  else if ( name == "triangle" )
344  {
345  mPolygon << QPointF( -1, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 );
346  return true;
347  }
348  else if ( name == "equilateral_triangle" )
349  {
350  mPolygon << QPointF( sin( DEG2RAD( 240.0 ) ), - cos( DEG2RAD( 240.0 ) ) )
351  << QPointF( sin( DEG2RAD( 120.0 ) ), - cos( DEG2RAD( 120.0 ) ) )
352  << QPointF( 0, -1 );
353  return true;
354  }
355  else if ( name == "star" )
356  {
357  double sixth = 1.0 / 3;
358 
359  mPolygon << QPointF( 0, -1 )
360  << QPointF( -sixth, -sixth )
361  << QPointF( -1, -sixth )
362  << QPointF( -sixth, 0 )
363  << QPointF( -1, 1 )
364  << QPointF( 0, + sixth )
365  << QPointF( 1, 1 )
366  << QPointF( + sixth, 0 )
367  << QPointF( 1, -sixth )
368  << QPointF( + sixth, -sixth );
369  return true;
370  }
371  else if ( name == "regular_star" )
372  {
373  double inner_r = cos( DEG2RAD( 72.0 ) ) / cos( DEG2RAD( 36.0 ) );
374 
375  mPolygon << QPointF( inner_r * sin( DEG2RAD( 324.0 ) ), - inner_r * cos( DEG2RAD( 324.0 ) ) ) // 324
376  << QPointF( sin( DEG2RAD( 288.0 ) ) , - cos( DEG2RAD( 288 ) ) ) // 288
377  << QPointF( inner_r * sin( DEG2RAD( 252.0 ) ), - inner_r * cos( DEG2RAD( 252.0 ) ) ) // 252
378  << QPointF( sin( DEG2RAD( 216.0 ) ) , - cos( DEG2RAD( 216.0 ) ) ) // 216
379  << QPointF( 0, inner_r ) // 180
380  << QPointF( sin( DEG2RAD( 144.0 ) ) , - cos( DEG2RAD( 144.0 ) ) ) // 144
381  << QPointF( inner_r * sin( DEG2RAD( 108.0 ) ), - inner_r * cos( DEG2RAD( 108.0 ) ) ) // 108
382  << QPointF( sin( DEG2RAD( 72.0 ) ) , - cos( DEG2RAD( 72.0 ) ) ) // 72
383  << QPointF( inner_r * sin( DEG2RAD( 36.0 ) ), - inner_r * cos( DEG2RAD( 36.0 ) ) ) // 36
384  << QPointF( 0, -1 ); // 0
385  return true;
386  }
387  else if ( name == "arrow" )
388  {
389  mPolygon
390  << QPointF( 0, -1 )
391  << QPointF( 0.5, -0.5 )
392  << QPointF( 0.25, -0.25 )
393  << QPointF( 0.25, 1 )
394  << QPointF( -0.25, 1 )
395  << QPointF( -0.25, -0.5 )
396  << QPointF( -0.5, -0.5 );
397  return true;
398  }
399  else if ( name == "filled_arrowhead" )
400  {
401  mPolygon << QPointF( 0, 0 ) << QPointF( -1, 1 ) << QPointF( -1, -1 );
402  return true;
403  }
404 
405  return false;
406 }
407 
409 {
410  mPath = QPainterPath();
411  if ( name.isNull() )
412  {
413  name = mName;
414  }
415 
416  if ( name == "circle" )
417  {
418  mPath.addEllipse( QRectF( -1, -1, 2, 2 ) ); // x,y,w,h
419  return true;
420  }
421  else if ( name == "cross" )
422  {
423  mPath.moveTo( -1, 0 );
424  mPath.lineTo( 1, 0 ); // horizontal
425  mPath.moveTo( 0, -1 );
426  mPath.lineTo( 0, 1 ); // vertical
427  return true;
428  }
429  else if ( name == "x" || name == "cross2" )
430  {
431  mPath.moveTo( -1, -1 );
432  mPath.lineTo( 1, 1 );
433  mPath.moveTo( 1, -1 );
434  mPath.lineTo( -1, 1 );
435  return true;
436  }
437  else if ( name == "line" )
438  {
439  mPath.moveTo( 0, -1 );
440  mPath.lineTo( 0, 1 ); // vertical line
441  return true;
442  }
443  else if ( name == "arrowhead" )
444  {
445  mPath.moveTo( 0, 0 );
446  mPath.lineTo( -1, -1 );
447  mPath.moveTo( 0, 0 );
448  mPath.lineTo( -1, 1 );
449  return true;
450  }
451 
452  return false;
453 }
454 
456 {
457  QPainter *p = context.renderContext().painter();
458  if ( !p )
459  {
460  return;
461  }
462 
463  //offset
464  double offsetX = 0;
465  double offsetY = 0;
466  markerOffset( context, offsetX, offsetY );
467  QPointF off( offsetX, offsetY );
468 
469  //angle
470  double angle = mAngle;
471  if ( mAngleExpression )
472  {
473  angle = mAngleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
474  }
475  if ( angle )
476  off = _rotatedOffset( off, angle );
477 
478  //data defined shape?
479  if ( mNameExpression )
480  {
481  QString name = mNameExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
482  if ( !prepareShape( name ) ) // drawing as a polygon
483  {
484  preparePath( name ); // drawing as a painter path
485  }
486  }
487 
488  if ( mUsingCache )
489  {
490  // we will use cached image
491  QImage &img = context.selected() ? mSelCache : mCache;
492  double s = img.width() / context.renderContext().rasterScaleFactor();
493  p->drawImage( QRectF( point.x() - s / 2.0 + off.x(),
494  point.y() - s / 2.0 + off.y(),
495  s, s ), img );
496  }
497  else
498  {
499  QMatrix transform;
500 
501  // move to the desired position
502  transform.translate( point.x() + off.x(), point.y() + off.y() );
503 
504  QgsExpression *sizeExpression = expression( "size" );
505  bool hasDataDefinedSize = context.renderHints() & QgsSymbolV2::DataDefinedSizeScale || sizeExpression;
506 
507  // resize if necessary
508  if ( hasDataDefinedSize )
509  {
510  double scaledSize = mSize;
511  if ( sizeExpression )
512  {
513  scaledSize = sizeExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
514  }
515 
516  switch ( mScaleMethod )
517  {
519  scaledSize = sqrt( scaledSize );
520  break;
522  break;
523  }
524 
526 
527  double half = scaledSize / 2.0;
528  transform.scale( half, half );
529  }
530 
531  bool hasDataDefinedRotation = context.renderHints() & QgsSymbolV2::DataDefinedRotation || mAngleExpression;
532  if ( angle != 0 && hasDataDefinedRotation )
533  transform.rotate( angle );
534 
535  QgsExpression* colorExpression = expression( "color" );
536  QgsExpression* colorBorderExpression = expression( "color_border" );
537  QgsExpression* outlineWidthExpression = expression( "outline_width" );
538  if ( colorExpression )
539  {
540  mBrush.setColor( QgsSymbolLayerV2Utils::decodeColor( colorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) );
541  }
542  if ( colorBorderExpression )
543  {
544  mPen.setColor( QgsSymbolLayerV2Utils::decodeColor( colorBorderExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) );
545  mSelPen.setColor( QgsSymbolLayerV2Utils::decodeColor( colorBorderExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) );
546  }
547  if ( outlineWidthExpression )
548  {
549  double outlineWidth = outlineWidthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
550  mPen.setWidthF( outlineWidth * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mOutlineWidthUnit ) );
552  }
553 
554  p->setBrush( context.selected() ? mSelBrush : mBrush );
555  p->setPen( context.selected() ? mSelPen : mPen );
556 
557  if ( !mPolygon.isEmpty() )
558  p->drawPolygon( transform.map( mPolygon ) );
559  else
560  p->drawPath( transform.map( mPath ) );
561  }
562 }
563 
564 
566 {
567  QgsStringMap map;
568  map["name"] = mName;
569  map["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
570  map["color_border"] = QgsSymbolLayerV2Utils::encodeColor( mBorderColor );
571  map["size"] = QString::number( mSize );
573  map["angle"] = QString::number( mAngle );
574  map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
577  map["outline_style"] = QgsSymbolLayerV2Utils::encodePenStyle( mOutlineStyle );
578  map["outline_width"] = QString::number( mOutlineWidth );
579  map["outline_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOutlineWidthUnit );
580  map["horizontal_anchor_point"] = QString::number( mHorizontalAnchorPoint );
581  map["vertical_anchor_point"] = QString::number( mVerticalAnchorPoint );
582 
583  //data define properties
585  return map;
586 }
587 
589 {
591  m->setOffset( mOffset );
592  m->setSizeUnit( mSizeUnit );
600  return m;
601 }
602 
603 void QgsSimpleMarkerSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
604 {
605  // <Graphic>
606  QDomElement graphicElem = doc.createElement( "se:Graphic" );
607  element.appendChild( graphicElem );
608 
610 
611  // <Rotation>
612  QString angleFunc;
613  bool ok;
614  double angle = props.value( "angle", "0" ).toDouble( &ok );
615  if ( !ok )
616  {
617  angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mAngle );
618  }
619  else if ( angle + mAngle != 0 )
620  {
621  angleFunc = QString::number( angle + mAngle );
622  }
623  QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
624 
625  // <Displacement>
627 }
628 
629 QString QgsSimpleMarkerSymbolLayerV2::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
630 {
631  Q_UNUSED( mmScaleFactor );
632  Q_UNUSED( mapUnitScaleFactor );
633 #if 0
634  QString ogrType = "3"; //default is circle
635  if ( mName == "square" )
636  {
637  ogrType = "5";
638  }
639  else if ( mName == "triangle" )
640  {
641  ogrType = "7";
642  }
643  else if ( mName == "star" )
644  {
645  ogrType = "9";
646  }
647  else if ( mName == "circle" )
648  {
649  ogrType = "3";
650  }
651  else if ( mName == "cross" )
652  {
653  ogrType = "0";
654  }
655  else if ( mName == "x" || mName == "cross2" )
656  {
657  ogrType = "1";
658  }
659  else if ( mName == "line" )
660  {
661  ogrType = "10";
662  }
663 
664  QString ogrString;
665  ogrString.append( "SYMBOL(" );
666  ogrString.append( "id:" );
667  ogrString.append( "\"" );
668  ogrString.append( "ogr-sym-" );
669  ogrString.append( ogrType );
670  ogrString.append( "\"" );
671  ogrString.append( ",c:" );
672  ogrString.append( mColor.name() );
673  ogrString.append( ",o:" );
674  ogrString.append( mBorderColor.name() );
675  ogrString.append( QString( ",s:%1mm" ).arg( mSize ) );
676  ogrString.append( ")" );
677  return ogrString;
678 #endif //0
679 
680  QString ogrString;
681  ogrString.append( "PEN(" );
682  ogrString.append( "c:" );
683  ogrString.append( mColor.name() );
684  ogrString.append( ",w:" );
685  ogrString.append( QString::number( mSize ) );
686  ogrString.append( "mm" );
687  ogrString.append( ")" );
688  return ogrString;
689 }
690 
692 {
693  QgsDebugMsg( "Entered." );
694 
695  QDomElement graphicElem = element.firstChildElement( "Graphic" );
696  if ( graphicElem.isNull() )
697  return NULL;
698 
699  QString name = "square";
700  QColor color, borderColor;
701  double borderWidth, size;
702  Qt::PenStyle borderStyle;
703 
704  if ( !QgsSymbolLayerV2Utils::wellKnownMarkerFromSld( graphicElem, name, color, borderColor, borderStyle, borderWidth, size ) )
705  return NULL;
706 
707  double angle = 0.0;
708  QString angleFunc;
709  if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
710  {
711  bool ok;
712  double d = angleFunc.toDouble( &ok );
713  if ( ok )
714  angle = d;
715  }
716 
717  QPointF offset;
719 
720  QgsSimpleMarkerSymbolLayerV2 *m = new QgsSimpleMarkerSymbolLayerV2( name, color, borderColor, size );
721  m->setAngle( angle );
722  m->setOffset( offset );
723  m->setOutlineStyle( borderStyle );
724  return m;
725 }
726 
728 {
729  Q_UNUSED( context );
730 
731  if ( mPolygon.count() != 0 )
732  {
733  p->drawPolygon( mPolygon );
734  }
735  else
736  {
737  p->drawPath( mPath );
738  }
739 }
740 
741 bool QgsSimpleMarkerSymbolLayerV2::writeDxf( QgsDxfExport& e, double mmMapUnitScaleFactor, const QString& layerName, const QgsSymbolV2RenderContext* context, const QgsFeature* f, const QPointF& shift ) const
742 {
743  //data defined size?
744  double size = mSize;
745 
746  QgsExpression *sizeExpression = expression( "size" );
747  bool hasDataDefinedSize = false;
748  if ( context )
749  {
750  hasDataDefinedSize = context->renderHints() & QgsSymbolV2::DataDefinedSizeScale || sizeExpression;
751  }
752 
753  //data defined size
754  if ( hasDataDefinedSize )
755  {
756  if ( sizeExpression )
757  {
758  size = sizeExpression->evaluate( const_cast<QgsFeature*>( context->feature() ) ).toDouble();
759  }
760 
761  switch ( mScaleMethod )
762  {
764  size = sqrt( size );
765  break;
767  break;
768  }
769 
771  }
772  if ( mSizeUnit == QgsSymbolV2::MM )
773  {
774  size *= mmMapUnitScaleFactor;
775  }
776  double halfSize = size / 2.0;
777 
778  //outlineWidth
779  double outlineWidth = mOutlineWidth;
780  QgsExpression* outlineWidthExpression = expression( "outline_width" );
781  if ( outlineWidthExpression )
782  {
783  outlineWidth = outlineWidthExpression->evaluate( const_cast<QgsFeature*>( context->feature() ) ).toDouble();
784  }
785  if ( mSizeUnit == QgsSymbolV2::MM )
786  {
787  outlineWidth *= mmMapUnitScaleFactor;
788  }
789 
790  //color
791  QColor c = mPen.color();
792  if ( mPen.style() == Qt::NoPen )
793  {
794  c = mBrush.color();
795  }
796  QgsExpression* colorExpression = expression( "color" );
797  if ( colorExpression )
798  {
799  c = QgsSymbolLayerV2Utils::decodeColor( colorExpression->evaluate( *f ).toString() );
800  }
801  int colorIndex = QgsDxfExport::closestColorMatch( c.rgb() );
802 
803  //offset
804  double offsetX = 0;
805  double offsetY = 0;
806  markerOffset( *context, offsetX, offsetY );
807  QPointF off( offsetX, offsetY );
808 
809  //angle
810  double angle = mAngle;
811  QgsExpression* angleExpression = expression( "angle" );
812  if ( angleExpression )
813  {
814  angle = angleExpression->evaluate( const_cast<QgsFeature*>( context->feature() ) ).toDouble();
815  }
816  angle = -angle; //rotation in Qt is counterclockwise
817  if ( angle )
818  off = _rotatedOffset( off, angle );
819 
820  if ( mSizeUnit == QgsSymbolV2::MM )
821  {
822  off *= mmMapUnitScaleFactor;
823  }
824 
825  QTransform t;
826  t.translate( shift.x() + offsetX, shift.y() + offsetY );
827 
828  if ( angle != 0 )
829  t.rotate( angle );
830 
831  //data defined symbol name
832 
833  if ( mName == "circle" )
834  {
835  e.writeGroup( 0, "CIRCLE" );
836  e.writeGroup( 8, layerName );
837 
838  e.writeGroup( 62, colorIndex );
839  e.writeGroup( 10, shift.x() );
840  e.writeGroup( 20, shift.y() );
841  e.writeGroup( 30, 0.0 );
842  e.writeGroup( 40, halfSize );
843  }
844  else if ( mName == "square" || mName == "rectangle" )
845  {
846  QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
847  QPointF pt2 = t.map( QPointF( halfSize, -halfSize ) );
848  QPointF pt3 = t.map( QPointF( -halfSize, halfSize ) );
849  QPointF pt4 = t.map( QPointF( halfSize, halfSize ) );
850  e.writeSolid( layerName, colorIndex, QgsPoint( pt1.x(), pt1.y() ), QgsPoint( pt2.x(), pt2.y() ), QgsPoint( pt3.x(), pt3.y() ), QgsPoint( pt4.x(), pt4.y() ) );
851  }
852  else if ( mName == "diamond" )
853  {
854  QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
855  QPointF pt2 = t.map( QPointF( 0, -halfSize ) );
856  QPointF pt3 = t.map( QPointF( 0, halfSize ) );
857  QPointF pt4 = t.map( QPointF( halfSize, 0 ) );
858  e.writeSolid( layerName, colorIndex, QgsPoint( pt1.x(), pt1.y() ), QgsPoint( pt2.x(), pt2.y() ), QgsPoint( pt3.x(), pt3.y() ), QgsPoint( pt4.x(), pt4.y() ) );
859  }
860  else if ( mName == "triangle" )
861  {
862  QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
863  QPointF pt2 = t.map( QPointF( halfSize, -halfSize ) );
864  QPointF pt3 = t.map( QPointF( 0, halfSize ) );
865  e.writeSolid( layerName, colorIndex, QgsPoint( pt1.x(), pt1.y() ), QgsPoint( pt2.x(), pt2.y() ), QgsPoint( pt3.x(), pt3.y() ), QgsPoint( pt3.x(), pt3.y() ) );
866  }
867  /*else if( mName == "equilateral_triangle" )
868  {
869 
870  }*/
871  else if ( mName == "line" )
872  {
873  QPointF pt1 = t.map( QPointF( 0, halfSize ) );
874  QPointF pt2 = t.map( QPointF( 0, -halfSize ) );
875  e.writeLine( QgsPoint( pt1.x(), pt1.y() ), QgsPoint( pt2.x(), pt2.y() ), layerName, "CONTINUOUS", colorIndex, outlineWidth );
876  }
877  else if ( mName == "coss" )
878  {
879  QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
880  QPointF pt2 = t.map( QPointF( halfSize, 0 ) );
881  QPointF pt3 = t.map( QPointF( 0, -halfSize ) );
882  QPointF pt4 = t.map( QPointF( 0, halfSize ) );
883  e.writeLine( QgsPoint( pt1.x(), pt1.y() ), QgsPoint( pt2.x(), pt2.y() ), layerName, "CONTINUOUS", colorIndex, outlineWidth );
884  e.writeLine( QgsPoint( pt3.x(), pt3.y() ), QgsPoint( pt4.x(), pt4.y() ), layerName, "CONTINUOUS", colorIndex, outlineWidth );
885  }
886  else if ( mName == "x" || mName == "cross2" )
887  {
888  QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
889  QPointF pt2 = t.map( QPointF( halfSize, halfSize ) );
890  QPointF pt3 = t.map( QPointF( -halfSize, halfSize ) );
891  QPointF pt4 = t.map( QPointF( halfSize, -halfSize ) );
892  e.writeLine( QgsPoint( pt1.x(), pt1.y() ), QgsPoint( pt2.x(), pt2.y() ), layerName, "CONTINUOUS", colorIndex, outlineWidth );
893  e.writeLine( QgsPoint( pt3.x(), pt3.y() ), QgsPoint( pt4.x(), pt4.y() ), layerName, "CONTINUOUS", colorIndex, outlineWidth );
894  }
895  else if ( mName == "arrowhead" )
896  {
897  QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
898  QPointF pt2 = t.map( QPointF( 0, 0 ) );
899  QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
900  e.writeLine( QgsPoint( pt1.x(), pt1.y() ), QgsPoint( pt2.x(), pt2.y() ), layerName, "CONTINUOUS", colorIndex, outlineWidth );
901  e.writeLine( QgsPoint( pt3.x(), pt3.y() ), QgsPoint( pt2.x(), pt2.y() ), layerName, "CONTINUOUS", colorIndex, outlineWidth );
902  }
903  else if ( mName == "filled_arrowhead" )
904  {
905  QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
906  QPointF pt2 = t.map( QPointF( 0, 0 ) );
907  QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
908  e.writeSolid( layerName, colorIndex, QgsPoint( pt1.x(), pt1.y() ), QgsPoint( pt2.x(), pt2.y() ), QgsPoint( pt3.x(), pt3.y() ), QgsPoint( pt3.x(), pt3.y() ) );
909  }
910  else
911  {
912  return false;
913  }
914  return true;
915 }
916 
918 
919 
921 {
923  mSize = size;
924  mAngle = angle;
925  mOffset = QPointF( 0, 0 );
926  mOutlineWidth = 1.0;
928  mFillColor = QColor( Qt::black );
929  mOutlineColor = QColor( Qt::black );
930 }
931 
932 
934 {
935  QString name = DEFAULT_SVGMARKER_NAME;
936  double size = DEFAULT_SVGMARKER_SIZE;
938 
939  if ( props.contains( "name" ) )
940  name = props["name"];
941  if ( props.contains( "size" ) )
942  size = props["size"].toDouble();
943  if ( props.contains( "angle" ) )
944  angle = props["angle"].toDouble();
945 
946  QgsSvgMarkerSymbolLayerV2* m = new QgsSvgMarkerSymbolLayerV2( name, size, angle );
947 
948  //we only check the svg default parameters if necessary, since it could be expensive
949  if ( !props.contains( "fill" ) && !props.contains( "outline" ) && !props.contains( "outline-width" ) )
950  {
951  QColor fillColor, outlineColor;
952  double outlineWidth;
953  bool hasFillParam, hasOutlineParam, hasOutlineWidthParam;
954  QgsSvgCache::instance()->containsParams( name, hasFillParam, fillColor, hasOutlineParam, outlineColor, hasOutlineWidthParam, outlineWidth );
955  if ( hasFillParam )
956  {
957  m->setFillColor( fillColor );
958  }
959  if ( hasOutlineParam )
960  {
961  m->setOutlineColor( outlineColor );
962  }
963  if ( hasOutlineWidthParam )
964  {
965  m->setOutlineWidth( outlineWidth );
966  }
967  }
968 
969  if ( props.contains( "size_unit" ) )
970  m->setSizeUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["size_unit"] ) );
971  if ( props.contains( "offset" ) )
972  m->setOffset( QgsSymbolLayerV2Utils::decodePoint( props["offset"] ) );
973  if ( props.contains( "offset_unit" ) )
974  m->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
975  if ( props.contains( "fill" ) )
976  m->setFillColor( QColor( props["fill"] ) );
977  if ( props.contains( "outline" ) )
978  m->setOutlineColor( QColor( props["outline"] ) );
979  if ( props.contains( "outline-width" ) )
980  m->setOutlineWidth( props["outline-width"].toDouble() );
981  if ( props.contains( "outline_width_unit" ) )
982  m->setOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["outline_width_unit"] ) );
983 
984  if ( props.contains( "horizontal_anchor_point" ) )
985  {
986  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayerV2::HorizontalAnchorPoint( props[ "horizontal_anchor_point" ].toInt() ) );
987  }
988  if ( props.contains( "vertical_anchor_point" ) )
989  {
990  m->setVerticalAnchorPoint( QgsMarkerSymbolLayerV2::VerticalAnchorPoint( props[ "vertical_anchor_point" ].toInt() ) );
991  }
992 
993  //data defined properties
994  if ( props.contains( "size_expression" ) )
995  {
996  m->setDataDefinedProperty( "size", props["size_expression"] );
997  }
998  if ( props.contains( "outline-width_expression" ) )
999  {
1000  m->setDataDefinedProperty( "outline-width", props["outline-width_expression"] );
1001  }
1002  if ( props.contains( "angle_expression" ) )
1003  {
1004  m->setDataDefinedProperty( "angle", props["angle_expression"] );
1005  }
1006  if ( props.contains( "offset_expression" ) )
1007  {
1008  m->setDataDefinedProperty( "offset", props["offset_expression"] );
1009  }
1010  if ( props.contains( "name_expression" ) )
1011  {
1012  m->setDataDefinedProperty( "name", props["name_expression"] );
1013  }
1014  if ( props.contains( "fill_expression" ) )
1015  {
1016  m->setDataDefinedProperty( "fill", props["fill_expression"] );
1017  }
1018  if ( props.contains( "outline_expression" ) )
1019  {
1020  m->setDataDefinedProperty( "outline", props["outline_expression"] );
1021  }
1022  if ( props.contains( "horizontal_anchor_point_expression" ) )
1023  {
1024  m->setDataDefinedProperty( "horizontal_anchor_point", props[ "horizontal_anchor_point_expression" ] );
1025  }
1026  if ( props.contains( "vertical_anchor_point_expression" ) )
1027  {
1028  m->setDataDefinedProperty( "vertical_anchor_point", props[ "vertical_anchor_point_expression" ] );
1029  }
1030  return m;
1031 }
1032 
1034 {
1035  mPath = path;
1036  QColor fillColor, outlineColor;
1037  double outlineWidth;
1038  bool hasFillParam, hasOutlineParam, hasOutlineWidthParam;
1039  QgsSvgCache::instance()->containsParams( path, hasFillParam, fillColor, hasOutlineParam, outlineColor, hasOutlineWidthParam, outlineWidth );
1040  if ( hasFillParam )
1041  {
1042  setFillColor( fillColor );
1043  }
1044  if ( hasOutlineParam )
1045  {
1046  setOutlineColor( outlineColor );
1047  }
1048  if ( hasOutlineWidthParam )
1049  {
1050  setOutlineWidth( outlineWidth );
1051  }
1052 }
1053 
1054 
1056 {
1057  return "SvgMarker";
1058 }
1059 
1061 {
1062  mOrigSize = mSize; // save in case the size would be data defined
1063  Q_UNUSED( context );
1064  prepareExpressions( context.layer(), context.renderContext().rendererScale() );
1065 }
1066 
1068 {
1069  Q_UNUSED( context );
1070 }
1071 
1073 {
1074  QPainter *p = context.renderContext().painter();
1075  if ( !p )
1076  return;
1077 
1078  double size = mSize;
1079  QgsExpression* sizeExpression = expression( "size" );
1080  bool hasDataDefinedSize = context.renderHints() & QgsSymbolV2::DataDefinedSizeScale || sizeExpression;
1081 
1082  if ( sizeExpression )
1083  {
1084  size = sizeExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
1085  }
1086 
1087  if ( hasDataDefinedSize )
1088  {
1089  switch ( mScaleMethod )
1090  {
1092  size = sqrt( size );
1093  break;
1095  break;
1096  }
1097  }
1099 
1100  //don't render symbols with size below one or above 10,000 pixels
1101  if (( int )size < 1 || 10000.0 < size )
1102  {
1103  return;
1104  }
1105 
1106  p->save();
1107 
1108  //offset
1109  double offsetX = 0;
1110  double offsetY = 0;
1111  markerOffset( context, offsetX, offsetY );
1112  QPointF outputOffset( offsetX, offsetY );
1113 
1114  double angle = mAngle;
1115  QgsExpression* angleExpression = expression( "angle" );
1116  if ( angleExpression )
1117  {
1118  angle = angleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
1119  }
1120  if ( angle )
1121  outputOffset = _rotatedOffset( outputOffset, angle );
1122  p->translate( point + outputOffset );
1123 
1124  bool rotated = !qgsDoubleNear( angle, 0 );
1125  bool drawOnScreen = qgsDoubleNear( context.renderContext().rasterScaleFactor(), 1.0, 0.1 );
1126  if ( rotated )
1127  p->rotate( angle );
1128 
1129  QString path = mPath;
1130  QgsExpression* nameExpression = expression( "name" );
1131  if ( nameExpression )
1132  {
1133  path = nameExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
1134  }
1135 
1136  double outlineWidth = mOutlineWidth;
1137  QgsExpression* outlineWidthExpression = expression( "outline_width" );
1138  if ( outlineWidthExpression )
1139  {
1140  outlineWidth = outlineWidthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
1141  }
1142 
1143  QColor fillColor = mFillColor;
1144  QgsExpression* fillExpression = expression( "fill" );
1145  if ( fillExpression )
1146  {
1147  fillColor = QgsSymbolLayerV2Utils::decodeColor( fillExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
1148  }
1149 
1150  QColor outlineColor = mOutlineColor;
1151  QgsExpression* outlineExpression = expression( "outline" );
1152  if ( outlineExpression )
1153  {
1154  outlineColor = QgsSymbolLayerV2Utils::decodeColor( outlineExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
1155  }
1156 
1157 
1158  bool fitsInCache = true;
1159  bool usePict = true;
1160  double hwRatio = 1.0;
1161  if ( drawOnScreen && !rotated )
1162  {
1163  usePict = false;
1164  const QImage& img = QgsSvgCache::instance()->svgAsImage( path, size, fillColor, outlineColor, outlineWidth,
1165  context.renderContext().scaleFactor(), context.renderContext().rasterScaleFactor(), fitsInCache );
1166  if ( fitsInCache && img.width() > 1 )
1167  {
1168  //consider transparency
1169  if ( !qgsDoubleNear( context.alpha(), 1.0 ) )
1170  {
1171  QImage transparentImage = img.copy();
1172  QgsSymbolLayerV2Utils::multiplyImageOpacity( &transparentImage, context.alpha() );
1173  p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
1174  hwRatio = ( double )transparentImage.height() / ( double )transparentImage.width();
1175  }
1176  else
1177  {
1178  p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
1179  hwRatio = ( double )img.height() / ( double )img.width();
1180  }
1181  }
1182  }
1183 
1184  if ( usePict || !fitsInCache )
1185  {
1186  p->setOpacity( context.alpha() );
1187  const QPicture& pct = QgsSvgCache::instance()->svgAsPicture( path, size, fillColor, outlineColor, outlineWidth,
1189 
1190  if ( pct.width() > 1 )
1191  {
1192  p->drawPicture( 0, 0, pct );
1193  hwRatio = ( double )pct.height() / ( double )pct.width();
1194  }
1195  }
1196 
1197  if ( context.selected() )
1198  {
1199  QPen pen( context.renderContext().selectionColor() );
1201  if ( penWidth > size / 20 )
1202  {
1203  // keep the pen width from covering symbol
1204  penWidth = size / 20;
1205  }
1206  double penOffset = penWidth / 2;
1207  pen.setWidth( penWidth );
1208  p->setPen( pen );
1209  p->setBrush( Qt::NoBrush );
1210  double wSize = size + penOffset;
1211  double hSize = size * hwRatio + penOffset;
1212  p->drawRect( QRectF( -wSize / 2.0, -hSize / 2.0, wSize, hSize ) );
1213  }
1214 
1215  p->restore();
1216 }
1217 
1218 
1220 {
1221  QgsStringMap map;
1223  map["size"] = QString::number( mSize );
1224  map["size_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mSizeUnit );
1225  map["angle"] = QString::number( mAngle );
1226  map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
1227  map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit );
1228  map["fill"] = mFillColor.name();
1229  map["outline"] = mOutlineColor.name();
1230  map["outline-width"] = QString::number( mOutlineWidth );
1231  map["outline_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOutlineWidthUnit );
1232  map["horizontal_anchor_point"] = QString::number( mHorizontalAnchorPoint );
1233  map["vertical_anchor_point"] = QString::number( mVerticalAnchorPoint );
1234 
1236  return map;
1237 }
1238 
1240 {
1242  m->setFillColor( mFillColor );
1246  m->setOffset( mOffset );
1247  m->setOffsetUnit( mOffsetUnit );
1248  m->setSizeUnit( mSizeUnit );
1252  return m;
1253 }
1254 
1256 {
1257  mSizeUnit = unit;
1258  mOffsetUnit = unit;
1259  mOutlineWidthUnit = unit;
1260 }
1261 
1263 {
1265  if ( unit != mOffsetUnit || unit != mOutlineWidthUnit )
1266  {
1267  return QgsSymbolV2::Mixed;
1268  }
1269  return unit;
1270 }
1271 
1272 void QgsSvgMarkerSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
1273 {
1274  // <Graphic>
1275  QDomElement graphicElem = doc.createElement( "se:Graphic" );
1276  element.appendChild( graphicElem );
1277 
1278  QgsSymbolLayerV2Utils::externalGraphicToSld( doc, graphicElem, mPath, "image/svg+xml", mFillColor, mSize );
1279 
1280  // <Rotation>
1281  QString angleFunc;
1282  bool ok;
1283  double angle = props.value( "angle", "0" ).toDouble( &ok );
1284  if ( !ok )
1285  {
1286  angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mAngle );
1287  }
1288  else if ( angle + mAngle != 0 )
1289  {
1290  angleFunc = QString::number( angle + mAngle );
1291  }
1292 
1293  QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
1294 
1295  // <Displacement>
1297 }
1298 
1300 {
1301  QgsDebugMsg( "Entered." );
1302 
1303  QDomElement graphicElem = element.firstChildElement( "Graphic" );
1304  if ( graphicElem.isNull() )
1305  return NULL;
1306 
1307  QString path, mimeType;
1308  QColor fillColor;
1309  double size;
1310 
1311  if ( !QgsSymbolLayerV2Utils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
1312  return NULL;
1313 
1314  if ( mimeType != "image/svg+xml" )
1315  return NULL;
1316 
1317  double angle = 0.0;
1318  QString angleFunc;
1319  if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
1320  {
1321  bool ok;
1322  double d = angleFunc.toDouble( &ok );
1323  if ( ok )
1324  angle = d;
1325  }
1326 
1327  QPointF offset;
1329 
1331  m->setFillColor( fillColor );
1332  //m->setOutlineColor( outlineColor );
1333  //m->setOutlineWidth( outlineWidth );
1334  m->setAngle( angle );
1335  m->setOffset( offset );
1336  return m;
1337 }
1338 
1339 bool QgsSvgMarkerSymbolLayerV2::writeDxf( QgsDxfExport& e, double mmMapUnitScaleFactor, const QString& layerName, const QgsSymbolV2RenderContext* context, const QgsFeature* f,
1340  const QPointF& shift ) const
1341 {
1342  Q_UNUSED( layerName );
1343  Q_UNUSED( shift ); //todo...
1344 
1345  QSvgRenderer r( mPath );
1346  if ( !r.isValid() )
1347  {
1348  return false;
1349  }
1350 
1351  QgsDxfPaintDevice pd( &e );
1352  pd.setDrawingSize( QSizeF( r.defaultSize() ) );
1353 
1354  //size
1355  double size = mSize;
1356  QgsExpression* sizeExpression = expression( "size" );
1357  bool hasDataDefinedSize = context->renderHints() & QgsSymbolV2::DataDefinedSizeScale || sizeExpression;
1358 
1359  if ( sizeExpression )
1360  {
1361  size = sizeExpression->evaluate( *f ).toDouble();
1362  }
1363 
1364  if ( hasDataDefinedSize )
1365  {
1366  switch ( mScaleMethod )
1367  {
1369  size = sqrt( size );
1370  break;
1372  break;
1373  }
1374  }
1375 
1376  if ( mSizeUnit == QgsSymbolV2::MM )
1377  {
1378  size *= mmMapUnitScaleFactor;
1379  }
1380 
1381  double halfSize = size / 2.0;
1382 
1383  //offset, angle
1384  QPointF offset = mOffset;
1385  QgsExpression* offsetExpression = expression( "offset" );
1386  if ( offsetExpression )
1387  {
1388  QString offsetString = offsetExpression->evaluate( *f ).toString();
1389  offset = QgsSymbolLayerV2Utils::decodePoint( offsetString );
1390  }
1391  double offsetX = offset.x();
1392  double offsetY = offset.y();
1393  if ( mSizeUnit == QgsSymbolV2::MM )
1394  {
1395  offsetX *= mmMapUnitScaleFactor;
1396  offsetY *= mmMapUnitScaleFactor;
1397  }
1398 
1399  QPointF outputOffset( offsetX, offsetY );
1400 
1401  double angle = mAngle;
1402  QgsExpression* angleExpression = expression( "angle" );
1403  if ( angleExpression )
1404  {
1405  angle = angleExpression->evaluate( *f ).toDouble();
1406  }
1407  //angle = -angle; //rotation in Qt is counterclockwise
1408  if ( angle )
1409  outputOffset = _rotatedOffset( outputOffset, angle );
1410 
1411  QPainter p;
1412  p.begin( &pd );
1413  if ( !qgsDoubleNear( angle, 0.0 ) )
1414  {
1415  p.translate( r.defaultSize().width() / 2.0, r.defaultSize().height() / 2.0 );
1416  p.rotate( angle );
1417  p.translate( -r.defaultSize().width() / 2.0, -r.defaultSize().height() / 2.0 );
1418  }
1419  pd.setShift( shift );
1420  pd.setOutputSize( QRectF( -halfSize, -halfSize, size, size ) );
1421  pd.setLayer( layerName );
1422  r.render( &p );
1423  p.end();
1424  return true;
1425 }
1426 
1428 
1429 QgsFontMarkerSymbolLayerV2::QgsFontMarkerSymbolLayerV2( QString fontFamily, QChar chr, double pointSize, QColor color, double angle )
1430 {
1432  mChr = chr;
1433  mColor = color;
1434  mAngle = angle;
1435  mSize = pointSize;
1437  mOffset = QPointF( 0, 0 );
1439 }
1440 
1442 {
1444  QChar chr = DEFAULT_FONTMARKER_CHR;
1445  double pointSize = DEFAULT_FONTMARKER_SIZE;
1448 
1449  if ( props.contains( "font" ) )
1450  fontFamily = props["font"];
1451  if ( props.contains( "chr" ) && props["chr"].length() > 0 )
1452  chr = props["chr"].at( 0 );
1453  if ( props.contains( "size" ) )
1454  pointSize = props["size"].toDouble();
1455  if ( props.contains( "color" ) )
1456  color = QgsSymbolLayerV2Utils::decodeColor( props["color"] );
1457  if ( props.contains( "angle" ) )
1458  angle = props["angle"].toDouble();
1459 
1460  QgsFontMarkerSymbolLayerV2* m = new QgsFontMarkerSymbolLayerV2( fontFamily, chr, pointSize, color, angle );
1461  if ( props.contains( "offset" ) )
1462  m->setOffset( QgsSymbolLayerV2Utils::decodePoint( props["offset"] ) );
1463  if ( props.contains( "offset_unit" ) )
1464  m->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit" ] ) );
1465  if ( props.contains( "size_unit" ) )
1466  m->setSizeUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["size_unit"] ) );
1467  if ( props.contains( "horizontal_anchor_point" ) )
1468  {
1469  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayerV2::HorizontalAnchorPoint( props[ "horizontal_anchor_point" ].toInt() ) );
1470  }
1471  if ( props.contains( "vertical_anchor_point" ) )
1472  {
1473  m->setVerticalAnchorPoint( QgsMarkerSymbolLayerV2::VerticalAnchorPoint( props[ "vertical_anchor_point" ].toInt() ) );
1474  }
1475  return m;
1476 }
1477 
1479 {
1480  return "FontMarker";
1481 }
1482 
1484 {
1485  mFont = QFont( mFontFamily );
1487  QFontMetrics fm( mFont );
1488  mChrOffset = QPointF( fm.width( mChr ) / 2, -fm.ascent() / 2 );
1489 
1490  mOrigSize = mSize; // save in case the size would be data defined
1491 }
1492 
1494 {
1495  Q_UNUSED( context );
1496 }
1497 
1499 {
1500  QPainter *p = context.renderContext().painter();
1501  if ( !p )
1502  return;
1503 
1504  QColor penColor = context.selected() ? context.renderContext().selectionColor() : mColor;
1505  penColor.setAlphaF( mColor.alphaF() * context.alpha() );
1506  p->setPen( penColor );
1507  p->setFont( mFont );
1508 
1509  p->save();
1510  //offset
1511  double offsetX = 0;
1512  double offsetY = 0;
1513  markerOffset( context, offsetX, offsetY );
1514  QPointF outputOffset( offsetX, offsetY );
1515  if ( mAngle )
1516  outputOffset = _rotatedOffset( outputOffset, mAngle );
1517  p->translate( point + outputOffset );
1518 
1520  {
1521  double s = mSize / mOrigSize;
1522  p->scale( s, s );
1523  }
1524 
1525  if ( mAngle != 0 )
1526  p->rotate( mAngle );
1527 
1528  p->drawText( -mChrOffset, mChr );
1529  p->restore();
1530 }
1531 
1533 {
1534  QgsStringMap props;
1535  props["font"] = mFontFamily;
1536  props["chr"] = mChr;
1537  props["size"] = QString::number( mSize );
1538  props["size_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mSizeUnit );
1539  props["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
1540  props["angle"] = QString::number( mAngle );
1541  props["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
1542  props["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit );
1543  props["horizontal_anchor_point"] = QString::number( mHorizontalAnchorPoint );
1544  props["vertical_anchor_point"] = QString::number( mVerticalAnchorPoint );
1545  return props;
1546 }
1547 
1549 {
1551  m->setOffset( mOffset );
1552  m->setOffsetUnit( mOffsetUnit );
1553  m->setSizeUnit( mSizeUnit );
1556  return m;
1557 }
1558 
1559 void QgsFontMarkerSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
1560 {
1561  // <Graphic>
1562  QDomElement graphicElem = doc.createElement( "se:Graphic" );
1563  element.appendChild( graphicElem );
1564 
1565  QString fontPath = QString( "ttf://%1" ).arg( mFontFamily );
1566  int markIndex = mChr.unicode();
1567  QgsSymbolLayerV2Utils::externalMarkerToSld( doc, graphicElem, fontPath, "ttf", &markIndex, mColor, mSize );
1568 
1569  // <Rotation>
1570  QString angleFunc;
1571  bool ok;
1572  double angle = props.value( "angle", "0" ).toDouble( &ok );
1573  if ( !ok )
1574  {
1575  angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mAngle );
1576  }
1577  else if ( angle + mAngle != 0 )
1578  {
1579  angleFunc = QString::number( angle + mAngle );
1580  }
1581  QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
1582 
1583  // <Displacement>
1585 }
1586 
1588 {
1589  QgsDebugMsg( "Entered." );
1590 
1591  QDomElement graphicElem = element.firstChildElement( "Graphic" );
1592  if ( graphicElem.isNull() )
1593  return NULL;
1594 
1595  QString name, format;
1596  QColor color;
1597  double size;
1598  int chr;
1599 
1600  if ( !QgsSymbolLayerV2Utils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
1601  return NULL;
1602 
1603  if ( !name.startsWith( "ttf://" ) || format != "ttf" )
1604  return NULL;
1605 
1606  QString fontFamily = name.mid( 6 );
1607 
1608  double angle = 0.0;
1609  QString angleFunc;
1610  if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
1611  {
1612  bool ok;
1613  double d = angleFunc.toDouble( &ok );
1614  if ( ok )
1615  angle = d;
1616  }
1617 
1618  QPointF offset;
1620 
1621  QgsMarkerSymbolLayerV2 *m = new QgsFontMarkerSymbolLayerV2( fontFamily, chr, size, color );
1622  m->setAngle( angle );
1623  m->setOffset( offset );
1624  return m;
1625 }