QGIS API Documentation  2.8.2-Wien
 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 
35 Q_GUI_EXPORT extern int qt_defaultDpiX();
36 Q_GUI_EXPORT extern int qt_defaultDpiY();
37 
38 static void _fixQPictureDPI( QPainter* p )
39 {
40  // QPicture makes an assumption that we drawing to it with system DPI.
41  // Then when being drawn, it scales the painter. The following call
42  // negates the effect. There is no way of setting QPicture's DPI.
43  // See QTBUG-20361
44  p->scale(( double )qt_defaultDpiX() / p->device()->logicalDpiX(),
45  ( double )qt_defaultDpiY() / p->device()->logicalDpiY() );
46 }
47 
48 const QString QgsSimpleMarkerSymbolLayerV2::EXPR_SIZE( "size" );
49 
51 
52 QgsSimpleMarkerSymbolLayerV2::QgsSimpleMarkerSymbolLayerV2( QString name, QColor color, QColor borderColor, double size, double angle, QgsSymbolV2::ScaleMethod scaleMethod )
53  : mOutlineStyle( Qt::SolidLine ), mOutlineWidth( 0 ), mOutlineWidthUnit( QgsSymbolV2::MM )
54 {
55  mName = name;
56  mColor = color;
58  mSize = size;
59  mAngle = angle;
60  mOffset = QPointF( 0, 0 );
64  mAngleExpression = NULL;
65  mNameExpression = NULL;
66  mUsingCache = false;
67 }
68 
70 {
77 
78  if ( props.contains( "name" ) )
79  name = props["name"];
80  if ( props.contains( "color" ) )
81  color = QgsSymbolLayerV2Utils::decodeColor( props["color"] );
82  if ( props.contains( "color_border" ) )
83  {
84  //pre 2.5 projects use "color_border"
85  borderColor = QgsSymbolLayerV2Utils::decodeColor( props["color_border"] );
86  }
87  else if ( props.contains( "outline_color" ) )
88  {
89  borderColor = QgsSymbolLayerV2Utils::decodeColor( props["outline_color"] );
90  }
91  else if ( props.contains( "line_color" ) )
92  {
93  borderColor = QgsSymbolLayerV2Utils::decodeColor( props["line_color"] );
94  }
95  if ( props.contains( "size" ) )
96  size = props["size"].toDouble();
97  if ( props.contains( "angle" ) )
98  angle = props["angle"].toDouble();
99  if ( props.contains( "scale_method" ) )
100  scaleMethod = QgsSymbolLayerV2Utils::decodeScaleMethod( props["scale_method"] );
101 
102  QgsSimpleMarkerSymbolLayerV2* m = new QgsSimpleMarkerSymbolLayerV2( name, color, borderColor, size, angle, scaleMethod );
103  if ( props.contains( "offset" ) )
104  m->setOffset( QgsSymbolLayerV2Utils::decodePoint( props["offset"] ) );
105  if ( props.contains( "offset_unit" ) )
106  m->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
107  if ( props.contains( "offset_map_unit_scale" ) )
108  m->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_map_unit_scale"] ) );
109  if ( props.contains( "size_unit" ) )
110  m->setSizeUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["size_unit"] ) );
111  if ( props.contains( "size_map_unit_scale" ) )
112  m->setSizeMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["size_map_unit_scale"] ) );
113 
114  if ( props.contains( "outline_style" ) )
115  {
116  m->setOutlineStyle( QgsSymbolLayerV2Utils::decodePenStyle( props["outline_style"] ) );
117  }
118  else if ( props.contains( "line_style" ) )
119  {
120  m->setOutlineStyle( QgsSymbolLayerV2Utils::decodePenStyle( props["line_style"] ) );
121  }
122  if ( props.contains( "outline_width" ) )
123  {
124  m->setOutlineWidth( props["outline_width"].toDouble() );
125  }
126  else if ( props.contains( "line_width" ) )
127  {
128  m->setOutlineWidth( props["line_width"].toDouble() );
129  }
130  if ( props.contains( "outline_width_unit" ) )
131  {
132  m->setOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["outline_width_unit"] ) );
133  }
134  if ( props.contains( "line_width_unit" ) )
135  {
136  m->setOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["line_width_unit"] ) );
137  }
138  if ( props.contains( "outline_width_map_unit_scale" ) )
139  {
140  m->setOutlineWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["outline_width_map_unit_scale"] ) );
141  }
142 
143  if ( props.contains( "horizontal_anchor_point" ) )
144  {
145  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayerV2::HorizontalAnchorPoint( props[ "horizontal_anchor_point" ].toInt() ) );
146  }
147  if ( props.contains( "vertical_anchor_point" ) )
148  {
149  m->setVerticalAnchorPoint( QgsMarkerSymbolLayerV2::VerticalAnchorPoint( props[ "vertical_anchor_point" ].toInt() ) );
150  }
151 
152  //data defined properties
153  if ( props.contains( "name_expression" ) )
154  {
155  m->setDataDefinedProperty( "name", props["name_expression"] );
156  }
157  if ( props.contains( "color_expression" ) )
158  {
159  m->setDataDefinedProperty( "color", props["color_expression"] );
160  }
161  if ( props.contains( "color_border_expression" ) )
162  {
163  m->setDataDefinedProperty( "color_border", props["color_border_expression"] );
164  }
165  if ( props.contains( "outline_style_expression" ) )
166  {
167  m->setDataDefinedProperty( "outline_style", props["outline_style_expression"] );
168  }
169  if ( props.contains( "outline_width_expression" ) )
170  {
171  m->setDataDefinedProperty( "outline_width", props["outline_width_expression"] );
172  }
173  if ( props.contains( "size_expression" ) )
174  {
175  m->setDataDefinedProperty( "size", props["size_expression"] );
176  }
177  if ( props.contains( "angle_expression" ) )
178  {
179  m->setDataDefinedProperty( "angle", props["angle_expression"] );
180  }
181  if ( props.contains( "offset_expression" ) )
182  {
183  m->setDataDefinedProperty( "offset", props["offset_expression"] );
184  }
185  if ( props.contains( "horizontal_anchor_point_expression" ) )
186  {
187  m->setDataDefinedProperty( "horizontal_anchor_point", props[ "horizontal_anchor_point_expression" ] );
188  }
189  if ( props.contains( "vertical_anchor_point_expression" ) )
190  {
191  m->setDataDefinedProperty( "vertical_anchor_point", props[ "vertical_anchor_point_expression" ] );
192  }
193  return m;
194 }
195 
196 
198 {
199  return "SimpleMarker";
200 }
201 
203 {
204  QColor brushColor = mColor;
205  QColor penColor = mBorderColor;
206 
207  brushColor.setAlphaF( mColor.alphaF() * context.alpha() );
208  penColor.setAlphaF( mBorderColor.alphaF() * context.alpha() );
209 
210  mBrush = QBrush( brushColor );
211  mPen = QPen( penColor );
212  mPen.setStyle( mOutlineStyle );
214 
215  QColor selBrushColor = context.renderContext().selectionColor();
216  QColor selPenColor = selBrushColor == mColor ? selBrushColor : mBorderColor;
217  if ( context.alpha() < 1 )
218  {
219  selBrushColor.setAlphaF( context.alpha() );
220  selPenColor.setAlphaF( context.alpha() );
221  }
222  mSelBrush = QBrush( selBrushColor );
223  mSelPen = QPen( selPenColor );
224  mSelPen.setStyle( mOutlineStyle );
226 
227  bool hasDataDefinedRotation = context.renderHints() & QgsSymbolV2::DataDefinedRotation || dataDefinedProperty( "angle" );
228  bool hasDataDefinedSize = context.renderHints() & QgsSymbolV2::DataDefinedSizeScale || dataDefinedProperty( "size" );
229 
230  // use caching only when:
231  // - size, rotation, shape, color, border color is not data-defined
232  // - drawing to screen (not printer)
233  mUsingCache = !hasDataDefinedRotation && !hasDataDefinedSize && !context.renderContext().forceVectorOutput()
234  && !dataDefinedProperty( "name" ) && !dataDefinedProperty( "color" ) && !dataDefinedProperty( "color_border" )
235  && !dataDefinedProperty( "outline_width" ) && !dataDefinedProperty( "outline_style" ) &&
236  !dataDefinedProperty( "size" );
237 
238  // use either QPolygonF or QPainterPath for drawing
239  // TODO: find out whether drawing directly doesn't bring overhead - if not, use it for all shapes
240  if ( !prepareShape() ) // drawing as a polygon
241  {
242  if ( preparePath() ) // drawing as a painter path
243  {
244  // some markers can't be drawn as a polygon (circle, cross)
245  // For these set the selected border color to the selected color
246 
247  if ( mName != "circle" )
248  mSelPen.setColor( selBrushColor );
249  }
250  else
251  {
252  QgsDebugMsg( "unknown symbol" );
253  return;
254  }
255  }
256 
257  QMatrix transform;
258 
259  // scale the shape (if the size is not going to be modified)
260  if ( !hasDataDefinedSize )
261  {
263  if ( mUsingCache )
264  scaledSize *= context.renderContext().rasterScaleFactor();
265  double half = scaledSize / 2.0;
266  transform.scale( half, half );
267  }
268 
269  // rotate if the rotation is not going to be changed during the rendering
270  if ( !hasDataDefinedRotation && mAngle != 0 )
271  {
272  transform.rotate( mAngle );
273  }
274 
275  if ( !mPolygon.isEmpty() )
276  mPolygon = transform.map( mPolygon );
277  else
278  mPath = transform.map( mPath );
279 
280  if ( mUsingCache )
281  {
282  if ( !prepareCache( context ) )
283  {
284  mUsingCache = false;
285  }
286  }
287  else
288  {
289  mCache = QImage();
290  mSelCache = QImage();
291  }
292 
293  prepareExpressions( context.fields(), context.renderContext().rendererScale() );
294  mAngleExpression = expression( "angle" );
295  mNameExpression = expression( "name" );
296 
298 }
299 
300 
302 {
304 
305  // calculate necessary image size for the cache
306  double pw = (( mPen.widthF() == 0 ? 1 : mPen.widthF() ) + 1 ) / 2 * 2; // make even (round up); handle cosmetic pen
307  int imageSize = (( int ) scaledSize + pw ) / 2 * 2 + 1; // make image width, height odd; account for pen width
308  double center = imageSize / 2.0;
309 
310  if ( imageSize > mMaximumCacheWidth )
311  {
312  return false;
313  }
314 
315  mCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
316  mCache.fill( 0 );
317 
318  QPainter p;
319  p.begin( &mCache );
320  p.setRenderHint( QPainter::Antialiasing );
321  p.setBrush( mBrush );
322  p.setPen( mPen );
323  p.translate( QPointF( center, center ) );
324  drawMarker( &p, context );
325  p.end();
326 
327  // Construct the selected version of the Cache
328 
329  QColor selColor = context.renderContext().selectionColor();
330 
331  mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
332  mSelCache.fill( 0 );
333 
334  p.begin( &mSelCache );
335  p.setRenderHint( QPainter::Antialiasing );
336  p.setBrush( mSelBrush );
337  p.setPen( mSelPen );
338  p.translate( QPointF( center, center ) );
339  drawMarker( &p, context );
340  p.end();
341 
342  // Check that the selected version is different. If not, then re-render,
343  // filling the background with the selection color and using the normal
344  // colors for the symbol .. could be ugly!
345 
346  if ( mSelCache == mCache )
347  {
348  p.begin( &mSelCache );
349  p.setRenderHint( QPainter::Antialiasing );
350  p.fillRect( 0, 0, imageSize, imageSize, selColor );
351  p.setBrush( mBrush );
352  p.setPen( mPen );
353  p.translate( QPointF( center, center ) );
354  drawMarker( &p, context );
355  p.end();
356  }
357 
358  return true;
359 }
360 
362 {
363  Q_UNUSED( context );
364 }
365 
367 {
368  mPolygon.clear();
369 
370  if ( name.isNull() )
371  {
372  name = mName;
373  }
374 
375  if ( name == "square" || name == "rectangle" )
376  {
377  mPolygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 1, 1 ) ) );
378  return true;
379  }
380  else if ( name == "diamond" )
381  {
382  mPolygon << QPointF( -1, 0 ) << QPointF( 0, 1 )
383  << QPointF( 1, 0 ) << QPointF( 0, -1 );
384  return true;
385  }
386  else if ( name == "pentagon" )
387  {
388  mPolygon << QPointF( sin( DEG2RAD( 288.0 ) ), - cos( DEG2RAD( 288.0 ) ) )
389  << QPointF( sin( DEG2RAD( 216.0 ) ), - cos( DEG2RAD( 216.0 ) ) )
390  << QPointF( sin( DEG2RAD( 144.0 ) ), - cos( DEG2RAD( 144.0 ) ) )
391  << QPointF( sin( DEG2RAD( 72.0 ) ), - cos( DEG2RAD( 72.0 ) ) )
392  << QPointF( 0, -1 );
393  return true;
394  }
395  else if ( name == "triangle" )
396  {
397  mPolygon << QPointF( -1, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 );
398  return true;
399  }
400  else if ( name == "equilateral_triangle" )
401  {
402  mPolygon << QPointF( sin( DEG2RAD( 240.0 ) ), - cos( DEG2RAD( 240.0 ) ) )
403  << QPointF( sin( DEG2RAD( 120.0 ) ), - cos( DEG2RAD( 120.0 ) ) )
404  << QPointF( 0, -1 );
405  return true;
406  }
407  else if ( name == "star" )
408  {
409  double sixth = 1.0 / 3;
410 
411  mPolygon << QPointF( 0, -1 )
412  << QPointF( -sixth, -sixth )
413  << QPointF( -1, -sixth )
414  << QPointF( -sixth, 0 )
415  << QPointF( -1, 1 )
416  << QPointF( 0, + sixth )
417  << QPointF( 1, 1 )
418  << QPointF( + sixth, 0 )
419  << QPointF( 1, -sixth )
420  << QPointF( + sixth, -sixth );
421  return true;
422  }
423  else if ( name == "regular_star" )
424  {
425  double inner_r = cos( DEG2RAD( 72.0 ) ) / cos( DEG2RAD( 36.0 ) );
426 
427  mPolygon << QPointF( inner_r * sin( DEG2RAD( 324.0 ) ), - inner_r * cos( DEG2RAD( 324.0 ) ) ) // 324
428  << QPointF( sin( DEG2RAD( 288.0 ) ), - cos( DEG2RAD( 288 ) ) ) // 288
429  << QPointF( inner_r * sin( DEG2RAD( 252.0 ) ), - inner_r * cos( DEG2RAD( 252.0 ) ) ) // 252
430  << QPointF( sin( DEG2RAD( 216.0 ) ), - cos( DEG2RAD( 216.0 ) ) ) // 216
431  << QPointF( 0, inner_r ) // 180
432  << QPointF( sin( DEG2RAD( 144.0 ) ), - cos( DEG2RAD( 144.0 ) ) ) // 144
433  << QPointF( inner_r * sin( DEG2RAD( 108.0 ) ), - inner_r * cos( DEG2RAD( 108.0 ) ) ) // 108
434  << QPointF( sin( DEG2RAD( 72.0 ) ), - cos( DEG2RAD( 72.0 ) ) ) // 72
435  << QPointF( inner_r * sin( DEG2RAD( 36.0 ) ), - inner_r * cos( DEG2RAD( 36.0 ) ) ) // 36
436  << QPointF( 0, -1 ); // 0
437  return true;
438  }
439  else if ( name == "arrow" )
440  {
441  mPolygon
442  << QPointF( 0, -1 )
443  << QPointF( 0.5, -0.5 )
444  << QPointF( 0.25, -0.5 )
445  << QPointF( 0.25, 1 )
446  << QPointF( -0.25, 1 )
447  << QPointF( -0.25, -0.5 )
448  << QPointF( -0.5, -0.5 );
449  return true;
450  }
451  else if ( name == "filled_arrowhead" )
452  {
453  mPolygon << QPointF( 0, 0 ) << QPointF( -1, 1 ) << QPointF( -1, -1 );
454  return true;
455  }
456 
457  return false;
458 }
459 
461 {
462  mPath = QPainterPath();
463  if ( name.isNull() )
464  {
465  name = mName;
466  }
467 
468  if ( name == "circle" )
469  {
470  mPath.addEllipse( QRectF( -1, -1, 2, 2 ) ); // x,y,w,h
471  return true;
472  }
473  else if ( name == "cross" )
474  {
475  mPath.moveTo( -1, 0 );
476  mPath.lineTo( 1, 0 ); // horizontal
477  mPath.moveTo( 0, -1 );
478  mPath.lineTo( 0, 1 ); // vertical
479  return true;
480  }
481  else if ( name == "x" || name == "cross2" )
482  {
483  mPath.moveTo( -1, -1 );
484  mPath.lineTo( 1, 1 );
485  mPath.moveTo( 1, -1 );
486  mPath.lineTo( -1, 1 );
487  return true;
488  }
489  else if ( name == "line" )
490  {
491  mPath.moveTo( 0, -1 );
492  mPath.lineTo( 0, 1 ); // vertical line
493  return true;
494  }
495  else if ( name == "arrowhead" )
496  {
497  mPath.moveTo( 0, 0 );
498  mPath.lineTo( -1, -1 );
499  mPath.moveTo( 0, 0 );
500  mPath.lineTo( -1, 1 );
501  return true;
502  }
503 
504  return false;
505 }
506 
508 {
509  QPainter *p = context.renderContext().painter();
510  if ( !p )
511  {
512  return;
513  }
514 
515  QgsExpression *sizeExpression = expression( EXPR_SIZE );
516  bool hasDataDefinedSize = context.renderHints() & QgsSymbolV2::DataDefinedSizeScale || sizeExpression;
517 
518  double scaledSize = mSize;
519  if ( hasDataDefinedSize )
520  {
521  if ( sizeExpression )
522  {
523  scaledSize = sizeExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
524  }
525 
527  {
528  scaledSize = sqrt( scaledSize );
529  }
530  }
531 
532  //offset
533  double offsetX = 0;
534  double offsetY = 0;
535  markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
536  QPointF off( offsetX, offsetY );
537 
538  //angle
539  double angle = mAngle;
540  if ( mAngleExpression )
541  {
542  angle = mAngleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
543  }
544 
545  bool hasDataDefinedRotation = context.renderHints() & QgsSymbolV2::DataDefinedRotation || mAngleExpression;
546  if ( hasDataDefinedRotation )
547  {
548  // For non-point markers, "dataDefinedRotation" means following the
549  // shape (shape-data defined). For them, "field-data defined" does
550  // not work at all. TODO: if "field-data defined" ever gets implemented
551  // we'll need a way to distinguish here between the two, possibly
552  // using another flag in renderHints()
553  const QgsFeature* f = context.feature();
554  if ( f )
555  {
556  QgsGeometry *g = f->geometry();
557  if ( g && g->type() == QGis::Point )
558  {
559  const QgsMapToPixel& m2p = context.renderContext().mapToPixel();
560  angle += m2p.mapRotation();
561  }
562  }
563  }
564 
565  if ( angle )
566  off = _rotatedOffset( off, angle );
567 
568  //data defined shape?
569  bool createdNewPath = false;
570  if ( mNameExpression )
571  {
572  QString name = mNameExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
573  if ( !prepareShape( name ) ) // drawing as a polygon
574  {
575  preparePath( name ); // drawing as a painter path
576  }
577  createdNewPath = true;
578  }
579 
580  if ( mUsingCache )
581  {
582  //QgsDebugMsg( QString("XXX using cache") );
583  // we will use cached image
584  QImage &img = context.selected() ? mSelCache : mCache;
585  double s = img.width() / context.renderContext().rasterScaleFactor();
586  p->drawImage( QRectF( point.x() - s / 2.0 + off.x(),
587  point.y() - s / 2.0 + off.y(),
588  s, s ), img );
589  }
590  else
591  {
592  QMatrix transform;
593 
594  // move to the desired position
595  transform.translate( point.x() + off.x(), point.y() + off.y() );
596 
597  // resize if necessary
598  if ( hasDataDefinedSize || createdNewPath )
599  {
601  double half = s / 2.0;
602  transform.scale( half, half );
603  }
604 
605  if ( angle != 0 && ( hasDataDefinedRotation || createdNewPath ) )
606  transform.rotate( angle );
607 
608  QgsExpression* colorExpression = expression( "color" );
609  QgsExpression* colorBorderExpression = expression( "color_border" );
610  QgsExpression* outlineWidthExpression = expression( "outline_width" );
611  QgsExpression* outlineStyleExpression = expression( "outline_style" );
612  if ( colorExpression )
613  {
614  mBrush.setColor( QgsSymbolLayerV2Utils::decodeColor( colorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) );
615  }
616  if ( colorBorderExpression )
617  {
618  mPen.setColor( QgsSymbolLayerV2Utils::decodeColor( colorBorderExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) );
619  mSelPen.setColor( QgsSymbolLayerV2Utils::decodeColor( colorBorderExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) );
620  }
621  if ( outlineWidthExpression )
622  {
623  double outlineWidth = outlineWidthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
626  }
627  if ( outlineStyleExpression )
628  {
629  QString outlineStyle = outlineStyleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
630  mPen.setStyle( QgsSymbolLayerV2Utils::decodePenStyle( outlineStyle ) );
631  mSelPen.setStyle( QgsSymbolLayerV2Utils::decodePenStyle( outlineStyle ) );
632  }
633 
634  p->setBrush( context.selected() ? mSelBrush : mBrush );
635  p->setPen( context.selected() ? mSelPen : mPen );
636 
637  if ( !mPolygon.isEmpty() )
638  p->drawPolygon( transform.map( mPolygon ) );
639  else
640  p->drawPath( transform.map( mPath ) );
641  }
642 }
643 
644 
646 {
647  QgsStringMap map;
648  map["name"] = mName;
649  map["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
650  map["outline_color"] = QgsSymbolLayerV2Utils::encodeColor( mBorderColor );
651  map["size"] = QString::number( mSize );
653  map["size_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mSizeMapUnitScale );
654  map["angle"] = QString::number( mAngle );
655  map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
657  map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
659  map["outline_style"] = QgsSymbolLayerV2Utils::encodePenStyle( mOutlineStyle );
660  map["outline_width"] = QString::number( mOutlineWidth );
661  map["outline_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOutlineWidthUnit );
662  map["outline_width_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOutlineWidthMapUnitScale );
663  map["horizontal_anchor_point"] = QString::number( mHorizontalAnchorPoint );
664  map["vertical_anchor_point"] = QString::number( mVerticalAnchorPoint );
665 
666 
667  //data define properties
669  return map;
670 }
671 
673 {
675  m->setOffset( mOffset );
676  m->setSizeUnit( mSizeUnit );
687  return m;
688 }
689 
690 void QgsSimpleMarkerSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
691 {
692  // <Graphic>
693  QDomElement graphicElem = doc.createElement( "se:Graphic" );
694  element.appendChild( graphicElem );
695 
697 
698  // <Rotation>
699  QString angleFunc;
700  bool ok;
701  double angle = props.value( "angle", "0" ).toDouble( &ok );
702  if ( !ok )
703  {
704  angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mAngle );
705  }
706  else if ( angle + mAngle != 0 )
707  {
708  angleFunc = QString::number( angle + mAngle );
709  }
710  QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
711 
712  // <Displacement>
714 }
715 
716 QString QgsSimpleMarkerSymbolLayerV2::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
717 {
718  Q_UNUSED( mmScaleFactor );
719  Q_UNUSED( mapUnitScaleFactor );
720 #if 0
721  QString ogrType = "3"; //default is circle
722  if ( mName == "square" )
723  {
724  ogrType = "5";
725  }
726  else if ( mName == "triangle" )
727  {
728  ogrType = "7";
729  }
730  else if ( mName == "star" )
731  {
732  ogrType = "9";
733  }
734  else if ( mName == "circle" )
735  {
736  ogrType = "3";
737  }
738  else if ( mName == "cross" )
739  {
740  ogrType = "0";
741  }
742  else if ( mName == "x" || mName == "cross2" )
743  {
744  ogrType = "1";
745  }
746  else if ( mName == "line" )
747  {
748  ogrType = "10";
749  }
750 
751  QString ogrString;
752  ogrString.append( "SYMBOL(" );
753  ogrString.append( "id:" );
754  ogrString.append( "\"" );
755  ogrString.append( "ogr-sym-" );
756  ogrString.append( ogrType );
757  ogrString.append( "\"" );
758  ogrString.append( ",c:" );
759  ogrString.append( mColor.name() );
760  ogrString.append( ",o:" );
761  ogrString.append( mBorderColor.name() );
762  ogrString.append( QString( ",s:%1mm" ).arg( mSize ) );
763  ogrString.append( ")" );
764  return ogrString;
765 #endif //0
766 
767  QString ogrString;
768  ogrString.append( "PEN(" );
769  ogrString.append( "c:" );
770  ogrString.append( mColor.name() );
771  ogrString.append( ",w:" );
772  ogrString.append( QString::number( mSize ) );
773  ogrString.append( "mm" );
774  ogrString.append( ")" );
775  return ogrString;
776 }
777 
779 {
780  QgsDebugMsg( "Entered." );
781 
782  QDomElement graphicElem = element.firstChildElement( "Graphic" );
783  if ( graphicElem.isNull() )
784  return NULL;
785 
786  QString name = "square";
787  QColor color, borderColor;
788  double borderWidth, size;
789  Qt::PenStyle borderStyle;
790 
791  if ( !QgsSymbolLayerV2Utils::wellKnownMarkerFromSld( graphicElem, name, color, borderColor, borderStyle, borderWidth, size ) )
792  return NULL;
793 
794  double angle = 0.0;
795  QString angleFunc;
796  if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
797  {
798  bool ok;
799  double d = angleFunc.toDouble( &ok );
800  if ( ok )
801  angle = d;
802  }
803 
804  QPointF offset;
806 
807  QgsSimpleMarkerSymbolLayerV2 *m = new QgsSimpleMarkerSymbolLayerV2( name, color, borderColor, size );
808  m->setAngle( angle );
809  m->setOffset( offset );
810  m->setOutlineStyle( borderStyle );
811  return m;
812 }
813 
815 {
816  Q_UNUSED( context );
817 
818  if ( mPolygon.count() != 0 )
819  {
820  p->drawPolygon( mPolygon );
821  }
822  else
823  {
824  p->drawPath( mPath );
825  }
826 }
827 
828 bool QgsSimpleMarkerSymbolLayerV2::writeDxf( QgsDxfExport& e, double mmMapUnitScaleFactor, const QString& layerName, const QgsSymbolV2RenderContext* context, const QgsFeature* f, const QPointF& shift ) const
829 {
830  //data defined size?
831  double size = mSize;
832 
833  QgsExpression *sizeExpression = expression( "size" );
834  bool hasDataDefinedSize = false;
835  if ( context )
836  {
837  hasDataDefinedSize = context->renderHints() & QgsSymbolV2::DataDefinedSizeScale || sizeExpression;
838  }
839 
840  //data defined size
841  if ( hasDataDefinedSize )
842  {
843  if ( sizeExpression )
844  {
845  size = sizeExpression->evaluate( const_cast<QgsFeature*>( context->feature() ) ).toDouble();
846  }
847 
848  switch ( mScaleMethod )
849  {
851  size = sqrt( size );
852  break;
854  break;
855  }
856 
858  }
859  if ( mSizeUnit == QgsSymbolV2::MM )
860  {
861  size *= mmMapUnitScaleFactor;
862  }
863  double halfSize = size / 2.0;
864 
865  //outlineWidth
866  double outlineWidth = mOutlineWidth;
867  QgsExpression* outlineWidthExpression = expression( "outline_width" );
868  if ( context && outlineWidthExpression )
869  {
870  outlineWidth = outlineWidthExpression->evaluate( const_cast<QgsFeature*>( context->feature() ) ).toDouble();
871  }
872  if ( mSizeUnit == QgsSymbolV2::MM )
873  {
874  outlineWidth *= mmMapUnitScaleFactor;
875  }
876 
877  //color
878  QColor pc = mPen.color();
879  QColor bc = mBrush.color();
880 
881  QgsExpression* colorExpression = expression( "color" );
882  if ( colorExpression )
883  {
884  bc = QgsSymbolLayerV2Utils::decodeColor( colorExpression->evaluate( *f ).toString() );
885  }
886 
887  QgsExpression* outlinecolorExpression = expression( "color_border" );
888  if ( outlinecolorExpression )
889  {
890  pc = QgsSymbolLayerV2Utils::decodeColor( outlinecolorExpression->evaluate( *f ).toString() );
891  }
892 
893  //offset
894  double offsetX = 0;
895  double offsetY = 0;
896  if ( context )
897  {
898  markerOffset( *context, offsetX, offsetY );
899  }
900  QPointF off( offsetX, offsetY );
901 
902  //angle
903  double angle = mAngle;
904  QgsExpression* angleExpression = expression( "angle" );
905  if ( context && angleExpression )
906  {
907  angle = angleExpression->evaluate( const_cast<QgsFeature*>( context->feature() ) ).toDouble();
908  }
909  angle = -angle; //rotation in Qt is counterclockwise
910  if ( angle )
911  off = _rotatedOffset( off, angle );
912 
913  if ( mSizeUnit == QgsSymbolV2::MM )
914  {
915  off *= mmMapUnitScaleFactor;
916  }
917 
918  QTransform t;
919  t.translate( shift.x() + offsetX, shift.y() + offsetY );
920 
921  if ( angle != 0 )
922  t.rotate( angle );
923 
924  //data defined symbol name
925 
926  if ( mName == "circle" )
927  {
928  if ( mBrush.style() != Qt::NoBrush )
929  e.writeFilledCircle( layerName, bc, shift, halfSize );
930  if ( mPen.style() != Qt::NoPen )
931  e.writeCircle( layerName, pc, shift, halfSize, "CONTINUOUS", outlineWidth );
932  }
933  else if ( mName == "square" || mName == "rectangle" )
934  {
935  // pt1 pt2
936  // pt3 pt4
937  QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
938  QPointF pt2 = t.map( QPointF( halfSize, -halfSize ) );
939  QPointF pt3 = t.map( QPointF( -halfSize, halfSize ) );
940  QPointF pt4 = t.map( QPointF( halfSize, halfSize ) );
941 
942  if ( mBrush.style() != Qt::NoBrush )
943  e.writeSolid( layerName, bc, pt1, pt2, pt3, pt4 );
944 
945  if ( mPen.style() != Qt::NoPen )
946  {
947  e.writeLine( pt1, pt2, layerName, "CONTINUOUS", pc, outlineWidth );
948  e.writeLine( pt2, pt4, layerName, "CONTINUOUS", pc, outlineWidth );
949  e.writeLine( pt4, pt3, layerName, "CONTINUOUS", pc, outlineWidth );
950  e.writeLine( pt3, pt1, layerName, "CONTINUOUS", pc, outlineWidth );
951  }
952  }
953  else if ( mName == "diamond" )
954  {
955  QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
956  QPointF pt2 = t.map( QPointF( 0, -halfSize ) );
957  QPointF pt3 = t.map( QPointF( 0, halfSize ) );
958  QPointF pt4 = t.map( QPointF( halfSize, 0 ) );
959 
960  if ( mBrush.style() != Qt::NoBrush )
961  e.writeSolid( layerName, bc, pt1, pt2, pt3, pt4 );
962 
963  if ( mPen.style() != Qt::NoPen )
964  {
965  e.writeLine( pt1, pt2, layerName, "CONTINUOUS", pc, outlineWidth );
966  e.writeLine( pt2, pt3, layerName, "CONTINUOUS", pc, outlineWidth );
967  e.writeLine( pt3, pt4, layerName, "CONTINUOUS", pc, outlineWidth );
968  e.writeLine( pt4, pt1, layerName, "CONTINUOUS", pc, outlineWidth );
969  }
970  }
971  else if ( mName == "triangle" )
972  {
973  QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
974  QPointF pt2 = t.map( QPointF( halfSize, -halfSize ) );
975  QPointF pt3 = t.map( QPointF( 0, halfSize ) );
976 
977  if ( mBrush.style() != Qt::NoBrush )
978  e.writeSolid( layerName, bc, pt1, pt2, pt3, pt3 );
979 
980  if ( mPen.style() != Qt::NoPen )
981  {
982  e.writeLine( pt1, pt2, layerName, "CONTINUOUS", pc, outlineWidth );
983  e.writeLine( pt2, pt3, layerName, "CONTINUOUS", pc, outlineWidth );
984  e.writeLine( pt3, pt1, layerName, "CONTINUOUS", pc, outlineWidth );
985  }
986  }
987 #if 0
988  else if ( mName == "equilateral_triangle" )
989  {
990 
991  }
992 #endif
993  else if ( mName == "line" )
994  {
995  QPointF pt1 = t.map( QPointF( 0, halfSize ) );
996  QPointF pt2 = t.map( QPointF( 0, -halfSize ) );
997 
998  if ( mPen.style() != Qt::NoPen )
999  e.writeLine( pt1, pt2, layerName, "CONTINUOUS", pc, outlineWidth );
1000  }
1001  else if ( mName == "cross" )
1002  {
1003  QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
1004  QPointF pt2 = t.map( QPointF( halfSize, 0 ) );
1005  QPointF pt3 = t.map( QPointF( 0, -halfSize ) );
1006  QPointF pt4 = t.map( QPointF( 0, halfSize ) );
1007 
1008  if ( mPen.style() != Qt::NoPen )
1009  {
1010  e.writeLine( pt1, pt2, layerName, "CONTINUOUS", pc, outlineWidth );
1011  e.writeLine( pt3, pt4, layerName, "CONTINUOUS", pc, outlineWidth );
1012  }
1013  }
1014  else if ( mName == "x" || mName == "cross2" )
1015  {
1016  QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
1017  QPointF pt2 = t.map( QPointF( halfSize, halfSize ) );
1018  QPointF pt3 = t.map( QPointF( -halfSize, halfSize ) );
1019  QPointF pt4 = t.map( QPointF( halfSize, -halfSize ) );
1020 
1021  if ( mPen.style() != Qt::NoPen )
1022  {
1023  e.writeLine( pt1, pt2, layerName, "CONTINUOUS", pc, outlineWidth );
1024  e.writeLine( pt3, pt4, layerName, "CONTINUOUS", pc, outlineWidth );
1025  }
1026  }
1027  else if ( mName == "arrowhead" )
1028  {
1029  QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
1030  QPointF pt2 = t.map( QPointF( 0, 0 ) );
1031  QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
1032 
1033  if ( mPen.style() != Qt::NoPen )
1034  {
1035  e.writeLine( pt1, pt2, layerName, "CONTINUOUS", pc, outlineWidth );
1036  e.writeLine( pt3, pt2, layerName, "CONTINUOUS", pc, outlineWidth );
1037  }
1038  }
1039  else if ( mName == "filled_arrowhead" )
1040  {
1041  QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
1042  QPointF pt2 = t.map( QPointF( 0, 0 ) );
1043  QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
1044 
1045  if ( mBrush.style() != Qt::NoBrush )
1046  {
1047  e.writeSolid( layerName, bc, pt1, pt2, pt3, pt3 );
1048  }
1049  }
1050  else
1051  {
1052  return false;
1053  }
1054 
1055  return true;
1056 }
1057 
1058 
1060 {
1062  mOutlineWidthUnit = unit;
1063 }
1064 
1066 {
1068  {
1069  return mOutlineWidthUnit;
1070  }
1071  return QgsSymbolV2::Mixed;
1072 }
1073 
1075 {
1077  mOutlineWidthMapUnitScale = scale;
1078 }
1079 
1081 {
1083  {
1085  }
1086  return QgsMapUnitScale();
1087 }
1088 
1090 
1091 
1093 {
1095  mSize = size;
1096  mAngle = angle;
1097  mOffset = QPointF( 0, 0 );
1099  mOutlineWidth = 1.0;
1101  mFillColor = QColor( Qt::black );
1102  mOutlineColor = QColor( Qt::black );
1103 }
1104 
1105 
1107 {
1108  QString name = DEFAULT_SVGMARKER_NAME;
1109  double size = DEFAULT_SVGMARKER_SIZE;
1110  double angle = DEFAULT_SVGMARKER_ANGLE;
1112 
1113  if ( props.contains( "name" ) )
1114  name = props["name"];
1115  if ( props.contains( "size" ) )
1116  size = props["size"].toDouble();
1117  if ( props.contains( "angle" ) )
1118  angle = props["angle"].toDouble();
1119  if ( props.contains( "scale_method" ) )
1120  scaleMethod = QgsSymbolLayerV2Utils::decodeScaleMethod( props["scale_method"] );
1121 
1122  QgsSvgMarkerSymbolLayerV2* m = new QgsSvgMarkerSymbolLayerV2( name, size, angle, scaleMethod );
1123 
1124  //we only check the svg default parameters if necessary, since it could be expensive
1125  if ( !props.contains( "fill" ) && !props.contains( "color" ) && !props.contains( "outline" ) &&
1126  !props.contains( "outline_color" ) && !props.contains( "outline-width" ) && !props.contains( "outline_width" ) )
1127  {
1128  QColor fillColor, outlineColor;
1129  double outlineWidth;
1130  bool hasFillParam, hasOutlineParam, hasOutlineWidthParam;
1131  QgsSvgCache::instance()->containsParams( name, hasFillParam, fillColor, hasOutlineParam, outlineColor, hasOutlineWidthParam, outlineWidth );
1132  if ( hasFillParam )
1133  {
1134  m->setFillColor( fillColor );
1135  }
1136  if ( hasOutlineParam )
1137  {
1138  m->setOutlineColor( outlineColor );
1139  }
1140  if ( hasOutlineWidthParam )
1141  {
1142  m->setOutlineWidth( outlineWidth );
1143  }
1144  }
1145 
1146  if ( props.contains( "size_unit" ) )
1147  m->setSizeUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["size_unit"] ) );
1148  if ( props.contains( "size_map_unit_scale" ) )
1149  m->setSizeMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["size_map_unit_scale"] ) );
1150  if ( props.contains( "offset" ) )
1151  m->setOffset( QgsSymbolLayerV2Utils::decodePoint( props["offset"] ) );
1152  if ( props.contains( "offset_unit" ) )
1153  m->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
1154  if ( props.contains( "offset_map_unit_scale" ) )
1155  m->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_map_unit_scale"] ) );
1156  if ( props.contains( "fill" ) )
1157  {
1158  //pre 2.5 projects used "fill"
1159  m->setFillColor( QColor( props["fill"] ) );
1160  }
1161  else if ( props.contains( "color" ) )
1162  {
1163  m->setFillColor( QColor( props["color"] ) );
1164  }
1165  if ( props.contains( "outline" ) )
1166  {
1167  //pre 2.5 projects used "outline"
1168  m->setOutlineColor( QColor( props["outline"] ) );
1169  }
1170  else if ( props.contains( "outline_color" ) )
1171  {
1172  m->setOutlineColor( QColor( props["outline_color"] ) );
1173  }
1174  else if ( props.contains( "line_color" ) )
1175  {
1176  m->setOutlineColor( QColor( props["line_color"] ) );
1177  }
1178 
1179  if ( props.contains( "outline-width" ) )
1180  {
1181  //pre 2.5 projects used "outline-width"
1182  m->setOutlineWidth( props["outline-width"].toDouble() );
1183  }
1184  else if ( props.contains( "outline_width" ) )
1185  {
1186  m->setOutlineWidth( props["outline_width"].toDouble() );
1187  }
1188  else if ( props.contains( "line_width" ) )
1189  {
1190  m->setOutlineWidth( props["line_width"].toDouble() );
1191  }
1192 
1193  if ( props.contains( "outline_width_unit" ) )
1194  {
1195  m->setOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["outline_width_unit"] ) );
1196  }
1197  else if ( props.contains( "line_width_unit" ) )
1198  {
1199  m->setOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["line_width_unit"] ) );
1200  }
1201  if ( props.contains( "outline_width_map_unit_scale" ) )
1202  m->setOutlineWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["outline_width_map_unit_scale"] ) );
1203 
1204  if ( props.contains( "horizontal_anchor_point" ) )
1205  {
1206  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayerV2::HorizontalAnchorPoint( props[ "horizontal_anchor_point" ].toInt() ) );
1207  }
1208  if ( props.contains( "vertical_anchor_point" ) )
1209  {
1210  m->setVerticalAnchorPoint( QgsMarkerSymbolLayerV2::VerticalAnchorPoint( props[ "vertical_anchor_point" ].toInt() ) );
1211  }
1212 
1213  //data defined properties
1214  if ( props.contains( "size_expression" ) )
1215  {
1216  m->setDataDefinedProperty( "size", props["size_expression"] );
1217  }
1218  if ( props.contains( "outline-width_expression" ) )
1219  {
1220  m->setDataDefinedProperty( "outline-width", props["outline-width_expression"] );
1221  }
1222  if ( props.contains( "angle_expression" ) )
1223  {
1224  m->setDataDefinedProperty( "angle", props["angle_expression"] );
1225  }
1226  if ( props.contains( "offset_expression" ) )
1227  {
1228  m->setDataDefinedProperty( "offset", props["offset_expression"] );
1229  }
1230  if ( props.contains( "name_expression" ) )
1231  {
1232  m->setDataDefinedProperty( "name", props["name_expression"] );
1233  }
1234  if ( props.contains( "fill_expression" ) )
1235  {
1236  m->setDataDefinedProperty( "fill", props["fill_expression"] );
1237  }
1238  if ( props.contains( "outline_expression" ) )
1239  {
1240  m->setDataDefinedProperty( "outline", props["outline_expression"] );
1241  }
1242  if ( props.contains( "horizontal_anchor_point_expression" ) )
1243  {
1244  m->setDataDefinedProperty( "horizontal_anchor_point", props[ "horizontal_anchor_point_expression" ] );
1245  }
1246  if ( props.contains( "vertical_anchor_point_expression" ) )
1247  {
1248  m->setDataDefinedProperty( "vertical_anchor_point", props[ "vertical_anchor_point_expression" ] );
1249  }
1250  return m;
1251 }
1252 
1254 {
1255  mPath = path;
1256  QColor fillColor, outlineColor;
1257  double outlineWidth;
1258  bool hasFillParam, hasOutlineParam, hasOutlineWidthParam;
1259  QgsSvgCache::instance()->containsParams( path, hasFillParam, fillColor, hasOutlineParam, outlineColor, hasOutlineWidthParam, outlineWidth );
1260  if ( hasFillParam )
1261  {
1262  setFillColor( fillColor );
1263  }
1264  if ( hasOutlineParam )
1265  {
1266  setOutlineColor( outlineColor );
1267  }
1268  if ( hasOutlineWidthParam )
1269  {
1270  setOutlineWidth( outlineWidth );
1271  }
1272 }
1273 
1274 
1276 {
1277  return "SvgMarker";
1278 }
1279 
1281 {
1282  QgsMarkerSymbolLayerV2::startRender( context ); // get anchor point expressions
1283  Q_UNUSED( context );
1284  prepareExpressions( context.fields(), context.renderContext().rendererScale() );
1285 }
1286 
1288 {
1289  Q_UNUSED( context );
1290 }
1291 
1293 {
1294  QPainter *p = context.renderContext().painter();
1295  if ( !p )
1296  return;
1297 
1298  double scaledSize = mSize;
1299  QgsExpression* sizeExpression = expression( "size" );
1300 
1301  bool hasDataDefinedSize = context.renderHints() & QgsSymbolV2::DataDefinedSizeScale || sizeExpression;
1302 
1303  if ( sizeExpression )
1304  {
1305  scaledSize = sizeExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
1306  }
1307 
1308  if ( hasDataDefinedSize )
1309  {
1310  switch ( mScaleMethod )
1311  {
1313  scaledSize = sqrt( scaledSize );
1314  break;
1316  break;
1317  }
1318  }
1319 
1321 
1322  //don't render symbols with size below one or above 10,000 pixels
1323  if (( int )size < 1 || 10000.0 < size )
1324  {
1325  return;
1326  }
1327 
1328  p->save();
1329 
1330  //offset
1331  double offsetX = 0;
1332  double offsetY = 0;
1333  markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
1334  QPointF outputOffset( offsetX, offsetY );
1335 
1336  double angle = mAngle;
1337  QgsExpression* angleExpression = expression( "angle" );
1338  if ( angleExpression )
1339  {
1340  angle = angleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
1341  }
1342 
1343  bool hasDataDefinedRotation = context.renderHints() & QgsSymbolV2::DataDefinedRotation || angleExpression;
1344  if ( hasDataDefinedRotation )
1345  {
1346  // For non-point markers, "dataDefinedRotation" means following the
1347  // shape (shape-data defined). For them, "field-data defined" does
1348  // not work at all. TODO: if "field-data defined" ever gets implemented
1349  // we'll need a way to distinguish here between the two, possibly
1350  // using another flag in renderHints()
1351  const QgsFeature* f = context.feature();
1352  if ( f )
1353  {
1354  QgsGeometry *g = f->geometry();
1355  if ( g && g->type() == QGis::Point )
1356  {
1357  const QgsMapToPixel& m2p = context.renderContext().mapToPixel();
1358  angle += m2p.mapRotation();
1359  }
1360  }
1361  }
1362 
1363  if ( angle )
1364  outputOffset = _rotatedOffset( outputOffset, angle );
1365  p->translate( point + outputOffset );
1366 
1367  bool rotated = !qgsDoubleNear( angle, 0 );
1368  if ( rotated )
1369  p->rotate( angle );
1370 
1371  QString path = mPath;
1372  QgsExpression* nameExpression = expression( "name" );
1373  if ( nameExpression )
1374  {
1375  path = nameExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
1376  }
1377 
1378  double outlineWidth = mOutlineWidth;
1379  QgsExpression* outlineWidthExpression = expression( "outline_width" );
1380  if ( outlineWidthExpression )
1381  {
1382  outlineWidth = outlineWidthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
1383  }
1384 
1385  QColor fillColor = mFillColor;
1386  QgsExpression* fillExpression = expression( "fill" );
1387  if ( fillExpression )
1388  {
1389  fillColor = QgsSymbolLayerV2Utils::decodeColor( fillExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
1390  }
1391 
1392  QColor outlineColor = mOutlineColor;
1393  QgsExpression* outlineExpression = expression( "outline" );
1394  if ( outlineExpression )
1395  {
1396  outlineColor = QgsSymbolLayerV2Utils::decodeColor( outlineExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
1397  }
1398 
1399 
1400  bool fitsInCache = true;
1401  bool usePict = true;
1402  double hwRatio = 1.0;
1403  if ( !context.renderContext().forceVectorOutput() && !rotated )
1404  {
1405  usePict = false;
1406  const QImage& img = QgsSvgCache::instance()->svgAsImage( path, size, fillColor, outlineColor, outlineWidth,
1407  context.renderContext().scaleFactor(), context.renderContext().rasterScaleFactor(), fitsInCache );
1408  if ( fitsInCache && img.width() > 1 )
1409  {
1410  //consider transparency
1411  if ( !qgsDoubleNear( context.alpha(), 1.0 ) )
1412  {
1413  QImage transparentImage = img.copy();
1414  QgsSymbolLayerV2Utils::multiplyImageOpacity( &transparentImage, context.alpha() );
1415  p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
1416  hwRatio = ( double )transparentImage.height() / ( double )transparentImage.width();
1417  }
1418  else
1419  {
1420  p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
1421  hwRatio = ( double )img.height() / ( double )img.width();
1422  }
1423  }
1424  }
1425 
1426  if ( usePict || !fitsInCache )
1427  {
1428  p->setOpacity( context.alpha() );
1429  const QPicture& pct = QgsSvgCache::instance()->svgAsPicture( path, size, fillColor, outlineColor, outlineWidth,
1431 
1432  if ( pct.width() > 1 )
1433  {
1434  p->save();
1435  _fixQPictureDPI( p );
1436  p->drawPicture( 0, 0, pct );
1437  p->restore();
1438  hwRatio = ( double )pct.height() / ( double )pct.width();
1439  }
1440  }
1441 
1442  if ( context.selected() )
1443  {
1444  QPen pen( context.renderContext().selectionColor() );
1446  if ( penWidth > size / 20 )
1447  {
1448  // keep the pen width from covering symbol
1449  penWidth = size / 20;
1450  }
1451  double penOffset = penWidth / 2;
1452  pen.setWidth( penWidth );
1453  p->setPen( pen );
1454  p->setBrush( Qt::NoBrush );
1455  double wSize = size + penOffset;
1456  double hSize = size * hwRatio + penOffset;
1457  p->drawRect( QRectF( -wSize / 2.0, -hSize / 2.0, wSize, hSize ) );
1458  }
1459 
1460  p->restore();
1461 }
1462 
1463 
1465 {
1466  QgsStringMap map;
1468  map["size"] = QString::number( mSize );
1469  map["size_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mSizeUnit );
1470  map["size_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mSizeMapUnitScale );
1471  map["angle"] = QString::number( mAngle );
1472  map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
1473  map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit );
1474  map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
1475  map["scale_method"] = QgsSymbolLayerV2Utils::encodeScaleMethod( mScaleMethod );
1476  map["color"] = mFillColor.name();
1477  map["outline_color"] = mOutlineColor.name();
1478  map["outline_width"] = QString::number( mOutlineWidth );
1479  map["outline_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOutlineWidthUnit );
1480  map["outline_width_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOutlineWidthMapUnitScale );
1481  map["horizontal_anchor_point"] = QString::number( mHorizontalAnchorPoint );
1482  map["vertical_anchor_point"] = QString::number( mVerticalAnchorPoint );
1483 
1485  return map;
1486 }
1487 
1489 {
1491  m->setFillColor( mFillColor );
1496  m->setOffset( mOffset );
1497  m->setOffsetUnit( mOffsetUnit );
1499  m->setSizeUnit( mSizeUnit );
1504  return m;
1505 }
1506 
1508 {
1510  mOutlineWidthUnit = unit;
1511 }
1512 
1514 {
1516  if ( unit != mOutlineWidthUnit )
1517  {
1518  return QgsSymbolV2::Mixed;
1519  }
1520  return unit;
1521 }
1522 
1524 {
1526  mOutlineWidthMapUnitScale = scale;
1527 }
1528 
1530 {
1532  {
1534  }
1535  return QgsMapUnitScale();
1536 }
1537 
1538 void QgsSvgMarkerSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
1539 {
1540  // <Graphic>
1541  QDomElement graphicElem = doc.createElement( "se:Graphic" );
1542  element.appendChild( graphicElem );
1543 
1544  QgsSymbolLayerV2Utils::externalGraphicToSld( doc, graphicElem, mPath, "image/svg+xml", mFillColor, mSize );
1545 
1546  // <Rotation>
1547  QString angleFunc;
1548  bool ok;
1549  double angle = props.value( "angle", "0" ).toDouble( &ok );
1550  if ( !ok )
1551  {
1552  angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mAngle );
1553  }
1554  else if ( angle + mAngle != 0 )
1555  {
1556  angleFunc = QString::number( angle + mAngle );
1557  }
1558 
1559  QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
1560 
1561  // <Displacement>
1563 }
1564 
1566 {
1567  QgsDebugMsg( "Entered." );
1568 
1569  QDomElement graphicElem = element.firstChildElement( "Graphic" );
1570  if ( graphicElem.isNull() )
1571  return NULL;
1572 
1573  QString path, mimeType;
1574  QColor fillColor;
1575  double size;
1576 
1577  if ( !QgsSymbolLayerV2Utils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
1578  return NULL;
1579 
1580  if ( mimeType != "image/svg+xml" )
1581  return NULL;
1582 
1583  double angle = 0.0;
1584  QString angleFunc;
1585  if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
1586  {
1587  bool ok;
1588  double d = angleFunc.toDouble( &ok );
1589  if ( ok )
1590  angle = d;
1591  }
1592 
1593  QPointF offset;
1595 
1597  m->setFillColor( fillColor );
1598  //m->setOutlineColor( outlineColor );
1599  //m->setOutlineWidth( outlineWidth );
1600  m->setAngle( angle );
1601  m->setOffset( offset );
1602  return m;
1603 }
1604 
1605 bool QgsSvgMarkerSymbolLayerV2::writeDxf( QgsDxfExport& e, double mmMapUnitScaleFactor, const QString& layerName, const QgsSymbolV2RenderContext* context, const QgsFeature* f,
1606  const QPointF& shift ) const
1607 {
1608  Q_UNUSED( layerName );
1609  Q_UNUSED( shift ); //todo...
1610 
1611  QSvgRenderer r( mPath );
1612  if ( !r.isValid() )
1613  {
1614  return false;
1615  }
1616 
1617  QgsDxfPaintDevice pd( &e );
1618  pd.setDrawingSize( QSizeF( r.defaultSize() ) );
1619 
1620  //size
1621  double size = mSize;
1622  QgsExpression* sizeExpression = expression( "size" );
1623  bool hasDataDefinedSize = context->renderHints() & QgsSymbolV2::DataDefinedSizeScale || sizeExpression;
1624 
1625  if ( sizeExpression )
1626  {
1627  size = sizeExpression->evaluate( *f ).toDouble();
1628  }
1629 
1630  if ( hasDataDefinedSize )
1631  {
1632  switch ( mScaleMethod )
1633  {
1635  size = sqrt( size );
1636  break;
1638  break;
1639  }
1640  }
1641 
1642  if ( mSizeUnit == QgsSymbolV2::MM )
1643  {
1644  size *= mmMapUnitScaleFactor;
1645  }
1646 
1647  double halfSize = size / 2.0;
1648 
1649  //offset, angle
1650  QPointF offset = mOffset;
1651  QgsExpression* offsetExpression = expression( "offset" );
1652  if ( offsetExpression )
1653  {
1654  QString offsetString = offsetExpression->evaluate( *f ).toString();
1655  offset = QgsSymbolLayerV2Utils::decodePoint( offsetString );
1656  }
1657  double offsetX = offset.x();
1658  double offsetY = offset.y();
1659  if ( mSizeUnit == QgsSymbolV2::MM )
1660  {
1661  offsetX *= mmMapUnitScaleFactor;
1662  offsetY *= mmMapUnitScaleFactor;
1663  }
1664 
1665  QPointF outputOffset( offsetX, offsetY );
1666 
1667  double angle = mAngle;
1668  QgsExpression* angleExpression = expression( "angle" );
1669  if ( angleExpression )
1670  {
1671  angle = angleExpression->evaluate( *f ).toDouble();
1672  }
1673  //angle = -angle; //rotation in Qt is counterclockwise
1674  if ( angle )
1675  outputOffset = _rotatedOffset( outputOffset, angle );
1676 
1677  QPainter p;
1678  p.begin( &pd );
1679  if ( !qgsDoubleNear( angle, 0.0 ) )
1680  {
1681  p.translate( r.defaultSize().width() / 2.0, r.defaultSize().height() / 2.0 );
1682  p.rotate( angle );
1683  p.translate( -r.defaultSize().width() / 2.0, -r.defaultSize().height() / 2.0 );
1684  }
1685  pd.setShift( shift );
1686  pd.setOutputSize( QRectF( -halfSize, -halfSize, size, size ) );
1687  pd.setLayer( layerName );
1688  r.render( &p );
1689  p.end();
1690  return true;
1691 }
1692 
1694 
1695 QgsFontMarkerSymbolLayerV2::QgsFontMarkerSymbolLayerV2( QString fontFamily, QChar chr, double pointSize, QColor color, double angle )
1696 {
1698  mChr = chr;
1699  mColor = color;
1700  mAngle = angle;
1701  mSize = pointSize;
1702  mOrigSize = pointSize;
1704  mOffset = QPointF( 0, 0 );
1706 }
1707 
1709 {
1711  QChar chr = DEFAULT_FONTMARKER_CHR;
1712  double pointSize = DEFAULT_FONTMARKER_SIZE;
1715 
1716  if ( props.contains( "font" ) )
1717  fontFamily = props["font"];
1718  if ( props.contains( "chr" ) && props["chr"].length() > 0 )
1719  chr = props["chr"].at( 0 );
1720  if ( props.contains( "size" ) )
1721  pointSize = props["size"].toDouble();
1722  if ( props.contains( "color" ) )
1723  color = QgsSymbolLayerV2Utils::decodeColor( props["color"] );
1724  if ( props.contains( "angle" ) )
1725  angle = props["angle"].toDouble();
1726 
1727  QgsFontMarkerSymbolLayerV2* m = new QgsFontMarkerSymbolLayerV2( fontFamily, chr, pointSize, color, angle );
1728  if ( props.contains( "offset" ) )
1729  m->setOffset( QgsSymbolLayerV2Utils::decodePoint( props["offset"] ) );
1730  if ( props.contains( "offset_unit" ) )
1731  m->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit" ] ) );
1732  if ( props.contains( "offset_map_unit_scale" ) )
1733  m->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_map_unit_scale" ] ) );
1734  if ( props.contains( "size_unit" ) )
1735  m->setSizeUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["size_unit"] ) );
1736  if ( props.contains( "size_map_unit_scale" ) )
1737  m->setSizeMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["size_map_unit_scale"] ) );
1738  if ( props.contains( "horizontal_anchor_point" ) )
1739  {
1740  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayerV2::HorizontalAnchorPoint( props[ "horizontal_anchor_point" ].toInt() ) );
1741  }
1742  if ( props.contains( "vertical_anchor_point" ) )
1743  {
1744  m->setVerticalAnchorPoint( QgsMarkerSymbolLayerV2::VerticalAnchorPoint( props[ "vertical_anchor_point" ].toInt() ) );
1745  }
1746  return m;
1747 }
1748 
1750 {
1751  return "FontMarker";
1752 }
1753 
1755 {
1756  mFont = QFont( mFontFamily );
1758  QFontMetrics fm( mFont );
1759  mChrOffset = QPointF( fm.width( mChr ) / 2, -fm.ascent() / 2 );
1760 
1761  mOrigSize = mSize; // save in case the size would be data defined
1762 }
1763 
1765 {
1766  Q_UNUSED( context );
1767 }
1768 
1770 {
1771  QPainter *p = context.renderContext().painter();
1772  if ( !p )
1773  return;
1774 
1775  QColor penColor = context.selected() ? context.renderContext().selectionColor() : mColor;
1776  penColor.setAlphaF( mColor.alphaF() * context.alpha() );
1777  p->setPen( penColor );
1778  p->setFont( mFont );
1779 
1780  p->save();
1781  //offset
1782  double offsetX = 0;
1783  double offsetY = 0;
1784  markerOffset( context, offsetX, offsetY );
1785  QPointF outputOffset( offsetX, offsetY );
1786  if ( mAngle )
1787  outputOffset = _rotatedOffset( outputOffset, mAngle );
1788  p->translate( point + outputOffset );
1789 
1791  {
1792  double s = mSize / mOrigSize;
1793  p->scale( s, s );
1794  }
1795 
1796  if ( mAngle != 0 )
1797  p->rotate( mAngle );
1798 
1799  p->drawText( -mChrOffset, mChr );
1800  p->restore();
1801 }
1802 
1804 {
1805  QgsStringMap props;
1806  props["font"] = mFontFamily;
1807  props["chr"] = mChr;
1808  props["size"] = QString::number( mSize );
1809  props["size_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mSizeUnit );
1810  props["size_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mSizeMapUnitScale );
1811  props["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
1812  props["angle"] = QString::number( mAngle );
1813  props["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
1814  props["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit );
1815  props["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
1816  props["horizontal_anchor_point"] = QString::number( mHorizontalAnchorPoint );
1817  props["vertical_anchor_point"] = QString::number( mVerticalAnchorPoint );
1818  return props;
1819 }
1820 
1822 {
1824  m->setOffset( mOffset );
1825  m->setOffsetUnit( mOffsetUnit );
1827  m->setSizeUnit( mSizeUnit );
1831  return m;
1832 }
1833 
1834 void QgsFontMarkerSymbolLayerV2::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
1835 {
1836  // <Graphic>
1837  QDomElement graphicElem = doc.createElement( "se:Graphic" );
1838  element.appendChild( graphicElem );
1839 
1840  QString fontPath = QString( "ttf://%1" ).arg( mFontFamily );
1841  int markIndex = mChr.unicode();
1842  QgsSymbolLayerV2Utils::externalMarkerToSld( doc, graphicElem, fontPath, "ttf", &markIndex, mColor, mSize );
1843 
1844  // <Rotation>
1845  QString angleFunc;
1846  bool ok;
1847  double angle = props.value( "angle", "0" ).toDouble( &ok );
1848  if ( !ok )
1849  {
1850  angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mAngle );
1851  }
1852  else if ( angle + mAngle != 0 )
1853  {
1854  angleFunc = QString::number( angle + mAngle );
1855  }
1856  QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
1857 
1858  // <Displacement>
1860 }
1861 
1863 {
1864  QgsDebugMsg( "Entered." );
1865 
1866  QDomElement graphicElem = element.firstChildElement( "Graphic" );
1867  if ( graphicElem.isNull() )
1868  return NULL;
1869 
1870  QString name, format;
1871  QColor color;
1872  double size;
1873  int chr;
1874 
1875  if ( !QgsSymbolLayerV2Utils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
1876  return NULL;
1877 
1878  if ( !name.startsWith( "ttf://" ) || format != "ttf" )
1879  return NULL;
1880 
1881  QString fontFamily = name.mid( 6 );
1882 
1883  double angle = 0.0;
1884  QString angleFunc;
1885  if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
1886  {
1887  bool ok;
1888  double d = angleFunc.toDouble( &ok );
1889  if ( ok )
1890  angle = d;
1891  }
1892 
1893  QPointF offset;
1895 
1896  QgsMarkerSymbolLayerV2 *m = new QgsFontMarkerSymbolLayerV2( fontFamily, chr, size, color );
1897  m->setAngle( angle );
1898  m->setOffset( offset );
1899  return m;
1900 }
1901 
1902