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 
191  return l;
192 }
193 
194 
196 {
197  return "SimpleLine";
198 }
199 
201 {
202  QColor penColor = mColor;
203  penColor.setAlphaF( mColor.alphaF() * context.alpha() );
204  mPen.setColor( penColor );
206  mPen.setWidthF( scaledWidth );
207  if ( mUseCustomDashPattern && scaledWidth != 0 )
208  {
209  mPen.setStyle( Qt::CustomDashLine );
210 
211  //scale pattern vector
212  double dashWidthDiv = scaledWidth;
213  //fix dash pattern width in Qt 4.8
214  QStringList versionSplit = QString( qVersion() ).split( "." );
215  if ( versionSplit.size() > 1
216  && versionSplit.at( 1 ).toInt() >= 8
217  && ( scaledWidth * context.renderContext().rasterScaleFactor() ) < 1.0 )
218  {
219  dashWidthDiv = 1.0;
220  }
221  QVector<qreal> scaledVector;
222  QVector<qreal>::const_iterator it = mCustomDashVector.constBegin();
223  for ( ; it != mCustomDashVector.constEnd(); ++it )
224  {
225  //the dash is specified in terms of pen widths, therefore the division
227  }
228  mPen.setDashPattern( scaledVector );
229  }
230  else
231  {
232  mPen.setStyle( mPenStyle );
233  }
234  mPen.setJoinStyle( mPenJoinStyle );
235  mPen.setCapStyle( mPenCapStyle );
236 
237  mSelPen = mPen;
238  QColor selColor = context.renderContext().selectionColor();
239  if ( ! selectionIsOpaque )
240  selColor.setAlphaF( context.alpha() );
241  mSelPen.setColor( selColor );
242 
243  //prepare expressions for data defined properties
244  prepareExpressions( context.fields(), context.renderContext().rendererScale() );
245 }
246 
248 {
249  Q_UNUSED( context );
250 }
251 
252 void QgsSimpleLineSymbolLayerV2::renderPolygonOutline( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
253 {
254  QPainter* p = context.renderContext().painter();
255  if ( !p )
256  {
257  return;
258  }
259 
260  if ( mDrawInsidePolygon )
261  {
262  //only drawing the line on the interior of the polygon, so set clip path for painter
263  p->save();
264  QPainterPath clipPath;
265  clipPath.addPolygon( points );
266 
267  if ( rings != NULL )
268  {
269  //add polygon rings
270  QList<QPolygonF>::const_iterator it = rings->constBegin();
271  for ( ; it != rings->constEnd(); ++it )
272  {
273  QPolygonF ring = *it;
274  clipPath.addPolygon( ring );
275  }
276  }
277 
278  //use intersect mode, as a clip path may already exist (eg, for composer maps)
279  p->setClipPath( clipPath, Qt::IntersectClip );
280  }
281 
282  renderPolyline( points, context );
283  if ( rings )
284  {
285  mOffset = -mOffset; // invert the offset for rings!
286  foreach ( const QPolygonF& ring, *rings )
287  renderPolyline( ring, context );
288  mOffset = -mOffset;
289  }
290 
291  if ( mDrawInsidePolygon )
292  {
293  //restore painter to reset clip path
294  p->restore();
295  }
296 
297 }
298 
300 {
301  QPainter* p = context.renderContext().painter();
302  if ( !p )
303  {
304  return;
305  }
306 
307  //size scaling by field
309  {
310  applySizeScale( context, mPen, mSelPen );
311  }
312 
313  double offset = mOffset;
314  applyDataDefinedSymbology( context, mPen, mSelPen, offset );
315 
316  p->setPen( context.selected() ? mSelPen : mPen );
317 
318  // Disable 'Antialiasing' if the geometry was generalized in the current RenderContext (We known that it must have least #2 points).
319  if ( points.size() <= 2 &&
322  ( p->renderHints() & QPainter::Antialiasing ) )
323  {
324  p->setRenderHint( QPainter::Antialiasing, false );
325  p->drawPolyline( points );
326  p->setRenderHint( QPainter::Antialiasing, true );
327  return;
328  }
329 
330  if ( qgsDoubleNear( offset, 0 ) )
331  {
332  p->drawPolyline( points );
333  }
334  else
335  {
337  QList<QPolygonF> mline = ::offsetLine( points, scaledOffset, context.feature() ? context.feature()->geometry()->type() : QGis::Line );
338  for ( int part = 0; part < mline.count(); ++part )
339  p->drawPolyline( mline[ part ] );
340  }
341 }
342 
344 {
345  QgsStringMap map;
346  map["line_color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
347  map["line_width"] = QString::number( mWidth );
348  map["line_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mWidthUnit );
349  map["width_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mWidthMapUnitScale );
350  map["line_style"] = QgsSymbolLayerV2Utils::encodePenStyle( mPenStyle );
353  map["offset"] = QString::number( mOffset );
355  map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
356  map["use_custom_dash"] = ( mUseCustomDashPattern ? "1" : "0" );
360  map["draw_inside_polygon"] = ( mDrawInsidePolygon ? "1" : "0" );
362  return map;
363 }
364 
366 {
368  l->setWidthUnit( mWidthUnit );
374  l->setOffset( mOffset );
381  return l;
382 }
383 
384 void QgsSimpleLineSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
385 {
386  if ( mPenStyle == Qt::NoPen )
387  return;
388 
389  QDomElement symbolizerElem = doc.createElement( "se:LineSymbolizer" );
390  if ( !props.value( "uom", "" ).isEmpty() )
391  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
392  element.appendChild( symbolizerElem );
393 
394  // <Geometry>
395  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
396 
397  // <Stroke>
398  QDomElement strokeElem = doc.createElement( "se:Stroke" );
399  symbolizerElem.appendChild( strokeElem );
400 
401  Qt::PenStyle penStyle = mUseCustomDashPattern ? Qt::CustomDashLine : mPenStyle;
402  QgsSymbolLayerV2Utils::lineToSld( doc, strokeElem, penStyle, mColor, mWidth,
404 
405  // <se:PerpendicularOffset>
406  if ( mOffset != 0 )
407  {
408  QDomElement perpOffsetElem = doc.createElement( "se:PerpendicularOffset" );
409  perpOffsetElem.appendChild( doc.createTextNode( QString::number( mOffset ) ) );
410  symbolizerElem.appendChild( perpOffsetElem );
411  }
412 }
413 
414 QString QgsSimpleLineSymbolLayerV2::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
415 {
416  if ( mUseCustomDashPattern )
417  {
418  return QgsSymbolLayerV2Utils::ogrFeatureStylePen( mWidth, mmScaleFactor, mapUnitScaleFactor,
419  mPen.color(), mPenJoinStyle,
421  }
422  else
423  {
424  return QgsSymbolLayerV2Utils::ogrFeatureStylePen( mWidth, mmScaleFactor, mapUnitScaleFactor, mPen.color(), mPenJoinStyle,
426  }
427 }
428 
430 {
431  QgsDebugMsg( "Entered." );
432 
433  QDomElement strokeElem = element.firstChildElement( "Stroke" );
434  if ( strokeElem.isNull() )
435  return NULL;
436 
437  Qt::PenStyle penStyle;
438  QColor color;
439  double width;
440  Qt::PenJoinStyle penJoinStyle;
441  Qt::PenCapStyle penCapStyle;
442  QVector<qreal> customDashVector;
443 
444  if ( !QgsSymbolLayerV2Utils::lineFromSld( strokeElem, penStyle,
445  color, width,
446  &penJoinStyle, &penCapStyle,
447  &customDashVector ) )
448  return NULL;
449 
450  double offset = 0.0;
451  QDomElement perpOffsetElem = element.firstChildElement( "PerpendicularOffset" );
452  if ( !perpOffsetElem.isNull() )
453  {
454  bool ok;
455  double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
456  if ( ok )
457  offset = d;
458  }
459 
460  QgsSimpleLineSymbolLayerV2* l = new QgsSimpleLineSymbolLayerV2( color, width, penStyle );
461  l->setOffset( offset );
462  l->setPenJoinStyle( penJoinStyle );
463  l->setPenCapStyle( penCapStyle );
464  l->setUseCustomDashPattern( penStyle == Qt::CustomDashLine );
465  l->setCustomDashVector( customDashVector );
466  return l;
467 }
468 
469 void QgsSimpleLineSymbolLayerV2::applySizeScale( QgsSymbolV2RenderContext& context, QPen& pen, QPen& selPen )
470 {
472  pen.setWidthF( scaledWidth );
473  selPen.setWidthF( scaledWidth );
474 }
475 
476 void QgsSimpleLineSymbolLayerV2::applyDataDefinedSymbology( QgsSymbolV2RenderContext& context, QPen& pen, QPen& selPen, double& offset )
477 {
478  if ( mDataDefinedProperties.isEmpty() )
479  return; // shortcut
480 
481  //data defined properties
482  QgsExpression* strokeWidthExpression = expression( "width" );
483  if ( strokeWidthExpression )
484  {
485  double scaledWidth = strokeWidthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble()
487  pen.setWidthF( scaledWidth );
488  selPen.setWidthF( scaledWidth );
489  }
490 
491  //color
492  QgsExpression* strokeColorExpression = expression( "color" );
493  if ( strokeColorExpression )
494  {
495  pen.setColor( QgsSymbolLayerV2Utils::decodeColor( strokeColorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) );
496  }
497 
498  //offset
499  QgsExpression* lineOffsetExpression = expression( "offset" );
500  if ( lineOffsetExpression )
501  {
502  offset = lineOffsetExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
503  }
504 
505  //dash dot vector
506  QgsExpression* dashPatternExpression = expression( "customdash" );
507  if ( dashPatternExpression )
508  {
510  double dashWidthDiv = mPen.widthF();
511 
512  if ( strokeWidthExpression )
513  {
514  dashWidthDiv = pen.widthF();
515  scaledWidth = pen.widthF();
516  }
517 
518  //fix dash pattern width in Qt 4.8
519  QStringList versionSplit = QString( qVersion() ).split( "." );
520  if ( versionSplit.size() > 1
521  && versionSplit.at( 1 ).toInt() >= 8
522  && ( scaledWidth * context.renderContext().rasterScaleFactor() ) < 1.0 )
523  {
524  dashWidthDiv = 1.0;
525  }
526 
527  QVector<qreal> dashVector;
528  QStringList dashList = dashPatternExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString().split( ";" );
529  QStringList::const_iterator dashIt = dashList.constBegin();
530  for ( ; dashIt != dashList.constEnd(); ++dashIt )
531  {
532  dashVector.push_back( dashIt->toDouble() * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mCustomDashPatternUnit, mCustomDashPatternMapUnitScale ) / dashWidthDiv );
533  }
534  pen.setDashPattern( dashVector );
535  }
536 
537  //join style
538  QgsExpression* joinStyleExpression = expression( "joinstyle" );
539  if ( joinStyleExpression )
540  {
541  QString joinStyleString = joinStyleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
542  pen.setJoinStyle( QgsSymbolLayerV2Utils::decodePenJoinStyle( joinStyleString ) );
543  }
544 
545  //cap style
546  QgsExpression* capStyleExpression = expression( "capstyle" );
547  if ( capStyleExpression )
548  {
549  QString capStyleString = capStyleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
550  pen.setCapStyle( QgsSymbolLayerV2Utils::decodePenCapStyle( capStyleString ) );
551  }
552 }
553 
555 {
556  if ( mDrawInsidePolygon )
557  {
558  //set to clip line to the interior of polygon, so we expect no bleed
559  return 0;
560  }
561  else
562  {
563  return ( mWidth / 2.0 ) + mOffset;
564  }
565 }
566 
568 {
569  unit = mCustomDashPatternUnit;
570  return mUseCustomDashPattern ? mCustomDashVector : QVector<qreal>();
571 }
572 
574 {
575  return mPenStyle;
576 }
577 
579 {
580  double width = mWidth;
581  QgsExpression* strokeWidthExpression = expression( "width" );
582  if ( strokeWidthExpression )
583  {
584  width = strokeWidthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble() * e.mapUnitScaleFactor( e.symbologyScaleDenominator(), widthUnit(), e.mapUnits() );
585  }
586  else if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale )
587  {
589  }
590 
591  return width * e.mapUnitScaleFactor( e.symbologyScaleDenominator(), widthUnit(), e.mapUnits() );
592 }
593 
595 {
596  QgsExpression* strokeColorExpression = expression( "color" );
597  if ( strokeColorExpression )
598  {
599  return ( QgsSymbolLayerV2Utils::decodeColor( strokeColorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) );
600  }
601  return mColor;
602 }
603 
605 
606 
607 class MyLine
608 {
609  public:
610  MyLine( QPointF p1, QPointF p2 ) : mVertical( false ), mIncreasing( false ), mT( 0.0 ), mLength( 0.0 )
611  {
612  if ( p1 == p2 )
613  return; // invalid
614 
615  // tangent and direction
616  if ( p1.x() == p2.x() )
617  {
618  // vertical line - tangent undefined
619  mVertical = true;
620  mIncreasing = ( p2.y() > p1.y() );
621  }
622  else
623  {
624  mVertical = false;
625  mT = float( p2.y() - p1.y() ) / ( p2.x() - p1.x() );
626  mIncreasing = ( p2.x() > p1.x() );
627  }
628 
629  // length
630  double x = ( p2.x() - p1.x() );
631  double y = ( p2.y() - p1.y() );
632  mLength = sqrt( x * x + y * y );
633  }
634 
635  // return angle in radians
636  double angle()
637  {
638  double a = ( mVertical ? M_PI / 2 : atan( mT ) );
639 
640  if ( !mIncreasing )
641  a += M_PI;
642  return a;
643  }
644 
645  // return difference for x,y when going along the line with specified interval
646  QPointF diffForInterval( double interval )
647  {
648  if ( mVertical )
649  return ( mIncreasing ? QPointF( 0, interval ) : QPointF( 0, -interval ) );
650 
651  double alpha = atan( mT );
652  double dx = cos( alpha ) * interval;
653  double dy = sin( alpha ) * interval;
654  return ( mIncreasing ? QPointF( dx, dy ) : QPointF( -dx, -dy ) );
655  }
656 
657  double length() { return mLength; }
658 
659  protected:
660  bool mVertical;
662  double mT;
663  double mLength;
664 };
665 
666 
667 QgsMarkerLineSymbolLayerV2::QgsMarkerLineSymbolLayerV2( bool rotateMarker, double interval )
668 {
672  mMarker = NULL;
673  mOffset = 0;
676  mOffsetAlongLine = 0;
678 
680 }
681 
683 {
684  delete mMarker;
685 }
686 
688 {
689  bool rotate = DEFAULT_MARKERLINE_ROTATE;
691 
692 
693  if ( props.contains( "interval" ) )
694  interval = props["interval"].toDouble();
695  if ( props.contains( "rotate" ) )
696  rotate = ( props["rotate"] == "1" );
697 
698  QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( rotate, interval );
699  if ( props.contains( "offset" ) )
700  {
701  x->setOffset( props["offset"].toDouble() );
702  }
703  if ( props.contains( "offset_unit" ) )
704  {
705  x->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
706  }
707  if ( props.contains( "interval_unit" ) )
708  {
709  x->setIntervalUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["interval_unit"] ) );
710  }
711  if ( props.contains( "offset_along_line" ) )
712  {
713  x->setOffsetAlongLine( props["offset_along_line"].toDouble() );
714  }
715  if ( props.contains( "offset_along_line_unit" ) )
716  {
717  x->setOffsetAlongLineUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_along_line_unit"] ) );
718  }
719  if ( props.contains(( "offset_along_line_map_unit_scale" ) ) )
720  {
721  x->setOffsetAlongLineMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_along_line_map_unit_scale"] ) );
722  }
723 
724  if ( props.contains( "offset_map_unit_scale" ) )
725  {
726  x->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_map_unit_scale"] ) );
727  }
728  if ( props.contains( "interval_map_unit_scale" ) )
729  {
730  x->setIntervalMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["interval_map_unit_scale"] ) );
731  }
732 
733  if ( props.contains( "placement" ) )
734  {
735  if ( props["placement"] == "vertex" )
736  x->setPlacement( Vertex );
737  else if ( props["placement"] == "lastvertex" )
738  x->setPlacement( LastVertex );
739  else if ( props["placement"] == "firstvertex" )
741  else if ( props["placement"] == "centralpoint" )
743  else
744  x->setPlacement( Interval );
745  }
746 
747  //data defined properties
748  if ( props.contains( "interval_expression" ) )
749  {
750  x->setDataDefinedProperty( "interval", props["interval_expression"] );
751  }
752  if ( props.contains( "offset_expression" ) )
753  {
754  x->setDataDefinedProperty( "offset", props["offset_expression"] );
755  }
756  if ( props.contains( "placement_expression" ) )
757  {
758  x->setDataDefinedProperty( "placement", props["placement_expression"] );
759  }
760  if ( props.contains( "offset_along_line_expression" ) )
761  {
762  x->setDataDefinedProperty( "offset_along_line", props["offset_along_line_expression"] );
763  }
764 
765  return x;
766 }
767 
769 {
770  return "MarkerLine";
771 }
772 
773 void QgsMarkerLineSymbolLayerV2::setColor( const QColor& color )
774 {
775  mMarker->setColor( color );
776  mColor = color;
777 }
778 
780 {
781  mMarker->setAlpha( context.alpha() );
782 
783  // if being rotated, it gets initialized with every line segment
784  int hints = 0;
785  if ( mRotateMarker )
789  mMarker->setRenderHints( hints );
790 
791  mMarker->startRender( context.renderContext(), context.fields() );
792 
793  //prepare expressions for data defined properties
794  prepareExpressions( context.fields(), context.renderContext().rendererScale() );
795 }
796 
798 {
799  mMarker->stopRender( context.renderContext() );
800 }
801 
803 {
804  double offset = mOffset;
805  QgsExpression* offsetExpression = expression( "offset" );
806  if ( offsetExpression )
807  {
808  offset = offsetExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
809  }
810 
812  QgsExpression* placementExpression = expression( "placement" );
813  if ( placementExpression )
814  {
815  QString placementString = placementExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
816  if ( placementString.compare( "vertex", Qt::CaseInsensitive ) == 0 )
817  {
818  placement = Vertex;
819  }
820  else if ( placementString.compare( "lastvertex", Qt::CaseInsensitive ) == 0 )
821  {
822  placement = LastVertex;
823  }
824  else if ( placementString.compare( "firstvertex", Qt::CaseInsensitive ) == 0 )
825  {
826  placement = FirstVertex;
827  }
828  else if ( placementString.compare( "centerpoint", Qt::CaseInsensitive ) == 0 )
829  {
830  placement = CentralPoint;
831  }
832  else
833  {
834  placement = Interval;
835  }
836  }
837 
838  if ( offset == 0 )
839  {
840  if ( placement == Interval )
841  renderPolylineInterval( points, context );
842  else if ( placement == CentralPoint )
843  renderPolylineCentral( points, context );
844  else
845  renderPolylineVertex( points, context, placement );
846  }
847  else
848  {
849  QList<QPolygonF> mline = ::offsetLine( points, offset * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mOffsetUnit, mOffsetMapUnitScale ), context.feature() ? context.feature()->geometry()->type() : QGis::Line );
850 
851  for ( int part = 0; part < mline.count(); ++part )
852  {
853  const QPolygonF &points2 = mline[ part ];
854 
855  if ( placement == Interval )
856  renderPolylineInterval( points2, context );
857  else if ( placement == CentralPoint )
858  renderPolylineCentral( points2, context );
859  else
860  renderPolylineVertex( points2, context, placement );
861  }
862  }
863 }
864 
865 void QgsMarkerLineSymbolLayerV2::renderPolygonOutline( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
866 {
867  renderPolyline( points, context );
868  if ( rings )
869  {
870  mOffset = -mOffset; // invert the offset for rings!
871  foreach ( const QPolygonF& ring, *rings )
872  renderPolyline( ring, context );
873  mOffset = -mOffset;
874  }
875 }
876 
878 {
879  if ( points.isEmpty() )
880  return;
881 
882  QPointF lastPt = points[0];
883  double lengthLeft = 0; // how much is left until next marker
884  bool first = mOffsetAlongLine ? false : true; //only draw marker at first vertex when no offset along line is set
885  double origAngle = mMarker->angle();
886 
887  QgsRenderContext& rc = context.renderContext();
888  double interval = mInterval;
889 
890  QgsExpression* intervalExpression = expression( "interval" );
891  if ( intervalExpression )
892  {
893  interval = intervalExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
894  }
895  if ( interval <= 0 )
896  {
897  interval = 0.1;
898  }
900  QgsExpression* offsetAlongLineExpression = expression( "offset_along_line" );
901  if ( offsetAlongLineExpression )
902  {
903  offsetAlongLine = offsetAlongLineExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
904  }
905 
906  double painterUnitInterval = interval * QgsSymbolLayerV2Utils::lineWidthScaleFactor( rc, mIntervalUnit, mIntervalMapUnitScale );
907  lengthLeft = painterUnitInterval - offsetAlongLine * QgsSymbolLayerV2Utils::lineWidthScaleFactor( rc, mIntervalUnit, mIntervalMapUnitScale );
908 
909  for ( int i = 1; i < points.count(); ++i )
910  {
911  const QPointF& pt = points[i];
912 
913  if ( lastPt == pt ) // must not be equal!
914  continue;
915 
916  // for each line, find out dx and dy, and length
917  MyLine l( lastPt, pt );
918  QPointF diff = l.diffForInterval( painterUnitInterval );
919 
920  // if there's some length left from previous line
921  // use only the rest for the first point in new line segment
922  double c = 1 - lengthLeft / painterUnitInterval;
923 
924  lengthLeft += l.length();
925 
926  // rotate marker (if desired)
927  if ( mRotateMarker )
928  {
929  mMarker->setAngle( origAngle + ( l.angle() * 180 / M_PI ) );
930  }
931 
932  // draw first marker
933  if ( first )
934  {
935  mMarker->renderPoint( lastPt, context.feature(), rc, -1, context.selected() );
936  first = false;
937  }
938 
939  // while we're not at the end of line segment, draw!
940  while ( lengthLeft > painterUnitInterval )
941  {
942  // "c" is 1 for regular point or in interval (0,1] for begin of line segment
943  lastPt += c * diff;
944  lengthLeft -= painterUnitInterval;
945  mMarker->renderPoint( lastPt, context.feature(), rc, -1, context.selected() );
946  c = 1; // reset c (if wasn't 1 already)
947  }
948 
949  lastPt = pt;
950  }
951 
952  // restore original rotation
953  mMarker->setAngle( origAngle );
954 
955 }
956 
957 static double _averageAngle( const QPointF& prevPt, const QPointF& pt, const QPointF& nextPt )
958 {
959  // calc average angle between the previous and next point
960  double a1 = MyLine( prevPt, pt ).angle();
961  double a2 = MyLine( pt, nextPt ).angle();
962  double unitX = cos( a1 ) + cos( a2 ), unitY = sin( a1 ) + sin( a2 );
963 
964  return atan2( unitY, unitX );
965 }
966 
967 void QgsMarkerLineSymbolLayerV2::renderPolylineVertex( const QPolygonF& points, QgsSymbolV2RenderContext& context, Placement placement )
968 {
969  if ( points.isEmpty() )
970  return;
971 
972  QgsRenderContext& rc = context.renderContext();
973 
974  double origAngle = mMarker->angle();
975  int i, maxCount;
976  bool isRing = false;
977 
979  QgsExpression* offsetAlongLineExpression = expression( "offset_along_line" );
980  if ( offsetAlongLineExpression )
981  {
982  offsetAlongLine = offsetAlongLineExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
983  }
984  if ( offsetAlongLine != 0 )
985  {
986  //scale offset along line
988  }
989 
990  if ( placement == FirstVertex )
991  {
992  i = 0;
993  maxCount = 1;
994  }
995  else if ( placement == LastVertex )
996  {
997  i = points.count() - 1;
998  maxCount = points.count();
999  }
1000  else
1001  {
1002  i = 0;
1003  maxCount = points.count();
1004  if ( points.first() == points.last() )
1005  isRing = true;
1006  }
1007 
1008  if ( offsetAlongLine > 0 && ( placement == FirstVertex || placement == LastVertex ) )
1009  {
1010  double distance;
1011  distance = placement == FirstVertex ? offsetAlongLine : -offsetAlongLine;
1012  renderOffsetVertexAlongLine( points, i, distance, context );
1013  // restore original rotation
1014  mMarker->setAngle( origAngle );
1015  return;
1016  }
1017 
1018  for ( ; i < maxCount; ++i )
1019  {
1020  if ( isRing && placement == Vertex && i == points.count() - 1 )
1021  {
1022  continue; // don't draw the last marker - it has been drawn already
1023  }
1024  // rotate marker (if desired)
1025  if ( mRotateMarker )
1026  {
1027  double angle = markerAngle( points, isRing, i );
1028  mMarker->setAngle( origAngle + angle * 180 / M_PI );
1029  }
1030 
1031  mMarker->renderPoint( points.at( i ), context.feature(), rc, -1, context.selected() );
1032  }
1033 
1034  // restore original rotation
1035  mMarker->setAngle( origAngle );
1036 }
1037 
1038 double QgsMarkerLineSymbolLayerV2::markerAngle( const QPolygonF& points, bool isRing, int vertex )
1039 {
1040  double angle = 0;
1041  const QPointF& pt = points[vertex];
1042 
1043  if ( isRing || ( vertex > 0 && vertex < points.count() - 1 ) )
1044  {
1045  int prevIndex = vertex - 1;
1046  int nextIndex = vertex + 1;
1047 
1048  if ( isRing && ( vertex == 0 || vertex == points.count() - 1 ) )
1049  {
1050  prevIndex = points.count() - 2;
1051  nextIndex = 1;
1052  }
1053 
1054  QPointF prevPoint, nextPoint;
1055  while ( prevIndex >= 0 )
1056  {
1057  prevPoint = points[ prevIndex ];
1058  if ( prevPoint != pt )
1059  {
1060  break;
1061  }
1062  --prevIndex;
1063  }
1064 
1065  while ( nextIndex < points.count() )
1066  {
1067  nextPoint = points[ nextIndex ];
1068  if ( nextPoint != pt )
1069  {
1070  break;
1071  }
1072  ++nextIndex;
1073  }
1074 
1075  if ( prevIndex >= 0 && nextIndex < points.count() )
1076  {
1077  angle = _averageAngle( prevPoint, pt, nextPoint );
1078  }
1079  }
1080  else //no ring and vertex is at start / at end
1081  {
1082  if ( vertex == 0 )
1083  {
1084  while ( vertex < points.size() - 1 )
1085  {
1086  const QPointF& nextPt = points[vertex+1];
1087  if ( pt != nextPt )
1088  {
1089  angle = MyLine( pt, nextPt ).angle();
1090  return angle;
1091  }
1092  ++vertex;
1093  }
1094  }
1095  else
1096  {
1097  // use last segment's angle
1098  while ( vertex >= 1 ) //in case of duplicated vertices, take the next suitable one
1099  {
1100  const QPointF& prevPt = points[vertex-1];
1101  if ( pt != prevPt )
1102  {
1103  angle = MyLine( prevPt, pt ).angle();
1104  return angle;
1105  }
1106  --vertex;
1107  }
1108  }
1109  }
1110  return angle;
1111 }
1112 
1113 void QgsMarkerLineSymbolLayerV2::renderOffsetVertexAlongLine( const QPolygonF &points, int vertex, double distance, QgsSymbolV2RenderContext& context )
1114 {
1115  if ( points.isEmpty() )
1116  return;
1117 
1118  QgsRenderContext& rc = context.renderContext();
1119  double origAngle = mMarker->angle();
1120  if ( distance == 0 )
1121  {
1122  // rotate marker (if desired)
1123  if ( mRotateMarker )
1124  {
1125  bool isRing = false;
1126  if ( points.first() == points.last() )
1127  isRing = true;
1128  double angle = markerAngle( points, isRing, vertex );
1129  mMarker->setAngle( origAngle + angle * 180 / M_PI );
1130  }
1131  mMarker->renderPoint( points[vertex], context.feature(), rc, -1, context.selected() );
1132  return;
1133  }
1134 
1135  int pointIncrement = distance > 0 ? 1 : -1;
1136  QPointF previousPoint = points[vertex];
1137  int startPoint = distance > 0 ? qMin( vertex + 1, points.count() - 1 ) : qMax( vertex - 1, 0 );
1138  int endPoint = distance > 0 ? points.count() - 1 : 0;
1139  double distanceLeft = qAbs( distance );
1140 
1141  for ( int i = startPoint; pointIncrement > 0 ? i <= endPoint : i >= endPoint; i += pointIncrement )
1142  {
1143  const QPointF& pt = points[i];
1144 
1145  if ( previousPoint == pt ) // must not be equal!
1146  continue;
1147 
1148  // create line segment
1149  MyLine l( previousPoint, pt );
1150 
1151  if ( distanceLeft < l.length() )
1152  {
1153  //destination point is in current segment
1154  QPointF markerPoint = previousPoint + l.diffForInterval( distanceLeft );
1155  // rotate marker (if desired)
1156  if ( mRotateMarker )
1157  {
1158  mMarker->setAngle( origAngle + ( l.angle() * 180 / M_PI ) );
1159  }
1160  mMarker->renderPoint( markerPoint, context.feature(), rc, -1, context.selected() );
1161  return;
1162  }
1163 
1164  distanceLeft -= l.length();
1165  previousPoint = pt;
1166  }
1167 
1168  //didn't find point
1169  return;
1170 }
1171 
1173 {
1174  if ( points.size() > 0 )
1175  {
1176  // calc length
1177  qreal length = 0;
1178  QPolygonF::const_iterator it = points.constBegin();
1179  QPointF last = *it;
1180  for ( ++it; it != points.constEnd(); ++it )
1181  {
1182  length += sqrt(( last.x() - it->x() ) * ( last.x() - it->x() ) +
1183  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1184  last = *it;
1185  }
1186 
1187  // find the segment where the central point lies
1188  it = points.constBegin();
1189  last = *it;
1190  qreal last_at = 0, next_at = 0;
1191  QPointF next;
1192  int segment = 0;
1193  for ( ++it; it != points.constEnd(); ++it )
1194  {
1195  next = *it;
1196  next_at += sqrt(( last.x() - it->x() ) * ( last.x() - it->x() ) +
1197  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1198  if ( next_at >= length / 2 )
1199  break; // we have reached the center
1200  last = *it;
1201  last_at = next_at;
1202  segment++;
1203  }
1204 
1205  // find out the central point on segment
1206  MyLine l( last, next ); // for line angle
1207  qreal k = ( length * 0.5 - last_at ) / ( next_at - last_at );
1208  QPointF pt = last + ( next - last ) * k;
1209 
1210  // draw the marker
1211  double origAngle = mMarker->angle();
1212  if ( mRotateMarker )
1213  mMarker->setAngle( origAngle + l.angle() * 180 / M_PI );
1214  mMarker->renderPoint( pt, context.feature(), context.renderContext(), -1, context.selected() );
1215  if ( mRotateMarker )
1216  mMarker->setAngle( origAngle );
1217  }
1218 }
1219 
1220 
1222 {
1223  QgsStringMap map;
1224  map["rotate"] = ( mRotateMarker ? "1" : "0" );
1225  map["interval"] = QString::number( mInterval );
1226  map["offset"] = QString::number( mOffset );
1227  map["offset_along_line"] = QString::number( mOffsetAlongLine );
1228  map["offset_along_line_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetAlongLineUnit );
1229  map["offset_along_line_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetAlongLineMapUnitScale );
1230  map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit );
1231  map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
1232  map["interval_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mIntervalUnit );
1233  map["interval_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mIntervalMapUnitScale );
1234  if ( mPlacement == Vertex )
1235  map["placement"] = "vertex";
1236  else if ( mPlacement == LastVertex )
1237  map["placement"] = "lastvertex";
1238  else if ( mPlacement == FirstVertex )
1239  map["placement"] = "firstvertex";
1240  else if ( mPlacement == CentralPoint )
1241  map["placement"] = "centralpoint";
1242  else
1243  map["placement"] = "interval";
1244 
1246  return map;
1247 }
1248 
1250 {
1251  return mMarker;
1252 }
1253 
1255 {
1256  if ( symbol == NULL || symbol->type() != QgsSymbolV2::Marker )
1257  {
1258  delete symbol;
1259  return false;
1260  }
1261 
1262  delete mMarker;
1263  mMarker = static_cast<QgsMarkerSymbolV2*>( symbol );
1264  mColor = mMarker->color();
1265  return true;
1266 }
1267 
1269 {
1271  x->setSubSymbol( mMarker->clone() );
1272  x->setOffset( mOffset );
1273  x->setPlacement( mPlacement );
1274  x->setOffsetUnit( mOffsetUnit );
1282  return x;
1283 }
1284 
1285 void QgsMarkerLineSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
1286 {
1287  for ( int i = 0; i < mMarker->symbolLayerCount(); i++ )
1288  {
1289  QDomElement symbolizerElem = doc.createElement( "se:LineSymbolizer" );
1290  if ( !props.value( "uom", "" ).isEmpty() )
1291  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
1292  element.appendChild( symbolizerElem );
1293 
1294  // <Geometry>
1295  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
1296 
1297  QString gap;
1298  switch ( mPlacement )
1299  {
1300  case FirstVertex:
1301  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "firstPoint" ) );
1302  break;
1303  case LastVertex:
1304  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "lastPoint" ) );
1305  break;
1306  case CentralPoint:
1307  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "centralPoint" ) );
1308  break;
1309  case Vertex:
1310  // no way to get line/polygon's vertices, use a VendorOption
1311  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "points" ) );
1312  break;
1313  default:
1314  gap = QString::number( mInterval );
1315  break;
1316  }
1317 
1318  if ( !mRotateMarker )
1319  {
1320  // markers in LineSymbolizer must be drawn following the line orientation,
1321  // use a VendorOption when no marker rotation
1322  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "rotateMarker", "0" ) );
1323  }
1324 
1325  // <Stroke>
1326  QDomElement strokeElem = doc.createElement( "se:Stroke" );
1327  symbolizerElem.appendChild( strokeElem );
1328 
1329  // <GraphicStroke>
1330  QDomElement graphicStrokeElem = doc.createElement( "se:GraphicStroke" );
1331  strokeElem.appendChild( graphicStrokeElem );
1332 
1333  QgsSymbolLayerV2 *layer = mMarker->symbolLayer( i );
1334  QgsMarkerSymbolLayerV2 *markerLayer = static_cast<QgsMarkerSymbolLayerV2 *>( layer );
1335  if ( !markerLayer )
1336  {
1337  graphicStrokeElem.appendChild( doc.createComment( QString( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( markerLayer->layerType() ) ) );
1338  }
1339  else
1340  {
1341  markerLayer->writeSldMarker( doc, graphicStrokeElem, props );
1342  }
1343 
1344  if ( !gap.isEmpty() )
1345  {
1346  QDomElement gapElem = doc.createElement( "se:Gap" );
1347  QgsSymbolLayerV2Utils::createFunctionElement( doc, gapElem, gap );
1348  graphicStrokeElem.appendChild( gapElem );
1349  }
1350 
1351  if ( !qgsDoubleNear( mOffset, 0.0 ) )
1352  {
1353  QDomElement perpOffsetElem = doc.createElement( "se:PerpendicularOffset" );
1354  perpOffsetElem.appendChild( doc.createTextNode( QString::number( mOffset ) ) );
1355  symbolizerElem.appendChild( perpOffsetElem );
1356  }
1357  }
1358 }
1359 
1361 {
1362  QgsDebugMsg( "Entered." );
1363 
1364  QDomElement strokeElem = element.firstChildElement( "Stroke" );
1365  if ( strokeElem.isNull() )
1366  return NULL;
1367 
1368  QDomElement graphicStrokeElem = strokeElem.firstChildElement( "GraphicStroke" );
1369  if ( graphicStrokeElem.isNull() )
1370  return NULL;
1371 
1372  // retrieve vendor options
1373  bool rotateMarker = true;
1375 
1376  QgsStringMap vendorOptions = QgsSymbolLayerV2Utils::getVendorOptionList( element );
1377  for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
1378  {
1379  if ( it.key() == "placement" )
1380  {
1381  if ( it.value() == "points" ) placement = Vertex;
1382  else if ( it.value() == "firstPoint" ) placement = FirstVertex;
1383  else if ( it.value() == "lastPoint" ) placement = LastVertex;
1384  else if ( it.value() == "centralPoint" ) placement = CentralPoint;
1385  }
1386  else if ( it.value() == "rotateMarker" )
1387  {
1388  rotateMarker = it.value() == "0";
1389  }
1390  }
1391 
1392  QgsMarkerSymbolV2 *marker = 0;
1393 
1395  if ( l )
1396  {
1397  QgsSymbolLayerV2List layers;
1398  layers.append( l );
1399  marker = new QgsMarkerSymbolV2( layers );
1400  }
1401 
1402  if ( !marker )
1403  return NULL;
1404 
1405  double interval = 0.0;
1406  QDomElement gapElem = graphicStrokeElem.firstChildElement( "Gap" );
1407  if ( !gapElem.isNull() )
1408  {
1409  bool ok;
1410  double d = gapElem.firstChild().nodeValue().toDouble( &ok );
1411  if ( ok )
1412  interval = d;
1413  }
1414 
1415  double offset = 0.0;
1416  QDomElement perpOffsetElem = graphicStrokeElem.firstChildElement( "PerpendicularOffset" );
1417  if ( !perpOffsetElem.isNull() )
1418  {
1419  bool ok;
1420  double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
1421  if ( ok )
1422  offset = d;
1423  }
1424 
1425  QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( rotateMarker );
1426  x->setPlacement( placement );
1427  x->setInterval( interval );
1428  x->setSubSymbol( marker );
1429  x->setOffset( offset );
1430  return x;
1431 }
1432 
1434 {
1435  mMarker->setSize( width );
1436 }
1437 
1439 {
1440  return mMarker->size();
1441 }
1442 
1444 {
1446  mIntervalUnit = unit;
1447  mOffsetUnit = unit;
1448  mOffsetAlongLineUnit = unit;
1449 }
1450 
1452 {
1454  if ( mIntervalUnit != unit || mOffsetUnit != unit || mOffsetAlongLineUnit != unit )
1455  {
1456  return QgsSymbolV2::Mixed;
1457  }
1458  return unit;
1459 }
1460 
1462 {
1464  mIntervalMapUnitScale = scale;
1465  mOffsetMapUnitScale = scale;
1467 }
1468 
1470 {
1474  {
1475  return mOffsetMapUnitScale;
1476  }
1477  return QgsMapUnitScale();
1478 }
1479 
1481 {
1482  return ( mMarker->size() / 2.0 ) + mOffset;
1483 }
1484 
1485 
1486 
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)
void renderPolygonOutline(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolV2RenderContext &context)
double estimateMaxBleed() const
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape...
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:87
#define DEFAULT_SIMPLELINE_PENSTYLE
void startRender(QgsSymbolV2RenderContext &context)
#define DEFAULT_MARKERLINE_ROTATE
void setOutputUnit(QgsSymbolV2::OutputUnit unit)
QMap< QString, QgsExpression * > mDataDefinedProperties
float threshold() const
Gets the simplification threshold of the vector layer managed.
int renderHints() const
Definition: qgssymbolv2.h:208
virtual double width() const
double markerAngle(const QPolygonF &points, bool isRing, int vertex)
QgsMapUnitScale mapUnitScale() const
QgsSymbolV2::OutputUnit outputUnit() const
#define DEFAULT_MARKERLINE_INTERVAL
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.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
void setCustomDashPatternUnit(QgsSymbolV2::OutputUnit unit)
QgsMapUnitScale mCustomDashPatternMapUnitScale
double rendererScale() const
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
static QVector< qreal > decodeRealVector(const QString &s)
void renderPolylineInterval(const QPolygonF &points, QgsSymbolV2RenderContext &context)
QVector< qreal > customDashVector() const
void setPenJoinStyle(Qt::PenJoinStyle style)
void setOffsetUnit(QgsSymbolV2::OutputUnit unit)
QgsSymbolV2::OutputUnit outputUnit() const
QGis::GeometryType type()
Returns type of the vector.
QgsSymbolV2::OutputUnit outputUnit() const
static QDomElement createVendorOptionElement(QDomDocument &doc, QString name, QString value)
static QColor decodeColor(QString str)
QgsSymbolLayerV2 * clone() const
static const bool selectionIsOpaque
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
bool setSubSymbol(QgsSymbolV2 *symbol)
QVector< qreal > dxfCustomDashPattern(QgsSymbolV2::OutputUnit &unit) const
QgsMapUnitScale mWidthMapUnitScale
void setIntervalMapUnitScale(const QgsMapUnitScale &scale)
void renderPolyline(const QPolygonF &points, QgsSymbolV2RenderContext &context)
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
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
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
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
static QgsSymbolLayerV2 * createMarkerLayerFromSld(QDomElement &element)
QgsStringMap properties() const
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.
const QgsFeature * feature() const
Current feature being rendered - may be null.
Definition: qgssymbolv2.h:213
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 setMapUnitScale(const QgsMapUnitScale &scale)
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)
#define M_PI
static QgsSymbolLayerV2 * createFromSld(QDomElement &element)
void setAngle(double angle)
#define DEFAULT_SIMPLELINE_CAPSTYLE
#define DEFAULT_SIMPLELINE_JOINSTYLE
void setOutputUnit(QgsSymbolV2::OutputUnit unit)
int symbolLayerCount()
Returns total number of symbol layers contained in the symbol.
Definition: qgssymbolv2.h:106
void setSize(double size)
double rasterScaleFactor() const
void startRender(QgsSymbolV2RenderContext &context)
QgsMapUnitScale mapUnitScale() const
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...
QString ogrFeatureStyle(double mmScaleFactor, double mapUnitScaleFactor) const
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)
void setPenCapStyle(Qt::PenCapStyle style)
void setCustomDashVector(const QVector< qreal > &vector)
static QgsSymbolLayerV2 * create(const QgsStringMap &properties=QgsStringMap())
void setMapUnitScale(const QgsMapUnitScale &scale)
QgsMapUnitScale mapUnitScale() const
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)
virtual void setWidth(double width)
void setColor(const QColor &color)
static QString encodeRealVector(const QVector< qreal > &v)
QgsSymbolV2::OutputUnit mCustomDashPatternUnit
virtual QString layerType() const =0
void renderPolyline(const QPolygonF &points, QgsSymbolV2RenderContext &context)
double symbologyScaleDenominator() const
Definition: qgsdxfexport.h:51
QgsSymbolLayerV2 * clone() const
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.
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 stopRender(QgsSymbolV2RenderContext &context)
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.
void toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props) const
QList< QPolygonF > offsetLine(QPolygonF polyline, double dist, QGis::GeometryType geometryType)
calculate geometry shifted by a specified distance
virtual QgsSymbolV2 * clone() const
QList< QgsSymbolLayerV2 * > QgsSymbolLayerV2List
Definition: qgssymbolv2.h:39
void setCustomDashPatternMapUnitScale(const QgsMapUnitScale &scale)
double dxfWidth(const QgsDxfExport &e, const QgsSymbolV2RenderContext &context) const
void renderPolygonOutline(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolV2RenderContext &context)
QgsRenderContext & renderContext()
Definition: qgssymbolv2.h:191
double estimateMaxBleed() const
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape...
SimplifyHints simplifyHints() const
Gets the simplification hints of the vector layer managed.
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)
void setOutputUnit(QgsSymbolV2::OutputUnit unit)
MyLine(QPointF p1, QPointF p2)
bool selected() const
Definition: qgssymbolv2.h:205
void toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props) const
QgsSymbolV2::OutputUnit mOffsetUnit
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
QgsSymbolLayerV2 * symbolLayer(int layer)
Returns a specific symbol layers contained in the symbol.
Qt::PenStyle dxfPenStyle() const
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)
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)
QColor dxfColor(const QgsSymbolV2RenderContext &context) const
void stopRender(QgsSymbolV2RenderContext &context)
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.
void setMapUnitScale(const QgsMapUnitScale &scale)
Qt::PenCapStyle penCapStyle() const
QColor color() const
static QString encodePenCapStyle(Qt::PenCapStyle style)
virtual void setDataDefinedProperty(const QString &property, const QString &expressionString)