26 #include <QDomDocument>
27 #include <QDomElement>
32 : mPenStyle( penStyle )
35 , mUseCustomDashPattern( false )
37 , mDrawInsidePolygon( false )
87 if ( props.contains(
"line_color" ) )
91 else if ( props.contains(
"outline_color" ) )
95 else if ( props.contains(
"color" ) )
100 if ( props.contains(
"line_width" ) )
102 width = props[
"line_width"].toDouble();
104 else if ( props.contains(
"outline_width" ) )
106 width = props[
"outline_width"].toDouble();
108 else if ( props.contains(
"width" ) )
111 width = props[
"width"].toDouble();
113 if ( props.contains(
"line_style" ) )
117 else if ( props.contains(
"outline_style" ) )
121 else if ( props.contains(
"penstyle" ) )
127 if ( props.contains(
"line_width_unit" ) )
131 else if ( props.contains(
"outline_width_unit" ) )
135 else if ( props.contains(
"width_unit" ) )
140 if ( props.contains(
"width_map_unit_scale" ) )
142 if ( props.contains(
"offset" ) )
143 l->
setOffset( props[
"offset"].toDouble() );
144 if ( props.contains(
"offset_unit" ) )
146 if ( props.contains(
"offset_map_unit_scale" ) )
148 if ( props.contains(
"joinstyle" ) )
150 if ( props.contains(
"capstyle" ) )
153 if ( props.contains(
"use_custom_dash" ) )
157 if ( props.contains(
"customdash" ) )
161 if ( props.contains(
"customdash_unit" ) )
165 if ( props.contains(
"customdash_map_unit_scale" ) )
170 if ( props.contains(
"draw_inside_polygon" ) )
176 if ( props.contains(
"color_expression" ) )
178 if ( props.contains(
"width_expression" ) )
180 if ( props.contains(
"offset_expression" ) )
182 if ( props.contains(
"customdash_expression" ) )
184 if ( props.contains(
"joinstyle_expression" ) )
186 if ( props.contains(
"capstyle_expression" ) )
188 if ( props.contains(
"line_style_expression" ) )
203 penColor.setAlphaF(
mColor.alphaF() * context.
alpha() );
204 mPen.setColor( penColor );
206 mPen.setWidthF( scaledWidth );
209 mPen.setStyle( Qt::CustomDashLine );
212 double dashWidthDiv = scaledWidth;
214 QStringList versionSplit = QString( qVersion() ).split(
"." );
215 if ( versionSplit.size() > 1
216 && versionSplit.at( 1 ).toInt() >= 8
221 QVector<qreal> scaledVector;
228 mPen.setDashPattern( scaledVector );
240 selColor.setAlphaF( context.
alpha() );
264 QPainterPath clipPath;
265 clipPath.addPolygon( points );
270 QList<QPolygonF>::const_iterator it = rings->constBegin();
271 for ( ; it != rings->constEnd(); ++it )
273 QPolygonF ring = *it;
274 clipPath.addPolygon( ring );
279 p->setClipPath( clipPath, Qt::IntersectClip );
286 foreach (
const QPolygonF& ring, *rings )
314 applyDataDefinedSymbology( context,
mPen,
mSelPen, offset );
319 if ( points.size() <= 2 &&
322 ( p->renderHints() & QPainter::Antialiasing ) )
324 p->setRenderHint( QPainter::Antialiasing,
false );
325 p->drawPolyline( points );
326 p->setRenderHint( QPainter::Antialiasing,
true );
332 p->drawPolyline( points );
338 for (
int part = 0; part < mline.count(); ++part )
339 p->drawPolyline( mline[ part ] );
347 map[
"line_width"] = QString::number(
mWidth );
353 map[
"offset"] = QString::number(
mOffset );
389 QDomElement symbolizerElem = doc.createElement(
"se:LineSymbolizer" );
390 if ( !props.value(
"uom",
"" ).isEmpty() )
391 symbolizerElem.setAttribute(
"uom", props.value(
"uom",
"" ) );
392 element.appendChild( symbolizerElem );
398 QDomElement strokeElem = doc.createElement(
"se:Stroke" );
399 symbolizerElem.appendChild( strokeElem );
408 QDomElement perpOffsetElem = doc.createElement(
"se:PerpendicularOffset" );
409 perpOffsetElem.appendChild( doc.createTextNode( QString::number(
mOffset ) ) );
410 symbolizerElem.appendChild( perpOffsetElem );
433 QDomElement strokeElem = element.firstChildElement(
"Stroke" );
434 if ( strokeElem.isNull() )
446 &penJoinStyle, &penCapStyle,
447 &customDashVector ) )
451 QDomElement perpOffsetElem = element.firstChildElement(
"PerpendicularOffset" );
452 if ( !perpOffsetElem.isNull() )
455 double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
472 pen.setWidthF( scaledWidth );
473 selPen.setWidthF( scaledWidth );
476 void QgsSimpleLineSymbolLayerV2::applyDataDefinedSymbology(
QgsSymbolV2RenderContext& context, QPen& pen, QPen& selPen,
double& offset )
483 if ( strokeWidthExpression )
485 double scaledWidth = strokeWidthExpression->
evaluate( const_cast<QgsFeature*>( context.
feature() ) ).toDouble()
487 pen.setWidthF( scaledWidth );
488 selPen.setWidthF( scaledWidth );
493 if ( strokeColorExpression )
500 if ( lineOffsetExpression )
502 offset = lineOffsetExpression->
evaluate( const_cast<QgsFeature*>( context.
feature() ) ).toDouble();
507 if ( dashPatternExpression )
510 double dashWidthDiv =
mPen.widthF();
512 if ( strokeWidthExpression )
514 dashWidthDiv = pen.widthF();
515 scaledWidth = pen.widthF();
519 QStringList versionSplit = QString( qVersion() ).split(
"." );
520 if ( versionSplit.size() > 1
521 && versionSplit.at( 1 ).toInt() >= 8
527 QVector<qreal> dashVector;
528 QStringList dashList = dashPatternExpression->
evaluate( const_cast<QgsFeature*>( context.
feature() ) ).toString().split(
";" );
529 QStringList::const_iterator dashIt = dashList.constBegin();
530 for ( ; dashIt != dashList.constEnd(); ++dashIt )
534 pen.setDashPattern( dashVector );
539 if ( lineStyleExpression )
541 QString lineStyleString = lineStyleExpression->
evaluate( const_cast<QgsFeature*>( context.
feature() ) ).toString();
547 if ( joinStyleExpression )
549 QString joinStyleString = joinStyleExpression->
evaluate( const_cast<QgsFeature*>( context.
feature() ) ).toString();
555 if ( capStyleExpression )
557 QString capStyleString = capStyleExpression->
evaluate( const_cast<QgsFeature*>( context.
feature() ) ).toString();
590 if ( strokeWidthExpression )
605 if ( strokeColorExpression )
617 if ( offsetExpression )
619 offset = offsetExpression->
evaluate( const_cast<QgsFeature*>( context.
feature() ) ).toDouble();
636 if ( p1.x() == p2.x() )
645 mT = float( p2.y() - p1.y() ) / ( p2.x() - p1.x() );
650 double x = ( p2.x() - p1.x() );
651 double y = ( p2.y() - p1.y() );
652 mLength = sqrt( x * x + y * y );
669 return (
mIncreasing ? QPointF( 0, interval ) : QPointF( 0, -interval ) );
671 double alpha = atan(
mT );
672 double dx = cos( alpha ) * interval;
673 double dy = sin( alpha ) * interval;
674 return (
mIncreasing ? QPointF( dx, dy ) : QPointF( -dx, -dy ) );
711 if ( props.contains(
"interval" ) )
712 interval = props[
"interval"].toDouble();
713 if ( props.contains(
"rotate" ) )
714 rotate = ( props[
"rotate"] ==
"1" );
717 if ( props.contains(
"offset" ) )
719 x->
setOffset( props[
"offset"].toDouble() );
721 if ( props.contains(
"offset_unit" ) )
725 if ( props.contains(
"interval_unit" ) )
729 if ( props.contains(
"offset_along_line" ) )
733 if ( props.contains(
"offset_along_line_unit" ) )
737 if ( props.contains((
"offset_along_line_map_unit_scale" ) ) )
742 if ( props.contains(
"offset_map_unit_scale" ) )
746 if ( props.contains(
"interval_map_unit_scale" ) )
751 if ( props.contains(
"placement" ) )
753 if ( props[
"placement"] ==
"vertex" )
755 else if ( props[
"placement"] ==
"lastvertex" )
757 else if ( props[
"placement"] ==
"firstvertex" )
759 else if ( props[
"placement"] ==
"centralpoint" )
766 if ( props.contains(
"interval_expression" ) )
770 if ( props.contains(
"offset_expression" ) )
774 if ( props.contains(
"placement_expression" ) )
778 if ( props.contains(
"offset_along_line_expression" ) )
824 if ( offsetExpression )
826 offset = offsetExpression->
evaluate( const_cast<QgsFeature*>( context.
feature() ) ).toDouble();
831 if ( placementExpression )
833 QString placementString = placementExpression->
evaluate( const_cast<QgsFeature*>( context.
feature() ) ).toString();
834 if ( placementString.compare(
"vertex", Qt::CaseInsensitive ) == 0 )
838 else if ( placementString.compare(
"lastvertex", Qt::CaseInsensitive ) == 0 )
842 else if ( placementString.compare(
"firstvertex", Qt::CaseInsensitive ) == 0 )
846 else if ( placementString.compare(
"centerpoint", Qt::CaseInsensitive ) == 0 )
869 for (
int part = 0; part < mline.count(); ++part )
871 const QPolygonF &points2 = mline[ part ];
889 foreach (
const QPolygonF& ring, *rings )
897 if ( points.isEmpty() )
900 QPointF lastPt = points[0];
901 double lengthLeft = 0;
909 if ( intervalExpression )
911 interval = intervalExpression->
evaluate( const_cast<QgsFeature*>( context.
feature() ) ).toDouble();
919 if ( offsetAlongLineExpression )
921 offsetAlongLine = offsetAlongLineExpression->
evaluate( const_cast<QgsFeature*>( context.
feature() ) ).toDouble();
927 for (
int i = 1; i < points.count(); ++i )
929 const QPointF& pt = points[i];
940 double c = 1 - lengthLeft / painterUnitInterval;
958 while ( lengthLeft > painterUnitInterval )
962 lengthLeft -= painterUnitInterval;
975 static double _averageAngle(
const QPointF& prevPt,
const QPointF& pt,
const QPointF& nextPt )
980 double unitX = cos( a1 ) + cos( a2 ), unitY = sin( a1 ) + sin( a2 );
982 return atan2( unitY, unitX );
987 if ( points.isEmpty() )
998 if ( offsetAlongLineExpression )
1000 offsetAlongLine = offsetAlongLineExpression->
evaluate( const_cast<QgsFeature*>( context.
feature() ) ).toDouble();
1002 if ( offsetAlongLine != 0 )
1015 i = points.count() - 1;
1016 maxCount = points.count();
1021 maxCount = points.count();
1022 if ( points.first() == points.last() )
1030 renderOffsetVertexAlongLine( points, i, distance, context );
1036 for ( ; i < maxCount; ++i )
1038 if ( isRing && placement ==
Vertex && i == points.count() - 1 )
1059 const QPointF& pt = points[vertex];
1061 if ( isRing || ( vertex > 0 && vertex < points.count() - 1 ) )
1063 int prevIndex = vertex - 1;
1064 int nextIndex = vertex + 1;
1066 if ( isRing && ( vertex == 0 || vertex == points.count() - 1 ) )
1068 prevIndex = points.count() - 2;
1072 QPointF prevPoint, nextPoint;
1073 while ( prevIndex >= 0 )
1075 prevPoint = points[ prevIndex ];
1076 if ( prevPoint != pt )
1083 while ( nextIndex < points.count() )
1085 nextPoint = points[ nextIndex ];
1086 if ( nextPoint != pt )
1093 if ( prevIndex >= 0 && nextIndex < points.count() )
1102 while ( vertex < points.size() - 1 )
1104 const QPointF& nextPt = points[vertex+1];
1116 while ( vertex >= 1 )
1118 const QPointF& prevPt = points[vertex-1];
1131 void QgsMarkerLineSymbolLayerV2::renderOffsetVertexAlongLine(
const QPolygonF &points,
int vertex,
double distance,
QgsSymbolV2RenderContext& context )
1133 if ( points.isEmpty() )
1138 if ( distance == 0 )
1143 bool isRing =
false;
1144 if ( points.first() == points.last() )
1153 int pointIncrement = distance > 0 ? 1 : -1;
1154 QPointF previousPoint = points[vertex];
1155 int startPoint = distance > 0 ? qMin( vertex + 1, points.count() - 1 ) : qMax( vertex - 1, 0 );
1156 int endPoint = distance > 0 ? points.count() - 1 : 0;
1157 double distanceLeft = qAbs( distance );
1159 for (
int i = startPoint; pointIncrement > 0 ? i <= endPoint : i >= endPoint; i += pointIncrement )
1161 const QPointF& pt = points[i];
1163 if ( previousPoint == pt )
1167 MyLine l( previousPoint, pt );
1169 if ( distanceLeft < l.length() )
1172 QPointF markerPoint = previousPoint + l.diffForInterval( distanceLeft );
1182 distanceLeft -= l.length();
1192 if ( points.size() > 0 )
1196 QPolygonF::const_iterator it = points.constBegin();
1198 for ( ++it; it != points.constEnd(); ++it )
1200 length += sqrt(( last.x() - it->x() ) * ( last.x() - it->x() ) +
1201 ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1206 it = points.constBegin();
1208 qreal last_at = 0, next_at = 0;
1211 for ( ++it; it != points.constEnd(); ++it )
1214 next_at += sqrt(( last.x() - it->x() ) * ( last.x() - it->x() ) +
1215 ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1216 if ( next_at >= length / 2 )
1225 qreal k = ( length * 0.5 - last_at ) / ( next_at - last_at );
1226 QPointF pt = last + ( next - last ) * k;
1243 map[
"interval"] = QString::number(
mInterval );
1244 map[
"offset"] = QString::number(
mOffset );
1253 map[
"placement"] =
"vertex";
1255 map[
"placement"] =
"lastvertex";
1257 map[
"placement"] =
"firstvertex";
1259 map[
"placement"] =
"centralpoint";
1261 map[
"placement"] =
"interval";
1307 QDomElement symbolizerElem = doc.createElement(
"se:LineSymbolizer" );
1308 if ( !props.value(
"uom",
"" ).isEmpty() )
1309 symbolizerElem.setAttribute(
"uom", props.value(
"uom",
"" ) );
1310 element.appendChild( symbolizerElem );
1344 QDomElement strokeElem = doc.createElement(
"se:Stroke" );
1345 symbolizerElem.appendChild( strokeElem );
1348 QDomElement graphicStrokeElem = doc.createElement(
"se:GraphicStroke" );
1349 strokeElem.appendChild( graphicStrokeElem );
1355 graphicStrokeElem.appendChild( doc.createComment( QString(
"MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( layer->
layerType() ) ) );
1362 if ( !gap.isEmpty() )
1364 QDomElement gapElem = doc.createElement(
"se:Gap" );
1366 graphicStrokeElem.appendChild( gapElem );
1371 QDomElement perpOffsetElem = doc.createElement(
"se:PerpendicularOffset" );
1372 perpOffsetElem.appendChild( doc.createTextNode( QString::number(
mOffset ) ) );
1373 symbolizerElem.appendChild( perpOffsetElem );
1382 QDomElement strokeElem = element.firstChildElement(
"Stroke" );
1383 if ( strokeElem.isNull() )
1386 QDomElement graphicStrokeElem = strokeElem.firstChildElement(
"GraphicStroke" );
1387 if ( graphicStrokeElem.isNull() )
1395 for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
1397 if ( it.key() ==
"placement" )
1399 if ( it.value() ==
"points" ) placement =
Vertex;
1400 else if ( it.value() ==
"firstPoint" ) placement =
FirstVertex;
1401 else if ( it.value() ==
"lastPoint" ) placement =
LastVertex;
1402 else if ( it.value() ==
"centralPoint" ) placement =
CentralPoint;
1404 else if ( it.value() ==
"rotateMarker" )
1406 rotateMarker = it.value() ==
"0";
1424 QDomElement gapElem = graphicStrokeElem.firstChildElement(
"Gap" );
1425 if ( !gapElem.isNull() )
1428 double d = gapElem.firstChild().nodeValue().toDouble( &ok );
1433 double offset = 0.0;
1434 QDomElement perpOffsetElem = graphicStrokeElem.firstChildElement(
"PerpendicularOffset" );
1435 if ( !perpOffsetElem.isNull() )
1438 double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );