QGIS API Documentation  2.8.2-Wien
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgsfillsymbollayerv2.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsfillsymbollayerv2.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 "qgsfillsymbollayerv2.h"
17 #include "qgslinesymbollayerv2.h"
18 #include "qgsmarkersymbollayerv2.h"
19 #include "qgssymbollayerv2utils.h"
20 #include "qgsdxfexport.h"
21 #include "qgsexpression.h"
22 #include "qgsrendercontext.h"
23 #include "qgsproject.h"
24 #include "qgssvgcache.h"
25 #include "qgslogger.h"
26 #include "qgsvectorcolorrampv2.h"
27 
28 #include <QPainter>
29 #include <QFile>
30 #include <QSvgRenderer>
31 #include <QDomDocument>
32 #include <QDomElement>
33 
34 QgsSimpleFillSymbolLayerV2::QgsSimpleFillSymbolLayerV2( QColor color, Qt::BrushStyle style, QColor borderColor, Qt::PenStyle borderStyle, double borderWidth,
35  Qt::PenJoinStyle penJoinStyle )
36  : mBrushStyle( style )
37  , mBorderColor( borderColor )
38  , mBorderStyle( borderStyle )
39  , mBorderWidth( borderWidth )
40  , mBorderWidthUnit( QgsSymbolV2::MM )
41  , mPenJoinStyle( penJoinStyle )
42  , mOffsetUnit( QgsSymbolV2::MM )
43 {
44  mColor = color;
45 }
46 
48 {
49  mBorderWidthUnit = unit;
50  mOffsetUnit = unit;
51 }
52 
54 {
56  if ( mOffsetUnit != unit )
57  {
58  return QgsSymbolV2::Mixed;
59  }
60  return unit;
61 }
62 
64 {
66  mOffsetMapUnitScale = scale;
67 }
68 
70 {
72  {
74  }
75  return QgsMapUnitScale();
76 }
77 
78 void QgsSimpleFillSymbolLayerV2::applyDataDefinedSymbology( QgsSymbolV2RenderContext& context, QBrush& brush, QPen& pen, QPen& selPen )
79 {
80  if ( mDataDefinedProperties.isEmpty() )
81  return; // shortcut
82 
83  QgsExpression* colorExpression = expression( "color" );
84  if ( colorExpression )
85  {
86  brush.setColor( QgsSymbolLayerV2Utils::decodeColor( colorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) );
87  }
88  QgsExpression* fillStyleExpression = expression( "fill_style" );
89  if ( fillStyleExpression )
90  {
91  brush.setStyle( QgsSymbolLayerV2Utils::decodeBrushStyle( fillStyleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) );
92  }
93  QgsExpression* colorBorderExpression = expression( "color_border" );
94  if ( colorBorderExpression )
95  {
96  pen.setColor( QgsSymbolLayerV2Utils::decodeColor( colorBorderExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) );
97  }
98  QgsExpression* widthBorderExpression = expression( "width_border" );
99  if ( widthBorderExpression )
100  {
101  double width = widthBorderExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
103  pen.setWidthF( width );
104  selPen.setWidthF( width );
105  }
106  QgsExpression* borderStyleExpression = expression( "border_style" );
107  if ( borderStyleExpression )
108  {
109  pen.setStyle( QgsSymbolLayerV2Utils::decodePenStyle( borderStyleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) );
110  selPen.setStyle( QgsSymbolLayerV2Utils::decodePenStyle( borderStyleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) );
111  }
112  QgsExpression* joinStyleExpression = expression( "join_style" );
113  if ( joinStyleExpression )
114  {
115  pen.setJoinStyle( QgsSymbolLayerV2Utils::decodePenJoinStyle( joinStyleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) );
116  selPen.setJoinStyle( QgsSymbolLayerV2Utils::decodePenJoinStyle( joinStyleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) );
117  }
118 }
119 
120 
122 {
124  Qt::BrushStyle style = DEFAULT_SIMPLEFILL_STYLE;
128  Qt::PenJoinStyle penJoinStyle = DEFAULT_SIMPLEFILL_JOINSTYLE;
129  QPointF offset;
130 
131  if ( props.contains( "color" ) )
132  color = QgsSymbolLayerV2Utils::decodeColor( props["color"] );
133  if ( props.contains( "style" ) )
134  style = QgsSymbolLayerV2Utils::decodeBrushStyle( props["style"] );
135  if ( props.contains( "color_border" ) )
136  {
137  //pre 2.5 projects used "color_border"
138  borderColor = QgsSymbolLayerV2Utils::decodeColor( props["color_border"] );
139  }
140  else if ( props.contains( "outline_color" ) )
141  {
142  borderColor = QgsSymbolLayerV2Utils::decodeColor( props["outline_color"] );
143  }
144  else if ( props.contains( "line_color" ) )
145  {
146  borderColor = QgsSymbolLayerV2Utils::decodeColor( props["line_color"] );
147  }
148 
149  if ( props.contains( "style_border" ) )
150  {
151  //pre 2.5 projects used "style_border"
152  borderStyle = QgsSymbolLayerV2Utils::decodePenStyle( props["style_border"] );
153  }
154  else if ( props.contains( "outline_style" ) )
155  {
156  borderStyle = QgsSymbolLayerV2Utils::decodePenStyle( props["outline_style"] );
157  }
158  else if ( props.contains( "line_style" ) )
159  {
160  borderStyle = QgsSymbolLayerV2Utils::decodePenStyle( props["line_style"] );
161  }
162  if ( props.contains( "width_border" ) )
163  {
164  //pre 2.5 projects used "width_border"
165  borderWidth = props["width_border"].toDouble();
166  }
167  else if ( props.contains( "outline_width" ) )
168  {
169  borderWidth = props["outline_width"].toDouble();
170  }
171  else if ( props.contains( "line_width" ) )
172  {
173  borderWidth = props["line_width"].toDouble();
174  }
175  if ( props.contains( "offset" ) )
176  offset = QgsSymbolLayerV2Utils::decodePoint( props["offset"] );
177  if ( props.contains( "joinstyle" ) )
178  penJoinStyle = QgsSymbolLayerV2Utils::decodePenJoinStyle( props["joinstyle"] );
179 
180  QgsSimpleFillSymbolLayerV2* sl = new QgsSimpleFillSymbolLayerV2( color, style, borderColor, borderStyle, borderWidth, penJoinStyle );
181  sl->setOffset( offset );
182  if ( props.contains( "border_width_unit" ) )
183  {
184  sl->setBorderWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["border_width_unit"] ) );
185  }
186  else if ( props.contains( "outline_width_unit" ) )
187  {
188  sl->setBorderWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["outline_width_unit"] ) );
189  }
190  else if ( props.contains( "line_width_unit" ) )
191  {
192  sl->setBorderWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["line_width_unit"] ) );
193  }
194  if ( props.contains( "offset_unit" ) )
195  sl->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
196 
197  if ( props.contains( "border_width_map_unit_scale" ) )
198  sl->setBorderWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["border_width_map_unit_scale"] ) );
199  if ( props.contains( "offset_map_unit_scale" ) )
200  sl->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_map_unit_scale"] ) );
201 
202  if ( props.contains( "color_expression" ) )
203  {
204  sl->setDataDefinedProperty( "color", props["color_expression"] );
205  }
206  if ( props.contains( "color_border_expression" ) )
207  {
208  sl->setDataDefinedProperty( "color_border", props["color_border_expression"] );
209  }
210  if ( props.contains( "width_border_expression" ) )
211  {
212  sl->setDataDefinedProperty( "width_border", props["width_border_expression"] );
213  }
214  if ( props.contains( "fill_style_expression" ) )
215  {
216  sl->setDataDefinedProperty( "fill_style", props["fill_style_expression"] );
217  }
218  if ( props.contains( "border_style_expression" ) )
219  {
220  sl->setDataDefinedProperty( "border_style", props["border_style_expression"] );
221  }
222  if ( props.contains( "join_style_expression" ) )
223  {
224  sl->setDataDefinedProperty( "join_style", props["join_style_expression"] );
225  }
226  return sl;
227 }
228 
229 
231 {
232  return "SimpleFill";
233 }
234 
236 {
237  QColor fillColor = mColor;
238  fillColor.setAlphaF( context.alpha() * mColor.alphaF() );
239  mBrush = QBrush( fillColor, mBrushStyle );
240 
241  // scale brush content for printout
243  if ( rasterScaleFactor != 1.0 )
244  {
245  mBrush.setMatrix( QMatrix().scale( 1.0 / rasterScaleFactor, 1.0 / rasterScaleFactor ) );
246  }
247 
248  QColor selColor = context.renderContext().selectionColor();
249  QColor selPenColor = selColor == mColor ? selColor : mBorderColor;
250  if ( ! selectionIsOpaque ) selColor.setAlphaF( context.alpha() );
251  mSelBrush = QBrush( selColor );
252  // N.B. unless a "selection line color" is implemented in addition to the "selection color" option
253  // this would mean symbols with "no fill" look the same whether or not they are selected
254  if ( selectFillStyle )
255  mSelBrush.setStyle( mBrushStyle );
256 
257  QColor borderColor = mBorderColor;
258  borderColor.setAlphaF( context.alpha() * mBorderColor.alphaF() );
259  mPen = QPen( borderColor );
260  mSelPen = QPen( selPenColor );
261  mPen.setStyle( mBorderStyle );
263  mPen.setJoinStyle( mPenJoinStyle );
264  prepareExpressions( context.fields(), context.renderContext().rendererScale() );
265 }
266 
268 {
269  Q_UNUSED( context );
270 }
271 
272 void QgsSimpleFillSymbolLayerV2::renderPolygon( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
273 {
274  QPainter* p = context.renderContext().painter();
275  if ( !p )
276  {
277  return;
278  }
279 
280  applyDataDefinedSymbology( context, mBrush, mPen, mSelPen );
281 
282  p->setBrush( context.selected() ? mSelBrush : mBrush );
283  p->setPen( context.selected() ? mSelPen : mPen );
284 
285  QPointF offset;
286  if ( !mOffset.isNull() )
287  {
290  p->translate( offset );
291  }
292 
293  _renderPolygon( p, points, rings, context );
294 
295  if ( !mOffset.isNull() )
296  {
297  p->translate( -offset );
298  }
299 }
300 
302 {
303  QgsStringMap map;
304  map["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
306  map["outline_color"] = QgsSymbolLayerV2Utils::encodeColor( mBorderColor );
307  map["outline_style"] = QgsSymbolLayerV2Utils::encodePenStyle( mBorderStyle );
308  map["outline_width"] = QString::number( mBorderWidth );
309  map["outline_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mBorderWidthUnit );
310  map["border_width_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mBorderWidthMapUnitScale );
312  map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
314  map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
316  return map;
317 }
318 
320 {
322  sl->setOffset( mOffset );
323  sl->setOffsetUnit( mOffsetUnit );
328  return sl;
329 }
330 
331 void QgsSimpleFillSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
332 {
333  if ( mBrushStyle == Qt::NoBrush && mBorderStyle == Qt::NoPen )
334  return;
335 
336  QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
337  if ( !props.value( "uom", "" ).isEmpty() )
338  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
339  element.appendChild( symbolizerElem );
340 
341  // <Geometry>
342  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
343 
344  if ( mBrushStyle != Qt::NoBrush )
345  {
346  // <Fill>
347  QDomElement fillElem = doc.createElement( "se:Fill" );
348  symbolizerElem.appendChild( fillElem );
350  }
351 
352  if ( mBorderStyle != Qt::NoPen )
353  {
354  // <Stroke>
355  QDomElement strokeElem = doc.createElement( "se:Stroke" );
356  symbolizerElem.appendChild( strokeElem );
358  }
359 
360  // <se:Displacement>
362 }
363 
364 QString QgsSimpleFillSymbolLayerV2::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
365 {
366  //brush
367  QString symbolStyle;
368  symbolStyle.append( QgsSymbolLayerV2Utils::ogrFeatureStyleBrush( mColor ) );
369  symbolStyle.append( ";" );
370  //pen
371  symbolStyle.append( QgsSymbolLayerV2Utils::ogrFeatureStylePen( mBorderWidth, mmScaleFactor, mapUnitScaleFactor, mBorderColor, mPenJoinStyle ) );
372  return symbolStyle;
373 }
374 
376 {
377  QgsDebugMsg( "Entered." );
378 
379  QColor color, borderColor;
380  Qt::BrushStyle fillStyle;
381  Qt::PenStyle borderStyle;
382  double borderWidth;
383 
384  QDomElement fillElem = element.firstChildElement( "Fill" );
385  QgsSymbolLayerV2Utils::fillFromSld( fillElem, fillStyle, color );
386 
387  QDomElement strokeElem = element.firstChildElement( "Stroke" );
388  QgsSymbolLayerV2Utils::lineFromSld( strokeElem, borderStyle, borderColor, borderWidth );
389 
390  QPointF offset;
392 
393  QgsSimpleFillSymbolLayerV2* sl = new QgsSimpleFillSymbolLayerV2( color, fillStyle, borderColor, borderStyle, borderWidth );
394  sl->setOffset( offset );
395  return sl;
396 }
397 
399 {
400  double penBleed = mBorderStyle == Qt::NoPen ? 0 : ( mBorderWidth / 2.0 );
401  double offsetBleed = mOffset.x() > mOffset.y() ? mOffset.x() : mOffset.y();
402  return penBleed + offsetBleed;
403 }
404 
406 {
407  double width = mBorderWidth;
408  QgsExpression* widthBorderExpression = expression( "width_border" );
409  if ( widthBorderExpression )
410  {
411  width = widthBorderExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
412  }
414 }
415 
417 {
418  QgsExpression* colorExpression = expression( "border_color" );
419  if ( colorExpression )
420  {
421  return QgsSymbolLayerV2Utils::decodeColor( colorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
422  }
423  return mBorderColor;
424 }
425 
427 {
428  return mBorderStyle;
429 }
430 
432 {
433  QgsExpression* colorExpression = expression( "color" );
434  if ( colorExpression )
435  {
436  return QgsSymbolLayerV2Utils::decodeColor( colorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
437  }
438  return mColor;
439 }
440 
442 {
443  return mBrushStyle;
444 }
445 
446 //QgsGradientFillSymbolLayer
447 
449  GradientColorType colorType, GradientType gradientType,
450  GradientCoordinateMode coordinateMode, GradientSpread spread )
451  : mGradientColorType( colorType )
452  , mGradientRamp( NULL )
453  , mGradientType( gradientType )
454  , mCoordinateMode( coordinateMode )
455  , mGradientSpread( spread )
456  , mReferencePoint1( QPointF( 0.5, 0 ) )
457  , mReferencePoint1IsCentroid( false )
458  , mReferencePoint2( QPointF( 0.5, 1 ) )
459  , mReferencePoint2IsCentroid( false )
460  , mOffsetUnit( QgsSymbolV2::MM )
461 {
462  mColor = color;
463  mColor2 = color2;
464 }
465 
467 {
468  delete mGradientRamp;
469 }
470 
472 {
473  //default to a two-color, linear gradient with feature mode and pad spreading
478  //default to gradient from the default fill color to white
479  QColor color = DEFAULT_SIMPLEFILL_COLOR, color2 = Qt::white;
480  QPointF referencePoint1 = QPointF( 0.5, 0 );
481  bool refPoint1IsCentroid = false;
482  QPointF referencePoint2 = QPointF( 0.5, 1 );
483  bool refPoint2IsCentroid = false;
484  double angle = 0;
485  QPointF offset;
486 
487  //update gradient properties from props
488  if ( props.contains( "type" ) )
489  type = ( GradientType )props["type"].toInt();
490  if ( props.contains( "coordinate_mode" ) )
491  coordinateMode = ( GradientCoordinateMode )props["coordinate_mode"].toInt();
492  if ( props.contains( "spread" ) )
493  gradientSpread = ( GradientSpread )props["spread"].toInt();
494  if ( props.contains( "color_type" ) )
495  colorType = ( GradientColorType )props["color_type"].toInt();
496  if ( props.contains( "gradient_color" ) )
497  {
498  //pre 2.5 projects used "gradient_color"
499  color = QgsSymbolLayerV2Utils::decodeColor( props["gradient_color"] );
500  }
501  else if ( props.contains( "color" ) )
502  {
503  color = QgsSymbolLayerV2Utils::decodeColor( props["color"] );
504  }
505  if ( props.contains( "gradient_color2" ) )
506  {
507  color2 = QgsSymbolLayerV2Utils::decodeColor( props["gradient_color2"] );
508  }
509 
510  if ( props.contains( "reference_point1" ) )
511  referencePoint1 = QgsSymbolLayerV2Utils::decodePoint( props["reference_point1"] );
512  if ( props.contains( "reference_point1_iscentroid" ) )
513  refPoint1IsCentroid = props["reference_point1_iscentroid"].toInt();
514  if ( props.contains( "reference_point2" ) )
515  referencePoint2 = QgsSymbolLayerV2Utils::decodePoint( props["reference_point2"] );
516  if ( props.contains( "reference_point2_iscentroid" ) )
517  refPoint2IsCentroid = props["reference_point2_iscentroid"].toInt();
518  if ( props.contains( "angle" ) )
519  angle = props["angle"].toDouble();
520 
521  if ( props.contains( "offset" ) )
522  offset = QgsSymbolLayerV2Utils::decodePoint( props["offset"] );
523 
524  //attempt to create color ramp from props
526 
527  //create a new gradient fill layer with desired properties
528  QgsGradientFillSymbolLayerV2* sl = new QgsGradientFillSymbolLayerV2( color, color2, colorType, type, coordinateMode, gradientSpread );
529  sl->setOffset( offset );
530  if ( props.contains( "offset_unit" ) )
531  sl->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
532  if ( props.contains( "offset_map_unit_scale" ) )
533  sl->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_map_unit_scale"] ) );
534  sl->setReferencePoint1( referencePoint1 );
535  sl->setReferencePoint1IsCentroid( refPoint1IsCentroid );
536  sl->setReferencePoint2( referencePoint2 );
537  sl->setReferencePoint2IsCentroid( refPoint2IsCentroid );
538  sl->setAngle( angle );
539  if ( gradientRamp )
540  sl->setColorRamp( gradientRamp );
541 
542  //data defined symbology expressions
543  if ( props.contains( "color_expression" ) )
544  sl->setDataDefinedProperty( "color", props["color_expression"] );
545  if ( props.contains( "color2_expression" ) )
546  sl->setDataDefinedProperty( "color2", props["color2_expression"] );
547  if ( props.contains( "angle_expression" ) )
548  sl->setDataDefinedProperty( "angle", props["angle_expression"] );
549  if ( props.contains( "gradient_type_expression" ) )
550  sl->setDataDefinedProperty( "gradient_type", props["gradient_type_expression"] );
551  if ( props.contains( "coordinate_mode_expression" ) )
552  sl->setDataDefinedProperty( "coordinate_mode", props["coordinate_mode_expression"] );
553  if ( props.contains( "spread_expression" ) )
554  sl->setDataDefinedProperty( "spread", props["spread_expression"] );
555  if ( props.contains( "reference1_x_expression" ) )
556  sl->setDataDefinedProperty( "reference1_x", props["reference1_x_expression"] );
557  if ( props.contains( "reference1_y_expression" ) )
558  sl->setDataDefinedProperty( "reference1_y", props["reference1_y_expression"] );
559  if ( props.contains( "reference1_iscentroid_expression" ) )
560  sl->setDataDefinedProperty( "reference1_iscentroid", props["reference1_iscentroid_expression"] );
561  if ( props.contains( "reference2_x_expression" ) )
562  sl->setDataDefinedProperty( "reference2_x", props["reference2_x_expression"] );
563  if ( props.contains( "reference2_y_expression" ) )
564  sl->setDataDefinedProperty( "reference2_y", props["reference2_y_expression"] );
565  if ( props.contains( "reference2_iscentroid_expression" ) )
566  sl->setDataDefinedProperty( "reference2_iscentroid", props["reference2_iscentroid_expression"] );
567 
568  return sl;
569 }
570 
572 {
573  delete mGradientRamp;
574  mGradientRamp = ramp;
575 }
576 
578 {
579  return "GradientFill";
580 }
581 
582 void QgsGradientFillSymbolLayerV2::applyDataDefinedSymbology( QgsSymbolV2RenderContext& context, const QPolygonF& points )
583 {
584 
586  {
587  //shortcut
590  return;
591  }
592 
593  //first gradient color
594  QgsExpression* colorExpression = expression( "color" );
595  QColor color = mColor;
596  if ( colorExpression )
597  color = QgsSymbolLayerV2Utils::decodeColor( colorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
598 
599  //second gradient color
600  QgsExpression* colorExpression2 = expression( "color2" );
601  QColor color2 = mColor2;
602  if ( colorExpression2 )
603  color2 = QgsSymbolLayerV2Utils::decodeColor( colorExpression2->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
604 
605  //gradient rotation angle
606  QgsExpression* angleExpression = expression( "angle" );
607  double angle = mAngle;
608  if ( angleExpression )
609  angle = angleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
610 
611  //gradient type
612  QgsExpression* typeExpression = expression( "gradient_type" );
614  if ( typeExpression )
615  {
616  QString currentType = typeExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
617  if ( currentType == QObject::tr( "linear" ) )
618  {
620  }
621  else if ( currentType == QObject::tr( "radial" ) )
622  {
624  }
625  else if ( currentType == QObject::tr( "conical" ) )
626  {
628  }
629  else
630  {
631  //default to linear
633  }
634  }
635 
636  //coordinate mode
637  QgsExpression* coordModeExpression = expression( "coordinate_mode" );
639  if ( coordModeExpression )
640  {
641  QString currentCoordMode = coordModeExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
642  if ( currentCoordMode == QObject::tr( "feature" ) )
643  {
644  coordinateMode = QgsGradientFillSymbolLayerV2::Feature;
645  }
646  else if ( currentCoordMode == QObject::tr( "viewport" ) )
647  {
649  }
650  else
651  {
652  //default to feature mode
653  coordinateMode = QgsGradientFillSymbolLayerV2::Feature;
654  }
655  }
656 
657  //gradient spread
658  QgsExpression* spreadExpression = expression( "spread" );
660  if ( spreadExpression )
661  {
662  QString currentSpread = spreadExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
663  if ( currentSpread == QObject::tr( "pad" ) )
664  {
666  }
667  else if ( currentSpread == QObject::tr( "repeat" ) )
668  {
670  }
671  else if ( currentSpread == QObject::tr( "reflect" ) )
672  {
674  }
675  else
676  {
677  //default to pad spread
679  }
680  }
681 
682  //reference point 1 x & y
683  QgsExpression* ref1XExpression = expression( "reference1_x" );
684  double refPoint1X = mReferencePoint1.x();
685  if ( ref1XExpression )
686  refPoint1X = ref1XExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
687  QgsExpression* ref1YExpression = expression( "reference1_y" );
688  double refPoint1Y = mReferencePoint1.y();
689  if ( ref1YExpression )
690  refPoint1Y = ref1YExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
691  QgsExpression* ref1IsCentroidExpression = expression( "reference1_iscentroid" );
692  bool refPoint1IsCentroid = mReferencePoint1IsCentroid;
693  if ( ref1IsCentroidExpression )
694  refPoint1IsCentroid = ref1IsCentroidExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toBool();
695 
696  //reference point 2 x & y
697  QgsExpression* ref2XExpression = expression( "reference2_x" );
698  double refPoint2X = mReferencePoint2.x();
699  if ( ref2XExpression )
700  refPoint2X = ref2XExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
701  QgsExpression* ref2YExpression = expression( "reference2_y" );
702  double refPoint2Y = mReferencePoint2.y();
703  if ( ref2YExpression )
704  refPoint2Y = ref2YExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
705  QgsExpression* ref2IsCentroidExpression = expression( "reference2_iscentroid" );
706  bool refPoint2IsCentroid = mReferencePoint2IsCentroid;
707  if ( ref2IsCentroidExpression )
708  refPoint2IsCentroid = ref2IsCentroidExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toBool();
709 
710  if ( refPoint1IsCentroid || refPoint2IsCentroid )
711  {
712  //either the gradient is starting or ending at a centroid, so calculate it
713  QPointF centroid = QgsSymbolLayerV2Utils::polygonCentroid( points );
714  //centroid coordinates need to be scaled to a range [0, 1] relative to polygon bounds
715  QRectF bbox = points.boundingRect();
716  double centroidX = ( centroid.x() - bbox.left() ) / bbox.width();
717  double centroidY = ( centroid.y() - bbox.top() ) / bbox.height();
718 
719  if ( refPoint1IsCentroid )
720  {
721  refPoint1X = centroidX;
722  refPoint1Y = centroidY;
723  }
724  if ( refPoint2IsCentroid )
725  {
726  refPoint2X = centroidX;
727  refPoint2Y = centroidY;
728  }
729  }
730 
731  //update gradient with data defined values
732  applyGradient( context, mBrush, color, color2, mGradientColorType, mGradientRamp, gradientType, coordinateMode,
733  spread, QPointF( refPoint1X, refPoint1Y ), QPointF( refPoint2X, refPoint2Y ), angle );
734 }
735 
736 QPointF QgsGradientFillSymbolLayerV2::rotateReferencePoint( const QPointF & refPoint, double angle )
737 {
738  //rotate a reference point by a specified angle around the point (0.5, 0.5)
739 
740  //create a line from the centrepoint of a rectangle bounded by (0, 0) and (1, 1) to the reference point
741  QLineF refLine = QLineF( QPointF( 0.5, 0.5 ), refPoint );
742  //rotate this line by the current rotation angle
743  refLine.setAngle( refLine.angle() + angle );
744  //get new end point of line
745  QPointF rotatedReferencePoint = refLine.p2();
746  //make sure coords of new end point is within [0, 1]
747  if ( rotatedReferencePoint.x() > 1 )
748  rotatedReferencePoint.setX( 1 );
749  if ( rotatedReferencePoint.x() < 0 )
750  rotatedReferencePoint.setX( 0 );
751  if ( rotatedReferencePoint.y() > 1 )
752  rotatedReferencePoint.setY( 1 );
753  if ( rotatedReferencePoint.y() < 0 )
754  rotatedReferencePoint.setY( 0 );
755 
756  return rotatedReferencePoint;
757 }
758 
759 void QgsGradientFillSymbolLayerV2::applyGradient( const QgsSymbolV2RenderContext &context, QBrush &brush,
760  const QColor &color, const QColor &color2, const GradientColorType &gradientColorType,
761  QgsVectorColorRampV2 *gradientRamp, const GradientType &gradientType,
762  const GradientCoordinateMode &coordinateMode, const GradientSpread &gradientSpread,
763  const QPointF &referencePoint1, const QPointF &referencePoint2, const double angle )
764 {
765  //update alpha of gradient colors
766  QColor fillColor = color;
767  fillColor.setAlphaF( context.alpha() * fillColor.alphaF() );
768  QColor fillColor2 = color2;
769  fillColor2.setAlphaF( context.alpha() * fillColor2.alphaF() );
770 
771  //rotate reference points
772  QPointF rotatedReferencePoint1 = angle != 0 ? rotateReferencePoint( referencePoint1, angle ) : referencePoint1;
773  QPointF rotatedReferencePoint2 = angle != 0 ? rotateReferencePoint( referencePoint2, angle ) : referencePoint2;
774 
775  //create a QGradient with the desired properties
776  QGradient gradient;
777  switch ( gradientType )
778  {
780  gradient = QLinearGradient( rotatedReferencePoint1, rotatedReferencePoint2 );
781  break;
783  gradient = QRadialGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).length() );
784  break;
786  gradient = QConicalGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).angle() );
787  break;
788  }
789  switch ( coordinateMode )
790  {
792  gradient.setCoordinateMode( QGradient::ObjectBoundingMode );
793  break;
795  gradient.setCoordinateMode( QGradient::StretchToDeviceMode );
796  break;
797  }
798  switch ( gradientSpread )
799  {
801  gradient.setSpread( QGradient::PadSpread );
802  break;
804  gradient.setSpread( QGradient::ReflectSpread );
805  break;
807  gradient.setSpread( QGradient::RepeatSpread );
808  break;
809  }
810 
811  //add stops to gradient
812  if ( gradientColorType == QgsGradientFillSymbolLayerV2::ColorRamp && gradientRamp && gradientRamp->type() == "gradient" )
813  {
814  //color ramp gradient
815  QgsVectorGradientColorRampV2* gradRamp = static_cast<QgsVectorGradientColorRampV2*>( gradientRamp );
816  gradRamp->addStopsToGradient( &gradient, context.alpha() );
817  }
818  else
819  {
820  //two color gradient
821  gradient.setColorAt( 0.0, fillColor );
822  gradient.setColorAt( 1.0, fillColor2 );
823  }
824 
825  //update QBrush use gradient
826  brush = QBrush( gradient );
827 }
828 
830 {
831  QColor selColor = context.renderContext().selectionColor();
832  if ( ! selectionIsOpaque ) selColor.setAlphaF( context.alpha() );
833  mSelBrush = QBrush( selColor );
834 
835  //update mBrush to use a gradient fill with specified properties
836  prepareExpressions( context.fields(), context.renderContext().rendererScale() );
837 }
838 
840 {
841  Q_UNUSED( context );
842 }
843 
844 void QgsGradientFillSymbolLayerV2::renderPolygon( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
845 {
846  QPainter* p = context.renderContext().painter();
847  if ( !p )
848  {
849  return;
850  }
851 
852  applyDataDefinedSymbology( context, points );
853 
854  p->setBrush( context.selected() ? mSelBrush : mBrush );
855  p->setPen( Qt::NoPen );
856 
857  QPointF offset;
858  if ( !mOffset.isNull() )
859  {
862  p->translate( offset );
863  }
864 
865  _renderPolygon( p, points, rings, context );
866 
867  if ( !mOffset.isNull() )
868  {
869  p->translate( -offset );
870  }
871 }
872 
874 {
875  QgsStringMap map;
876  map["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
877  map["gradient_color2"] = QgsSymbolLayerV2Utils::encodeColor( mColor2 );
878  map["color_type"] = QString::number( mGradientColorType );
879  map["type"] = QString::number( mGradientType );
880  map["coordinate_mode"] = QString::number( mCoordinateMode );
881  map["spread"] = QString::number( mGradientSpread );
882  map["reference_point1"] = QgsSymbolLayerV2Utils::encodePoint( mReferencePoint1 );
883  map["reference_point1_iscentroid"] = QString::number( mReferencePoint1IsCentroid );
884  map["reference_point2"] = QgsSymbolLayerV2Utils::encodePoint( mReferencePoint2 );
885  map["reference_point2_iscentroid"] = QString::number( mReferencePoint2IsCentroid );
886  map["angle"] = QString::number( mAngle );
887  map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
889  map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
891  if ( mGradientRamp )
892  {
893  map.unite( mGradientRamp->properties() );
894  }
895  return map;
896 }
897 
899 {
901  if ( mGradientRamp )
902  sl->setColorRamp( mGradientRamp->clone() );
907  sl->setAngle( mAngle );
908  sl->setOffset( mOffset );
909  sl->setOffsetUnit( mOffsetUnit );
912  return sl;
913 }
914 
916 {
917  double offsetBleed = mOffset.x() > mOffset.y() ? mOffset.x() : mOffset.y();
918  return offsetBleed;
919 }
920 
922 {
923  mOffsetUnit = unit;
924 }
925 
927 {
928  return mOffsetUnit;
929 }
930 
932 {
933  mOffsetMapUnitScale = scale;
934 }
935 
937 {
938  return mOffsetMapUnitScale;
939 }
940 
941 //QgsShapeburstFillSymbolLayer
942 
944  int blurRadius, bool useWholeShape, double maxDistance ) :
945 
946  mBlurRadius( blurRadius ),
947  mUseWholeShape( useWholeShape ),
948  mMaxDistance( maxDistance ),
949  mDistanceUnit( QgsSymbolV2::MM ),
950  mColorType( colorType ),
951  mColor2( color2 ),
952  mGradientRamp( NULL ),
953  mTwoColorGradientRamp( 0 ),
954  mIgnoreRings( false ),
955  mOffsetUnit( QgsSymbolV2::MM )
956 {
957  mColor = color;
958 }
959 
961 {
962  delete mGradientRamp;
963 }
964 
966 {
967  //default to a two-color gradient
969  QColor color = DEFAULT_SIMPLEFILL_COLOR, color2 = Qt::white;
970  int blurRadius = 0;
971  bool useWholeShape = true;
972  double maxDistance = 5;
973  QPointF offset;
974 
975  //update fill properties from props
976  if ( props.contains( "color_type" ) )
977  {
978  colorType = ( ShapeburstColorType )props["color_type"].toInt();
979  }
980  if ( props.contains( "shapeburst_color" ) )
981  {
982  //pre 2.5 projects used "shapeburst_color"
983  color = QgsSymbolLayerV2Utils::decodeColor( props["shapeburst_color"] );
984  }
985  else if ( props.contains( "color" ) )
986  {
987  color = QgsSymbolLayerV2Utils::decodeColor( props["color"] );
988  }
989 
990  if ( props.contains( "shapeburst_color2" ) )
991  {
992  //pre 2.5 projects used "shapeburst_color2"
993  color2 = QgsSymbolLayerV2Utils::decodeColor( props["shapeburst_color2"] );
994  }
995  else if ( props.contains( "gradient_color2" ) )
996  {
997  color2 = QgsSymbolLayerV2Utils::decodeColor( props["gradient_color2"] );
998  }
999  if ( props.contains( "blur_radius" ) )
1000  {
1001  blurRadius = props["blur_radius"].toInt();
1002  }
1003  if ( props.contains( "use_whole_shape" ) )
1004  {
1005  useWholeShape = props["use_whole_shape"].toInt();
1006  }
1007  if ( props.contains( "max_distance" ) )
1008  {
1009  maxDistance = props["max_distance"].toDouble();
1010  }
1011  if ( props.contains( "offset" ) )
1012  {
1013  offset = QgsSymbolLayerV2Utils::decodePoint( props["offset"] );
1014  }
1015 
1016  //attempt to create color ramp from props
1018 
1019  //create a new shapeburst fill layer with desired properties
1020  QgsShapeburstFillSymbolLayerV2* sl = new QgsShapeburstFillSymbolLayerV2( color, color2, colorType, blurRadius, useWholeShape, maxDistance );
1021  sl->setOffset( offset );
1022  if ( props.contains( "offset_unit" ) )
1023  {
1024  sl->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
1025  }
1026  if ( props.contains( "distance_unit" ) )
1027  {
1028  sl->setDistanceUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["distance_unit"] ) );
1029  }
1030  if ( props.contains( "offset_map_unit_scale" ) )
1031  {
1032  sl->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_map_unit_scale"] ) );
1033  }
1034  if ( props.contains( "distance_map_unit_scale" ) )
1035  {
1036  sl->setDistanceMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["distance_map_unit_scale"] ) );
1037  }
1038  if ( props.contains( "ignore_rings" ) )
1039  {
1040  sl->setIgnoreRings( props["ignore_rings"].toInt() );
1041  }
1042  if ( gradientRamp )
1043  {
1044  sl->setColorRamp( gradientRamp );
1045  }
1046 
1047  if ( props.contains( "color_expression" ) )
1048  sl->setDataDefinedProperty( "color", props["color_expression"] );
1049  if ( props.contains( "color2_expression" ) )
1050  sl->setDataDefinedProperty( "color2", props["color2_expression"] );
1051  if ( props.contains( "blur_radius_expression" ) )
1052  sl->setDataDefinedProperty( "blur_radius", props["blur_radius_expression"] );
1053  if ( props.contains( "use_whole_shape_expression" ) )
1054  sl->setDataDefinedProperty( "use_whole_shape", props["use_whole_shape_expression"] );
1055  if ( props.contains( "max_distance_expression" ) )
1056  sl->setDataDefinedProperty( "max_distance", props["max_distance_expression"] );
1057  if ( props.contains( "ignore_rings_expression" ) )
1058  sl->setDataDefinedProperty( "ignore_rings", props["ignore_rings_expression"] );
1059 
1060  return sl;
1061 }
1062 
1064 {
1065  return "ShapeburstFill";
1066 }
1067 
1069 {
1070  delete mGradientRamp;
1071  mGradientRamp = ramp;
1072 }
1073 
1074 void QgsShapeburstFillSymbolLayerV2::applyDataDefinedSymbology( QgsSymbolV2RenderContext& context, QColor& color, QColor& color2, int& blurRadius, bool& useWholeShape,
1075  double& maxDistance, bool& ignoreRings )
1076 {
1077  //first gradient color
1078  QgsExpression* colorExpression = expression( "color" );
1079  color = mColor;
1080  if ( colorExpression )
1081  color = QgsSymbolLayerV2Utils::decodeColor( colorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
1082 
1083  //second gradient color
1084  QgsExpression* colorExpression2 = expression( "color2" );
1085  color2 = mColor2;
1086  if ( colorExpression2 )
1087  color2 = QgsSymbolLayerV2Utils::decodeColor( colorExpression2->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
1088 
1089  //blur radius
1090  QgsExpression* blurRadiusExpression = expression( "blur_radius" );
1091  blurRadius = mBlurRadius;
1092  if ( blurRadiusExpression )
1093  blurRadius = blurRadiusExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toInt();
1094 
1095  //use whole shape
1096  QgsExpression* useWholeShapeExpression = expression( "use_whole_shape" );
1097  useWholeShape = mUseWholeShape;
1098  if ( useWholeShapeExpression )
1099  useWholeShape = useWholeShapeExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toBool();
1100 
1101  //max distance
1102  QgsExpression* maxDistanceExpression = expression( "max_distance" );
1103  maxDistance = mMaxDistance;
1104  if ( maxDistanceExpression )
1105  maxDistance = maxDistanceExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
1106 
1107  //ignore rings
1108  QgsExpression* ignoreRingsExpression = expression( "ignore_rings" );
1109  ignoreRings = mIgnoreRings;
1110  if ( ignoreRingsExpression )
1111  ignoreRings = ignoreRingsExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toBool();
1112 
1113 }
1114 
1116 {
1117  //TODO - check this
1118  QColor selColor = context.renderContext().selectionColor();
1119  if ( ! selectionIsOpaque ) selColor.setAlphaF( context.alpha() );
1120  mSelBrush = QBrush( selColor );
1121 
1122  prepareExpressions( context.fields(), context.renderContext().rendererScale() );
1123 }
1124 
1126 {
1127  Q_UNUSED( context );
1128 }
1129 
1130 void QgsShapeburstFillSymbolLayerV2::renderPolygon( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
1131 {
1132  QPainter* p = context.renderContext().painter();
1133  if ( !p )
1134  {
1135  return;
1136  }
1137 
1138  if ( context.selected() )
1139  {
1140  //feature is selected, draw using selection style
1141  p->setBrush( mSelBrush );
1142  QPointF offset;
1143  if ( !mOffset.isNull() )
1144  {
1147  p->translate( offset );
1148  }
1149  _renderPolygon( p, points, rings, context );
1150  if ( !mOffset.isNull() )
1151  {
1152  p->translate( -offset );
1153  }
1154  return;
1155  }
1156 
1157  QColor color1, color2;
1158  int blurRadius;
1159  bool useWholeShape;
1160  double maxDistance;
1161  bool ignoreRings;
1162  //calculate data defined symbology
1163  applyDataDefinedSymbology( context, color1, color2, blurRadius, useWholeShape, maxDistance, ignoreRings );
1164 
1165  //calculate max distance for shapeburst fill to extend from polygon boundary, in pixels
1166  int outputPixelMaxDist = 0;
1167  if ( !useWholeShape && maxDistance != 0 )
1168  {
1169  //convert max distance to pixels
1170  const QgsRenderContext& ctx = context.renderContext();
1171  outputPixelMaxDist = maxDistance * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mDistanceUnit, mDistanceMapUnitScale );
1172  }
1173 
1174  //if we are using the two color mode, create a gradient ramp
1176  {
1177  mTwoColorGradientRamp = new QgsVectorGradientColorRampV2( color1, color2 );
1178  }
1179 
1180  //no border for shapeburst fills
1181  p->setPen( QPen( Qt::NoPen ) );
1182 
1183  //calculate margin size in pixels so that QImage of polygon has sufficient space to draw the full blur effect
1184  int sideBuffer = 4 + ( blurRadius + 2 ) * 4;
1185  //create a QImage to draw shapeburst in
1186  double imWidth = points.boundingRect().width() + ( sideBuffer * 2 );
1187  double imHeight = points.boundingRect().height() + ( sideBuffer * 2 );
1188  QImage * fillImage = new QImage( imWidth * context.renderContext().rasterScaleFactor(),
1189  imHeight * context.renderContext().rasterScaleFactor(), QImage::Format_ARGB32_Premultiplied );
1190  //Fill this image with black. Initially the distance transform is drawn in greyscale, where black pixels have zero distance from the
1191  //polygon boundary. Since we don't care about pixels which fall outside the polygon, we start with a black image and then draw over it the
1192  //polygon in white. The distance transform function then fills in the correct distance values for the white pixels.
1193  fillImage->fill( Qt::black );
1194 
1195  //also create an image to store the alpha channel
1196  QImage * alphaImage = new QImage( fillImage->width(), fillImage->height(), QImage::Format_ARGB32_Premultiplied );
1197  //initially fill the alpha channel image with a transparent color
1198  alphaImage->fill( Qt::transparent );
1199 
1200  //now, draw the polygon in the alpha channel image
1201  QPainter imgPainter;
1202  imgPainter.begin( alphaImage );
1203  imgPainter.setRenderHint( QPainter::Antialiasing, true );
1204  imgPainter.setBrush( QBrush( Qt::white ) );
1205  imgPainter.setPen( QPen( Qt::black ) );
1206  imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1207  imgPainter.scale( context.renderContext().rasterScaleFactor(), context.renderContext().rasterScaleFactor() );
1208  _renderPolygon( &imgPainter, points, rings, context );
1209  imgPainter.end();
1210 
1211  //now that we have a render of the polygon in white, draw this onto the shapeburst fill image too
1212  //(this avoids calling _renderPolygon twice, since that can be slow)
1213  imgPainter.begin( fillImage );
1214  if ( !ignoreRings )
1215  {
1216  imgPainter.drawImage( 0, 0, *alphaImage );
1217  }
1218  else
1219  {
1220  //using ignore rings mode, so the alpha image can't be used
1221  //directly as the alpha channel contains polygon rings and we need
1222  //to draw now without any rings
1223  imgPainter.setBrush( QBrush( Qt::white ) );
1224  imgPainter.setPen( QPen( Qt::black ) );
1225  imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1226  imgPainter.scale( context.renderContext().rasterScaleFactor(), context.renderContext().rasterScaleFactor() );
1227  _renderPolygon( &imgPainter, points, NULL, context );
1228  }
1229  imgPainter.end();
1230 
1231  //apply distance transform to image, uses the current color ramp to calculate final pixel colors
1232  double * dtArray = distanceTransform( fillImage );
1233 
1234  //copy distance transform values back to QImage, shading by appropriate color ramp
1236  context.alpha(), useWholeShape, outputPixelMaxDist );
1237 
1238  //clean up some variables
1239  delete [] dtArray;
1241  {
1242  delete mTwoColorGradientRamp;
1243  }
1244 
1245  //apply blur if desired
1246  if ( blurRadius > 0 )
1247  {
1248  QgsSymbolLayerV2Utils::blurImageInPlace( *fillImage, QRect( 0, 0, fillImage->width(), fillImage->height() ), blurRadius, false );
1249  }
1250 
1251  //apply alpha channel to distance transform image, so that areas outside the polygon are transparent
1252  imgPainter.begin( fillImage );
1253  imgPainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
1254  imgPainter.drawImage( 0, 0, *alphaImage );
1255  imgPainter.end();
1256  //we're finished with the alpha channel image now
1257  delete alphaImage;
1258 
1259  //draw shapeburst image in correct place in the destination painter
1260 
1261  p->save();
1262  QPointF offset;
1263  if ( !mOffset.isNull() )
1264  {
1267  p->translate( offset );
1268  }
1269 
1270  p->scale( 1 / context.renderContext().rasterScaleFactor(), 1 / context.renderContext().rasterScaleFactor() );
1271  p->drawImage( points.boundingRect().left() - sideBuffer, points.boundingRect().top() - sideBuffer, *fillImage );
1272 
1273  delete fillImage;
1274 
1275  if ( !mOffset.isNull() )
1276  {
1277  p->translate( -offset );
1278  }
1279  p->restore();
1280 
1281 }
1282 
1283 //fast distance transform code, adapted from http://cs.brown.edu/~pff/dt/
1284 
1285 /* distance transform of a 1d function using squared distance */
1286 void QgsShapeburstFillSymbolLayerV2::distanceTransform1d( double *f, int n, int *v, double *z, double *d )
1287 {
1288  int k = 0;
1289  v[0] = 0;
1290  z[0] = -INF;
1291  z[1] = + INF;
1292  for ( int q = 1; q <= n - 1; q++ )
1293  {
1294  double s = (( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1295  while ( s <= z[k] )
1296  {
1297  k--;
1298  s = (( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1299  }
1300  k++;
1301  v[k] = q;
1302  z[k] = s;
1303  z[k+1] = + INF;
1304  }
1305 
1306  k = 0;
1307  for ( int q = 0; q <= n - 1; q++ )
1308  {
1309  while ( z[k+1] < q )
1310  k++;
1311  d[q] = ( q - v[k] ) * ( q - v[k] ) + f[v[k]];
1312  }
1313 }
1314 
1315 /* distance transform of 2d function using squared distance */
1316 void QgsShapeburstFillSymbolLayerV2::distanceTransform2d( double * im, int width, int height )
1317 {
1318  int maxDimension = qMax( width, height );
1319  double *f = new double[ maxDimension ];
1320  int *v = new int[ maxDimension ];
1321  double *z = new double[ maxDimension + 1 ];
1322  double *d = new double[ maxDimension ];
1323 
1324  // transform along columns
1325  for ( int x = 0; x < width; x++ )
1326  {
1327  for ( int y = 0; y < height; y++ )
1328  {
1329  f[y] = im[ x + y * width ];
1330  }
1331  distanceTransform1d( f, height, v, z, d );
1332  for ( int y = 0; y < height; y++ )
1333  {
1334  im[ x + y * width ] = d[y];
1335  }
1336  }
1337 
1338  // transform along rows
1339  for ( int y = 0; y < height; y++ )
1340  {
1341  for ( int x = 0; x < width; x++ )
1342  {
1343  f[x] = im[ x + y*width ];
1344  }
1345  distanceTransform1d( f, width, v, z, d );
1346  for ( int x = 0; x < width; x++ )
1347  {
1348  im[ x + y*width ] = d[x];
1349  }
1350  }
1351 
1352  delete [] d;
1353  delete [] f;
1354  delete [] v;
1355  delete [] z;
1356 }
1357 
1358 /* distance transform of a binary QImage */
1359 double * QgsShapeburstFillSymbolLayerV2::distanceTransform( QImage *im )
1360 {
1361  int width = im->width();
1362  int height = im->height();
1363 
1364  double * dtArray = new double[width * height];
1365 
1366  //load qImage to array
1367  QRgb tmpRgb;
1368  int idx = 0;
1369  for ( int heightIndex = 0; heightIndex < height; ++heightIndex )
1370  {
1371  QRgb* scanLine = ( QRgb* )im->constScanLine( heightIndex );
1372  for ( int widthIndex = 0; widthIndex < width; ++widthIndex )
1373  {
1374  tmpRgb = scanLine[widthIndex];
1375  if ( qRed( tmpRgb ) == 0 )
1376  {
1377  //black pixel, so zero distance
1378  dtArray[ idx ] = 0;
1379  }
1380  else
1381  {
1382  //white pixel, so initially set distance as infinite
1383  dtArray[ idx ] = INF;
1384  }
1385  idx++;
1386  }
1387  }
1388 
1389  //calculate squared distance transform
1390  distanceTransform2d( dtArray, width, height );
1391 
1392  return dtArray;
1393 }
1394 
1395 void QgsShapeburstFillSymbolLayerV2::dtArrayToQImage( double * array, QImage *im, QgsVectorColorRampV2* ramp, double layerAlpha, bool useWholeShape, int maxPixelDistance )
1396 {
1397  int width = im->width();
1398  int height = im->height();
1399 
1400  //find maximum distance value
1401  double maxDistanceValue;
1402 
1403  if ( useWholeShape )
1404  {
1405  //no max distance specified in symbol properties, so calculate from maximum value in distance transform results
1406  double dtMaxValue = array[0];
1407  for ( int i = 1; i < ( width * height ); ++i )
1408  {
1409  if ( array[i] > dtMaxValue )
1410  {
1411  dtMaxValue = array[i];
1412  }
1413  }
1414 
1415  //values in distance transform are squared
1416  maxDistanceValue = sqrt( dtMaxValue );
1417  }
1418  else
1419  {
1420  //use max distance set in symbol properties
1421  maxDistanceValue = maxPixelDistance;
1422  }
1423 
1424  //update the pixels in the provided QImage
1425  int idx = 0;
1426  double squaredVal = 0;
1427  double pixVal = 0;
1428  QColor pixColor;
1429  bool layerHasAlpha = layerAlpha < 1.0;
1430 
1431  for ( int heightIndex = 0; heightIndex < height; ++heightIndex )
1432  {
1433  QRgb* scanLine = ( QRgb* )im->scanLine( heightIndex );
1434  for ( int widthIndex = 0; widthIndex < width; ++widthIndex )
1435  {
1436  //result of distance transform
1437  squaredVal = array[idx];
1438 
1439  //scale result to fit in the range [0, 1]
1440  if ( maxDistanceValue > 0 )
1441  {
1442  pixVal = squaredVal > 0 ? qMin(( sqrt( squaredVal ) / maxDistanceValue ), 1.0 ) : 0;
1443  }
1444  else
1445  {
1446  pixVal = 1.0;
1447  }
1448 
1449  //convert value to color from ramp
1450  pixColor = ramp->color( pixVal );
1451 
1452  int pixAlpha = pixColor.alpha();
1453  if (( layerHasAlpha ) || ( pixAlpha != 255 ) )
1454  {
1455  //apply layer's transparency to alpha value
1456  double alpha = pixAlpha * layerAlpha;
1457  //premultiply ramp color since we are storing this in a ARGB32_Premultiplied QImage
1458  QgsSymbolLayerV2Utils::premultiplyColor( pixColor, alpha );
1459  }
1460 
1461  scanLine[widthIndex] = pixColor.rgba();
1462  idx++;
1463  }
1464  }
1465 }
1466 
1468 {
1469  QgsStringMap map;
1470  map["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
1471  map["gradient_color2"] = QgsSymbolLayerV2Utils::encodeColor( mColor2 );
1472  map["color_type"] = QString::number( mColorType );
1473  map["blur_radius"] = QString::number( mBlurRadius );
1474  map["use_whole_shape"] = QString::number( mUseWholeShape );
1475  map["max_distance"] = QString::number( mMaxDistance );
1476  map["distance_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mDistanceUnit );
1477  map["distance_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mDistanceMapUnitScale );
1478  map["ignore_rings"] = QString::number( mIgnoreRings );
1479  map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
1480  map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit );
1481  map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
1482 
1484 
1485  if ( mGradientRamp )
1486  {
1487  map.unite( mGradientRamp->properties() );
1488  }
1489 
1490  return map;
1491 }
1492 
1494 {
1496  if ( mGradientRamp )
1497  {
1498  sl->setColorRamp( mGradientRamp->clone() );
1499  }
1503  sl->setOffset( mOffset );
1504  sl->setOffsetUnit( mOffsetUnit );
1507  return sl;
1508 }
1509 
1511 {
1512  double offsetBleed = qMax( mOffset.x(), mOffset.y() );
1513  return offsetBleed;
1514 }
1515 
1517 {
1518  mDistanceUnit = unit;
1519  mOffsetUnit = unit;
1520 }
1521 
1523 {
1524  if ( mDistanceUnit == mOffsetUnit )
1525  {
1526  return mDistanceUnit;
1527  }
1528  return QgsSymbolV2::Mixed;
1529 }
1530 
1532 {
1533  mDistanceMapUnitScale = scale;
1534  mOffsetMapUnitScale = scale;
1535 }
1536 
1538 {
1540  {
1541  return mDistanceMapUnitScale;
1542  }
1543  return QgsMapUnitScale();
1544 }
1545 
1546 
1547 //QgsImageFillSymbolLayer
1548 
1550  : mNextAngle( 0.0 )
1551  , mOutlineWidth( 0.0 )
1552  , mOutlineWidthUnit( QgsSymbolV2::MM )
1553  , mOutline( 0 )
1554 {
1555  setSubSymbol( new QgsLineSymbolV2() );
1556 }
1557 
1559 {
1560 }
1561 
1562 void QgsImageFillSymbolLayer::renderPolygon( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
1563 {
1564  QPainter* p = context.renderContext().painter();
1565  if ( !p )
1566  {
1567  return;
1568  }
1569 
1570  mNextAngle = mAngle;
1571  applyDataDefinedSettings( context );
1572 
1573  p->setPen( QPen( Qt::NoPen ) );
1574  if ( context.selected() )
1575  {
1576  QColor selColor = context.renderContext().selectionColor();
1577  // Alister - this doesn't seem to work here
1578  //if ( ! selectionIsOpaque )
1579  // selColor.setAlphaF( context.alpha() );
1580  p->setBrush( QBrush( selColor ) );
1581  _renderPolygon( p, points, rings, context );
1582  }
1583 
1584  if ( qgsDoubleNear( mNextAngle, 0.0 ) )
1585  {
1586  p->setBrush( mBrush );
1587  }
1588  else
1589  {
1590  QTransform t = mBrush.transform();
1591  t.rotate( mNextAngle );
1592  QBrush rotatedBrush = mBrush;
1593  rotatedBrush.setTransform( t );
1594  p->setBrush( rotatedBrush );
1595  }
1596  _renderPolygon( p, points, rings, context );
1597  if ( mOutline )
1598  {
1599  mOutline->renderPolyline( points, context.feature(), context.renderContext(), -1, selectFillBorder && context.selected() );
1600  if ( rings )
1601  {
1602  QList<QPolygonF>::const_iterator ringIt = rings->constBegin();
1603  for ( ; ringIt != rings->constEnd(); ++ringIt )
1604  {
1605  mOutline->renderPolyline( *ringIt, context.feature(), context.renderContext(), -1, selectFillBorder && context.selected() );
1606  }
1607  }
1608  }
1609 }
1610 
1612 {
1613  if ( !symbol ) //unset current outline
1614  {
1615  delete mOutline;
1616  mOutline = 0;
1617  return true;
1618  }
1619 
1620  if ( symbol->type() != QgsSymbolV2::Line )
1621  {
1622  delete symbol;
1623  return false;
1624  }
1625 
1626  QgsLineSymbolV2* lineSymbol = dynamic_cast<QgsLineSymbolV2*>( symbol );
1627  if ( lineSymbol )
1628  {
1629  delete mOutline;
1630  mOutline = lineSymbol;
1631  return true;
1632  }
1633 
1634  delete symbol;
1635  return false;
1636 }
1637 
1639 {
1640  mOutlineWidthUnit = unit;
1641 }
1642 
1644 {
1645  return mOutlineWidthUnit;
1646 }
1647 
1649 {
1650  mOutlineWidthMapUnitScale = scale;
1651 }
1652 
1654 {
1656 }
1657 
1659 {
1660  if ( mOutline && mOutline->symbolLayer( 0 ) )
1661  {
1662  double subLayerBleed = mOutline->symbolLayer( 0 )->estimateMaxBleed();
1663  return subLayerBleed;
1664  }
1665  return 0;
1666 }
1667 
1669 {
1670  double width = mOutlineWidth;
1671  QgsExpression* widthExpression = expression( "width" );
1672  if ( widthExpression )
1673  {
1674  width = widthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
1675  }
1677 }
1678 
1680 {
1681  Q_UNUSED( context );
1682  if ( !mOutline )
1683  {
1684  return QColor( Qt::black );
1685  }
1686  return mOutline->color();
1687 }
1688 
1690 {
1691  return Qt::SolidLine;
1692 #if 0
1693  if ( !mOutline )
1694  {
1695  return Qt::SolidLine;
1696  }
1697  else
1698  {
1699  return mOutline->dxfPenStyle();
1700  }
1701 #endif //0
1702 }
1703 
1704 
1705 //QgsSVGFillSymbolLayer
1706 
1707 QgsSVGFillSymbolLayer::QgsSVGFillSymbolLayer( const QString& svgFilePath, double width, double angle ): QgsImageFillSymbolLayer(),
1708  mPatternWidth( width ),
1709  mPatternWidthUnit( QgsSymbolV2::MM ),
1710  mSvgOutlineWidthUnit( QgsSymbolV2::MM )
1711 {
1712  setSvgFilePath( svgFilePath );
1713  mOutlineWidth = 0.3;
1714  mAngle = angle;
1715  setDefaultSvgParams();
1716  mSvgPattern = 0;
1717 }
1718 
1719 QgsSVGFillSymbolLayer::QgsSVGFillSymbolLayer( const QByteArray& svgData, double width, double angle ): QgsImageFillSymbolLayer(),
1720  mPatternWidth( width ),
1721  mPatternWidthUnit( QgsSymbolV2::MM ),
1722  mSvgData( svgData ),
1723  mSvgOutlineWidthUnit( QgsSymbolV2::MM )
1724 {
1725  storeViewBox();
1726  mOutlineWidth = 0.3;
1727  mAngle = angle;
1728  setSubSymbol( new QgsLineSymbolV2() );
1729  setDefaultSvgParams();
1730  mSvgPattern = 0;
1731 }
1732 
1734 {
1735  delete mSvgPattern;
1736 }
1737 
1739 {
1741  mPatternWidthUnit = unit;
1742  mSvgOutlineWidthUnit = unit;
1743  mOutlineWidthUnit = unit;
1744 }
1745 
1747 {
1749  if ( mPatternWidthUnit != unit || mSvgOutlineWidthUnit != unit || mOutlineWidthUnit != unit )
1750  {
1751  return QgsSymbolV2::Mixed;
1752  }
1753  return unit;
1754 }
1755 
1757 {
1759  mPatternWidthMapUnitScale = scale;
1761  mOutlineWidthMapUnitScale = scale;
1762 }
1763 
1765 {
1769  {
1771  }
1772  return QgsMapUnitScale();
1773 }
1774 
1775 void QgsSVGFillSymbolLayer::setSvgFilePath( const QString& svgPath )
1776 {
1778  storeViewBox();
1779 
1780  mSvgFilePath = svgPath;
1781  setDefaultSvgParams();
1782 }
1783 
1785 {
1786  QByteArray data;
1787  double width = 20;
1788  QString svgFilePath;
1789  double angle = 0.0;
1790 
1791  if ( properties.contains( "width" ) )
1792  {
1793  width = properties["width"].toDouble();
1794  }
1795  if ( properties.contains( "svgFile" ) )
1796  {
1797  QString svgName = properties["svgFile"];
1798  QString savePath = QgsSymbolLayerV2Utils::symbolNameToPath( svgName );
1799  svgFilePath = ( savePath.isEmpty() ? svgName : savePath );
1800  }
1801  if ( properties.contains( "angle" ) )
1802  {
1803  angle = properties["angle"].toDouble();
1804  }
1805 
1806  QgsSVGFillSymbolLayer* symbolLayer = 0;
1807  if ( !svgFilePath.isEmpty() )
1808  {
1809  symbolLayer = new QgsSVGFillSymbolLayer( svgFilePath, width, angle );
1810  }
1811  else
1812  {
1813  if ( properties.contains( "data" ) )
1814  {
1815  data = QByteArray::fromHex( properties["data"].toLocal8Bit() );
1816  }
1817  symbolLayer = new QgsSVGFillSymbolLayer( data, width, angle );
1818  }
1819 
1820  //svg parameters
1821  if ( properties.contains( "svgFillColor" ) )
1822  {
1823  //pre 2.5 projects used "svgFillColor"
1824  symbolLayer->setSvgFillColor( QgsSymbolLayerV2Utils::decodeColor( properties["svgFillColor"] ) );
1825  }
1826  else if ( properties.contains( "color" ) )
1827  {
1828  symbolLayer->setSvgFillColor( QgsSymbolLayerV2Utils::decodeColor( properties["color"] ) );
1829  }
1830  if ( properties.contains( "svgOutlineColor" ) )
1831  {
1832  //pre 2.5 projects used "svgOutlineColor"
1833  symbolLayer->setSvgOutlineColor( QgsSymbolLayerV2Utils::decodeColor( properties["svgOutlineColor"] ) );
1834  }
1835  else if ( properties.contains( "outline_color" ) )
1836  {
1837  symbolLayer->setSvgOutlineColor( QgsSymbolLayerV2Utils::decodeColor( properties["outline_color"] ) );
1838  }
1839  else if ( properties.contains( "line_color" ) )
1840  {
1841  symbolLayer->setSvgOutlineColor( QgsSymbolLayerV2Utils::decodeColor( properties["line_color"] ) );
1842  }
1843  if ( properties.contains( "svgOutlineWidth" ) )
1844  {
1845  //pre 2.5 projects used "svgOutlineWidth"
1846  symbolLayer->setSvgOutlineWidth( properties["svgOutlineWidth"].toDouble() );
1847  }
1848  else if ( properties.contains( "outline_width" ) )
1849  {
1850  symbolLayer->setSvgOutlineWidth( properties["outline_width"].toDouble() );
1851  }
1852  else if ( properties.contains( "line_width" ) )
1853  {
1854  symbolLayer->setSvgOutlineWidth( properties["line_width"].toDouble() );
1855  }
1856 
1857  //units
1858  if ( properties.contains( "pattern_width_unit" ) )
1859  {
1860  symbolLayer->setPatternWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["pattern_width_unit"] ) );
1861  }
1862  if ( properties.contains( "pattern_width_map_unit_scale" ) )
1863  {
1864  symbolLayer->setPatternWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["pattern_width_map_unit_scale"] ) );
1865  }
1866  if ( properties.contains( "svg_outline_width_unit" ) )
1867  {
1868  symbolLayer->setSvgOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["svg_outline_width_unit"] ) );
1869  }
1870  if ( properties.contains( "svg_outline_width_map_unit_scale" ) )
1871  {
1872  symbolLayer->setSvgOutlineWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["svg_outline_width_map_unit_scale"] ) );
1873  }
1874  if ( properties.contains( "outline_width_unit" ) )
1875  {
1876  symbolLayer->setOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["outline_width_unit"] ) );
1877  }
1878  if ( properties.contains( "outline_width_map_unit_scale" ) )
1879  {
1880  symbolLayer->setOutlineWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["outline_width_map_unit_scale"] ) );
1881  }
1882 
1883  if ( properties.contains( "width_expression" ) )
1884  symbolLayer->setDataDefinedProperty( "width", properties["width_expression"] );
1885  if ( properties.contains( "svgFile_expression" ) )
1886  symbolLayer->setDataDefinedProperty( "svgFile", properties["svgFile_expression"] );
1887  if ( properties.contains( "angle_expression" ) )
1888  symbolLayer->setDataDefinedProperty( "angle", properties["angle_expression"] );
1889  if ( properties.contains( "svgFillColor_expression" ) )
1890  symbolLayer->setDataDefinedProperty( "svgFillColor", properties["svgFillColor_expression"] );
1891  if ( properties.contains( "svgOutlineColor_expression" ) )
1892  symbolLayer->setDataDefinedProperty( "svgOutlineColor", properties["svgOutlineColor_expression"] );
1893  if ( properties.contains( "svgOutlineWidth_expression" ) )
1894  symbolLayer->setDataDefinedProperty( "svgOutlineWidth", properties["svgOutlineWidth_expression"] );
1895 
1896  return symbolLayer;
1897 }
1898 
1900 {
1901  return "SVGFill";
1902 }
1903 
1904 void QgsSVGFillSymbolLayer::applyPattern( QBrush& brush, const QString& svgFilePath, double patternWidth, QgsSymbolV2::OutputUnit patternWidthUnit,
1905  const QColor& svgFillColor, const QColor& svgOutlineColor, double svgOutlineWidth,
1906  QgsSymbolV2::OutputUnit svgOutlineWidthUnit, const QgsSymbolV2RenderContext& context,
1907  const QgsMapUnitScale& patternWidthMapUnitScale, const QgsMapUnitScale& svgOutlineWidthMapUnitScale )
1908 {
1909  if ( mSvgViewBox.isNull() )
1910  {
1911  return;
1912  }
1913 
1914  delete mSvgPattern;
1915  mSvgPattern = 0;
1917 
1918  if (( int )size < 1.0 || 10000.0 < size )
1919  {
1920  mSvgPattern = new QImage();
1921  brush.setTextureImage( *mSvgPattern );
1922  }
1923  else
1924  {
1925  bool fitsInCache = true;
1927  const QImage& patternImage = QgsSvgCache::instance()->svgAsImage( svgFilePath, size, svgFillColor, svgOutlineColor, outlineWidth,
1928  context.renderContext().scaleFactor(), context.renderContext().rasterScaleFactor(), fitsInCache );
1929  if ( !fitsInCache )
1930  {
1931  const QPicture& patternPict = QgsSvgCache::instance()->svgAsPicture( svgFilePath, size, svgFillColor, svgOutlineColor, outlineWidth,
1932  context.renderContext().scaleFactor(), 1.0 );
1933  double hwRatio = 1.0;
1934  if ( patternPict.width() > 0 )
1935  {
1936  hwRatio = ( double )patternPict.height() / ( double )patternPict.width();
1937  }
1938  mSvgPattern = new QImage(( int )size, ( int )( size * hwRatio ), QImage::Format_ARGB32_Premultiplied );
1939  mSvgPattern->fill( 0 ); // transparent background
1940 
1941  QPainter p( mSvgPattern );
1942  p.drawPicture( QPointF( size / 2, size * hwRatio / 2 ), patternPict );
1943  }
1944 
1945  QTransform brushTransform;
1946  brushTransform.scale( 1.0 / context.renderContext().rasterScaleFactor(), 1.0 / context.renderContext().rasterScaleFactor() );
1947  if ( !qgsDoubleNear( context.alpha(), 1.0 ) )
1948  {
1949  QImage transparentImage = fitsInCache ? patternImage.copy() : mSvgPattern->copy();
1950  QgsSymbolLayerV2Utils::multiplyImageOpacity( &transparentImage, context.alpha() );
1951  brush.setTextureImage( transparentImage );
1952  }
1953  else
1954  {
1955  brush.setTextureImage( fitsInCache ? patternImage : *mSvgPattern );
1956  }
1957  brush.setTransform( brushTransform );
1958  }
1959 }
1960 
1962 {
1963 
1965 
1966  if ( mOutline )
1967  {
1968  mOutline->startRender( context.renderContext(), context.fields() );
1969  }
1970 
1971  prepareExpressions( context.fields(), context.renderContext().rendererScale() );
1972 }
1973 
1975 {
1976  if ( mOutline )
1977  {
1978  mOutline->stopRender( context.renderContext() );
1979  }
1980 }
1981 
1983 {
1984  QgsStringMap map;
1985  if ( !mSvgFilePath.isEmpty() )
1986  {
1987  map.insert( "svgFile", QgsSymbolLayerV2Utils::symbolPathToName( mSvgFilePath ) );
1988  }
1989  else
1990  {
1991  map.insert( "data", QString( mSvgData.toHex() ) );
1992  }
1993 
1994  map.insert( "width", QString::number( mPatternWidth ) );
1995  map.insert( "angle", QString::number( mAngle ) );
1996 
1997  //svg parameters
1998  map.insert( "color", QgsSymbolLayerV2Utils::encodeColor( mSvgFillColor ) );
1999  map.insert( "outline_color", QgsSymbolLayerV2Utils::encodeColor( mSvgOutlineColor ) );
2000  map.insert( "outline_width", QString::number( mSvgOutlineWidth ) );
2001 
2002  //units
2003  map.insert( "pattern_width_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mPatternWidthUnit ) );
2004  map.insert( "pattern_width_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mPatternWidthMapUnitScale ) );
2005  map.insert( "svg_outline_width_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mSvgOutlineWidthUnit ) );
2006  map.insert( "svg_outline_width_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mSvgOutlineWidthMapUnitScale ) );
2007  map.insert( "outline_width_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mOutlineWidthUnit ) );
2008  map.insert( "outline_width_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mOutlineWidthMapUnitScale ) );
2009 
2011  return map;
2012 }
2013 
2015 {
2016  QgsSVGFillSymbolLayer* clonedLayer = 0;
2017  if ( !mSvgFilePath.isEmpty() )
2018  {
2019  clonedLayer = new QgsSVGFillSymbolLayer( mSvgFilePath, mPatternWidth, mAngle );
2020  clonedLayer->setSvgFillColor( mSvgFillColor );
2021  clonedLayer->setSvgOutlineColor( mSvgOutlineColor );
2022  clonedLayer->setSvgOutlineWidth( mSvgOutlineWidth );
2023  }
2024  else
2025  {
2026  clonedLayer = new QgsSVGFillSymbolLayer( mSvgData, mPatternWidth, mAngle );
2027  }
2028 
2029  clonedLayer->setPatternWidthUnit( mPatternWidthUnit );
2033  clonedLayer->setOutlineWidthUnit( mOutlineWidthUnit );
2035 
2036  if ( mOutline )
2037  {
2038  clonedLayer->setSubSymbol( mOutline->clone() );
2039  }
2040  copyDataDefinedProperties( clonedLayer );
2041  return clonedLayer;
2042 }
2043 
2044 void QgsSVGFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
2045 {
2046  QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
2047  if ( !props.value( "uom", "" ).isEmpty() )
2048  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
2049  element.appendChild( symbolizerElem );
2050 
2051  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
2052 
2053  QDomElement fillElem = doc.createElement( "se:Fill" );
2054  symbolizerElem.appendChild( fillElem );
2055 
2056  QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" );
2057  fillElem.appendChild( graphicFillElem );
2058 
2059  QDomElement graphicElem = doc.createElement( "se:Graphic" );
2060  graphicFillElem.appendChild( graphicElem );
2061 
2062  if ( !mSvgFilePath.isEmpty() )
2063  {
2065  }
2066  else
2067  {
2068  // TODO: create svg from data
2069  // <se:InlineContent>
2070  symbolizerElem.appendChild( doc.createComment( "SVG from data not implemented yet" ) );
2071  }
2072 
2073  if ( mSvgOutlineColor.isValid() || mSvgOutlineWidth >= 0 )
2074  {
2075  QgsSymbolLayerV2Utils::lineToSld( doc, graphicElem, Qt::SolidLine, mSvgOutlineColor, mSvgOutlineWidth );
2076  }
2077 
2078  // <Rotation>
2079  QString angleFunc;
2080  bool ok;
2081  double angle = props.value( "angle", "0" ).toDouble( &ok );
2082  if ( !ok )
2083  {
2084  angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mAngle );
2085  }
2086  else if ( angle + mAngle != 0 )
2087  {
2088  angleFunc = QString::number( angle + mAngle );
2089  }
2090  QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
2091 
2092  if ( mOutline )
2093  {
2094  // the outline sub symbol should be stored within the Stroke element,
2095  // but it will be stored in a separated LineSymbolizer because it could
2096  // have more than one layer
2097  mOutline->toSld( doc, element, props );
2098  }
2099 }
2100 
2102 {
2103  QgsDebugMsg( "Entered." );
2104 
2105  QString path, mimeType;
2106  QColor fillColor, borderColor;
2107  Qt::PenStyle penStyle;
2108  double size, borderWidth;
2109 
2110  QDomElement fillElem = element.firstChildElement( "Fill" );
2111  if ( fillElem.isNull() )
2112  return NULL;
2113 
2114  QDomElement graphicFillElem = fillElem.firstChildElement( "GraphicFill" );
2115  if ( graphicFillElem.isNull() )
2116  return NULL;
2117 
2118  QDomElement graphicElem = graphicFillElem.firstChildElement( "Graphic" );
2119  if ( graphicElem.isNull() )
2120  return NULL;
2121 
2122  if ( !QgsSymbolLayerV2Utils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
2123  return NULL;
2124 
2125  if ( mimeType != "image/svg+xml" )
2126  return NULL;
2127 
2128  QgsSymbolLayerV2Utils::lineFromSld( graphicElem, penStyle, borderColor, borderWidth );
2129 
2130  double angle = 0.0;
2131  QString angleFunc;
2132  if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
2133  {
2134  bool ok;
2135  double d = angleFunc.toDouble( &ok );
2136  if ( ok )
2137  angle = d;
2138  }
2139 
2140  QgsSVGFillSymbolLayer* sl = new QgsSVGFillSymbolLayer( path, size, angle );
2141  sl->setSvgFillColor( fillColor );
2142  sl->setSvgOutlineColor( borderColor );
2143  sl->setSvgOutlineWidth( borderWidth );
2144 
2145  // try to get the outline
2146  QDomElement strokeElem = element.firstChildElement( "Stroke" );
2147  if ( !strokeElem.isNull() )
2148  {
2150  if ( l )
2151  {
2152  QgsSymbolLayerV2List layers;
2153  layers.append( l );
2154  sl->setSubSymbol( new QgsLineSymbolV2( layers ) );
2155  }
2156  }
2157 
2158  return sl;
2159 }
2160 
2162 {
2163  QgsExpression* widthExpression = expression( "width" );
2164  QgsExpression* svgFileExpression = expression( "svgFile" );
2165  QgsExpression* fillColorExpression = expression( "svgFillColor" );
2166  QgsExpression* outlineColorExpression = expression( "svgOutlineColor" );
2167  QgsExpression* outlineWidthExpression = expression( "svgOutlineWidth" );
2168  QgsExpression* angleExpression = expression( "angle" );
2169  if ( !widthExpression && !svgFileExpression && !fillColorExpression && !outlineColorExpression && !outlineWidthExpression && !angleExpression )
2170  {
2171  return; //no data defined settings
2172  }
2173 
2174  if ( angleExpression )
2175  {
2176  mNextAngle = angleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
2177  }
2178 
2179  double width = mPatternWidth;
2180  if ( widthExpression )
2181  {
2182  width = widthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
2183  }
2184  QString svgFile = mSvgFilePath;
2185  if ( svgFileExpression )
2186  {
2187  svgFile = svgFileExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
2188  }
2189  QColor svgFillColor = mSvgFillColor;
2190  if ( fillColorExpression )
2191  {
2192  svgFillColor = QgsSymbolLayerV2Utils::decodeColor( fillColorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
2193  }
2194  QColor svgOutlineColor = mSvgOutlineColor;
2195  if ( outlineColorExpression )
2196  {
2197  svgOutlineColor = QgsSymbolLayerV2Utils::decodeColor( outlineColorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
2198  }
2199  double outlineWidth = mSvgOutlineWidth;
2200  if ( outlineWidthExpression )
2201  {
2202  outlineWidth = outlineWidthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
2203  }
2204  applyPattern( mBrush, svgFile, width, mPatternWidthUnit, svgFillColor, svgOutlineColor, outlineWidth,
2206 
2207 }
2208 
2209 void QgsSVGFillSymbolLayer::storeViewBox()
2210 {
2211  if ( !mSvgData.isEmpty() )
2212  {
2213  QSvgRenderer r( mSvgData );
2214  if ( r.isValid() )
2215  {
2216  mSvgViewBox = r.viewBoxF();
2217  return;
2218  }
2219  }
2220 
2221  mSvgViewBox = QRectF();
2222  return;
2223 }
2224 
2225 void QgsSVGFillSymbolLayer::setDefaultSvgParams()
2226 {
2227  //default values
2228  mSvgFillColor = QColor( 0, 0, 0 );
2229  mSvgOutlineColor = QColor( 0, 0, 0 );
2230  mSvgOutlineWidth = 0.3;
2231 
2232  if ( mSvgFilePath.isEmpty() )
2233  {
2234  return;
2235  }
2236 
2237  bool hasFillParam, hasOutlineParam, hasOutlineWidthParam;
2238  QColor defaultFillColor, defaultOutlineColor;
2239  double defaultOutlineWidth;
2240  QgsSvgCache::instance()->containsParams( mSvgFilePath, hasFillParam, defaultFillColor, hasOutlineParam, defaultOutlineColor, hasOutlineWidthParam,
2241  defaultOutlineWidth );
2242 
2243  if ( hasFillParam )
2244  {
2245  mSvgFillColor = defaultFillColor;
2246  }
2247  if ( hasOutlineParam )
2248  {
2249  mSvgOutlineColor = defaultOutlineColor;
2250  }
2251  if ( hasOutlineWidthParam )
2252  {
2253  mSvgOutlineWidth = defaultOutlineWidth;
2254  }
2255 }
2256 
2257 
2260  , mDistance( 5.0 )
2261  , mDistanceUnit( QgsSymbolV2::MM )
2262  , mLineWidth( 0 )
2263  , mLineWidthUnit( QgsSymbolV2::MM )
2264  , mLineAngle( 45.0 )
2265  , mOffset( 0.0 )
2266  , mOffsetUnit( QgsSymbolV2::MM )
2267  , mFillLineSymbol( 0 )
2268 {
2269  setSubSymbol( new QgsLineSymbolV2() );
2270  QgsImageFillSymbolLayer::setSubSymbol( 0 ); //no outline
2271 }
2272 
2274 {
2275  mFillLineSymbol->setWidth( w );
2276  mLineWidth = w;
2277 }
2278 
2280 {
2281  mFillLineSymbol->setColor( c );
2282  mColor = c;
2283 }
2284 
2286 {
2287  delete mFillLineSymbol;
2288 }
2289 
2291 {
2292  if ( !symbol )
2293  {
2294  return false;
2295  }
2296 
2297  if ( symbol->type() == QgsSymbolV2::Line )
2298  {
2299  QgsLineSymbolV2* lineSymbol = dynamic_cast<QgsLineSymbolV2*>( symbol );
2300  if ( lineSymbol )
2301  {
2302  delete mFillLineSymbol;
2303  mFillLineSymbol = lineSymbol;
2304 
2305  return true;
2306  }
2307  }
2308  delete symbol;
2309  return false;
2310 }
2311 
2313 {
2314  return mFillLineSymbol;
2315 }
2316 
2318 {
2319  return 0;
2320 }
2321 
2323 {
2325  mDistanceUnit = unit;
2326  mLineWidthUnit = unit;
2327  mOffsetUnit = unit;
2328 }
2329 
2331 {
2333  if ( mDistanceUnit != unit || mLineWidthUnit != unit || mOffsetUnit != unit )
2334  {
2335  return QgsSymbolV2::Mixed;
2336  }
2337  return unit;
2338 }
2339 
2341 {
2343  mDistanceMapUnitScale = scale;
2344  mLineWidthMapUnitScale = scale;
2345  mOffsetMapUnitScale = scale;
2346 }
2347 
2349 {
2353  {
2354  return mDistanceMapUnitScale;
2355  }
2356  return QgsMapUnitScale();
2357 }
2358 
2360 {
2362 
2363  //default values
2364  double lineAngle = 45;
2365  double distance = 5;
2366  double lineWidth = 0.5;
2367  QColor color( Qt::black );
2368  double offset = 0.0;
2369 
2370  if ( properties.contains( "lineangle" ) )
2371  {
2372  //pre 2.5 projects used "lineangle"
2373  lineAngle = properties["lineangle"].toDouble();
2374  }
2375  else if ( properties.contains( "angle" ) )
2376  {
2377  lineAngle = properties["angle"].toDouble();
2378  }
2379  patternLayer->setLineAngle( lineAngle );
2380 
2381  if ( properties.contains( "distance" ) )
2382  {
2383  distance = properties["distance"].toDouble();
2384  }
2385  patternLayer->setDistance( distance );
2386 
2387  if ( properties.contains( "linewidth" ) )
2388  {
2389  //pre 2.5 projects used "linewidth"
2390  lineWidth = properties["linewidth"].toDouble();
2391  }
2392  else if ( properties.contains( "outline_width" ) )
2393  {
2394  lineWidth = properties["outline_width"].toDouble();
2395  }
2396  else if ( properties.contains( "line_width" ) )
2397  {
2398  lineWidth = properties["line_width"].toDouble();
2399  }
2400  patternLayer->setLineWidth( lineWidth );
2401 
2402  if ( properties.contains( "color" ) )
2403  {
2404  color = QgsSymbolLayerV2Utils::decodeColor( properties["color"] );
2405  }
2406  else if ( properties.contains( "outline_color" ) )
2407  {
2408  color = QgsSymbolLayerV2Utils::decodeColor( properties["outline_color"] );
2409  }
2410  else if ( properties.contains( "line_color" ) )
2411  {
2412  color = QgsSymbolLayerV2Utils::decodeColor( properties["line_color"] );
2413  }
2414  patternLayer->setColor( color );
2415 
2416  if ( properties.contains( "offset" ) )
2417  {
2418  offset = properties["offset"].toDouble();
2419  }
2420  patternLayer->setOffset( offset );
2421 
2422 
2423  if ( properties.contains( "distance_unit" ) )
2424  {
2425  patternLayer->setDistanceUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["distance_unit"] ) );
2426  }
2427  if ( properties.contains( "distance_map_unit_scale" ) )
2428  {
2429  patternLayer->setDistanceMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["distance_map_unit_scale"] ) );
2430  }
2431  if ( properties.contains( "line_width_unit" ) )
2432  {
2433  patternLayer->setLineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["line_width_unit"] ) );
2434  }
2435  else if ( properties.contains( "outline_width_unit" ) )
2436  {
2437  patternLayer->setLineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["outline_width_unit"] ) );
2438  }
2439  if ( properties.contains( "line_width_map_unit_scale" ) )
2440  {
2441  patternLayer->setLineWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["line_width_map_unit_scale"] ) );
2442  }
2443  if ( properties.contains( "offset_unit" ) )
2444  {
2445  patternLayer->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["offset_unit"] ) );
2446  }
2447  if ( properties.contains( "offset_map_unit_scale" ) )
2448  {
2449  patternLayer->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["offset_map_unit_scale"] ) );
2450  }
2451  if ( properties.contains( "outline_width_unit" ) )
2452  {
2453  patternLayer->setOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["outline_width_unit"] ) );
2454  }
2455  if ( properties.contains( "outline_width_map_unit_scale" ) )
2456  {
2457  patternLayer->setOutlineWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["outline_width_map_unit_scale"] ) );
2458  }
2459 
2460 
2461  //data defined properties
2462  if ( properties.contains( "lineangle_expression" ) )
2463  {
2464  patternLayer->setDataDefinedProperty( "lineangle", properties["lineangle_expression"] );
2465  }
2466  if ( properties.contains( "distance_expression" ) )
2467  {
2468  patternLayer->setDataDefinedProperty( "distance", properties["distance_expression"] );
2469  }
2470  if ( properties.contains( "linewidth_expression" ) )
2471  {
2472  patternLayer->setDataDefinedProperty( "linewidth", properties["linewidth_expression"] );
2473  }
2474  if ( properties.contains( "color_expression" ) )
2475  {
2476  patternLayer->setDataDefinedProperty( "color", properties["color_expression"] );
2477  }
2478  return patternLayer;
2479 }
2480 
2482 {
2483  return "LinePatternFill";
2484 }
2485 
2486 void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolV2RenderContext& context, QBrush& brush, double lineAngle, double distance,
2487  double lineWidth, const QColor& color )
2488 {
2489  Q_UNUSED( lineWidth );
2490  Q_UNUSED( color );
2491 
2492  mBrush.setTextureImage( QImage() ); // set empty in case we have to return
2493 
2494  if ( !mFillLineSymbol )
2495  {
2496  return;
2497  }
2498  // We have to make a copy because marker intervals will have to be adjusted
2499  QgsLineSymbolV2* fillLineSymbol = dynamic_cast<QgsLineSymbolV2*>( mFillLineSymbol->clone() );
2500  if ( !fillLineSymbol )
2501  {
2502  return;
2503  }
2504 
2505  const QgsRenderContext& ctx = context.renderContext();
2506  //double outlinePixelWidth = lineWidth * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mLineWidthUnit, mLineWidthMapUnitScale );
2507  double outputPixelDist = distance * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mDistanceUnit, mDistanceMapUnitScale );
2509 
2510  // To get all patterns into image, we have to consider symbols size (estimateMaxBleed()).
2511  // For marker lines we have to get markers interval.
2512  double outputPixelBleed = 0;
2513  double outputPixelInterval = 0; // maximum interval
2514  for ( int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2515  {
2516  QgsSymbolLayerV2 *layer = fillLineSymbol->symbolLayer( i );
2517  double layerBleed = layer->estimateMaxBleed();
2518  // TODO: to get real bleed we have to scale it using context and units,
2519  // unfortunately estimateMaxBleed() ignore units completely, e.g.
2520  // QgsMarkerLineSymbolLayerV2::estimateMaxBleed() is mixing marker size and
2521  // offset regardless units. This has to be fixed especially
2522  // in estimateMaxBleed(), context probably has to be used.
2523  // For now, we only support millimeters
2524  double outputPixelLayerBleed = layerBleed * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, QgsSymbolV2::MM );
2525  outputPixelBleed = qMax( outputPixelBleed, outputPixelLayerBleed );
2526 
2527  QgsMarkerLineSymbolLayerV2 *markerLineLayer = dynamic_cast<QgsMarkerLineSymbolLayerV2 *>( layer );
2528  if ( markerLineLayer )
2529  {
2530  double outputPixelLayerInterval = markerLineLayer->interval() * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, markerLineLayer->intervalUnit(), markerLineLayer->intervalMapUnitScale() );
2531 
2532  // There may be multiple marker lines with different intervals.
2533  // In theory we should find the least common multiple, but that could be too
2534  // big (multiplication of intervals in the worst case).
2535  // Because patterns without small common interval would look strange, we
2536  // believe that the longest interval should usually be sufficient.
2537  outputPixelInterval = qMax( outputPixelInterval, outputPixelLayerInterval );
2538  }
2539  }
2540 
2541  if ( outputPixelInterval > 0 )
2542  {
2543  // We have to adjust marker intervals to integer pixel size to get
2544  // repeatable pattern.
2545  double intervalScale = qRound( outputPixelInterval ) / outputPixelInterval;
2546  outputPixelInterval = qRound( outputPixelInterval );
2547 
2548  for ( int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2549  {
2550  QgsSymbolLayerV2 *layer = fillLineSymbol->symbolLayer( i );
2551 
2552  QgsMarkerLineSymbolLayerV2 *markerLineLayer = dynamic_cast<QgsMarkerLineSymbolLayerV2 *>( layer );
2553  if ( markerLineLayer )
2554  {
2555  markerLineLayer->setInterval( intervalScale * markerLineLayer->interval() );
2556  }
2557  }
2558  }
2559 
2560  //create image
2561  int height, width;
2562  if ( qgsDoubleNear( lineAngle, 0 ) || qgsDoubleNear( lineAngle, 360 ) || qgsDoubleNear( lineAngle, 180 ) )
2563  {
2564  height = outputPixelDist;
2565  width = outputPixelInterval > 0 ? outputPixelInterval : height;
2566  }
2567  else if ( qgsDoubleNear( lineAngle, 90 ) || qgsDoubleNear( lineAngle, 270 ) )
2568  {
2569  width = outputPixelDist;
2570  height = outputPixelInterval > 0 ? outputPixelInterval : width;
2571  }
2572  else
2573  {
2574  height = outputPixelDist / cos( lineAngle * M_PI / 180 ); //keep perpendicular distance between lines constant
2575  width = outputPixelDist / sin( lineAngle * M_PI / 180 );
2576 
2577  // recalculate real angle and distance after rounding to pixels
2578  lineAngle = 180 * atan2(( double ) height, ( double ) width ) / M_PI;
2579  if ( lineAngle < 0 )
2580  {
2581  lineAngle += 360.;
2582  }
2583 
2584  height = qAbs( height );
2585  width = qAbs( width );
2586 
2587  outputPixelDist = height * cos( lineAngle * M_PI / 180 );
2588 
2589  // Round offset to correspond to one pixel height, otherwise lines may
2590  // be shifted on tile border if offset falls close to pixel center
2591  int offsetHeight = qRound( qAbs( outputPixelOffset / cos( lineAngle * M_PI / 180 ) ) );
2592  outputPixelOffset = offsetHeight * cos( lineAngle * M_PI / 180 );
2593  }
2594 
2595  //depending on the angle, we might need to render into a larger image and use a subset of it
2596  double dx = 0;
2597  double dy = 0;
2598 
2599  // Add buffer based on bleed but keep precisely the height/width ratio (angle)
2600  // thus we add integer multiplications of width and height covering the bleed
2601  int bufferMulti = qMax( qCeil( outputPixelBleed / width ), qCeil( outputPixelBleed / width ) );
2602 
2603  // Always buffer at least once so that center of line marker in upper right corner
2604  // does not fall outside due to representation error
2605  bufferMulti = qMax( bufferMulti, 1 );
2606 
2607  int xBuffer = width * bufferMulti;
2608  int yBuffer = height * bufferMulti;
2609  int innerWidth = width;
2610  int innerHeight = height;
2611  width += 2 * xBuffer;
2612  height += 2 * yBuffer;
2613 
2614  if ( width > 10000 || height > 10000 ) //protect symbol layer from eating too much memory
2615  {
2616  return;
2617  }
2618 
2619  QImage patternImage( width, height, QImage::Format_ARGB32 );
2620  patternImage.fill( 0 );
2621 
2622  QPointF p1, p2, p3, p4, p5, p6;
2623  if ( qgsDoubleNear( lineAngle, 0.0 ) || qgsDoubleNear( lineAngle, 360.0 ) || qgsDoubleNear( lineAngle, 180.0 ) )
2624  {
2625  p1 = QPointF( 0, yBuffer );
2626  p2 = QPointF( width, yBuffer );
2627  p3 = QPointF( 0, yBuffer + innerHeight );
2628  p4 = QPointF( width, yBuffer + innerHeight );
2629  }
2630  else if ( qgsDoubleNear( lineAngle, 90.0 ) || qgsDoubleNear( lineAngle, 270.0 ) )
2631  {
2632  p1 = QPointF( xBuffer, height );
2633  p2 = QPointF( xBuffer, 0 );
2634  p3 = QPointF( xBuffer + innerWidth, height );
2635  p4 = QPointF( xBuffer + innerWidth, 0 );
2636  }
2637  else if ( lineAngle > 0 && lineAngle < 90 )
2638  {
2639  dx = outputPixelDist * cos(( 90 - lineAngle ) * M_PI / 180.0 );
2640  dy = outputPixelDist * sin(( 90 - lineAngle ) * M_PI / 180.0 );
2641  p1 = QPointF( 0, height );
2642  p2 = QPointF( width, 0 );
2643  p3 = QPointF( -dx, height - dy );
2644  p4 = QPointF( width - dx, -dy );
2645  p5 = QPointF( dx, height + dy );
2646  p6 = QPointF( width + dx, dy );
2647  }
2648  else if ( lineAngle > 180 && lineAngle < 270 )
2649  {
2650  dx = outputPixelDist * cos(( 90 - lineAngle ) * M_PI / 180.0 );
2651  dy = outputPixelDist * sin(( 90 - lineAngle ) * M_PI / 180.0 );
2652  p1 = QPointF( width, 0 );
2653  p2 = QPointF( 0, height );
2654  p3 = QPointF( width - dx, -dy );
2655  p4 = QPointF( -dx, height - dy );
2656  p5 = QPointF( width + dx, dy );
2657  p6 = QPointF( dx, height + dy );
2658  }
2659  else if ( lineAngle > 90 && lineAngle < 180 )
2660  {
2661  dy = outputPixelDist * cos(( 180 - lineAngle ) * M_PI / 180 );
2662  dx = outputPixelDist * sin(( 180 - lineAngle ) * M_PI / 180 );
2663  p1 = QPointF( 0, 0 );
2664  p2 = QPointF( width, height );
2665  p5 = QPointF( dx, -dy );
2666  p6 = QPointF( width + dx, height - dy );
2667  p3 = QPointF( -dx, dy );
2668  p4 = QPointF( width - dx, height + dy );
2669  }
2670  else if ( lineAngle > 270 && lineAngle < 360 )
2671  {
2672  dy = outputPixelDist * cos(( 180 - lineAngle ) * M_PI / 180 );
2673  dx = outputPixelDist * sin(( 180 - lineAngle ) * M_PI / 180 );
2674  p1 = QPointF( width, height );
2675  p2 = QPointF( 0, 0 );
2676  p5 = QPointF( width + dx, height - dy );
2677  p6 = QPointF( dx, -dy );
2678  p3 = QPointF( width - dx, height + dy );
2679  p4 = QPointF( -dx, dy );
2680  }
2681 
2682  if ( !qgsDoubleNear( mOffset, 0.0 ) ) //shift everything
2683  {
2684  QPointF tempPt;
2685  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p1, p3, outputPixelDist + outputPixelOffset );
2686  p3 = QPointF( tempPt.x(), tempPt.y() );
2687  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p2, p4, outputPixelDist + outputPixelOffset );
2688  p4 = QPointF( tempPt.x(), tempPt.y() );
2689  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p1, p5, outputPixelDist - outputPixelOffset );
2690  p5 = QPointF( tempPt.x(), tempPt.y() );
2691  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p2, p6, outputPixelDist - outputPixelOffset );
2692  p6 = QPointF( tempPt.x(), tempPt.y() );
2693 
2694  //update p1, p2 last
2695  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p1, p3, outputPixelOffset );
2696  p1 = QPointF( tempPt.x(), tempPt.y() );
2697  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p2, p4, outputPixelOffset );
2698  p2 = QPointF( tempPt.x(), tempPt.y() );;
2699  }
2700 
2701  QPainter p( &patternImage );
2702 
2703 #if 0
2704  // DEBUG: Draw rectangle
2705  p.setRenderHint( QPainter::Antialiasing, false ); // get true rect
2706  QPen pen( QColor( Qt::black ) );
2707  pen.setWidthF( 0.1 );
2708  pen.setCapStyle( Qt::FlatCap );
2709  p.setPen( pen );
2710 
2711  // To see this rectangle, comment buffer cut below.
2712  // Subtract 1 because not antialiased are rendered to the right/down by 1 pixel
2713  QPolygon polygon = QPolygon() << QPoint( 0, 0 ) << QPoint( width - 1, 0 ) << QPoint( width - 1, height - 1 ) << QPoint( 0, height - 1 ) << QPoint( 0, 0 );
2714  p.drawPolygon( polygon );
2715 
2716  polygon = QPolygon() << QPoint( xBuffer, yBuffer ) << QPoint( width - xBuffer - 1, yBuffer ) << QPoint( width - xBuffer - 1, height - yBuffer - 1 ) << QPoint( xBuffer, height - yBuffer - 1 ) << QPoint( xBuffer, yBuffer );
2717  p.drawPolygon( polygon );
2718 #endif
2719 
2720  // Use antialiasing because without antialiasing lines are rendered to the
2721  // right and below the mathematically defined points (not symetrical)
2722  // and such tiles become useless for are filling
2723  p.setRenderHint( QPainter::Antialiasing, true );
2724 
2725  // line rendering needs context for drawing on patternImage
2726  QgsRenderContext lineRenderContext;
2727  lineRenderContext.setPainter( &p );
2728  lineRenderContext.setRasterScaleFactor( 1.0 );
2729  lineRenderContext.setScaleFactor( context.renderContext().scaleFactor() * context.renderContext().rasterScaleFactor() );
2731  lineRenderContext.setMapToPixel( mtp );
2732  lineRenderContext.setForceVectorOutput( false );
2733 
2734  fillLineSymbol->startRender( lineRenderContext, context.fields() );
2735 
2736  QVector<QPolygonF> polygons;
2737  polygons.append( QPolygonF() << p1 << p2 );
2738  polygons.append( QPolygonF() << p3 << p4 );
2739  if ( !qgsDoubleNear( lineAngle, 0 ) && !qgsDoubleNear( lineAngle, 360 ) && !qgsDoubleNear( lineAngle, 90 ) && !qgsDoubleNear( lineAngle, 180 ) && !qgsDoubleNear( lineAngle, 270 ) )
2740  {
2741  polygons.append( QPolygonF() << p5 << p6 );
2742  }
2743 
2744  foreach ( QPolygonF polygon, polygons )
2745  {
2746  fillLineSymbol->renderPolyline( polygon, context.feature(), lineRenderContext, -1, context.selected() );
2747  }
2748 
2749  fillLineSymbol->stopRender( lineRenderContext );
2750  p.end();
2751 
2752  // Cut off the buffer
2753  patternImage = patternImage.copy( xBuffer, yBuffer, patternImage.width() - 2 * xBuffer, patternImage.height() - 2 * yBuffer );
2754 
2755  //set image to mBrush
2756  if ( !qgsDoubleNear( context.alpha(), 1.0 ) )
2757  {
2758  QImage transparentImage = patternImage.copy();
2759  QgsSymbolLayerV2Utils::multiplyImageOpacity( &transparentImage, context.alpha() );
2760  brush.setTextureImage( transparentImage );
2761  }
2762  else
2763  {
2764  brush.setTextureImage( patternImage );
2765  }
2766 
2767  QTransform brushTransform;
2768  brushTransform.scale( 1.0 / context.renderContext().rasterScaleFactor(), 1.0 / context.renderContext().rasterScaleFactor() );
2769  brush.setTransform( brushTransform );
2770 
2771  delete fillLineSymbol;
2772 }
2773 
2775 {
2776  applyPattern( context, mBrush, mLineAngle, mDistance, mLineWidth, mColor );
2777 
2778  if ( mFillLineSymbol )
2779  {
2780  mFillLineSymbol->startRender( context.renderContext(), context.fields() );
2781  }
2782 
2783  prepareExpressions( context.fields(), context.renderContext().rendererScale() );
2784 }
2785 
2787 {
2788 }
2789 
2791 {
2792  QgsStringMap map;
2793  map.insert( "angle", QString::number( mLineAngle ) );
2794  map.insert( "distance", QString::number( mDistance ) );
2795  map.insert( "line_width", QString::number( mLineWidth ) );
2796  map.insert( "color", QgsSymbolLayerV2Utils::encodeColor( mColor ) );
2797  map.insert( "offset", QString::number( mOffset ) );
2798  map.insert( "distance_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mDistanceUnit ) );
2799  map.insert( "line_width_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mLineWidthUnit ) );
2800  map.insert( "offset_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit ) );
2801  map.insert( "distance_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mDistanceMapUnitScale ) );
2802  map.insert( "line_width_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mLineWidthMapUnitScale ) );
2803  map.insert( "offset_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale ) );
2804  map.insert( "outline_width_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mOutlineWidthUnit ) );
2805  map.insert( "outline_width_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mOutlineWidthMapUnitScale ) );
2807  return map;
2808 }
2809 
2811 {
2813  if ( mFillLineSymbol )
2814  {
2815  clonedLayer->setSubSymbol( mFillLineSymbol->clone() );
2816  }
2817  return clonedLayer;
2818 }
2819 
2820 void QgsLinePatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
2821 {
2822  QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
2823  if ( !props.value( "uom", "" ).isEmpty() )
2824  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
2825  element.appendChild( symbolizerElem );
2826 
2827  // <Geometry>
2828  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
2829 
2830  QDomElement fillElem = doc.createElement( "se:Fill" );
2831  symbolizerElem.appendChild( fillElem );
2832 
2833  QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" );
2834  fillElem.appendChild( graphicFillElem );
2835 
2836  QDomElement graphicElem = doc.createElement( "se:Graphic" );
2837  graphicFillElem.appendChild( graphicElem );
2838 
2839  //line properties must be inside the graphic definition
2840  QColor lineColor = mFillLineSymbol ? mFillLineSymbol->color() : QColor();
2841  double lineWidth = mFillLineSymbol ? mFillLineSymbol->width() : 0.0;
2842  QgsSymbolLayerV2Utils::wellKnownMarkerToSld( doc, graphicElem, "horline", QColor(), lineColor, Qt::SolidLine, lineWidth, mDistance );
2843 
2844  // <Rotation>
2845  QString angleFunc;
2846  bool ok;
2847  double angle = props.value( "angle", "0" ).toDouble( &ok );
2848  if ( !ok )
2849  {
2850  angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mLineAngle );
2851  }
2852  else if ( angle + mLineAngle != 0 )
2853  {
2854  angleFunc = QString::number( angle + mLineAngle );
2855  }
2856  QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
2857 
2858  // <se:Displacement>
2859  QPointF lineOffset( sin( mLineAngle ) * mOffset, cos( mLineAngle ) * mOffset );
2860  QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, lineOffset );
2861 }
2862 
2864 {
2865  QString featureStyle;
2866  featureStyle.append( "Brush(" );
2867  featureStyle.append( QString( "fc:%1" ).arg( mColor.name() ) );
2868  featureStyle.append( QString( ",bc:%1" ).arg( "#00000000" ) ); //transparent background
2869  featureStyle.append( ",id:\"ogr-brush-2\"" );
2870  featureStyle.append( QString( ",a:%1" ).arg( mLineAngle ) );
2871  featureStyle.append( QString( ",s:%1" ).arg( mLineWidth * widthScaleFactor ) );
2872  featureStyle.append( ",dx:0mm" );
2873  featureStyle.append( QString( ",dy:%1mm" ).arg( mDistance * widthScaleFactor ) );
2874  featureStyle.append( ")" );
2875  return featureStyle;
2876 }
2877 
2879 {
2880  QgsExpression* lineAngleExpression = expression( "lineangle" );
2881  QgsExpression* distanceExpression = expression( "distance" );
2882  QgsExpression* lineWidthExpression = expression( "linewidth" );
2883  QgsExpression* colorExpression = expression( "color" );
2884  if ( !lineAngleExpression && !distanceExpression && !lineWidthExpression && !colorExpression )
2885  {
2886  return; //no data defined settings
2887  }
2888 
2889  double lineAngle = mLineAngle;
2890  if ( lineAngleExpression )
2891  {
2892  lineAngle = lineAngleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
2893  }
2894  double distance = mDistance;
2895  if ( distanceExpression )
2896  {
2897  distance = distanceExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
2898  }
2899  double lineWidth = mLineWidth;
2900  if ( lineWidthExpression )
2901  {
2902  lineWidth = lineWidthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
2903  }
2904  QColor color = mColor;
2905  if ( colorExpression )
2906  {
2907  color = QgsSymbolLayerV2Utils::decodeColor( colorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
2908  }
2909  applyPattern( context, mBrush, lineAngle, distance, lineWidth, color );
2910 }
2911 
2913 {
2914  QgsDebugMsg( "Entered." );
2915 
2916  QString name;
2917  QColor fillColor, lineColor;
2918  double size, lineWidth;
2919  Qt::PenStyle lineStyle;
2920 
2921  QDomElement fillElem = element.firstChildElement( "Fill" );
2922  if ( fillElem.isNull() )
2923  return NULL;
2924 
2925  QDomElement graphicFillElem = fillElem.firstChildElement( "GraphicFill" );
2926  if ( graphicFillElem.isNull() )
2927  return NULL;
2928 
2929  QDomElement graphicElem = graphicFillElem.firstChildElement( "Graphic" );
2930  if ( graphicElem.isNull() )
2931  return NULL;
2932 
2933  if ( !QgsSymbolLayerV2Utils::wellKnownMarkerFromSld( graphicElem, name, fillColor, lineColor, lineStyle, lineWidth, size ) )
2934  return NULL;
2935 
2936  if ( name != "horline" )
2937  return NULL;
2938 
2939  double angle = 0.0;
2940  QString angleFunc;
2941  if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
2942  {
2943  bool ok;
2944  double d = angleFunc.toDouble( &ok );
2945  if ( ok )
2946  angle = d;
2947  }
2948 
2949  double offset = 0.0;
2950  QPointF vectOffset;
2951  if ( QgsSymbolLayerV2Utils::displacementFromSldElement( graphicElem, vectOffset ) )
2952  {
2953  offset = sqrt( pow( vectOffset.x(), 2 ) + pow( vectOffset.y(), 2 ) );
2954  }
2955 
2957  sl->setColor( lineColor );
2958  sl->setLineWidth( lineWidth );
2959  sl->setLineAngle( angle );
2960  sl->setOffset( offset );
2961  sl->setDistance( size );
2962 
2963  // try to get the outline
2964  QDomElement strokeElem = element.firstChildElement( "Stroke" );
2965  if ( !strokeElem.isNull() )
2966  {
2968  if ( l )
2969  {
2970  QgsSymbolLayerV2List layers;
2971  layers.append( l );
2972  sl->setSubSymbol( new QgsLineSymbolV2( layers ) );
2973  }
2974  }
2975 
2976  return sl;
2977 }
2978 
2979 
2981 
2983  mDistanceXUnit( QgsSymbolV2::MM ), mDistanceY( 15 ), mDistanceYUnit( QgsSymbolV2::MM ), mDisplacementX( 0 ), mDisplacementXUnit( QgsSymbolV2::MM ),
2984  mDisplacementY( 0 ), mDisplacementYUnit( QgsSymbolV2::MM )
2985 {
2986  mDistanceX = 15;
2987  mDistanceY = 15;
2988  mDisplacementX = 0;
2989  mDisplacementY = 0;
2991  QgsImageFillSymbolLayer::setSubSymbol( 0 ); //no outline
2992 }
2993 
2995 {
2996  delete mMarkerSymbol;
2997 }
2998 
3000 {
3002  mDistanceXUnit = unit;
3003  mDistanceYUnit = unit;
3004  mDisplacementXUnit = unit;
3005  mDisplacementYUnit = unit;
3006 }
3007 
3009 {
3011  if ( mDistanceXUnit != unit || mDistanceYUnit != unit || mDisplacementXUnit != unit || mDisplacementYUnit != unit )
3012  {
3013  return QgsSymbolV2::Mixed;
3014  }
3015  return unit;
3016 }
3017 
3019 {
3021  mDistanceXMapUnitScale = scale;
3022  mDistanceYMapUnitScale = scale;
3025 }
3026 
3028 {
3033  {
3034  return mDistanceXMapUnitScale;
3035  }
3036  return QgsMapUnitScale();
3037 }
3038 
3040 {
3042  if ( properties.contains( "distance_x" ) )
3043  {
3044  layer->setDistanceX( properties["distance_x"].toDouble() );
3045  }
3046  if ( properties.contains( "distance_y" ) )
3047  {
3048  layer->setDistanceY( properties["distance_y"].toDouble() );
3049  }
3050  if ( properties.contains( "displacement_x" ) )
3051  {
3052  layer->setDisplacementX( properties["displacement_x"].toDouble() );
3053  }
3054  if ( properties.contains( "displacement_y" ) )
3055  {
3056  layer->setDisplacementY( properties["displacement_y"].toDouble() );
3057  }
3058 
3059  if ( properties.contains( "distance_x_unit" ) )
3060  {
3061  layer->setDistanceXUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["distance_x_unit"] ) );
3062  }
3063  if ( properties.contains( "distance_x_map_unit_scale" ) )
3064  {
3065  layer->setDistanceXMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["distance_x_map_unit_scale"] ) );
3066  }
3067  if ( properties.contains( "distance_y_unit" ) )
3068  {
3069  layer->setDistanceYUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["distance_y_unit"] ) );
3070  }
3071  if ( properties.contains( "distance_y_map_unit_scale" ) )
3072  {
3073  layer->setDistanceYMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["distance_y_map_unit_scale"] ) );
3074  }
3075  if ( properties.contains( "displacement_x_unit" ) )
3076  {
3077  layer->setDisplacementXUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["displacement_x_unit"] ) );
3078  }
3079  if ( properties.contains( "displacement_x_map_unit_scale" ) )
3080  {
3081  layer->setDisplacementXMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["displacement_x_map_unit_scale"] ) );
3082  }
3083  if ( properties.contains( "displacement_y_unit" ) )
3084  {
3085  layer->setDisplacementYUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["displacement_y_unit"] ) );
3086  }
3087  if ( properties.contains( "displacement_y_map_unit_scale" ) )
3088  {
3089  layer->setDisplacementYMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["displacement_y_map_unit_scale"] ) );
3090  }
3091  if ( properties.contains( "outline_width_unit" ) )
3092  {
3093  layer->setOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["outline_width_unit"] ) );
3094  }
3095  if ( properties.contains( "outline_width_map_unit_scale" ) )
3096  {
3097  layer->setOutlineWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["outline_width_map_unit_scale"] ) );
3098  }
3099 
3100  //data defined properties
3101  if ( properties.contains( "distance_x_expression" ) )
3102  {
3103  layer->setDataDefinedProperty( "distance_x", properties["distance_x_expression"] );
3104  }
3105  if ( properties.contains( "distance_y_expression" ) )
3106  {
3107  layer->setDataDefinedProperty( "distance_y", properties["distance_y_expression"] );
3108  }
3109  if ( properties.contains( "displacement_x_expression" ) )
3110  {
3111  layer->setDataDefinedProperty( "displacement_x", properties["displacement_x_expression"] );
3112  }
3113  if ( properties.contains( "displacement_y_expression" ) )
3114  {
3115  layer->setDataDefinedProperty( "displacement_y", properties["displacement_y_expression"] );
3116  }
3117  return layer;
3118 }
3119 
3121 {
3122  return "PointPatternFill";
3123 }
3124 
3125 void QgsPointPatternFillSymbolLayer::applyPattern( const QgsSymbolV2RenderContext& context, QBrush& brush, double distanceX, double distanceY,
3126  double displacementX, double displacementY )
3127 {
3128  //render 3 rows and columns in one go to easily incorporate displacement
3129  const QgsRenderContext& ctx = context.renderContext();
3130  double width = distanceX * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mDistanceXUnit, mDistanceXMapUnitScale ) * 2.0;
3132 
3133  if ( width > 10000 || height > 10000 ) //protect symbol layer from eating too much memory
3134  {
3135  QImage img;
3136  brush.setTextureImage( img );
3137  return;
3138  }
3139 
3140  QImage patternImage( width, height, QImage::Format_ARGB32 );
3141  patternImage.fill( 0 );
3142 
3143  if ( mMarkerSymbol )
3144  {
3145  QPainter p( &patternImage );
3146 
3147  //marker rendering needs context for drawing on patternImage
3148  QgsRenderContext pointRenderContext;
3149  pointRenderContext.setRendererScale( context.renderContext().rendererScale() );
3150  pointRenderContext.setPainter( &p );
3151  pointRenderContext.setRasterScaleFactor( 1.0 );
3152  pointRenderContext.setScaleFactor( context.renderContext().scaleFactor() * context.renderContext().rasterScaleFactor() );
3154  pointRenderContext.setMapToPixel( mtp );
3155  pointRenderContext.setForceVectorOutput( false );
3156 
3157  mMarkerSymbol->startRender( pointRenderContext, context.fields() );
3158 
3159  //render corner points
3160  mMarkerSymbol->renderPoint( QPointF( 0, 0 ), context.feature(), pointRenderContext );
3161  mMarkerSymbol->renderPoint( QPointF( width, 0 ), context.feature(), pointRenderContext );
3162  mMarkerSymbol->renderPoint( QPointF( 0, height ), context.feature(), pointRenderContext );
3163  mMarkerSymbol->renderPoint( QPointF( width, height ), context.feature(), pointRenderContext );
3164 
3165  //render displaced points
3166  double displacementPixelX = displacementX * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mDisplacementXUnit, mDisplacementXMapUnitScale );
3167  double displacementPixelY = displacementY * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mDisplacementYUnit, mDisplacementYMapUnitScale );
3168  mMarkerSymbol->renderPoint( QPointF( width / 2.0, -displacementPixelY ), context.feature(), pointRenderContext );
3169  mMarkerSymbol->renderPoint( QPointF( displacementPixelX, height / 2.0 ), context.feature(), pointRenderContext );
3170  mMarkerSymbol->renderPoint( QPointF( width / 2.0 + displacementPixelX, height / 2.0 - displacementPixelY ), context.feature(), pointRenderContext );
3171  mMarkerSymbol->renderPoint( QPointF( width + displacementPixelX, height / 2.0 ), context.feature(), pointRenderContext );
3172  mMarkerSymbol->renderPoint( QPointF( width / 2.0, height - displacementPixelY ), context.feature(), pointRenderContext );
3173 
3174  mMarkerSymbol->stopRender( pointRenderContext );
3175  }
3176 
3177  if ( !qgsDoubleNear( context.alpha(), 1.0 ) )
3178  {
3179  QImage transparentImage = patternImage.copy();
3180  QgsSymbolLayerV2Utils::multiplyImageOpacity( &transparentImage, context.alpha() );
3181  brush.setTextureImage( transparentImage );
3182  }
3183  else
3184  {
3185  brush.setTextureImage( patternImage );
3186  }
3187  QTransform brushTransform;
3188  brushTransform.scale( 1.0 / context.renderContext().rasterScaleFactor(), 1.0 / context.renderContext().rasterScaleFactor() );
3189  brush.setTransform( brushTransform );
3190 }
3191 
3193 {
3194  applyPattern( context, mBrush, mDistanceX, mDistanceY, mDisplacementX, mDisplacementY );
3195 
3196  if ( mOutline )
3197  {
3198  mOutline->startRender( context.renderContext(), context.fields() );
3199  }
3200  prepareExpressions( context.fields(), context.renderContext().rendererScale() );
3201 }
3202 
3204 {
3205  if ( mOutline )
3206  {
3207  mOutline->stopRender( context.renderContext() );
3208  }
3209 }
3210 
3212 {
3213  QgsStringMap map;
3214  map.insert( "distance_x", QString::number( mDistanceX ) );
3215  map.insert( "distance_y", QString::number( mDistanceY ) );
3216  map.insert( "displacement_x", QString::number( mDisplacementX ) );
3217  map.insert( "displacement_y", QString::number( mDisplacementY ) );
3218  map.insert( "distance_x_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mDistanceXUnit ) );
3219  map.insert( "distance_y_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mDistanceYUnit ) );
3220  map.insert( "displacement_x_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mDisplacementXUnit ) );
3221  map.insert( "displacement_y_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mDisplacementYUnit ) );
3222  map.insert( "distance_x_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mDistanceXMapUnitScale ) );
3223  map.insert( "distance_y_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mDistanceYMapUnitScale ) );
3224  map.insert( "displacement_x_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mDisplacementXMapUnitScale ) );
3225  map.insert( "displacement_y_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mDisplacementYMapUnitScale ) );
3226  map.insert( "outline_width_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mOutlineWidthUnit ) );
3227  map.insert( "outline_width_map_unit_scale", QgsSymbolLayerV2Utils::encodeMapUnitScale( mOutlineWidthMapUnitScale ) );
3229  return map;
3230 }
3231 
3233 {
3235  if ( mMarkerSymbol )
3236  {
3237  clonedLayer->setSubSymbol( mMarkerSymbol->clone() );
3238  }
3239  return clonedLayer;
3240 }
3241 
3242 void QgsPointPatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
3243 {
3244  for ( int i = 0; i < mMarkerSymbol->symbolLayerCount(); i++ )
3245  {
3246  QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
3247  if ( !props.value( "uom", "" ).isEmpty() )
3248  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
3249  element.appendChild( symbolizerElem );
3250 
3251  // <Geometry>
3252  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
3253 
3254  QDomElement fillElem = doc.createElement( "se:Fill" );
3255  symbolizerElem.appendChild( fillElem );
3256 
3257  QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" );
3258  fillElem.appendChild( graphicFillElem );
3259 
3260  // store distanceX, distanceY, displacementX, displacementY in a <VendorOption>
3261  QString dist = QgsSymbolLayerV2Utils::encodePoint( QPointF( mDistanceX, mDistanceY ) );
3262  QDomElement distanceElem = QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "distance", dist );
3263  symbolizerElem.appendChild( distanceElem );
3264 
3266  QgsMarkerSymbolLayerV2 *markerLayer = static_cast<QgsMarkerSymbolLayerV2 *>( layer );
3267  if ( !markerLayer )
3268  {
3269  QString errorMsg = QString( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( layer->layerType() );
3270  graphicFillElem.appendChild( doc.createComment( errorMsg ) );
3271  }
3272  else
3273  {
3274  markerLayer->writeSldMarker( doc, graphicFillElem, props );
3275  }
3276  }
3277 }
3278 
3280 {
3281  Q_UNUSED( element );
3282  return NULL;
3283 }
3284 
3286 {
3287  if ( !symbol )
3288  {
3289  return false;
3290  }
3291 
3292  if ( symbol->type() == QgsSymbolV2::Marker )
3293  {
3294  QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( symbol );
3295  delete mMarkerSymbol;
3296  mMarkerSymbol = markerSymbol;
3297  }
3298  return true;
3299 }
3300 
3302 {
3303  QgsExpression* distanceXExpression = expression( "distance_x" );
3304  QgsExpression* distanceYExpression = expression( "distance_y" );
3305  QgsExpression* displacementXExpression = expression( "displacement_x" );
3306  QgsExpression* displacementYExpression = expression( "displacement_y" );
3307 
3308 #if 0
3309  // TODO: enable but check also if mMarkerSymbol has data defined properties
3310  if ( !distanceXExpression && !distanceYExpression && !displacementXExpression && !displacementYExpression )
3311  {
3312  return;
3313  }
3314 #endif
3315 
3316  double distanceX = mDistanceX;
3317  if ( distanceXExpression )
3318  {
3319  distanceX = distanceXExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
3320  }
3321  double distanceY = mDistanceY;
3322  if ( distanceYExpression )
3323  {
3324  distanceY = distanceYExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
3325  }
3326  double displacementX = mDisplacementX;
3327  if ( displacementXExpression )
3328  {
3329  displacementX = displacementXExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
3330  }
3331  double displacementY = mDisplacementY;
3332  if ( displacementYExpression )
3333  {
3334  displacementY = displacementYExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
3335  }
3336  applyPattern( context, mBrush, distanceX, distanceY, displacementX, displacementY );
3337 }
3338 
3340 {
3341  return 0;
3342 }
3343 
3345 {
3346  QSet<QString> attributes = QgsSymbolLayerV2::usedAttributes();
3347 
3348  if ( mMarkerSymbol )
3349  attributes.unite( mMarkerSymbol->usedAttributes() );
3350 
3351  return attributes;
3352 }
3353 
3355 
3356 
3357 QgsCentroidFillSymbolLayerV2::QgsCentroidFillSymbolLayerV2(): mMarker( NULL ), mPointOnSurface( false )
3358 {
3360 }
3361 
3363 {
3364  delete mMarker;
3365 }
3366 
3368 {
3370 
3371  if ( properties.contains( "point_on_surface" ) )
3372  sl->setPointOnSurface( properties["point_on_surface"].toInt() != 0 );
3373 
3374  return sl;
3375 }
3376 
3378 {
3379  return "CentroidFill";
3380 }
3381 
3382 void QgsCentroidFillSymbolLayerV2::setColor( const QColor& color )
3383 {
3384  mMarker->setColor( color );
3385  mColor = color;
3386 }
3387 
3389 {
3390  mMarker->setAlpha( context.alpha() );
3391  mMarker->startRender( context.renderContext(), context.fields() );
3392 }
3393 
3395 {
3396  mMarker->stopRender( context.renderContext() );
3397 }
3398 
3399 void QgsCentroidFillSymbolLayerV2::renderPolygon( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
3400 {
3401  Q_UNUSED( rings );
3402 
3404  mMarker->renderPoint( centroid, context.feature(), context.renderContext(), -1, context.selected() );
3405 }
3406 
3408 {
3409  QgsStringMap map;
3410  map["point_on_surface"] = QString::number( mPointOnSurface );
3411  return map;
3412 }
3413 
3415 {
3417  x->mAngle = mAngle;
3418  x->mColor = mColor;
3419  x->setSubSymbol( mMarker->clone() );
3421  return x;
3422 }
3423 
3424 void QgsCentroidFillSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
3425 {
3426  // SLD 1.0 specs says: "if a line, polygon, or raster geometry is
3427  // used with PointSymbolizer, then the semantic is to use the centroid
3428  // of the geometry, or any similar representative point.
3429  mMarker->toSld( doc, element, props );
3430 }
3431 
3433 {
3434  QgsDebugMsg( "Entered." );
3435 
3437  if ( !l )
3438  return NULL;
3439 
3440  QgsSymbolLayerV2List layers;
3441  layers.append( l );
3442  QgsMarkerSymbolV2 *marker = new QgsMarkerSymbolV2( layers );
3443 
3445  sl->setSubSymbol( marker );
3446  return sl;
3447 }
3448 
3449 
3451 {
3452  return mMarker;
3453 }
3454 
3456 {
3457  if ( symbol == NULL || symbol->type() != QgsSymbolV2::Marker )
3458  {
3459  delete symbol;
3460  return false;
3461  }
3462 
3463  delete mMarker;
3464  mMarker = static_cast<QgsMarkerSymbolV2*>( symbol );
3465  mColor = mMarker->color();
3466  return true;
3467 }
3468 
3470 {
3471  QSet<QString> attributes;
3472 
3473  attributes.unite( QgsSymbolLayerV2::usedAttributes() );
3474 
3475  if ( mMarker )
3476  attributes.unite( mMarker->usedAttributes() );
3477 
3478  return attributes;
3479 }
3480 
3482 {
3483  if ( mMarker )
3484  {
3485  mMarker->setOutputUnit( unit );
3486  }
3487 }
3488 
3490 {
3491  if ( mMarker )
3492  {
3493  return mMarker->outputUnit();
3494  }
3495  return QgsSymbolV2::Mixed; //mOutputUnit;
3496 }
3497 
3499 {
3500  if ( mMarker )
3501  {
3502  mMarker->setMapUnitScale( scale );
3503  }
3504 }
3505 
3507 {
3508  if ( mMarker )
3509  {
3510  return mMarker->mapUnitScale();
3511  }
3512  return QgsMapUnitScale();
3513 }
3514 
3515 
3516 
3517 
3520  , mImageFilePath( imageFilePath )
3521  , mCoordinateMode( QgsRasterFillSymbolLayer::Feature )
3522  , mAlpha( 1.0 )
3523  , mOffsetUnit( QgsSymbolV2::MM )
3524  , mWidth( 0.0 )
3525  , mWidthUnit( QgsSymbolV2::Pixel )
3526 {
3527  QgsImageFillSymbolLayer::setSubSymbol( 0 ); //disable sub symbol
3528 }
3529 
3531 {
3532 
3533 }
3534 
3536 {
3538  double alpha = 1.0;
3539  QPointF offset;
3540  double angle = 0.0;
3541  double width = 0.0;
3542 
3543  QString imagePath;
3544  if ( properties.contains( "imageFile" ) )
3545  {
3546  imagePath = properties["imageFile"];
3547  }
3548  if ( properties.contains( "coordinate_mode" ) )
3549  {
3550  mode = ( FillCoordinateMode )properties["coordinate_mode"].toInt();
3551  }
3552  if ( properties.contains( "alpha" ) )
3553  {
3554  alpha = properties["alpha"].toDouble();
3555  }
3556  if ( properties.contains( "offset" ) )
3557  {
3558  offset = QgsSymbolLayerV2Utils::decodePoint( properties["offset"] );
3559  }
3560  if ( properties.contains( "angle" ) )
3561  {
3562  angle = properties["angle"].toDouble();
3563  }
3564  if ( properties.contains( "width" ) )
3565  {
3566  width = properties["width"].toDouble();
3567  }
3568  QgsRasterFillSymbolLayer* symbolLayer = new QgsRasterFillSymbolLayer( imagePath );
3569  symbolLayer->setCoordinateMode( mode );
3570  symbolLayer->setAlpha( alpha );
3571  symbolLayer->setOffset( offset );
3572  symbolLayer->setAngle( angle );
3573  symbolLayer->setWidth( width );
3574  if ( properties.contains( "offset_unit" ) )
3575  {
3576  symbolLayer->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["offset_unit"] ) );
3577  }
3578  if ( properties.contains( "offset_map_unit_scale" ) )
3579  {
3580  symbolLayer->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["offset_map_unit_scale"] ) );
3581  }
3582  if ( properties.contains( "width_unit" ) )
3583  {
3584  symbolLayer->setWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["width_unit"] ) );
3585  }
3586  if ( properties.contains( "width_map_unit_scale" ) )
3587  {
3588  symbolLayer->setWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( properties["width_map_unit_scale"] ) );
3589  }
3590 
3591  //data defined
3592  if ( properties.contains( "file_expression" ) )
3593  {
3594  symbolLayer->setDataDefinedProperty( "file", properties["file_expression"] );
3595  }
3596  if ( properties.contains( "alpha_expression" ) )
3597  {
3598  symbolLayer->setDataDefinedProperty( "alpha", properties["alpha_expression"] );
3599  }
3600  if ( properties.contains( "angle_expression" ) )
3601  {
3602  symbolLayer->setDataDefinedProperty( "angle", properties["angle_expression"] );
3603  }
3604  if ( properties.contains( "width_expression" ) )
3605  {
3606  symbolLayer->setDataDefinedProperty( "width", properties["width_expression"] );
3607  }
3608  return symbolLayer;
3609 }
3610 
3612 {
3613  Q_UNUSED( symbol );
3614  return true;
3615 }
3616 
3618 {
3619  return "RasterFill";
3620 }
3621 
3622 void QgsRasterFillSymbolLayer::renderPolygon( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolV2RenderContext &context )
3623 {
3624  QPainter* p = context.renderContext().painter();
3625  if ( !p )
3626  {
3627  return;
3628  }
3629 
3630  QPointF offset;
3631  if ( !mOffset.isNull() )
3632  {
3635  p->translate( offset );
3636  }
3637  if ( mCoordinateMode == Feature )
3638  {
3639  QRectF boundingRect = points.boundingRect();
3640  mBrush.setTransform( mBrush.transform().translate( boundingRect.left() - mBrush.transform().dx(),
3641  boundingRect.top() - mBrush.transform().dy() ) );
3642  }
3643 
3644  QgsImageFillSymbolLayer::renderPolygon( points, rings, context );
3645  if ( !mOffset.isNull() )
3646  {
3647  p->translate( -offset );
3648  }
3649 }
3650 
3652 {
3653  prepareExpressions( context.fields(), context.renderContext().rendererScale() );
3654  applyPattern( mBrush, mImageFilePath, mWidth, mAlpha, context );
3655 }
3656 
3658 {
3659  Q_UNUSED( context );
3660 }
3661 
3663 {
3664  QgsStringMap map;
3665  map["imageFile"] = mImageFilePath;
3666  map["coordinate_mode"] = QString::number( mCoordinateMode );
3667  map["alpha"] = QString::number( mAlpha );
3668  map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
3669  map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit );
3670  map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
3671  map["angle"] = QString::number( mAngle );
3672  map["width"] = QString::number( mWidth );
3673  map["width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mWidthUnit );
3674  map["width_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mWidthMapUnitScale );
3675 
3677  return map;
3678 }
3679 
3681 {
3684  sl->setAlpha( mAlpha );
3685  sl->setOffset( mOffset );
3686  sl->setOffsetUnit( mOffsetUnit );
3688  sl->setAngle( mAngle );
3689  sl->setWidth( mWidth );
3690  sl->setWidthUnit( mWidthUnit );
3693  return sl;
3694 }
3695 
3697 {
3698  return mOffset.x() > mOffset.y() ? mOffset.x() : mOffset.y();
3699 }
3700 
3701 void QgsRasterFillSymbolLayer::setImageFilePath( const QString &imagePath )
3702 {
3703  mImageFilePath = imagePath;
3704 }
3705 
3707 {
3708  mCoordinateMode = mode;
3709 }
3710 
3711 void QgsRasterFillSymbolLayer::setAlpha( const double alpha )
3712 {
3713  mAlpha = alpha;
3714 }
3715 
3717 {
3718  if ( mDataDefinedProperties.isEmpty() )
3719  return; // shortcut
3720 
3721  QgsExpression* widthExpression = expression( "width" );
3722  QgsExpression* fileExpression = expression( "file" );
3723  QgsExpression* alphaExpression = expression( "alpha" );
3724  QgsExpression* angleExpression = expression( "angle" );
3725 
3726  if ( !widthExpression && !angleExpression && !alphaExpression && !fileExpression )
3727  {
3728  return; //no data defined settings
3729  }
3730 
3731  if ( angleExpression )
3732  {
3733  mNextAngle = angleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
3734  }
3735 
3736  if ( !widthExpression && !alphaExpression && !fileExpression )
3737  {
3738  return; //nothing further to do
3739  }
3740 
3741  double width = mWidth;
3742  if ( widthExpression )
3743  {
3744  width = widthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
3745  }
3746  double alpha = mAlpha;
3747  if ( alphaExpression )
3748  {
3749  alpha = alphaExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
3750  }
3751  QString file = mImageFilePath;
3752  if ( fileExpression )
3753  {
3754  file = fileExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
3755  }
3756  applyPattern( mBrush, file, width, alpha, context );
3757 }
3758 
3759 void QgsRasterFillSymbolLayer::applyPattern( QBrush &brush, const QString &imageFilePath, const double width, const double alpha, const QgsSymbolV2RenderContext &context )
3760 {
3761  QImage image( imageFilePath );
3762  if ( image.isNull() )
3763  {
3764  return;
3765  }
3766  if ( !image.hasAlphaChannel() )
3767  {
3768  image = image.convertToFormat( QImage::Format_ARGB32 );
3769  }
3770 
3771  double pixelWidth;
3772  if ( width > 0 )
3773  {
3775  }
3776  else
3777  {
3778  pixelWidth = image.width();
3779  }
3780 
3781  //reduce alpha of image
3782  if ( alpha < 1.0 )
3783  {
3784  QPainter p;
3785  p.begin( &image );
3786  p.setCompositionMode( QPainter::CompositionMode_DestinationIn );
3787  QColor alphaColor( 0, 0, 0 );
3788  alphaColor.setAlphaF( alpha );
3789  p.fillRect( image.rect(), alphaColor );
3790  p.end();
3791  }
3792 
3793  //resize image if required
3794  if ( !qgsDoubleNear( pixelWidth, image.width() ) )
3795  {
3796  image = image.scaledToWidth( pixelWidth, Qt::SmoothTransformation );
3797  }
3798 
3799  brush.setTextureImage( image );
3800 }