48#include <QSvgRenderer>
49#include <QDomDocument>
59 Qt::PenJoinStyle penJoinStyle )
60 : mBrushStyle( style )
61 , mStrokeColor( strokeColor )
62 , mStrokeStyle( strokeStyle )
63 , mStrokeWidth( strokeWidth )
64 , mPenJoinStyle( penJoinStyle )
108void QgsSimpleFillSymbolLayer::applyDataDefinedSymbology(
QgsSymbolRenderContext &context, QBrush &brush, QPen &pen, QPen &selPen )
133 penColor.setAlphaF( context.
opacity() * penColor.alphaF() );
134 pen.setColor( penColor );
142 double width = exprVal.toDouble( &ok );
146 pen.setWidthF( width );
147 selPen.setWidthF( width );
184 if ( props.contains( QStringLiteral(
"color" ) ) )
186 if ( props.contains( QStringLiteral(
"style" ) ) )
188 if ( props.contains( QStringLiteral(
"color_border" ) ) )
193 else if ( props.contains( QStringLiteral(
"outline_color" ) ) )
197 else if ( props.contains( QStringLiteral(
"line_color" ) ) )
202 if ( props.contains( QStringLiteral(
"style_border" ) ) )
207 else if ( props.contains( QStringLiteral(
"outline_style" ) ) )
211 else if ( props.contains( QStringLiteral(
"line_style" ) ) )
215 if ( props.contains( QStringLiteral(
"width_border" ) ) )
218 strokeWidth = props[QStringLiteral(
"width_border" )].toDouble();
220 else if ( props.contains( QStringLiteral(
"outline_width" ) ) )
222 strokeWidth = props[QStringLiteral(
"outline_width" )].toDouble();
224 else if ( props.contains( QStringLiteral(
"line_width" ) ) )
226 strokeWidth = props[QStringLiteral(
"line_width" )].toDouble();
228 if ( props.contains( QStringLiteral(
"offset" ) ) )
230 if ( props.contains( QStringLiteral(
"joinstyle" ) ) )
235 if ( props.contains( QStringLiteral(
"border_width_unit" ) ) )
239 else if ( props.contains( QStringLiteral(
"outline_width_unit" ) ) )
243 else if ( props.contains( QStringLiteral(
"line_width_unit" ) ) )
247 if ( props.contains( QStringLiteral(
"offset_unit" ) ) )
250 if ( props.contains( QStringLiteral(
"border_width_map_unit_scale" ) ) )
252 if ( props.contains( QStringLiteral(
"offset_map_unit_scale" ) ) )
255 sl->restoreOldDataDefinedProperties( props );
263 return QStringLiteral(
"SimpleFill" );
275 selColor.setAlphaF( context.
opacity() );
333 if (
mBrush.style() == Qt::SolidPattern ||
mBrush.style() == Qt::NoBrush || !
dynamic_cast<QPrinter *
>( p->device() ) )
347 p->setPen( Qt::NoPen );
351 p->setBrush( Qt::NoBrush );
369 map[QStringLiteral(
"outline_width" )] = QString::number(
mStrokeWidth );
397 QDomElement symbolizerElem = doc.createElement( QStringLiteral(
"se:PolygonSymbolizer" ) );
398 if ( !props.value( QStringLiteral(
"uom" ), QString() ).toString().isEmpty() )
399 symbolizerElem.setAttribute( QStringLiteral(
"uom" ), props.value( QStringLiteral(
"uom" ), QString() ).toString() );
400 element.appendChild( symbolizerElem );
409 bool exportOk {
false };
413 if ( ! image.isNull() )
416 QDomElement fillElem = doc.createElement( QStringLiteral(
"se:Fill" ) );
417 symbolizerElem.appendChild( fillElem );
418 QDomElement graphicFillElem = doc.createElement( QStringLiteral(
"se:GraphicFill" ) );
419 fillElem.appendChild( graphicFillElem );
420 QDomElement graphicElem = doc.createElement( QStringLiteral(
"se:Graphic" ) );
421 graphicFillElem.appendChild( graphicElem );
423 const QFileInfo info { context.exportFilePath() };
424 QString pngPath { info.completeSuffix().isEmpty() ? context.exportFilePath() : context.exportFilePath().chopped( info.completeSuffix().length() ).append( QStringLiteral(
"png" ) ) };
426 image.save( pngPath );
441 const double alpha { props.value( QStringLiteral(
"alpha" ), QVariant() ).toDouble( &ok ) };
447 QDomElement fillElem = doc.createElement( QStringLiteral(
"se:Fill" ) );
448 symbolizerElem.appendChild( fillElem );
455 QDomElement strokeElem = doc.createElement( QStringLiteral(
"se:Stroke" ) );
456 symbolizerElem.appendChild( strokeElem );
460 const double alpha { props.value( QStringLiteral(
"alpha" ), QVariant() ).toDouble( &ok ) };
480 symbolStyle.append(
';' );
489 Qt::BrushStyle fillStyle;
493 QDomElement fillElem = element.firstChildElement( QStringLiteral(
"Fill" ) );
496 QDomElement strokeElem = element.firstChildElement( QStringLiteral(
"Stroke" ) );
502 double scaleFactor = 1.0;
503 const QString uom = element.attribute( QStringLiteral(
"uom" ) );
510 sl->setOutputUnit( sldUnitSize );
519 return penBleed + offsetBleed;
577 QPixmap pixmap( QSize( 32, 32 ) );
578 pixmap.fill( Qt::transparent );
580 painter.begin( &pixmap );
581 painter.setRenderHint( QPainter::Antialiasing );
587 QgsSymbolRenderContext symbolContext( renderContext, QgsUnitTypes::RenderUnit::RenderPixels, 1.0,
false, Qgis::SymbolRenderHints() );
589 std::unique_ptr< QgsSimpleFillSymbolLayer > layerClone(
clone() );
590 layerClone->setStrokeStyle( Qt::PenStyle::NoPen );
591 layerClone->drawPreviewIcon( symbolContext, pixmap.size() );
593 return pixmap.toImage();
601 : mGradientColorType( colorType )
602 , mGradientType( gradientType )
603 , mCoordinateMode( coordinateMode )
604 , mGradientSpread( spread )
605 , mReferencePoint1( QPointF( 0.5, 0 ) )
606 , mReferencePoint2( QPointF( 0.5, 1 ) )
627 bool refPoint1IsCentroid =
false;
629 bool refPoint2IsCentroid =
false;
634 if ( props.contains( QStringLiteral(
"type" ) ) )
636 if ( props.contains( QStringLiteral(
"coordinate_mode" ) ) )
638 if ( props.contains( QStringLiteral(
"spread" ) ) )
640 if ( props.contains( QStringLiteral(
"color_type" ) ) )
642 if ( props.contains( QStringLiteral(
"gradient_color" ) ) )
647 else if ( props.contains( QStringLiteral(
"color" ) ) )
651 if ( props.contains( QStringLiteral(
"gradient_color2" ) ) )
656 if ( props.contains( QStringLiteral(
"reference_point1" ) ) )
658 if ( props.contains( QStringLiteral(
"reference_point1_iscentroid" ) ) )
659 refPoint1IsCentroid = props[QStringLiteral(
"reference_point1_iscentroid" )].toInt();
660 if ( props.contains( QStringLiteral(
"reference_point2" ) ) )
662 if ( props.contains( QStringLiteral(
"reference_point2_iscentroid" ) ) )
663 refPoint2IsCentroid = props[QStringLiteral(
"reference_point2_iscentroid" )].toInt();
664 if ( props.contains( QStringLiteral(
"angle" ) ) )
665 angle = props[QStringLiteral(
"angle" )].toDouble();
667 if ( props.contains( QStringLiteral(
"offset" ) ) )
684 if ( props.contains( QStringLiteral(
"offset_unit" ) ) )
686 if ( props.contains( QStringLiteral(
"offset_map_unit_scale" ) ) )
689 sl->setReferencePoint1IsCentroid( refPoint1IsCentroid );
691 sl->setReferencePoint2IsCentroid( refPoint2IsCentroid );
692 sl->setAngle(
angle );
694 sl->setColorRamp( gradientRamp );
696 sl->restoreOldDataDefinedProperties( props );
709 return QStringLiteral(
"GradientFill" );
712void QgsGradientFillSymbolLayer::applyDataDefinedSymbology(
QgsSymbolRenderContext &context,
const QPolygonF &points )
757 if ( currentType == QObject::tr(
"linear" ) )
761 else if ( currentType == QObject::tr(
"radial" ) )
765 else if ( currentType == QObject::tr(
"conical" ) )
779 if ( currentCoordMode == QObject::tr(
"feature" ) )
783 else if ( currentCoordMode == QObject::tr(
"viewport" ) )
797 if ( currentSpread == QObject::tr(
"pad" ) )
801 else if ( currentSpread == QObject::tr(
"repeat" ) )
805 else if ( currentSpread == QObject::tr(
"reflect" ) )
852 if ( refPoint1IsCentroid || refPoint2IsCentroid )
857 QRectF bbox = points.boundingRect();
858 double centroidX = (
centroid.
x() - bbox.left() ) / bbox.width();
859 double centroidY = (
centroid.
y() - bbox.top() ) / bbox.height();
861 if ( refPoint1IsCentroid )
863 refPoint1X = centroidX;
864 refPoint1Y = centroidY;
866 if ( refPoint2IsCentroid )
868 refPoint2X = centroidX;
869 refPoint2Y = centroidY;
875 spread, QPointF( refPoint1X, refPoint1Y ), QPointF( refPoint2X, refPoint2Y ),
angle );
878QPointF QgsGradientFillSymbolLayer::rotateReferencePoint( QPointF refPoint,
double angle )
883 QLineF refLine = QLineF( QPointF( 0.5, 0.5 ), refPoint );
885 refLine.setAngle( refLine.angle() +
angle );
887 QPointF rotatedReferencePoint = refLine.p2();
889 if ( rotatedReferencePoint.x() > 1 )
890 rotatedReferencePoint.setX( 1 );
891 if ( rotatedReferencePoint.x() < 0 )
892 rotatedReferencePoint.setX( 0 );
893 if ( rotatedReferencePoint.y() > 1 )
894 rotatedReferencePoint.setY( 1 );
895 if ( rotatedReferencePoint.y() < 0 )
896 rotatedReferencePoint.setY( 0 );
898 return rotatedReferencePoint;
905 QPointF referencePoint1, QPointF referencePoint2,
const double angle )
910 QColor fillColor2 =
color2;
911 fillColor2.setAlphaF( context.
opacity() * fillColor2.alphaF() );
922 gradient = QLinearGradient( rotatedReferencePoint1, rotatedReferencePoint2 );
925 gradient = QRadialGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).length() );
928 gradient = QConicalGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).
angle() );
934 gradient.setCoordinateMode( QGradient::ObjectBoundingMode );
937 gradient.setCoordinateMode( QGradient::StretchToDeviceMode );
943 gradient.setSpread( QGradient::PadSpread );
946 gradient.setSpread( QGradient::ReflectSpread );
949 gradient.setSpread( QGradient::RepeatSpread );
965 gradient.setColorAt( 1.0, fillColor2 );
969 brush = QBrush( gradient );
976 selColor.setAlphaF( context.
opacity() );
993 applyDataDefinedSymbology( context, points );
996 p->setPen( Qt::NoPen );
1029 map[QStringLiteral(
"color_type" )] = QString::number(
static_cast< int >(
mGradientColorType ) );
1030 map[QStringLiteral(
"type" )] = QString::number(
static_cast<int>(
mGradientType ) );
1031 map[QStringLiteral(
"coordinate_mode" )] = QString::number(
static_cast< int >(
mCoordinateMode ) );
1032 map[QStringLiteral(
"spread" )] = QString::number(
static_cast< int >(
mGradientSpread ) );
1037 map[QStringLiteral(
"angle" )] = QString::number(
mAngle );
1043#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
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" );
1223 if ( mGradientRamp.get() == ramp )
1226 mGradientRamp.reset( ramp );
1229void QgsShapeburstFillSymbolLayer::applyDataDefinedSymbology(
QgsSymbolRenderContext &context, QColor &color, QColor &color2,
int &blurRadius,
bool &useWholeShape,
1230 double &maxDistance,
bool &ignoreRings )
1287 selColor.setAlphaF( context.
opacity() );
1288 mSelBrush = QBrush( selColor );
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] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1514 s = ( ( f[q] + q * q ) - ( f[v[k]] + ( 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] = ( 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 + y * width ];
1550 distanceTransform1d( f, height, v, z, d );
1551 for (
int y = 0; y < height; y++ )
1553 im[ x + 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 + y * width ];
1567 distanceTransform1d( f, width, v, z, d );
1568 for (
int x = 0; x < width; x++ )
1570 im[ x + 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[width * height];
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 (
int i = 1; i < ( width * height ); ++i )
1634 if ( array[i] > dtMaxValue )
1636 dtMaxValue = array[i];
1641 maxDistanceValue = std::sqrt( dtMaxValue );
1646 maxDistanceValue = maxPixelDistance;
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 );
1815 p->setBrush( QBrush( selColor ) );
1821 QTransform t =
mBrush.transform();
1823 mBrush.setTransform( t );
1828 mBrush.setTransform( bkTransform );
1864 return Qt::SolidLine;
1868 return Qt::SolidLine;
1872 return mStroke->dxfPenStyle();
1908 , mPatternWidth( width )
1912 mColor = QColor( 255, 255, 255 );
1918 , mPatternWidth( width )
1919 , mSvgData( svgData )
1924 mColor = QColor( 255, 255, 255 );
1925 setDefaultSvgParams();
1933 mPatternWidthUnit = unit;
1934 mSvgStrokeWidthUnit = unit;
1937 mStroke->setOutputUnit( unit );
1943 if ( mPatternWidthUnit != unit || mSvgStrokeWidthUnit != unit ||
mStrokeWidthUnit != unit )
1953 mPatternWidthMapUnitScale = scale;
1954 mSvgStrokeWidthMapUnitScale = scale;
1960 mPatternWidthMapUnitScale == mSvgStrokeWidthMapUnitScale &&
1963 return mPatternWidthMapUnitScale;
1973 mSvgFilePath = svgPath;
1974 setDefaultSvgParams();
1984 if (
properties.contains( QStringLiteral(
"width" ) ) )
1986 width =
properties[QStringLiteral(
"width" )].toDouble();
1988 if (
properties.contains( QStringLiteral(
"svgFile" ) ) )
1992 if (
properties.contains( QStringLiteral(
"angle" ) ) )
1997 std::unique_ptr< QgsSVGFillSymbolLayer > symbolLayer;
2000 symbolLayer = std::make_unique< QgsSVGFillSymbolLayer >(
svgFilePath, width,
angle );
2004 if (
properties.contains( QStringLiteral(
"data" ) ) )
2006 data = QByteArray::fromHex(
properties[QStringLiteral(
"data" )].toString().toLocal8Bit() );
2008 symbolLayer = std::make_unique< QgsSVGFillSymbolLayer >( data, width,
angle );
2012 if (
properties.contains( QStringLiteral(
"svgFillColor" ) ) )
2017 else if (
properties.contains( QStringLiteral(
"color" ) ) )
2021 if (
properties.contains( QStringLiteral(
"svgOutlineColor" ) ) )
2026 else if (
properties.contains( QStringLiteral(
"outline_color" ) ) )
2030 else if (
properties.contains( QStringLiteral(
"line_color" ) ) )
2034 if (
properties.contains( QStringLiteral(
"svgOutlineWidth" ) ) )
2037 symbolLayer->setSvgStrokeWidth(
properties[QStringLiteral(
"svgOutlineWidth" )].toDouble() );
2039 else if (
properties.contains( QStringLiteral(
"outline_width" ) ) )
2041 symbolLayer->setSvgStrokeWidth(
properties[QStringLiteral(
"outline_width" )].toDouble() );
2043 else if (
properties.contains( QStringLiteral(
"line_width" ) ) )
2045 symbolLayer->setSvgStrokeWidth(
properties[QStringLiteral(
"line_width" )].toDouble() );
2049 if (
properties.contains( QStringLiteral(
"pattern_width_unit" ) ) )
2053 if (
properties.contains( QStringLiteral(
"pattern_width_map_unit_scale" ) ) )
2057 if (
properties.contains( QStringLiteral(
"svg_outline_width_unit" ) ) )
2061 if (
properties.contains( QStringLiteral(
"svg_outline_width_map_unit_scale" ) ) )
2065 if (
properties.contains( QStringLiteral(
"outline_width_unit" ) ) )
2069 if (
properties.contains( QStringLiteral(
"outline_width_map_unit_scale" ) ) )
2074 if (
properties.contains( QStringLiteral(
"parameters" ) ) )
2080 symbolLayer->restoreOldDataDefinedProperties(
properties );
2082 return symbolLayer.release();
2087 QVariantMap::iterator it =
properties.find( QStringLiteral(
"svgFile" ) );
2099 return QStringLiteral(
"SVGFill" );
2102void QgsSVGFillSymbolLayer::applyPattern( QBrush &brush,
const QString &svgFilePath,
double patternWidth,
QgsUnitTypes::RenderUnit patternWidthUnit,
2103 const QColor &svgFillColor,
const QColor &svgStrokeColor,
double svgStrokeWidth,
2107 if ( mSvgViewBox.isNull() )
2114 if (
static_cast< int >( size ) < 1.0 || 10000.0 < size )
2116 brush.setTextureImage( QImage() );
2120 bool fitsInCache =
true;
2128 double hwRatio = 1.0;
2129 if ( patternPict.width() > 0 )
2131 hwRatio =
static_cast< double >( patternPict.height() ) /
static_cast< double >( patternPict.width() );
2133 patternImage = QImage(
static_cast< int >( size ),
static_cast< int >( size * hwRatio ), QImage::Format_ARGB32_Premultiplied );
2134 patternImage.fill( 0 );
2136 QPainter p( &patternImage );
2137 p.drawPicture( QPointF( size / 2, size * hwRatio / 2 ), patternPict );
2140 QTransform brushTransform;
2143 QImage transparentImage = patternImage.copy();
2145 brush.setTextureImage( transparentImage );
2149 brush.setTextureImage( patternImage );
2151 brush.setTransform( brushTransform );
2159 applyPattern(
mBrush, mSvgFilePath, mPatternWidth, mPatternWidthUnit,
mColor, mSvgStrokeColor, mSvgStrokeWidth, mSvgStrokeWidthUnit, context, mPatternWidthMapUnitScale, mSvgStrokeWidthMapUnitScale, evaluatedParameters );
2184 for (
auto ringIt = rings->constBegin(); ringIt != rings->constEnd(); ++ringIt )
2195 if ( !mSvgFilePath.isEmpty() )
2197 map.insert( QStringLiteral(
"svgFile" ), mSvgFilePath );
2201 map.insert( QStringLiteral(
"data" ), QString( mSvgData.toHex() ) );
2204 map.insert( QStringLiteral(
"width" ), QString::number( mPatternWidth ) );
2205 map.insert( QStringLiteral(
"angle" ), QString::number(
mAngle ) );
2210 map.insert( QStringLiteral(
"outline_width" ), QString::number( mSvgStrokeWidth ) );
2227 std::unique_ptr< QgsSVGFillSymbolLayer > clonedLayer;
2228 if ( !mSvgFilePath.isEmpty() )
2230 clonedLayer = std::make_unique< QgsSVGFillSymbolLayer >( mSvgFilePath, mPatternWidth,
mAngle );
2231 clonedLayer->setSvgFillColor(
mColor );
2232 clonedLayer->setSvgStrokeColor( mSvgStrokeColor );
2233 clonedLayer->setSvgStrokeWidth( mSvgStrokeWidth );
2237 clonedLayer = std::make_unique< QgsSVGFillSymbolLayer >( mSvgData, mPatternWidth,
mAngle );
2240 clonedLayer->setPatternWidthUnit( mPatternWidthUnit );
2241 clonedLayer->setPatternWidthMapUnitScale( mPatternWidthMapUnitScale );
2242 clonedLayer->setSvgStrokeWidthUnit( mSvgStrokeWidthUnit );
2243 clonedLayer->setSvgStrokeWidthMapUnitScale( mSvgStrokeWidthMapUnitScale );
2247 clonedLayer->setParameters( mParameters );
2251 clonedLayer->setSubSymbol( mStroke->clone() );
2255 return clonedLayer.release();
2260 QDomElement symbolizerElem = doc.createElement( QStringLiteral(
"se:PolygonSymbolizer" ) );
2261 if ( !props.value( QStringLiteral(
"uom" ), QString() ).toString().isEmpty() )
2262 symbolizerElem.setAttribute( QStringLiteral(
"uom" ), props.value( QStringLiteral(
"uom" ), QString() ).toString() );
2263 element.appendChild( symbolizerElem );
2267 QDomElement fillElem = doc.createElement( QStringLiteral(
"se:Fill" ) );
2268 symbolizerElem.appendChild( fillElem );
2270 QDomElement graphicFillElem = doc.createElement( QStringLiteral(
"se:GraphicFill" ) );
2271 fillElem.appendChild( graphicFillElem );
2273 QDomElement graphicElem = doc.createElement( QStringLiteral(
"se:Graphic" ) );
2274 graphicFillElem.appendChild( graphicElem );
2276 if ( !mSvgFilePath.isEmpty() )
2287 symbolizerElem.appendChild( doc.createComment( QStringLiteral(
"SVG from data not implemented yet" ) ) );
2293 double angle = props.value( QStringLiteral(
"angle" ), QStringLiteral(
"0" ) ).toDouble( &ok );
2296 angleFunc = QStringLiteral(
"%1 + %2" ).arg( props.value( QStringLiteral(
"angle" ), QStringLiteral(
"0" ) ).toString() ).arg(
mAngle );
2309 mStroke->toSld( doc, element, props );
2321 return mStroke.get();
2328 mStroke.reset(
nullptr );
2341 mStroke.reset( lineSymbol );
2351 if ( mStroke && mStroke->symbolLayer( 0 ) )
2353 double subLayerBleed = mStroke->symbolLayer( 0 )->estimateMaxBleed( context );
2354 return subLayerBleed;
2364 return QColor( Qt::black );
2366 return mStroke->color();
2373 attr.unite( mStroke->usedAttributes( context ) );
2381 if ( mStroke && mStroke->hasDataDefinedProperties() )
2388 QString path, mimeType;
2390 Qt::PenStyle penStyle;
2391 double size, strokeWidth;
2393 QDomElement fillElem = element.firstChildElement( QStringLiteral(
"Fill" ) );
2394 if ( fillElem.isNull() )
2397 QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral(
"GraphicFill" ) );
2398 if ( graphicFillElem.isNull() )
2401 QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral(
"Graphic" ) );
2402 if ( graphicElem.isNull() )
2408 if ( mimeType != QLatin1String(
"image/svg+xml" ) )
2413 double scaleFactor = 1.0;
2414 const QString uom = element.attribute( QStringLiteral(
"uom" ) );
2416 size = size * scaleFactor;
2417 strokeWidth = strokeWidth * scaleFactor;
2424 double d = angleFunc.toDouble( &ok );
2429 std::unique_ptr< QgsSVGFillSymbolLayer > sl = std::make_unique< QgsSVGFillSymbolLayer >( path, size,
angle );
2430 sl->setOutputUnit( sldUnitSize );
2433 sl->setSvgStrokeWidth( strokeWidth );
2436 QDomElement strokeElem = element.firstChildElement( QStringLiteral(
"Stroke" ) );
2437 if ( !strokeElem.isNull() )
2448 return sl.release();
2466 double width = mPatternWidth;
2472 QString svgFile = mSvgFilePath;
2491 double strokeWidth = mSvgStrokeWidth;
2500 mSvgStrokeWidthUnit, context, mPatternWidthMapUnitScale, mSvgStrokeWidthMapUnitScale, evaluatedParameters );
2504void QgsSVGFillSymbolLayer::storeViewBox()
2506 if ( !mSvgData.isEmpty() )
2508 QSvgRenderer r( mSvgData );
2511 mSvgViewBox = r.viewBoxF();
2516 mSvgViewBox = QRectF();
2519void QgsSVGFillSymbolLayer::setDefaultSvgParams()
2521 if ( mSvgFilePath.isEmpty() )
2526 bool hasFillParam, hasFillOpacityParam, hasStrokeParam, hasStrokeWidthParam, hasStrokeOpacityParam;
2527 bool hasDefaultFillColor, hasDefaultFillOpacity, hasDefaultStrokeColor, hasDefaultStrokeWidth, hasDefaultStrokeOpacity;
2528 QColor defaultFillColor, defaultStrokeColor;
2529 double defaultStrokeWidth, defaultFillOpacity, defaultStrokeOpacity;
2531 hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
2532 hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2533 hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
2534 hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity );
2536 double newFillOpacity = hasFillOpacityParam ?
mColor.alphaF() : 1.0;
2537 double newStrokeOpacity = hasStrokeOpacityParam ? mSvgStrokeColor.alphaF() : 1.0;
2539 if ( hasDefaultFillColor )
2541 mColor = defaultFillColor;
2542 mColor.setAlphaF( newFillOpacity );
2544 if ( hasDefaultFillOpacity )
2546 mColor.setAlphaF( defaultFillOpacity );
2548 if ( hasDefaultStrokeColor )
2550 mSvgStrokeColor = defaultStrokeColor;
2551 mSvgStrokeColor.setAlphaF( newStrokeOpacity );
2553 if ( hasDefaultStrokeOpacity )
2555 mSvgStrokeColor.setAlphaF( defaultStrokeOpacity );
2557 if ( hasDefaultStrokeWidth )
2559 mSvgStrokeWidth = defaultStrokeWidth;
2572 mFillLineSymbol = std::make_unique<QgsLineSymbol>( );
2580 mFillLineSymbol->setWidth( w );
2586 mFillLineSymbol->setColor(
c );
2592 return mFillLineSymbol ? mFillLineSymbol->color() :
mColor;
2604 mFillLineSymbol.reset( qgis::down_cast<QgsLineSymbol *>( symbol ) );
2613 return mFillLineSymbol.get();
2619 if ( mFillLineSymbol )
2620 attr.unite( mFillLineSymbol->usedAttributes( context ) );
2628 if ( mFillLineSymbol && mFillLineSymbol->hasDataDefinedProperties() )
2646 double lineAngleRads { qDegreesToRadians( mLineAngle ) };
2649 QSize size {
static_cast<int>( distancePx ),
static_cast<int>( distancePx ) };
2651 if (
static_cast<int>( mLineAngle ) % 90 != 0 )
2653 size = QSize(
static_cast<int>( distancePx / std::sin( lineAngleRads ) ),
static_cast<int>( distancePx / std::cos( lineAngleRads ) ) );
2656 QPixmap pixmap( size );
2657 pixmap.fill( Qt::transparent );
2659 painter.begin( &pixmap );
2660 painter.setRenderHint( QPainter::Antialiasing );
2666 QgsSymbolRenderContext symbolContext( renderContext, QgsUnitTypes::RenderUnit::RenderPixels, 1.0,
false, Qgis::SymbolRenderHints() );
2668 std::unique_ptr< QgsLinePatternFillSymbolLayer > layerClone(
clone() );
2669 layerClone->setOffset( 0 );
2670 layerClone->drawPreviewIcon( symbolContext, pixmap.size() );
2672 return pixmap.toImage();
2684 mDistanceUnit = unit;
2685 mLineWidthUnit = unit;
2688 if ( mFillLineSymbol )
2689 mFillLineSymbol->setOutputUnit( unit );
2712 mDistanceMapUnitScale = scale;
2713 mLineWidthMapUnitScale = scale;
2714 mOffsetMapUnitScale = scale;
2720 mDistanceMapUnitScale == mLineWidthMapUnitScale &&
2721 mLineWidthMapUnitScale == mOffsetMapUnitScale )
2723 return mDistanceMapUnitScale;
2730 std::unique_ptr< QgsLinePatternFillSymbolLayer > patternLayer = std::make_unique< QgsLinePatternFillSymbolLayer >();
2736 QColor
color( Qt::black );
2739 if (
properties.contains( QStringLiteral(
"lineangle" ) ) )
2744 else if (
properties.contains( QStringLiteral(
"angle" ) ) )
2748 patternLayer->setLineAngle(
lineAngle );
2750 if (
properties.contains( QStringLiteral(
"distance" ) ) )
2754 patternLayer->setDistance(
distance );
2756 if (
properties.contains( QStringLiteral(
"linewidth" ) ) )
2761 else if (
properties.contains( QStringLiteral(
"outline_width" ) ) )
2765 else if (
properties.contains( QStringLiteral(
"line_width" ) ) )
2769 patternLayer->setLineWidth(
lineWidth );
2771 if (
properties.contains( QStringLiteral(
"color" ) ) )
2775 else if (
properties.contains( QStringLiteral(
"outline_color" ) ) )
2779 else if (
properties.contains( QStringLiteral(
"line_color" ) ) )
2783 patternLayer->setColor(
color );
2785 if (
properties.contains( QStringLiteral(
"offset" ) ) )
2789 patternLayer->setOffset(
offset );
2792 if (
properties.contains( QStringLiteral(
"distance_unit" ) ) )
2796 if (
properties.contains( QStringLiteral(
"distance_map_unit_scale" ) ) )
2800 if (
properties.contains( QStringLiteral(
"line_width_unit" ) ) )
2804 else if (
properties.contains( QStringLiteral(
"outline_width_unit" ) ) )
2808 if (
properties.contains( QStringLiteral(
"line_width_map_unit_scale" ) ) )
2812 if (
properties.contains( QStringLiteral(
"offset_unit" ) ) )
2816 if (
properties.contains( QStringLiteral(
"offset_map_unit_scale" ) ) )
2820 if (
properties.contains( QStringLiteral(
"outline_width_unit" ) ) )
2824 if (
properties.contains( QStringLiteral(
"outline_width_map_unit_scale" ) ) )
2828 if (
properties.contains( QStringLiteral(
"coordinate_reference" ) ) )
2832 if (
properties.contains( QStringLiteral(
"clip_mode" ) ) )
2837 patternLayer->restoreOldDataDefinedProperties(
properties );
2839 return patternLayer.release();
2844 return QStringLiteral(
"LinePatternFill" );
2847void QgsLinePatternFillSymbolLayer::applyPattern(
const QgsSymbolRenderContext &context, QBrush &brush,
double lineAngle,
double distance )
2849 mBrush.setTextureImage( QImage() );
2851 if ( !mFillLineSymbol )
2856 std::unique_ptr< QgsLineSymbol > fillLineSymbol( mFillLineSymbol->clone() );
2857 if ( !fillLineSymbol )
2873 outputPixelOffset = std::fmod( outputPixelOffset, outputPixelDist );
2874 if ( outputPixelOffset > outputPixelDist / 2.0 )
2875 outputPixelOffset -= outputPixelDist;
2879 double outputPixelBleed = 0;
2880 double outputPixelInterval = 0;
2881 for (
int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2885 outputPixelBleed = std::max( outputPixelBleed, outputPixelLayerBleed );
2888 if ( markerLineLayer )
2897 outputPixelInterval = std::max( outputPixelInterval, outputPixelLayerInterval );
2901 if ( outputPixelInterval > 0 )
2905 double intervalScale = std::round( outputPixelInterval ) / outputPixelInterval;
2906 outputPixelInterval = std::round( outputPixelInterval );
2908 for (
int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2913 if ( markerLineLayer )
2927 height = outputPixelDist;
2928 width = outputPixelInterval > 0 ? outputPixelInterval : height;
2932 width = outputPixelDist;
2933 height = outputPixelInterval > 0 ? outputPixelInterval : width;
2937 height = outputPixelDist / std::cos(
lineAngle * M_PI / 180 );
2938 width = outputPixelDist / std::sin(
lineAngle * M_PI / 180 );
2941 lineAngle = 180 * std::atan2(
static_cast< double >( height ),
static_cast< double >( width ) ) / M_PI;
2947 height = std::abs( height );
2948 width = std::abs( width );
2950 outputPixelDist = std::abs( height * std::cos(
lineAngle * M_PI / 180 ) );
2954 int offsetHeight =
static_cast< int >( std::round( outputPixelOffset / std::cos(
lineAngle * M_PI / 180 ) ) );
2955 outputPixelOffset = offsetHeight * std::cos(
lineAngle * M_PI / 180 );
2964 int bufferMulti =
static_cast< int >( std::max( std::ceil( outputPixelBleed / width ), std::ceil( outputPixelBleed / width ) ) );
2968 bufferMulti = std::max( bufferMulti, 1 );
2970 int xBuffer = width * bufferMulti;
2971 int yBuffer = height * bufferMulti;
2972 int innerWidth = width;
2973 int innerHeight = height;
2974 width += 2 * xBuffer;
2975 height += 2 * yBuffer;
2978 if ( width > 10000 || height > 10000 || width == 0 || height == 0 )
2983 QImage patternImage( width, height, QImage::Format_ARGB32 );
2984 patternImage.fill( 0 );
2986 QPointF p1, p2, p3, p4, p5, p6;
2989 p1 = QPointF( 0, yBuffer );
2990 p2 = QPointF( width, yBuffer );
2991 p3 = QPointF( 0, yBuffer + innerHeight );
2992 p4 = QPointF( width, yBuffer + innerHeight );
2996 p1 = QPointF( xBuffer, height );
2997 p2 = QPointF( xBuffer, 0 );
2998 p3 = QPointF( xBuffer + innerWidth, height );
2999 p4 = QPointF( xBuffer + innerWidth, 0 );
3003 dx = outputPixelDist * std::cos( ( 90 -
lineAngle ) * M_PI / 180.0 );
3004 dy = outputPixelDist * std::sin( ( 90 -
lineAngle ) * M_PI / 180.0 );
3005 p1 = QPointF( 0, height );
3006 p2 = QPointF( width, 0 );
3007 p3 = QPointF( -dx, height - dy );
3008 p4 = QPointF( width - dx, -dy );
3009 p5 = QPointF( dx, height + dy );
3010 p6 = QPointF( width + dx, dy );
3014 dx = outputPixelDist * std::cos( ( 90 -
lineAngle ) * M_PI / 180.0 );
3015 dy = outputPixelDist * std::sin( ( 90 -
lineAngle ) * M_PI / 180.0 );
3016 p1 = QPointF( width, 0 );
3017 p2 = QPointF( 0, height );
3018 p3 = QPointF( width - dx, -dy );
3019 p4 = QPointF( -dx, height - dy );
3020 p5 = QPointF( width + dx, dy );
3021 p6 = QPointF( dx, height + dy );
3025 dy = outputPixelDist * std::cos( ( 180 -
lineAngle ) * M_PI / 180 );
3026 dx = outputPixelDist * std::sin( ( 180 -
lineAngle ) * M_PI / 180 );
3027 p1 = QPointF( 0, 0 );
3028 p2 = QPointF( width, height );
3029 p5 = QPointF( dx, -dy );
3030 p6 = QPointF( width + dx, height - dy );
3031 p3 = QPointF( -dx, dy );
3032 p4 = QPointF( width - dx, height + dy );
3036 dy = outputPixelDist * std::cos( ( 180 -
lineAngle ) * M_PI / 180 );
3037 dx = outputPixelDist * std::sin( ( 180 -
lineAngle ) * M_PI / 180 );
3038 p1 = QPointF( width, height );
3039 p2 = QPointF( 0, 0 );
3040 p5 = QPointF( width + dx, height - dy );
3041 p6 = QPointF( dx, -dy );
3042 p3 = QPointF( width - dx, height + dy );
3043 p4 = QPointF( -dx, dy );
3050 p3 = QPointF( tempPt.x(), tempPt.y() );
3052 p4 = QPointF( tempPt.x(), tempPt.y() );
3054 p5 = QPointF( tempPt.x(), tempPt.y() );
3056 p6 = QPointF( tempPt.x(), tempPt.y() );
3060 p1 = QPointF( tempPt.x(), tempPt.y() );
3062 p2 = QPointF( tempPt.x(), tempPt.y() );
3065 QPainter p( &patternImage );
3069 p.setRenderHint( QPainter::Antialiasing,
false );
3070 QPen pen( QColor( Qt::black ) );
3071 pen.setWidthF( 0.1 );
3072 pen.setCapStyle( Qt::FlatCap );
3077 QPolygon polygon = QPolygon() << QPoint( 0, 0 ) << QPoint( width - 1, 0 ) << QPoint( width - 1, height - 1 ) << QPoint( 0, height - 1 ) << QPoint( 0, 0 );
3078 p.drawPolygon( polygon );
3080 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 );
3081 p.drawPolygon( polygon );
3087 p.setRenderHint( QPainter::Antialiasing,
true );
3099 fillLineSymbol->startRender( lineRenderContext, context.
fields() );
3101 QVector<QPolygonF> polygons;
3102 polygons.append( QPolygonF() << p1 << p2 );
3103 polygons.append( QPolygonF() << p3 << p4 );
3106 polygons.append( QPolygonF() << p5 << p6 );
3109 for (
const QPolygonF &polygon : std::as_const( polygons ) )
3111 fillLineSymbol->renderPolyline( polygon, context.
feature(), lineRenderContext, -1, context.
selected() );
3114 fillLineSymbol->stopRender( lineRenderContext );
3118 patternImage = patternImage.copy( xBuffer, yBuffer, patternImage.width() - 2 * xBuffer, patternImage.height() - 2 * yBuffer );
3123 QImage transparentImage = patternImage.copy();
3125 brush.setTextureImage( transparentImage );
3129 brush.setTextureImage( patternImage );
3132 QTransform brushTransform;
3133 brush.setTransform( brushTransform );
3141 || mFillLineSymbol->hasDataDefinedProperties()
3145 if ( mRenderUsingLines )
3147 if ( mFillLineSymbol )
3153 applyPattern( context,
mBrush, mLineAngle, mDistance );
3159 if ( mRenderUsingLines && mFillLineSymbol )
3167 if ( !mRenderUsingLines )
3201 outputPixelOffset = std::fmod( outputPixelOffset, outputPixelDistance );
3202 if ( outputPixelOffset > outputPixelDistance / 2.0 )
3203 outputPixelOffset -= outputPixelDistance;
3205 p->setPen( QPen( Qt::NoPen ) );
3210 p->setBrush( QBrush( selColor ) );
3234 std::unique_ptr< QgsPolygon > shapePolygon;
3235 std::unique_ptr< QgsGeometryEngine > shapeEngine;
3243 shapePolygon = std::make_unique< QgsPolygon >();
3247 for (
const QPolygonF &ring : *rings )
3253 shapeEngine->prepareGeometry();
3260 path.addPolygon( points );
3263 for (
const QPolygonF &ring : *rings )
3265 path.addPolygon( ring );
3268 p->setClipPath( path, Qt::IntersectClip );
3274 const QRectF boundingRect = points.boundingRect();
3276 QTransform invertedRotateTransform;
3282 QTransform transform;
3283 if ( applyBrushTransform )
3286 transform.translate( -boundingRect.center().x(),
3287 -boundingRect.center().y() );
3289 transform.translate( boundingRect.center().x(),
3290 boundingRect.center().y() );
3298 const QRectF transformedBounds = transform.map( points ).boundingRect();
3302 left = transformedBounds.left() - buffer * 2;
3303 top = transformedBounds.top() - buffer * 2;
3304 right = transformedBounds.right() + buffer * 2;
3305 bottom = transformedBounds.bottom() + buffer * 2;
3306 invertedRotateTransform = transform.inverted();
3308 if ( !applyBrushTransform )
3310 top -= transformedBounds.top() - ( outputPixelDistance * std::floor( transformedBounds.top() / outputPixelDistance ) );
3315 const bool needsExpressionContext = mFillLineSymbol->hasDataDefinedProperties();
3320 int currentLine = 0;
3321 for (
double currentY = top; currentY <= bottom; currentY += outputPixelDistance )
3326 if ( needsExpressionContext )
3330 double y1 = currentY;
3332 double y2 = currentY;
3333 invertedRotateTransform.map( left, currentY - outputPixelOffset, &x1, &y1 );
3334 invertedRotateTransform.map( right, currentY - outputPixelOffset, &x2, &y2 );
3339 std::unique_ptr< QgsAbstractGeometry > intersection( shapeEngine->intersection( &ls ) );
3340 for (
auto it = intersection->const_parts_begin(); it != intersection->const_parts_end(); ++it )
3342 if (
const QgsLineString *ls = qgsgeometry_cast< const QgsLineString * >( *it ) )
3350 mFillLineSymbol->renderPolyline( QPolygonF() << QPointF( x1, y1 ) << QPointF( x2, y2 ), context.
feature(), context.
renderContext() );
3362 map.insert( QStringLiteral(
"angle" ), QString::number( mLineAngle ) );
3363 map.insert( QStringLiteral(
"distance" ), QString::number( mDistance ) );
3364 map.insert( QStringLiteral(
"line_width" ), QString::number( mLineWidth ) );
3366 map.insert( QStringLiteral(
"offset" ), QString::number( mOffset ) );
3382 if ( mFillLineSymbol )
3393 QDomElement symbolizerElem = doc.createElement( QStringLiteral(
"se:PolygonSymbolizer" ) );
3394 if ( !props.value( QStringLiteral(
"uom" ), QString() ).toString().isEmpty() )
3395 symbolizerElem.setAttribute( QStringLiteral(
"uom" ), props.value( QStringLiteral(
"uom" ), QString() ).toString() );
3396 element.appendChild( symbolizerElem );
3401 QDomElement fillElem = doc.createElement( QStringLiteral(
"se:Fill" ) );
3402 symbolizerElem.appendChild( fillElem );
3404 QDomElement graphicFillElem = doc.createElement( QStringLiteral(
"se:GraphicFill" ) );
3405 fillElem.appendChild( graphicFillElem );
3407 QDomElement graphicElem = doc.createElement( QStringLiteral(
"se:Graphic" ) );
3408 graphicFillElem.appendChild( graphicElem );
3413 bool exportOk {
false };
3417 if ( ! image.isNull() )
3419 const QFileInfo info { context.exportFilePath() };
3420 QString pngPath { info.completeSuffix().isEmpty() ? context.exportFilePath() : context.exportFilePath().chopped( info.completeSuffix().length() ).append( QStringLiteral(
"png" ) ) };
3422 image.save( pngPath );
3431 QColor lineColor = mFillLineSymbol ? mFillLineSymbol->color() : QColor();
3432 double lineWidth = mFillLineSymbol ? mFillLineSymbol->width() : 0.0;
3440 double angle = props.value( QStringLiteral(
"angle" ), QStringLiteral(
"0" ) ).toDouble( &ok );
3443 angleFunc = QStringLiteral(
"%1 + %2" ).arg( props.value( QStringLiteral(
"angle" ), QStringLiteral(
"0" ) ).toString() ).arg( mLineAngle );
3447 angleFunc = QString::number(
angle + mLineAngle );
3452 QPointF lineOffset( std::sin( mLineAngle ) * mOffset, std::cos( mLineAngle ) * mOffset );
3460 QString featureStyle;
3461 featureStyle.append(
"Brush(" );
3462 featureStyle.append( QStringLiteral(
"fc:%1" ).arg(
mColor.name() ) );
3463 featureStyle.append( QStringLiteral(
",bc:%1" ).arg( QLatin1String(
"#00000000" ) ) );
3464 featureStyle.append(
",id:\"ogr-brush-2\"" );
3465 featureStyle.append( QStringLiteral(
",a:%1" ).arg( mLineAngle ) );
3466 featureStyle.append( QStringLiteral(
",s:%1" ).arg( mLineWidth * widthScaleFactor ) );
3467 featureStyle.append(
",dx:0mm" );
3468 featureStyle.append( QStringLiteral(
",dy:%1mm" ).arg( mDistance * widthScaleFactor ) );
3469 featureStyle.append(
')' );
3470 return featureStyle;
3476 && ( !mFillLineSymbol || !mFillLineSymbol->hasDataDefinedProperties() ) )
3501 Qt::PenStyle lineStyle;
3503 QDomElement fillElem = element.firstChildElement( QStringLiteral(
"Fill" ) );
3504 if ( fillElem.isNull() )
3507 QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral(
"GraphicFill" ) );
3508 if ( graphicFillElem.isNull() )
3511 QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral(
"Graphic" ) );
3512 if ( graphicElem.isNull() )
3518 if ( name != QLatin1String(
"horline" ) )
3526 double d = angleFunc.toDouble( &ok );
3535 offset = std::sqrt( std::pow( vectOffset.x(), 2 ) + std::pow( vectOffset.y(), 2 ) );
3538 double scaleFactor = 1.0;
3539 const QString uom = element.attribute( QStringLiteral(
"uom" ) );
3541 size = size * scaleFactor;
3544 std::unique_ptr< QgsLinePatternFillSymbolLayer > sl = std::make_unique< QgsLinePatternFillSymbolLayer >();
3545 sl->setOutputUnit( sldUnitSize );
3546 sl->setColor( lineColor );
3548 sl->setLineAngle(
angle );
3550 sl->setDistance( size );
3553 QDomElement strokeElem = element.firstChildElement( QStringLiteral(
"Stroke" ) );
3554 if ( !strokeElem.isNull() )
3565 return sl.release();
3665 std::unique_ptr< QgsPointPatternFillSymbolLayer > layer = std::make_unique< QgsPointPatternFillSymbolLayer >();
3666 if (
properties.contains( QStringLiteral(
"distance_x" ) ) )
3668 layer->setDistanceX(
properties[QStringLiteral(
"distance_x" )].toDouble() );
3670 if (
properties.contains( QStringLiteral(
"distance_y" ) ) )
3672 layer->setDistanceY(
properties[QStringLiteral(
"distance_y" )].toDouble() );
3674 if (
properties.contains( QStringLiteral(
"displacement_x" ) ) )
3676 layer->setDisplacementX(
properties[QStringLiteral(
"displacement_x" )].toDouble() );
3678 if (
properties.contains( QStringLiteral(
"displacement_y" ) ) )
3680 layer->setDisplacementY(
properties[QStringLiteral(
"displacement_y" )].toDouble() );
3682 if (
properties.contains( QStringLiteral(
"offset_x" ) ) )
3684 layer->setOffsetX(
properties[QStringLiteral(
"offset_x" )].toDouble() );
3686 if (
properties.contains( QStringLiteral(
"offset_y" ) ) )
3688 layer->setOffsetY(
properties[QStringLiteral(
"offset_y" )].toDouble() );
3691 if (
properties.contains( QStringLiteral(
"distance_x_unit" ) ) )
3695 if (
properties.contains( QStringLiteral(
"distance_x_map_unit_scale" ) ) )
3699 if (
properties.contains( QStringLiteral(
"distance_y_unit" ) ) )
3703 if (
properties.contains( QStringLiteral(
"distance_y_map_unit_scale" ) ) )
3707 if (
properties.contains( QStringLiteral(
"displacement_x_unit" ) ) )
3711 if (
properties.contains( QStringLiteral(
"displacement_x_map_unit_scale" ) ) )
3715 if (
properties.contains( QStringLiteral(
"displacement_y_unit" ) ) )
3719 if (
properties.contains( QStringLiteral(
"displacement_y_map_unit_scale" ) ) )
3723 if (
properties.contains( QStringLiteral(
"offset_x_unit" ) ) )
3727 if (
properties.contains( QStringLiteral(
"offset_x_map_unit_scale" ) ) )
3731 if (
properties.contains( QStringLiteral(
"offset_y_unit" ) ) )
3735 if (
properties.contains( QStringLiteral(
"offset_y_map_unit_scale" ) ) )
3740 if (
properties.contains( QStringLiteral(
"random_deviation_x" ) ) )
3742 layer->setMaximumRandomDeviationX(
properties[QStringLiteral(
"random_deviation_x" )].toDouble() );
3744 if (
properties.contains( QStringLiteral(
"random_deviation_y" ) ) )
3746 layer->setMaximumRandomDeviationY(
properties[QStringLiteral(
"random_deviation_y" )].toDouble() );
3748 if (
properties.contains( QStringLiteral(
"random_deviation_x_unit" ) ) )
3752 if (
properties.contains( QStringLiteral(
"random_deviation_x_map_unit_scale" ) ) )
3756 if (
properties.contains( QStringLiteral(
"random_deviation_y_unit" ) ) )
3760 if (
properties.contains( QStringLiteral(
"random_deviation_y_map_unit_scale" ) ) )
3764 unsigned long seed = 0;
3765 if (
properties.contains( QStringLiteral(
"seed" ) ) )
3771 std::random_device rd;
3772 std::mt19937 mt(
seed == 0 ? rd() :
seed );
3773 std::uniform_int_distribution<> uniformDist( 1, 999999999 );
3774 seed = uniformDist( mt );
3776 layer->setSeed(
seed );
3778 if (
properties.contains( QStringLiteral(
"outline_width_unit" ) ) )
3782 if (
properties.contains( QStringLiteral(
"outline_width_map_unit_scale" ) ) )
3786 if (
properties.contains( QStringLiteral(
"clip_mode" ) ) )
3790 if (
properties.contains( QStringLiteral(
"coordinate_reference" ) ) )
3795 if (
properties.contains( QStringLiteral(
"angle" ) ) )
3797 layer->setAngle(
properties[QStringLiteral(
"angle" )].toDouble() );
3800 layer->restoreOldDataDefinedProperties(
properties );
3802 return layer.release();
3807 return QStringLiteral(
"PointPatternFill" );
3810void QgsPointPatternFillSymbolLayer::applyPattern(
const QgsSymbolRenderContext &context, QBrush &brush,
double distanceX,
double distanceY,
3811 double displacementX,
double displacementY,
double offsetX,
double offsetY )
3818 double widthOffset = std::fmod(
3821 double heightOffset = std::fmod(
3825 if ( width > 10000 || height > 10000 )
3828 brush.setTextureImage( img );
3832 QImage patternImage( width, height, QImage::Format_ARGB32 );
3833 patternImage.fill( 0 );
3834 if ( patternImage.isNull() )
3836 brush.setTextureImage( QImage() );
3841 QPainter p( &patternImage );
3859 for (
double currentX = -width; currentX <= width * 2.0; currentX += width )
3861 for (
double currentY = -height; currentY <= height * 2.0; currentY += height )
3863 mMarkerSymbol->renderPoint( QPointF( currentX + widthOffset, currentY + heightOffset ), context.
feature(), pointRenderContext );
3874 for (
double currentX = -width; currentX <= width * 2.0; currentX += width )
3876 for (
double currentY = -height / 2.0; currentY <= height * 2.0; currentY += height )
3878 mMarkerSymbol->renderPoint( QPointF( currentX + widthOffset + displacementPixelX, currentY + heightOffset ), context.
feature(), pointRenderContext );
3882 for (
double currentX = -width / 2.0; currentX <= width * 2.0; currentX += width )
3884 for (
double currentY = -height; currentY <= height * 2.0; currentY += height / 2.0 )
3886 mMarkerSymbol->renderPoint( QPointF( currentX + widthOffset + ( std::fmod( currentY, height ) != 0 ? displacementPixelX : 0 ), currentY + heightOffset - displacementPixelY ), context.
feature(), pointRenderContext );
3895 QImage transparentImage = patternImage.copy();
3897 brush.setTextureImage( transparentImage );
3901 brush.setTextureImage( patternImage );
3903 QTransform brushTransform;
3904 brush.setTransform( brushTransform );
3922 if ( mRenderUsingMarkers )
3935 if ( mRenderUsingMarkers )
3957 if ( !mRenderUsingMarkers )
4000 const double widthOffset = std::fmod(
4012 const double heightOffset = std::fmod(
4038 p->setPen( QPen( Qt::NoPen ) );
4043 p->setBrush( QBrush( selColor ) );
4067 std::unique_ptr< QgsPolygon > shapePolygon;
4068 std::unique_ptr< QgsGeometryEngine > shapeEngine;
4075 shapePolygon = std::make_unique< QgsPolygon >();
4079 for (
const QPolygonF &ring : *rings )
4085 shapeEngine->prepareGeometry();
4092 path.addPolygon( points );
4095 for (
const QPolygonF &ring : *rings )
4097 path.addPolygon( ring );
4100 p->setClipPath( path, Qt::IntersectClip );
4106 const QRectF boundingRect = points.boundingRect();
4108 QTransform invertedRotateTransform;
4116 QTransform transform;
4117 if ( applyBrushTransform )
4120 transform.translate( -boundingRect.center().x(),
4121 -boundingRect.center().y() );
4122 transform.rotate( -
angle );
4123 transform.translate( boundingRect.center().x(),
4124 boundingRect.center().y() );
4129 transform.rotate( -
angle );
4132 const QRectF transformedBounds = transform.map( points ).boundingRect();
4133 left = transformedBounds.left() - 2 * width;
4134 top = transformedBounds.top() - 2 * height;
4135 right = transformedBounds.right() + 2 * width;
4136 bottom = transformedBounds.bottom() + 2 * height;
4137 invertedRotateTransform = transform.inverted();
4139 if ( !applyBrushTransform )
4141 left -= transformedBounds.left() - ( width * std::floor( transformedBounds.left() / width ) );
4142 top -= transformedBounds.top() - ( height * std::floor( transformedBounds.top() / height ) );
4147 left = boundingRect.left() - 2 * width;
4148 top = boundingRect.top() - 2 * height;
4149 right = boundingRect.right() + 2 * width;
4150 bottom = boundingRect.bottom() + 2 * height;
4152 if ( !applyBrushTransform )
4154 left -= boundingRect.left() - ( width * std::floor( boundingRect.left() / width ) );
4155 top -= boundingRect.top() - ( height * std::floor( boundingRect.top() / height ) );
4184 std::random_device rd;
4185 std::mt19937 mt(
seed == 0 ? rd() :
seed );
4186 std::uniform_real_distribution<> uniformDist( 0, 1 );
4192 const bool needsExpressionContext =
mMarkerSymbol->hasDataDefinedProperties();
4200 bool alternateColumn =
false;
4201 int currentCol = -3;
4202 for (
double currentX = left; currentX <= right; currentX += width, alternateColumn = !alternateColumn )
4207 if ( needsExpressionContext )
4210 bool alternateRow =
false;
4211 const double columnX = currentX + widthOffset;
4212 int currentRow = -3;
4213 for (
double currentY = top; currentY <= bottom; currentY += height, alternateRow = !alternateRow )
4218 double y = currentY + heightOffset;
4221 x += displacementPixelX;
4223 if ( !alternateColumn )
4224 y -= displacementPixelY;
4230 invertedRotateTransform.map( xx, yy, &x, &y );
4233 if ( useRandomShift )
4235 x += ( 2 * uniformDist( mt ) - 1 ) * maxRandomDeviationPixelX;
4236 y += ( 2 * uniformDist( mt ) - 1 ) * maxRandomDeviationPixelY;
4239 if ( needsExpressionContext )
4247 bool renderPoint =
true;
4255 renderPoint = shapeEngine->intersects( &p );
4265 renderPoint = shapeEngine->contains( markerBounds.
constGet() );
4267 renderPoint = shapeEngine->intersects( markerBounds.
constGet() );
4293 map.insert( QStringLiteral(
"distance_x" ), QString::number(
mDistanceX ) );
4294 map.insert( QStringLiteral(
"distance_y" ), QString::number(
mDistanceY ) );
4295 map.insert( QStringLiteral(
"displacement_x" ), QString::number(
mDisplacementX ) );
4296 map.insert( QStringLiteral(
"displacement_y" ), QString::number(
mDisplacementY ) );
4297 map.insert( QStringLiteral(
"offset_x" ), QString::number(
mOffsetX ) );
4298 map.insert( QStringLiteral(
"offset_y" ), QString::number(
mOffsetY ) );
4314 map.insert( QStringLiteral(
"random_deviation_x" ), QString::number(
mRandomDeviationX ) );
4315 map.insert( QStringLiteral(
"random_deviation_y" ), QString::number(
mRandomDeviationY ) );
4320 map.insert( QStringLiteral(
"seed" ), QString::number(
mSeed ) );
4321 map.insert( QStringLiteral(
"angle" ),
mAngle );
4340 for (
int symbolLayerIdx = 0; symbolLayerIdx <
mMarkerSymbol->symbolLayerCount(); symbolLayerIdx++ )
4342 QDomElement symbolizerElem = doc.createElement( QStringLiteral(
"se:PolygonSymbolizer" ) );
4343 if ( !props.value( QStringLiteral(
"uom" ), QString() ).toString().isEmpty() )
4344 symbolizerElem.setAttribute( QStringLiteral(
"uom" ), props.value( QStringLiteral(
"uom" ), QString() ).toString() );
4345 element.appendChild( symbolizerElem );
4350 QDomElement fillElem = doc.createElement( QStringLiteral(
"se:Fill" ) );
4351 symbolizerElem.appendChild( fillElem );
4353 QDomElement graphicFillElem = doc.createElement( QStringLiteral(
"se:GraphicFill" ) );
4354 fillElem.appendChild( graphicFillElem );
4361 bool exportOk {
false };
4365 if ( ! image.isNull() )
4367 QDomElement graphicElem = doc.createElement( QStringLiteral(
"se:Graphic" ) );
4368 graphicFillElem.appendChild( graphicElem );
4369 const QFileInfo info { context.exportFilePath() };
4370 QString pngPath { info.completeSuffix().isEmpty() ? context.exportFilePath() : context.exportFilePath().chopped( info.completeSuffix().length() ).append( QStringLiteral(
"png" ) ) };
4372 image.save( pngPath );
4391 symbolizerElem.appendChild( graphicMarginElem );
4395 markerLayer->writeSldMarker( doc, graphicFillElem, props );
4399 QString errorMsg = QStringLiteral(
"QgsMarkerSymbolLayer expected, %1 found. Skip it." ).arg( layer->
layerType() );
4400 graphicFillElem.appendChild( doc.createComment( errorMsg ) );
4404 QString errorMsg = QStringLiteral(
"Missing point pattern symbol layer. Skip it." );
4405 graphicFillElem.appendChild( doc.createComment( errorMsg ) );
4414 double angleRads { qDegreesToRadians(
mAngle ) };
4421 QPixmap pixmap( size );
4422 pixmap.fill( Qt::transparent );
4424 painter.begin( &pixmap );
4425 painter.setRenderHint( QPainter::Antialiasing );
4431 QgsSymbolRenderContext symbolContext( renderContext, QgsUnitTypes::RenderUnit::RenderPixels, 1.0,
false, Qgis::SymbolRenderHints() );
4433 std::unique_ptr< QgsPointPatternFillSymbolLayer > layerClone(
clone() );
4435 layerClone->setAngle( qRadiansToDegrees( angleRads ) );
4437 layerClone->drawPreviewIcon( symbolContext, pixmap.size() );
4439 return pixmap.toImage();
4447 QDomElement fillElem = element.firstChildElement( QStringLiteral(
"Fill" ) );
4448 if ( fillElem.isNull() )
4451 QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral(
"GraphicFill" ) );
4452 if ( graphicFillElem.isNull() )
4455 QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral(
"Graphic" ) );
4456 if ( graphicElem.isNull() )
4460 if ( !simpleMarkerSl )
4465 layers.append( simpleMarkerSl );
4467 std::unique_ptr< QgsMarkerSymbol > marker = std::make_unique< QgsMarkerSymbol >( layers );
4470 const double markerSize { marker->size() };
4472 std::unique_ptr< QgsPointPatternFillSymbolLayer > pointPatternFillSl = std::make_unique< QgsPointPatternFillSymbolLayer >();
4473 pointPatternFillSl->setSubSymbol( marker.release() );
4475 auto distanceParser = [ & ](
const QStringList & values )
4479 pointPatternFillSl->setDistanceXUnit( QgsUnitTypes::RenderUnit::RenderPixels );
4480 pointPatternFillSl->setDistanceYUnit( QgsUnitTypes::RenderUnit::RenderPixels );
4482 switch ( values.count( ) )
4487 const double v { values.at( 0 ).toDouble( &ok ) };
4490 pointPatternFillSl->setDistanceX( v * 2 + markerSize );
4491 pointPatternFillSl->setDistanceY( v * 2 + markerSize );
4498 const double vX { values.at( 1 ).toDouble( &ok ) };
4501 pointPatternFillSl->setDistanceX( vX * 2 + markerSize );
4503 const double vY { values.at( 0 ).toDouble( &ok ) };
4506 pointPatternFillSl->setDistanceY( vY * 2 + markerSize );
4513 const double vX { values.at( 1 ).toDouble( &ok ) };
4516 pointPatternFillSl->setDistanceX( vX * 2 + markerSize );
4518 const double vYt { values.at( 0 ).toDouble( &ok ) };
4521 const double vYb { values.at( 2 ).toDouble( &ok ) };
4524 pointPatternFillSl->setDistanceY( ( vYt + vYb ) + markerSize );
4532 const double vYt { values.at( 0 ).toDouble( &ok ) };
4535 const double vYb { values.at( 2 ).toDouble( &ok ) };
4538 pointPatternFillSl->setDistanceY( ( vYt + vYb ) + markerSize );
4541 const double vXr { values.at( 1 ).toDouble( &ok ) };
4544 const double vXl { values.at( 3 ).toDouble( &ok ) };
4547 pointPatternFillSl->setDistanceX( ( vXr + vXl ) + markerSize );
4559 for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
4562 if ( it.key() == QLatin1String(
"distance" ) )
4564 distanceParser( it.value().split(
',' ) );
4567 else if ( it.key() == QLatin1String(
"graphic-margin" ) )
4569 distanceParser( it.value().split(
' ' ) );
4573 return pointPatternFillSl.release();
4655 attributes.unite(
mMarkerSymbol->usedAttributes( context ) );
4693 std::unique_ptr< QgsCentroidFillSymbolLayer > sl = std::make_unique< QgsCentroidFillSymbolLayer >();
4695 if (
properties.contains( QStringLiteral(
"point_on_surface" ) ) )
4696 sl->setPointOnSurface(
properties[QStringLiteral(
"point_on_surface" )].toInt() != 0 );
4697 if (
properties.contains( QStringLiteral(
"point_on_all_parts" ) ) )
4698 sl->setPointOnAllParts(
properties[QStringLiteral(
"point_on_all_parts" )].toInt() != 0 );
4699 if (
properties.contains( QStringLiteral(
"clip_points" ) ) )
4700 sl->setClipPoints(
properties[QStringLiteral(
"clip_points" )].toInt() != 0 );
4701 if (
properties.contains( QStringLiteral(
"clip_on_current_part_only" ) ) )
4702 sl->setClipOnCurrentPartOnly(
properties[QStringLiteral(
"clip_on_current_part_only" )].toInt() != 0 );
4704 sl->restoreOldDataDefinedProperties(
properties );
4706 return sl.release();
4711 return QStringLiteral(
"CentroidFill" );
4738 part.exterior = points;
4740 part.rings = *rings;
4747 mCurrentParts << part;
4752 const double prevOpacity =
mMarker->opacity();
4755 mMarker->setOpacity( prevOpacity );