44#include <QPagedPaintDevice>
46#include <QSvgRenderer>
47#include <QDomDocument>
54 Qt::PenJoinStyle penJoinStyle )
55 : mBrushStyle( style )
56 , mStrokeColor( strokeColor )
57 , mStrokeStyle( strokeStyle )
58 , mStrokeWidth( strokeWidth )
59 , mPenJoinStyle( penJoinStyle )
103void QgsSimpleFillSymbolLayer::applyDataDefinedSymbology(
QgsSymbolRenderContext &context, QBrush &brush, QPen &pen, QPen &selPen )
128 penColor.setAlphaF( context.
opacity() * penColor.alphaF() );
129 pen.setColor( penColor );
137 double width = exprVal.toDouble( &ok );
141 pen.setWidthF( width );
142 selPen.setWidthF( width );
179 if ( props.contains( QStringLiteral(
"color" ) ) )
181 if ( props.contains( QStringLiteral(
"style" ) ) )
183 if ( props.contains( QStringLiteral(
"color_border" ) ) )
188 else if ( props.contains( QStringLiteral(
"outline_color" ) ) )
192 else if ( props.contains( QStringLiteral(
"line_color" ) ) )
197 if ( props.contains( QStringLiteral(
"style_border" ) ) )
202 else if ( props.contains( QStringLiteral(
"outline_style" ) ) )
206 else if ( props.contains( QStringLiteral(
"line_style" ) ) )
210 if ( props.contains( QStringLiteral(
"width_border" ) ) )
213 strokeWidth = props[QStringLiteral(
"width_border" )].toDouble();
215 else if ( props.contains( QStringLiteral(
"outline_width" ) ) )
217 strokeWidth = props[QStringLiteral(
"outline_width" )].toDouble();
219 else if ( props.contains( QStringLiteral(
"line_width" ) ) )
221 strokeWidth = props[QStringLiteral(
"line_width" )].toDouble();
223 if ( props.contains( QStringLiteral(
"offset" ) ) )
225 if ( props.contains( QStringLiteral(
"joinstyle" ) ) )
230 if ( props.contains( QStringLiteral(
"border_width_unit" ) ) )
234 else if ( props.contains( QStringLiteral(
"outline_width_unit" ) ) )
238 else if ( props.contains( QStringLiteral(
"line_width_unit" ) ) )
242 if ( props.contains( QStringLiteral(
"offset_unit" ) ) )
245 if ( props.contains( QStringLiteral(
"border_width_map_unit_scale" ) ) )
247 if ( props.contains( QStringLiteral(
"offset_map_unit_scale" ) ) )
250 sl->restoreOldDataDefinedProperties( props );
258 return QStringLiteral(
"SimpleFill" );
275 selColor.setAlphaF( context.
opacity() );
334 if (
mBrush.style() == Qt::SolidPattern ||
mBrush.style() == Qt::NoBrush || !
dynamic_cast<QPagedPaintDevice *
>( p->device() ) )
346 p->setPen( Qt::NoPen );
350 p->setBrush( Qt::NoBrush );
367 map[QStringLiteral(
"outline_width" )] = QString::number(
mStrokeWidth );
395 QDomElement symbolizerElem = doc.createElement( QStringLiteral(
"se:PolygonSymbolizer" ) );
396 if ( !props.value( QStringLiteral(
"uom" ), QString() ).toString().isEmpty() )
397 symbolizerElem.setAttribute( QStringLiteral(
"uom" ), props.value( QStringLiteral(
"uom" ), QString() ).toString() );
398 element.appendChild( symbolizerElem );
407 bool exportOk {
false };
411 if ( ! image.isNull() )
414 QDomElement fillElem = doc.createElement( QStringLiteral(
"se:Fill" ) );
415 symbolizerElem.appendChild( fillElem );
416 QDomElement graphicFillElem = doc.createElement( QStringLiteral(
"se:GraphicFill" ) );
417 fillElem.appendChild( graphicFillElem );
418 QDomElement graphicElem = doc.createElement( QStringLiteral(
"se:Graphic" ) );
419 graphicFillElem.appendChild( graphicElem );
421 const QFileInfo info { context.exportFilePath() };
422 QString pngPath { info.completeSuffix().isEmpty() ? context.exportFilePath() : context.exportFilePath().chopped( info.completeSuffix().length() ).append( QStringLiteral(
"png" ) ) };
424 image.save( pngPath );
439 const double alpha { props.value( QStringLiteral(
"alpha" ), QVariant() ).toDouble( &ok ) };
445 QDomElement fillElem = doc.createElement( QStringLiteral(
"se:Fill" ) );
446 symbolizerElem.appendChild( fillElem );
453 QDomElement strokeElem = doc.createElement( QStringLiteral(
"se:Stroke" ) );
454 symbolizerElem.appendChild( strokeElem );
458 const double alpha { props.value( QStringLiteral(
"alpha" ), QVariant() ).toDouble( &ok ) };
478 symbolStyle.append(
';' );
487 Qt::BrushStyle fillStyle;
491 QDomElement fillElem = element.firstChildElement( QStringLiteral(
"Fill" ) );
494 QDomElement strokeElem = element.firstChildElement( QStringLiteral(
"Stroke" ) );
500 double scaleFactor = 1.0;
501 const QString uom = element.attribute( QStringLiteral(
"uom" ) );
508 sl->setOutputUnit( sldUnitSize );
517 return penBleed + offsetBleed;
575 QPixmap pixmap( QSize( 32, 32 ) );
576 pixmap.fill( Qt::transparent );
578 painter.begin( &pixmap );
579 painter.setRenderHint( QPainter::Antialiasing );
587 std::unique_ptr< QgsSimpleFillSymbolLayer > layerClone(
clone() );
588 layerClone->setStrokeStyle( Qt::PenStyle::NoPen );
589 layerClone->drawPreviewIcon( symbolContext, pixmap.size() );
591 return pixmap.toImage();
599 : mGradientColorType( colorType )
600 , mGradientType( gradientType )
601 , mCoordinateMode( coordinateMode )
602 , mGradientSpread( spread )
603 , mReferencePoint1( QPointF( 0.5, 0 ) )
604 , mReferencePoint2( QPointF( 0.5, 1 ) )
625 bool refPoint1IsCentroid =
false;
627 bool refPoint2IsCentroid =
false;
632 if ( props.contains( QStringLiteral(
"type" ) ) )
634 if ( props.contains( QStringLiteral(
"coordinate_mode" ) ) )
636 if ( props.contains( QStringLiteral(
"spread" ) ) )
638 if ( props.contains( QStringLiteral(
"color_type" ) ) )
640 if ( props.contains( QStringLiteral(
"gradient_color" ) ) )
645 else if ( props.contains( QStringLiteral(
"color" ) ) )
649 if ( props.contains( QStringLiteral(
"gradient_color2" ) ) )
654 if ( props.contains( QStringLiteral(
"reference_point1" ) ) )
656 if ( props.contains( QStringLiteral(
"reference_point1_iscentroid" ) ) )
657 refPoint1IsCentroid = props[QStringLiteral(
"reference_point1_iscentroid" )].toInt();
658 if ( props.contains( QStringLiteral(
"reference_point2" ) ) )
660 if ( props.contains( QStringLiteral(
"reference_point2_iscentroid" ) ) )
661 refPoint2IsCentroid = props[QStringLiteral(
"reference_point2_iscentroid" )].toInt();
662 if ( props.contains( QStringLiteral(
"angle" ) ) )
663 angle = props[QStringLiteral(
"angle" )].toDouble();
665 if ( props.contains( QStringLiteral(
"offset" ) ) )
682 if ( props.contains( QStringLiteral(
"offset_unit" ) ) )
684 if ( props.contains( QStringLiteral(
"offset_map_unit_scale" ) ) )
687 sl->setReferencePoint1IsCentroid( refPoint1IsCentroid );
689 sl->setReferencePoint2IsCentroid( refPoint2IsCentroid );
690 sl->setAngle(
angle );
692 sl->setColorRamp( gradientRamp );
694 sl->restoreOldDataDefinedProperties( props );
712 return QStringLiteral(
"GradientFill" );
715void QgsGradientFillSymbolLayer::applyDataDefinedSymbology(
QgsSymbolRenderContext &context,
const QPolygonF &points )
760 if ( currentType == QObject::tr(
"linear" ) )
764 else if ( currentType == QObject::tr(
"radial" ) )
768 else if ( currentType == QObject::tr(
"conical" ) )
782 if ( currentCoordMode == QObject::tr(
"feature" ) )
786 else if ( currentCoordMode == QObject::tr(
"viewport" ) )
800 if ( currentSpread == QObject::tr(
"pad" ) )
804 else if ( currentSpread == QObject::tr(
"repeat" ) )
808 else if ( currentSpread == QObject::tr(
"reflect" ) )
855 if ( refPoint1IsCentroid || refPoint2IsCentroid )
860 QRectF bbox = points.boundingRect();
861 double centroidX = ( centroid.
x() - bbox.left() ) / bbox.width();
862 double centroidY = ( centroid.
y() - bbox.top() ) / bbox.height();
864 if ( refPoint1IsCentroid )
866 refPoint1X = centroidX;
867 refPoint1Y = centroidY;
869 if ( refPoint2IsCentroid )
871 refPoint2X = centroidX;
872 refPoint2Y = centroidY;
878 spread, QPointF( refPoint1X, refPoint1Y ), QPointF( refPoint2X, refPoint2Y ),
angle );
881QPointF QgsGradientFillSymbolLayer::rotateReferencePoint( QPointF refPoint,
double angle )
886 QLineF refLine = QLineF( QPointF( 0.5, 0.5 ), refPoint );
888 refLine.setAngle( refLine.angle() +
angle );
890 QPointF rotatedReferencePoint = refLine.p2();
892 if ( rotatedReferencePoint.x() > 1 )
893 rotatedReferencePoint.setX( 1 );
894 if ( rotatedReferencePoint.x() < 0 )
895 rotatedReferencePoint.setX( 0 );
896 if ( rotatedReferencePoint.y() > 1 )
897 rotatedReferencePoint.setY( 1 );
898 if ( rotatedReferencePoint.y() < 0 )
899 rotatedReferencePoint.setY( 0 );
901 return rotatedReferencePoint;
908 QPointF referencePoint1, QPointF referencePoint2,
const double angle )
913 QColor fillColor2 =
color2;
914 fillColor2.setAlphaF( context.
opacity() * fillColor2.alphaF() );
925 gradient = QLinearGradient( rotatedReferencePoint1, rotatedReferencePoint2 );
928 gradient = QRadialGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).length() );
931 gradient = QConicalGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).
angle() );
937 gradient.setCoordinateMode( QGradient::ObjectBoundingMode );
940 gradient.setCoordinateMode( QGradient::StretchToDeviceMode );
946 gradient.setSpread( QGradient::PadSpread );
949 gradient.setSpread( QGradient::ReflectSpread );
952 gradient.setSpread( QGradient::RepeatSpread );
968 gradient.setColorAt( 1.0, fillColor2 );
972 brush = QBrush( gradient );
979 selColor.setAlphaF( context.
opacity() );
996 applyDataDefinedSymbology( context, points );
1000 p->setPen( Qt::NoPen );
1033 map[QStringLiteral(
"color_type" )] = QString::number(
static_cast< int >(
mGradientColorType ) );
1034 map[QStringLiteral(
"type" )] = QString::number(
static_cast<int>(
mGradientType ) );
1035 map[QStringLiteral(
"coordinate_mode" )] = QString::number(
static_cast< int >(
mCoordinateMode ) );
1036 map[QStringLiteral(
"spread" )] = QString::number(
static_cast< int >(
mGradientSpread ) );
1041 map[QStringLiteral(
"angle" )] = QString::number(
mAngle );
1067 return sl.release();
1109 int blurRadius,
bool useWholeShape,
double maxDistance )
1110 : mBlurRadius( blurRadius )
1111 , mUseWholeShape( useWholeShape )
1112 , mMaxDistance( maxDistance )
1113 , mColorType( colorType )
1132 if ( props.contains( QStringLiteral(
"color_type" ) ) )
1136 if ( props.contains( QStringLiteral(
"shapeburst_color" ) ) )
1141 else if ( props.contains( QStringLiteral(
"color" ) ) )
1146 if ( props.contains( QStringLiteral(
"shapeburst_color2" ) ) )
1151 else if ( props.contains( QStringLiteral(
"gradient_color2" ) ) )
1155 if ( props.contains( QStringLiteral(
"blur_radius" ) ) )
1157 blurRadius = props[QStringLiteral(
"blur_radius" )].toInt();
1159 if ( props.contains( QStringLiteral(
"use_whole_shape" ) ) )
1161 useWholeShape = props[QStringLiteral(
"use_whole_shape" )].toInt();
1163 if ( props.contains( QStringLiteral(
"max_distance" ) ) )
1165 maxDistance = props[QStringLiteral(
"max_distance" )].toDouble();
1167 if ( props.contains( QStringLiteral(
"offset" ) ) )
1186 if ( props.contains( QStringLiteral(
"offset_unit" ) ) )
1190 if ( props.contains( QStringLiteral(
"distance_unit" ) ) )
1194 if ( props.contains( QStringLiteral(
"offset_map_unit_scale" ) ) )
1198 if ( props.contains( QStringLiteral(
"distance_map_unit_scale" ) ) )
1202 if ( props.contains( QStringLiteral(
"ignore_rings" ) ) )
1204 sl->setIgnoreRings( props[QStringLiteral(
"ignore_rings" )].toInt() );
1208 sl->setColorRamp( gradientRamp );
1211 sl->restoreOldDataDefinedProperties( props );
1213 return sl.release();
1218 return QStringLiteral(
"ShapeburstFill" );
1228 if ( mGradientRamp.get() == ramp )
1231 mGradientRamp.reset( ramp );
1234void QgsShapeburstFillSymbolLayer::applyDataDefinedSymbology(
QgsSymbolRenderContext &context, QColor &color, QColor &color2,
int &blurRadius,
bool &useWholeShape,
1235 double &maxDistance,
bool &ignoreRings )
1292 selColor.setAlphaF( context.
opacity() );
1293 mSelBrush = QBrush( selColor );
1310 if ( useSelectedColor )
1313 p->setBrush( mSelBrush );
1314 QPointF
offset = mOffset;
1349 int outputPixelMaxDist = 0;
1357 std::unique_ptr< QgsGradientColorRamp > twoColorGradientRamp;
1360 twoColorGradientRamp = std::make_unique< QgsGradientColorRamp >( color1,
color2 );
1364 p->setPen( QPen( Qt::NoPen ) );
1369 int pointsWidth =
static_cast< int >( std::round( points.boundingRect().width() ) );
1370 int pointsHeight =
static_cast< int >( std::round( points.boundingRect().height() ) );
1371 int imWidth = pointsWidth + ( sideBuffer * 2 );
1372 int imHeight = pointsHeight + ( sideBuffer * 2 );
1378 std::unique_ptr< QImage > fillImage = std::make_unique< QImage >( imWidth,
1379 imHeight, QImage::Format_ARGB32_Premultiplied );
1380 if ( fillImage->isNull() )
1390 std::unique_ptr< QImage > alphaImage = std::make_unique< QImage >( fillImage->width(), fillImage->height(), QImage::Format_ARGB32_Premultiplied );
1391 if ( alphaImage->isNull() )
1403 fillImage->fill( Qt::black );
1409 alphaImage->fill( Qt::transparent );
1415 QPainter imgPainter;
1416 imgPainter.begin( alphaImage.get() );
1417 imgPainter.setRenderHint( QPainter::Antialiasing,
true );
1418 imgPainter.setBrush( QBrush( Qt::white ) );
1419 imgPainter.setPen( QPen( Qt::black ) );
1420 imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1429 imgPainter.begin( fillImage.get() );
1432 imgPainter.drawImage( 0, 0, *alphaImage );
1439 imgPainter.setBrush( QBrush( Qt::white ) );
1440 imgPainter.setPen( QPen( Qt::black ) );
1441 imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1450 double *dtArray = distanceTransform( fillImage.get(), context.
renderContext() );
1470 imgPainter.begin( fillImage.get() );
1471 imgPainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
1472 imgPainter.drawImage( 0, 0, *alphaImage );
1480 QPointF
offset = mOffset;
1497 p->drawImage( points.boundingRect().left() - sideBuffer, points.boundingRect().top() - sideBuffer, *fillImage );
1508void QgsShapeburstFillSymbolLayer::distanceTransform1d(
double *f,
int n,
int *v,
double *z,
double *d )
1514 for (
int q = 1; q <= n - 1; q++ )
1516 double s = ( ( f[q] +
static_cast< double >( q ) * q ) - ( f[v[k]] + (
static_cast< double >( v[k] ) * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1520 s = ( ( f[q] +
static_cast< double >( q ) * q ) - ( f[v[k]] + (
static_cast< double >( v[k] ) * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1529 for (
int q = 0; q <= n - 1; q++ )
1531 while ( z[k + 1] < q )
1533 d[q] =
static_cast< double >( q - v[k] ) * ( q - v[k] ) + f[v[k]];
1538void QgsShapeburstFillSymbolLayer::distanceTransform2d(
double *im,
int width,
int height,
QgsRenderContext &context )
1540 int maxDimension = std::max( width, height );
1541 double *f =
new double[ maxDimension ];
1542 int *v =
new int[ maxDimension ];
1543 double *z =
new double[ maxDimension + 1 ];
1544 double *d =
new double[ maxDimension ];
1547 for (
int x = 0; x < width; x++ )
1552 for (
int y = 0; y < height; y++ )
1554 f[y] = im[ x +
static_cast< std::size_t
>( y ) * width ];
1556 distanceTransform1d( f, height, v, z, d );
1557 for (
int y = 0; y < height; y++ )
1559 im[ x +
static_cast< std::size_t
>( y ) * width ] = d[y];
1564 for (
int y = 0; y < height; y++ )
1569 for (
int x = 0; x < width; x++ )
1571 f[x] = im[ x +
static_cast< std::size_t
>( y ) * width ];
1573 distanceTransform1d( f, width, v, z, d );
1574 for (
int x = 0; x < width; x++ )
1576 im[ x +
static_cast< std::size_t
>( y ) * width ] = d[x];
1587double *QgsShapeburstFillSymbolLayer::distanceTransform( QImage *im,
QgsRenderContext &context )
1589 int width = im->width();
1590 int height = im->height();
1592 double *dtArray =
new double[
static_cast< std::size_t
>( width ) * height];
1596 std::size_t idx = 0;
1597 for (
int heightIndex = 0; heightIndex < height; ++heightIndex )
1602 const QRgb *scanLine =
reinterpret_cast< const QRgb *
>( im->constScanLine( heightIndex ) );
1603 for (
int widthIndex = 0; widthIndex < width; ++widthIndex )
1605 tmpRgb = scanLine[widthIndex];
1606 if ( qRed( tmpRgb ) == 0 )
1614 dtArray[ idx ] =
INF;
1621 distanceTransform2d( dtArray, width, height, context );
1626void QgsShapeburstFillSymbolLayer::dtArrayToQImage(
double *array, QImage *im,
QgsColorRamp *ramp,
QgsRenderContext &context,
bool useWholeShape,
int maxPixelDistance )
1628 int width = im->width();
1629 int height = im->height();
1632 double maxDistanceValue;
1637 double dtMaxValue = array[0];
1638 for ( std::size_t i = 1; i < static_cast< std::size_t >( width ) * height; ++i )
1640 if ( array[i] > dtMaxValue )
1642 dtMaxValue = array[i];
1647 maxDistanceValue = std::sqrt( dtMaxValue );
1652 maxDistanceValue = maxPixelDistance;
1656 std::size_t idx = 0;
1657 double squaredVal = 0;
1660 for (
int heightIndex = 0; heightIndex < height; ++heightIndex )
1665 QRgb *scanLine =
reinterpret_cast< QRgb *
>( im->scanLine( heightIndex ) );
1666 for (
int widthIndex = 0; widthIndex < width; ++widthIndex )
1669 squaredVal = array[idx];
1672 if ( maxDistanceValue > 0 )
1674 pixVal = squaredVal > 0 ? std::min( ( std::sqrt( squaredVal ) / maxDistanceValue ), 1.0 ) : 0;
1683 scanLine[widthIndex] = qPremultiply( ramp->
color( pixVal ).rgba() );
1694 map[QStringLiteral(
"color_type" )] = QString::number(
static_cast< int >( mColorType ) );
1695 map[QStringLiteral(
"blur_radius" )] = QString::number( mBlurRadius );
1696 map[QStringLiteral(
"use_whole_shape" )] = QString::number( mUseWholeShape );
1697 map[QStringLiteral(
"max_distance" )] = QString::number( mMaxDistance );
1700 map[QStringLiteral(
"ignore_rings" )] = QString::number( mIgnoreRings );
1704 if ( mGradientRamp )
1706 map.insert( mGradientRamp->properties() );
1714 std::unique_ptr< QgsShapeburstFillSymbolLayer > sl = std::make_unique< QgsShapeburstFillSymbolLayer >(
mColor, mColor2, mColorType, mBlurRadius, mUseWholeShape, mMaxDistance );
1715 if ( mGradientRamp )
1717 sl->setColorRamp( mGradientRamp->clone() );
1719 sl->setDistanceUnit( mDistanceUnit );
1720 sl->setDistanceMapUnitScale( mDistanceMapUnitScale );
1721 sl->setIgnoreRings( mIgnoreRings );
1722 sl->setOffset( mOffset );
1723 sl->setOffsetUnit( mOffsetUnit );
1724 sl->setOffsetMapUnitScale( mOffsetMapUnitScale );
1727 return sl.release();
1732 double offsetBleed = context.
convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale );
1743 mDistanceUnit = unit;
1749 if ( mDistanceUnit == mOffsetUnit )
1751 return mDistanceUnit;
1764 mDistanceMapUnitScale = scale;
1765 mOffsetMapUnitScale = scale;
1770 if ( mDistanceMapUnitScale == mOffsetMapUnitScale )
1772 return mDistanceMapUnitScale;
1797 p->setPen( QPen( Qt::NoPen ) );
1799 QTransform bkTransform =
mBrush.transform();
1803 QTransform t =
mBrush.transform();
1804 t.translate( leftCorner.x(), leftCorner.y() );
1805 mBrush.setTransform( t );
1809 QTransform t =
mBrush.transform();
1810 t.translate( 0, 0 );
1811 mBrush.setTransform( t );
1815 if ( useSelectedColor )
1818 p->setBrush( QBrush( selColor ) );
1824 QTransform t =
mBrush.transform();
1826 mBrush.setTransform( t );
1831 mBrush.setTransform( bkTransform );
1867 return Qt::SolidLine;
1871 return Qt::SolidLine;
1875 return mStroke->dxfPenStyle();
1911 , mPatternWidth( width )
1915 mColor = QColor( 255, 255, 255 );
1921 , mPatternWidth( width )
1922 , mSvgData( svgData )
1927 mColor = QColor( 255, 255, 255 );
1928 setDefaultSvgParams();
1936 mPatternWidthUnit = unit;
1937 mSvgStrokeWidthUnit = unit;
1940 mStroke->setOutputUnit( unit );
1946 if ( mPatternWidthUnit != unit || mSvgStrokeWidthUnit != unit ||
mStrokeWidthUnit != unit )
1956 mPatternWidthMapUnitScale = scale;
1957 mSvgStrokeWidthMapUnitScale = scale;
1963 mPatternWidthMapUnitScale == mSvgStrokeWidthMapUnitScale &&
1966 return mPatternWidthMapUnitScale;
1976 mSvgFilePath = svgPath;
1977 setDefaultSvgParams();
1987 if (
properties.contains( QStringLiteral(
"width" ) ) )
1989 width =
properties[QStringLiteral(
"width" )].toDouble();
1991 if (
properties.contains( QStringLiteral(
"svgFile" ) ) )
1995 if (
properties.contains( QStringLiteral(
"angle" ) ) )
2000 std::unique_ptr< QgsSVGFillSymbolLayer > symbolLayer;
2003 symbolLayer = std::make_unique< QgsSVGFillSymbolLayer >(
svgFilePath, width,
angle );
2007 if (
properties.contains( QStringLiteral(
"data" ) ) )
2009 data = QByteArray::fromHex(
properties[QStringLiteral(
"data" )].toString().toLocal8Bit() );
2011 symbolLayer = std::make_unique< QgsSVGFillSymbolLayer >( data, width,
angle );
2015 if (
properties.contains( QStringLiteral(
"svgFillColor" ) ) )
2020 else if (
properties.contains( QStringLiteral(
"color" ) ) )
2024 if (
properties.contains( QStringLiteral(
"svgOutlineColor" ) ) )
2029 else if (
properties.contains( QStringLiteral(
"outline_color" ) ) )
2033 else if (
properties.contains( QStringLiteral(
"line_color" ) ) )
2037 if (
properties.contains( QStringLiteral(
"svgOutlineWidth" ) ) )
2040 symbolLayer->setSvgStrokeWidth(
properties[QStringLiteral(
"svgOutlineWidth" )].toDouble() );
2042 else if (
properties.contains( QStringLiteral(
"outline_width" ) ) )
2044 symbolLayer->setSvgStrokeWidth(
properties[QStringLiteral(
"outline_width" )].toDouble() );
2046 else if (
properties.contains( QStringLiteral(
"line_width" ) ) )
2048 symbolLayer->setSvgStrokeWidth(
properties[QStringLiteral(
"line_width" )].toDouble() );
2052 if (
properties.contains( QStringLiteral(
"pattern_width_unit" ) ) )
2056 if (
properties.contains( QStringLiteral(
"pattern_width_map_unit_scale" ) ) )
2060 if (
properties.contains( QStringLiteral(
"svg_outline_width_unit" ) ) )
2064 if (
properties.contains( QStringLiteral(
"svg_outline_width_map_unit_scale" ) ) )
2068 if (
properties.contains( QStringLiteral(
"outline_width_unit" ) ) )
2072 if (
properties.contains( QStringLiteral(
"outline_width_map_unit_scale" ) ) )
2077 if (
properties.contains( QStringLiteral(
"parameters" ) ) )
2083 symbolLayer->restoreOldDataDefinedProperties(
properties );
2085 return symbolLayer.release();
2090 QVariantMap::iterator it =
properties.find( QStringLiteral(
"svgFile" ) );
2102 return QStringLiteral(
"SVGFill" );
2105void QgsSVGFillSymbolLayer::applyPattern( QBrush &brush,
const QString &svgFilePath,
double patternWidth,
Qgis::RenderUnit patternWidthUnit,
2106 const QColor &svgFillColor,
const QColor &svgStrokeColor,
double svgStrokeWidth,
2110 if ( mSvgViewBox.isNull() )
2117 if (
static_cast< int >( size ) < 1.0 || 10000.0 < size )
2119 brush.setTextureImage( QImage() );
2123 bool fitsInCache =
true;
2131 double hwRatio = 1.0;
2132 if ( patternPict.width() > 0 )
2134 hwRatio =
static_cast< double >( patternPict.height() ) /
static_cast< double >( patternPict.width() );
2136 patternImage = QImage(
static_cast< int >( size ),
static_cast< int >( size * hwRatio ), QImage::Format_ARGB32_Premultiplied );
2137 patternImage.fill( 0 );
2139 QPainter p( &patternImage );
2140 p.drawPicture( QPointF( size / 2, size * hwRatio / 2 ), patternPict );
2143 QTransform brushTransform;
2146 QImage transparentImage = patternImage.copy();
2148 brush.setTextureImage( transparentImage );
2152 brush.setTextureImage( patternImage );
2154 brush.setTransform( brushTransform );
2162 applyPattern(
mBrush, mSvgFilePath, mPatternWidth, mPatternWidthUnit,
mColor, mSvgStrokeColor, mSvgStrokeWidth, mSvgStrokeWidthUnit, context, mPatternWidthMapUnitScale, mSvgStrokeWidthMapUnitScale, evaluatedParameters );
2186 mStroke->renderPolyline( points, context.
feature(), context.
renderContext(), -1, useSelectedColor );
2189 for (
auto ringIt = rings->constBegin(); ringIt != rings->constEnd(); ++ringIt )
2191 mStroke->renderPolyline( *ringIt, context.
feature(), context.
renderContext(), -1, useSelectedColor );
2200 if ( !mSvgFilePath.isEmpty() )
2202 map.insert( QStringLiteral(
"svgFile" ), mSvgFilePath );
2206 map.insert( QStringLiteral(
"data" ), QString( mSvgData.toHex() ) );
2209 map.insert( QStringLiteral(
"width" ), QString::number( mPatternWidth ) );
2210 map.insert( QStringLiteral(
"angle" ), QString::number(
mAngle ) );
2215 map.insert( QStringLiteral(
"outline_width" ), QString::number( mSvgStrokeWidth ) );
2232 std::unique_ptr< QgsSVGFillSymbolLayer > clonedLayer;
2233 if ( !mSvgFilePath.isEmpty() )
2235 clonedLayer = std::make_unique< QgsSVGFillSymbolLayer >( mSvgFilePath, mPatternWidth,
mAngle );
2236 clonedLayer->setSvgFillColor(
mColor );
2237 clonedLayer->setSvgStrokeColor( mSvgStrokeColor );
2238 clonedLayer->setSvgStrokeWidth( mSvgStrokeWidth );
2242 clonedLayer = std::make_unique< QgsSVGFillSymbolLayer >( mSvgData, mPatternWidth,
mAngle );
2245 clonedLayer->setPatternWidthUnit( mPatternWidthUnit );
2246 clonedLayer->setPatternWidthMapUnitScale( mPatternWidthMapUnitScale );
2247 clonedLayer->setSvgStrokeWidthUnit( mSvgStrokeWidthUnit );
2248 clonedLayer->setSvgStrokeWidthMapUnitScale( mSvgStrokeWidthMapUnitScale );
2252 clonedLayer->setParameters( mParameters );
2256 clonedLayer->setSubSymbol( mStroke->clone() );
2260 return clonedLayer.release();
2265 QDomElement symbolizerElem = doc.createElement( QStringLiteral(
"se:PolygonSymbolizer" ) );
2266 if ( !props.value( QStringLiteral(
"uom" ), QString() ).toString().isEmpty() )
2267 symbolizerElem.setAttribute( QStringLiteral(
"uom" ), props.value( QStringLiteral(
"uom" ), QString() ).toString() );
2268 element.appendChild( symbolizerElem );
2272 QDomElement fillElem = doc.createElement( QStringLiteral(
"se:Fill" ) );
2273 symbolizerElem.appendChild( fillElem );
2275 QDomElement graphicFillElem = doc.createElement( QStringLiteral(
"se:GraphicFill" ) );
2276 fillElem.appendChild( graphicFillElem );
2278 QDomElement graphicElem = doc.createElement( QStringLiteral(
"se:Graphic" ) );
2279 graphicFillElem.appendChild( graphicElem );
2281 if ( !mSvgFilePath.isEmpty() )
2292 symbolizerElem.appendChild( doc.createComment( QStringLiteral(
"SVG from data not implemented yet" ) ) );
2298 double angle = props.value( QStringLiteral(
"angle" ), QStringLiteral(
"0" ) ).toDouble( &ok );
2301 angleFunc = QStringLiteral(
"%1 + %2" ).arg( props.value( QStringLiteral(
"angle" ), QStringLiteral(
"0" ) ).toString() ).arg(
mAngle );
2314 mStroke->toSld( doc, element, props );
2326 return mStroke.get();
2333 mStroke.reset(
nullptr );
2346 mStroke.reset( lineSymbol );
2356 if ( mStroke && mStroke->symbolLayer( 0 ) )
2358 double subLayerBleed = mStroke->symbolLayer( 0 )->estimateMaxBleed( context );
2359 return subLayerBleed;
2369 return QColor( Qt::black );
2371 return mStroke->color();
2378 attr.unite( mStroke->usedAttributes( context ) );
2386 if ( mStroke && mStroke->hasDataDefinedProperties() )
2393 QString path, mimeType;
2395 Qt::PenStyle penStyle;
2396 double size, strokeWidth;
2398 QDomElement fillElem = element.firstChildElement( QStringLiteral(
"Fill" ) );
2399 if ( fillElem.isNull() )
2402 QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral(
"GraphicFill" ) );
2403 if ( graphicFillElem.isNull() )
2406 QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral(
"Graphic" ) );
2407 if ( graphicElem.isNull() )
2413 if ( mimeType != QLatin1String(
"image/svg+xml" ) )
2418 double scaleFactor = 1.0;
2419 const QString uom = element.attribute( QStringLiteral(
"uom" ) );
2421 size = size * scaleFactor;
2422 strokeWidth = strokeWidth * scaleFactor;
2429 double d = angleFunc.toDouble( &ok );
2434 std::unique_ptr< QgsSVGFillSymbolLayer > sl = std::make_unique< QgsSVGFillSymbolLayer >( path, size,
angle );
2435 sl->setOutputUnit( sldUnitSize );
2438 sl->setSvgStrokeWidth( strokeWidth );
2441 QDomElement strokeElem = element.firstChildElement( QStringLiteral(
"Stroke" ) );
2442 if ( !strokeElem.isNull() )
2453 return sl.release();
2471 double width = mPatternWidth;
2477 QString svgFile = mSvgFilePath;
2496 double strokeWidth = mSvgStrokeWidth;
2505 mSvgStrokeWidthUnit, context, mPatternWidthMapUnitScale, mSvgStrokeWidthMapUnitScale, evaluatedParameters );
2509void QgsSVGFillSymbolLayer::storeViewBox()
2511 if ( !mSvgData.isEmpty() )
2513 QSvgRenderer r( mSvgData );
2516 mSvgViewBox = r.viewBoxF();
2521 mSvgViewBox = QRectF();
2524void QgsSVGFillSymbolLayer::setDefaultSvgParams()
2526 if ( mSvgFilePath.isEmpty() )
2531 bool hasFillParam, hasFillOpacityParam, hasStrokeParam, hasStrokeWidthParam, hasStrokeOpacityParam;
2532 bool hasDefaultFillColor, hasDefaultFillOpacity, hasDefaultStrokeColor, hasDefaultStrokeWidth, hasDefaultStrokeOpacity;
2533 QColor defaultFillColor, defaultStrokeColor;
2534 double defaultStrokeWidth, defaultFillOpacity, defaultStrokeOpacity;
2536 hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
2537 hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2538 hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
2539 hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity );
2541 double newFillOpacity = hasFillOpacityParam ?
mColor.alphaF() : 1.0;
2542 double newStrokeOpacity = hasStrokeOpacityParam ? mSvgStrokeColor.alphaF() : 1.0;
2544 if ( hasDefaultFillColor )
2546 mColor = defaultFillColor;
2547 mColor.setAlphaF( newFillOpacity );
2549 if ( hasDefaultFillOpacity )
2551 mColor.setAlphaF( defaultFillOpacity );
2553 if ( hasDefaultStrokeColor )
2555 mSvgStrokeColor = defaultStrokeColor;
2556 mSvgStrokeColor.setAlphaF( newStrokeOpacity );
2558 if ( hasDefaultStrokeOpacity )
2560 mSvgStrokeColor.setAlphaF( defaultStrokeOpacity );
2562 if ( hasDefaultStrokeWidth )
2564 mSvgStrokeWidth = defaultStrokeWidth;
2577 mFillLineSymbol = std::make_unique<QgsLineSymbol>( );
2585 mFillLineSymbol->setWidth( w );
2591 mFillLineSymbol->setColor(
c );
2597 return mFillLineSymbol ? mFillLineSymbol->color() :
mColor;
2609 mFillLineSymbol.reset( qgis::down_cast<QgsLineSymbol *>( symbol ) );
2618 return mFillLineSymbol.get();
2624 if ( mFillLineSymbol )
2625 attr.unite( mFillLineSymbol->usedAttributes( context ) );
2633 if ( mFillLineSymbol && mFillLineSymbol->hasDataDefinedProperties() )
2655 double lineAngleRad { qDegreesToRadians( mLineAngle ) };
2657 const int quadrant {
static_cast<int>( lineAngleRad / M_PI_2 ) };
2658 Q_ASSERT( quadrant >= 0 && quadrant <= 3 );
2668 lineAngleRad -= M_PI / 2;
2673 lineAngleRad -= M_PI;
2678 lineAngleRad -= M_PI + M_PI_2;
2686 QSize size {
static_cast<int>( distancePx ),
static_cast<int>( distancePx ) };
2688 if (
static_cast<int>( mLineAngle ) % 90 != 0 )
2690 size = QSize(
static_cast<int>( distancePx / std::sin( lineAngleRad ) ),
static_cast<int>( distancePx / std::cos( lineAngleRad ) ) );
2693 QPixmap pixmap( size );
2694 pixmap.fill( Qt::transparent );
2696 painter.begin( &pixmap );
2697 painter.setRenderHint( QPainter::Antialiasing );
2705 std::unique_ptr< QgsLinePatternFillSymbolLayer > layerClone(
clone() );
2706 layerClone->setOffset( 0 );
2707 layerClone->drawPreviewIcon( symbolContext, pixmap.size() );
2709 return pixmap.toImage();
2721 mDistanceUnit = unit;
2722 mLineWidthUnit = unit;
2725 if ( mFillLineSymbol )
2726 mFillLineSymbol->setOutputUnit( unit );
2749 mDistanceMapUnitScale = scale;
2750 mLineWidthMapUnitScale = scale;
2751 mOffsetMapUnitScale = scale;
2757 mDistanceMapUnitScale == mLineWidthMapUnitScale &&
2758 mLineWidthMapUnitScale == mOffsetMapUnitScale )
2760 return mDistanceMapUnitScale;
2767 std::unique_ptr< QgsLinePatternFillSymbolLayer > patternLayer = std::make_unique< QgsLinePatternFillSymbolLayer >();
2773 QColor
color( Qt::black );
2776 if (
properties.contains( QStringLiteral(
"lineangle" ) ) )
2781 else if (
properties.contains( QStringLiteral(
"angle" ) ) )
2785 patternLayer->setLineAngle(
lineAngle );
2787 if (
properties.contains( QStringLiteral(
"distance" ) ) )
2791 patternLayer->setDistance(
distance );
2793 if (
properties.contains( QStringLiteral(
"linewidth" ) ) )
2798 else if (
properties.contains( QStringLiteral(
"outline_width" ) ) )
2802 else if (
properties.contains( QStringLiteral(
"line_width" ) ) )
2806 patternLayer->setLineWidth(
lineWidth );
2808 if (
properties.contains( QStringLiteral(
"color" ) ) )
2812 else if (
properties.contains( QStringLiteral(
"outline_color" ) ) )
2816 else if (
properties.contains( QStringLiteral(
"line_color" ) ) )
2820 patternLayer->setColor(
color );
2822 if (
properties.contains( QStringLiteral(
"offset" ) ) )
2826 patternLayer->setOffset(
offset );
2829 if (
properties.contains( QStringLiteral(
"distance_unit" ) ) )
2833 if (
properties.contains( QStringLiteral(
"distance_map_unit_scale" ) ) )
2837 if (
properties.contains( QStringLiteral(
"line_width_unit" ) ) )
2841 else if (
properties.contains( QStringLiteral(
"outline_width_unit" ) ) )
2845 if (
properties.contains( QStringLiteral(
"line_width_map_unit_scale" ) ) )
2849 if (
properties.contains( QStringLiteral(
"offset_unit" ) ) )
2853 if (
properties.contains( QStringLiteral(
"offset_map_unit_scale" ) ) )
2857 if (
properties.contains( QStringLiteral(
"outline_width_unit" ) ) )
2861 if (
properties.contains( QStringLiteral(
"outline_width_map_unit_scale" ) ) )
2865 if (
properties.contains( QStringLiteral(
"coordinate_reference" ) ) )
2869 if (
properties.contains( QStringLiteral(
"clip_mode" ) ) )
2874 patternLayer->restoreOldDataDefinedProperties(
properties );
2876 return patternLayer.release();
2881 return QStringLiteral(
"LinePatternFill" );
2884bool QgsLinePatternFillSymbolLayer::applyPattern(
const QgsSymbolRenderContext &context, QBrush &brush,
double lineAngle,
double distance )
2886 mBrush.setTextureImage( QImage() );
2888 if ( !mFillLineSymbol )
2893 std::unique_ptr< QgsLineSymbol > fillLineSymbol( mFillLineSymbol->clone() );
2894 if ( !fillLineSymbol )
2910 outputPixelOffset = std::fmod( outputPixelOffset, outputPixelDist );
2911 if ( outputPixelOffset > outputPixelDist / 2.0 )
2912 outputPixelOffset -= outputPixelDist;
2916 double outputPixelBleed = 0;
2917 double outputPixelInterval = 0;
2918 for (
int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2922 outputPixelBleed = std::max( outputPixelBleed, outputPixelLayerBleed );
2925 if ( markerLineLayer )
2934 outputPixelInterval = std::max( outputPixelInterval, outputPixelLayerInterval );
2938 if ( outputPixelInterval > 0 )
2942 double intervalScale = std::round( outputPixelInterval ) / outputPixelInterval;
2943 outputPixelInterval = std::round( outputPixelInterval );
2945 for (
int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2950 if ( markerLineLayer )
2964 height = outputPixelDist;
2965 width = outputPixelInterval > 0 ? outputPixelInterval : height;
2969 width = outputPixelDist;
2970 height = outputPixelInterval > 0 ? outputPixelInterval : width;
2974 height = outputPixelDist / std::cos(
lineAngle * M_PI / 180 );
2975 width = outputPixelDist / std::sin(
lineAngle * M_PI / 180 );
2978 lineAngle = 180 * std::atan2(
static_cast< double >( height ),
static_cast< double >( width ) ) / M_PI;
2984 height = std::abs( height );
2985 width = std::abs( width );
2987 outputPixelDist = std::abs( height * std::cos(
lineAngle * M_PI / 180 ) );
2991 int offsetHeight =
static_cast< int >( std::round( outputPixelOffset / std::cos(
lineAngle * M_PI / 180 ) ) );
2992 outputPixelOffset = offsetHeight * std::cos(
lineAngle * M_PI / 180 );
3001 int bufferMulti =
static_cast< int >( std::max( std::ceil( outputPixelBleed / width ), std::ceil( outputPixelBleed / width ) ) );
3005 bufferMulti = std::max( bufferMulti, 1 );
3007 int xBuffer = width * bufferMulti;
3008 int yBuffer = height * bufferMulti;
3009 int innerWidth = width;
3010 int innerHeight = height;
3011 width += 2 * xBuffer;
3012 height += 2 * yBuffer;
3015 if ( width > 2000 || height > 2000 || width == 0 || height == 0 )
3020 QImage patternImage( width, height, QImage::Format_ARGB32 );
3021 patternImage.fill( 0 );
3023 QPointF p1, p2, p3, p4, p5, p6;
3026 p1 = QPointF( 0, yBuffer );
3027 p2 = QPointF( width, yBuffer );
3028 p3 = QPointF( 0, yBuffer + innerHeight );
3029 p4 = QPointF( width, yBuffer + innerHeight );
3033 p1 = QPointF( xBuffer, height );
3034 p2 = QPointF( xBuffer, 0 );
3035 p3 = QPointF( xBuffer + innerWidth, height );
3036 p4 = QPointF( xBuffer + innerWidth, 0 );
3040 dx = outputPixelDist * std::cos( ( 90 -
lineAngle ) * M_PI / 180.0 );
3041 dy = outputPixelDist * std::sin( ( 90 -
lineAngle ) * M_PI / 180.0 );
3042 p1 = QPointF( 0, height );
3043 p2 = QPointF( width, 0 );
3044 p3 = QPointF( -dx, height - dy );
3045 p4 = QPointF( width - dx, -dy );
3046 p5 = QPointF( dx, height + dy );
3047 p6 = QPointF( width + dx, dy );
3051 dx = outputPixelDist * std::cos( ( 90 -
lineAngle ) * M_PI / 180.0 );
3052 dy = outputPixelDist * std::sin( ( 90 -
lineAngle ) * M_PI / 180.0 );
3053 p1 = QPointF( width, 0 );
3054 p2 = QPointF( 0, height );
3055 p3 = QPointF( width - dx, -dy );
3056 p4 = QPointF( -dx, height - dy );
3057 p5 = QPointF( width + dx, dy );
3058 p6 = QPointF( dx, height + dy );
3062 dy = outputPixelDist * std::cos( ( 180 -
lineAngle ) * M_PI / 180 );
3063 dx = outputPixelDist * std::sin( ( 180 -
lineAngle ) * M_PI / 180 );
3064 p1 = QPointF( 0, 0 );
3065 p2 = QPointF( width, height );
3066 p5 = QPointF( dx, -dy );
3067 p6 = QPointF( width + dx, height - dy );
3068 p3 = QPointF( -dx, dy );
3069 p4 = QPointF( width - dx, height + dy );
3073 dy = outputPixelDist * std::cos( ( 180 -
lineAngle ) * M_PI / 180 );
3074 dx = outputPixelDist * std::sin( ( 180 -
lineAngle ) * M_PI / 180 );
3075 p1 = QPointF( width, height );
3076 p2 = QPointF( 0, 0 );
3077 p5 = QPointF( width + dx, height - dy );
3078 p6 = QPointF( dx, -dy );
3079 p3 = QPointF( width - dx, height + dy );
3080 p4 = QPointF( -dx, dy );
3087 p3 = QPointF( tempPt.x(), tempPt.y() );
3089 p4 = QPointF( tempPt.x(), tempPt.y() );
3091 p5 = QPointF( tempPt.x(), tempPt.y() );
3093 p6 = QPointF( tempPt.x(), tempPt.y() );
3097 p1 = QPointF( tempPt.x(), tempPt.y() );
3099 p2 = QPointF( tempPt.x(), tempPt.y() );
3102 QPainter p( &patternImage );
3106 p.setRenderHint( QPainter::Antialiasing,
false );
3107 QPen pen( QColor( Qt::black ) );
3108 pen.setWidthF( 0.1 );
3109 pen.setCapStyle( Qt::FlatCap );
3114 QPolygon polygon = QPolygon() << QPoint( 0, 0 ) << QPoint( width - 1, 0 ) << QPoint( width - 1, height - 1 ) << QPoint( 0, height - 1 ) << QPoint( 0, 0 );
3115 p.drawPolygon( polygon );
3117 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 );
3118 p.drawPolygon( polygon );
3124 p.setRenderHint( QPainter::Antialiasing,
true );
3138 fillLineSymbol->startRender( lineRenderContext, context.
fields() );
3140 QVector<QPolygonF> polygons;
3141 polygons.append( QPolygonF() << p1 << p2 );
3142 polygons.append( QPolygonF() << p3 << p4 );
3145 polygons.append( QPolygonF() << p5 << p6 );
3149 for (
const QPolygonF &polygon : std::as_const( polygons ) )
3151 fillLineSymbol->renderPolyline( polygon, context.
feature(), lineRenderContext, -1, useSelectedColor );
3154 fillLineSymbol->stopRender( lineRenderContext );
3158 patternImage = patternImage.copy( xBuffer, yBuffer, patternImage.width() - 2 * xBuffer, patternImage.height() - 2 * yBuffer );
3163 QImage transparentImage = patternImage.copy();
3165 brush.setTextureImage( transparentImage );
3169 brush.setTextureImage( patternImage );
3172 QTransform brushTransform;
3173 brush.setTransform( brushTransform );
3183 || ( mFillLineSymbol && mFillLineSymbol->hasDataDefinedProperties() )
3187 if ( !mRenderUsingLines )
3191 mRenderUsingLines = !applyPattern( context,
mBrush, mLineAngle, mDistance );
3194 if ( mRenderUsingLines && mFillLineSymbol )
3198 mFillLineSymbolRenderStarted =
true;
3204 if ( mFillLineSymbolRenderStarted )
3207 mFillLineSymbolRenderStarted =
false;
3214 if ( !useSelectedColor && !mRenderUsingLines )
3221 if ( !mFillLineSymbolRenderStarted && mFillLineSymbol )
3225 mFillLineSymbolRenderStarted =
true;
3255 outputPixelOffset = std::fmod( outputPixelOffset, outputPixelDistance );
3256 if ( outputPixelOffset > outputPixelDistance / 2.0 )
3257 outputPixelOffset -= outputPixelDistance;
3259 p->setPen( QPen( Qt::NoPen ) );
3281 std::unique_ptr< QgsPolygon > shapePolygon;
3282 std::unique_ptr< QgsGeometryEngine > shapeEngine;
3290 shapePolygon = std::make_unique< QgsPolygon >();
3294 for (
const QPolygonF &ring : *rings )
3300 shapeEngine->prepareGeometry();
3307 path.addPolygon( points );
3310 for (
const QPolygonF &ring : *rings )
3312 path.addPolygon( ring );
3315 p->setClipPath( path, Qt::IntersectClip );
3321 const QRectF boundingRect = points.boundingRect();
3323 QTransform invertedRotateTransform;
3329 QTransform transform;
3330 if ( applyBrushTransform )
3333 transform.translate( -boundingRect.center().x(),
3334 -boundingRect.center().y() );
3336 transform.translate( boundingRect.center().x(),
3337 boundingRect.center().y() );
3345 const QRectF transformedBounds = transform.map( points ).boundingRect();
3349 left = transformedBounds.left() - buffer * 2;
3350 top = transformedBounds.top() - buffer * 2;
3351 right = transformedBounds.right() + buffer * 2;
3352 bottom = transformedBounds.bottom() + buffer * 2;
3353 invertedRotateTransform = transform.inverted();
3355 if ( !applyBrushTransform )
3357 top -= transformedBounds.top() - ( outputPixelDistance * std::floor( transformedBounds.top() / outputPixelDistance ) );
3362 const bool needsExpressionContext = mFillLineSymbol->hasDataDefinedProperties();
3367 int currentLine = 0;
3368 for (
double currentY = top; currentY <= bottom; currentY += outputPixelDistance )
3373 if ( needsExpressionContext )
3377 double y1 = currentY;
3379 double y2 = currentY;
3380 invertedRotateTransform.map( left, currentY - outputPixelOffset, &x1, &y1 );
3381 invertedRotateTransform.map( right, currentY - outputPixelOffset, &x2, &y2 );
3386 std::unique_ptr< QgsAbstractGeometry > intersection( shapeEngine->intersection( &ls ) );
3387 for (
auto it = intersection->const_parts_begin(); it != intersection->const_parts_end(); ++it )
3389 if (
const QgsLineString *ls = qgsgeometry_cast< const QgsLineString * >( *it ) )
3397 mFillLineSymbol->renderPolyline( QPolygonF() << QPointF( x1, y1 ) << QPointF( x2, y2 ), context.
feature(), context.
renderContext(), -1, useSelectedColor );
3409 map.insert( QStringLiteral(
"angle" ), QString::number( mLineAngle ) );
3410 map.insert( QStringLiteral(
"distance" ), QString::number( mDistance ) );
3411 map.insert( QStringLiteral(
"line_width" ), QString::number( mLineWidth ) );
3413 map.insert( QStringLiteral(
"offset" ), QString::number( mOffset ) );
3429 if ( mFillLineSymbol )
3440 QDomElement symbolizerElem = doc.createElement( QStringLiteral(
"se:PolygonSymbolizer" ) );
3441 if ( !props.value( QStringLiteral(
"uom" ), QString() ).toString().isEmpty() )
3442 symbolizerElem.setAttribute( QStringLiteral(
"uom" ), props.value( QStringLiteral(
"uom" ), QString() ).toString() );
3443 element.appendChild( symbolizerElem );
3448 QDomElement fillElem = doc.createElement( QStringLiteral(
"se:Fill" ) );
3449 symbolizerElem.appendChild( fillElem );
3451 QDomElement graphicFillElem = doc.createElement( QStringLiteral(
"se:GraphicFill" ) );
3452 fillElem.appendChild( graphicFillElem );
3454 QDomElement graphicElem = doc.createElement( QStringLiteral(
"se:Graphic" ) );
3455 graphicFillElem.appendChild( graphicElem );
3460 bool exportOk {
false };
3464 if ( ! image.isNull() )
3466 const QFileInfo info { context.exportFilePath() };
3467 QString pngPath { info.completeSuffix().isEmpty() ? context.exportFilePath() : context.exportFilePath().chopped( info.completeSuffix().length() ).append( QStringLiteral(
"png" ) ) };
3469 image.save( pngPath );
3478 QColor lineColor = mFillLineSymbol ? mFillLineSymbol->color() : QColor();
3479 double lineWidth = mFillLineSymbol ? mFillLineSymbol->width() : 0.0;
3487 double angle = props.value( QStringLiteral(
"angle" ), QStringLiteral(
"0" ) ).toDouble( &ok );
3490 angleFunc = QStringLiteral(
"%1 + %2" ).arg( props.value( QStringLiteral(
"angle" ), QStringLiteral(
"0" ) ).toString() ).arg( mLineAngle );
3494 angleFunc = QString::number(
angle + mLineAngle );
3499 QPointF lineOffset( std::sin( mLineAngle ) * mOffset, std::cos( mLineAngle ) * mOffset );
3507 QString featureStyle;
3508 featureStyle.append(
"Brush(" );
3509 featureStyle.append( QStringLiteral(
"fc:%1" ).arg(
mColor.name() ) );
3510 featureStyle.append( QStringLiteral(
",bc:%1" ).arg( QLatin1String(
"#00000000" ) ) );
3511 featureStyle.append(
",id:\"ogr-brush-2\"" );
3512 featureStyle.append( QStringLiteral(
",a:%1" ).arg( mLineAngle ) );
3513 featureStyle.append( QStringLiteral(
",s:%1" ).arg( mLineWidth * widthScaleFactor ) );
3514 featureStyle.append(
",dx:0mm" );
3515 featureStyle.append( QStringLiteral(
",dy:%1mm" ).arg( mDistance * widthScaleFactor ) );
3516 featureStyle.append(
')' );
3517 return featureStyle;
3523 && ( !mFillLineSymbol || !mFillLineSymbol->hasDataDefinedProperties() ) )
3548 Qt::PenStyle lineStyle;
3550 QDomElement fillElem = element.firstChildElement( QStringLiteral(
"Fill" ) );
3551 if ( fillElem.isNull() )
3554 QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral(
"GraphicFill" ) );
3555 if ( graphicFillElem.isNull() )
3558 QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral(
"Graphic" ) );
3559 if ( graphicElem.isNull() )
3565 if ( name != QLatin1String(
"horline" ) )
3573 double d = angleFunc.toDouble( &ok );
3582 offset = std::sqrt( std::pow( vectOffset.x(), 2 ) + std::pow( vectOffset.y(), 2 ) );
3585 double scaleFactor = 1.0;
3586 const QString uom = element.attribute( QStringLiteral(
"uom" ) );
3588 size = size * scaleFactor;
3591 std::unique_ptr< QgsLinePatternFillSymbolLayer > sl = std::make_unique< QgsLinePatternFillSymbolLayer >();
3592 sl->setOutputUnit( sldUnitSize );
3593 sl->setColor( lineColor );
3595 sl->setLineAngle(
angle );
3597 sl->setDistance( size );
3600 QDomElement strokeElem = element.firstChildElement( QStringLiteral(
"Stroke" ) );
3601 if ( !strokeElem.isNull() )
3612 return sl.release();
3712 std::unique_ptr< QgsPointPatternFillSymbolLayer > layer = std::make_unique< QgsPointPatternFillSymbolLayer >();
3713 if (
properties.contains( QStringLiteral(
"distance_x" ) ) )
3715 layer->setDistanceX(
properties[QStringLiteral(
"distance_x" )].toDouble() );
3717 if (
properties.contains( QStringLiteral(
"distance_y" ) ) )
3719 layer->setDistanceY(
properties[QStringLiteral(
"distance_y" )].toDouble() );
3721 if (
properties.contains( QStringLiteral(
"displacement_x" ) ) )
3723 layer->setDisplacementX(
properties[QStringLiteral(
"displacement_x" )].toDouble() );
3725 if (
properties.contains( QStringLiteral(
"displacement_y" ) ) )
3727 layer->setDisplacementY(
properties[QStringLiteral(
"displacement_y" )].toDouble() );
3729 if (
properties.contains( QStringLiteral(
"offset_x" ) ) )
3731 layer->setOffsetX(
properties[QStringLiteral(
"offset_x" )].toDouble() );
3733 if (
properties.contains( QStringLiteral(
"offset_y" ) ) )
3735 layer->setOffsetY(
properties[QStringLiteral(
"offset_y" )].toDouble() );
3738 if (
properties.contains( QStringLiteral(
"distance_x_unit" ) ) )
3742 if (
properties.contains( QStringLiteral(
"distance_x_map_unit_scale" ) ) )
3746 if (
properties.contains( QStringLiteral(
"distance_y_unit" ) ) )
3750 if (
properties.contains( QStringLiteral(
"distance_y_map_unit_scale" ) ) )
3754 if (
properties.contains( QStringLiteral(
"displacement_x_unit" ) ) )
3758 if (
properties.contains( QStringLiteral(
"displacement_x_map_unit_scale" ) ) )
3762 if (
properties.contains( QStringLiteral(
"displacement_y_unit" ) ) )
3766 if (
properties.contains( QStringLiteral(
"displacement_y_map_unit_scale" ) ) )
3770 if (
properties.contains( QStringLiteral(
"offset_x_unit" ) ) )
3774 if (
properties.contains( QStringLiteral(
"offset_x_map_unit_scale" ) ) )
3778 if (
properties.contains( QStringLiteral(
"offset_y_unit" ) ) )
3782 if (
properties.contains( QStringLiteral(
"offset_y_map_unit_scale" ) ) )
3787 if (
properties.contains( QStringLiteral(
"random_deviation_x" ) ) )
3789 layer->setMaximumRandomDeviationX(
properties[QStringLiteral(
"random_deviation_x" )].toDouble() );
3791 if (
properties.contains( QStringLiteral(
"random_deviation_y" ) ) )
3793 layer->setMaximumRandomDeviationY(
properties[QStringLiteral(
"random_deviation_y" )].toDouble() );
3795 if (
properties.contains( QStringLiteral(
"random_deviation_x_unit" ) ) )
3799 if (
properties.contains( QStringLiteral(
"random_deviation_x_map_unit_scale" ) ) )
3803 if (
properties.contains( QStringLiteral(
"random_deviation_y_unit" ) ) )
3807 if (
properties.contains( QStringLiteral(
"random_deviation_y_map_unit_scale" ) ) )
3811 unsigned long seed = 0;
3812 if (
properties.contains( QStringLiteral(
"seed" ) ) )
3818 std::random_device rd;
3819 std::mt19937 mt(
seed == 0 ? rd() :
seed );
3820 std::uniform_int_distribution<> uniformDist( 1, 999999999 );
3821 seed = uniformDist( mt );
3823 layer->setSeed(
seed );
3825 if (
properties.contains( QStringLiteral(
"outline_width_unit" ) ) )
3829 if (
properties.contains( QStringLiteral(
"outline_width_map_unit_scale" ) ) )
3833 if (
properties.contains( QStringLiteral(
"clip_mode" ) ) )
3837 if (
properties.contains( QStringLiteral(
"coordinate_reference" ) ) )
3842 if (
properties.contains( QStringLiteral(
"angle" ) ) )
3844 layer->setAngle(
properties[QStringLiteral(
"angle" )].toDouble() );
3847 layer->restoreOldDataDefinedProperties(
properties );
3849 return layer.release();
3854 return QStringLiteral(
"PointPatternFill" );
3857bool QgsPointPatternFillSymbolLayer::applyPattern(
const QgsSymbolRenderContext &context, QBrush &brush,
double distanceX,
double distanceY,
3858 double displacementX,
double displacementY,
double offsetX,
double offsetY )
3865 double widthOffset = std::fmod(
3868 double heightOffset = std::fmod(
3872 if ( width > 2000 || height > 2000 )
3874 brush.setTextureImage( QImage() );
3878 QImage patternImage( width, height, QImage::Format_ARGB32 );
3879 patternImage.fill( 0 );
3880 if ( patternImage.isNull() )
3882 brush.setTextureImage( QImage() );
3887 QPainter p( &patternImage );
3906 for (
double currentX = -width; currentX <= width * 2.0; currentX += width )
3908 for (
double currentY = -height; currentY <= height * 2.0; currentY += height )
3910 mMarkerSymbol->renderPoint( QPointF( currentX + widthOffset, currentY + heightOffset ), context.
feature(), pointRenderContext );
3921 for (
double currentX = -width; currentX <= width * 2.0; currentX += width )
3923 for (
double currentY = -height / 2.0; currentY <= height * 2.0; currentY += height )
3925 mMarkerSymbol->renderPoint( QPointF( currentX + widthOffset + displacementPixelX, currentY + heightOffset ), context.
feature(), pointRenderContext );
3929 for (
double currentX = -width / 2.0; currentX <= width * 2.0; currentX += width )
3931 for (
double currentY = -height; currentY <= height * 2.0; currentY += height / 2.0 )
3933 mMarkerSymbol->renderPoint( QPointF( currentX + widthOffset + ( std::fmod( currentY, height ) != 0 ? displacementPixelX : 0 ), currentY + heightOffset - displacementPixelY ), context.feature(), pointRenderContext );
3942 QImage transparentImage = patternImage.copy();
3944 brush.setTextureImage( transparentImage );
3948 brush.setTextureImage( patternImage );
3950 QTransform brushTransform;
3951 brush.setTransform( brushTransform );
3971 if ( !mRenderUsingMarkers )
4016 if ( !useSelectedColor && !mRenderUsingMarkers )
4066 const double widthOffset = std::fmod(
4078 const double heightOffset = std::fmod(
4104 p->setPen( QPen( Qt::NoPen ) );
4126 std::unique_ptr< QgsPolygon > shapePolygon;
4127 std::unique_ptr< QgsGeometryEngine > shapeEngine;
4134 shapePolygon = std::make_unique< QgsPolygon >();
4138 for (
const QPolygonF &ring : *rings )
4144 shapeEngine->prepareGeometry();
4151 path.addPolygon( points );
4154 for (
const QPolygonF &ring : *rings )
4156 path.addPolygon( ring );
4159 p->setClipPath( path, Qt::IntersectClip );
4165 const QRectF boundingRect = points.boundingRect();
4167 QTransform invertedRotateTransform;
4175 QTransform transform;
4176 if ( applyBrushTransform )
4179 transform.translate( -boundingRect.center().x(),
4180 -boundingRect.center().y() );
4181 transform.rotate( -
angle );
4182 transform.translate( boundingRect.center().x(),
4183 boundingRect.center().y() );
4188 transform.rotate( -
angle );
4191 const QRectF transformedBounds = transform.map( points ).boundingRect();
4192 left = transformedBounds.left() - 2 * width;
4193 top = transformedBounds.top() - 2 * height;
4194 right = transformedBounds.right() + 2 * width;
4195 bottom = transformedBounds.bottom() + 2 * height;
4196 invertedRotateTransform = transform.inverted();
4198 if ( !applyBrushTransform )
4200 left -= transformedBounds.left() - ( width * std::floor( transformedBounds.left() / width ) );
4201 top -= transformedBounds.top() - ( height * std::floor( transformedBounds.top() / height ) );
4206 left = boundingRect.left() - 2 * width;
4207 top = boundingRect.top() - 2 * height;
4208 right = boundingRect.right() + 2 * width;
4209 bottom = boundingRect.bottom() + 2 * height;
4211 if ( !applyBrushTransform )
4213 left -= boundingRect.left() - ( width * std::floor( boundingRect.left() / width ) );
4214 top -= boundingRect.top() - ( height * std::floor( boundingRect.top() / height ) );
4243 std::random_device rd;
4244 std::mt19937 mt(
seed == 0 ? rd() :
seed );
4245 std::uniform_real_distribution<> uniformDist( 0, 1 );
4251 const bool needsExpressionContext =
mMarkerSymbol->hasDataDefinedProperties();
4259 bool alternateColumn =
false;
4260 int currentCol = -3;
4261 for (
double currentX = left; currentX <= right; currentX += width, alternateColumn = !alternateColumn )
4266 if ( needsExpressionContext )
4269 bool alternateRow =
false;
4270 const double columnX = currentX + widthOffset;
4271 int currentRow = -3;
4272 for (
double currentY = top; currentY <= bottom; currentY += height, alternateRow = !alternateRow )
4277 double y = currentY + heightOffset;
4280 x += displacementPixelX;
4282 if ( !alternateColumn )
4283 y -= displacementPixelY;
4289 invertedRotateTransform.map( xx, yy, &x, &y );
4292 if ( useRandomShift )
4294 x += ( 2 * uniformDist( mt ) - 1 ) * maxRandomDeviationPixelX;
4295 y += ( 2 * uniformDist( mt ) - 1 ) * maxRandomDeviationPixelY;
4298 if ( needsExpressionContext )
4306 bool renderPoint =
true;
4314 renderPoint = shapeEngine->intersects( &p );
4326 renderPoint = shapeEngine->intersects( markerBounds.
constGet() );
4352 map.insert( QStringLiteral(
"distance_x" ), QString::number(
mDistanceX ) );
4353 map.insert( QStringLiteral(
"distance_y" ), QString::number(
mDistanceY ) );
4354 map.insert( QStringLiteral(
"displacement_x" ), QString::number(
mDisplacementX ) );
4355 map.insert( QStringLiteral(
"displacement_y" ), QString::number(
mDisplacementY ) );
4356 map.insert( QStringLiteral(
"offset_x" ), QString::number(
mOffsetX ) );
4357 map.insert( QStringLiteral(
"offset_y" ), QString::number(
mOffsetY ) );
4373 map.insert( QStringLiteral(
"random_deviation_x" ), QString::number(
mRandomDeviationX ) );
4374 map.insert( QStringLiteral(
"random_deviation_y" ), QString::number(
mRandomDeviationY ) );
4379 map.insert( QStringLiteral(
"seed" ), QString::number(
mSeed ) );
4380 map.insert( QStringLiteral(
"angle" ),
mAngle );
4399 for (
int symbolLayerIdx = 0; symbolLayerIdx <
mMarkerSymbol->symbolLayerCount(); symbolLayerIdx++ )
4401 QDomElement symbolizerElem = doc.createElement( QStringLiteral(
"se:PolygonSymbolizer" ) );
4402 if ( !props.value( QStringLiteral(
"uom" ), QString() ).toString().isEmpty() )
4403 symbolizerElem.setAttribute( QStringLiteral(
"uom" ), props.value( QStringLiteral(
"uom" ), QString() ).toString() );
4404 element.appendChild( symbolizerElem );
4409 QDomElement fillElem = doc.createElement( QStringLiteral(
"se:Fill" ) );
4410 symbolizerElem.appendChild( fillElem );
4412 QDomElement graphicFillElem = doc.createElement( QStringLiteral(
"se:GraphicFill" ) );
4413 fillElem.appendChild( graphicFillElem );
4420 bool exportOk {
false };
4424 if ( ! image.isNull() )
4426 QDomElement graphicElem = doc.createElement( QStringLiteral(
"se:Graphic" ) );
4427 graphicFillElem.appendChild( graphicElem );
4428 const QFileInfo info { context.exportFilePath() };
4429 QString pngPath { info.completeSuffix().isEmpty() ? context.exportFilePath() : context.exportFilePath().chopped( info.completeSuffix().length() ).append( QStringLiteral(
"png" ) ) };
4431 image.save( pngPath );
4450 symbolizerElem.appendChild( graphicMarginElem );
4454 markerLayer->writeSldMarker( doc, graphicFillElem, props );
4458 QString errorMsg = QStringLiteral(
"QgsMarkerSymbolLayer expected, %1 found. Skip it." ).arg( layer->
layerType() );
4459 graphicFillElem.appendChild( doc.createComment( errorMsg ) );
4463 QString errorMsg = QStringLiteral(
"Missing point pattern symbol layer. Skip it." );
4464 graphicFillElem.appendChild( doc.createComment( errorMsg ) );
4473 double angleRads { qDegreesToRadians(
mAngle ) };
4482 if ( displacementXPx != 0 )
4487 if ( displacementYPx != 0 )
4494 QPixmap pixmap( size );
4495 pixmap.fill( Qt::transparent );
4497 painter.begin( &pixmap );
4498 painter.setRenderHint( QPainter::Antialiasing );
4506 std::unique_ptr< QgsPointPatternFillSymbolLayer > layerClone(
clone() );
4508 layerClone->setAngle( qRadiansToDegrees( angleRads ) );
4511 layerClone->setMaximumRandomDeviationX( 0 );
4512 layerClone->setMaximumRandomDeviationY( 0 );
4514 layerClone->drawPreviewIcon( symbolContext, pixmap.size() );
4516 return pixmap.toImage();
4524 QDomElement fillElem = element.firstChildElement( QStringLiteral(
"Fill" ) );
4525 if ( fillElem.isNull() )
4528 QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral(
"GraphicFill" ) );
4529 if ( graphicFillElem.isNull() )
4532 QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral(
"Graphic" ) );
4533 if ( graphicElem.isNull() )
4537 if ( !simpleMarkerSl )
4542 layers.append( simpleMarkerSl );
4544 std::unique_ptr< QgsMarkerSymbol > marker = std::make_unique< QgsMarkerSymbol >( layers );
4547 const double markerSize { marker->size() };
4549 std::unique_ptr< QgsPointPatternFillSymbolLayer > pointPatternFillSl = std::make_unique< QgsPointPatternFillSymbolLayer >();
4550 pointPatternFillSl->setSubSymbol( marker.release() );
4555 auto distanceParser = [ & ](
const QStringList & values )
4557 switch ( values.count( ) )
4562 const double v { values.at( 0 ).toDouble( &ok ) };
4565 pointPatternFillSl->setDistanceX( v * 2 + markerSize );
4566 pointPatternFillSl->setDistanceY( v * 2 + markerSize );
4573 const double vX { values.at( 1 ).toDouble( &ok ) };
4576 pointPatternFillSl->setDistanceX( vX * 2 + markerSize );
4578 const double vY { values.at( 0 ).toDouble( &ok ) };
4581 pointPatternFillSl->setDistanceY( vY * 2 + markerSize );
4588 const double vX { values.at( 1 ).toDouble( &ok ) };
4591 pointPatternFillSl->setDistanceX( vX * 2 + markerSize );
4593 const double vYt { values.at( 0 ).toDouble( &ok ) };
4596 const double vYb { values.at( 2 ).toDouble( &ok ) };
4599 pointPatternFillSl->setDistanceY( ( vYt + vYb ) + markerSize );
4607 const double vYt { values.at( 0 ).toDouble( &ok ) };
4610 const double vYb { values.at( 2 ).toDouble( &ok ) };
4613 pointPatternFillSl->setDistanceY( ( vYt + vYb ) + markerSize );
4616 const double vXr { values.at( 1 ).toDouble( &ok ) };
4619 const double vXl { values.at( 3 ).toDouble( &ok ) };
4622 pointPatternFillSl->setDistanceX( ( vXr + vXl ) + markerSize );
4633 bool distanceFromVendorOption {
false };
4635 for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
4638 if ( it.key() == QLatin1String(
"distance" ) )
4640 distanceParser( it.value().split(
',' ) );
4641 distanceFromVendorOption =
true;
4644 else if ( it.key() == QLatin1String(
"graphic-margin" ) )
4646 distanceParser( it.value().split(
' ' ) );
4647 distanceFromVendorOption =
true;
4652 if ( ! distanceFromVendorOption && ! graphicFillElem.elementsByTagName( QStringLiteral(
"Size" ) ).isEmpty() )
4654 const QDomElement sizeElement { graphicFillElem.elementsByTagName( QStringLiteral(
"Size" ) ).at( 0 ).toElement() };
4656 const double size { sizeElement.text().toDouble( &ok ) };
4659 pointPatternFillSl->setDistanceX( size );
4660 pointPatternFillSl->setDistanceY( size );
4664 return pointPatternFillSl.release();