QGIS API Documentation  2.0.1-Dufour
 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 #include "qgsfillsymbollayerv2.h"
16 #include "qgsmarkersymbollayerv2.h"
17 #include "qgssymbollayerv2utils.h"
18 
19 #include "qgsexpression.h"
20 #include "qgsrendercontext.h"
21 #include "qgsproject.h"
22 #include "qgssvgcache.h"
23 #include "qgslogger.h"
24 
25 #include <QPainter>
26 #include <QFile>
27 #include <QSvgRenderer>
28 #include <QDomDocument>
29 #include <QDomElement>
30 
31 QgsSimpleFillSymbolLayerV2::QgsSimpleFillSymbolLayerV2( QColor color, Qt::BrushStyle style, QColor borderColor, Qt::PenStyle borderStyle, double borderWidth )
32  : mBrushStyle( style ), mBorderColor( borderColor ), mBorderStyle( borderStyle ), mBorderWidth( borderWidth ), mBorderWidthUnit( QgsSymbolV2::MM ),
33  mOffsetUnit( QgsSymbolV2::MM )
34 {
35  mColor = color;
36 }
37 
39 {
40  mBorderWidthUnit = unit;
41  mOffsetUnit = unit;
42 }
43 
45 {
47  if ( mOffsetUnit != unit )
48  {
49  return QgsSymbolV2::Mixed;
50  }
51  return unit;
52 }
53 
54 void QgsSimpleFillSymbolLayerV2::applyDataDefinedSymbology( QgsSymbolV2RenderContext& context, QBrush& brush, QPen& pen, QPen& selPen )
55 {
56  QgsExpression* colorExpression = expression( "color" );
57  if ( colorExpression )
58  {
59  brush.setColor( QgsSymbolLayerV2Utils::decodeColor( colorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) );
60  }
61  QgsExpression* colorBorderExpression = expression( "color_border" );
62  if ( colorBorderExpression )
63  {
64  pen.setColor( QgsSymbolLayerV2Utils::decodeColor( colorBorderExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) );
65  }
66  QgsExpression* widthBorderExpression = expression( "width_border" );
67  if ( widthBorderExpression )
68  {
69  double width = widthBorderExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
71  pen.setWidthF( width );
72  selPen.setWidthF( width );
73  }
74 }
75 
76 
78 {
80  Qt::BrushStyle style = DEFAULT_SIMPLEFILL_STYLE;
84  QPointF offset;
85 
86  if ( props.contains( "color" ) )
87  color = QgsSymbolLayerV2Utils::decodeColor( props["color"] );
88  if ( props.contains( "style" ) )
89  style = QgsSymbolLayerV2Utils::decodeBrushStyle( props["style"] );
90  if ( props.contains( "color_border" ) )
91  borderColor = QgsSymbolLayerV2Utils::decodeColor( props["color_border"] );
92  if ( props.contains( "style_border" ) )
93  borderStyle = QgsSymbolLayerV2Utils::decodePenStyle( props["style_border"] );
94  if ( props.contains( "width_border" ) )
95  borderWidth = props["width_border"].toDouble();
96  if ( props.contains( "offset" ) )
97  offset = QgsSymbolLayerV2Utils::decodePoint( props["offset"] );
98 
99  QgsSimpleFillSymbolLayerV2* sl = new QgsSimpleFillSymbolLayerV2( color, style, borderColor, borderStyle, borderWidth );
100  sl->setOffset( offset );
101  if ( props.contains( "border_width_unit" ) )
102  sl->setBorderWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["border_width_unit"] ) );
103  if ( props.contains( "offset_unit" ) )
104  sl->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
105 
106  if ( props.contains( "color_expression" ) )
107  {
108  sl->setDataDefinedProperty( "color", props["color_expression"] );
109  }
110  if ( props.contains( "color_border_expression" ) )
111  {
112  sl->setDataDefinedProperty( "color_border", props["color_border_expression"] );
113  }
114  if ( props.contains( "width_border_expression" ) )
115  {
116  sl->setDataDefinedProperty( "width_border", props["width_border_expression"] );
117  }
118  return sl;
119 }
120 
121 
123 {
124  return "SimpleFill";
125 }
126 
128 {
129  QColor fillColor = mColor;
130  fillColor.setAlphaF( context.alpha() * mColor.alphaF() );
131  mBrush = QBrush( fillColor, mBrushStyle );
132 
133  // scale brush content for printout
135  if ( rasterScaleFactor != 1.0 )
136  {
137  mBrush.setMatrix( QMatrix().scale( 1.0 / rasterScaleFactor, 1.0 / rasterScaleFactor ) );
138  }
139 
140  QColor selColor = context.renderContext().selectionColor();
141  QColor selPenColor = selColor == mColor ? selColor : mBorderColor;
142  if ( ! selectionIsOpaque ) selColor.setAlphaF( context.alpha() );
143  mSelBrush = QBrush( selColor );
144  // N.B. unless a "selection line color" is implemented in addition to the "selection color" option
145  // this would mean symbols with "no fill" look the same whether or not they are selected
146  if ( selectFillStyle )
147  mSelBrush.setStyle( mBrushStyle );
148 
149  QColor borderColor = mBorderColor;
150  borderColor.setAlphaF( context.alpha() * mBorderColor.alphaF() );
151  mPen = QPen( borderColor );
152  mSelPen = QPen( selPenColor );
153  mPen.setStyle( mBorderStyle );
155  prepareExpressions( context.layer() );
156 }
157 
159 {
160  Q_UNUSED( context );
161 }
162 
163 void QgsSimpleFillSymbolLayerV2::renderPolygon( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
164 {
165  QPainter* p = context.renderContext().painter();
166  if ( !p )
167  {
168  return;
169  }
170 
172 
173  p->setBrush( context.selected() ? mSelBrush : mBrush );
174  p->setPen( mPen );
175  p->setPen( context.selected() ? mSelPen : mPen );
176 
177  QPointF offset;
178  if ( !mOffset.isNull() )
179  {
182  p->translate( offset );
183  }
184 
185  _renderPolygon( p, points, rings );
186 
187  if ( !mOffset.isNull() )
188  {
189  p->translate( -offset );
190  }
191 }
192 
194 {
195  QgsStringMap map;
196  map["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
198  map["color_border"] = QgsSymbolLayerV2Utils::encodeColor( mBorderColor );
199  map["style_border"] = QgsSymbolLayerV2Utils::encodePenStyle( mBorderStyle );
200  map["width_border"] = QString::number( mBorderWidth );
201  map["border_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mBorderWidthUnit );
202  map["offset"] = QgsSymbolLayerV2Utils::encodePoint( mOffset );
205  return map;
206 }
207 
209 {
211  sl->setOffset( mOffset );
212  sl->setOffsetUnit( mOffsetUnit );
215  return sl;
216 }
217 
218 void QgsSimpleFillSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
219 {
220  if ( mBrushStyle == Qt::NoBrush && mBorderStyle == Qt::NoPen )
221  return;
222 
223  QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
224  if ( !props.value( "uom", "" ).isEmpty() )
225  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
226  element.appendChild( symbolizerElem );
227 
228  // <Geometry>
229  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
230 
231  if ( mBrushStyle != Qt::NoBrush )
232  {
233  // <Fill>
234  QDomElement fillElem = doc.createElement( "se:Fill" );
235  symbolizerElem.appendChild( fillElem );
237  }
238 
239  if ( mBorderStyle != Qt::NoPen )
240  {
241  // <Stroke>
242  QDomElement strokeElem = doc.createElement( "se:Stroke" );
243  symbolizerElem.appendChild( strokeElem );
245  }
246 
247  // <se:Displacement>
249 }
250 
251 QString QgsSimpleFillSymbolLayerV2::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
252 {
253  //brush
254  QString symbolStyle;
255  symbolStyle.append( QgsSymbolLayerV2Utils::ogrFeatureStyleBrush( mColor ) );
256  symbolStyle.append( ";" );
257  //pen
258  symbolStyle.append( QgsSymbolLayerV2Utils::ogrFeatureStylePen( mBorderWidth, mmScaleFactor, mapUnitScaleFactor, mBorderColor ) );
259  return symbolStyle;
260 }
261 
263 {
264  QgsDebugMsg( "Entered." );
265 
266  QColor color, borderColor;
267  Qt::BrushStyle fillStyle;
268  Qt::PenStyle borderStyle;
269  double borderWidth;
270 
271  QDomElement fillElem = element.firstChildElement( "Fill" );
272  QgsSymbolLayerV2Utils::fillFromSld( fillElem, fillStyle, color );
273 
274  QDomElement strokeElem = element.firstChildElement( "Stroke" );
275  QgsSymbolLayerV2Utils::lineFromSld( strokeElem, borderStyle, borderColor, borderWidth );
276 
277  QPointF offset;
279 
280  QgsSimpleFillSymbolLayerV2* sl = new QgsSimpleFillSymbolLayerV2( color, fillStyle, borderColor, borderStyle, borderWidth );
281  sl->setOffset( offset );
282  return sl;
283 }
284 
285 
286 //QgsImageFillSymbolLayer
287 
288 QgsImageFillSymbolLayer::QgsImageFillSymbolLayer(): mOutlineWidth( 0.0 ), mOutlineWidthUnit( QgsSymbolV2::MM ), mOutline( 0 )
289 {
290  setSubSymbol( new QgsLineSymbolV2() );
291 }
292 
294 {
295 }
296 
297 void QgsImageFillSymbolLayer::renderPolygon( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
298 {
299  QPainter* p = context.renderContext().painter();
300  if ( !p )
301  {
302  return;
303  }
304 
305  mNextAngle = mAngle;
306  applyDataDefinedSettings( context );
307 
308  p->setPen( QPen( Qt::NoPen ) );
309  if ( context.selected() )
310  {
311  QColor selColor = context.renderContext().selectionColor();
312  // Alister - this doesn't seem to work here
313  //if ( ! selectionIsOpaque )
314  // selColor.setAlphaF( context.alpha() );
315  p->setBrush( QBrush( selColor ) );
316  _renderPolygon( p, points, rings );
317  }
318 
319  if ( qgsDoubleNear( mNextAngle, 0.0 ) )
320  {
321  p->setBrush( mBrush );
322  }
323  else
324  {
325  QTransform t = mBrush.transform();
326  t.rotate( mNextAngle );
327  QBrush rotatedBrush = mBrush;
328  rotatedBrush.setTransform( t );
329  p->setBrush( rotatedBrush );
330  }
331  _renderPolygon( p, points, rings );
332  if ( mOutline )
333  {
334  mOutline->renderPolyline( points, context.feature(), context.renderContext(), -1, selectFillBorder && context.selected() );
335  if ( rings )
336  {
337  QList<QPolygonF>::const_iterator ringIt = rings->constBegin();
338  for ( ; ringIt != rings->constEnd(); ++ringIt )
339  {
340  mOutline->renderPolyline( *ringIt, context.feature(), context.renderContext(), -1, selectFillBorder && context.selected() );
341  }
342  }
343  }
344 }
345 
347 {
348  if ( !symbol ) //unset current outline
349  {
350  delete mOutline;
351  mOutline = 0;
352  return true;
353  }
354 
355  if ( symbol->type() != QgsSymbolV2::Line )
356  {
357  delete symbol;
358  return false;
359  }
360 
361  QgsLineSymbolV2* lineSymbol = dynamic_cast<QgsLineSymbolV2*>( symbol );
362  if ( lineSymbol )
363  {
364  delete mOutline;
365  mOutline = lineSymbol;
366  return true;
367  }
368 
369  delete symbol;
370  return false;
371 }
372 
373 //QgsSVGFillSymbolLayer
374 
375 QgsSVGFillSymbolLayer::QgsSVGFillSymbolLayer( const QString& svgFilePath, double width, double angle ): QgsImageFillSymbolLayer(), mPatternWidth( width ),
376  mPatternWidthUnit( QgsSymbolV2::MM ), mSvgOutlineWidthUnit( QgsSymbolV2::MM )
377 {
378  setSvgFilePath( svgFilePath );
379  mOutlineWidth = 0.3;
380  mAngle = angle;
382  mSvgPattern = 0;
383 }
384 
385 QgsSVGFillSymbolLayer::QgsSVGFillSymbolLayer( const QByteArray& svgData, double width, double angle ): QgsImageFillSymbolLayer(), mPatternWidth( width ),
386  mSvgData( svgData )
387 {
388  storeViewBox();
389  mOutlineWidth = 0.3;
390  mAngle = angle;
391  setSubSymbol( new QgsLineSymbolV2() );
393  mSvgPattern = 0;
394 }
395 
397 {
398  delete mOutline;
399  delete mSvgPattern;
400 }
401 
403 {
404  mPatternWidthUnit = unit;
405  mSvgOutlineWidthUnit = unit;
406  mOutlineWidthUnit = unit;
407 }
408 
410 {
412  if ( mSvgOutlineWidthUnit != unit || mOutlineWidthUnit != unit )
413  {
414  return QgsSymbolV2::Mixed;
415  }
416  return unit;
417 }
418 
419 void QgsSVGFillSymbolLayer::setSvgFilePath( const QString& svgPath )
420 {
422  storeViewBox();
423 
424  mSvgFilePath = svgPath;
426 }
427 
429 {
430  QByteArray data;
431  double width = 20;
432  QString svgFilePath;
433  double angle = 0.0;
434 
435  if ( properties.contains( "width" ) )
436  {
437  width = properties["width"].toDouble();
438  }
439  if ( properties.contains( "svgFile" ) )
440  {
441  QString svgName = properties["svgFile"];
442  QString savePath = QgsSymbolLayerV2Utils::symbolNameToPath( svgName );
443  svgFilePath = ( savePath.isEmpty() ? svgName : savePath );
444  }
445  if ( properties.contains( "angle" ) )
446  {
447  angle = properties["angle"].toDouble();
448  }
449 
450  QgsSVGFillSymbolLayer* symbolLayer = 0;
451  if ( !svgFilePath.isEmpty() )
452  {
453  symbolLayer = new QgsSVGFillSymbolLayer( svgFilePath, width, angle );
454  }
455  else
456  {
457  if ( properties.contains( "data" ) )
458  {
459  data = QByteArray::fromHex( properties["data"].toLocal8Bit() );
460  }
461  symbolLayer = new QgsSVGFillSymbolLayer( data, width, angle );
462  }
463 
464  //svg parameters
465  if ( properties.contains( "svgFillColor" ) )
466  {
467  symbolLayer->setSvgFillColor( QColor( properties["svgFillColor"] ) );
468  }
469  if ( properties.contains( "svgOutlineColor" ) )
470  {
471  symbolLayer->setSvgOutlineColor( QColor( properties["svgOutlineColor"] ) );
472  }
473  if ( properties.contains( "svgOutlineWidth" ) )
474  {
475  symbolLayer->setSvgOutlineWidth( properties["svgOutlineWidth"].toDouble() );
476  }
477 
478  //units
479  if ( properties.contains( "pattern_width_unit" ) )
480  {
481  symbolLayer->setPatternWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["pattern_width_unit"] ) );
482  }
483  if ( properties.contains( "svg_outline_width_unit" ) )
484  {
485  symbolLayer->setSvgOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["svg_outline_width_unit"] ) );
486  }
487  if ( properties.contains( "outline_width_unit" ) )
488  {
489  symbolLayer->setOutlineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["outline_width_unit"] ) );
490  }
491 
492  if ( properties.contains( "width_expression" ) )
493  symbolLayer->setDataDefinedProperty( "width", properties["width_expression"] );
494  if ( properties.contains( "svgFile_expression" ) )
495  symbolLayer->setDataDefinedProperty( "svgFile", properties["svgFile_expression"] );
496  if ( properties.contains( "angle_expression" ) )
497  symbolLayer->setDataDefinedProperty( "angle", properties["angle_expression"] );
498  if ( properties.contains( "svgFillColor_expression" ) )
499  symbolLayer->setDataDefinedProperty( "svgFillColor", "svgFillColor_expression" );
500  if ( properties.contains( "svgOutlineColor_expression" ) )
501  symbolLayer->setDataDefinedProperty( "svgOutlineColor", "svgOutlineColor_expression" );
502  if ( properties.contains( "svgOutlineWidth" ) )
503  symbolLayer->setDataDefinedProperty( "svgOutlineWidth", "svgOutlineWidth_expression" );
504 
505  return symbolLayer;
506 }
507 
509 {
510  return "SVGFill";
511 }
512 
513 void QgsSVGFillSymbolLayer::applyPattern( QBrush& brush, const QString& svgFilePath, double patternWidth, QgsSymbolV2::OutputUnit patternWidthUnit,
514  const QColor& svgFillColor, const QColor& svgOutlineColor, double svgOutlineWidth,
515  QgsSymbolV2::OutputUnit svgOutlineWidthUnit, const QgsSymbolV2RenderContext& context )
516 {
517  if ( mSvgViewBox.isNull() )
518  {
519  return;
520  }
521 
522  delete mSvgPattern;
523  mSvgPattern = 0;
525 
526  if (( int )size < 1.0 || 10000.0 < size )
527  {
528  mSvgPattern = new QImage();
529  brush.setTextureImage( *mSvgPattern );
530  }
531  else
532  {
533  bool fitsInCache = true;
535  const QImage& patternImage = QgsSvgCache::instance()->svgAsImage( svgFilePath, size, svgFillColor, svgOutlineColor, outlineWidth,
536  context.renderContext().scaleFactor(), context.renderContext().rasterScaleFactor(), fitsInCache );
537  if ( !fitsInCache )
538  {
539  const QPicture& patternPict = QgsSvgCache::instance()->svgAsPicture( svgFilePath, size, svgFillColor, svgOutlineColor, outlineWidth,
540  context.renderContext().scaleFactor(), 1.0 );
541  double hwRatio = 1.0;
542  if ( patternPict.width() > 0 )
543  {
544  hwRatio = ( double )patternPict.height() / ( double )patternPict.width();
545  }
546  mSvgPattern = new QImage(( int )size, ( int )( size * hwRatio ), QImage::Format_ARGB32_Premultiplied );
547  mSvgPattern->fill( 0 ); // transparent background
548 
549  QPainter p( mSvgPattern );
550  p.drawPicture( QPointF( size / 2, size * hwRatio / 2 ), patternPict );
551  }
552 
553  QTransform brushTransform;
554  brushTransform.scale( 1.0 / context.renderContext().rasterScaleFactor(), 1.0 / context.renderContext().rasterScaleFactor() );
555  if ( !qgsDoubleNear( context.alpha(), 1.0 ) )
556  {
557  QImage transparentImage = fitsInCache ? patternImage.copy() : mSvgPattern->copy();
558  QgsSymbolLayerV2Utils::multiplyImageOpacity( &transparentImage, context.alpha() );
559  brush.setTextureImage( transparentImage );
560  }
561  else
562  {
563  brush.setTextureImage( fitsInCache ? patternImage : *mSvgPattern );
564  }
565  brush.setTransform( brushTransform );
566  }
567 }
568 
570 {
571 
573 
574  if ( mOutline )
575  {
576  mOutline->startRender( context.renderContext() );
577  }
578 
579  prepareExpressions( context.layer() );
580 }
581 
583 {
584  if ( mOutline )
585  {
586  mOutline->stopRender( context.renderContext() );
587  }
588 }
589 
591 {
592  QgsStringMap map;
593  if ( !mSvgFilePath.isEmpty() )
594  {
595  map.insert( "svgFile", QgsSymbolLayerV2Utils::symbolPathToName( mSvgFilePath ) );
596  }
597  else
598  {
599  map.insert( "data", QString( mSvgData.toHex() ) );
600  }
601 
602  map.insert( "width", QString::number( mPatternWidth ) );
603  map.insert( "angle", QString::number( mAngle ) );
604 
605  //svg parameters
606  map.insert( "svgFillColor", mSvgFillColor.name() );
607  map.insert( "svgOutlineColor", mSvgOutlineColor.name() );
608  map.insert( "svgOutlineWidth", QString::number( mSvgOutlineWidth ) );
609 
610  //units
611  map["pattern_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mPatternWidthUnit );
612  map["svg_outline_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mSvgOutlineWidthUnit );
613  map["outline_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOutlineWidthUnit );
614 
616  return map;
617 }
618 
620 {
621  QgsSVGFillSymbolLayer* clonedLayer = 0;
622  if ( !mSvgFilePath.isEmpty() )
623  {
625  clonedLayer->setSvgFillColor( mSvgFillColor );
626  clonedLayer->setSvgOutlineColor( mSvgOutlineColor );
627  clonedLayer->setSvgOutlineWidth( mSvgOutlineWidth );
628  }
629  else
630  {
631  clonedLayer = new QgsSVGFillSymbolLayer( mSvgData, mPatternWidth, mAngle );
632  }
633 
634  clonedLayer->setPatternWidthUnit( mPatternWidthUnit );
636  clonedLayer->setOutlineWidthUnit( mOutlineWidthUnit );
637 
638  if ( mOutline )
639  {
640  clonedLayer->setSubSymbol( mOutline->clone() );
641  }
642  copyDataDefinedProperties( clonedLayer );
643  return clonedLayer;
644 }
645 
646 void QgsSVGFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
647 {
648  QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
649  if ( !props.value( "uom", "" ).isEmpty() )
650  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
651  element.appendChild( symbolizerElem );
652 
653  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
654 
655  QDomElement fillElem = doc.createElement( "se:Fill" );
656  symbolizerElem.appendChild( fillElem );
657 
658  QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" );
659  fillElem.appendChild( graphicFillElem );
660 
661  QDomElement graphicElem = doc.createElement( "se:Graphic" );
662  graphicFillElem.appendChild( graphicElem );
663 
664  if ( !mSvgFilePath.isEmpty() )
665  {
667  }
668  else
669  {
670  // TODO: create svg from data
671  // <se:InlineContent>
672  symbolizerElem.appendChild( doc.createComment( "SVG from data not implemented yet" ) );
673  }
674 
675  if ( mSvgOutlineColor.isValid() || mSvgOutlineWidth >= 0 )
676  {
677  QgsSymbolLayerV2Utils::lineToSld( doc, graphicElem, Qt::SolidLine, mSvgOutlineColor, mSvgOutlineWidth );
678  }
679 
680  // <Rotation>
681  QString angleFunc;
682  bool ok;
683  double angle = props.value( "angle", "0" ).toDouble( &ok );
684  if ( !ok )
685  {
686  angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mAngle );
687  }
688  else if ( angle + mAngle != 0 )
689  {
690  angleFunc = QString::number( angle + mAngle );
691  }
692  QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
693 
694  if ( mOutline )
695  {
696  // the outline sub symbol should be stored within the Stroke element,
697  // but it will be stored in a separated LineSymbolizer because it could
698  // have more than one layer
699  mOutline->toSld( doc, element, props );
700  }
701 }
702 
704 {
705  QgsDebugMsg( "Entered." );
706 
707  QString path, mimeType;
708  QColor fillColor, borderColor;
709  Qt::PenStyle penStyle;
710  double size, borderWidth;
711 
712  QDomElement fillElem = element.firstChildElement( "Fill" );
713  if ( fillElem.isNull() )
714  return NULL;
715 
716  QDomElement graphicFillElem = fillElem.firstChildElement( "GraphicFill" );
717  if ( graphicFillElem.isNull() )
718  return NULL;
719 
720  QDomElement graphicElem = graphicFillElem.firstChildElement( "Graphic" );
721  if ( graphicElem.isNull() )
722  return NULL;
723 
724  if ( !QgsSymbolLayerV2Utils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
725  return NULL;
726 
727  if ( mimeType != "image/svg+xml" )
728  return NULL;
729 
730  QgsSymbolLayerV2Utils::lineFromSld( graphicElem, penStyle, borderColor, borderWidth );
731 
732  double angle = 0.0;
733  QString angleFunc;
734  if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
735  {
736  bool ok;
737  double d = angleFunc.toDouble( &ok );
738  if ( ok )
739  angle = d;
740  }
741 
742  QgsSVGFillSymbolLayer* sl = new QgsSVGFillSymbolLayer( path, size, angle );
743  sl->setSvgFillColor( fillColor );
744  sl->setSvgOutlineColor( borderColor );
745  sl->setSvgOutlineWidth( borderWidth );
746 
747  // try to get the outline
748  QDomElement strokeElem = element.firstChildElement( "Stroke" );
749  if ( !strokeElem.isNull() )
750  {
752  if ( l )
753  {
754  QgsSymbolLayerV2List layers;
755  layers.append( l );
756  sl->setSubSymbol( new QgsLineSymbolV2( layers ) );
757  }
758  }
759 
760  return sl;
761 }
762 
764 {
765  QgsExpression* widthExpression = expression( "width" );
766  QgsExpression* svgFileExpression = expression( "svgFile" );
767  QgsExpression* fillColorExpression = expression( "svgFillColor" );
768  QgsExpression* outlineColorExpression = expression( "svgOutlineColor" );
769  QgsExpression* outlineWidthExpression = expression( "svgOutlineWidth" );
770  QgsExpression* angleExpression = expression( "angle" );
771  if ( !widthExpression && !svgFileExpression && !fillColorExpression && !outlineColorExpression && !outlineWidthExpression && !angleExpression )
772  {
773  return; //no data defined settings
774  }
775 
776  if ( angleExpression )
777  {
778  mNextAngle = angleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
779  }
780 
781  double width = mPatternWidth;
782  if ( widthExpression )
783  {
784  width = widthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
785  }
786  QString svgFile = mSvgFilePath;
787  if ( svgFileExpression )
788  {
789  svgFile = svgFileExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
790  }
791  QColor svgFillColor = mSvgFillColor;
792  if ( fillColorExpression )
793  {
794  svgFillColor = QgsSymbolLayerV2Utils::decodeColor( fillColorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
795  }
797  if ( outlineColorExpression )
798  {
799  svgOutlineColor = QgsSymbolLayerV2Utils::decodeColor( outlineColorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
800  }
802  if ( outlineWidthExpression )
803  {
804  outlineWidth = outlineWidthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
805  }
806  applyPattern( mBrush, svgFile, width, mPatternWidthUnit, svgFillColor, svgOutlineColor, outlineWidth,
807  mSvgOutlineWidthUnit, context );
808 
809 }
810 
812 {
813  if ( !mSvgData.isEmpty() )
814  {
815  QSvgRenderer r( mSvgData );
816  if ( r.isValid() )
817  {
818  mSvgViewBox = r.viewBoxF();
819  return;
820  }
821  }
822 
823  mSvgViewBox = QRectF();
824  return;
825 }
826 
828 {
829  //default values
830  mSvgFillColor = QColor( 0, 0, 0 );
831  mSvgOutlineColor = QColor( 0, 0, 0 );
832  mSvgOutlineWidth = 0.3;
833 
834  if ( mSvgFilePath.isEmpty() )
835  {
836  return;
837  }
838 
839  bool hasFillParam, hasOutlineParam, hasOutlineWidthParam;
840  QColor defaultFillColor, defaultOutlineColor;
841  double defaultOutlineWidth;
842  QgsSvgCache::instance()->containsParams( mSvgFilePath, hasFillParam, defaultFillColor, hasOutlineParam, defaultOutlineColor, hasOutlineWidthParam,
843  defaultOutlineWidth );
844 
845  if ( hasFillParam )
846  {
847  mSvgFillColor = defaultFillColor;
848  }
849  if ( hasOutlineParam )
850  {
851  mSvgOutlineColor = defaultOutlineColor;
852  }
853  if ( hasOutlineWidthParam )
854  {
855  mSvgOutlineWidth = defaultOutlineWidth;
856  }
857 }
858 
860  mOffsetUnit( QgsSymbolV2::MM )
861 {
862 }
863 
865 {
866 }
867 
869 {
870  mDistanceUnit = unit;
871  mLineWidthUnit = unit;
872  mOffsetUnit = unit;
873 }
874 
876 {
878  if ( mLineWidthUnit != unit || mOffsetUnit != unit )
879  {
880  return QgsSymbolV2::Mixed;
881  }
882  return unit;
883 }
884 
886 {
888 
889  //default values
890  double lineAngle = 45;
891  double distance = 5;
892  double lineWidth = 0.5;
893  QColor color( Qt::black );
894  double offset = 0.0;
895 
896  if ( properties.contains( "lineangle" ) )
897  {
898  lineAngle = properties["lineangle"].toDouble();
899  }
900  patternLayer->setLineAngle( lineAngle );
901 
902  if ( properties.contains( "distance" ) )
903  {
904  distance = properties["distance"].toDouble();
905  }
906  patternLayer->setDistance( distance );
907 
908  if ( properties.contains( "linewidth" ) )
909  {
910  lineWidth = properties["linewidth"].toDouble();
911  }
912  patternLayer->setLineWidth( lineWidth );
913 
914  if ( properties.contains( "color" ) )
915  {
916  color = QgsSymbolLayerV2Utils::decodeColor( properties["color"] );
917  }
918  patternLayer->setColor( color );
919 
920  if ( properties.contains( "offset" ) )
921  {
922  offset = properties["offset"].toDouble();
923  }
924  patternLayer->setOffset( offset );
925 
926 
927  if ( properties.contains( "distance_unit" ) )
928  {
929  patternLayer->setDistanceUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["distance_unit"] ) );
930  }
931  if ( properties.contains( "line_width_unit" ) )
932  {
933  patternLayer->setLineWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["line_width_unit"] ) );
934  }
935  if ( properties.contains( "offset_unit" ) )
936  {
937  patternLayer->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["offset_unit"] ) );
938  }
939 
940  //data defined properties
941  if ( properties.contains( "lineangle_expression" ) )
942  {
943  patternLayer->setDataDefinedProperty( "lineangle", properties["lineangle_expression"] );
944  }
945  if ( properties.contains( "distance_expression" ) )
946  {
947  patternLayer->setDataDefinedProperty( "distance", properties["distance_expression"] );
948  }
949  if ( properties.contains( "linewidth_expression" ) )
950  {
951  patternLayer->setDataDefinedProperty( "linewidth", properties["linewidth_expression"] );
952  }
953  if ( properties.contains( "color_expression" ) )
954  {
955  patternLayer->setDataDefinedProperty( "color", properties["color_expression"] );
956  }
957  return patternLayer;
958 }
959 
961 {
962  return "LinePatternFill";
963 }
964 
965 void QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolV2RenderContext& context, QBrush& brush, double lineAngle, double distance,
966  double lineWidth, const QColor& color )
967 {
968  const QgsRenderContext& ctx = context.renderContext();
969  double outlinePixelWidth = lineWidth * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mLineWidthUnit );
970  double outputPixelDist = distance * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mDistanceUnit );
971  double outputPixelOffset = mOffset * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mOffsetUnit );
972 
973  //create image
974  int height, width;
975  if ( qgsDoubleNear( lineAngle, 0 ) || qgsDoubleNear( lineAngle, 360 ) || qgsDoubleNear( lineAngle, 90 ) || qgsDoubleNear( lineAngle, 180 ) || qgsDoubleNear( lineAngle, 270 ) )
976  {
977  height = outputPixelDist;
978  width = height; //width can be set to arbitrary value
979  }
980  else
981  {
982  height = qAbs( outputPixelDist / cos( lineAngle * M_PI / 180 ) ); //keep perpendicular distance between lines constant
983  width = qAbs( height / tan( lineAngle * M_PI / 180 ) );
984  }
985 
986  //depending on the angle, we might need to render into a larger image and use a subset of it
987  int dx = 0;
988  int dy = 0;
989 
990  if ( width > 10000 || height > 10000 ) //protect symbol layer from eating too much memory
991  {
992  QImage img;
993  mBrush.setTextureImage( img );
994  return;
995  }
996 
997  QImage patternImage( width, height, QImage::Format_ARGB32 );
998  patternImage.fill( 0 );
999  QPainter p( &patternImage );
1000 
1001  p.setRenderHint( QPainter::Antialiasing, true );
1002  QPen pen( color );
1003  pen.setWidthF( outlinePixelWidth );
1004  pen.setCapStyle( Qt::FlatCap );
1005  p.setPen( pen );
1006 
1007  QPoint p1, p2, p3, p4, p5, p6;
1008  if ( qgsDoubleNear( lineAngle, 0.0 ) || qgsDoubleNear( lineAngle, 360.0 ) || qgsDoubleNear( lineAngle, 180.0 ) )
1009  {
1010  p1 = QPoint( 0, height );
1011  p2 = QPoint( width, height );
1012  p3 = QPoint( 0, 0 );
1013  p4 = QPoint( width, 0 );
1014  p5 = QPoint( 0, 2 * height );
1015  p6 = QPoint( width, 2 * height );
1016  }
1017  else if ( qgsDoubleNear( lineAngle, 90.0 ) || qgsDoubleNear( lineAngle, 270.0 ) )
1018  {
1019  p1 = QPoint( 0, height );
1020  p2 = QPoint( 0, 0 );
1021  p3 = QPoint( width, height );
1022  p4 = QPoint( width, 0 );
1023  p5 = QPoint( -width, height );
1024  p6 = QPoint( -width, 0 );
1025  }
1026  else if (( lineAngle > 0 && lineAngle < 90 ) || ( lineAngle > 180 && lineAngle < 270 ) )
1027  {
1028  dx = outputPixelDist * cos(( 90 - lineAngle ) * M_PI / 180.0 );
1029  dy = outputPixelDist * sin(( 90 - lineAngle ) * M_PI / 180.0 );
1030  p1 = QPoint( 0, height );
1031  p2 = QPoint( width, 0 );
1032  p3 = QPoint( -dx, height - dy );
1033  p4 = QPoint( width - dx, -dy ); //p4 = QPoint( p3.x() + width, p3.y() - height );
1034  p5 = QPoint( dx, height + dy );
1035  p6 = QPoint( width + dx, dy ); //p6 = QPoint( p5.x() + width, p5.y() - height );
1036  }
1037  else if (( lineAngle < 180 ) || ( lineAngle > 270 && lineAngle < 360 ) )
1038  {
1039  dy = outputPixelDist * cos(( 180 - lineAngle ) * M_PI / 180 );
1040  dx = outputPixelDist * sin(( 180 - lineAngle ) * M_PI / 180 );
1041  p1 = QPoint( width, height );
1042  p2 = QPoint( 0, 0 );
1043  p5 = QPoint( width + dx, height - dy );
1044  p6 = QPoint( p5.x() - width, p5.y() - height ); //p6 = QPoint( dx, -dy );
1045  p3 = QPoint( width - dx, height + dy );
1046  p4 = QPoint( p3.x() - width, p3.y() - height ); //p4 = QPoint( -dx, dy );
1047  }
1048 
1049  if ( !qgsDoubleNear( mOffset, 0.0 ) ) //shift everything
1050  {
1051  QPointF tempPt;
1052  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p1, p3, outputPixelDist + outputPixelOffset );
1053  p3 = QPoint( tempPt.x(), tempPt.y() );
1054  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p2, p4, outputPixelDist + outputPixelOffset );
1055  p4 = QPoint( tempPt.x(), tempPt.y() );
1056  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p1, p5, outputPixelDist - outputPixelOffset );
1057  p5 = QPoint( tempPt.x(), tempPt.y() );
1058  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p2, p6, outputPixelDist - outputPixelOffset );
1059  p6 = QPoint( tempPt.x(), tempPt.y() );
1060 
1061  //update p1, p2 last
1062  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p1, p3, outputPixelOffset ).toPoint();
1063  p1 = QPoint( tempPt.x(), tempPt.y() );
1064  tempPt = QgsSymbolLayerV2Utils::pointOnLineWithDistance( p2, p4, outputPixelOffset ).toPoint();
1065  p2 = QPoint( tempPt.x(), tempPt.y() );;
1066  }
1067 
1068  p.drawLine( p1, p2 );
1069  p.drawLine( p3, p4 );
1070  p.drawLine( p5, p6 );
1071  p.end();
1072 
1073  //set image to mBrush
1074  if ( !qgsDoubleNear( context.alpha(), 1.0 ) )
1075  {
1076  QImage transparentImage = patternImage.copy();
1077  QgsSymbolLayerV2Utils::multiplyImageOpacity( &transparentImage, context.alpha() );
1078  brush.setTextureImage( transparentImage );
1079  }
1080  else
1081  {
1082  brush.setTextureImage( patternImage );
1083  }
1084 
1085  QTransform brushTransform;
1086  brushTransform.scale( 1.0 / context.renderContext().rasterScaleFactor(), 1.0 / context.renderContext().rasterScaleFactor() );
1087  brush.setTransform( brushTransform );
1088 }
1089 
1091 {
1093 
1094  if ( mOutline )
1095  {
1096  mOutline->startRender( context.renderContext() );
1097  }
1098 
1099  prepareExpressions( context.layer() );
1100 }
1101 
1103 {
1104 }
1105 
1107 {
1108  QgsStringMap map;
1109  map.insert( "lineangle", QString::number( mLineAngle ) );
1110  map.insert( "distance", QString::number( mDistance ) );
1111  map.insert( "linewidth", QString::number( mLineWidth ) );
1112  map.insert( "color", QgsSymbolLayerV2Utils::encodeColor( mColor ) );
1113  map.insert( "offset", QString::number( mOffset ) );
1114  map.insert( "distance_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mDistanceUnit ) );
1115  map.insert( "line_width_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mLineWidthUnit ) );
1116  map.insert( "offset_unit", QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit ) );
1118  return map;
1119 }
1120 
1122 {
1124  if ( mOutline )
1125  {
1126  clonedLayer->setSubSymbol( mOutline->clone() );
1127  }
1128  clonedLayer->setDistanceUnit( mDistanceUnit );
1129  clonedLayer->setLineWidthUnit( mLineWidthUnit );
1130  clonedLayer->setOffsetUnit( mOffsetUnit );
1131  copyDataDefinedProperties( clonedLayer );
1132  return clonedLayer;
1133 }
1134 
1135 void QgsLinePatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
1136 {
1137  QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
1138  if ( !props.value( "uom", "" ).isEmpty() )
1139  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
1140  element.appendChild( symbolizerElem );
1141 
1142  // <Geometry>
1143  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
1144 
1145  QDomElement fillElem = doc.createElement( "se:Fill" );
1146  symbolizerElem.appendChild( fillElem );
1147 
1148  QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" );
1149  fillElem.appendChild( graphicFillElem );
1150 
1151  QDomElement graphicElem = doc.createElement( "se:Graphic" );
1152  graphicFillElem.appendChild( graphicElem );
1153 
1154  QgsSymbolLayerV2Utils::wellKnownMarkerToSld( doc, graphicElem, "horline", QColor(), mColor, mLineWidth, mDistance );
1155 
1156  // <Rotation>
1157  QString angleFunc;
1158  bool ok;
1159  double angle = props.value( "angle", "0" ).toDouble( &ok );
1160  if ( !ok )
1161  {
1162  angleFunc = QString( "%1 + %2" ).arg( props.value( "angle", "0" ) ).arg( mLineAngle );
1163  }
1164  else if ( angle + mLineAngle != 0 )
1165  {
1166  angleFunc = QString::number( angle + mLineAngle );
1167  }
1168  QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, angleFunc );
1169 
1170  // <se:Displacement>
1171  QPointF lineOffset( sin( mLineAngle ) * mOffset, cos( mLineAngle ) * mOffset );
1172  QgsSymbolLayerV2Utils::createDisplacementElement( doc, graphicElem, lineOffset );
1173 
1174  if ( mOutline )
1175  {
1176  // the outline sub symbol should be stored within the Stroke element,
1177  // but it will be stored in a separated LineSymbolizer because it could
1178  // have more than one layer
1179  mOutline->toSld( doc, element, props );
1180  }
1181 }
1182 
1184 {
1185  QString featureStyle;
1186  featureStyle.append( "Brush(" );
1187  featureStyle.append( QString( "fc:%1" ).arg( mColor.name() ) );
1188  featureStyle.append( QString( ",bc:%1" ).arg( "#00000000" ) ); //transparent background
1189  featureStyle.append( ",id:\"ogr-brush-2\"" );
1190  featureStyle.append( QString( ",a:%1" ).arg( mLineAngle ) );
1191  featureStyle.append( QString( ",s:%1" ).arg( mLineWidth * widthScaleFactor ) );
1192  featureStyle.append( ",dx:0mm" );
1193  featureStyle.append( QString( ",dy:%1mm" ).arg( mDistance * widthScaleFactor ) );
1194  featureStyle.append( ")" );
1195  return featureStyle;
1196 }
1197 
1199 {
1200  QgsExpression* lineAngleExpression = expression( "lineangle" );
1201  QgsExpression* distanceExpression = expression( "distance" );
1202  QgsExpression* lineWidthExpression = expression( "linewidth" );
1203  QgsExpression* colorExpression = expression( "color" );
1204  if ( !lineAngleExpression && !distanceExpression && !lineWidthExpression && !colorExpression )
1205  {
1206  return; //no data defined settings
1207  }
1208 
1209  double lineAngle = mLineAngle;
1210  if ( lineAngleExpression )
1211  {
1212  lineAngle = lineAngleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
1213  }
1214  double distance = mDistance;
1215  if ( distanceExpression )
1216  {
1217  distance = distanceExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
1218  }
1219  double lineWidth = mLineWidth;
1220  if ( lineWidthExpression )
1221  {
1222  lineWidth = lineWidthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
1223  }
1224  QColor color = mColor;
1225  if ( colorExpression )
1226  {
1227  color = QgsSymbolLayerV2Utils::decodeColor( colorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
1228  }
1229  applyPattern( context, mBrush, lineAngle, distance, lineWidth, color );
1230 }
1231 
1233 {
1234  QgsDebugMsg( "Entered." );
1235 
1236  QString name;
1237  QColor fillColor, lineColor;
1238  double size, lineWidth;
1239 
1240  QDomElement fillElem = element.firstChildElement( "Fill" );
1241  if ( fillElem.isNull() )
1242  return NULL;
1243 
1244  QDomElement graphicFillElem = fillElem.firstChildElement( "GraphicFill" );
1245  if ( graphicFillElem.isNull() )
1246  return NULL;
1247 
1248  QDomElement graphicElem = graphicFillElem.firstChildElement( "Graphic" );
1249  if ( graphicElem.isNull() )
1250  return NULL;
1251 
1252  if ( !QgsSymbolLayerV2Utils::wellKnownMarkerFromSld( graphicElem, name, fillColor, lineColor, lineWidth, size ) )
1253  return NULL;
1254 
1255  if ( name != "horline" )
1256  return NULL;
1257 
1258  double angle = 0.0;
1259  QString angleFunc;
1260  if ( QgsSymbolLayerV2Utils::rotationFromSldElement( graphicElem, angleFunc ) )
1261  {
1262  bool ok;
1263  double d = angleFunc.toDouble( &ok );
1264  if ( ok )
1265  angle = d;
1266  }
1267 
1268  double offset = 0.0;
1269  QPointF vectOffset;
1270  if ( QgsSymbolLayerV2Utils::displacementFromSldElement( graphicElem, vectOffset ) )
1271  {
1272  offset = sqrt( pow( vectOffset.x(), 2 ) + pow( vectOffset.y(), 2 ) );
1273  }
1274 
1276  sl->setColor( lineColor );
1277  sl->setLineWidth( lineWidth );
1278  sl->setLineAngle( angle );
1279  sl->setOffset( offset );
1280  sl->setDistance( size );
1281 
1282  // try to get the outline
1283  QDomElement strokeElem = element.firstChildElement( "Stroke" );
1284  if ( !strokeElem.isNull() )
1285  {
1287  if ( l )
1288  {
1289  QgsSymbolLayerV2List layers;
1290  layers.append( l );
1291  sl->setSubSymbol( new QgsLineSymbolV2( layers ) );
1292  }
1293  }
1294 
1295  return sl;
1296 }
1297 
1299 
1301  mDistanceXUnit( QgsSymbolV2::MM ), mDistanceY( 15 ), mDistanceYUnit( QgsSymbolV2::MM ), mDisplacementX( 0 ), mDisplacementXUnit( QgsSymbolV2::MM ),
1302  mDisplacementY( 0 ), mDisplacementYUnit( QgsSymbolV2::MM )
1303 {
1304  mDistanceX = 15;
1305  mDistanceY = 15;
1306  mDisplacementX = 0;
1307  mDisplacementY = 0;
1309  QgsImageFillSymbolLayer::setSubSymbol( 0 ); //no outline
1310 }
1311 
1313 {
1314 }
1315 
1317 {
1318  mDistanceXUnit = unit;
1319  mDistanceYUnit = unit;
1320  mDisplacementXUnit = unit;
1321  mDisplacementYUnit = unit;
1322 }
1323 
1325 {
1327  if ( mDistanceYUnit != unit || mDisplacementXUnit != unit || mDisplacementYUnit != unit )
1328  {
1329  return QgsSymbolV2::Mixed;
1330  }
1331  return unit;
1332 }
1333 
1335 {
1337  if ( properties.contains( "distance_x" ) )
1338  {
1339  layer->setDistanceX( properties["distance_x"].toDouble() );
1340  }
1341  if ( properties.contains( "distance_y" ) )
1342  {
1343  layer->setDistanceY( properties["distance_y"].toDouble() );
1344  }
1345  if ( properties.contains( "displacement_x" ) )
1346  {
1347  layer->setDisplacementX( properties["displacement_x"].toDouble() );
1348  }
1349  if ( properties.contains( "displacement_y" ) )
1350  {
1351  layer->setDisplacementY( properties["displacement_y"].toDouble() );
1352  }
1353 
1354  if ( properties.contains( "distance_x_unit" ) )
1355  {
1356  layer->setDistanceXUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["distance_x_unit"] ) );
1357  }
1358  if ( properties.contains( "distance_y_unit" ) )
1359  {
1360  layer->setDistanceYUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["distance_y_unit"] ) );
1361  }
1362  if ( properties.contains( "displacement_x_unit" ) )
1363  {
1364  layer->setDisplacementXUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["displacement_x_unit"] ) );
1365  }
1366  if ( properties.contains( "displacement_y_unit" ) )
1367  {
1368  layer->setDisplacementYUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( properties["displacement_y_unit"] ) );
1369  }
1370 
1371  //data defined properties
1372  if ( properties.contains( "distance_x_expression" ) )
1373  {
1374  layer->setDataDefinedProperty( "distance_x", properties["distance_x_expression"] );
1375  }
1376  if ( properties.contains( "distance_y_expression" ) )
1377  {
1378  layer->setDataDefinedProperty( "distance_y", properties["distance_y_expression"] );
1379  }
1380  if ( properties.contains( "displacement_x_expression" ) )
1381  {
1382  layer->setDataDefinedProperty( "displacement_x", properties["displacement_x_expression"] );
1383  }
1384  if ( properties.contains( "displacement_y_expression" ) )
1385  {
1386  layer->setDataDefinedProperty( "displacement_y", properties["displacement_y_expression"] );
1387  }
1388  return layer;
1389 }
1390 
1392 {
1393  return "PointPatternFill";
1394 }
1395 
1396 void QgsPointPatternFillSymbolLayer::applyPattern( const QgsSymbolV2RenderContext& context, QBrush& brush, double distanceX, double distanceY,
1397  double displacementX, double displacementY )
1398 {
1399  //render 3 rows and columns in one go to easily incorporate displacement
1400  const QgsRenderContext& ctx = context.renderContext();
1401  double width = distanceX * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mDistanceXUnit ) * 2.0;
1402  double height = distanceY * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mDistanceYUnit ) * 2.0;
1403 
1404  if ( width > 10000 || height > 10000 ) //protect symbol layer from eating too much memory
1405  {
1406  QImage img;
1407  brush.setTextureImage( img );
1408  return;
1409  }
1410 
1411  QImage patternImage( width, height, QImage::Format_ARGB32 );
1412  patternImage.fill( 0 );
1413 
1414  if ( mMarkerSymbol )
1415  {
1416  QPainter p( &patternImage );
1417 
1418  //marker rendering needs context for drawing on patternImage
1419  QgsRenderContext pointRenderContext;
1420  pointRenderContext.setPainter( &p );
1421  pointRenderContext.setRasterScaleFactor( 1.0 );
1422  pointRenderContext.setScaleFactor( context.renderContext().scaleFactor() * context.renderContext().rasterScaleFactor() );
1424  pointRenderContext.setMapToPixel( mtp );
1425  pointRenderContext.setForceVectorOutput( false );
1426 
1427  mMarkerSymbol->startRender( pointRenderContext );
1428 
1429  //render corner points
1430  mMarkerSymbol->renderPoint( QPointF( 0, 0 ), context.feature(), pointRenderContext );
1431  mMarkerSymbol->renderPoint( QPointF( width, 0 ), context.feature(), pointRenderContext );
1432  mMarkerSymbol->renderPoint( QPointF( 0, height ), context.feature(), pointRenderContext );
1433  mMarkerSymbol->renderPoint( QPointF( width, height ), context.feature(), pointRenderContext );
1434 
1435  //render displaced points
1436  double displacementPixelX = displacementX * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mDisplacementXUnit );
1437  double displacementPixelY = displacementY * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( ctx, mDisplacementYUnit );
1438  mMarkerSymbol->renderPoint( QPointF( width / 2.0, -displacementPixelY ), context.feature(), pointRenderContext );
1439  mMarkerSymbol->renderPoint( QPointF( displacementPixelX, height / 2.0 ), context.feature(), pointRenderContext );
1440  mMarkerSymbol->renderPoint( QPointF( width / 2.0 + displacementPixelX, height / 2.0 - displacementPixelY ), context.feature(), pointRenderContext );
1441  mMarkerSymbol->renderPoint( QPointF( width + displacementPixelX, height / 2.0 ), context.feature(), pointRenderContext );
1442  mMarkerSymbol->renderPoint( QPointF( width / 2.0, height - displacementPixelY ), context.feature(), pointRenderContext );
1443 
1444  mMarkerSymbol->stopRender( pointRenderContext );
1445  }
1446 
1447  if ( !qgsDoubleNear( context.alpha(), 1.0 ) )
1448  {
1449  QImage transparentImage = patternImage.copy();
1450  QgsSymbolLayerV2Utils::multiplyImageOpacity( &transparentImage, context.alpha() );
1451  brush.setTextureImage( transparentImage );
1452  }
1453  else
1454  {
1455  brush.setTextureImage( patternImage );
1456  }
1457  QTransform brushTransform;
1458  brushTransform.scale( 1.0 / context.renderContext().rasterScaleFactor(), 1.0 / context.renderContext().rasterScaleFactor() );
1459  brush.setTransform( brushTransform );
1460 }
1461 
1463 {
1465 
1466  if ( mOutline )
1467  {
1468  mOutline->startRender( context.renderContext() );
1469  }
1470  prepareExpressions( context.layer() );
1471 }
1472 
1474 {
1475  if ( mOutline )
1476  {
1477  mOutline->stopRender( context.renderContext() );
1478  }
1479 }
1480 
1482 {
1483  QgsStringMap propertyMap;
1484  propertyMap["distance_x"] = QString::number( mDistanceX );
1485  propertyMap["distance_y"] = QString::number( mDistanceY );
1486  propertyMap["displacement_x"] = QString::number( mDisplacementX );
1487  propertyMap["displacement_y"] = QString::number( mDisplacementY );
1488  propertyMap["distance_x_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mDistanceXUnit );
1489  propertyMap["distance_y_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mDistanceYUnit );
1490  propertyMap["displacement_x_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mDisplacementXUnit );
1491  propertyMap["displacement_y_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mDisplacementYUnit );
1492  saveDataDefinedProperties( propertyMap );
1493  return propertyMap;
1494 }
1495 
1497 {
1499  if ( mMarkerSymbol )
1500  {
1501  clonedLayer->setSubSymbol( mMarkerSymbol->clone() );
1502  }
1503  copyDataDefinedProperties( clonedLayer );
1504  return clonedLayer;
1505 }
1506 
1507 void QgsPointPatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
1508 {
1509  for ( int i = 0; i < mMarkerSymbol->symbolLayerCount(); i++ )
1510  {
1511  QDomElement symbolizerElem = doc.createElement( "se:PolygonSymbolizer" );
1512  if ( !props.value( "uom", "" ).isEmpty() )
1513  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
1514  element.appendChild( symbolizerElem );
1515 
1516  // <Geometry>
1517  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
1518 
1519  QDomElement fillElem = doc.createElement( "se:Fill" );
1520  symbolizerElem.appendChild( fillElem );
1521 
1522  QDomElement graphicFillElem = doc.createElement( "se:GraphicFill" );
1523  fillElem.appendChild( graphicFillElem );
1524 
1525  // store distanceX, distanceY, displacementX, displacementY in a <VendorOption>
1526  QString dist = QgsSymbolLayerV2Utils::encodePoint( QPointF( mDistanceX, mDistanceY ) );
1527  QDomElement distanceElem = QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "distance", dist );
1528  symbolizerElem.appendChild( distanceElem );
1529 
1531  QgsMarkerSymbolLayerV2 *markerLayer = static_cast<QgsMarkerSymbolLayerV2 *>( layer );
1532  if ( !markerLayer )
1533  {
1534  QString errorMsg = QString( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( layer->layerType() );
1535  graphicFillElem.appendChild( doc.createComment( errorMsg ) );
1536  }
1537  else
1538  {
1539  markerLayer->writeSldMarker( doc, graphicFillElem, props );
1540  }
1541  }
1542 }
1543 
1545 {
1546  Q_UNUSED( element );
1547  return NULL;
1548 }
1549 
1551 {
1552  if ( !symbol )
1553  {
1554  return false;
1555  }
1556 
1557  if ( symbol->type() == QgsSymbolV2::Marker )
1558  {
1559  QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( symbol );
1560  delete mMarkerSymbol;
1561  mMarkerSymbol = markerSymbol;
1562  }
1563  return true;
1564 }
1565 
1567 {
1568  QgsExpression* distanceXExpression = expression( "distance_x" );
1569  QgsExpression* distanceYExpression = expression( "distance_y" );
1570  QgsExpression* displacementXExpression = expression( "displacement_x" );
1571  QgsExpression* displacementYExpression = expression( "displacement_y" );
1572  if ( !distanceXExpression && !distanceYExpression && !displacementXExpression && !displacementYExpression )
1573  {
1574  return;
1575  }
1576 
1577  double distanceX = mDistanceX;
1578  if ( distanceXExpression )
1579  {
1580  distanceX = distanceXExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
1581  }
1582  double distanceY = mDistanceY;
1583  if ( distanceYExpression )
1584  {
1585  distanceY = distanceYExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
1586  }
1587  double displacementX = mDisplacementX;
1588  if ( displacementXExpression )
1589  {
1590  displacementX = displacementXExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
1591  }
1592  double displacementY = mDisplacementY;
1593  if ( displacementYExpression )
1594  {
1595  displacementY = displacementYExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
1596  }
1597  applyPattern( context, mBrush, distanceX, distanceY, displacementX, displacementY );
1598 }
1599 
1601 
1602 
1604 {
1606 }
1607 
1609 {
1610  delete mMarker;
1611 }
1612 
1614 {
1615  Q_UNUSED( properties );
1616  return new QgsCentroidFillSymbolLayerV2();
1617 }
1618 
1620 {
1621  return "CentroidFill";
1622 }
1623 
1624 void QgsCentroidFillSymbolLayerV2::setColor( const QColor& color )
1625 {
1626  mMarker->setColor( color );
1627  mColor = color;
1628 }
1629 
1631 {
1632  mMarker->setAlpha( context.alpha() );
1633  mMarker->startRender( context.renderContext() );
1634 }
1635 
1637 {
1638  mMarker->stopRender( context.renderContext() );
1639 }
1640 
1641 void QgsCentroidFillSymbolLayerV2::renderPolygon( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
1642 {
1643  Q_UNUSED( rings );
1644 
1645  // calculate centroid
1646  double cx = 0, cy = 0;
1647  double area, sum = 0;
1648  for ( int i = points.count() - 1, j = 0; j < points.count(); i = j++ )
1649  {
1650  const QPointF& p1 = points[i];
1651  const QPointF& p2 = points[j];
1652  area = p1.x() * p2.y() - p1.y() * p2.x();
1653  sum += area;
1654  cx += ( p1.x() + p2.x() ) * area;
1655  cy += ( p1.y() + p2.y() ) * area;
1656  }
1657  sum *= 3.0;
1658  cx /= sum;
1659  cy /= sum;
1660 
1661  mMarker->renderPoint( QPointF( cx, cy ), context.feature(), context.renderContext(), -1, context.selected() );
1662 }
1663 
1665 {
1666  return QgsStringMap();
1667 }
1668 
1670 {
1672  x->setSubSymbol( mMarker->clone() );
1673  return x;
1674 }
1675 
1676 void QgsCentroidFillSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
1677 {
1678  // SLD 1.0 specs says: "if a line, polygon, or raster geometry is
1679  // used with PointSymbolizer, then the semantic is to use the centroid
1680  // of the geometry, or any similar representative point.
1681  mMarker->toSld( doc, element, props );
1682 }
1683 
1685 {
1686  QgsDebugMsg( "Entered." );
1687 
1689  if ( !l )
1690  return NULL;
1691 
1692  QgsSymbolLayerV2List layers;
1693  layers.append( l );
1694  QgsMarkerSymbolV2 *marker = new QgsMarkerSymbolV2( layers );
1695 
1697  x->setSubSymbol( marker );
1698  return x;
1699 }
1700 
1701 
1703 {
1704  return mMarker;
1705 }
1706 
1708 {
1709  if ( symbol == NULL || symbol->type() != QgsSymbolV2::Marker )
1710  {
1711  delete symbol;
1712  return false;
1713  }
1714 
1715  delete mMarker;
1716  mMarker = static_cast<QgsMarkerSymbolV2*>( symbol );
1717  mColor = mMarker->color();
1718  return true;
1719 }
1720 
1722 {
1723  QSet<QString> attributes;
1724 
1725  attributes.unite( QgsSymbolLayerV2::usedAttributes() );
1726 
1727  if ( mMarker )
1728  attributes.unite( mMarker->usedAttributes() );
1729 
1730  return attributes;
1731 }
1732 
1734 {
1735  if ( mMarker )
1736  {
1737  return mMarker->outputUnit();
1738  }
1739  return QgsSymbolV2::Mixed; //mOutputUnit;
1740 }