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