44#include <QSvgRenderer>
45#include <QDomDocument>
55 Qt::PenJoinStyle penJoinStyle )
56 : mBrushStyle( style )
57 , mStrokeColor( strokeColor )
58 , mStrokeStyle( strokeStyle )
59 , mStrokeWidth( strokeWidth )
60 , mPenJoinStyle( penJoinStyle )
104void QgsSimpleFillSymbolLayer::applyDataDefinedSymbology(
QgsSymbolRenderContext &context, QBrush &brush, QPen &pen, QPen &selPen )
129 penColor.setAlphaF( context.
opacity() * penColor.alphaF() );
130 pen.setColor( penColor );
138 double width = exprVal.toDouble( &ok );
142 pen.setWidthF( width );
143 selPen.setWidthF( width );
180 if ( props.contains( QStringLiteral(
"color" ) ) )
182 if ( props.contains( QStringLiteral(
"style" ) ) )
184 if ( props.contains( QStringLiteral(
"color_border" ) ) )
189 else if ( props.contains( QStringLiteral(
"outline_color" ) ) )
193 else if ( props.contains( QStringLiteral(
"line_color" ) ) )
198 if ( props.contains( QStringLiteral(
"style_border" ) ) )
203 else if ( props.contains( QStringLiteral(
"outline_style" ) ) )
207 else if ( props.contains( QStringLiteral(
"line_style" ) ) )
211 if ( props.contains( QStringLiteral(
"width_border" ) ) )
214 strokeWidth = props[QStringLiteral(
"width_border" )].toDouble();
216 else if ( props.contains( QStringLiteral(
"outline_width" ) ) )
218 strokeWidth = props[QStringLiteral(
"outline_width" )].toDouble();
220 else if ( props.contains( QStringLiteral(
"line_width" ) ) )
222 strokeWidth = props[QStringLiteral(
"line_width" )].toDouble();
224 if ( props.contains( QStringLiteral(
"offset" ) ) )
226 if ( props.contains( QStringLiteral(
"joinstyle" ) ) )
231 if ( props.contains( QStringLiteral(
"border_width_unit" ) ) )
235 else if ( props.contains( QStringLiteral(
"outline_width_unit" ) ) )
239 else if ( props.contains( QStringLiteral(
"line_width_unit" ) ) )
243 if ( props.contains( QStringLiteral(
"offset_unit" ) ) )
246 if ( props.contains( QStringLiteral(
"border_width_map_unit_scale" ) ) )
248 if ( props.contains( QStringLiteral(
"offset_map_unit_scale" ) ) )
251 sl->restoreOldDataDefinedProperties( props );
259 return QStringLiteral(
"SimpleFill" );
271 selColor.setAlphaF( context.
opacity() );
331 if (
mBrush.style() == Qt::SolidPattern ||
mBrush.style() == Qt::NoBrush || !
dynamic_cast<QPrinter *
>( p->device() ) )
345 p->setPen( Qt::NoPen );
349 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 );
707 return QStringLiteral(
"GradientFill" );
710void QgsGradientFillSymbolLayer::applyDataDefinedSymbology(
QgsSymbolRenderContext &context,
const QPolygonF &points )
755 if ( currentType == QObject::tr(
"linear" ) )
759 else if ( currentType == QObject::tr(
"radial" ) )
763 else if ( currentType == QObject::tr(
"conical" ) )
777 if ( currentCoordMode == QObject::tr(
"feature" ) )
781 else if ( currentCoordMode == QObject::tr(
"viewport" ) )
795 if ( currentSpread == QObject::tr(
"pad" ) )
799 else if ( currentSpread == QObject::tr(
"repeat" ) )
803 else if ( currentSpread == QObject::tr(
"reflect" ) )
850 if ( refPoint1IsCentroid || refPoint2IsCentroid )
855 QRectF bbox = points.boundingRect();
856 double centroidX = ( centroid.
x() - bbox.left() ) / bbox.width();
857 double centroidY = ( centroid.
y() - bbox.top() ) / bbox.height();
859 if ( refPoint1IsCentroid )
861 refPoint1X = centroidX;
862 refPoint1Y = centroidY;
864 if ( refPoint2IsCentroid )
866 refPoint2X = centroidX;
867 refPoint2Y = centroidY;
873 spread, QPointF( refPoint1X, refPoint1Y ), QPointF( refPoint2X, refPoint2Y ),
angle );
876QPointF QgsGradientFillSymbolLayer::rotateReferencePoint( QPointF refPoint,
double angle )
881 QLineF refLine = QLineF( QPointF( 0.5, 0.5 ), refPoint );
883 refLine.setAngle( refLine.angle() +
angle );
885 QPointF rotatedReferencePoint = refLine.p2();
887 if ( rotatedReferencePoint.x() > 1 )
888 rotatedReferencePoint.setX( 1 );
889 if ( rotatedReferencePoint.x() < 0 )
890 rotatedReferencePoint.setX( 0 );
891 if ( rotatedReferencePoint.y() > 1 )
892 rotatedReferencePoint.setY( 1 );
893 if ( rotatedReferencePoint.y() < 0 )
894 rotatedReferencePoint.setY( 0 );
896 return rotatedReferencePoint;
903 QPointF referencePoint1, QPointF referencePoint2,
const double angle )
908 QColor fillColor2 =
color2;
909 fillColor2.setAlphaF( context.
opacity() * fillColor2.alphaF() );
920 gradient = QLinearGradient( rotatedReferencePoint1, rotatedReferencePoint2 );
923 gradient = QRadialGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).length() );
926 gradient = QConicalGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).
angle() );
932 gradient.setCoordinateMode( QGradient::ObjectBoundingMode );
935 gradient.setCoordinateMode( QGradient::StretchToDeviceMode );
941 gradient.setSpread( QGradient::PadSpread );
944 gradient.setSpread( QGradient::ReflectSpread );
947 gradient.setSpread( QGradient::RepeatSpread );
963 gradient.setColorAt( 1.0, fillColor2 );
967 brush = QBrush( gradient );
974 selColor.setAlphaF( context.
opacity() );
991 applyDataDefinedSymbology( context, points );
995 p->setPen( Qt::NoPen );
1028 map[QStringLiteral(
"color_type" )] = QString::number(
static_cast< int >(
mGradientColorType ) );
1029 map[QStringLiteral(
"type" )] = QString::number(
static_cast<int>(
mGradientType ) );
1030 map[QStringLiteral(
"coordinate_mode" )] = QString::number(
static_cast< int >(
mCoordinateMode ) );
1031 map[QStringLiteral(
"spread" )] = QString::number(
static_cast< int >(
mGradientSpread ) );
1036 map[QStringLiteral(
"angle" )] = QString::number(
mAngle );
1042#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
1066 return sl.release();
1108 int blurRadius,
bool useWholeShape,
double maxDistance )
1109 : mBlurRadius( blurRadius )
1110 , mUseWholeShape( useWholeShape )
1111 , mMaxDistance( maxDistance )
1112 , mColorType( colorType )
1131 if ( props.contains( QStringLiteral(
"color_type" ) ) )
1135 if ( props.contains( QStringLiteral(
"shapeburst_color" ) ) )
1140 else if ( props.contains( QStringLiteral(
"color" ) ) )
1145 if ( props.contains( QStringLiteral(
"shapeburst_color2" ) ) )
1150 else if ( props.contains( QStringLiteral(
"gradient_color2" ) ) )
1154 if ( props.contains( QStringLiteral(
"blur_radius" ) ) )
1156 blurRadius = props[QStringLiteral(
"blur_radius" )].toInt();
1158 if ( props.contains( QStringLiteral(
"use_whole_shape" ) ) )
1160 useWholeShape = props[QStringLiteral(
"use_whole_shape" )].toInt();
1162 if ( props.contains( QStringLiteral(
"max_distance" ) ) )
1164 maxDistance = props[QStringLiteral(
"max_distance" )].toDouble();
1166 if ( props.contains( QStringLiteral(
"offset" ) ) )
1185 if ( props.contains( QStringLiteral(
"offset_unit" ) ) )
1189 if ( props.contains( QStringLiteral(
"distance_unit" ) ) )
1193 if ( props.contains( QStringLiteral(
"offset_map_unit_scale" ) ) )
1197 if ( props.contains( QStringLiteral(
"distance_map_unit_scale" ) ) )
1201 if ( props.contains( QStringLiteral(
"ignore_rings" ) ) )
1203 sl->setIgnoreRings( props[QStringLiteral(
"ignore_rings" )].toInt() );
1207 sl->setColorRamp( gradientRamp );
1210 sl->restoreOldDataDefinedProperties( props );
1212 return sl.release();
1217 return QStringLiteral(
"ShapeburstFill" );
1222 if ( mGradientRamp.get() == ramp )
1225 mGradientRamp.reset( ramp );
1228void QgsShapeburstFillSymbolLayer::applyDataDefinedSymbology(
QgsSymbolRenderContext &context, QColor &color, QColor &color2,
int &blurRadius,
bool &useWholeShape,
1229 double &maxDistance,
bool &ignoreRings )
1286 selColor.setAlphaF( context.
opacity() );
1287 mSelBrush = QBrush( selColor );
1304 if ( useSelectedColor )
1307 p->setBrush( mSelBrush );
1308 QPointF
offset = mOffset;
1343 int outputPixelMaxDist = 0;
1351 std::unique_ptr< QgsGradientColorRamp > twoColorGradientRamp;
1354 twoColorGradientRamp = std::make_unique< QgsGradientColorRamp >( color1,
color2 );
1358 p->setPen( QPen( Qt::NoPen ) );
1363 int pointsWidth =
static_cast< int >( std::round( points.boundingRect().width() ) );
1364 int pointsHeight =
static_cast< int >( std::round( points.boundingRect().height() ) );
1365 int imWidth = pointsWidth + ( sideBuffer * 2 );
1366 int imHeight = pointsHeight + ( sideBuffer * 2 );
1372 std::unique_ptr< QImage > fillImage = std::make_unique< QImage >( imWidth,
1373 imHeight, QImage::Format_ARGB32_Premultiplied );
1374 if ( fillImage->isNull() )
1384 std::unique_ptr< QImage > alphaImage = std::make_unique< QImage >( fillImage->width(), fillImage->height(), QImage::Format_ARGB32_Premultiplied );
1385 if ( alphaImage->isNull() )
1397 fillImage->fill( Qt::black );
1403 alphaImage->fill( Qt::transparent );
1409 QPainter imgPainter;
1410 imgPainter.begin( alphaImage.get() );
1411 imgPainter.setRenderHint( QPainter::Antialiasing,
true );
1412 imgPainter.setBrush( QBrush( Qt::white ) );
1413 imgPainter.setPen( QPen( Qt::black ) );
1414 imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1423 imgPainter.begin( fillImage.get() );
1426 imgPainter.drawImage( 0, 0, *alphaImage );
1433 imgPainter.setBrush( QBrush( Qt::white ) );
1434 imgPainter.setPen( QPen( Qt::black ) );
1435 imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1444 double *dtArray = distanceTransform( fillImage.get(), context.
renderContext() );
1464 imgPainter.begin( fillImage.get() );
1465 imgPainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
1466 imgPainter.drawImage( 0, 0, *alphaImage );
1474 QPointF
offset = mOffset;
1491 p->drawImage( points.boundingRect().left() - sideBuffer, points.boundingRect().top() - sideBuffer, *fillImage );
1502void QgsShapeburstFillSymbolLayer::distanceTransform1d(
double *f,
int n,
int *v,
double *z,
double *d )
1508 for (
int q = 1; q <= n - 1; q++ )
1510 double s = ( ( f[q] +
static_cast< double >( q ) * q ) - ( f[v[k]] + (
static_cast< double >( v[k] ) * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1514 s = ( ( f[q] +
static_cast< double >( q ) * q ) - ( f[v[k]] + (
static_cast< double >( v[k] ) * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1523 for (
int q = 0; q <= n - 1; q++ )
1525 while ( z[k + 1] < q )
1527 d[q] =
static_cast< double >( q - v[k] ) * ( q - v[k] ) + f[v[k]];
1532void QgsShapeburstFillSymbolLayer::distanceTransform2d(
double *im,
int width,
int height,
QgsRenderContext &context )
1534 int maxDimension = std::max( width, height );
1535 double *f =
new double[ maxDimension ];
1536 int *v =
new int[ maxDimension ];
1537 double *z =
new double[ maxDimension + 1 ];
1538 double *d =
new double[ maxDimension ];
1541 for (
int x = 0; x < width; x++ )
1546 for (
int y = 0; y < height; y++ )
1548 f[y] = im[ x +
static_cast< std::size_t
>( y ) * width ];
1550 distanceTransform1d( f, height, v, z, d );
1551 for (
int y = 0; y < height; y++ )
1553 im[ x +
static_cast< std::size_t
>( y ) * width ] = d[y];
1558 for (
int y = 0; y < height; y++ )
1563 for (
int x = 0; x < width; x++ )
1565 f[x] = im[ x +
static_cast< std::size_t
>( y ) * width ];
1567 distanceTransform1d( f, width, v, z, d );
1568 for (
int x = 0; x < width; x++ )
1570 im[ x +
static_cast< std::size_t
>( y ) * width ] = d[x];
1581double *QgsShapeburstFillSymbolLayer::distanceTransform( QImage *im,
QgsRenderContext &context )
1583 int width = im->width();
1584 int height = im->height();
1586 double *dtArray =
new double[
static_cast< std::size_t
>( width ) * height];
1590 std::size_t idx = 0;
1591 for (
int heightIndex = 0; heightIndex < height; ++heightIndex )
1596 const QRgb *scanLine =
reinterpret_cast< const QRgb *
>( im->constScanLine( heightIndex ) );
1597 for (
int widthIndex = 0; widthIndex < width; ++widthIndex )
1599 tmpRgb = scanLine[widthIndex];
1600 if ( qRed( tmpRgb ) == 0 )
1608 dtArray[ idx ] =
INF;
1615 distanceTransform2d( dtArray, width, height, context );
1620void QgsShapeburstFillSymbolLayer::dtArrayToQImage(
double *array, QImage *im,
QgsColorRamp *ramp,
QgsRenderContext &context,
bool useWholeShape,
int maxPixelDistance )
1622 int width = im->width();
1623 int height = im->height();
1626 double maxDistanceValue;
1631 double dtMaxValue = array[0];
1632 for ( std::size_t i = 1; i < static_cast< std::size_t >( width ) * height; ++i )
1634 if ( array[i] > dtMaxValue )
1636 dtMaxValue = array[i];
1641 maxDistanceValue = std::sqrt( dtMaxValue );
1646 maxDistanceValue = maxPixelDistance;
1650 std::size_t idx = 0;
1651 double squaredVal = 0;
1654 for (
int heightIndex = 0; heightIndex < height; ++heightIndex )
1659 QRgb *scanLine =
reinterpret_cast< QRgb *
>( im->scanLine( heightIndex ) );
1660 for (
int widthIndex = 0; widthIndex < width; ++widthIndex )
1663 squaredVal = array[idx];
1666 if ( maxDistanceValue > 0 )
1668 pixVal = squaredVal > 0 ? std::min( ( std::sqrt( squaredVal ) / maxDistanceValue ), 1.0 ) : 0;
1677 scanLine[widthIndex] = qPremultiply( ramp->
color( pixVal ).rgba() );
1688 map[QStringLiteral(
"color_type" )] = QString::number(
static_cast< int >( mColorType ) );
1689 map[QStringLiteral(
"blur_radius" )] = QString::number( mBlurRadius );
1690 map[QStringLiteral(
"use_whole_shape" )] = QString::number( mUseWholeShape );
1691 map[QStringLiteral(
"max_distance" )] = QString::number( mMaxDistance );
1694 map[QStringLiteral(
"ignore_rings" )] = QString::number( mIgnoreRings );
1698 if ( mGradientRamp )
1700#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
1701 map.unite( mGradientRamp->properties() );
1703 map.insert( mGradientRamp->properties() );
1712 std::unique_ptr< QgsShapeburstFillSymbolLayer > sl = std::make_unique< QgsShapeburstFillSymbolLayer >(
mColor, mColor2, mColorType, mBlurRadius, mUseWholeShape, mMaxDistance );
1713 if ( mGradientRamp )
1715 sl->setColorRamp( mGradientRamp->clone() );
1717 sl->setDistanceUnit( mDistanceUnit );
1718 sl->setDistanceMapUnitScale( mDistanceMapUnitScale );
1719 sl->setIgnoreRings( mIgnoreRings );
1720 sl->setOffset( mOffset );
1721 sl->setOffsetUnit( mOffsetUnit );
1722 sl->setOffsetMapUnitScale( mOffsetMapUnitScale );
1725 return sl.release();
1730 double offsetBleed = context.
convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale );
1741 mDistanceUnit = unit;
1747 if ( mDistanceUnit == mOffsetUnit )
1749 return mDistanceUnit;
1762 mDistanceMapUnitScale = scale;
1763 mOffsetMapUnitScale = scale;
1768 if ( mDistanceMapUnitScale == mOffsetMapUnitScale )
1770 return mDistanceMapUnitScale;
1795 p->setPen( QPen( Qt::NoPen ) );
1797 QTransform bkTransform =
mBrush.transform();
1801 QTransform t =
mBrush.transform();
1802 t.translate( leftCorner.x(), leftCorner.y() );
1803 mBrush.setTransform( t );
1807 QTransform t =
mBrush.transform();
1808 t.translate( 0, 0 );
1809 mBrush.setTransform( t );
1813 if ( useSelectedColor )
1816 p->setBrush( QBrush( selColor ) );
1822 QTransform t =
mBrush.transform();
1824 mBrush.setTransform( t );
1829 mBrush.setTransform( bkTransform );
1865 return Qt::SolidLine;
1869 return Qt::SolidLine;
1873 return mStroke->dxfPenStyle();
1909 , mPatternWidth( width )
1913 mColor = QColor( 255, 255, 255 );
1919 , mPatternWidth( width )
1920 , mSvgData( svgData )
1925 mColor = QColor( 255, 255, 255 );
1926 setDefaultSvgParams();
1934 mPatternWidthUnit = unit;
1935 mSvgStrokeWidthUnit = unit;
1938 mStroke->setOutputUnit( unit );
1944 if ( mPatternWidthUnit != unit || mSvgStrokeWidthUnit != unit ||
mStrokeWidthUnit != unit )
1954 mPatternWidthMapUnitScale = scale;
1955 mSvgStrokeWidthMapUnitScale = scale;
1961 mPatternWidthMapUnitScale == mSvgStrokeWidthMapUnitScale &&
1964 return mPatternWidthMapUnitScale;
1974 mSvgFilePath = svgPath;
1975 setDefaultSvgParams();
1985 if (
properties.contains( QStringLiteral(
"width" ) ) )
1987 width =
properties[QStringLiteral(
"width" )].toDouble();
1989 if (
properties.contains( QStringLiteral(
"svgFile" ) ) )
1993 if (
properties.contains( QStringLiteral(
"angle" ) ) )
1998 std::unique_ptr< QgsSVGFillSymbolLayer > symbolLayer;
2001 symbolLayer = std::make_unique< QgsSVGFillSymbolLayer >(
svgFilePath, width,
angle );
2005 if (
properties.contains( QStringLiteral(
"data" ) ) )
2007 data = QByteArray::fromHex(
properties[QStringLiteral(
"data" )].toString().toLocal8Bit() );
2009 symbolLayer = std::make_unique< QgsSVGFillSymbolLayer >( data, width,
angle );
2013 if (
properties.contains( QStringLiteral(
"svgFillColor" ) ) )
2018 else if (
properties.contains( QStringLiteral(
"color" ) ) )
2022 if (
properties.contains( QStringLiteral(
"svgOutlineColor" ) ) )
2027 else if (
properties.contains( QStringLiteral(
"outline_color" ) ) )
2031 else if (
properties.contains( QStringLiteral(
"line_color" ) ) )
2035 if (
properties.contains( QStringLiteral(
"svgOutlineWidth" ) ) )
2038 symbolLayer->setSvgStrokeWidth(
properties[QStringLiteral(
"svgOutlineWidth" )].toDouble() );
2040 else if (
properties.contains( QStringLiteral(
"outline_width" ) ) )
2042 symbolLayer->setSvgStrokeWidth(
properties[QStringLiteral(
"outline_width" )].toDouble() );
2044 else if (
properties.contains( QStringLiteral(
"line_width" ) ) )
2046 symbolLayer->setSvgStrokeWidth(
properties[QStringLiteral(
"line_width" )].toDouble() );
2050 if (
properties.contains( QStringLiteral(
"pattern_width_unit" ) ) )
2054 if (
properties.contains( QStringLiteral(
"pattern_width_map_unit_scale" ) ) )
2058 if (
properties.contains( QStringLiteral(
"svg_outline_width_unit" ) ) )
2062 if (
properties.contains( QStringLiteral(
"svg_outline_width_map_unit_scale" ) ) )
2066 if (
properties.contains( QStringLiteral(
"outline_width_unit" ) ) )
2070 if (
properties.contains( QStringLiteral(
"outline_width_map_unit_scale" ) ) )
2075 if (
properties.contains( QStringLiteral(
"parameters" ) ) )
2081 symbolLayer->restoreOldDataDefinedProperties(
properties );
2083 return symbolLayer.release();
2088 QVariantMap::iterator it =
properties.find( QStringLiteral(
"svgFile" ) );
2100 return QStringLiteral(
"SVGFill" );
2103void QgsSVGFillSymbolLayer::applyPattern( QBrush &brush,
const QString &svgFilePath,
double patternWidth,
Qgis::RenderUnit patternWidthUnit,
2104 const QColor &svgFillColor,
const QColor &svgStrokeColor,
double svgStrokeWidth,
2108 if ( mSvgViewBox.isNull() )
2115 if (
static_cast< int >( size ) < 1.0 || 10000.0 < size )
2117 brush.setTextureImage( QImage() );
2121 bool fitsInCache =
true;
2129 double hwRatio = 1.0;
2130 if ( patternPict.width() > 0 )
2132 hwRatio =
static_cast< double >( patternPict.height() ) /
static_cast< double >( patternPict.width() );
2134 patternImage = QImage(
static_cast< int >( size ),
static_cast< int >( size * hwRatio ), QImage::Format_ARGB32_Premultiplied );
2135 patternImage.fill( 0 );
2137 QPainter p( &patternImage );
2138 p.drawPicture( QPointF( size / 2, size * hwRatio / 2 ), patternPict );
2141 QTransform brushTransform;
2144 QImage transparentImage = patternImage.copy();
2146 brush.setTextureImage( transparentImage );
2150 brush.setTextureImage( patternImage );
2152 brush.setTransform( brushTransform );
2160 applyPattern(
mBrush, mSvgFilePath, mPatternWidth, mPatternWidthUnit,
mColor, mSvgStrokeColor, mSvgStrokeWidth, mSvgStrokeWidthUnit, context, mPatternWidthMapUnitScale, mSvgStrokeWidthMapUnitScale, evaluatedParameters );
2183 mStroke->renderPolyline( points, context.
feature(), context.
renderContext(), -1, useSelectedColor );
2186 for (
auto ringIt = rings->constBegin(); ringIt != rings->constEnd(); ++ringIt )
2188 mStroke->renderPolyline( *ringIt, context.
feature(), context.
renderContext(), -1, useSelectedColor );
2197 if ( !mSvgFilePath.isEmpty() )
2199 map.insert( QStringLiteral(
"svgFile" ), mSvgFilePath );
2203 map.insert( QStringLiteral(
"data" ), QString( mSvgData.toHex() ) );
2206 map.insert( QStringLiteral(
"width" ), QString::number( mPatternWidth ) );
2207 map.insert( QStringLiteral(
"angle" ), QString::number(
mAngle ) );
2212 map.insert( QStringLiteral(
"outline_width" ), QString::number( mSvgStrokeWidth ) );
2229 std::unique_ptr< QgsSVGFillSymbolLayer > clonedLayer;
2230 if ( !mSvgFilePath.isEmpty() )
2232 clonedLayer = std::make_unique< QgsSVGFillSymbolLayer >( mSvgFilePath, mPatternWidth,
mAngle );
2233 clonedLayer->setSvgFillColor(
mColor );
2234 clonedLayer->setSvgStrokeColor( mSvgStrokeColor );
2235 clonedLayer->setSvgStrokeWidth( mSvgStrokeWidth );
2239 clonedLayer = std::make_unique< QgsSVGFillSymbolLayer >( mSvgData, mPatternWidth,
mAngle );
2242 clonedLayer->setPatternWidthUnit( mPatternWidthUnit );
2243 clonedLayer->setPatternWidthMapUnitScale( mPatternWidthMapUnitScale );
2244 clonedLayer->setSvgStrokeWidthUnit( mSvgStrokeWidthUnit );
2245 clonedLayer->setSvgStrokeWidthMapUnitScale( mSvgStrokeWidthMapUnitScale );
2249 clonedLayer->setParameters( mParameters );
2253 clonedLayer->setSubSymbol( mStroke->clone() );
2257 return clonedLayer.release();
2262 QDomElement symbolizerElem = doc.createElement( QStringLiteral(
"se:PolygonSymbolizer" ) );
2263 if ( !props.value( QStringLiteral(
"uom" ), QString() ).toString().isEmpty() )
2264 symbolizerElem.setAttribute( QStringLiteral(
"uom" ), props.value( QStringLiteral(
"uom" ), QString() ).toString() );
2265 element.appendChild( symbolizerElem );
2269 QDomElement fillElem = doc.createElement( QStringLiteral(
"se:Fill" ) );
2270 symbolizerElem.appendChild( fillElem );
2272 QDomElement graphicFillElem = doc.createElement( QStringLiteral(
"se:GraphicFill" ) );
2273 fillElem.appendChild( graphicFillElem );
2275 QDomElement graphicElem = doc.createElement( QStringLiteral(
"se:Graphic" ) );
2276 graphicFillElem.appendChild( graphicElem );
2278 if ( !mSvgFilePath.isEmpty() )
2289 symbolizerElem.appendChild( doc.createComment( QStringLiteral(
"SVG from data not implemented yet" ) ) );
2295 double angle = props.value( QStringLiteral(
"angle" ), QStringLiteral(
"0" ) ).toDouble( &ok );
2298 angleFunc = QStringLiteral(
"%1 + %2" ).arg( props.value( QStringLiteral(
"angle" ), QStringLiteral(
"0" ) ).toString() ).arg(
mAngle );
2311 mStroke->toSld( doc, element, props );
2323 return mStroke.get();
2330 mStroke.reset(
nullptr );
2343 mStroke.reset( lineSymbol );
2353 if ( mStroke && mStroke->symbolLayer( 0 ) )
2355 double subLayerBleed = mStroke->symbolLayer( 0 )->estimateMaxBleed( context );
2356 return subLayerBleed;
2366 return QColor( Qt::black );
2368 return mStroke->color();
2375 attr.unite( mStroke->usedAttributes( context ) );
2383 if ( mStroke && mStroke->hasDataDefinedProperties() )
2390 QString path, mimeType;
2392 Qt::PenStyle penStyle;
2393 double size, strokeWidth;
2395 QDomElement fillElem = element.firstChildElement( QStringLiteral(
"Fill" ) );
2396 if ( fillElem.isNull() )
2399 QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral(
"GraphicFill" ) );
2400 if ( graphicFillElem.isNull() )
2403 QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral(
"Graphic" ) );
2404 if ( graphicElem.isNull() )
2410 if ( mimeType != QLatin1String(
"image/svg+xml" ) )
2415 double scaleFactor = 1.0;
2416 const QString uom = element.attribute( QStringLiteral(
"uom" ) );
2418 size = size * scaleFactor;
2419 strokeWidth = strokeWidth * scaleFactor;
2426 double d = angleFunc.toDouble( &ok );
2431 std::unique_ptr< QgsSVGFillSymbolLayer > sl = std::make_unique< QgsSVGFillSymbolLayer >( path, size,
angle );
2432 sl->setOutputUnit( sldUnitSize );
2435 sl->setSvgStrokeWidth( strokeWidth );
2438 QDomElement strokeElem = element.firstChildElement( QStringLiteral(
"Stroke" ) );
2439 if ( !strokeElem.isNull() )
2450 return sl.release();
2468 double width = mPatternWidth;
2474 QString svgFile = mSvgFilePath;
2493 double strokeWidth = mSvgStrokeWidth;
2502 mSvgStrokeWidthUnit, context, mPatternWidthMapUnitScale, mSvgStrokeWidthMapUnitScale, evaluatedParameters );
2506void QgsSVGFillSymbolLayer::storeViewBox()
2508 if ( !mSvgData.isEmpty() )
2510 QSvgRenderer r( mSvgData );
2513 mSvgViewBox = r.viewBoxF();
2518 mSvgViewBox = QRectF();
2521void QgsSVGFillSymbolLayer::setDefaultSvgParams()
2523 if ( mSvgFilePath.isEmpty() )
2528 bool hasFillParam, hasFillOpacityParam, hasStrokeParam, hasStrokeWidthParam, hasStrokeOpacityParam;
2529 bool hasDefaultFillColor, hasDefaultFillOpacity, hasDefaultStrokeColor, hasDefaultStrokeWidth, hasDefaultStrokeOpacity;
2530 QColor defaultFillColor, defaultStrokeColor;
2531 double defaultStrokeWidth, defaultFillOpacity, defaultStrokeOpacity;
2533 hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
2534 hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2535 hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
2536 hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity );
2538 double newFillOpacity = hasFillOpacityParam ?
mColor.alphaF() : 1.0;
2539 double newStrokeOpacity = hasStrokeOpacityParam ? mSvgStrokeColor.alphaF() : 1.0;
2541 if ( hasDefaultFillColor )
2543 mColor = defaultFillColor;
2544 mColor.setAlphaF( newFillOpacity );
2546 if ( hasDefaultFillOpacity )
2548 mColor.setAlphaF( defaultFillOpacity );
2550 if ( hasDefaultStrokeColor )
2552 mSvgStrokeColor = defaultStrokeColor;
2553 mSvgStrokeColor.setAlphaF( newStrokeOpacity );
2555 if ( hasDefaultStrokeOpacity )
2557 mSvgStrokeColor.setAlphaF( defaultStrokeOpacity );
2559 if ( hasDefaultStrokeWidth )
2561 mSvgStrokeWidth = defaultStrokeWidth;
2574 mFillLineSymbol = std::make_unique<QgsLineSymbol>( );
2582 mFillLineSymbol->setWidth( w );
2588 mFillLineSymbol->setColor(
c );
2594 return mFillLineSymbol ? mFillLineSymbol->color() :
mColor;
2606 mFillLineSymbol.reset( qgis::down_cast<QgsLineSymbol *>( symbol ) );
2615 return mFillLineSymbol.get();
2621 if ( mFillLineSymbol )
2622 attr.unite( mFillLineSymbol->usedAttributes( context ) );
2630 if ( mFillLineSymbol && mFillLineSymbol->hasDataDefinedProperties() )
2652 double lineAngleRads { qDegreesToRadians( mLineAngle ) };
2655 QSize size {
static_cast<int>( distancePx ),
static_cast<int>( distancePx ) };
2657 if (
static_cast<int>( mLineAngle ) % 90 != 0 )
2659 size = QSize(
static_cast<int>( distancePx / std::sin( lineAngleRads ) ),
static_cast<int>( distancePx / std::cos( lineAngleRads ) ) );
2662 QPixmap pixmap( size );
2663 pixmap.fill( Qt::transparent );
2665 painter.begin( &pixmap );
2666 painter.setRenderHint( QPainter::Antialiasing );
2674 std::unique_ptr< QgsLinePatternFillSymbolLayer > layerClone(
clone() );
2675 layerClone->setOffset( 0 );
2676 layerClone->drawPreviewIcon( symbolContext, pixmap.size() );
2678 return pixmap.toImage();
2690 mDistanceUnit = unit;
2691 mLineWidthUnit = unit;
2694 if ( mFillLineSymbol )
2695 mFillLineSymbol->setOutputUnit( unit );
2718 mDistanceMapUnitScale = scale;
2719 mLineWidthMapUnitScale = scale;
2720 mOffsetMapUnitScale = scale;
2726 mDistanceMapUnitScale == mLineWidthMapUnitScale &&
2727 mLineWidthMapUnitScale == mOffsetMapUnitScale )
2729 return mDistanceMapUnitScale;
2736 std::unique_ptr< QgsLinePatternFillSymbolLayer > patternLayer = std::make_unique< QgsLinePatternFillSymbolLayer >();
2742 QColor
color( Qt::black );
2745 if (
properties.contains( QStringLiteral(
"lineangle" ) ) )
2750 else if (
properties.contains( QStringLiteral(
"angle" ) ) )
2754 patternLayer->setLineAngle(
lineAngle );
2756 if (
properties.contains( QStringLiteral(
"distance" ) ) )
2760 patternLayer->setDistance(
distance );
2762 if (
properties.contains( QStringLiteral(
"linewidth" ) ) )
2767 else if (
properties.contains( QStringLiteral(
"outline_width" ) ) )
2771 else if (
properties.contains( QStringLiteral(
"line_width" ) ) )
2775 patternLayer->setLineWidth(
lineWidth );
2777 if (
properties.contains( QStringLiteral(
"color" ) ) )
2781 else if (
properties.contains( QStringLiteral(
"outline_color" ) ) )
2785 else if (
properties.contains( QStringLiteral(
"line_color" ) ) )
2789 patternLayer->setColor(
color );
2791 if (
properties.contains( QStringLiteral(
"offset" ) ) )
2795 patternLayer->setOffset(
offset );
2798 if (
properties.contains( QStringLiteral(
"distance_unit" ) ) )
2802 if (
properties.contains( QStringLiteral(
"distance_map_unit_scale" ) ) )
2806 if (
properties.contains( QStringLiteral(
"line_width_unit" ) ) )
2810 else if (
properties.contains( QStringLiteral(
"outline_width_unit" ) ) )
2814 if (
properties.contains( QStringLiteral(
"line_width_map_unit_scale" ) ) )
2818 if (
properties.contains( QStringLiteral(
"offset_unit" ) ) )
2822 if (
properties.contains( QStringLiteral(
"offset_map_unit_scale" ) ) )
2826 if (
properties.contains( QStringLiteral(
"outline_width_unit" ) ) )
2830 if (
properties.contains( QStringLiteral(
"outline_width_map_unit_scale" ) ) )
2834 if (
properties.contains( QStringLiteral(
"coordinate_reference" ) ) )
2838 if (
properties.contains( QStringLiteral(
"clip_mode" ) ) )
2843 patternLayer->restoreOldDataDefinedProperties(
properties );
2845 return patternLayer.release();
2850 return QStringLiteral(
"LinePatternFill" );
2853bool QgsLinePatternFillSymbolLayer::applyPattern(
const QgsSymbolRenderContext &context, QBrush &brush,
double lineAngle,
double distance )
2855 mBrush.setTextureImage( QImage() );
2857 if ( !mFillLineSymbol )
2862 std::unique_ptr< QgsLineSymbol > fillLineSymbol( mFillLineSymbol->clone() );
2863 if ( !fillLineSymbol )
2879 outputPixelOffset = std::fmod( outputPixelOffset, outputPixelDist );
2880 if ( outputPixelOffset > outputPixelDist / 2.0 )
2881 outputPixelOffset -= outputPixelDist;
2885 double outputPixelBleed = 0;
2886 double outputPixelInterval = 0;
2887 for (
int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2891 outputPixelBleed = std::max( outputPixelBleed, outputPixelLayerBleed );
2894 if ( markerLineLayer )
2903 outputPixelInterval = std::max( outputPixelInterval, outputPixelLayerInterval );
2907 if ( outputPixelInterval > 0 )
2911 double intervalScale = std::round( outputPixelInterval ) / outputPixelInterval;
2912 outputPixelInterval = std::round( outputPixelInterval );
2914 for (
int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2919 if ( markerLineLayer )
2933 height = outputPixelDist;
2934 width = outputPixelInterval > 0 ? outputPixelInterval : height;
2938 width = outputPixelDist;
2939 height = outputPixelInterval > 0 ? outputPixelInterval : width;
2943 height = outputPixelDist / std::cos(
lineAngle * M_PI / 180 );
2944 width = outputPixelDist / std::sin(
lineAngle * M_PI / 180 );
2947 lineAngle = 180 * std::atan2(
static_cast< double >( height ),
static_cast< double >( width ) ) / M_PI;
2953 height = std::abs( height );
2954 width = std::abs( width );
2956 outputPixelDist = std::abs( height * std::cos(
lineAngle * M_PI / 180 ) );
2960 int offsetHeight =
static_cast< int >( std::round( outputPixelOffset / std::cos(
lineAngle * M_PI / 180 ) ) );
2961 outputPixelOffset = offsetHeight * std::cos(
lineAngle * M_PI / 180 );
2970 int bufferMulti =
static_cast< int >( std::max( std::ceil( outputPixelBleed / width ), std::ceil( outputPixelBleed / width ) ) );
2974 bufferMulti = std::max( bufferMulti, 1 );
2976 int xBuffer = width * bufferMulti;
2977 int yBuffer = height * bufferMulti;
2978 int innerWidth = width;
2979 int innerHeight = height;
2980 width += 2 * xBuffer;
2981 height += 2 * yBuffer;
2984 if ( width > 2000 || height > 2000 || width == 0 || height == 0 )
2989 QImage patternImage( width, height, QImage::Format_ARGB32 );
2990 patternImage.fill( 0 );
2992 QPointF p1, p2, p3, p4, p5, p6;
2995 p1 = QPointF( 0, yBuffer );
2996 p2 = QPointF( width, yBuffer );
2997 p3 = QPointF( 0, yBuffer + innerHeight );
2998 p4 = QPointF( width, yBuffer + innerHeight );
3002 p1 = QPointF( xBuffer, height );
3003 p2 = QPointF( xBuffer, 0 );
3004 p3 = QPointF( xBuffer + innerWidth, height );
3005 p4 = QPointF( xBuffer + innerWidth, 0 );
3009 dx = outputPixelDist * std::cos( ( 90 -
lineAngle ) * M_PI / 180.0 );
3010 dy = outputPixelDist * std::sin( ( 90 -
lineAngle ) * M_PI / 180.0 );
3011 p1 = QPointF( 0, height );
3012 p2 = QPointF( width, 0 );
3013 p3 = QPointF( -dx, height - dy );
3014 p4 = QPointF( width - dx, -dy );
3015 p5 = QPointF( dx, height + dy );
3016 p6 = QPointF( width + dx, dy );
3020 dx = outputPixelDist * std::cos( ( 90 -
lineAngle ) * M_PI / 180.0 );
3021 dy = outputPixelDist * std::sin( ( 90 -
lineAngle ) * M_PI / 180.0 );
3022 p1 = QPointF( width, 0 );
3023 p2 = QPointF( 0, height );
3024 p3 = QPointF( width - dx, -dy );
3025 p4 = QPointF( -dx, height - dy );
3026 p5 = QPointF( width + dx, dy );
3027 p6 = QPointF( dx, height + dy );
3031 dy = outputPixelDist * std::cos( ( 180 -
lineAngle ) * M_PI / 180 );
3032 dx = outputPixelDist * std::sin( ( 180 -
lineAngle ) * M_PI / 180 );
3033 p1 = QPointF( 0, 0 );
3034 p2 = QPointF( width, height );
3035 p5 = QPointF( dx, -dy );
3036 p6 = QPointF( width + dx, height - dy );
3037 p3 = QPointF( -dx, dy );
3038 p4 = QPointF( width - dx, height + dy );
3042 dy = outputPixelDist * std::cos( ( 180 -
lineAngle ) * M_PI / 180 );
3043 dx = outputPixelDist * std::sin( ( 180 -
lineAngle ) * M_PI / 180 );
3044 p1 = QPointF( width, height );
3045 p2 = QPointF( 0, 0 );
3046 p5 = QPointF( width + dx, height - dy );
3047 p6 = QPointF( dx, -dy );
3048 p3 = QPointF( width - dx, height + dy );
3049 p4 = QPointF( -dx, dy );
3056 p3 = QPointF( tempPt.x(), tempPt.y() );
3058 p4 = QPointF( tempPt.x(), tempPt.y() );
3060 p5 = QPointF( tempPt.x(), tempPt.y() );
3062 p6 = QPointF( tempPt.x(), tempPt.y() );
3066 p1 = QPointF( tempPt.x(), tempPt.y() );
3068 p2 = QPointF( tempPt.x(), tempPt.y() );
3071 QPainter p( &patternImage );
3075 p.setRenderHint( QPainter::Antialiasing,
false );
3076 QPen pen( QColor( Qt::black ) );
3077 pen.setWidthF( 0.1 );
3078 pen.setCapStyle( Qt::FlatCap );
3083 QPolygon polygon = QPolygon() << QPoint( 0, 0 ) << QPoint( width - 1, 0 ) << QPoint( width - 1, height - 1 ) << QPoint( 0, height - 1 ) << QPoint( 0, 0 );
3084 p.drawPolygon( polygon );
3086 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 );
3087 p.drawPolygon( polygon );
3093 p.setRenderHint( QPainter::Antialiasing,
true );
3106 fillLineSymbol->startRender( lineRenderContext, context.
fields() );
3108 QVector<QPolygonF> polygons;
3109 polygons.append( QPolygonF() << p1 << p2 );
3110 polygons.append( QPolygonF() << p3 << p4 );
3113 polygons.append( QPolygonF() << p5 << p6 );
3117 for (
const QPolygonF &polygon : std::as_const( polygons ) )
3119 fillLineSymbol->renderPolyline( polygon, context.
feature(), lineRenderContext, -1, useSelectedColor );
3122 fillLineSymbol->stopRender( lineRenderContext );
3126 patternImage = patternImage.copy( xBuffer, yBuffer, patternImage.width() - 2 * xBuffer, patternImage.height() - 2 * yBuffer );
3131 QImage transparentImage = patternImage.copy();
3133 brush.setTextureImage( transparentImage );
3137 brush.setTextureImage( patternImage );
3140 QTransform brushTransform;
3141 brush.setTransform( brushTransform );
3151 || mFillLineSymbol->hasDataDefinedProperties()
3155 if ( !mRenderUsingLines )
3159 mRenderUsingLines = !applyPattern( context,
mBrush, mLineAngle, mDistance );
3162 if ( mRenderUsingLines )
3164 if ( mFillLineSymbol )
3171 if ( mRenderUsingLines && mFillLineSymbol )
3179 if ( !mRenderUsingLines )
3213 outputPixelOffset = std::fmod( outputPixelOffset, outputPixelDistance );
3214 if ( outputPixelOffset > outputPixelDistance / 2.0 )
3215 outputPixelOffset -= outputPixelDistance;
3217 p->setPen( QPen( Qt::NoPen ) );
3220 if ( useSelectedColor )
3223 p->setBrush( QBrush( selColor ) );
3247 std::unique_ptr< QgsPolygon > shapePolygon;
3248 std::unique_ptr< QgsGeometryEngine > shapeEngine;
3256 shapePolygon = std::make_unique< QgsPolygon >();
3260 for (
const QPolygonF &ring : *rings )
3266 shapeEngine->prepareGeometry();
3273 path.addPolygon( points );
3276 for (
const QPolygonF &ring : *rings )
3278 path.addPolygon( ring );
3281 p->setClipPath( path, Qt::IntersectClip );
3287 const QRectF boundingRect = points.boundingRect();
3289 QTransform invertedRotateTransform;
3295 QTransform transform;
3296 if ( applyBrushTransform )
3299 transform.translate( -boundingRect.center().x(),
3300 -boundingRect.center().y() );
3302 transform.translate( boundingRect.center().x(),
3303 boundingRect.center().y() );
3311 const QRectF transformedBounds = transform.map( points ).boundingRect();
3315 left = transformedBounds.left() - buffer * 2;
3316 top = transformedBounds.top() - buffer * 2;
3317 right = transformedBounds.right() + buffer * 2;
3318 bottom = transformedBounds.bottom() + buffer * 2;
3319 invertedRotateTransform = transform.inverted();
3321 if ( !applyBrushTransform )
3323 top -= transformedBounds.top() - ( outputPixelDistance * std::floor( transformedBounds.top() / outputPixelDistance ) );
3328 const bool needsExpressionContext = mFillLineSymbol->hasDataDefinedProperties();
3333 int currentLine = 0;
3334 for (
double currentY = top; currentY <= bottom; currentY += outputPixelDistance )
3339 if ( needsExpressionContext )
3343 double y1 = currentY;
3345 double y2 = currentY;
3346 invertedRotateTransform.map( left, currentY - outputPixelOffset, &x1, &y1 );
3347 invertedRotateTransform.map( right, currentY - outputPixelOffset, &x2, &y2 );
3352 std::unique_ptr< QgsAbstractGeometry > intersection( shapeEngine->intersection( &ls ) );
3353 for (
auto it = intersection->const_parts_begin(); it != intersection->const_parts_end(); ++it )
3355 if (
const QgsLineString *ls = qgsgeometry_cast< const QgsLineString * >( *it ) )
3363 mFillLineSymbol->renderPolyline( QPolygonF() << QPointF( x1, y1 ) << QPointF( x2, y2 ), context.
feature(), context.
renderContext() );
3375 map.insert( QStringLiteral(
"angle" ), QString::number( mLineAngle ) );
3376 map.insert( QStringLiteral(
"distance" ), QString::number( mDistance ) );
3377 map.insert( QStringLiteral(
"line_width" ), QString::number( mLineWidth ) );
3379 map.insert( QStringLiteral(
"offset" ), QString::number( mOffset ) );
3395 if ( mFillLineSymbol )
3406 QDomElement symbolizerElem = doc.createElement( QStringLiteral(
"se:PolygonSymbolizer" ) );
3407 if ( !props.value( QStringLiteral(
"uom" ), QString() ).toString().isEmpty() )
3408 symbolizerElem.setAttribute( QStringLiteral(
"uom" ), props.value( QStringLiteral(
"uom" ), QString() ).toString() );
3409 element.appendChild( symbolizerElem );
3414 QDomElement fillElem = doc.createElement( QStringLiteral(
"se:Fill" ) );
3415 symbolizerElem.appendChild( fillElem );
3417 QDomElement graphicFillElem = doc.createElement( QStringLiteral(
"se:GraphicFill" ) );
3418 fillElem.appendChild( graphicFillElem );
3420 QDomElement graphicElem = doc.createElement( QStringLiteral(
"se:Graphic" ) );
3421 graphicFillElem.appendChild( graphicElem );
3426 bool exportOk {
false };
3430 if ( ! image.isNull() )
3432 const QFileInfo info { context.exportFilePath() };
3433 QString pngPath { info.completeSuffix().isEmpty() ? context.exportFilePath() : context.exportFilePath().chopped( info.completeSuffix().length() ).append( QStringLiteral(
"png" ) ) };
3435 image.save( pngPath );
3444 QColor lineColor = mFillLineSymbol ? mFillLineSymbol->color() : QColor();
3445 double lineWidth = mFillLineSymbol ? mFillLineSymbol->width() : 0.0;
3453 double angle = props.value( QStringLiteral(
"angle" ), QStringLiteral(
"0" ) ).toDouble( &ok );
3456 angleFunc = QStringLiteral(
"%1 + %2" ).arg( props.value( QStringLiteral(
"angle" ), QStringLiteral(
"0" ) ).toString() ).arg( mLineAngle );
3460 angleFunc = QString::number(
angle + mLineAngle );
3465 QPointF lineOffset( std::sin( mLineAngle ) * mOffset, std::cos( mLineAngle ) * mOffset );
3473 QString featureStyle;
3474 featureStyle.append(
"Brush(" );
3475 featureStyle.append( QStringLiteral(
"fc:%1" ).arg(
mColor.name() ) );
3476 featureStyle.append( QStringLiteral(
",bc:%1" ).arg( QLatin1String(
"#00000000" ) ) );
3477 featureStyle.append(
",id:\"ogr-brush-2\"" );
3478 featureStyle.append( QStringLiteral(
",a:%1" ).arg( mLineAngle ) );
3479 featureStyle.append( QStringLiteral(
",s:%1" ).arg( mLineWidth * widthScaleFactor ) );
3480 featureStyle.append(
",dx:0mm" );
3481 featureStyle.append( QStringLiteral(
",dy:%1mm" ).arg( mDistance * widthScaleFactor ) );
3482 featureStyle.append(
')' );
3483 return featureStyle;
3489 && ( !mFillLineSymbol || !mFillLineSymbol->hasDataDefinedProperties() ) )
3514 Qt::PenStyle lineStyle;
3516 QDomElement fillElem = element.firstChildElement( QStringLiteral(
"Fill" ) );
3517 if ( fillElem.isNull() )
3520 QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral(
"GraphicFill" ) );
3521 if ( graphicFillElem.isNull() )
3524 QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral(
"Graphic" ) );
3525 if ( graphicElem.isNull() )
3531 if ( name != QLatin1String(
"horline" ) )
3539 double d = angleFunc.toDouble( &ok );
3548 offset = std::sqrt( std::pow( vectOffset.x(), 2 ) + std::pow( vectOffset.y(), 2 ) );
3551 double scaleFactor = 1.0;
3552 const QString uom = element.attribute( QStringLiteral(
"uom" ) );
3554 size = size * scaleFactor;
3557 std::unique_ptr< QgsLinePatternFillSymbolLayer > sl = std::make_unique< QgsLinePatternFillSymbolLayer >();
3558 sl->setOutputUnit( sldUnitSize );
3559 sl->setColor( lineColor );
3561 sl->setLineAngle(
angle );
3563 sl->setDistance( size );
3566 QDomElement strokeElem = element.firstChildElement( QStringLiteral(
"Stroke" ) );
3567 if ( !strokeElem.isNull() )
3578 return sl.release();
3678 std::unique_ptr< QgsPointPatternFillSymbolLayer > layer = std::make_unique< QgsPointPatternFillSymbolLayer >();
3679 if (
properties.contains( QStringLiteral(
"distance_x" ) ) )
3681 layer->setDistanceX(
properties[QStringLiteral(
"distance_x" )].toDouble() );
3683 if (
properties.contains( QStringLiteral(
"distance_y" ) ) )
3685 layer->setDistanceY(
properties[QStringLiteral(
"distance_y" )].toDouble() );
3687 if (
properties.contains( QStringLiteral(
"displacement_x" ) ) )
3689 layer->setDisplacementX(
properties[QStringLiteral(
"displacement_x" )].toDouble() );
3691 if (
properties.contains( QStringLiteral(
"displacement_y" ) ) )
3693 layer->setDisplacementY(
properties[QStringLiteral(
"displacement_y" )].toDouble() );
3695 if (
properties.contains( QStringLiteral(
"offset_x" ) ) )
3697 layer->setOffsetX(
properties[QStringLiteral(
"offset_x" )].toDouble() );
3699 if (
properties.contains( QStringLiteral(
"offset_y" ) ) )
3701 layer->setOffsetY(
properties[QStringLiteral(
"offset_y" )].toDouble() );
3704 if (
properties.contains( QStringLiteral(
"distance_x_unit" ) ) )
3708 if (
properties.contains( QStringLiteral(
"distance_x_map_unit_scale" ) ) )
3712 if (
properties.contains( QStringLiteral(
"distance_y_unit" ) ) )
3716 if (
properties.contains( QStringLiteral(
"distance_y_map_unit_scale" ) ) )
3720 if (
properties.contains( QStringLiteral(
"displacement_x_unit" ) ) )
3724 if (
properties.contains( QStringLiteral(
"displacement_x_map_unit_scale" ) ) )
3728 if (
properties.contains( QStringLiteral(
"displacement_y_unit" ) ) )
3732 if (
properties.contains( QStringLiteral(
"displacement_y_map_unit_scale" ) ) )
3736 if (
properties.contains( QStringLiteral(
"offset_x_unit" ) ) )
3740 if (
properties.contains( QStringLiteral(
"offset_x_map_unit_scale" ) ) )
3744 if (
properties.contains( QStringLiteral(
"offset_y_unit" ) ) )
3748 if (
properties.contains( QStringLiteral(
"offset_y_map_unit_scale" ) ) )
3753 if (
properties.contains( QStringLiteral(
"random_deviation_x" ) ) )
3755 layer->setMaximumRandomDeviationX(
properties[QStringLiteral(
"random_deviation_x" )].toDouble() );
3757 if (
properties.contains( QStringLiteral(
"random_deviation_y" ) ) )
3759 layer->setMaximumRandomDeviationY(
properties[QStringLiteral(
"random_deviation_y" )].toDouble() );
3761 if (
properties.contains( QStringLiteral(
"random_deviation_x_unit" ) ) )
3765 if (
properties.contains( QStringLiteral(
"random_deviation_x_map_unit_scale" ) ) )
3769 if (
properties.contains( QStringLiteral(
"random_deviation_y_unit" ) ) )
3773 if (
properties.contains( QStringLiteral(
"random_deviation_y_map_unit_scale" ) ) )
3777 unsigned long seed = 0;
3778 if (
properties.contains( QStringLiteral(
"seed" ) ) )
3784 std::random_device rd;
3785 std::mt19937 mt(
seed == 0 ? rd() :
seed );
3786 std::uniform_int_distribution<> uniformDist( 1, 999999999 );
3787 seed = uniformDist( mt );
3789 layer->setSeed(
seed );
3791 if (
properties.contains( QStringLiteral(
"outline_width_unit" ) ) )
3795 if (
properties.contains( QStringLiteral(
"outline_width_map_unit_scale" ) ) )
3799 if (
properties.contains( QStringLiteral(
"clip_mode" ) ) )
3803 if (
properties.contains( QStringLiteral(
"coordinate_reference" ) ) )
3808 if (
properties.contains( QStringLiteral(
"angle" ) ) )
3810 layer->setAngle(
properties[QStringLiteral(
"angle" )].toDouble() );
3813 layer->restoreOldDataDefinedProperties(
properties );
3815 return layer.release();
3820 return QStringLiteral(
"PointPatternFill" );
3823bool QgsPointPatternFillSymbolLayer::applyPattern(
const QgsSymbolRenderContext &context, QBrush &brush,
double distanceX,
double distanceY,
3824 double displacementX,
double displacementY,
double offsetX,
double offsetY )
3831 double widthOffset = std::fmod(
3834 double heightOffset = std::fmod(
3838 if ( width > 2000 || height > 2000 )
3840 brush.setTextureImage( QImage() );
3844 QImage patternImage( width, height, QImage::Format_ARGB32 );
3845 patternImage.fill( 0 );
3846 if ( patternImage.isNull() )
3848 brush.setTextureImage( QImage() );
3853 QPainter p( &patternImage );
3871 for (
double currentX = -width; currentX <= width * 2.0; currentX += width )
3873 for (
double currentY = -height; currentY <= height * 2.0; currentY += height )
3875 mMarkerSymbol->renderPoint( QPointF( currentX + widthOffset, currentY + heightOffset ), context.
feature(), pointRenderContext );
3886 for (
double currentX = -width; currentX <= width * 2.0; currentX += width )
3888 for (
double currentY = -height / 2.0; currentY <= height * 2.0; currentY += height )
3890 mMarkerSymbol->renderPoint( QPointF( currentX + widthOffset + displacementPixelX, currentY + heightOffset ), context.
feature(), pointRenderContext );
3894 for (
double currentX = -width / 2.0; currentX <= width * 2.0; currentX += width )
3896 for (
double currentY = -height; currentY <= height * 2.0; currentY += height / 2.0 )
3898 mMarkerSymbol->renderPoint( QPointF( currentX + widthOffset + ( std::fmod( currentY, height ) != 0 ? displacementPixelX : 0 ), currentY + heightOffset - displacementPixelY ), context.feature(), pointRenderContext );
3907 QImage transparentImage = patternImage.copy();
3909 brush.setTextureImage( transparentImage );
3913 brush.setTextureImage( patternImage );
3915 QTransform brushTransform;
3916 brush.setTransform( brushTransform );
3936 if ( !mRenderUsingMarkers )
3943 if ( mRenderUsingMarkers )
3951 if ( mRenderUsingMarkers )
3977 if ( !mRenderUsingMarkers )
4020 const double widthOffset = std::fmod(
4032 const double heightOffset = std::fmod(
4058 p->setPen( QPen( Qt::NoPen ) );
4061 if ( useSelectedColor )
4064 p->setBrush( QBrush( selColor ) );
4088 std::unique_ptr< QgsPolygon > shapePolygon;
4089 std::unique_ptr< QgsGeometryEngine > shapeEngine;
4096 shapePolygon = std::make_unique< QgsPolygon >();
4100 for (
const QPolygonF &ring : *rings )
4106 shapeEngine->prepareGeometry();
4113 path.addPolygon( points );
4116 for (
const QPolygonF &ring : *rings )
4118 path.addPolygon( ring );
4121 p->setClipPath( path, Qt::IntersectClip );
4127 const QRectF boundingRect = points.boundingRect();
4129 QTransform invertedRotateTransform;
4137 QTransform transform;
4138 if ( applyBrushTransform )
4141 transform.translate( -boundingRect.center().x(),
4142 -boundingRect.center().y() );
4143 transform.rotate( -
angle );
4144 transform.translate( boundingRect.center().x(),
4145 boundingRect.center().y() );
4150 transform.rotate( -
angle );
4153 const QRectF transformedBounds = transform.map( points ).boundingRect();
4154 left = transformedBounds.left() - 2 * width;
4155 top = transformedBounds.top() - 2 * height;
4156 right = transformedBounds.right() + 2 * width;
4157 bottom = transformedBounds.bottom() + 2 * height;
4158 invertedRotateTransform = transform.inverted();
4160 if ( !applyBrushTransform )
4162 left -= transformedBounds.left() - ( width * std::floor( transformedBounds.left() / width ) );
4163 top -= transformedBounds.top() - ( height * std::floor( transformedBounds.top() / height ) );
4168 left = boundingRect.left() - 2 * width;
4169 top = boundingRect.top() - 2 * height;
4170 right = boundingRect.right() + 2 * width;
4171 bottom = boundingRect.bottom() + 2 * height;
4173 if ( !applyBrushTransform )
4175 left -= boundingRect.left() - ( width * std::floor( boundingRect.left() / width ) );
4176 top -= boundingRect.top() - ( height * std::floor( boundingRect.top() / height ) );
4205 std::random_device rd;
4206 std::mt19937 mt(
seed == 0 ? rd() :
seed );
4207 std::uniform_real_distribution<> uniformDist( 0, 1 );
4213 const bool needsExpressionContext =
mMarkerSymbol->hasDataDefinedProperties();
4221 bool alternateColumn =
false;
4222 int currentCol = -3;
4223 for (
double currentX = left; currentX <= right; currentX += width, alternateColumn = !alternateColumn )
4228 if ( needsExpressionContext )
4231 bool alternateRow =
false;
4232 const double columnX = currentX + widthOffset;
4233 int currentRow = -3;
4234 for (
double currentY = top; currentY <= bottom; currentY += height, alternateRow = !alternateRow )
4239 double y = currentY + heightOffset;
4242 x += displacementPixelX;
4244 if ( !alternateColumn )
4245 y -= displacementPixelY;
4251 invertedRotateTransform.map( xx, yy, &x, &y );
4254 if ( useRandomShift )
4256 x += ( 2 * uniformDist( mt ) - 1 ) * maxRandomDeviationPixelX;
4257 y += ( 2 * uniformDist( mt ) - 1 ) * maxRandomDeviationPixelY;
4260 if ( needsExpressionContext )
4268 bool renderPoint =
true;
4276 renderPoint = shapeEngine->intersects( &p );
4288 renderPoint = shapeEngine->intersects( markerBounds.
constGet() );
4314 map.insert( QStringLiteral(
"distance_x" ), QString::number(
mDistanceX ) );
4315 map.insert( QStringLiteral(
"distance_y" ), QString::number(
mDistanceY ) );
4316 map.insert( QStringLiteral(
"displacement_x" ), QString::number(
mDisplacementX ) );
4317 map.insert( QStringLiteral(
"displacement_y" ), QString::number(
mDisplacementY ) );
4318 map.insert( QStringLiteral(
"offset_x" ), QString::number(
mOffsetX ) );
4319 map.insert( QStringLiteral(
"offset_y" ), QString::number(
mOffsetY ) );
4335 map.insert( QStringLiteral(
"random_deviation_x" ), QString::number(
mRandomDeviationX ) );
4336 map.insert( QStringLiteral(
"random_deviation_y" ), QString::number(
mRandomDeviationY ) );
4341 map.insert( QStringLiteral(
"seed" ), QString::number(
mSeed ) );
4342 map.insert( QStringLiteral(
"angle" ),
mAngle );
4361 for (
int symbolLayerIdx = 0; symbolLayerIdx <
mMarkerSymbol->symbolLayerCount(); symbolLayerIdx++ )
4363 QDomElement symbolizerElem = doc.createElement( QStringLiteral(
"se:PolygonSymbolizer" ) );
4364 if ( !props.value( QStringLiteral(
"uom" ), QString() ).toString().isEmpty() )
4365 symbolizerElem.setAttribute( QStringLiteral(
"uom" ), props.value( QStringLiteral(
"uom" ), QString() ).toString() );
4366 element.appendChild( symbolizerElem );
4371 QDomElement fillElem = doc.createElement( QStringLiteral(
"se:Fill" ) );
4372 symbolizerElem.appendChild( fillElem );
4374 QDomElement graphicFillElem = doc.createElement( QStringLiteral(
"se:GraphicFill" ) );
4375 fillElem.appendChild( graphicFillElem );
4382 bool exportOk {
false };
4386 if ( ! image.isNull() )
4388 QDomElement graphicElem = doc.createElement( QStringLiteral(
"se:Graphic" ) );
4389 graphicFillElem.appendChild( graphicElem );
4390 const QFileInfo info { context.exportFilePath() };
4391 QString pngPath { info.completeSuffix().isEmpty() ? context.exportFilePath() : context.exportFilePath().chopped( info.completeSuffix().length() ).append( QStringLiteral(
"png" ) ) };
4393 image.save( pngPath );
4412 symbolizerElem.appendChild( graphicMarginElem );
4416 markerLayer->writeSldMarker( doc, graphicFillElem, props );
4420 QString errorMsg = QStringLiteral(
"QgsMarkerSymbolLayer expected, %1 found. Skip it." ).arg( layer->
layerType() );
4421 graphicFillElem.appendChild( doc.createComment( errorMsg ) );
4425 QString errorMsg = QStringLiteral(
"Missing point pattern symbol layer. Skip it." );
4426 graphicFillElem.appendChild( doc.createComment( errorMsg ) );
4435 double angleRads { qDegreesToRadians(
mAngle ) };
4444 if ( displacementXPx != 0 )
4449 if ( displacementYPx != 0 )
4456 QPixmap pixmap( size );
4457 pixmap.fill( Qt::transparent );
4459 painter.begin( &pixmap );
4460 painter.setRenderHint( QPainter::Antialiasing );
4468 std::unique_ptr< QgsPointPatternFillSymbolLayer > layerClone(
clone() );
4470 layerClone->setAngle( qRadiansToDegrees( angleRads ) );
4473 layerClone->setMaximumRandomDeviationX( 0 );
4474 layerClone->setMaximumRandomDeviationY( 0 );
4476 layerClone->drawPreviewIcon( symbolContext, pixmap.size() );
4478 return pixmap.toImage();
4486 QDomElement fillElem = element.firstChildElement( QStringLiteral(
"Fill" ) );
4487 if ( fillElem.isNull() )
4490 QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral(
"GraphicFill" ) );
4491 if ( graphicFillElem.isNull() )
4494 QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral(
"Graphic" ) );
4495 if ( graphicElem.isNull() )
4499 if ( !simpleMarkerSl )
4504 layers.append( simpleMarkerSl );
4506 std::unique_ptr< QgsMarkerSymbol > marker = std::make_unique< QgsMarkerSymbol >( layers );
4509 const double markerSize { marker->size() };
4511 std::unique_ptr< QgsPointPatternFillSymbolLayer > pointPatternFillSl = std::make_unique< QgsPointPatternFillSymbolLayer >();
4512 pointPatternFillSl->setSubSymbol( marker.release() );
4517 auto distanceParser = [ & ](
const QStringList & values )
4519 switch ( values.count( ) )
4524 const double v { values.at( 0 ).toDouble( &ok ) };
4527 pointPatternFillSl->setDistanceX( v * 2 + markerSize );
4528 pointPatternFillSl->setDistanceY( v * 2 + markerSize );
4535 const double vX { values.at( 1 ).toDouble( &ok ) };
4538 pointPatternFillSl->setDistanceX( vX * 2 + markerSize );
4540 const double vY { values.at( 0 ).toDouble( &ok ) };
4543 pointPatternFillSl->setDistanceY( vY * 2 + markerSize );
4550 const double vX { values.at( 1 ).toDouble( &ok ) };
4553 pointPatternFillSl->setDistanceX( vX * 2 + markerSize );
4555 const double vYt { values.at( 0 ).toDouble( &ok ) };
4558 const double vYb { values.at( 2 ).toDouble( &ok ) };
4561 pointPatternFillSl->setDistanceY( ( vYt + vYb ) + markerSize );
4569 const double vYt { values.at( 0 ).toDouble( &ok ) };
4572 const double vYb { values.at( 2 ).toDouble( &ok ) };
4575 pointPatternFillSl->setDistanceY( ( vYt + vYb ) + markerSize );
4578 const double vXr { values.at( 1 ).toDouble( &ok ) };
4581 const double vXl { values.at( 3 ).toDouble( &ok ) };
4584 pointPatternFillSl->setDistanceX( ( vXr + vXl ) + markerSize );
4595 bool distanceFromVendorOption {
false };
4597 for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
4600 if ( it.key() == QLatin1String(
"distance" ) )
4602 distanceParser( it.value().split(
',' ) );
4603 distanceFromVendorOption =
true;
4606 else if ( it.key() == QLatin1String(
"graphic-margin" ) )
4608 distanceParser( it.value().split(
' ' ) );
4609 distanceFromVendorOption =
true;
4614 if ( ! distanceFromVendorOption && ! graphicFillElem.elementsByTagName( QStringLiteral(
"Size" ) ).isEmpty() )
4616 const QDomElement sizeElement { graphicFillElem.elementsByTagName( QStringLiteral(
"Size" ) ).at( 0 ).toElement() };
4618 const double size { sizeElement.text().toDouble( &ok ) };
4621 pointPatternFillSl->setDistanceX( size );
4622 pointPatternFillSl->setDistanceY( size );
4626 return pointPatternFillSl.release();
4708 attributes.unite(
mMarkerSymbol->usedAttributes( context ) );