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