46#include <QSvgRenderer>
47#include <QDomDocument>
56 Qt::PenJoinStyle penJoinStyle )
57 : mBrushStyle( style )
58 , mStrokeColor( strokeColor )
59 , mStrokeStyle( strokeStyle )
60 , mStrokeWidth( strokeWidth )
61 , mPenJoinStyle( penJoinStyle )
105void QgsSimpleFillSymbolLayer::applyDataDefinedSymbology(
QgsSymbolRenderContext &context, QBrush &brush, QPen &pen, QPen &selPen )
130 penColor.setAlphaF( context.
opacity() * penColor.alphaF() );
131 pen.setColor( penColor );
139 double width = exprVal.toDouble( &ok );
143 pen.setWidthF( width );
144 selPen.setWidthF( width );
181 if ( props.contains( QStringLiteral(
"color" ) ) )
183 if ( props.contains( QStringLiteral(
"style" ) ) )
185 if ( props.contains( QStringLiteral(
"color_border" ) ) )
190 else if ( props.contains( QStringLiteral(
"outline_color" ) ) )
194 else if ( props.contains( QStringLiteral(
"line_color" ) ) )
199 if ( props.contains( QStringLiteral(
"style_border" ) ) )
204 else if ( props.contains( QStringLiteral(
"outline_style" ) ) )
208 else if ( props.contains( QStringLiteral(
"line_style" ) ) )
212 if ( props.contains( QStringLiteral(
"width_border" ) ) )
215 strokeWidth = props[QStringLiteral(
"width_border" )].toDouble();
217 else if ( props.contains( QStringLiteral(
"outline_width" ) ) )
219 strokeWidth = props[QStringLiteral(
"outline_width" )].toDouble();
221 else if ( props.contains( QStringLiteral(
"line_width" ) ) )
223 strokeWidth = props[QStringLiteral(
"line_width" )].toDouble();
225 if ( props.contains( QStringLiteral(
"offset" ) ) )
227 if ( props.contains( QStringLiteral(
"joinstyle" ) ) )
232 if ( props.contains( QStringLiteral(
"border_width_unit" ) ) )
236 else if ( props.contains( QStringLiteral(
"outline_width_unit" ) ) )
240 else if ( props.contains( QStringLiteral(
"line_width_unit" ) ) )
244 if ( props.contains( QStringLiteral(
"offset_unit" ) ) )
247 if ( props.contains( QStringLiteral(
"border_width_map_unit_scale" ) ) )
249 if ( props.contains( QStringLiteral(
"offset_map_unit_scale" ) ) )
252 sl->restoreOldDataDefinedProperties( props );
260 return QStringLiteral(
"SimpleFill" );
272 selColor.setAlphaF( context.
opacity() );
330 if (
mBrush.style() == Qt::SolidPattern ||
mBrush.style() == Qt::NoBrush || !
dynamic_cast<QPrinter *
>( p->device() ) )
344 p->setPen( Qt::NoPen );
348 p->setBrush( Qt::NoBrush );
366 map[QStringLiteral(
"outline_width" )] = QString::number(
mStrokeWidth );
394 QDomElement symbolizerElem = doc.createElement( QStringLiteral(
"se:PolygonSymbolizer" ) );
395 if ( !props.value( QStringLiteral(
"uom" ), QString() ).toString().isEmpty() )
396 symbolizerElem.setAttribute( QStringLiteral(
"uom" ), props.value( QStringLiteral(
"uom" ), QString() ).toString() );
397 element.appendChild( symbolizerElem );
405 QDomElement fillElem = doc.createElement( QStringLiteral(
"se:Fill" ) );
406 symbolizerElem.appendChild( fillElem );
412 const double alpha { props.value( QStringLiteral(
"alpha" ), QVariant() ).toDouble( &ok ) };
423 QDomElement strokeElem = doc.createElement( QStringLiteral(
"se:Stroke" ) );
424 symbolizerElem.appendChild( strokeElem );
428 const double alpha { props.value( QStringLiteral(
"alpha" ), QVariant() ).toDouble( &ok ) };
447 symbolStyle.append(
';' );
456 Qt::BrushStyle fillStyle;
460 QDomElement fillElem = element.firstChildElement( QStringLiteral(
"Fill" ) );
463 QDomElement strokeElem = element.firstChildElement( QStringLiteral(
"Stroke" ) );
469 double scaleFactor = 1.0;
470 const QString uom = element.attribute( QStringLiteral(
"uom" ) );
477 sl->setOutputUnit( sldUnitSize );
486 return penBleed + offsetBleed;
547 : mGradientColorType( colorType )
548 , mGradientType( gradientType )
549 , mCoordinateMode( coordinateMode )
550 , mGradientSpread( spread )
551 , mReferencePoint1( QPointF( 0.5, 0 ) )
552 , mReferencePoint2( QPointF( 0.5, 1 ) )
573 bool refPoint1IsCentroid =
false;
575 bool refPoint2IsCentroid =
false;
580 if ( props.contains( QStringLiteral(
"type" ) ) )
582 if ( props.contains( QStringLiteral(
"coordinate_mode" ) ) )
584 if ( props.contains( QStringLiteral(
"spread" ) ) )
586 if ( props.contains( QStringLiteral(
"color_type" ) ) )
588 if ( props.contains( QStringLiteral(
"gradient_color" ) ) )
593 else if ( props.contains( QStringLiteral(
"color" ) ) )
597 if ( props.contains( QStringLiteral(
"gradient_color2" ) ) )
602 if ( props.contains( QStringLiteral(
"reference_point1" ) ) )
604 if ( props.contains( QStringLiteral(
"reference_point1_iscentroid" ) ) )
605 refPoint1IsCentroid = props[QStringLiteral(
"reference_point1_iscentroid" )].toInt();
606 if ( props.contains( QStringLiteral(
"reference_point2" ) ) )
608 if ( props.contains( QStringLiteral(
"reference_point2_iscentroid" ) ) )
609 refPoint2IsCentroid = props[QStringLiteral(
"reference_point2_iscentroid" )].toInt();
610 if ( props.contains( QStringLiteral(
"angle" ) ) )
611 angle = props[QStringLiteral(
"angle" )].toDouble();
613 if ( props.contains( QStringLiteral(
"offset" ) ) )
630 if ( props.contains( QStringLiteral(
"offset_unit" ) ) )
632 if ( props.contains( QStringLiteral(
"offset_map_unit_scale" ) ) )
635 sl->setReferencePoint1IsCentroid( refPoint1IsCentroid );
637 sl->setReferencePoint2IsCentroid( refPoint2IsCentroid );
638 sl->setAngle(
angle );
640 sl->setColorRamp( gradientRamp );
642 sl->restoreOldDataDefinedProperties( props );
655 return QStringLiteral(
"GradientFill" );
658void QgsGradientFillSymbolLayer::applyDataDefinedSymbology(
QgsSymbolRenderContext &context,
const QPolygonF &points )
703 if ( currentType == QObject::tr(
"linear" ) )
707 else if ( currentType == QObject::tr(
"radial" ) )
711 else if ( currentType == QObject::tr(
"conical" ) )
725 if ( currentCoordMode == QObject::tr(
"feature" ) )
729 else if ( currentCoordMode == QObject::tr(
"viewport" ) )
743 if ( currentSpread == QObject::tr(
"pad" ) )
747 else if ( currentSpread == QObject::tr(
"repeat" ) )
751 else if ( currentSpread == QObject::tr(
"reflect" ) )
798 if ( refPoint1IsCentroid || refPoint2IsCentroid )
803 QRectF bbox = points.boundingRect();
804 double centroidX = (
centroid.
x() - bbox.left() ) / bbox.width();
805 double centroidY = (
centroid.
y() - bbox.top() ) / bbox.height();
807 if ( refPoint1IsCentroid )
809 refPoint1X = centroidX;
810 refPoint1Y = centroidY;
812 if ( refPoint2IsCentroid )
814 refPoint2X = centroidX;
815 refPoint2Y = centroidY;
821 spread, QPointF( refPoint1X, refPoint1Y ), QPointF( refPoint2X, refPoint2Y ),
angle );
824QPointF QgsGradientFillSymbolLayer::rotateReferencePoint( QPointF refPoint,
double angle )
829 QLineF refLine = QLineF( QPointF( 0.5, 0.5 ), refPoint );
831 refLine.setAngle( refLine.angle() +
angle );
833 QPointF rotatedReferencePoint = refLine.p2();
835 if ( rotatedReferencePoint.x() > 1 )
836 rotatedReferencePoint.setX( 1 );
837 if ( rotatedReferencePoint.x() < 0 )
838 rotatedReferencePoint.setX( 0 );
839 if ( rotatedReferencePoint.y() > 1 )
840 rotatedReferencePoint.setY( 1 );
841 if ( rotatedReferencePoint.y() < 0 )
842 rotatedReferencePoint.setY( 0 );
844 return rotatedReferencePoint;
851 QPointF referencePoint1, QPointF referencePoint2,
const double angle )
856 QColor fillColor2 =
color2;
857 fillColor2.setAlphaF( context.
opacity() * fillColor2.alphaF() );
868 gradient = QLinearGradient( rotatedReferencePoint1, rotatedReferencePoint2 );
871 gradient = QRadialGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).length() );
874 gradient = QConicalGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).
angle() );
880 gradient.setCoordinateMode( QGradient::ObjectBoundingMode );
883 gradient.setCoordinateMode( QGradient::StretchToDeviceMode );
889 gradient.setSpread( QGradient::PadSpread );
892 gradient.setSpread( QGradient::ReflectSpread );
895 gradient.setSpread( QGradient::RepeatSpread );
911 gradient.setColorAt( 1.0, fillColor2 );
915 brush = QBrush( gradient );
922 selColor.setAlphaF( context.
opacity() );
939 applyDataDefinedSymbology( context, points );
942 p->setPen( Qt::NoPen );
975 map[QStringLiteral(
"color_type" )] = QString::number(
static_cast< int >(
mGradientColorType ) );
976 map[QStringLiteral(
"type" )] = QString::number(
static_cast<int>(
mGradientType ) );
977 map[QStringLiteral(
"coordinate_mode" )] = QString::number(
static_cast< int >(
mCoordinateMode ) );
978 map[QStringLiteral(
"spread" )] = QString::number(
static_cast< int >(
mGradientSpread ) );
983 map[QStringLiteral(
"angle" )] = QString::number(
mAngle );
989#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
1013 return sl.release();
1055 int blurRadius,
bool useWholeShape,
double maxDistance )
1056 : mBlurRadius( blurRadius )
1057 , mUseWholeShape( useWholeShape )
1058 , mMaxDistance( maxDistance )
1059 , mColorType( colorType )
1078 if ( props.contains( QStringLiteral(
"color_type" ) ) )
1082 if ( props.contains( QStringLiteral(
"shapeburst_color" ) ) )
1087 else if ( props.contains( QStringLiteral(
"color" ) ) )
1092 if ( props.contains( QStringLiteral(
"shapeburst_color2" ) ) )
1097 else if ( props.contains( QStringLiteral(
"gradient_color2" ) ) )
1101 if ( props.contains( QStringLiteral(
"blur_radius" ) ) )
1103 blurRadius = props[QStringLiteral(
"blur_radius" )].toInt();
1105 if ( props.contains( QStringLiteral(
"use_whole_shape" ) ) )
1107 useWholeShape = props[QStringLiteral(
"use_whole_shape" )].toInt();
1109 if ( props.contains( QStringLiteral(
"max_distance" ) ) )
1111 maxDistance = props[QStringLiteral(
"max_distance" )].toDouble();
1113 if ( props.contains( QStringLiteral(
"offset" ) ) )
1132 if ( props.contains( QStringLiteral(
"offset_unit" ) ) )
1136 if ( props.contains( QStringLiteral(
"distance_unit" ) ) )
1140 if ( props.contains( QStringLiteral(
"offset_map_unit_scale" ) ) )
1144 if ( props.contains( QStringLiteral(
"distance_map_unit_scale" ) ) )
1148 if ( props.contains( QStringLiteral(
"ignore_rings" ) ) )
1150 sl->setIgnoreRings( props[QStringLiteral(
"ignore_rings" )].toInt() );
1154 sl->setColorRamp( gradientRamp );
1157 sl->restoreOldDataDefinedProperties( props );
1159 return sl.release();
1164 return QStringLiteral(
"ShapeburstFill" );
1169 if ( mGradientRamp.get() == ramp )
1172 mGradientRamp.reset( ramp );
1175void QgsShapeburstFillSymbolLayer::applyDataDefinedSymbology(
QgsSymbolRenderContext &context, QColor &color, QColor &color2,
int &blurRadius,
bool &useWholeShape,
1176 double &maxDistance,
bool &ignoreRings )
1233 selColor.setAlphaF( context.
opacity() );
1234 mSelBrush = QBrush( selColor );
1253 p->setBrush( mSelBrush );
1254 QPointF
offset = mOffset;
1289 int outputPixelMaxDist = 0;
1297 std::unique_ptr< QgsGradientColorRamp > twoColorGradientRamp;
1300 twoColorGradientRamp = std::make_unique< QgsGradientColorRamp >( color1,
color2 );
1304 p->setPen( QPen( Qt::NoPen ) );
1309 int pointsWidth =
static_cast< int >( std::round( points.boundingRect().width() ) );
1310 int pointsHeight =
static_cast< int >( std::round( points.boundingRect().height() ) );
1311 int imWidth = pointsWidth + ( sideBuffer * 2 );
1312 int imHeight = pointsHeight + ( sideBuffer * 2 );
1318 std::unique_ptr< QImage > fillImage = std::make_unique< QImage >( imWidth,
1319 imHeight, QImage::Format_ARGB32_Premultiplied );
1320 if ( fillImage->isNull() )
1330 std::unique_ptr< QImage > alphaImage = std::make_unique< QImage >( fillImage->width(), fillImage->height(), QImage::Format_ARGB32_Premultiplied );
1331 if ( alphaImage->isNull() )
1343 fillImage->fill( Qt::black );
1349 alphaImage->fill( Qt::transparent );
1355 QPainter imgPainter;
1356 imgPainter.begin( alphaImage.get() );
1357 imgPainter.setRenderHint( QPainter::Antialiasing,
true );
1358 imgPainter.setBrush( QBrush( Qt::white ) );
1359 imgPainter.setPen( QPen( Qt::black ) );
1360 imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1369 imgPainter.begin( fillImage.get() );
1372 imgPainter.drawImage( 0, 0, *alphaImage );
1379 imgPainter.setBrush( QBrush( Qt::white ) );
1380 imgPainter.setPen( QPen( Qt::black ) );
1381 imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1390 double *dtArray = distanceTransform( fillImage.get(), context.
renderContext() );
1410 imgPainter.begin( fillImage.get() );
1411 imgPainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
1412 imgPainter.drawImage( 0, 0, *alphaImage );
1420 QPointF
offset = mOffset;
1437 p->drawImage( points.boundingRect().left() - sideBuffer, points.boundingRect().top() - sideBuffer, *fillImage );
1448void QgsShapeburstFillSymbolLayer::distanceTransform1d(
double *f,
int n,
int *v,
double *z,
double *d )
1454 for (
int q = 1; q <= n - 1; q++ )
1456 double s = ( ( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1460 s = ( ( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1469 for (
int q = 0; q <= n - 1; q++ )
1471 while ( z[k + 1] < q )
1473 d[q] = ( q - v[k] ) * ( q - v[k] ) + f[v[k]];
1478void QgsShapeburstFillSymbolLayer::distanceTransform2d(
double *im,
int width,
int height,
QgsRenderContext &context )
1480 int maxDimension = std::max( width, height );
1481 double *f =
new double[ maxDimension ];
1482 int *v =
new int[ maxDimension ];
1483 double *z =
new double[ maxDimension + 1 ];
1484 double *d =
new double[ maxDimension ];
1487 for (
int x = 0; x < width; x++ )
1492 for (
int y = 0; y < height; y++ )
1494 f[y] = im[ x + y * width ];
1496 distanceTransform1d( f, height, v, z, d );
1497 for (
int y = 0; y < height; y++ )
1499 im[ x + y * width ] = d[y];
1504 for (
int y = 0; y < height; y++ )
1509 for (
int x = 0; x < width; x++ )
1511 f[x] = im[ x + y * width ];
1513 distanceTransform1d( f, width, v, z, d );
1514 for (
int x = 0; x < width; x++ )
1516 im[ x + y * width ] = d[x];
1527double *QgsShapeburstFillSymbolLayer::distanceTransform( QImage *im,
QgsRenderContext &context )
1529 int width = im->width();
1530 int height = im->height();
1532 double *dtArray =
new double[width * height];
1537 for (
int heightIndex = 0; heightIndex < height; ++heightIndex )
1542 const QRgb *scanLine =
reinterpret_cast< const QRgb *
>( im->constScanLine( heightIndex ) );
1543 for (
int widthIndex = 0; widthIndex < width; ++widthIndex )
1545 tmpRgb = scanLine[widthIndex];
1546 if ( qRed( tmpRgb ) == 0 )
1554 dtArray[ idx ] =
INF;
1561 distanceTransform2d( dtArray, width, height, context );
1566void QgsShapeburstFillSymbolLayer::dtArrayToQImage(
double *array, QImage *im,
QgsColorRamp *ramp,
QgsRenderContext &context,
bool useWholeShape,
int maxPixelDistance )
1568 int width = im->width();
1569 int height = im->height();
1572 double maxDistanceValue;
1577 double dtMaxValue = array[0];
1578 for (
int i = 1; i < ( width * height ); ++i )
1580 if ( array[i] > dtMaxValue )
1582 dtMaxValue = array[i];
1587 maxDistanceValue = std::sqrt( dtMaxValue );
1592 maxDistanceValue = maxPixelDistance;
1597 double squaredVal = 0;
1600 for (
int heightIndex = 0; heightIndex < height; ++heightIndex )
1605 QRgb *scanLine =
reinterpret_cast< QRgb *
>( im->scanLine( heightIndex ) );
1606 for (
int widthIndex = 0; widthIndex < width; ++widthIndex )
1609 squaredVal = array[idx];
1612 if ( maxDistanceValue > 0 )
1614 pixVal = squaredVal > 0 ? std::min( ( std::sqrt( squaredVal ) / maxDistanceValue ), 1.0 ) : 0;
1623 scanLine[widthIndex] = qPremultiply( ramp->
color( pixVal ).rgba() );
1634 map[QStringLiteral(
"color_type" )] = QString::number(
static_cast< int >( mColorType ) );
1635 map[QStringLiteral(
"blur_radius" )] = QString::number( mBlurRadius );
1636 map[QStringLiteral(
"use_whole_shape" )] = QString::number( mUseWholeShape );
1637 map[QStringLiteral(
"max_distance" )] = QString::number( mMaxDistance );
1640 map[QStringLiteral(
"ignore_rings" )] = QString::number( mIgnoreRings );
1644 if ( mGradientRamp )
1646#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
1647 map.unite( mGradientRamp->properties() );
1649 map.insert( mGradientRamp->properties() );
1658 std::unique_ptr< QgsShapeburstFillSymbolLayer > sl = std::make_unique< QgsShapeburstFillSymbolLayer >(
mColor, mColor2, mColorType, mBlurRadius, mUseWholeShape, mMaxDistance );
1659 if ( mGradientRamp )
1661 sl->setColorRamp( mGradientRamp->clone() );
1663 sl->setDistanceUnit( mDistanceUnit );
1664 sl->setDistanceMapUnitScale( mDistanceMapUnitScale );
1665 sl->setIgnoreRings( mIgnoreRings );
1666 sl->setOffset( mOffset );
1667 sl->setOffsetUnit( mOffsetUnit );
1668 sl->setOffsetMapUnitScale( mOffsetMapUnitScale );
1671 return sl.release();
1676 double offsetBleed = context.
convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale );
1687 mDistanceUnit = unit;
1693 if ( mDistanceUnit == mOffsetUnit )
1695 return mDistanceUnit;
1708 mDistanceMapUnitScale = scale;
1709 mOffsetMapUnitScale = scale;
1714 if ( mDistanceMapUnitScale == mOffsetMapUnitScale )
1716 return mDistanceMapUnitScale;
1741 p->setPen( QPen( Qt::NoPen ) );
1743 QTransform bkTransform =
mBrush.transform();
1747 QTransform t =
mBrush.transform();
1748 t.translate( leftCorner.x(), leftCorner.y() );
1749 mBrush.setTransform( t );
1753 QTransform t =
mBrush.transform();
1754 t.translate( 0, 0 );
1755 mBrush.setTransform( t );
1761 p->setBrush( QBrush( selColor ) );
1767 QTransform t =
mBrush.transform();
1769 mBrush.setTransform( t );
1774 mBrush.setTransform( bkTransform );
1810 return Qt::SolidLine;
1814 return Qt::SolidLine;
1818 return mStroke->dxfPenStyle();
1854 , mPatternWidth( width )
1858 mColor = QColor( 255, 255, 255 );
1864 , mPatternWidth( width )
1865 , mSvgData( svgData )
1870 mColor = QColor( 255, 255, 255 );
1871 setDefaultSvgParams();
1879 mPatternWidthUnit = unit;
1880 mSvgStrokeWidthUnit = unit;
1883 mStroke->setOutputUnit( unit );
1889 if ( mPatternWidthUnit != unit || mSvgStrokeWidthUnit != unit ||
mStrokeWidthUnit != unit )
1899 mPatternWidthMapUnitScale = scale;
1900 mSvgStrokeWidthMapUnitScale = scale;
1906 mPatternWidthMapUnitScale == mSvgStrokeWidthMapUnitScale &&
1909 return mPatternWidthMapUnitScale;
1919 mSvgFilePath = svgPath;
1920 setDefaultSvgParams();
1930 if (
properties.contains( QStringLiteral(
"width" ) ) )
1932 width =
properties[QStringLiteral(
"width" )].toDouble();
1934 if (
properties.contains( QStringLiteral(
"svgFile" ) ) )
1938 if (
properties.contains( QStringLiteral(
"angle" ) ) )
1943 std::unique_ptr< QgsSVGFillSymbolLayer > symbolLayer;
1946 symbolLayer = std::make_unique< QgsSVGFillSymbolLayer >(
svgFilePath, width,
angle );
1950 if (
properties.contains( QStringLiteral(
"data" ) ) )
1952 data = QByteArray::fromHex(
properties[QStringLiteral(
"data" )].toString().toLocal8Bit() );
1954 symbolLayer = std::make_unique< QgsSVGFillSymbolLayer >( data, width,
angle );
1958 if (
properties.contains( QStringLiteral(
"svgFillColor" ) ) )
1963 else if (
properties.contains( QStringLiteral(
"color" ) ) )
1967 if (
properties.contains( QStringLiteral(
"svgOutlineColor" ) ) )
1972 else if (
properties.contains( QStringLiteral(
"outline_color" ) ) )
1976 else if (
properties.contains( QStringLiteral(
"line_color" ) ) )
1980 if (
properties.contains( QStringLiteral(
"svgOutlineWidth" ) ) )
1983 symbolLayer->setSvgStrokeWidth(
properties[QStringLiteral(
"svgOutlineWidth" )].toDouble() );
1985 else if (
properties.contains( QStringLiteral(
"outline_width" ) ) )
1987 symbolLayer->setSvgStrokeWidth(
properties[QStringLiteral(
"outline_width" )].toDouble() );
1989 else if (
properties.contains( QStringLiteral(
"line_width" ) ) )
1991 symbolLayer->setSvgStrokeWidth(
properties[QStringLiteral(
"line_width" )].toDouble() );
1995 if (
properties.contains( QStringLiteral(
"pattern_width_unit" ) ) )
1999 if (
properties.contains( QStringLiteral(
"pattern_width_map_unit_scale" ) ) )
2003 if (
properties.contains( QStringLiteral(
"svg_outline_width_unit" ) ) )
2007 if (
properties.contains( QStringLiteral(
"svg_outline_width_map_unit_scale" ) ) )
2011 if (
properties.contains( QStringLiteral(
"outline_width_unit" ) ) )
2015 if (
properties.contains( QStringLiteral(
"outline_width_map_unit_scale" ) ) )
2020 if (
properties.contains( QStringLiteral(
"parameters" ) ) )
2026 symbolLayer->restoreOldDataDefinedProperties(
properties );
2028 return symbolLayer.release();
2033 QVariantMap::iterator it =
properties.find( QStringLiteral(
"svgFile" ) );
2045 return QStringLiteral(
"SVGFill" );
2048void QgsSVGFillSymbolLayer::applyPattern( QBrush &brush,
const QString &svgFilePath,
double patternWidth,
QgsUnitTypes::RenderUnit patternWidthUnit,
2049 const QColor &svgFillColor,
const QColor &svgStrokeColor,
double svgStrokeWidth,
2053 if ( mSvgViewBox.isNull() )
2060 if (
static_cast< int >( size ) < 1.0 || 10000.0 < size )
2062 brush.setTextureImage( QImage() );
2066 bool fitsInCache =
true;
2074 double hwRatio = 1.0;
2075 if ( patternPict.width() > 0 )
2077 hwRatio =
static_cast< double >( patternPict.height() ) /
static_cast< double >( patternPict.width() );
2079 patternImage = QImage(
static_cast< int >( size ),
static_cast< int >( size * hwRatio ), QImage::Format_ARGB32_Premultiplied );
2080 patternImage.fill( 0 );
2082 QPainter p( &patternImage );
2083 p.drawPicture( QPointF( size / 2, size * hwRatio / 2 ), patternPict );
2086 QTransform brushTransform;
2089 QImage transparentImage = patternImage.copy();
2091 brush.setTextureImage( transparentImage );
2095 brush.setTextureImage( patternImage );
2097 brush.setTransform( brushTransform );
2105 applyPattern(
mBrush, mSvgFilePath, mPatternWidth, mPatternWidthUnit,
mColor, mSvgStrokeColor, mSvgStrokeWidth, mSvgStrokeWidthUnit, context, mPatternWidthMapUnitScale, mSvgStrokeWidthMapUnitScale, evaluatedParameters );
2130 for (
auto ringIt = rings->constBegin(); ringIt != rings->constEnd(); ++ringIt )
2141 if ( !mSvgFilePath.isEmpty() )
2143 map.insert( QStringLiteral(
"svgFile" ), mSvgFilePath );
2147 map.insert( QStringLiteral(
"data" ), QString( mSvgData.toHex() ) );
2150 map.insert( QStringLiteral(
"width" ), QString::number( mPatternWidth ) );
2151 map.insert( QStringLiteral(
"angle" ), QString::number(
mAngle ) );
2156 map.insert( QStringLiteral(
"outline_width" ), QString::number( mSvgStrokeWidth ) );
2173 std::unique_ptr< QgsSVGFillSymbolLayer > clonedLayer;
2174 if ( !mSvgFilePath.isEmpty() )
2176 clonedLayer = std::make_unique< QgsSVGFillSymbolLayer >( mSvgFilePath, mPatternWidth,
mAngle );
2177 clonedLayer->setSvgFillColor(
mColor );
2178 clonedLayer->setSvgStrokeColor( mSvgStrokeColor );
2179 clonedLayer->setSvgStrokeWidth( mSvgStrokeWidth );
2183 clonedLayer = std::make_unique< QgsSVGFillSymbolLayer >( mSvgData, mPatternWidth,
mAngle );
2186 clonedLayer->setPatternWidthUnit( mPatternWidthUnit );
2187 clonedLayer->setPatternWidthMapUnitScale( mPatternWidthMapUnitScale );
2188 clonedLayer->setSvgStrokeWidthUnit( mSvgStrokeWidthUnit );
2189 clonedLayer->setSvgStrokeWidthMapUnitScale( mSvgStrokeWidthMapUnitScale );
2193 clonedLayer->setParameters( mParameters );
2197 clonedLayer->setSubSymbol( mStroke->clone() );
2201 return clonedLayer.release();
2206 QDomElement symbolizerElem = doc.createElement( QStringLiteral(
"se:PolygonSymbolizer" ) );
2207 if ( !props.value( QStringLiteral(
"uom" ), QString() ).toString().isEmpty() )
2208 symbolizerElem.setAttribute( QStringLiteral(
"uom" ), props.value( QStringLiteral(
"uom" ), QString() ).toString() );
2209 element.appendChild( symbolizerElem );
2213 QDomElement fillElem = doc.createElement( QStringLiteral(
"se:Fill" ) );
2214 symbolizerElem.appendChild( fillElem );
2216 QDomElement graphicFillElem = doc.createElement( QStringLiteral(
"se:GraphicFill" ) );
2217 fillElem.appendChild( graphicFillElem );
2219 QDomElement graphicElem = doc.createElement( QStringLiteral(
"se:Graphic" ) );
2220 graphicFillElem.appendChild( graphicElem );
2222 if ( !mSvgFilePath.isEmpty() )
2233 symbolizerElem.appendChild( doc.createComment( QStringLiteral(
"SVG from data not implemented yet" ) ) );
2239 double angle = props.value( QStringLiteral(
"angle" ), QStringLiteral(
"0" ) ).toDouble( &ok );
2242 angleFunc = QStringLiteral(
"%1 + %2" ).arg( props.value( QStringLiteral(
"angle" ), QStringLiteral(
"0" ) ).toString() ).arg(
mAngle );
2255 mStroke->toSld( doc, element, props );
2267 return mStroke.get();
2274 mStroke.reset(
nullptr );
2287 mStroke.reset( lineSymbol );
2297 if ( mStroke && mStroke->symbolLayer( 0 ) )
2299 double subLayerBleed = mStroke->symbolLayer( 0 )->estimateMaxBleed( context );
2300 return subLayerBleed;
2310 return QColor( Qt::black );
2312 return mStroke->color();
2319 attr.unite( mStroke->usedAttributes( context ) );
2327 if ( mStroke && mStroke->hasDataDefinedProperties() )
2334 QString path, mimeType;
2336 Qt::PenStyle penStyle;
2337 double size, strokeWidth;
2339 QDomElement fillElem = element.firstChildElement( QStringLiteral(
"Fill" ) );
2340 if ( fillElem.isNull() )
2343 QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral(
"GraphicFill" ) );
2344 if ( graphicFillElem.isNull() )
2347 QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral(
"Graphic" ) );
2348 if ( graphicElem.isNull() )
2354 if ( mimeType != QLatin1String(
"image/svg+xml" ) )
2359 double scaleFactor = 1.0;
2360 const QString uom = element.attribute( QStringLiteral(
"uom" ) );
2362 size = size * scaleFactor;
2363 strokeWidth = strokeWidth * scaleFactor;
2370 double d = angleFunc.toDouble( &ok );
2375 std::unique_ptr< QgsSVGFillSymbolLayer > sl = std::make_unique< QgsSVGFillSymbolLayer >( path, size,
angle );
2376 sl->setOutputUnit( sldUnitSize );
2379 sl->setSvgStrokeWidth( strokeWidth );
2382 QDomElement strokeElem = element.firstChildElement( QStringLiteral(
"Stroke" ) );
2383 if ( !strokeElem.isNull() )
2394 return sl.release();
2412 double width = mPatternWidth;
2418 QString svgFile = mSvgFilePath;
2437 double strokeWidth = mSvgStrokeWidth;
2446 mSvgStrokeWidthUnit, context, mPatternWidthMapUnitScale, mSvgStrokeWidthMapUnitScale, evaluatedParameters );
2450void QgsSVGFillSymbolLayer::storeViewBox()
2452 if ( !mSvgData.isEmpty() )
2454 QSvgRenderer r( mSvgData );
2457 mSvgViewBox = r.viewBoxF();
2462 mSvgViewBox = QRectF();
2465void QgsSVGFillSymbolLayer::setDefaultSvgParams()
2467 if ( mSvgFilePath.isEmpty() )
2472 bool hasFillParam, hasFillOpacityParam, hasStrokeParam, hasStrokeWidthParam, hasStrokeOpacityParam;
2473 bool hasDefaultFillColor, hasDefaultFillOpacity, hasDefaultStrokeColor, hasDefaultStrokeWidth, hasDefaultStrokeOpacity;
2474 QColor defaultFillColor, defaultStrokeColor;
2475 double defaultStrokeWidth, defaultFillOpacity, defaultStrokeOpacity;
2477 hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
2478 hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2479 hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
2480 hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity );
2482 double newFillOpacity = hasFillOpacityParam ?
mColor.alphaF() : 1.0;
2483 double newStrokeOpacity = hasStrokeOpacityParam ? mSvgStrokeColor.alphaF() : 1.0;
2485 if ( hasDefaultFillColor )
2487 mColor = defaultFillColor;
2488 mColor.setAlphaF( newFillOpacity );
2490 if ( hasDefaultFillOpacity )
2492 mColor.setAlphaF( defaultFillOpacity );
2494 if ( hasDefaultStrokeColor )
2496 mSvgStrokeColor = defaultStrokeColor;
2497 mSvgStrokeColor.setAlphaF( newStrokeOpacity );
2499 if ( hasDefaultStrokeOpacity )
2501 mSvgStrokeColor.setAlphaF( defaultStrokeOpacity );
2503 if ( hasDefaultStrokeWidth )
2505 mSvgStrokeWidth = defaultStrokeWidth;
2526 mFillLineSymbol->setWidth( w );
2532 mFillLineSymbol->setColor(
c );
2538 return mFillLineSymbol ? mFillLineSymbol->color() :
mColor;
2550 mFillLineSymbol.reset( qgis::down_cast<QgsLineSymbol *>( symbol ) );
2559 return mFillLineSymbol.get();
2565 if ( mFillLineSymbol )
2566 attr.unite( mFillLineSymbol->usedAttributes( context ) );
2574 if ( mFillLineSymbol && mFillLineSymbol->hasDataDefinedProperties() )
2597 mDistanceUnit = unit;
2598 mLineWidthUnit = unit;
2601 if ( mFillLineSymbol )
2602 mFillLineSymbol->setOutputUnit( unit );
2625 mDistanceMapUnitScale = scale;
2626 mLineWidthMapUnitScale = scale;
2627 mOffsetMapUnitScale = scale;
2633 mDistanceMapUnitScale == mLineWidthMapUnitScale &&
2634 mLineWidthMapUnitScale == mOffsetMapUnitScale )
2636 return mDistanceMapUnitScale;
2643 std::unique_ptr< QgsLinePatternFillSymbolLayer > patternLayer = std::make_unique< QgsLinePatternFillSymbolLayer >();
2649 QColor
color( Qt::black );
2652 if (
properties.contains( QStringLiteral(
"lineangle" ) ) )
2657 else if (
properties.contains( QStringLiteral(
"angle" ) ) )
2661 patternLayer->setLineAngle(
lineAngle );
2663 if (
properties.contains( QStringLiteral(
"distance" ) ) )
2667 patternLayer->setDistance(
distance );
2669 if (
properties.contains( QStringLiteral(
"linewidth" ) ) )
2674 else if (
properties.contains( QStringLiteral(
"outline_width" ) ) )
2678 else if (
properties.contains( QStringLiteral(
"line_width" ) ) )
2682 patternLayer->setLineWidth(
lineWidth );
2684 if (
properties.contains( QStringLiteral(
"color" ) ) )
2688 else if (
properties.contains( QStringLiteral(
"outline_color" ) ) )
2692 else if (
properties.contains( QStringLiteral(
"line_color" ) ) )
2696 patternLayer->setColor(
color );
2698 if (
properties.contains( QStringLiteral(
"offset" ) ) )
2702 patternLayer->setOffset(
offset );
2705 if (
properties.contains( QStringLiteral(
"distance_unit" ) ) )
2709 if (
properties.contains( QStringLiteral(
"distance_map_unit_scale" ) ) )
2713 if (
properties.contains( QStringLiteral(
"line_width_unit" ) ) )
2717 else if (
properties.contains( QStringLiteral(
"outline_width_unit" ) ) )
2721 if (
properties.contains( QStringLiteral(
"line_width_map_unit_scale" ) ) )
2725 if (
properties.contains( QStringLiteral(
"offset_unit" ) ) )
2729 if (
properties.contains( QStringLiteral(
"offset_map_unit_scale" ) ) )
2733 if (
properties.contains( QStringLiteral(
"outline_width_unit" ) ) )
2737 if (
properties.contains( QStringLiteral(
"outline_width_map_unit_scale" ) ) )
2741 if (
properties.contains( QStringLiteral(
"coordinate_reference" ) ) )
2745 if (
properties.contains( QStringLiteral(
"clip_mode" ) ) )
2750 patternLayer->restoreOldDataDefinedProperties(
properties );
2752 return patternLayer.release();
2757 return QStringLiteral(
"LinePatternFill" );
2760void QgsLinePatternFillSymbolLayer::applyPattern(
const QgsSymbolRenderContext &context, QBrush &brush,
double lineAngle,
double distance )
2762 mBrush.setTextureImage( QImage() );
2764 if ( !mFillLineSymbol )
2769 std::unique_ptr< QgsLineSymbol > fillLineSymbol( mFillLineSymbol->clone() );
2770 if ( !fillLineSymbol )
2786 outputPixelOffset = std::fmod( outputPixelOffset, outputPixelDist );
2787 if ( outputPixelOffset > outputPixelDist / 2.0 )
2788 outputPixelOffset -= outputPixelDist;
2792 double outputPixelBleed = 0;
2793 double outputPixelInterval = 0;
2794 for (
int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2798 outputPixelBleed = std::max( outputPixelBleed, outputPixelLayerBleed );
2801 if ( markerLineLayer )
2810 outputPixelInterval = std::max( outputPixelInterval, outputPixelLayerInterval );
2814 if ( outputPixelInterval > 0 )
2818 double intervalScale = std::round( outputPixelInterval ) / outputPixelInterval;
2819 outputPixelInterval = std::round( outputPixelInterval );
2821 for (
int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2826 if ( markerLineLayer )
2840 height = outputPixelDist;
2841 width = outputPixelInterval > 0 ? outputPixelInterval : height;
2845 width = outputPixelDist;
2846 height = outputPixelInterval > 0 ? outputPixelInterval : width;
2850 height = outputPixelDist / std::cos(
lineAngle * M_PI / 180 );
2851 width = outputPixelDist / std::sin(
lineAngle * M_PI / 180 );
2854 lineAngle = 180 * std::atan2(
static_cast< double >( height ),
static_cast< double >( width ) ) / M_PI;
2860 height = std::abs( height );
2861 width = std::abs( width );
2863 outputPixelDist = std::abs( height * std::cos(
lineAngle * M_PI / 180 ) );
2867 int offsetHeight =
static_cast< int >( std::round( outputPixelOffset / std::cos(
lineAngle * M_PI / 180 ) ) );
2868 outputPixelOffset = offsetHeight * std::cos(
lineAngle * M_PI / 180 );
2877 int bufferMulti =
static_cast< int >( std::max( std::ceil( outputPixelBleed / width ), std::ceil( outputPixelBleed / width ) ) );
2881 bufferMulti = std::max( bufferMulti, 1 );
2883 int xBuffer = width * bufferMulti;
2884 int yBuffer = height * bufferMulti;
2885 int innerWidth = width;
2886 int innerHeight = height;
2887 width += 2 * xBuffer;
2888 height += 2 * yBuffer;
2891 if ( width > 10000 || height > 10000 || width == 0 || height == 0 )
2896 QImage patternImage( width, height, QImage::Format_ARGB32 );
2897 patternImage.fill( 0 );
2899 QPointF p1, p2, p3, p4, p5, p6;
2902 p1 = QPointF( 0, yBuffer );
2903 p2 = QPointF( width, yBuffer );
2904 p3 = QPointF( 0, yBuffer + innerHeight );
2905 p4 = QPointF( width, yBuffer + innerHeight );
2909 p1 = QPointF( xBuffer, height );
2910 p2 = QPointF( xBuffer, 0 );
2911 p3 = QPointF( xBuffer + innerWidth, height );
2912 p4 = QPointF( xBuffer + innerWidth, 0 );
2916 dx = outputPixelDist * std::cos( ( 90 -
lineAngle ) * M_PI / 180.0 );
2917 dy = outputPixelDist * std::sin( ( 90 -
lineAngle ) * M_PI / 180.0 );
2918 p1 = QPointF( 0, height );
2919 p2 = QPointF( width, 0 );
2920 p3 = QPointF( -dx, height - dy );
2921 p4 = QPointF( width - dx, -dy );
2922 p5 = QPointF( dx, height + dy );
2923 p6 = QPointF( width + dx, dy );
2927 dx = outputPixelDist * std::cos( ( 90 -
lineAngle ) * M_PI / 180.0 );
2928 dy = outputPixelDist * std::sin( ( 90 -
lineAngle ) * M_PI / 180.0 );
2929 p1 = QPointF( width, 0 );
2930 p2 = QPointF( 0, height );
2931 p3 = QPointF( width - dx, -dy );
2932 p4 = QPointF( -dx, height - dy );
2933 p5 = QPointF( width + dx, dy );
2934 p6 = QPointF( dx, height + dy );
2938 dy = outputPixelDist * std::cos( ( 180 -
lineAngle ) * M_PI / 180 );
2939 dx = outputPixelDist * std::sin( ( 180 -
lineAngle ) * M_PI / 180 );
2940 p1 = QPointF( 0, 0 );
2941 p2 = QPointF( width, height );
2942 p5 = QPointF( dx, -dy );
2943 p6 = QPointF( width + dx, height - dy );
2944 p3 = QPointF( -dx, dy );
2945 p4 = QPointF( width - dx, height + dy );
2949 dy = outputPixelDist * std::cos( ( 180 -
lineAngle ) * M_PI / 180 );
2950 dx = outputPixelDist * std::sin( ( 180 -
lineAngle ) * M_PI / 180 );
2951 p1 = QPointF( width, height );
2952 p2 = QPointF( 0, 0 );
2953 p5 = QPointF( width + dx, height - dy );
2954 p6 = QPointF( dx, -dy );
2955 p3 = QPointF( width - dx, height + dy );
2956 p4 = QPointF( -dx, dy );
2963 p3 = QPointF( tempPt.x(), tempPt.y() );
2965 p4 = QPointF( tempPt.x(), tempPt.y() );
2967 p5 = QPointF( tempPt.x(), tempPt.y() );
2969 p6 = QPointF( tempPt.x(), tempPt.y() );
2973 p1 = QPointF( tempPt.x(), tempPt.y() );
2975 p2 = QPointF( tempPt.x(), tempPt.y() );
2978 QPainter p( &patternImage );
2982 p.setRenderHint( QPainter::Antialiasing,
false );
2983 QPen pen( QColor( Qt::black ) );
2984 pen.setWidthF( 0.1 );
2985 pen.setCapStyle( Qt::FlatCap );
2990 QPolygon polygon = QPolygon() << QPoint( 0, 0 ) << QPoint( width - 1, 0 ) << QPoint( width - 1, height - 1 ) << QPoint( 0, height - 1 ) << QPoint( 0, 0 );
2991 p.drawPolygon( polygon );
2993 polygon = QPolygon() << QPoint( xBuffer, yBuffer ) << QPoint( width - xBuffer - 1, yBuffer ) << QPoint( width - xBuffer - 1, height - yBuffer - 1 ) << QPoint( xBuffer, height - yBuffer - 1 ) << QPoint( xBuffer, yBuffer );
2994 p.drawPolygon( polygon );
3000 p.setRenderHint( QPainter::Antialiasing,
true );
3012 fillLineSymbol->startRender( lineRenderContext, context.
fields() );
3014 QVector<QPolygonF> polygons;
3015 polygons.append( QPolygonF() << p1 << p2 );
3016 polygons.append( QPolygonF() << p3 << p4 );
3019 polygons.append( QPolygonF() << p5 << p6 );
3022 for (
const QPolygonF &polygon : std::as_const( polygons ) )
3024 fillLineSymbol->renderPolyline( polygon, context.
feature(), lineRenderContext, -1, context.
selected() );
3027 fillLineSymbol->stopRender( lineRenderContext );
3031 patternImage = patternImage.copy( xBuffer, yBuffer, patternImage.width() - 2 * xBuffer, patternImage.height() - 2 * yBuffer );
3036 QImage transparentImage = patternImage.copy();
3038 brush.setTextureImage( transparentImage );
3042 brush.setTextureImage( patternImage );
3045 QTransform brushTransform;
3046 brush.setTransform( brushTransform );
3054 || mFillLineSymbol->hasDataDefinedProperties()
3058 if ( mRenderUsingLines )
3060 if ( mFillLineSymbol )
3066 applyPattern( context,
mBrush, mLineAngle, mDistance );
3072 if ( mRenderUsingLines && mFillLineSymbol )
3080 if ( !mRenderUsingLines )
3114 outputPixelOffset = std::fmod( outputPixelOffset, outputPixelDistance );
3115 if ( outputPixelOffset > outputPixelDistance / 2.0 )
3116 outputPixelOffset -= outputPixelDistance;
3118 p->setPen( QPen( Qt::NoPen ) );
3123 p->setBrush( QBrush( selColor ) );
3147 std::unique_ptr< QgsPolygon > shapePolygon;
3148 std::unique_ptr< QgsGeometryEngine > shapeEngine;
3156 shapePolygon = std::make_unique< QgsPolygon >();
3160 for (
const QPolygonF &ring : *rings )
3166 shapeEngine->prepareGeometry();
3173 path.addPolygon( points );
3176 for (
const QPolygonF &ring : *rings )
3178 path.addPolygon( ring );
3181 p->setClipPath( path, Qt::IntersectClip );
3187 const QRectF boundingRect = points.boundingRect();
3189 QTransform invertedRotateTransform;
3195 QTransform transform;
3196 if ( applyBrushTransform )
3199 transform.translate( -boundingRect.center().x(),
3200 -boundingRect.center().y() );
3202 transform.translate( boundingRect.center().x(),
3203 boundingRect.center().y() );
3211 const QRectF transformedBounds = transform.map( points ).boundingRect();
3215 left = transformedBounds.left() - buffer * 2;
3216 top = transformedBounds.top() - buffer * 2;
3217 right = transformedBounds.right() + buffer * 2;
3218 bottom = transformedBounds.bottom() + buffer * 2;
3219 invertedRotateTransform = transform.inverted();
3221 if ( !applyBrushTransform )
3223 top -= transformedBounds.top() - ( outputPixelDistance * std::floor( transformedBounds.top() / outputPixelDistance ) );
3228 const bool needsExpressionContext = mFillLineSymbol->hasDataDefinedProperties();
3233 int currentLine = 0;
3234 for (
double currentY = top; currentY <= bottom; currentY += outputPixelDistance )
3239 if ( needsExpressionContext )
3243 double y1 = currentY;
3245 double y2 = currentY;
3246 invertedRotateTransform.map( left, currentY - outputPixelOffset, &x1, &y1 );
3247 invertedRotateTransform.map( right, currentY - outputPixelOffset, &x2, &y2 );
3252 std::unique_ptr< QgsAbstractGeometry > intersection( shapeEngine->intersection( &ls ) );
3253 for (
auto it = intersection->const_parts_begin(); it != intersection->const_parts_end(); ++it )
3255 if (
const QgsLineString *ls = qgsgeometry_cast< const QgsLineString * >( *it ) )
3263 mFillLineSymbol->renderPolyline( QPolygonF() << QPointF( x1, y1 ) << QPointF( x2, y2 ), context.
feature(), context.
renderContext() );
3275 map.insert( QStringLiteral(
"angle" ), QString::number( mLineAngle ) );
3276 map.insert( QStringLiteral(
"distance" ), QString::number( mDistance ) );
3277 map.insert( QStringLiteral(
"line_width" ), QString::number( mLineWidth ) );
3279 map.insert( QStringLiteral(
"offset" ), QString::number( mOffset ) );
3295 if ( mFillLineSymbol )
3306 QDomElement symbolizerElem = doc.createElement( QStringLiteral(
"se:PolygonSymbolizer" ) );
3307 if ( !props.value( QStringLiteral(
"uom" ), QString() ).toString().isEmpty() )
3308 symbolizerElem.setAttribute( QStringLiteral(
"uom" ), props.value( QStringLiteral(
"uom" ), QString() ).toString() );
3309 element.appendChild( symbolizerElem );
3314 QDomElement fillElem = doc.createElement( QStringLiteral(
"se:Fill" ) );
3315 symbolizerElem.appendChild( fillElem );
3317 QDomElement graphicFillElem = doc.createElement( QStringLiteral(
"se:GraphicFill" ) );
3318 fillElem.appendChild( graphicFillElem );
3320 QDomElement graphicElem = doc.createElement( QStringLiteral(
"se:Graphic" ) );
3321 graphicFillElem.appendChild( graphicElem );
3324 QColor lineColor = mFillLineSymbol ? mFillLineSymbol->color() : QColor();
3325 double lineWidth = mFillLineSymbol ? mFillLineSymbol->width() : 0.0;
3333 double angle = props.value( QStringLiteral(
"angle" ), QStringLiteral(
"0" ) ).toDouble( &ok );
3336 angleFunc = QStringLiteral(
"%1 + %2" ).arg( props.value( QStringLiteral(
"angle" ), QStringLiteral(
"0" ) ).toString() ).arg( mLineAngle );
3340 angleFunc = QString::number(
angle + mLineAngle );
3345 QPointF lineOffset( std::sin( mLineAngle ) * mOffset, std::cos( mLineAngle ) * mOffset );
3352 QString featureStyle;
3353 featureStyle.append(
"Brush(" );
3354 featureStyle.append( QStringLiteral(
"fc:%1" ).arg(
mColor.name() ) );
3355 featureStyle.append( QStringLiteral(
",bc:%1" ).arg( QLatin1String(
"#00000000" ) ) );
3356 featureStyle.append(
",id:\"ogr-brush-2\"" );
3357 featureStyle.append( QStringLiteral(
",a:%1" ).arg( mLineAngle ) );
3358 featureStyle.append( QStringLiteral(
",s:%1" ).arg( mLineWidth * widthScaleFactor ) );
3359 featureStyle.append(
",dx:0mm" );
3360 featureStyle.append( QStringLiteral(
",dy:%1mm" ).arg( mDistance * widthScaleFactor ) );
3361 featureStyle.append(
')' );
3362 return featureStyle;
3368 && ( !mFillLineSymbol || !mFillLineSymbol->hasDataDefinedProperties() ) )
3393 Qt::PenStyle lineStyle;
3395 QDomElement fillElem = element.firstChildElement( QStringLiteral(
"Fill" ) );
3396 if ( fillElem.isNull() )
3399 QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral(
"GraphicFill" ) );
3400 if ( graphicFillElem.isNull() )
3403 QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral(
"Graphic" ) );
3404 if ( graphicElem.isNull() )
3410 if ( name != QLatin1String(
"horline" ) )
3418 double d = angleFunc.toDouble( &ok );
3427 offset = std::sqrt( std::pow( vectOffset.x(), 2 ) + std::pow( vectOffset.y(), 2 ) );
3430 double scaleFactor = 1.0;
3431 const QString uom = element.attribute( QStringLiteral(
"uom" ) );
3433 size = size * scaleFactor;
3436 std::unique_ptr< QgsLinePatternFillSymbolLayer > sl = std::make_unique< QgsLinePatternFillSymbolLayer >();
3437 sl->setOutputUnit( sldUnitSize );
3438 sl->setColor( lineColor );
3440 sl->setLineAngle(
angle );
3442 sl->setDistance( size );
3445 QDomElement strokeElem = element.firstChildElement( QStringLiteral(
"Stroke" ) );
3446 if ( !strokeElem.isNull() )
3457 return sl.release();
3557 std::unique_ptr< QgsPointPatternFillSymbolLayer > layer = std::make_unique< QgsPointPatternFillSymbolLayer >();
3558 if (
properties.contains( QStringLiteral(
"distance_x" ) ) )
3560 layer->setDistanceX(
properties[QStringLiteral(
"distance_x" )].toDouble() );
3562 if (
properties.contains( QStringLiteral(
"distance_y" ) ) )
3564 layer->setDistanceY(
properties[QStringLiteral(
"distance_y" )].toDouble() );
3566 if (
properties.contains( QStringLiteral(
"displacement_x" ) ) )
3568 layer->setDisplacementX(
properties[QStringLiteral(
"displacement_x" )].toDouble() );
3570 if (
properties.contains( QStringLiteral(
"displacement_y" ) ) )
3572 layer->setDisplacementY(
properties[QStringLiteral(
"displacement_y" )].toDouble() );
3574 if (
properties.contains( QStringLiteral(
"offset_x" ) ) )
3576 layer->setOffsetX(
properties[QStringLiteral(
"offset_x" )].toDouble() );
3578 if (
properties.contains( QStringLiteral(
"offset_y" ) ) )
3580 layer->setOffsetY(
properties[QStringLiteral(
"offset_y" )].toDouble() );
3583 if (
properties.contains( QStringLiteral(
"distance_x_unit" ) ) )
3587 if (
properties.contains( QStringLiteral(
"distance_x_map_unit_scale" ) ) )
3591 if (
properties.contains( QStringLiteral(
"distance_y_unit" ) ) )
3595 if (
properties.contains( QStringLiteral(
"distance_y_map_unit_scale" ) ) )
3599 if (
properties.contains( QStringLiteral(
"displacement_x_unit" ) ) )
3603 if (
properties.contains( QStringLiteral(
"displacement_x_map_unit_scale" ) ) )
3607 if (
properties.contains( QStringLiteral(
"displacement_y_unit" ) ) )
3611 if (
properties.contains( QStringLiteral(
"displacement_y_map_unit_scale" ) ) )
3615 if (
properties.contains( QStringLiteral(
"offset_x_unit" ) ) )
3619 if (
properties.contains( QStringLiteral(
"offset_x_map_unit_scale" ) ) )
3623 if (
properties.contains( QStringLiteral(
"offset_y_unit" ) ) )
3627 if (
properties.contains( QStringLiteral(
"offset_y_map_unit_scale" ) ) )
3632 if (
properties.contains( QStringLiteral(
"random_deviation_x" ) ) )
3634 layer->setMaximumRandomDeviationX(
properties[QStringLiteral(
"random_deviation_x" )].toDouble() );
3636 if (
properties.contains( QStringLiteral(
"random_deviation_y" ) ) )
3638 layer->setMaximumRandomDeviationY(
properties[QStringLiteral(
"random_deviation_y" )].toDouble() );
3640 if (
properties.contains( QStringLiteral(
"random_deviation_x_unit" ) ) )
3644 if (
properties.contains( QStringLiteral(
"random_deviation_x_map_unit_scale" ) ) )
3648 if (
properties.contains( QStringLiteral(
"random_deviation_y_unit" ) ) )
3652 if (
properties.contains( QStringLiteral(
"random_deviation_y_map_unit_scale" ) ) )
3656 unsigned long seed = 0;
3657 if (
properties.contains( QStringLiteral(
"seed" ) ) )
3663 std::random_device rd;
3664 std::mt19937 mt(
seed == 0 ? rd() :
seed );
3665 std::uniform_int_distribution<> uniformDist( 1, 999999999 );
3666 seed = uniformDist( mt );
3668 layer->setSeed(
seed );
3670 if (
properties.contains( QStringLiteral(
"outline_width_unit" ) ) )
3674 if (
properties.contains( QStringLiteral(
"outline_width_map_unit_scale" ) ) )
3678 if (
properties.contains( QStringLiteral(
"clip_mode" ) ) )
3682 if (
properties.contains( QStringLiteral(
"coordinate_reference" ) ) )
3687 if (
properties.contains( QStringLiteral(
"angle" ) ) )
3689 layer->setAngle(
properties[QStringLiteral(
"angle" )].toDouble() );
3692 layer->restoreOldDataDefinedProperties(
properties );
3694 return layer.release();
3699 return QStringLiteral(
"PointPatternFill" );
3702void QgsPointPatternFillSymbolLayer::applyPattern(
const QgsSymbolRenderContext &context, QBrush &brush,
double distanceX,
double distanceY,
3703 double displacementX,
double displacementY,
double offsetX,
double offsetY )
3710 double widthOffset = std::fmod(
3713 double heightOffset = std::fmod(
3717 if ( width > 10000 || height > 10000 )
3720 brush.setTextureImage( img );
3724 QImage patternImage( width, height, QImage::Format_ARGB32 );
3725 patternImage.fill( 0 );
3726 if ( patternImage.isNull() )
3728 brush.setTextureImage( QImage() );
3733 QPainter p( &patternImage );
3751 for (
double currentX = -width; currentX <= width * 2.0; currentX += width )
3753 for (
double currentY = -height; currentY <= height * 2.0; currentY += height )
3755 mMarkerSymbol->renderPoint( QPointF( currentX + widthOffset, currentY + heightOffset ), context.
feature(), pointRenderContext );
3766 for (
double currentX = -width; currentX <= width * 2.0; currentX += width )
3768 for (
double currentY = -height / 2.0; currentY <= height * 2.0; currentY += height )
3770 mMarkerSymbol->renderPoint( QPointF( currentX + widthOffset + displacementPixelX, currentY + heightOffset ), context.
feature(), pointRenderContext );
3774 for (
double currentX = -width / 2.0; currentX <= width * 2.0; currentX += width )
3776 for (
double currentY = -height; currentY <= height * 2.0; currentY += height / 2.0 )
3778 mMarkerSymbol->renderPoint( QPointF( currentX + widthOffset + ( std::fmod( currentY, height ) != 0 ? displacementPixelX : 0 ), currentY + heightOffset - displacementPixelY ), context.
feature(), pointRenderContext );
3787 QImage transparentImage = patternImage.copy();
3789 brush.setTextureImage( transparentImage );
3793 brush.setTextureImage( patternImage );
3795 QTransform brushTransform;
3796 brush.setTransform( brushTransform );
3814 if ( mRenderUsingMarkers )
3827 if ( mRenderUsingMarkers )
3849 if ( !mRenderUsingMarkers )
3892 const double widthOffset = std::fmod(
3904 const double heightOffset = std::fmod(
3930 p->setPen( QPen( Qt::NoPen ) );
3935 p->setBrush( QBrush( selColor ) );
3959 std::unique_ptr< QgsPolygon > shapePolygon;
3960 std::unique_ptr< QgsGeometryEngine > shapeEngine;
3967 shapePolygon = std::make_unique< QgsPolygon >();
3971 for (
const QPolygonF &ring : *rings )
3977 shapeEngine->prepareGeometry();
3984 path.addPolygon( points );
3987 for (
const QPolygonF &ring : *rings )
3989 path.addPolygon( ring );
3992 p->setClipPath( path, Qt::IntersectClip );
3998 const QRectF boundingRect = points.boundingRect();
4000 QTransform invertedRotateTransform;
4008 QTransform transform;
4009 if ( applyBrushTransform )
4012 transform.translate( -boundingRect.center().x(),
4013 -boundingRect.center().y() );
4014 transform.rotate( -
angle );
4015 transform.translate( boundingRect.center().x(),
4016 boundingRect.center().y() );
4021 transform.rotate( -
angle );
4024 const QRectF transformedBounds = transform.map( points ).boundingRect();
4025 left = transformedBounds.left() - 2 * width;
4026 top = transformedBounds.top() - 2 * height;
4027 right = transformedBounds.right() + 2 * width;
4028 bottom = transformedBounds.bottom() + 2 * height;
4029 invertedRotateTransform = transform.inverted();
4031 if ( !applyBrushTransform )
4033 left -= transformedBounds.left() - ( width * std::floor( transformedBounds.left() / width ) );
4034 top -= transformedBounds.top() - ( height * std::floor( transformedBounds.top() / height ) );
4039 left = boundingRect.left() - 2 * width;
4040 top = boundingRect.top() - 2 * height;
4041 right = boundingRect.right() + 2 * width;
4042 bottom = boundingRect.bottom() + 2 * height;
4044 if ( !applyBrushTransform )
4046 left -= boundingRect.left() - ( width * std::floor( boundingRect.left() / width ) );
4047 top -= boundingRect.top() - ( height * std::floor( boundingRect.top() / height ) );
4076 std::random_device rd;
4077 std::mt19937 mt(
seed == 0 ? rd() :
seed );
4078 std::uniform_real_distribution<> uniformDist( 0, 1 );
4084 const bool needsExpressionContext =
mMarkerSymbol->hasDataDefinedProperties();
4092 bool alternateColumn =
false;
4093 int currentCol = -3;
4094 for (
double currentX = left; currentX <= right; currentX += width, alternateColumn = !alternateColumn )
4099 if ( needsExpressionContext )
4102 bool alternateRow =
false;
4103 const double columnX = currentX + widthOffset;
4104 int currentRow = -3;
4105 for (
double currentY = top; currentY <= bottom; currentY += height, alternateRow = !alternateRow )
4110 double y = currentY + heightOffset;
4113 x += displacementPixelX;
4115 if ( !alternateColumn )
4116 y -= displacementPixelY;
4122 invertedRotateTransform.map( xx, yy, &x, &y );
4125 if ( useRandomShift )
4127 x += ( 2 * uniformDist( mt ) - 1 ) * maxRandomDeviationPixelX;
4128 y += ( 2 * uniformDist( mt ) - 1 ) * maxRandomDeviationPixelY;
4131 if ( needsExpressionContext )
4139 bool renderPoint =
true;
4147 renderPoint = shapeEngine->intersects( &p );
4157 renderPoint = shapeEngine->contains( markerBounds.
constGet() );
4159 renderPoint = shapeEngine->intersects( markerBounds.
constGet() );
4185 map.insert( QStringLiteral(
"distance_x" ), QString::number(
mDistanceX ) );
4186 map.insert( QStringLiteral(
"distance_y" ), QString::number(
mDistanceY ) );
4187 map.insert( QStringLiteral(
"displacement_x" ), QString::number(
mDisplacementX ) );
4188 map.insert( QStringLiteral(
"displacement_y" ), QString::number(
mDisplacementY ) );
4189 map.insert( QStringLiteral(
"offset_x" ), QString::number(
mOffsetX ) );
4190 map.insert( QStringLiteral(
"offset_y" ), QString::number(
mOffsetY ) );
4206 map.insert( QStringLiteral(
"random_deviation_x" ), QString::number(
mRandomDeviationX ) );
4207 map.insert( QStringLiteral(
"random_deviation_y" ), QString::number(
mRandomDeviationY ) );
4212 map.insert( QStringLiteral(
"seed" ), QString::number(
mSeed ) );
4213 map.insert( QStringLiteral(
"angle" ),
mAngle );
4232 for (
int i = 0; i <
mMarkerSymbol->symbolLayerCount(); i++ )
4234 QDomElement symbolizerElem = doc.createElement( QStringLiteral(
"se:PolygonSymbolizer" ) );
4235 if ( !props.value( QStringLiteral(
"uom" ), QString() ).toString().isEmpty() )
4236 symbolizerElem.setAttribute( QStringLiteral(
"uom" ), props.value( QStringLiteral(
"uom" ), QString() ).toString() );
4237 element.appendChild( symbolizerElem );
4242 QDomElement fillElem = doc.createElement( QStringLiteral(
"se:Fill" ) );
4243 symbolizerElem.appendChild( fillElem );
4245 QDomElement graphicFillElem = doc.createElement( QStringLiteral(
"se:GraphicFill" ) );
4246 fillElem.appendChild( graphicFillElem );
4253 symbolizerElem.appendChild( distanceElem );
4258 markerLayer->writeSldMarker( doc, graphicFillElem, props );
4262 QString errorMsg = QStringLiteral(
"QgsMarkerSymbolLayer expected, %1 found. Skip it." ).arg( layer->
layerType() );
4263 graphicFillElem.appendChild( doc.createComment( errorMsg ) );
4267 QString errorMsg = QStringLiteral(
"Missing point pattern symbol layer. Skip it." );
4268 graphicFillElem.appendChild( doc.createComment( errorMsg ) );
4358 attributes.unite(
mMarkerSymbol->usedAttributes( context ) );
4396 std::unique_ptr< QgsCentroidFillSymbolLayer > sl = std::make_unique< QgsCentroidFillSymbolLayer >();
4398 if (
properties.contains( QStringLiteral(
"point_on_surface" ) ) )
4399 sl->setPointOnSurface(
properties[QStringLiteral(
"point_on_surface" )].toInt() != 0 );
4400 if (
properties.contains( QStringLiteral(
"point_on_all_parts" ) ) )
4401 sl->setPointOnAllParts(
properties[QStringLiteral(
"point_on_all_parts" )].toInt() != 0 );
4402 if (
properties.contains( QStringLiteral(
"clip_points" ) ) )
4403 sl->setClipPoints(
properties[QStringLiteral(
"clip_points" )].toInt() != 0 );
4404 if (
properties.contains( QStringLiteral(
"clip_on_current_part_only" ) ) )
4405 sl->setClipOnCurrentPartOnly(
properties[QStringLiteral(
"clip_on_current_part_only" )].toInt() != 0 );
4407 sl->restoreOldDataDefinedProperties(
properties );
4409 return sl.release();
4414 return QStringLiteral(
"CentroidFill" );
4441 part.exterior = points;
4443 part.rings = *rings;
4450 mCurrentParts << part;
4455 const double prevOpacity =
mMarker->opacity();
4458 mMarker->setOpacity( prevOpacity );
4465 mCurrentParts.clear();
4472 const double prevOpacity =
mMarker->opacity();
4475 render( context, mCurrentParts, feature,
false );
4477 mMarker->setOpacity( prevOpacity );
4480void QgsCentroidFillSymbolLayer::render(
QgsRenderContext &context,
const QVector<QgsCentroidFillSymbolLayer::Part> &parts,
const QgsFeature &feature,
bool selected )
4489 QVector< QgsGeometry > geometryParts;
4490 geometryParts.reserve( parts.size() );
4491 QPainterPath globalPath;
4494 int maxAreaPartIdx = 0;
4496 for (
int i = 0; i < parts.size(); i++ )
4498 const Part part = parts[i];
4501 if ( !geom.
isNull() && !part.rings.empty() )
4503 QgsPolygon *poly = qgsgeometry_cast< QgsPolygon * >( geom.
get() );
4507 int area = poly->
area();
4509 if ( area > maxArea )
4519 globalPath.addPolygon( part.exterior );
4520 for (
const QPolygonF &ring : part.rings )
4522 globalPath.addPolygon( ring );
4527 for (
int i = 0; i < parts.size(); i++ )
4532 const Part part = parts[i];
4540 path.addPolygon( part.exterior );
4541 for (
const QPolygonF &ring : part.rings )
4543 path.addPolygon( ring );
4552 context.
painter()->setClipPath( path );
4572 map[QStringLiteral(
"point_on_surface" )] = QString::number(
mPointOnSurface );
4573 map[QStringLiteral(
"point_on_all_parts" )] = QString::number(
mPointOnAllParts );
4574 map[QStringLiteral(
"clip_points" )] = QString::number(
mClipPoints );
4581 std::unique_ptr< QgsCentroidFillSymbolLayer > x = std::make_unique< QgsCentroidFillSymbolLayer >();
4584 x->setSubSymbol(
mMarker->clone() );
4599 mMarker->toSld( doc, element, props );
4610 std::unique_ptr< QgsMarkerSymbol > marker(
new QgsMarkerSymbol( layers ) );
4612 std::unique_ptr< QgsCentroidFillSymbolLayer > sl = std::make_unique< QgsCentroidFillSymbolLayer >();
4613 sl->setSubSymbol( marker.release() );
4614 sl->setPointOnAllParts(
false );
4615 return sl.release();
4642 attributes.unite(
mMarker->usedAttributes( context ) );
4665 mMarker->setOutputUnit( unit );
4682 return mMarker->usesMapUnits();
4691 mMarker->setMapUnitScale( scale );
4699 return mMarker->mapUnitScale();
4709 , mImageFilePath( imageFilePath )
4726 if (
properties.contains( QStringLiteral(
"imageFile" ) ) )
4728 imagePath =
properties[QStringLiteral(
"imageFile" )].toString();
4730 if (
properties.contains( QStringLiteral(
"coordinate_mode" ) ) )
4734 if (
properties.contains( QStringLiteral(
"alpha" ) ) )
4736 alpha =
properties[QStringLiteral(
"alpha" )].toDouble();
4738 if (
properties.contains( QStringLiteral(
"offset" ) ) )