QGIS API Documentation  2.7.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
qgslinesymbollayerv2.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslinesymbollayerv2.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 "qgslinesymbollayerv2.h"
17 #include "qgsdxfexport.h"
18 #include "qgssymbollayerv2utils.h"
19 #include "qgsexpression.h"
20 #include "qgsrendercontext.h"
21 #include "qgslogger.h"
22 #include "qgsvectorlayer.h"
23 #include "qgsgeometrysimplifier.h"
24 
25 #include <QPainter>
26 #include <QDomDocument>
27 #include <QDomElement>
28 
29 #include <cmath>
30 
31 QgsSimpleLineSymbolLayerV2::QgsSimpleLineSymbolLayerV2( QColor color, double width, Qt::PenStyle penStyle )
32  : mPenStyle( penStyle )
33  , mPenJoinStyle( DEFAULT_SIMPLELINE_JOINSTYLE )
34  , mPenCapStyle( DEFAULT_SIMPLELINE_CAPSTYLE )
35  , mOffset( 0 )
36  , mOffsetUnit( QgsSymbolV2::MM )
37  , mUseCustomDashPattern( false )
38  , mCustomDashPatternUnit( QgsSymbolV2::MM )
39  , mDrawInsidePolygon( false )
40 {
41  mColor = color;
42  mWidth = width;
43  mCustomDashVector << 5 << 2;
44 }
45 
47 {
49  mWidthUnit = unit;
50  mOffsetUnit = unit;
52 }
53 
55 {
57  if ( mWidthUnit != unit || mOffsetUnit != unit || mCustomDashPatternUnit != unit )
58  {
59  return QgsSymbolV2::Mixed;
60  }
61  return unit;
62 }
63 
65 {
67  mWidthMapUnitScale = scale;
68  mOffsetMapUnitScale = scale;
70 }
71 
73 {
77  {
78  return mWidthMapUnitScale;
79  }
80  return QgsMapUnitScale();
81 }
82 
84 {
88 
89  if ( props.contains( "line_color" ) )
90  {
91  color = QgsSymbolLayerV2Utils::decodeColor( props["line_color"] );
92  }
93  else if ( props.contains( "outline_color" ) )
94  {
95  color = QgsSymbolLayerV2Utils::decodeColor( props["outline_color"] );
96  }
97  else if ( props.contains( "color" ) )
98  {
99  //pre 2.5 projects used "color"
100  color = QgsSymbolLayerV2Utils::decodeColor( props["color"] );
101  }
102  if ( props.contains( "line_width" ) )
103  {
104  width = props["line_width"].toDouble();
105  }
106  else if ( props.contains( "outline_width" ) )
107  {
108  width = props["outline_width"].toDouble();
109  }
110  else if ( props.contains( "width" ) )
111  {
112  //pre 2.5 projects used "width"
113  width = props["width"].toDouble();
114  }
115  if ( props.contains( "line_style" ) )
116  {
117  penStyle = QgsSymbolLayerV2Utils::decodePenStyle( props["line_style"] );
118  }
119  else if ( props.contains( "outline_style" ) )
120  {
121  penStyle = QgsSymbolLayerV2Utils::decodePenStyle( props["outline_style"] );
122  }
123  else if ( props.contains( "penstyle" ) )
124  {
125  penStyle = QgsSymbolLayerV2Utils::decodePenStyle( props["penstyle"] );
126  }
127 
128  QgsSimpleLineSymbolLayerV2* l = new QgsSimpleLineSymbolLayerV2( color, width, penStyle );
129  if ( props.contains( "line_width_unit" ) )
130  {
131  l->setWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["line_width_unit"] ) );
132  }
133  else if ( props.contains( "outline_width_unit" ) )
134  {
135  l->setWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["outline_width_unit"] ) );
136  }
137  else if ( props.contains( "width_unit" ) )
138  {
139  //pre 2.5 projects used "width_unit"
140  l->setWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["width_unit"] ) );
141  }
142  if ( props.contains( "width_map_unit_scale" ) )
143  l->setWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["width_map_unit_scale"] ) );
144  if ( props.contains( "offset" ) )
145  l->setOffset( props["offset"].toDouble() );
146  if ( props.contains( "offset_unit" ) )
147  l->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
148  if ( props.contains( "offset_map_unit_scale" ) )
149  l->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_map_unit_scale"] ) );
150  if ( props.contains( "joinstyle" ) )
152  if ( props.contains( "capstyle" ) )
153  l->setPenCapStyle( QgsSymbolLayerV2Utils::decodePenCapStyle( props["capstyle"] ) );
154 
155  if ( props.contains( "use_custom_dash" ) )
156  {
157  l->setUseCustomDashPattern( props["use_custom_dash"].toInt() );
158  }
159  if ( props.contains( "customdash" ) )
160  {
162  }
163  if ( props.contains( "customdash_unit" ) )
164  {
165  l->setCustomDashPatternUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["customdash_unit"] ) );
166  }
167  if ( props.contains( "customdash_map_unit_scale" ) )
168  {
169  l->setCustomDashPatternMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["customdash_map_unit_scale"] ) );
170  }
171 
172  if ( props.contains( "draw_inside_polygon" ) )
173  {
174  l->setDrawInsidePolygon( props["draw_inside_polygon"].toInt() );
175  }
176 
177  //data defined properties
178  if ( props.contains( "color_expression" ) )
179  l->setDataDefinedProperty( "color", props["color_expression"] );
180  if ( props.contains( "width_expression" ) )
181  l->setDataDefinedProperty( "width", props["width_expression"] );
182  if ( props.contains( "offset_expression" ) )
183  l->setDataDefinedProperty( "offset", props["offset_expression"] );
184  if ( props.contains( "customdash_expression" ) )
185  l->setDataDefinedProperty( "customdash", props["customdash_expression"] );
186  if ( props.contains( "joinstyle_expression" ) )
187  l->setDataDefinedProperty( "joinstyle", props["joinstyle_expression"] );
188  if ( props.contains( "capstyle_expression" ) )
189  l->setDataDefinedProperty( "capstyle", props["capstyle_expression"] );
190  if ( props.contains( "line_style_expression" ) )
191  l->setDataDefinedProperty( "line_style", props["line_style_expression"] );
192 
193  return l;
194 }
195 
196 
198 {
199  return "SimpleLine";
200 }
201 
203 {
204  QColor penColor = mColor;
205  penColor.setAlphaF( mColor.alphaF() * context.alpha() );
206  mPen.setColor( penColor );
208  mPen.setWidthF( scaledWidth );
209  if ( mUseCustomDashPattern && scaledWidth != 0 )
210  {
211  mPen.setStyle( Qt::CustomDashLine );
212 
213  //scale pattern vector
214  double dashWidthDiv = scaledWidth;
215  //fix dash pattern width in Qt 4.8
216  QStringList versionSplit = QString( qVersion() ).split( "." );
217  if ( versionSplit.size() > 1
218  && versionSplit.at( 1 ).toInt() >= 8
219  && ( scaledWidth * context.renderContext().rasterScaleFactor() ) < 1.0 )
220  {
221  dashWidthDiv = 1.0;
222  }
223  QVector<qreal> scaledVector;
224  QVector<qreal>::const_iterator it = mCustomDashVector.constBegin();
225  for ( ; it != mCustomDashVector.constEnd(); ++it )
226  {
227  //the dash is specified in terms of pen widths, therefore the division
229  }
230  mPen.setDashPattern( scaledVector );
231  }
232  else
233  {
234  mPen.setStyle( mPenStyle );
235  }
236  mPen.setJoinStyle( mPenJoinStyle );
237  mPen.setCapStyle( mPenCapStyle );
238 
239  mSelPen = mPen;
240  QColor selColor = context.renderContext().selectionColor();
241  if ( ! selectionIsOpaque )
242  selColor.setAlphaF( context.alpha() );
243  mSelPen.setColor( selColor );
244 
245  //prepare expressions for data defined properties
246  prepareExpressions( context.fields(), context.renderContext().rendererScale() );
247 }
248 
250 {
251  Q_UNUSED( context );
252 }
253 
254 void QgsSimpleLineSymbolLayerV2::renderPolygonOutline( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
255 {
256  QPainter* p = context.renderContext().painter();
257  if ( !p )
258  {
259  return;
260  }
261 
262  if ( mDrawInsidePolygon )
263  {
264  //only drawing the line on the interior of the polygon, so set clip path for painter
265  p->save();
266  QPainterPath clipPath;
267  clipPath.addPolygon( points );
268 
269  if ( rings != NULL )
270  {
271  //add polygon rings
272  QList<QPolygonF>::const_iterator it = rings->constBegin();
273  for ( ; it != rings->constEnd(); ++it )
274  {
275  QPolygonF ring = *it;
276  clipPath.addPolygon( ring );
277  }
278  }
279 
280  //use intersect mode, as a clip path may already exist (eg, for composer maps)
281  p->setClipPath( clipPath, Qt::IntersectClip );
282  }
283 
284  renderPolyline( points, context );
285  if ( rings )
286  {
287  mOffset = -mOffset; // invert the offset for rings!
288  foreach ( const QPolygonF& ring, *rings )
289  renderPolyline( ring, context );
290  mOffset = -mOffset;
291  }
292 
293  if ( mDrawInsidePolygon )
294  {
295  //restore painter to reset clip path
296  p->restore();
297  }
298 
299 }
300 
302 {
303  QPainter* p = context.renderContext().painter();
304  if ( !p )
305  {
306  return;
307  }
308 
309  //size scaling by field
311  {
312  applySizeScale( context, mPen, mSelPen );
313  }
314 
315  double offset = mOffset;
316  applyDataDefinedSymbology( context, mPen, mSelPen, offset );
317 
318  p->setPen( context.selected() ? mSelPen : mPen );
319 
320  // Disable 'Antialiasing' if the geometry was generalized in the current RenderContext (We known that it must have least #2 points).
321  if ( points.size() <= 2 &&
324  ( p->renderHints() & QPainter::Antialiasing ) )
325  {
326  p->setRenderHint( QPainter::Antialiasing, false );
327  p->drawPolyline( points );
328  p->setRenderHint( QPainter::Antialiasing, true );
329  return;
330  }
331 
332  if ( qgsDoubleNear( offset, 0 ) )
333  {
334  p->drawPolyline( points );
335  }
336  else
337  {
339  QList<QPolygonF> mline = ::offsetLine( points, scaledOffset, context.feature() ? context.feature()->geometry()->type() : QGis::Line );
340  for ( int part = 0; part < mline.count(); ++part )
341  p->drawPolyline( mline[ part ] );
342  }
343 }
344 
346 {
347  QgsStringMap map;
348  map["line_color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
349  map["line_width"] = QString::number( mWidth );
350  map["line_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mWidthUnit );
351  map["width_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mWidthMapUnitScale );
352  map["line_style"] = QgsSymbolLayerV2Utils::encodePenStyle( mPenStyle );
355  map["offset"] = QString::number( mOffset );
357  map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
358  map["use_custom_dash"] = ( mUseCustomDashPattern ? "1" : "0" );
362  map["draw_inside_polygon"] = ( mDrawInsidePolygon ? "1" : "0" );
364  return map;
365 }
366 
368 {
370  l->setWidthUnit( mWidthUnit );
376  l->setOffset( mOffset );
383  return l;
384 }
385 
386 void QgsSimpleLineSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
387 {
388  if ( mPenStyle == Qt::NoPen )
389  return;
390 
391  QDomElement symbolizerElem = doc.createElement( "se:LineSymbolizer" );
392  if ( !props.value( "uom", "" ).isEmpty() )
393  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
394  element.appendChild( symbolizerElem );
395 
396  // <Geometry>
397  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
398 
399  // <Stroke>
400  QDomElement strokeElem = doc.createElement( "se:Stroke" );
401  symbolizerElem.appendChild( strokeElem );
402 
403  Qt::PenStyle penStyle = mUseCustomDashPattern ? Qt::CustomDashLine : mPenStyle;
404  QgsSymbolLayerV2Utils::lineToSld( doc, strokeElem, penStyle, mColor, mWidth,
406 
407  // <se:PerpendicularOffset>
408  if ( mOffset != 0 )
409  {
410  QDomElement perpOffsetElem = doc.createElement( "se:PerpendicularOffset" );
411  perpOffsetElem.appendChild( doc.createTextNode( QString::number( mOffset ) ) );
412  symbolizerElem.appendChild( perpOffsetElem );
413  }
414 }
415 
416 QString QgsSimpleLineSymbolLayerV2::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
417 {
418  if ( mUseCustomDashPattern )
419  {
420  return QgsSymbolLayerV2Utils::ogrFeatureStylePen( mWidth, mmScaleFactor, mapUnitScaleFactor,
421  mPen.color(), mPenJoinStyle,
423  }
424  else
425  {
426  return QgsSymbolLayerV2Utils::ogrFeatureStylePen( mWidth, mmScaleFactor, mapUnitScaleFactor, mPen.color(), mPenJoinStyle,
428  }
429 }
430 
432 {
433  QgsDebugMsg( "Entered." );
434 
435  QDomElement strokeElem = element.firstChildElement( "Stroke" );
436  if ( strokeElem.isNull() )
437  return NULL;
438 
439  Qt::PenStyle penStyle;
440  QColor color;
441  double width;
442  Qt::PenJoinStyle penJoinStyle;
443  Qt::PenCapStyle penCapStyle;
444  QVector<qreal> customDashVector;
445 
446  if ( !QgsSymbolLayerV2Utils::lineFromSld( strokeElem, penStyle,
447  color, width,
448  &penJoinStyle, &penCapStyle,
449  &customDashVector ) )
450  return NULL;
451 
452  double offset = 0.0;
453  QDomElement perpOffsetElem = element.firstChildElement( "PerpendicularOffset" );
454  if ( !perpOffsetElem.isNull() )
455  {
456  bool ok;
457  double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
458  if ( ok )
459  offset = d;
460  }
461 
462  QgsSimpleLineSymbolLayerV2* l = new QgsSimpleLineSymbolLayerV2( color, width, penStyle );
463  l->setOffset( offset );
464  l->setPenJoinStyle( penJoinStyle );
465  l->setPenCapStyle( penCapStyle );
466  l->setUseCustomDashPattern( penStyle == Qt::CustomDashLine );
467  l->setCustomDashVector( customDashVector );
468  return l;
469 }
470 
471 void QgsSimpleLineSymbolLayerV2::applySizeScale( QgsSymbolV2RenderContext& context, QPen& pen, QPen& selPen )
472 {
474  pen.setWidthF( scaledWidth );
475  selPen.setWidthF( scaledWidth );
476 }
477 
478 void QgsSimpleLineSymbolLayerV2::applyDataDefinedSymbology( QgsSymbolV2RenderContext& context, QPen& pen, QPen& selPen, double& offset )
479 {
480  if ( mDataDefinedProperties.isEmpty() )
481  return; // shortcut
482 
483  //data defined properties
484  QgsExpression* strokeWidthExpression = expression( "width" );
485  if ( strokeWidthExpression )
486  {
487  double scaledWidth = strokeWidthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble()
489  pen.setWidthF( scaledWidth );
490  selPen.setWidthF( scaledWidth );
491  }
492 
493  //color
494  QgsExpression* strokeColorExpression = expression( "color" );
495  if ( strokeColorExpression )
496  {
497  pen.setColor( QgsSymbolLayerV2Utils::decodeColor( strokeColorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) );
498  }
499 
500  //offset
501  QgsExpression* lineOffsetExpression = expression( "offset" );
502  if ( lineOffsetExpression )
503  {
504  offset = lineOffsetExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
505  }
506 
507  //dash dot vector
508  QgsExpression* dashPatternExpression = expression( "customdash" );
509  if ( dashPatternExpression )
510  {
512  double dashWidthDiv = mPen.widthF();
513 
514  if ( strokeWidthExpression )
515  {
516  dashWidthDiv = pen.widthF();
517  scaledWidth = pen.widthF();
518  }
519 
520  //fix dash pattern width in Qt 4.8
521  QStringList versionSplit = QString( qVersion() ).split( "." );
522  if ( versionSplit.size() > 1
523  && versionSplit.at( 1 ).toInt() >= 8
524  && ( scaledWidth * context.renderContext().rasterScaleFactor() ) < 1.0 )
525  {
526  dashWidthDiv = 1.0;
527  }
528 
529  QVector<qreal> dashVector;
530  QStringList dashList = dashPatternExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString().split( ";" );
531  QStringList::const_iterator dashIt = dashList.constBegin();
532  for ( ; dashIt != dashList.constEnd(); ++dashIt )
533  {
534  dashVector.push_back( dashIt->toDouble() * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mCustomDashPatternUnit, mCustomDashPatternMapUnitScale ) / dashWidthDiv );
535  }
536  pen.setDashPattern( dashVector );
537  }
538 
539  //line style
540  QgsExpression* lineStyleExpression = expression( "line_style" );
541  if ( lineStyleExpression )
542  {
543  QString lineStyleString = lineStyleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
544  pen.setStyle( QgsSymbolLayerV2Utils::decodePenStyle( lineStyleString ) );
545  }
546 
547  //join style
548  QgsExpression* joinStyleExpression = expression( "joinstyle" );
549  if ( joinStyleExpression )
550  {
551  QString joinStyleString = joinStyleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
552  pen.setJoinStyle( QgsSymbolLayerV2Utils::decodePenJoinStyle( joinStyleString ) );
553  }
554 
555  //cap style
556  QgsExpression* capStyleExpression = expression( "capstyle" );
557  if ( capStyleExpression )
558  {
559  QString capStyleString = capStyleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
560  pen.setCapStyle( QgsSymbolLayerV2Utils::decodePenCapStyle( capStyleString ) );
561  }
562 }
563 
565 {
566  if ( mDrawInsidePolygon )
567  {
568  //set to clip line to the interior of polygon, so we expect no bleed
569  return 0;
570  }
571  else
572  {
573  return ( mWidth / 2.0 ) + mOffset;
574  }
575 }
576 
578 {
579  unit = mCustomDashPatternUnit;
580  return mUseCustomDashPattern ? mCustomDashVector : QVector<qreal>();
581 }
582 
584 {
585  return mPenStyle;
586 }
587 
589 {
590  double width = mWidth;
591  QgsExpression* strokeWidthExpression = expression( "width" );
592  if ( strokeWidthExpression )
593  {
594  width = strokeWidthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble() * e.mapUnitScaleFactor( e.symbologyScaleDenominator(), widthUnit(), e.mapUnits() );
595  }
596  else if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale )
597  {
599  }
600 
601  return width * e.mapUnitScaleFactor( e.symbologyScaleDenominator(), widthUnit(), e.mapUnits() );
602 }
603 
605 {
606  QgsExpression* strokeColorExpression = expression( "color" );
607  if ( strokeColorExpression )
608  {
609  return ( QgsSymbolLayerV2Utils::decodeColor( strokeColorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) );
610  }
611  return mColor;
612 }
613 
615 {
616  double offset = mOffset;
617  QgsExpression* offsetExpression = expression( "offset" );
618  if ( offsetExpression )
619  {
620  offset = offsetExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
621  }
622  return offset;
623 }
624 
626 
627 
628 class MyLine
629 {
630  public:
631  MyLine( QPointF p1, QPointF p2 ) : mVertical( false ), mIncreasing( false ), mT( 0.0 ), mLength( 0.0 )
632  {
633  if ( p1 == p2 )
634  return; // invalid
635 
636  // tangent and direction
637  if ( p1.x() == p2.x() )
638  {
639  // vertical line - tangent undefined
640  mVertical = true;
641  mIncreasing = ( p2.y() > p1.y() );
642  }
643  else
644  {
645  mVertical = false;
646  mT = float( p2.y() - p1.y() ) / ( p2.x() - p1.x() );
647  mIncreasing = ( p2.x() > p1.x() );
648  }
649 
650  // length
651  double x = ( p2.x() - p1.x() );
652  double y = ( p2.y() - p1.y() );
653  mLength = sqrt( x * x + y * y );
654  }
655 
656  // return angle in radians
657  double angle()
658  {
659  double a = ( mVertical ? M_PI / 2 : atan( mT ) );
660 
661  if ( !mIncreasing )
662  a += M_PI;
663  return a;
664  }
665 
666  // return difference for x,y when going along the line with specified interval
667  QPointF diffForInterval( double interval )
668  {
669  if ( mVertical )
670  return ( mIncreasing ? QPointF( 0, interval ) : QPointF( 0, -interval ) );
671 
672  double alpha = atan( mT );
673  double dx = cos( alpha ) * interval;
674  double dy = sin( alpha ) * interval;
675  return ( mIncreasing ? QPointF( dx, dy ) : QPointF( -dx, -dy ) );
676  }
677 
678  double length() { return mLength; }
679 
680  protected:
681  bool mVertical;
683  double mT;
684  double mLength;
685 };
686 
687 
688 QgsMarkerLineSymbolLayerV2::QgsMarkerLineSymbolLayerV2( bool rotateMarker, double interval )
689 {
693  mMarker = NULL;
694  mOffset = 0;
697  mOffsetAlongLine = 0;
699 
701 }
702 
704 {
705  delete mMarker;
706 }
707 
709 {
710  bool rotate = DEFAULT_MARKERLINE_ROTATE;
712 
713 
714  if ( props.contains( "interval" ) )
715  interval = props["interval"].toDouble();
716  if ( props.contains( "rotate" ) )
717  rotate = ( props["rotate"] == "1" );
718 
719  QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( rotate, interval );
720  if ( props.contains( "offset" ) )
721  {
722  x->setOffset( props["offset"].toDouble() );
723  }
724  if ( props.contains( "offset_unit" ) )
725  {
726  x->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
727  }
728  if ( props.contains( "interval_unit" ) )
729  {
730  x->setIntervalUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["interval_unit"] ) );
731  }
732  if ( props.contains( "offset_along_line" ) )
733  {
734  x->setOffsetAlongLine( props["offset_along_line"].toDouble() );
735  }
736  if ( props.contains( "offset_along_line_unit" ) )
737  {
738  x->setOffsetAlongLineUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_along_line_unit"] ) );
739  }
740  if ( props.contains(( "offset_along_line_map_unit_scale" ) ) )
741  {
742  x->setOffsetAlongLineMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_along_line_map_unit_scale"] ) );
743  }
744 
745  if ( props.contains( "offset_map_unit_scale" ) )
746  {
747  x->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_map_unit_scale"] ) );
748  }
749  if ( props.contains( "interval_map_unit_scale" ) )
750  {
751  x->setIntervalMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["interval_map_unit_scale"] ) );
752  }
753 
754  if ( props.contains( "placement" ) )
755  {
756  if ( props["placement"] == "vertex" )
757  x->setPlacement( Vertex );
758  else if ( props["placement"] == "lastvertex" )
759  x->setPlacement( LastVertex );
760  else if ( props["placement"] == "firstvertex" )
762  else if ( props["placement"] == "centralpoint" )
764  else
765  x->setPlacement( Interval );
766  }
767 
768  //data defined properties
769  if ( props.contains( "interval_expression" ) )
770  {
771  x->setDataDefinedProperty( "interval", props["interval_expression"] );
772  }
773  if ( props.contains( "offset_expression" ) )
774  {
775  x->setDataDefinedProperty( "offset", props["offset_expression"] );
776  }
777  if ( props.contains( "placement_expression" ) )
778  {
779  x->setDataDefinedProperty( "placement", props["placement_expression"] );
780  }
781  if ( props.contains( "offset_along_line_expression" ) )
782  {
783  x->setDataDefinedProperty( "offset_along_line", props["offset_along_line_expression"] );
784  }
785 
786  return x;
787 }
788 
790 {
791  return "MarkerLine";
792 }
793 
794 void QgsMarkerLineSymbolLayerV2::setColor( const QColor& color )
795 {
796  mMarker->setColor( color );
797  mColor = color;
798 }
799 
801 {
802  mMarker->setAlpha( context.alpha() );
803 
804  // if being rotated, it gets initialized with every line segment
805  int hints = 0;
806  if ( mRotateMarker )
810  mMarker->setRenderHints( hints );
811 
812  mMarker->startRender( context.renderContext(), context.fields() );
813 
814  //prepare expressions for data defined properties
815  prepareExpressions( context.fields(), context.renderContext().rendererScale() );
816 }
817 
819 {
820  mMarker->stopRender( context.renderContext() );
821 }
822 
824 {
825  double offset = mOffset;
826  QgsExpression* offsetExpression = expression( "offset" );
827  if ( offsetExpression )
828  {
829  offset = offsetExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
830  }
831 
833  QgsExpression* placementExpression = expression( "placement" );
834  if ( placementExpression )
835  {
836  QString placementString = placementExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
837  if ( placementString.compare( "vertex", Qt::CaseInsensitive ) == 0 )
838  {
839  placement = Vertex;
840  }
841  else if ( placementString.compare( "lastvertex", Qt::CaseInsensitive ) == 0 )
842  {
843  placement = LastVertex;
844  }
845  else if ( placementString.compare( "firstvertex", Qt::CaseInsensitive ) == 0 )
846  {
847  placement = FirstVertex;
848  }
849  else if ( placementString.compare( "centerpoint", Qt::CaseInsensitive ) == 0 )
850  {
851  placement = CentralPoint;
852  }
853  else
854  {
855  placement = Interval;
856  }
857  }
858 
859  if ( offset == 0 )
860  {
861  if ( placement == Interval )
862  renderPolylineInterval( points, context );
863  else if ( placement == CentralPoint )
864  renderPolylineCentral( points, context );
865  else
866  renderPolylineVertex( points, context, placement );
867  }
868  else
869  {
870  QList<QPolygonF> mline = ::offsetLine( points, offset * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mOffsetUnit, mOffsetMapUnitScale ), context.feature() ? context.feature()->geometry()->type() : QGis::Line );
871 
872  for ( int part = 0; part < mline.count(); ++part )
873  {
874  const QPolygonF &points2 = mline[ part ];
875 
876  if ( placement == Interval )
877  renderPolylineInterval( points2, context );
878  else if ( placement == CentralPoint )
879  renderPolylineCentral( points2, context );
880  else
881  renderPolylineVertex( points2, context, placement );
882  }
883  }
884 }
885 
886 void QgsMarkerLineSymbolLayerV2::renderPolygonOutline( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
887 {
888  renderPolyline( points, context );
889  if ( rings )
890  {
891  mOffset = -mOffset; // invert the offset for rings!
892  foreach ( const QPolygonF& ring, *rings )
893  renderPolyline( ring, context );
894  mOffset = -mOffset;
895  }
896 }
897 
899 {
900  if ( points.isEmpty() )
901  return;
902 
903  QPointF lastPt = points[0];
904  double lengthLeft = 0; // how much is left until next marker
905  bool first = mOffsetAlongLine ? false : true; //only draw marker at first vertex when no offset along line is set
906  double origAngle = mMarker->angle();
907 
908  QgsRenderContext& rc = context.renderContext();
909  double interval = mInterval;
910 
911  QgsExpression* intervalExpression = expression( "interval" );
912  if ( intervalExpression )
913  {
914  interval = intervalExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
915  }
916  if ( interval <= 0 )
917  {
918  interval = 0.1;
919  }
921  QgsExpression* offsetAlongLineExpression = expression( "offset_along_line" );
922  if ( offsetAlongLineExpression )
923  {
924  offsetAlongLine = offsetAlongLineExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
925  }
926 
927  double painterUnitInterval = interval * QgsSymbolLayerV2Utils::lineWidthScaleFactor( rc, mIntervalUnit, mIntervalMapUnitScale );
928  lengthLeft = painterUnitInterval - offsetAlongLine * QgsSymbolLayerV2Utils::lineWidthScaleFactor( rc, mIntervalUnit, mIntervalMapUnitScale );
929 
930  for ( int i = 1; i < points.count(); ++i )
931  {
932  const QPointF& pt = points[i];
933 
934  if ( lastPt == pt ) // must not be equal!
935  continue;
936 
937  // for each line, find out dx and dy, and length
938  MyLine l( lastPt, pt );
939  QPointF diff = l.diffForInterval( painterUnitInterval );
940 
941  // if there's some length left from previous line
942  // use only the rest for the first point in new line segment
943  double c = 1 - lengthLeft / painterUnitInterval;
944 
945  lengthLeft += l.length();
946 
947  // rotate marker (if desired)
948  if ( mRotateMarker )
949  {
950  mMarker->setAngle( origAngle + ( l.angle() * 180 / M_PI ) );
951  }
952 
953  // draw first marker
954  if ( first )
955  {
956  mMarker->renderPoint( lastPt, context.feature(), rc, -1, context.selected() );
957  first = false;
958  }
959 
960  // while we're not at the end of line segment, draw!
961  while ( lengthLeft > painterUnitInterval )
962  {
963  // "c" is 1 for regular point or in interval (0,1] for begin of line segment
964  lastPt += c * diff;
965  lengthLeft -= painterUnitInterval;
966  mMarker->renderPoint( lastPt, context.feature(), rc, -1, context.selected() );
967  c = 1; // reset c (if wasn't 1 already)
968  }
969 
970  lastPt = pt;
971  }
972 
973  // restore original rotation
974  mMarker->setAngle( origAngle );
975 
976 }
977 
978 static double _averageAngle( const QPointF& prevPt, const QPointF& pt, const QPointF& nextPt )
979 {
980  // calc average angle between the previous and next point
981  double a1 = MyLine( prevPt, pt ).angle();
982  double a2 = MyLine( pt, nextPt ).angle();
983  double unitX = cos( a1 ) + cos( a2 ), unitY = sin( a1 ) + sin( a2 );
984 
985  return atan2( unitY, unitX );
986 }
987 
988 void QgsMarkerLineSymbolLayerV2::renderPolylineVertex( const QPolygonF& points, QgsSymbolV2RenderContext& context, Placement placement )
989 {
990  if ( points.isEmpty() )
991  return;
992 
993  QgsRenderContext& rc = context.renderContext();
994 
995  double origAngle = mMarker->angle();
996  int i, maxCount;
997  bool isRing = false;
998 
1000  QgsExpression* offsetAlongLineExpression = expression( "offset_along_line" );
1001  if ( offsetAlongLineExpression )
1002  {
1003  offsetAlongLine = offsetAlongLineExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
1004  }
1005  if ( offsetAlongLine != 0 )
1006  {
1007  //scale offset along line
1009  }
1010 
1011  if ( placement == FirstVertex )
1012  {
1013  i = 0;
1014  maxCount = 1;
1015  }
1016  else if ( placement == LastVertex )
1017  {
1018  i = points.count() - 1;
1019  maxCount = points.count();
1020  }
1021  else
1022  {
1023  i = 0;
1024  maxCount = points.count();
1025  if ( points.first() == points.last() )
1026  isRing = true;
1027  }
1028 
1029  if ( offsetAlongLine > 0 && ( placement == FirstVertex || placement == LastVertex ) )
1030  {
1031  double distance;
1032  distance = placement == FirstVertex ? offsetAlongLine : -offsetAlongLine;
1033  renderOffsetVertexAlongLine( points, i, distance, context );
1034  // restore original rotation
1035  mMarker->setAngle( origAngle );
1036  return;
1037  }
1038 
1039  for ( ; i < maxCount; ++i )
1040  {
1041  if ( isRing && placement == Vertex && i == points.count() - 1 )
1042  {
1043  continue; // don't draw the last marker - it has been drawn already
1044  }
1045  // rotate marker (if desired)
1046  if ( mRotateMarker )
1047  {
1048  double angle = markerAngle( points, isRing, i );
1049  mMarker->setAngle( origAngle + angle * 180 / M_PI );
1050  }
1051 
1052  mMarker->renderPoint( points.at( i ), context.feature(), rc, -1, context.selected() );
1053  }
1054 
1055  // restore original rotation
1056  mMarker->setAngle( origAngle );
1057 }
1058 
1059 double QgsMarkerLineSymbolLayerV2::markerAngle( const QPolygonF& points, bool isRing, int vertex )
1060 {
1061  double angle = 0;
1062  const QPointF& pt = points[vertex];
1063 
1064  if ( isRing || ( vertex > 0 && vertex < points.count() - 1 ) )
1065  {
1066  int prevIndex = vertex - 1;
1067  int nextIndex = vertex + 1;
1068 
1069  if ( isRing && ( vertex == 0 || vertex == points.count() - 1 ) )
1070  {
1071  prevIndex = points.count() - 2;
1072  nextIndex = 1;
1073  }
1074 
1075  QPointF prevPoint, nextPoint;
1076  while ( prevIndex >= 0 )
1077  {
1078  prevPoint = points[ prevIndex ];
1079  if ( prevPoint != pt )
1080  {
1081  break;
1082  }
1083  --prevIndex;
1084  }
1085 
1086  while ( nextIndex < points.count() )
1087  {
1088  nextPoint = points[ nextIndex ];
1089  if ( nextPoint != pt )
1090  {
1091  break;
1092  }
1093  ++nextIndex;
1094  }
1095 
1096  if ( prevIndex >= 0 && nextIndex < points.count() )
1097  {
1098  angle = _averageAngle( prevPoint, pt, nextPoint );
1099  }
1100  }
1101  else //no ring and vertex is at start / at end
1102  {
1103  if ( vertex == 0 )
1104  {
1105  while ( vertex < points.size() - 1 )
1106  {
1107  const QPointF& nextPt = points[vertex+1];
1108  if ( pt != nextPt )
1109  {
1110  angle = MyLine( pt, nextPt ).angle();
1111  return angle;
1112  }
1113  ++vertex;
1114  }
1115  }
1116  else
1117  {
1118  // use last segment's angle
1119  while ( vertex >= 1 ) //in case of duplicated vertices, take the next suitable one
1120  {
1121  const QPointF& prevPt = points[vertex-1];
1122  if ( pt != prevPt )
1123  {
1124  angle = MyLine( prevPt, pt ).angle();
1125  return angle;
1126  }
1127  --vertex;
1128  }
1129  }
1130  }
1131  return angle;
1132 }
1133 
1134 void QgsMarkerLineSymbolLayerV2::renderOffsetVertexAlongLine( const QPolygonF &points, int vertex, double distance, QgsSymbolV2RenderContext& context )
1135 {
1136  if ( points.isEmpty() )
1137  return;
1138 
1139  QgsRenderContext& rc = context.renderContext();
1140  double origAngle = mMarker->angle();
1141  if ( distance == 0 )
1142  {
1143  // rotate marker (if desired)
1144  if ( mRotateMarker )
1145  {
1146  bool isRing = false;
1147  if ( points.first() == points.last() )
1148  isRing = true;
1149  double angle = markerAngle( points, isRing, vertex );
1150  mMarker->setAngle( origAngle + angle * 180 / M_PI );
1151  }
1152  mMarker->renderPoint( points[vertex], context.feature(), rc, -1, context.selected() );
1153  return;
1154  }
1155 
1156  int pointIncrement = distance > 0 ? 1 : -1;
1157  QPointF previousPoint = points[vertex];
1158  int startPoint = distance > 0 ? qMin( vertex + 1, points.count() - 1 ) : qMax( vertex - 1, 0 );
1159  int endPoint = distance > 0 ? points.count() - 1 : 0;
1160  double distanceLeft = qAbs( distance );
1161 
1162  for ( int i = startPoint; pointIncrement > 0 ? i <= endPoint : i >= endPoint; i += pointIncrement )
1163  {
1164  const QPointF& pt = points[i];
1165 
1166  if ( previousPoint == pt ) // must not be equal!
1167  continue;
1168 
1169  // create line segment
1170  MyLine l( previousPoint, pt );
1171 
1172  if ( distanceLeft < l.length() )
1173  {
1174  //destination point is in current segment
1175  QPointF markerPoint = previousPoint + l.diffForInterval( distanceLeft );
1176  // rotate marker (if desired)
1177  if ( mRotateMarker )
1178  {
1179  mMarker->setAngle( origAngle + ( l.angle() * 180 / M_PI ) );
1180  }
1181  mMarker->renderPoint( markerPoint, context.feature(), rc, -1, context.selected() );
1182  return;
1183  }
1184 
1185  distanceLeft -= l.length();
1186  previousPoint = pt;
1187  }
1188 
1189  //didn't find point
1190  return;
1191 }
1192 
1194 {
1195  if ( points.size() > 0 )
1196  {
1197  // calc length
1198  qreal length = 0;
1199  QPolygonF::const_iterator it = points.constBegin();
1200  QPointF last = *it;
1201  for ( ++it; it != points.constEnd(); ++it )
1202  {
1203  length += sqrt(( last.x() - it->x() ) * ( last.x() - it->x() ) +
1204  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1205  last = *it;
1206  }
1207 
1208  // find the segment where the central point lies
1209  it = points.constBegin();
1210  last = *it;
1211  qreal last_at = 0, next_at = 0;
1212  QPointF next;
1213  int segment = 0;
1214  for ( ++it; it != points.constEnd(); ++it )
1215  {
1216  next = *it;
1217  next_at += sqrt(( last.x() - it->x() ) * ( last.x() - it->x() ) +
1218  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1219  if ( next_at >= length / 2 )
1220  break; // we have reached the center
1221  last = *it;
1222  last_at = next_at;
1223  segment++;
1224  }
1225 
1226  // find out the central point on segment
1227  MyLine l( last, next ); // for line angle
1228  qreal k = ( length * 0.5 - last_at ) / ( next_at - last_at );
1229  QPointF pt = last + ( next - last ) * k;
1230 
1231  // draw the marker
1232  double origAngle = mMarker->angle();
1233  if ( mRotateMarker )
1234  mMarker->setAngle( origAngle + l.angle() * 180 / M_PI );
1235  mMarker->renderPoint( pt, context.feature(), context.renderContext(), -1, context.selected() );
1236  if ( mRotateMarker )
1237  mMarker->setAngle( origAngle );
1238  }
1239 }
1240 
1241 
1243 {
1244  QgsStringMap map;
1245  map["rotate"] = ( mRotateMarker ? "1" : "0" );
1246  map["interval"] = QString::number( mInterval );
1247  map["offset"] = QString::number( mOffset );
1248  map["offset_along_line"] = QString::number( mOffsetAlongLine );
1249  map["offset_along_line_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetAlongLineUnit );
1250  map["offset_along_line_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetAlongLineMapUnitScale );
1251  map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit );
1252  map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
1253  map["interval_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mIntervalUnit );
1254  map["interval_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mIntervalMapUnitScale );
1255  if ( mPlacement == Vertex )
1256  map["placement"] = "vertex";
1257  else if ( mPlacement == LastVertex )
1258  map["placement"] = "lastvertex";
1259  else if ( mPlacement == FirstVertex )
1260  map["placement"] = "firstvertex";
1261  else if ( mPlacement == CentralPoint )
1262  map["placement"] = "centralpoint";
1263  else
1264  map["placement"] = "interval";
1265 
1267  return map;
1268 }
1269 
1271 {
1272  return mMarker;
1273 }
1274 
1276 {
1277  if ( symbol == NULL || symbol->type() != QgsSymbolV2::Marker )
1278  {
1279  delete symbol;
1280  return false;
1281  }
1282 
1283  delete mMarker;
1284  mMarker = static_cast<QgsMarkerSymbolV2*>( symbol );
1285  mColor = mMarker->color();
1286  return true;
1287 }
1288 
1290 {
1292  x->setSubSymbol( mMarker->clone() );
1293  x->setOffset( mOffset );
1294  x->setPlacement( mPlacement );
1295  x->setOffsetUnit( mOffsetUnit );
1303  return x;
1304 }
1305 
1306 void QgsMarkerLineSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
1307 {
1308  for ( int i = 0; i < mMarker->symbolLayerCount(); i++ )
1309  {
1310  QDomElement symbolizerElem = doc.createElement( "se:LineSymbolizer" );
1311  if ( !props.value( "uom", "" ).isEmpty() )
1312  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
1313  element.appendChild( symbolizerElem );
1314 
1315  // <Geometry>
1316  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
1317 
1318  QString gap;
1319  switch ( mPlacement )
1320  {
1321  case FirstVertex:
1322  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "firstPoint" ) );
1323  break;
1324  case LastVertex:
1325  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "lastPoint" ) );
1326  break;
1327  case CentralPoint:
1328  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "centralPoint" ) );
1329  break;
1330  case Vertex:
1331  // no way to get line/polygon's vertices, use a VendorOption
1332  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "points" ) );
1333  break;
1334  default:
1335  gap = QString::number( mInterval );
1336  break;
1337  }
1338 
1339  if ( !mRotateMarker )
1340  {
1341  // markers in LineSymbolizer must be drawn following the line orientation,
1342  // use a VendorOption when no marker rotation
1343  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "rotateMarker", "0" ) );
1344  }
1345 
1346  // <Stroke>
1347  QDomElement strokeElem = doc.createElement( "se:Stroke" );
1348  symbolizerElem.appendChild( strokeElem );
1349 
1350  // <GraphicStroke>
1351  QDomElement graphicStrokeElem = doc.createElement( "se:GraphicStroke" );
1352  strokeElem.appendChild( graphicStrokeElem );
1353 
1354  QgsSymbolLayerV2 *layer = mMarker->symbolLayer( i );
1355  QgsMarkerSymbolLayerV2 *markerLayer = static_cast<QgsMarkerSymbolLayerV2 *>( layer );
1356  if ( !markerLayer )
1357  {
1358  graphicStrokeElem.appendChild( doc.createComment( QString( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( markerLayer->layerType() ) ) );
1359  }
1360  else
1361  {
1362  markerLayer->writeSldMarker( doc, graphicStrokeElem, props );
1363  }
1364 
1365  if ( !gap.isEmpty() )
1366  {
1367  QDomElement gapElem = doc.createElement( "se:Gap" );
1368  QgsSymbolLayerV2Utils::createFunctionElement( doc, gapElem, gap );
1369  graphicStrokeElem.appendChild( gapElem );
1370  }
1371 
1372  if ( !qgsDoubleNear( mOffset, 0.0 ) )
1373  {
1374  QDomElement perpOffsetElem = doc.createElement( "se:PerpendicularOffset" );
1375  perpOffsetElem.appendChild( doc.createTextNode( QString::number( mOffset ) ) );
1376  symbolizerElem.appendChild( perpOffsetElem );
1377  }
1378  }
1379 }
1380 
1382 {
1383  QgsDebugMsg( "Entered." );
1384 
1385  QDomElement strokeElem = element.firstChildElement( "Stroke" );
1386  if ( strokeElem.isNull() )
1387  return NULL;
1388 
1389  QDomElement graphicStrokeElem = strokeElem.firstChildElement( "GraphicStroke" );
1390  if ( graphicStrokeElem.isNull() )
1391  return NULL;
1392 
1393  // retrieve vendor options
1394  bool rotateMarker = true;
1396 
1397  QgsStringMap vendorOptions = QgsSymbolLayerV2Utils::getVendorOptionList( element );
1398  for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
1399  {
1400  if ( it.key() == "placement" )
1401  {
1402  if ( it.value() == "points" ) placement = Vertex;
1403  else if ( it.value() == "firstPoint" ) placement = FirstVertex;
1404  else if ( it.value() == "lastPoint" ) placement = LastVertex;
1405  else if ( it.value() == "centralPoint" ) placement = CentralPoint;
1406  }
1407  else if ( it.value() == "rotateMarker" )
1408  {
1409  rotateMarker = it.value() == "0";
1410  }
1411  }
1412 
1413  QgsMarkerSymbolV2 *marker = 0;
1414 
1416  if ( l )
1417  {
1418  QgsSymbolLayerV2List layers;
1419  layers.append( l );
1420  marker = new QgsMarkerSymbolV2( layers );
1421  }
1422 
1423  if ( !marker )
1424  return NULL;
1425 
1426  double interval = 0.0;
1427  QDomElement gapElem = graphicStrokeElem.firstChildElement( "Gap" );
1428  if ( !gapElem.isNull() )
1429  {
1430  bool ok;
1431  double d = gapElem.firstChild().nodeValue().toDouble( &ok );
1432  if ( ok )
1433  interval = d;
1434  }
1435 
1436  double offset = 0.0;
1437  QDomElement perpOffsetElem = graphicStrokeElem.firstChildElement( "PerpendicularOffset" );
1438  if ( !perpOffsetElem.isNull() )
1439  {
1440  bool ok;
1441  double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
1442  if ( ok )
1443  offset = d;
1444  }
1445 
1446  QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( rotateMarker );
1447  x->setPlacement( placement );
1448  x->setInterval( interval );
1449  x->setSubSymbol( marker );
1450  x->setOffset( offset );
1451  return x;
1452 }
1453 
1455 {
1456  mMarker->setSize( width );
1457 }
1458 
1460 {
1461  return mMarker->size();
1462 }
1463 
1465 {
1467  mIntervalUnit = unit;
1468  mOffsetUnit = unit;
1469  mOffsetAlongLineUnit = unit;
1470 }
1471 
1473 {
1475  if ( mIntervalUnit != unit || mOffsetUnit != unit || mOffsetAlongLineUnit != unit )
1476  {
1477  return QgsSymbolV2::Mixed;
1478  }
1479  return unit;
1480 }
1481 
1483 {
1485  mIntervalMapUnitScale = scale;
1486  mOffsetMapUnitScale = scale;
1488 }
1489 
1491 {
1495  {
1496  return mOffsetMapUnitScale;
1497  }
1498  return QgsMapUnitScale();
1499 }
1500 
1502 {
1503  return ( mMarker->size() / 2.0 ) + mOffset;
1504 }
1505 
1506 
1507 
static double _averageAngle(const QPointF &prevPt, const QPointF &pt, const QPointF &nextPt)
static double mapUnitScaleFactor(double scaleDenominator, QgsSymbolV2::OutputUnit symbolUnits, QGis::UnitType mapUnits)
void setIntervalUnit(QgsSymbolV2::OutputUnit unit)
static QString encodeOutputUnit(QgsSymbolV2::OutputUnit unit)
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:87
#define DEFAULT_SIMPLELINE_PENSTYLE
#define DEFAULT_MARKERLINE_ROTATE
double estimateMaxBleed() const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape...
QMap< QString, QgsExpression * > mDataDefinedProperties
float threshold() const
Gets the simplification threshold of the vector layer managed.
int renderHints() const
Definition: qgssymbolv2.h:208
QVector< qreal > dxfCustomDashPattern(QgsSymbolV2::OutputUnit &unit) const override
virtual double width() const
double markerAngle(const QPolygonF &points, bool isRing, int vertex)
void renderPolyline(const QPolygonF &points, QgsSymbolV2RenderContext &context) override
#define DEFAULT_MARKERLINE_INTERVAL
QgsStringMap properties() const override
SymbolType type() const
Definition: qgssymbolv2.h:79
QVariant evaluate(const QgsFeature *f=NULL)
Evaluate the feature and return the result.
QColor selectionColor() const
const QgsVectorSimplifyMethod & vectorSimplifyMethod() const
Added in QGIS v2.4.
virtual void setWidth(double width) override
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
void setCustomDashPatternUnit(QgsSymbolV2::OutputUnit unit)
QgsMapUnitScale mCustomDashPatternMapUnitScale
double rendererScale() const
void renderPolyline(const QPolygonF &points, QgsSymbolV2RenderContext &context) override
static QgsStringMap getVendorOptionList(QDomElement &element)
QgsGeometry * geometry() const
Get the geometry object associated with this feature.
Definition: qgsfeature.cpp:112
QGis::UnitType mapUnits() const
Definition: qgsdxfexport.h:54
void startRender(QgsSymbolV2RenderContext &context) override
static QVector< qreal > decodeRealVector(const QString &s)
void renderPolylineInterval(const QPolygonF &points, QgsSymbolV2RenderContext &context)
QgsSymbolV2::OutputUnit outputUnit() const override
QVector< qreal > customDashVector() const
bool setSubSymbol(QgsSymbolV2 *symbol) override
QGis::GeometryType type() const
Returns type of the vector.
void setPenJoinStyle(Qt::PenJoinStyle style)
void setOffsetUnit(QgsSymbolV2::OutputUnit unit)
static QDomElement createVendorOptionElement(QDomDocument &doc, QString name, QString value)
static QColor decodeColor(QString str)
static const bool selectionIsOpaque
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
QgsMapUnitScale mWidthMapUnitScale
void setIntervalMapUnitScale(const QgsMapUnitScale &scale)
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
QString layerType() const override
void setMapUnitScale(const QgsMapUnitScale &scale) override
QMap< QString, QString > QgsStringMap
Definition: qgis.h:416
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:325
Qt::PenStyle penStyle() const
void setWidthUnit(QgsSymbolV2::OutputUnit unit)
QgsMapUnitScale mOffsetAlongLineMapUnitScale
double dxfWidth(const QgsDxfExport &e, const QgsSymbolV2RenderContext &context) const override
void setOffsetAlongLine(double offsetAlongLine)
Sets the the offset along the line for the marker placement.
static QString encodeColor(QColor color)
void setInterval(double interval)
virtual QgsExpression * expression(const QString &property) const
QgsSymbolV2::OutputUnit mOffsetUnit
QgsMapUnitScale mapUnitScale() const override
qreal alpha() const
Get alpha transparency 1 for opaque, 0 for invisible.
Definition: qgssymbolv2.h:201
void setDrawInsidePolygon(bool drawInsidePolygon)
static QString encodePenStyle(Qt::PenStyle style)
QPointF diffForInterval(double interval)
void setColor(const QColor &color)
QgsSymbolV2::OutputUnit mIntervalUnit
virtual void writeSldMarker(QDomDocument &doc, QDomElement &element, QgsStringMap props) const
QgsSymbolLayerV2 * clone() const override
QgsSymbolLayerV2 * clone() const override
static QgsSymbolLayerV2 * createMarkerLayerFromSld(QDomElement &element)
double offsetAlongLine() const
Returns the offset along the line for the marker placement.
static QString ogrFeatureStylePen(double width, double mmScaleFactor, double mapUnitsScaleFactor, const QColor &c, Qt::PenJoinStyle joinStyle=Qt::MiterJoin, Qt::PenCapStyle capStyle=Qt::FlatCap, double offset=0.0, const QVector< qreal > *dashPattern=0)
Create ogr feature style string for pen.
void setOutputUnit(QgsSymbolV2::OutputUnit unit) override
void setMapUnitScale(const QgsMapUnitScale &scale) override
const QgsFeature * feature() const
Current feature being rendered - may be null.
Definition: qgssymbolv2.h:213
void renderPolygonOutline(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolV2RenderContext &context) override
QgsSymbolV2::OutputUnit outputUnit() const override
void startRender(QgsRenderContext &context, const QgsFields *fields=0)
The geometries can be rendered with 'AntiAliasing' disabled because of it is '1-pixel size'...
static Qt::PenCapStyle decodePenCapStyle(QString str)
static QgsSymbolLayerV2 * create(const QgsStringMap &properties=QgsStringMap())
static Qt::PenStyle decodePenStyle(QString str)
QgsSymbolV2::OutputUnit mWidthUnit
void setWidthMapUnitScale(const QgsMapUnitScale &scale)
static bool createFunctionElement(QDomDocument &doc, QDomElement &element, QString function)
QVector< qreal > mCustomDashVector
Vector with an even number of entries for the.
#define DEFAULT_SIMPLELINE_WIDTH
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
void renderPolygonOutline(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolV2RenderContext &context) override
void toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props) const override
#define M_PI
static QgsSymbolLayerV2 * createFromSld(QDomElement &element)
void setAngle(double angle)
#define DEFAULT_SIMPLELINE_CAPSTYLE
virtual QgsSymbolV2 * clone() const override
#define DEFAULT_SIMPLELINE_JOINSTYLE
int symbolLayerCount()
Returns total number of symbol layers contained in the symbol.
Definition: qgssymbolv2.h:106
QString layerType() const override
void setSize(double size)
double rasterScaleFactor() const
void stopRender(QgsSymbolV2RenderContext &context) override
virtual QColor color() const
void setOffsetAlongLineMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale used for calculating the offset in map units along line for markers...
void setOffsetAlongLineUnit(QgsSymbolV2::OutputUnit unit)
Sets the unit used for calculating the offset along line for markers.
void renderPoint(const QPointF &point, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
virtual void prepareExpressions(const QgsFields *fields, double scale=-1.0)
double estimateMaxBleed() const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape...
QgsSymbolV2 * subSymbol() override
void setPenCapStyle(Qt::PenCapStyle style)
void setCustomDashVector(const QVector< qreal > &vector)
static QgsSymbolLayerV2 * create(const QgsStringMap &properties=QgsStringMap())
QString ogrFeatureStyle(double mmScaleFactor, double mapUnitScaleFactor) const override
double dxfOffset(const QgsDxfExport &e, const QgsSymbolV2RenderContext &context) const override
double ANALYSIS_EXPORT angle(Point3D *p1, Point3D *p2, Point3D *p3, Point3D *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
static QString encodeRealVector(const QVector< qreal > &v)
QgsSymbolV2::OutputUnit outputUnit() const override
QgsSymbolV2::OutputUnit mCustomDashPatternUnit
virtual QString layerType() const =0
QColor dxfColor(const QgsSymbolV2RenderContext &context) const override
double symbologyScaleDenominator() const
Definition: qgsdxfexport.h:51
void toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props) const override
static QgsSymbolLayerV2 * createFromSld(QDomElement &element)
static void lineToSld(QDomDocument &doc, QDomElement &element, Qt::PenStyle penStyle, QColor color, double width=-1, const Qt::PenJoinStyle *penJoinStyle=0, const Qt::PenCapStyle *penCapStyle=0, const QVector< qreal > *customDashPattern=0, double dashOffset=0.0)
#define DEFAULT_SIMPLELINE_COLOR
static void createGeometryElement(QDomDocument &doc, QDomElement &element, QString geomFunc)
Contains information about the context of a rendering operation.
QgsMapUnitScale mapUnitScale() const override
QgsSymbolV2::OutputUnit mOffsetAlongLineUnit
QPainter * painter()
void stopRender(QgsRenderContext &context)
QgsSimpleLineSymbolLayerV2(QColor color=DEFAULT_SIMPLELINE_COLOR, double width=DEFAULT_SIMPLELINE_WIDTH, Qt::PenStyle penStyle=DEFAULT_SIMPLELINE_PENSTYLE)
void setMapUnitScale(const QgsMapUnitScale &scale) override
static double lineWidthScaleFactor(const QgsRenderContext &c, QgsSymbolV2::OutputUnit u, const QgsMapUnitScale &scale=QgsMapUnitScale())
Returns the line width scale factor depending on the unit and the paint device.
Qt::PenStyle dxfPenStyle() const override
QList< QPolygonF > offsetLine(QPolygonF polyline, double dist, QGis::GeometryType geometryType)
calculate geometry shifted by a specified distance
QList< QgsSymbolLayerV2 * > QgsSymbolLayerV2List
Definition: qgssymbolv2.h:39
void setCustomDashPatternMapUnitScale(const QgsMapUnitScale &scale)
void stopRender(QgsSymbolV2RenderContext &context) override
QgsRenderContext & renderContext()
Definition: qgssymbolv2.h:191
void setOutputUnit(QgsSymbolV2::OutputUnit unit) override
QgsMapUnitScale mapUnitScale() const override
void setOutputUnit(QgsSymbolV2::OutputUnit unit) override
SimplifyHints simplifyHints() const
Gets the simplification hints of the vector layer managed.
void setColor(const QColor &color) override
const QgsFields * fields() const
Fields of the layer.
Definition: qgssymbolv2.h:219
void setRenderHints(int hints)
Definition: qgssymbolv2.h:155
static Qt::PenJoinStyle decodePenJoinStyle(QString str)
MyLine(QPointF p1, QPointF p2)
bool selected() const
Definition: qgssymbolv2.h:205
QgsSymbolV2::OutputUnit mOffsetUnit
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
QgsSymbolLayerV2 * symbolLayer(int layer)
Returns a specific symbol layers contained in the symbol.
static bool lineFromSld(QDomElement &element, Qt::PenStyle &penStyle, QColor &color, double &width, Qt::PenJoinStyle *penJoinStyle=0, Qt::PenCapStyle *penCapStyle=0, QVector< qreal > *customDashPattern=0, double *dashOffset=0)
void startRender(QgsSymbolV2RenderContext &context) override
QgsMarkerLineSymbolLayerV2(bool rotateMarker=DEFAULT_MARKERLINE_ROTATE, double interval=DEFAULT_MARKERLINE_INTERVAL)
static bool isGeneralizableByDeviceBoundingBox(const QgsRectangle &envelope, float mapToPixelTol=1.0f)
Returns whether the device-envelope can be replaced by its BBOX when is applied the specified toleran...
void saveDataDefinedProperties(QgsStringMap &stringMap) const
Saves data defined properties to string map.
void setOffsetUnit(QgsSymbolV2::OutputUnit unit)
void setAlpha(qreal alpha)
Set alpha transparency 1 for opaque, 0 for invisible.
Definition: qgssymbolv2.h:153
void renderPolylineCentral(const QPolygonF &points, QgsSymbolV2RenderContext &context)
virtual double width() const override
QgsSymbolV2::OutputUnit widthUnit() const
static QgsSymbolV2::OutputUnit decodeOutputUnit(QString str)
Qt::PenJoinStyle penJoinStyle() const
void renderPolylineVertex(const QPolygonF &points, QgsSymbolV2RenderContext &context, Placement placement=Vertex)
void copyDataDefinedProperties(QgsSymbolLayerV2 *destLayer) const
Copies data defined properties of this layer to another symbol layer.
Qt::PenCapStyle penCapStyle() const
QColor color() const
static QString encodePenCapStyle(Qt::PenCapStyle style)
QgsStringMap properties() const override
virtual void setDataDefinedProperty(const QString &property, const QString &expressionString)