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();
2720 mDistanceUnit = unit;
2721 mLineWidthUnit = unit;
2724 if ( mFillLineSymbol )
2725 mFillLineSymbol->setOutputUnit( unit );
2748 mDistanceMapUnitScale = scale;
2749 mLineWidthMapUnitScale = scale;
2750 mOffsetMapUnitScale = scale;
2756 mDistanceMapUnitScale == mLineWidthMapUnitScale &&
2757 mLineWidthMapUnitScale == mOffsetMapUnitScale )
2759 return mDistanceMapUnitScale;
2766 std::unique_ptr< QgsLinePatternFillSymbolLayer > patternLayer = std::make_unique< QgsLinePatternFillSymbolLayer >();
2772 QColor
color( Qt::black );
2775 if (
properties.contains( QStringLiteral(
"lineangle" ) ) )
2780 else if (
properties.contains( QStringLiteral(
"angle" ) ) )
2784 patternLayer->setLineAngle(
lineAngle );
2786 if (
properties.contains( QStringLiteral(
"distance" ) ) )
2790 patternLayer->setDistance(
distance );
2792 if (
properties.contains( QStringLiteral(
"linewidth" ) ) )
2797 else if (
properties.contains( QStringLiteral(
"outline_width" ) ) )
2801 else if (
properties.contains( QStringLiteral(
"line_width" ) ) )
2805 patternLayer->setLineWidth(
lineWidth );
2807 if (
properties.contains( QStringLiteral(
"color" ) ) )
2811 else if (
properties.contains( QStringLiteral(
"outline_color" ) ) )
2815 else if (
properties.contains( QStringLiteral(
"line_color" ) ) )
2819 patternLayer->setColor(
color );
2821 if (
properties.contains( QStringLiteral(
"offset" ) ) )
2825 patternLayer->setOffset(
offset );
2828 if (
properties.contains( QStringLiteral(
"distance_unit" ) ) )
2832 if (
properties.contains( QStringLiteral(
"distance_map_unit_scale" ) ) )
2836 if (
properties.contains( QStringLiteral(
"line_width_unit" ) ) )
2840 else if (
properties.contains( QStringLiteral(
"outline_width_unit" ) ) )
2844 if (
properties.contains( QStringLiteral(
"line_width_map_unit_scale" ) ) )
2848 if (
properties.contains( QStringLiteral(
"offset_unit" ) ) )
2852 if (
properties.contains( QStringLiteral(
"offset_map_unit_scale" ) ) )
2856 if (
properties.contains( QStringLiteral(
"outline_width_unit" ) ) )
2860 if (
properties.contains( QStringLiteral(
"outline_width_map_unit_scale" ) ) )
2864 if (
properties.contains( QStringLiteral(
"coordinate_reference" ) ) )
2868 if (
properties.contains( QStringLiteral(
"clip_mode" ) ) )
2873 patternLayer->restoreOldDataDefinedProperties(
properties );
2875 return patternLayer.release();
2880 return QStringLiteral(
"LinePatternFill" );
2883bool QgsLinePatternFillSymbolLayer::applyPattern(
const QgsSymbolRenderContext &context, QBrush &brush,
double lineAngle,
double distance )
2885 mBrush.setTextureImage( QImage() );
2887 if ( !mFillLineSymbol )
2892 std::unique_ptr< QgsLineSymbol > fillLineSymbol( mFillLineSymbol->clone() );
2893 if ( !fillLineSymbol )
2909 outputPixelOffset = std::fmod( outputPixelOffset, outputPixelDist );
2910 if ( outputPixelOffset > outputPixelDist / 2.0 )
2911 outputPixelOffset -= outputPixelDist;
2915 double outputPixelBleed = 0;
2916 double outputPixelInterval = 0;
2917 for (
int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2921 outputPixelBleed = std::max( outputPixelBleed, outputPixelLayerBleed );
2924 if ( markerLineLayer )
2933 outputPixelInterval = std::max( outputPixelInterval, outputPixelLayerInterval );
2937 if ( outputPixelInterval > 0 )
2941 double intervalScale = std::round( outputPixelInterval ) / outputPixelInterval;
2942 outputPixelInterval = std::round( outputPixelInterval );
2944 for (
int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2949 if ( markerLineLayer )
2963 height = outputPixelDist;
2964 width = outputPixelInterval > 0 ? outputPixelInterval : height;
2968 width = outputPixelDist;
2969 height = outputPixelInterval > 0 ? outputPixelInterval : width;
2973 height = outputPixelDist / std::cos(
lineAngle * M_PI / 180 );
2974 width = outputPixelDist / std::sin(
lineAngle * M_PI / 180 );
2977 lineAngle = 180 * std::atan2(
static_cast< double >( height ),
static_cast< double >( width ) ) / M_PI;
2983 height = std::abs( height );
2984 width = std::abs( width );
2986 outputPixelDist = std::abs( height * std::cos(
lineAngle * M_PI / 180 ) );
2990 int offsetHeight =
static_cast< int >( std::round( outputPixelOffset / std::cos(
lineAngle * M_PI / 180 ) ) );
2991 outputPixelOffset = offsetHeight * std::cos(
lineAngle * M_PI / 180 );
3000 int bufferMulti =
static_cast< int >( std::max( std::ceil( outputPixelBleed / width ), std::ceil( outputPixelBleed / width ) ) );
3004 bufferMulti = std::max( bufferMulti, 1 );
3006 int xBuffer = width * bufferMulti;
3007 int yBuffer = height * bufferMulti;
3008 int innerWidth = width;
3009 int innerHeight = height;
3010 width += 2 * xBuffer;
3011 height += 2 * yBuffer;
3014 if ( width > 2000 || height > 2000 || width == 0 || height == 0 )
3019 QImage patternImage( width, height, QImage::Format_ARGB32 );
3020 patternImage.fill( 0 );
3022 QPointF p1, p2, p3, p4, p5, p6;
3025 p1 = QPointF( 0, yBuffer );
3026 p2 = QPointF( width, yBuffer );
3027 p3 = QPointF( 0, yBuffer + innerHeight );
3028 p4 = QPointF( width, yBuffer + innerHeight );
3032 p1 = QPointF( xBuffer, height );
3033 p2 = QPointF( xBuffer, 0 );
3034 p3 = QPointF( xBuffer + innerWidth, height );
3035 p4 = QPointF( xBuffer + innerWidth, 0 );
3039 dx = outputPixelDist * std::cos( ( 90 -
lineAngle ) * M_PI / 180.0 );
3040 dy = outputPixelDist * std::sin( ( 90 -
lineAngle ) * M_PI / 180.0 );
3041 p1 = QPointF( 0, height );
3042 p2 = QPointF( width, 0 );
3043 p3 = QPointF( -dx, height - dy );
3044 p4 = QPointF( width - dx, -dy );
3045 p5 = QPointF( dx, height + dy );
3046 p6 = QPointF( width + dx, dy );
3050 dx = outputPixelDist * std::cos( ( 90 -
lineAngle ) * M_PI / 180.0 );
3051 dy = outputPixelDist * std::sin( ( 90 -
lineAngle ) * M_PI / 180.0 );
3052 p1 = QPointF( width, 0 );
3053 p2 = QPointF( 0, height );
3054 p3 = QPointF( width - dx, -dy );
3055 p4 = QPointF( -dx, height - dy );
3056 p5 = QPointF( width + dx, dy );
3057 p6 = QPointF( dx, height + dy );
3061 dy = outputPixelDist * std::cos( ( 180 -
lineAngle ) * M_PI / 180 );
3062 dx = outputPixelDist * std::sin( ( 180 -
lineAngle ) * M_PI / 180 );
3063 p1 = QPointF( 0, 0 );
3064 p2 = QPointF( width, height );
3065 p5 = QPointF( dx, -dy );
3066 p6 = QPointF( width + dx, height - dy );
3067 p3 = QPointF( -dx, dy );
3068 p4 = QPointF( width - dx, height + dy );
3072 dy = outputPixelDist * std::cos( ( 180 -
lineAngle ) * M_PI / 180 );
3073 dx = outputPixelDist * std::sin( ( 180 -
lineAngle ) * M_PI / 180 );
3074 p1 = QPointF( width, height );
3075 p2 = QPointF( 0, 0 );
3076 p5 = QPointF( width + dx, height - dy );
3077 p6 = QPointF( dx, -dy );
3078 p3 = QPointF( width - dx, height + dy );
3079 p4 = QPointF( -dx, dy );
3086 p3 = QPointF( tempPt.x(), tempPt.y() );
3088 p4 = QPointF( tempPt.x(), tempPt.y() );
3090 p5 = QPointF( tempPt.x(), tempPt.y() );
3092 p6 = QPointF( tempPt.x(), tempPt.y() );
3096 p1 = QPointF( tempPt.x(), tempPt.y() );
3098 p2 = QPointF( tempPt.x(), tempPt.y() );
3101 QPainter p( &patternImage );
3105 p.setRenderHint( QPainter::Antialiasing,
false );
3106 QPen pen( QColor( Qt::black ) );
3107 pen.setWidthF( 0.1 );
3108 pen.setCapStyle( Qt::FlatCap );
3113 QPolygon polygon = QPolygon() << QPoint( 0, 0 ) << QPoint( width - 1, 0 ) << QPoint( width - 1, height - 1 ) << QPoint( 0, height - 1 ) << QPoint( 0, 0 );
3114 p.drawPolygon( polygon );
3116 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 );
3117 p.drawPolygon( polygon );
3123 p.setRenderHint( QPainter::Antialiasing,
true );
3137 fillLineSymbol->startRender( lineRenderContext, context.
fields() );
3139 QVector<QPolygonF> polygons;
3140 polygons.append( QPolygonF() << p1 << p2 );
3141 polygons.append( QPolygonF() << p3 << p4 );
3144 polygons.append( QPolygonF() << p5 << p6 );
3148 for (
const QPolygonF &polygon : std::as_const( polygons ) )
3150 fillLineSymbol->renderPolyline( polygon, context.
feature(), lineRenderContext, -1, useSelectedColor );
3153 fillLineSymbol->stopRender( lineRenderContext );
3157 patternImage = patternImage.copy( xBuffer, yBuffer, patternImage.width() - 2 * xBuffer, patternImage.height() - 2 * yBuffer );
3162 QImage transparentImage = patternImage.copy();
3164 brush.setTextureImage( transparentImage );
3168 brush.setTextureImage( patternImage );
3171 QTransform brushTransform;
3172 brush.setTransform( brushTransform );
3182 || ( mFillLineSymbol && mFillLineSymbol->hasDataDefinedProperties() )
3186 if ( !mRenderUsingLines )
3190 mRenderUsingLines = !applyPattern( context,
mBrush, mLineAngle, mDistance );
3193 if ( mRenderUsingLines && mFillLineSymbol )
3197 mFillLineSymbolRenderStarted =
true;
3203 if ( mFillLineSymbolRenderStarted )
3206 mFillLineSymbolRenderStarted =
false;
3213 if ( !useSelectedColor && !mRenderUsingLines )
3220 if ( !mFillLineSymbolRenderStarted && mFillLineSymbol )
3224 mFillLineSymbolRenderStarted =
true;
3254 outputPixelOffset = std::fmod( outputPixelOffset, outputPixelDistance );
3255 if ( outputPixelOffset > outputPixelDistance / 2.0 )
3256 outputPixelOffset -= outputPixelDistance;
3258 p->setPen( QPen( Qt::NoPen ) );
3280 std::unique_ptr< QgsPolygon > shapePolygon;
3281 std::unique_ptr< QgsGeometryEngine > shapeEngine;
3289 shapePolygon = std::make_unique< QgsPolygon >();
3291 shapePolygon->setExteriorRing( fromPolygon.release() );
3294 for (
const QPolygonF &ring : *rings )
3297 shapePolygon->addInteriorRing( fromRing.release() );
3301 shapeEngine->prepareGeometry();
3308 path.addPolygon( points );
3311 for (
const QPolygonF &ring : *rings )
3313 path.addPolygon( ring );
3316 p->setClipPath( path, Qt::IntersectClip );
3322 const QRectF boundingRect = points.boundingRect();
3324 QTransform invertedRotateTransform;
3330 QTransform transform;
3331 if ( applyBrushTransform )
3334 transform.translate( -boundingRect.center().x(),
3335 -boundingRect.center().y() );
3337 transform.translate( boundingRect.center().x(),
3338 boundingRect.center().y() );
3346 const QRectF transformedBounds = transform.map( points ).boundingRect();
3350 left = transformedBounds.left() - buffer * 2;
3351 top = transformedBounds.top() - buffer * 2;
3352 right = transformedBounds.right() + buffer * 2;
3353 bottom = transformedBounds.bottom() + buffer * 2;
3354 invertedRotateTransform = transform.inverted();
3356 if ( !applyBrushTransform )
3358 top -= transformedBounds.top() - ( outputPixelDistance * std::floor( transformedBounds.top() / outputPixelDistance ) );
3363 const bool needsExpressionContext = mFillLineSymbol->hasDataDefinedProperties();
3368 int currentLine = 0;
3369 for (
double currentY = top; currentY <= bottom; currentY += outputPixelDistance )
3374 if ( needsExpressionContext )
3378 double y1 = currentY;
3380 double y2 = currentY;
3381 invertedRotateTransform.map( left, currentY - outputPixelOffset, &x1, &y1 );
3382 invertedRotateTransform.map( right, currentY - outputPixelOffset, &x2, &y2 );
3387 std::unique_ptr< QgsAbstractGeometry > intersection( shapeEngine->intersection( &ls ) );
3388 for (
auto it = intersection->const_parts_begin(); it != intersection->const_parts_end(); ++it )
3390 if (
const QgsLineString *ls = qgsgeometry_cast< const QgsLineString * >( *it ) )
3398 mFillLineSymbol->renderPolyline( QPolygonF() << QPointF( x1, y1 ) << QPointF( x2, y2 ), context.
feature(), context.
renderContext(), -1, useSelectedColor );
3410 map.insert( QStringLiteral(
"angle" ), QString::number( mLineAngle ) );
3411 map.insert( QStringLiteral(
"distance" ), QString::number( mDistance ) );
3412 map.insert( QStringLiteral(
"line_width" ), QString::number( mLineWidth ) );
3414 map.insert( QStringLiteral(
"offset" ), QString::number( mOffset ) );
3430 if ( mFillLineSymbol )
3441 QDomElement symbolizerElem = doc.createElement( QStringLiteral(
"se:PolygonSymbolizer" ) );
3442 if ( !props.value( QStringLiteral(
"uom" ), QString() ).toString().isEmpty() )
3443 symbolizerElem.setAttribute( QStringLiteral(
"uom" ), props.value( QStringLiteral(
"uom" ), QString() ).toString() );
3444 element.appendChild( symbolizerElem );
3449 QDomElement fillElem = doc.createElement( QStringLiteral(
"se:Fill" ) );
3450 symbolizerElem.appendChild( fillElem );
3452 QDomElement graphicFillElem = doc.createElement( QStringLiteral(
"se:GraphicFill" ) );
3453 fillElem.appendChild( graphicFillElem );
3455 QDomElement graphicElem = doc.createElement( QStringLiteral(
"se:Graphic" ) );
3456 graphicFillElem.appendChild( graphicElem );
3461 bool exportOk {
false };
3465 if ( ! image.isNull() )
3467 const QFileInfo info { context.exportFilePath() };
3468 QString pngPath { info.completeSuffix().isEmpty() ? context.exportFilePath() : context.exportFilePath().chopped( info.completeSuffix().length() ).append( QStringLiteral(
"png" ) ) };
3470 image.save( pngPath );
3479 QColor lineColor = mFillLineSymbol ? mFillLineSymbol->color() : QColor();
3480 double lineWidth = mFillLineSymbol ? mFillLineSymbol->width() : 0.0;
3488 double angle = props.value( QStringLiteral(
"angle" ), QStringLiteral(
"0" ) ).toDouble( &ok );
3491 angleFunc = QStringLiteral(
"%1 + %2" ).arg( props.value( QStringLiteral(
"angle" ), QStringLiteral(
"0" ) ).toString() ).arg( mLineAngle );
3495 angleFunc = QString::number(
angle + mLineAngle );
3500 QPointF lineOffset( std::sin( mLineAngle ) * mOffset, std::cos( mLineAngle ) * mOffset );
3508 QString featureStyle;
3509 featureStyle.append(
"Brush(" );
3510 featureStyle.append( QStringLiteral(
"fc:%1" ).arg(
mColor.name() ) );
3511 featureStyle.append( QStringLiteral(
",bc:%1" ).arg( QLatin1String(
"#00000000" ) ) );
3512 featureStyle.append(
",id:\"ogr-brush-2\"" );
3513 featureStyle.append( QStringLiteral(
",a:%1" ).arg( mLineAngle ) );
3514 featureStyle.append( QStringLiteral(
",s:%1" ).arg( mLineWidth * widthScaleFactor ) );
3515 featureStyle.append(
",dx:0mm" );
3516 featureStyle.append( QStringLiteral(
",dy:%1mm" ).arg( mDistance * widthScaleFactor ) );
3517 featureStyle.append(
')' );
3518 return featureStyle;
3524 && ( !mFillLineSymbol || !mFillLineSymbol->hasDataDefinedProperties() ) )
3549 Qt::PenStyle lineStyle;
3551 QDomElement fillElem = element.firstChildElement( QStringLiteral(
"Fill" ) );
3552 if ( fillElem.isNull() )
3555 QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral(
"GraphicFill" ) );
3556 if ( graphicFillElem.isNull() )
3559 QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral(
"Graphic" ) );
3560 if ( graphicElem.isNull() )
3566 if ( name != QLatin1String(
"horline" ) )
3574 double d = angleFunc.toDouble( &ok );
3583 offset = std::sqrt( std::pow( vectOffset.x(), 2 ) + std::pow( vectOffset.y(), 2 ) );
3586 double scaleFactor = 1.0;
3587 const QString uom = element.attribute( QStringLiteral(
"uom" ) );
3589 size = size * scaleFactor;
3592 std::unique_ptr< QgsLinePatternFillSymbolLayer > sl = std::make_unique< QgsLinePatternFillSymbolLayer >();
3593 sl->setOutputUnit( sldUnitSize );
3594 sl->setColor( lineColor );
3596 sl->setLineAngle(
angle );
3598 sl->setDistance( size );
3601 QDomElement strokeElem = element.firstChildElement( QStringLiteral(
"Stroke" ) );
3602 if ( !strokeElem.isNull() )
3613 return sl.release();
3713 std::unique_ptr< QgsPointPatternFillSymbolLayer > layer = std::make_unique< QgsPointPatternFillSymbolLayer >();
3714 if (
properties.contains( QStringLiteral(
"distance_x" ) ) )
3716 layer->setDistanceX(
properties[QStringLiteral(
"distance_x" )].toDouble() );
3718 if (
properties.contains( QStringLiteral(
"distance_y" ) ) )
3720 layer->setDistanceY(
properties[QStringLiteral(
"distance_y" )].toDouble() );
3722 if (
properties.contains( QStringLiteral(
"displacement_x" ) ) )
3724 layer->setDisplacementX(
properties[QStringLiteral(
"displacement_x" )].toDouble() );
3726 if (
properties.contains( QStringLiteral(
"displacement_y" ) ) )
3728 layer->setDisplacementY(
properties[QStringLiteral(
"displacement_y" )].toDouble() );
3730 if (
properties.contains( QStringLiteral(
"offset_x" ) ) )
3732 layer->setOffsetX(
properties[QStringLiteral(
"offset_x" )].toDouble() );
3734 if (
properties.contains( QStringLiteral(
"offset_y" ) ) )
3736 layer->setOffsetY(
properties[QStringLiteral(
"offset_y" )].toDouble() );
3739 if (
properties.contains( QStringLiteral(
"distance_x_unit" ) ) )
3743 if (
properties.contains( QStringLiteral(
"distance_x_map_unit_scale" ) ) )
3747 if (
properties.contains( QStringLiteral(
"distance_y_unit" ) ) )
3751 if (
properties.contains( QStringLiteral(
"distance_y_map_unit_scale" ) ) )
3755 if (
properties.contains( QStringLiteral(
"displacement_x_unit" ) ) )
3759 if (
properties.contains( QStringLiteral(
"displacement_x_map_unit_scale" ) ) )
3763 if (
properties.contains( QStringLiteral(
"displacement_y_unit" ) ) )
3767 if (
properties.contains( QStringLiteral(
"displacement_y_map_unit_scale" ) ) )
3771 if (
properties.contains( QStringLiteral(
"offset_x_unit" ) ) )
3775 if (
properties.contains( QStringLiteral(
"offset_x_map_unit_scale" ) ) )
3779 if (
properties.contains( QStringLiteral(
"offset_y_unit" ) ) )
3783 if (
properties.contains( QStringLiteral(
"offset_y_map_unit_scale" ) ) )
3788 if (
properties.contains( QStringLiteral(
"random_deviation_x" ) ) )
3790 layer->setMaximumRandomDeviationX(
properties[QStringLiteral(
"random_deviation_x" )].toDouble() );
3792 if (
properties.contains( QStringLiteral(
"random_deviation_y" ) ) )
3794 layer->setMaximumRandomDeviationY(
properties[QStringLiteral(
"random_deviation_y" )].toDouble() );
3796 if (
properties.contains( QStringLiteral(
"random_deviation_x_unit" ) ) )
3800 if (
properties.contains( QStringLiteral(
"random_deviation_x_map_unit_scale" ) ) )
3804 if (
properties.contains( QStringLiteral(
"random_deviation_y_unit" ) ) )
3808 if (
properties.contains( QStringLiteral(
"random_deviation_y_map_unit_scale" ) ) )
3812 unsigned long seed = 0;
3813 if (
properties.contains( QStringLiteral(
"seed" ) ) )
3819 std::random_device rd;
3820 std::mt19937 mt(
seed == 0 ? rd() :
seed );
3821 std::uniform_int_distribution<> uniformDist( 1, 999999999 );
3822 seed = uniformDist( mt );
3824 layer->setSeed(
seed );
3826 if (
properties.contains( QStringLiteral(
"outline_width_unit" ) ) )
3830 if (
properties.contains( QStringLiteral(
"outline_width_map_unit_scale" ) ) )
3834 if (
properties.contains( QStringLiteral(
"clip_mode" ) ) )
3838 if (
properties.contains( QStringLiteral(
"coordinate_reference" ) ) )
3843 if (
properties.contains( QStringLiteral(
"angle" ) ) )
3845 layer->setAngle(
properties[QStringLiteral(
"angle" )].toDouble() );
3848 layer->restoreOldDataDefinedProperties(
properties );
3850 return layer.release();
3855 return QStringLiteral(
"PointPatternFill" );
3858bool QgsPointPatternFillSymbolLayer::applyPattern(
const QgsSymbolRenderContext &context, QBrush &brush,
double distanceX,
double distanceY,
3859 double displacementX,
double displacementY,
double offsetX,
double offsetY )
3866 double widthOffset = std::fmod(
3869 double heightOffset = std::fmod(
3873 if ( width > 2000 || height > 2000 )
3875 brush.setTextureImage( QImage() );
3879 QImage patternImage( width, height, QImage::Format_ARGB32 );
3880 patternImage.fill( 0 );
3881 if ( patternImage.isNull() )
3883 brush.setTextureImage( QImage() );
3888 QPainter p( &patternImage );
3907 for (
double currentX = -width; currentX <= width * 2.0; currentX += width )
3909 for (
double currentY = -height; currentY <= height * 2.0; currentY += height )
3911 mMarkerSymbol->renderPoint( QPointF( currentX + widthOffset, currentY + heightOffset ), context.
feature(), pointRenderContext );
3922 for (
double currentX = -width; currentX <= width * 2.0; currentX += width )
3924 for (
double currentY = -height / 2.0; currentY <= height * 2.0; currentY += height )
3926 mMarkerSymbol->renderPoint( QPointF( currentX + widthOffset + displacementPixelX, currentY + heightOffset ), context.
feature(), pointRenderContext );
3930 for (
double currentX = -width / 2.0; currentX <= width * 2.0; currentX += width )
3932 for (
double currentY = -height; currentY <= height * 2.0; currentY += height / 2.0 )
3934 mMarkerSymbol->renderPoint( QPointF( currentX + widthOffset + ( std::fmod( currentY, height ) != 0 ? displacementPixelX : 0 ), currentY + heightOffset - displacementPixelY ), context.feature(), pointRenderContext );
3943 QImage transparentImage = patternImage.copy();
3945 brush.setTextureImage( transparentImage );
3949 brush.setTextureImage( patternImage );
3951 QTransform brushTransform;
3952 brush.setTransform( brushTransform );
3972 if ( !mRenderUsingMarkers )
4017 if ( !useSelectedColor && !mRenderUsingMarkers )
4067 const double widthOffset = std::fmod(
4079 const double heightOffset = std::fmod(
4105 p->setPen( QPen( Qt::NoPen ) );
4127 std::unique_ptr< QgsPolygon > shapePolygon;
4128 std::unique_ptr< QgsGeometryEngine > shapeEngine;
4135 shapePolygon = std::make_unique< QgsPolygon >();
4137 shapePolygon->setExteriorRing( fromPolygon.release() );
4140 for (
const QPolygonF &ring : *rings )
4143 shapePolygon->addInteriorRing( fromRing.release() );
4147 shapeEngine->prepareGeometry();
4154 path.addPolygon( points );
4157 for (
const QPolygonF &ring : *rings )
4159 path.addPolygon( ring );
4162 p->setClipPath( path, Qt::IntersectClip );
4168 const QRectF boundingRect = points.boundingRect();
4170 QTransform invertedRotateTransform;
4178 QTransform transform;
4179 if ( applyBrushTransform )
4182 transform.translate( -boundingRect.center().x(),
4183 -boundingRect.center().y() );
4184 transform.rotate( -
angle );
4185 transform.translate( boundingRect.center().x(),
4186 boundingRect.center().y() );
4191 transform.rotate( -
angle );
4194 const QRectF transformedBounds = transform.map( points ).boundingRect();
4195 left = transformedBounds.left() - 2 * width;
4196 top = transformedBounds.top() - 2 * height;
4197 right = transformedBounds.right() + 2 * width;
4198 bottom = transformedBounds.bottom() + 2 * height;
4199 invertedRotateTransform = transform.inverted();
4201 if ( !applyBrushTransform )
4203 left -= transformedBounds.left() - ( width * std::floor( transformedBounds.left() / width ) );
4204 top -= transformedBounds.top() - ( height * std::floor( transformedBounds.top() / height ) );
4209 left = boundingRect.left() - 2 * width;
4210 top = boundingRect.top() - 2 * height;
4211 right = boundingRect.right() + 2 * width;
4212 bottom = boundingRect.bottom() + 2 * height;
4214 if ( !applyBrushTransform )
4216 left -= boundingRect.left() - ( width * std::floor( boundingRect.left() / width ) );
4217 top -= boundingRect.top() - ( height * std::floor( boundingRect.top() / height ) );
4246 std::random_device rd;
4247 std::mt19937 mt(
seed == 0 ? rd() :
seed );
4248 std::uniform_real_distribution<> uniformDist( 0, 1 );
4254 const bool needsExpressionContext =
mMarkerSymbol->hasDataDefinedProperties();
4262 bool alternateColumn =
false;
4263 int currentCol = -3;
4264 for (
double currentX = left; currentX <= right; currentX += width, alternateColumn = !alternateColumn )
4269 if ( needsExpressionContext )
4272 bool alternateRow =
false;
4273 const double columnX = currentX + widthOffset;
4274 int currentRow = -3;
4275 for (
double currentY = top; currentY <= bottom; currentY += height, alternateRow = !alternateRow )
4280 double y = currentY + heightOffset;
4283 x += displacementPixelX;
4285 if ( !alternateColumn )
4286 y -= displacementPixelY;
4292 invertedRotateTransform.map( xx, yy, &x, &y );
4295 if ( useRandomShift )
4297 x += ( 2 * uniformDist( mt ) - 1 ) * maxRandomDeviationPixelX;
4298 y += ( 2 * uniformDist( mt ) - 1 ) * maxRandomDeviationPixelY;
4301 if ( needsExpressionContext )
4309 bool renderPoint =
true;
4317 renderPoint = shapeEngine->intersects( &p );
4329 renderPoint = shapeEngine->intersects( markerBounds.
constGet() );
4355 map.insert( QStringLiteral(
"distance_x" ), QString::number(
mDistanceX ) );
4356 map.insert( QStringLiteral(
"distance_y" ), QString::number(
mDistanceY ) );
4357 map.insert( QStringLiteral(
"displacement_x" ), QString::number(
mDisplacementX ) );
4358 map.insert( QStringLiteral(
"displacement_y" ), QString::number(
mDisplacementY ) );
4359 map.insert( QStringLiteral(
"offset_x" ), QString::number(
mOffsetX ) );
4360 map.insert( QStringLiteral(
"offset_y" ), QString::number(
mOffsetY ) );
4376 map.insert( QStringLiteral(
"random_deviation_x" ), QString::number(
mRandomDeviationX ) );
4377 map.insert( QStringLiteral(
"random_deviation_y" ), QString::number(
mRandomDeviationY ) );
4382 map.insert( QStringLiteral(
"seed" ), QString::number(
mSeed ) );
4383 map.insert( QStringLiteral(
"angle" ),
mAngle );
4402 for (
int symbolLayerIdx = 0; symbolLayerIdx <
mMarkerSymbol->symbolLayerCount(); symbolLayerIdx++ )
4404 QDomElement symbolizerElem = doc.createElement( QStringLiteral(
"se:PolygonSymbolizer" ) );
4405 if ( !props.value( QStringLiteral(
"uom" ), QString() ).toString().isEmpty() )
4406 symbolizerElem.setAttribute( QStringLiteral(
"uom" ), props.value( QStringLiteral(
"uom" ), QString() ).toString() );
4407 element.appendChild( symbolizerElem );
4412 QDomElement fillElem = doc.createElement( QStringLiteral(
"se:Fill" ) );
4413 symbolizerElem.appendChild( fillElem );
4415 QDomElement graphicFillElem = doc.createElement( QStringLiteral(
"se:GraphicFill" ) );
4416 fillElem.appendChild( graphicFillElem );
4423 bool exportOk {
false };
4427 if ( ! image.isNull() )
4429 QDomElement graphicElem = doc.createElement( QStringLiteral(
"se:Graphic" ) );
4430 graphicFillElem.appendChild( graphicElem );
4431 const QFileInfo info { context.exportFilePath() };
4432 QString pngPath { info.completeSuffix().isEmpty() ? context.exportFilePath() : context.exportFilePath().chopped( info.completeSuffix().length() ).append( QStringLiteral(
"png" ) ) };
4434 image.save( pngPath );
4453 symbolizerElem.appendChild( graphicMarginElem );
4457 markerLayer->writeSldMarker( doc, graphicFillElem, props );
4461 QString errorMsg = QStringLiteral(
"QgsMarkerSymbolLayer expected, %1 found. Skip it." ).arg( layer->
layerType() );
4462 graphicFillElem.appendChild( doc.createComment( errorMsg ) );
4466 QString errorMsg = QStringLiteral(
"Missing point pattern symbol layer. Skip it." );
4467 graphicFillElem.appendChild( doc.createComment( errorMsg ) );
4476 double angleRads { qDegreesToRadians(
mAngle ) };
4485 if ( displacementXPx != 0 )
4490 if ( displacementYPx != 0 )
4497 QPixmap pixmap( size );
4498 pixmap.fill( Qt::transparent );
4500 painter.begin( &pixmap );
4501 painter.setRenderHint( QPainter::Antialiasing );
4509 std::unique_ptr< QgsPointPatternFillSymbolLayer > layerClone(
clone() );
4511 layerClone->setAngle( qRadiansToDegrees( angleRads ) );
4514 layerClone->setMaximumRandomDeviationX( 0 );
4515 layerClone->setMaximumRandomDeviationY( 0 );
4517 layerClone->drawPreviewIcon( symbolContext, pixmap.size() );
4519 return pixmap.toImage();
4527 QDomElement fillElem = element.firstChildElement( QStringLiteral(
"Fill" ) );
4528 if ( fillElem.isNull() )
4531 QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral(
"GraphicFill" ) );
4532 if ( graphicFillElem.isNull() )
4535 QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral(
"Graphic" ) );
4536 if ( graphicElem.isNull() )
4540 if ( !simpleMarkerSl )
4545 layers.append( simpleMarkerSl );
4547 std::unique_ptr< QgsMarkerSymbol > marker = std::make_unique< QgsMarkerSymbol >( layers );
4550 const double markerSize { marker->size() };
4552 std::unique_ptr< QgsPointPatternFillSymbolLayer > pointPatternFillSl = std::make_unique< QgsPointPatternFillSymbolLayer >();
4553 pointPatternFillSl->setSubSymbol( marker.release() );
4558 auto distanceParser = [ & ](
const QStringList & values )
4560 switch ( values.count( ) )
4565 const double v { values.at( 0 ).toDouble( &ok ) };
4568 pointPatternFillSl->setDistanceX( v * 2 + markerSize );
4569 pointPatternFillSl->setDistanceY( v * 2 + markerSize );
4576 const double vX { values.at( 1 ).toDouble( &ok ) };
4579 pointPatternFillSl->setDistanceX( vX * 2 + markerSize );
4581 const double vY { values.at( 0 ).toDouble( &ok ) };
4584 pointPatternFillSl->setDistanceY( vY * 2 + markerSize );
4591 const double vX { values.at( 1 ).toDouble( &ok ) };
4594 pointPatternFillSl->setDistanceX( vX * 2 + markerSize );
4596 const double vYt { values.at( 0 ).toDouble( &ok ) };
4599 const double vYb { values.at( 2 ).toDouble( &ok ) };
4602 pointPatternFillSl->setDistanceY( ( vYt + vYb ) + markerSize );
4610 const double vYt { values.at( 0 ).toDouble( &ok ) };
4613 const double vYb { values.at( 2 ).toDouble( &ok ) };
4616 pointPatternFillSl->setDistanceY( ( vYt + vYb ) + markerSize );
4619 const double vXr { values.at( 1 ).toDouble( &ok ) };
4622 const double vXl { values.at( 3 ).toDouble( &ok ) };
4625 pointPatternFillSl->setDistanceX( ( vXr + vXl ) + markerSize );
4636 bool distanceFromVendorOption {
false };
4638 for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
4641 if ( it.key() == QLatin1String(
"distance" ) )
4643 distanceParser( it.value().split(
',' ) );
4644 distanceFromVendorOption =
true;
4647 else if ( it.key() == QLatin1String(
"graphic-margin" ) )
4649 distanceParser( it.value().split(
' ' ) );
4650 distanceFromVendorOption =
true;
4655 if ( ! distanceFromVendorOption && ! graphicFillElem.elementsByTagName( QStringLiteral(
"Size" ) ).isEmpty() )
4657 const QDomElement sizeElement { graphicFillElem.elementsByTagName( QStringLiteral(
"Size" ) ).at( 0 ).toElement() };
4659 const double size { sizeElement.text().toDouble( &ok ) };
4662 pointPatternFillSl->setDistanceX( size );
4663 pointPatternFillSl->setDistanceY( size );
4667 return pointPatternFillSl.release();