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