46 #include <QSvgRenderer>
47 #include <QDomDocument>
48 #include <QDomElement>
56 Qt::PenJoinStyle penJoinStyle )
57 : mBrushStyle( style )
58 , mStrokeColor( strokeColor )
59 , mStrokeStyle( strokeStyle )
60 , mStrokeWidth( strokeWidth )
61 , mPenJoinStyle( penJoinStyle )
105 void QgsSimpleFillSymbolLayer::applyDataDefinedSymbology(
QgsSymbolRenderContext &context, QBrush &brush, QPen &pen, QPen &selPen )
123 if ( !exprVal.isNull() )
130 penColor.setAlphaF( context.
opacity() * penColor.alphaF() );
131 pen.setColor( penColor );
137 if ( !exprVal.isNull() )
139 double width = exprVal.toDouble( &ok );
143 pen.setWidthF( width );
144 selPen.setWidthF( width );
181 if ( props.contains( QStringLiteral(
"color" ) ) )
183 if ( props.contains( QStringLiteral(
"style" ) ) )
185 if ( props.contains( QStringLiteral(
"color_border" ) ) )
190 else if ( props.contains( QStringLiteral(
"outline_color" ) ) )
194 else if ( props.contains( QStringLiteral(
"line_color" ) ) )
199 if ( props.contains( QStringLiteral(
"style_border" ) ) )
204 else if ( props.contains( QStringLiteral(
"outline_style" ) ) )
208 else if ( props.contains( QStringLiteral(
"line_style" ) ) )
212 if ( props.contains( QStringLiteral(
"width_border" ) ) )
215 strokeWidth = props[QStringLiteral(
"width_border" )].toDouble();
217 else if ( props.contains( QStringLiteral(
"outline_width" ) ) )
219 strokeWidth = props[QStringLiteral(
"outline_width" )].toDouble();
221 else if ( props.contains( QStringLiteral(
"line_width" ) ) )
223 strokeWidth = props[QStringLiteral(
"line_width" )].toDouble();
225 if ( props.contains( QStringLiteral(
"offset" ) ) )
227 if ( props.contains( QStringLiteral(
"joinstyle" ) ) )
232 if ( props.contains( QStringLiteral(
"border_width_unit" ) ) )
236 else if ( props.contains( QStringLiteral(
"outline_width_unit" ) ) )
240 else if ( props.contains( QStringLiteral(
"line_width_unit" ) ) )
244 if ( props.contains( QStringLiteral(
"offset_unit" ) ) )
247 if ( props.contains( QStringLiteral(
"border_width_map_unit_scale" ) ) )
249 if ( props.contains( QStringLiteral(
"offset_map_unit_scale" ) ) )
252 sl->restoreOldDataDefinedProperties( props );
260 return QStringLiteral(
"SimpleFill" );
272 selColor.setAlphaF( context.
opacity() );
329 #ifndef QT_NO_PRINTER
330 if (
mBrush.style() == Qt::SolidPattern ||
mBrush.style() == Qt::NoBrush || !
dynamic_cast<QPrinter *
>( p->device() ) )
337 #ifndef QT_NO_PRINTER
344 p->setPen( Qt::NoPen );
348 p->setBrush( Qt::NoBrush );
366 map[QStringLiteral(
"outline_width" )] = QString::number(
mStrokeWidth );
394 QDomElement symbolizerElem = doc.createElement( QStringLiteral(
"se:PolygonSymbolizer" ) );
395 if ( !props.value( QStringLiteral(
"uom" ), QString() ).toString().isEmpty() )
396 symbolizerElem.setAttribute( QStringLiteral(
"uom" ), props.value( QStringLiteral(
"uom" ), QString() ).toString() );
397 element.appendChild( symbolizerElem );
405 QDomElement fillElem = doc.createElement( QStringLiteral(
"se:Fill" ) );
406 symbolizerElem.appendChild( fillElem );
413 QDomElement strokeElem = doc.createElement( QStringLiteral(
"se:Stroke" ) );
414 symbolizerElem.appendChild( strokeElem );
429 symbolStyle.append(
';' );
438 Qt::BrushStyle fillStyle;
442 QDomElement fillElem = element.firstChildElement( QStringLiteral(
"Fill" ) );
445 QDomElement strokeElem = element.firstChildElement( QStringLiteral(
"Stroke" ) );
451 double scaleFactor = 1.0;
452 const QString uom = element.attribute( QStringLiteral(
"uom" ) );
459 sl->setOutputUnit( sldUnitSize );
468 return penBleed + offsetBleed;
529 : mGradientColorType( colorType )
530 , mGradientType( gradientType )
531 , mCoordinateMode( coordinateMode )
532 , mGradientSpread( spread )
533 , mReferencePoint1( QPointF( 0.5, 0 ) )
534 , mReferencePoint2( QPointF( 0.5, 1 ) )
555 bool refPoint1IsCentroid =
false;
557 bool refPoint2IsCentroid =
false;
562 if ( props.contains( QStringLiteral(
"type" ) ) )
564 if ( props.contains( QStringLiteral(
"coordinate_mode" ) ) )
566 if ( props.contains( QStringLiteral(
"spread" ) ) )
568 if ( props.contains( QStringLiteral(
"color_type" ) ) )
570 if ( props.contains( QStringLiteral(
"gradient_color" ) ) )
575 else if ( props.contains( QStringLiteral(
"color" ) ) )
579 if ( props.contains( QStringLiteral(
"gradient_color2" ) ) )
584 if ( props.contains( QStringLiteral(
"reference_point1" ) ) )
586 if ( props.contains( QStringLiteral(
"reference_point1_iscentroid" ) ) )
587 refPoint1IsCentroid = props[QStringLiteral(
"reference_point1_iscentroid" )].toInt();
588 if ( props.contains( QStringLiteral(
"reference_point2" ) ) )
590 if ( props.contains( QStringLiteral(
"reference_point2_iscentroid" ) ) )
591 refPoint2IsCentroid = props[QStringLiteral(
"reference_point2_iscentroid" )].toInt();
592 if ( props.contains( QStringLiteral(
"angle" ) ) )
593 angle = props[QStringLiteral(
"angle" )].toDouble();
595 if ( props.contains( QStringLiteral(
"offset" ) ) )
612 if ( props.contains( QStringLiteral(
"offset_unit" ) ) )
614 if ( props.contains( QStringLiteral(
"offset_map_unit_scale" ) ) )
617 sl->setReferencePoint1IsCentroid( refPoint1IsCentroid );
619 sl->setReferencePoint2IsCentroid( refPoint2IsCentroid );
620 sl->setAngle(
angle );
622 sl->setColorRamp( gradientRamp );
624 sl->restoreOldDataDefinedProperties( props );
637 return QStringLiteral(
"GradientFill" );
640 void QgsGradientFillSymbolLayer::applyDataDefinedSymbology(
QgsSymbolRenderContext &context,
const QPolygonF &points )
685 if ( currentType == QObject::tr(
"linear" ) )
689 else if ( currentType == QObject::tr(
"radial" ) )
693 else if ( currentType == QObject::tr(
"conical" ) )
707 if ( currentCoordMode == QObject::tr(
"feature" ) )
711 else if ( currentCoordMode == QObject::tr(
"viewport" ) )
725 if ( currentSpread == QObject::tr(
"pad" ) )
729 else if ( currentSpread == QObject::tr(
"repeat" ) )
733 else if ( currentSpread == QObject::tr(
"reflect" ) )
780 if ( refPoint1IsCentroid || refPoint2IsCentroid )
785 QRectF bbox = points.boundingRect();
786 double centroidX = (
centroid.
x() - bbox.left() ) / bbox.width();
787 double centroidY = (
centroid.
y() - bbox.top() ) / bbox.height();
789 if ( refPoint1IsCentroid )
791 refPoint1X = centroidX;
792 refPoint1Y = centroidY;
794 if ( refPoint2IsCentroid )
796 refPoint2X = centroidX;
797 refPoint2Y = centroidY;
803 spread, QPointF( refPoint1X, refPoint1Y ), QPointF( refPoint2X, refPoint2Y ),
angle );
806 QPointF QgsGradientFillSymbolLayer::rotateReferencePoint( QPointF refPoint,
double angle )
811 QLineF refLine = QLineF( QPointF( 0.5, 0.5 ), refPoint );
813 refLine.setAngle( refLine.angle() +
angle );
815 QPointF rotatedReferencePoint = refLine.p2();
817 if ( rotatedReferencePoint.x() > 1 )
818 rotatedReferencePoint.setX( 1 );
819 if ( rotatedReferencePoint.x() < 0 )
820 rotatedReferencePoint.setX( 0 );
821 if ( rotatedReferencePoint.y() > 1 )
822 rotatedReferencePoint.setY( 1 );
823 if ( rotatedReferencePoint.y() < 0 )
824 rotatedReferencePoint.setY( 0 );
826 return rotatedReferencePoint;
833 QPointF referencePoint1, QPointF referencePoint2,
const double angle )
838 QColor fillColor2 =
color2;
839 fillColor2.setAlphaF( context.
opacity() * fillColor2.alphaF() );
850 gradient = QLinearGradient( rotatedReferencePoint1, rotatedReferencePoint2 );
853 gradient = QRadialGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).length() );
856 gradient = QConicalGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).
angle() );
862 gradient.setCoordinateMode( QGradient::ObjectBoundingMode );
865 gradient.setCoordinateMode( QGradient::StretchToDeviceMode );
871 gradient.setSpread( QGradient::PadSpread );
874 gradient.setSpread( QGradient::ReflectSpread );
877 gradient.setSpread( QGradient::RepeatSpread );
893 gradient.setColorAt( 1.0, fillColor2 );
897 brush = QBrush( gradient );
904 selColor.setAlphaF( context.
opacity() );
921 applyDataDefinedSymbology( context, points );
924 p->setPen( Qt::NoPen );
957 map[QStringLiteral(
"color_type" )] = QString::number(
static_cast< int >(
mGradientColorType ) );
958 map[QStringLiteral(
"type" )] = QString::number(
static_cast<int>(
mGradientType ) );
959 map[QStringLiteral(
"coordinate_mode" )] = QString::number(
static_cast< int >(
mCoordinateMode ) );
960 map[QStringLiteral(
"spread" )] = QString::number(
static_cast< int >(
mGradientSpread ) );
965 map[QStringLiteral(
"angle" )] = QString::number(
mAngle );
971 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
1037 int blurRadius,
bool useWholeShape,
double maxDistance )
1038 : mBlurRadius( blurRadius )
1039 , mUseWholeShape( useWholeShape )
1040 , mMaxDistance( maxDistance )
1041 , mColorType( colorType )
1060 if ( props.contains( QStringLiteral(
"color_type" ) ) )
1064 if ( props.contains( QStringLiteral(
"shapeburst_color" ) ) )
1069 else if ( props.contains( QStringLiteral(
"color" ) ) )
1074 if ( props.contains( QStringLiteral(
"shapeburst_color2" ) ) )
1079 else if ( props.contains( QStringLiteral(
"gradient_color2" ) ) )
1083 if ( props.contains( QStringLiteral(
"blur_radius" ) ) )
1085 blurRadius = props[QStringLiteral(
"blur_radius" )].toInt();
1087 if ( props.contains( QStringLiteral(
"use_whole_shape" ) ) )
1089 useWholeShape = props[QStringLiteral(
"use_whole_shape" )].toInt();
1091 if ( props.contains( QStringLiteral(
"max_distance" ) ) )
1093 maxDistance = props[QStringLiteral(
"max_distance" )].toDouble();
1095 if ( props.contains( QStringLiteral(
"offset" ) ) )
1114 if ( props.contains( QStringLiteral(
"offset_unit" ) ) )
1118 if ( props.contains( QStringLiteral(
"distance_unit" ) ) )
1122 if ( props.contains( QStringLiteral(
"offset_map_unit_scale" ) ) )
1126 if ( props.contains( QStringLiteral(
"distance_map_unit_scale" ) ) )
1130 if ( props.contains( QStringLiteral(
"ignore_rings" ) ) )
1132 sl->setIgnoreRings( props[QStringLiteral(
"ignore_rings" )].toInt() );
1136 sl->setColorRamp( gradientRamp );
1139 sl->restoreOldDataDefinedProperties( props );
1141 return sl.release();
1146 return QStringLiteral(
"ShapeburstFill" );
1151 if ( mGradientRamp.get() == ramp )
1154 mGradientRamp.reset( ramp );
1157 void QgsShapeburstFillSymbolLayer::applyDataDefinedSymbology(
QgsSymbolRenderContext &context, QColor &color, QColor &color2,
int &blurRadius,
bool &useWholeShape,
1158 double &maxDistance,
bool &ignoreRings )
1215 selColor.setAlphaF( context.
opacity() );
1216 mSelBrush = QBrush( selColor );
1235 p->setBrush( mSelBrush );
1236 QPointF
offset = mOffset;
1271 int outputPixelMaxDist = 0;
1279 std::unique_ptr< QgsGradientColorRamp > twoColorGradientRamp;
1282 twoColorGradientRamp = std::make_unique< QgsGradientColorRamp >( color1,
color2 );
1286 p->setPen( QPen( Qt::NoPen ) );
1291 int pointsWidth =
static_cast< int >( std::round( points.boundingRect().width() ) );
1292 int pointsHeight =
static_cast< int >( std::round( points.boundingRect().height() ) );
1293 int imWidth = pointsWidth + ( sideBuffer * 2 );
1294 int imHeight = pointsHeight + ( sideBuffer * 2 );
1300 std::unique_ptr< QImage > fillImage = std::make_unique< QImage >( imWidth,
1301 imHeight, QImage::Format_ARGB32_Premultiplied );
1302 if ( fillImage->isNull() )
1312 std::unique_ptr< QImage > alphaImage = std::make_unique< QImage >( fillImage->width(), fillImage->height(), QImage::Format_ARGB32_Premultiplied );
1313 if ( alphaImage->isNull() )
1325 fillImage->fill( Qt::black );
1331 alphaImage->fill( Qt::transparent );
1337 QPainter imgPainter;
1338 imgPainter.begin( alphaImage.get() );
1339 imgPainter.setRenderHint( QPainter::Antialiasing,
true );
1340 imgPainter.setBrush( QBrush( Qt::white ) );
1341 imgPainter.setPen( QPen( Qt::black ) );
1342 imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1351 imgPainter.begin( fillImage.get() );
1354 imgPainter.drawImage( 0, 0, *alphaImage );
1361 imgPainter.setBrush( QBrush( Qt::white ) );
1362 imgPainter.setPen( QPen( Qt::black ) );
1363 imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1372 double *dtArray = distanceTransform( fillImage.get(), context.
renderContext() );
1392 imgPainter.begin( fillImage.get() );
1393 imgPainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
1394 imgPainter.drawImage( 0, 0, *alphaImage );
1402 QPointF
offset = mOffset;
1419 p->drawImage( points.boundingRect().left() - sideBuffer, points.boundingRect().top() - sideBuffer, *fillImage );
1430 void QgsShapeburstFillSymbolLayer::distanceTransform1d(
double *f,
int n,
int *v,
double *z,
double *d )
1436 for (
int q = 1; q <= n - 1; q++ )
1438 double s = ( ( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1442 s = ( ( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1451 for (
int q = 0; q <= n - 1; q++ )
1453 while ( z[k + 1] < q )
1455 d[q] = ( q - v[k] ) * ( q - v[k] ) + f[v[k]];
1460 void QgsShapeburstFillSymbolLayer::distanceTransform2d(
double *im,
int width,
int height,
QgsRenderContext &context )
1462 int maxDimension = std::max( width, height );
1463 double *f =
new double[ maxDimension ];
1464 int *v =
new int[ maxDimension ];
1465 double *z =
new double[ maxDimension + 1 ];
1466 double *d =
new double[ maxDimension ];
1469 for (
int x = 0; x < width; x++ )
1474 for (
int y = 0; y < height; y++ )
1476 f[y] = im[ x + y * width ];
1478 distanceTransform1d( f, height, v, z, d );
1479 for (
int y = 0; y < height; y++ )
1481 im[ x + y * width ] = d[y];
1486 for (
int y = 0; y < height; y++ )
1491 for (
int x = 0; x < width; x++ )
1493 f[x] = im[ x + y * width ];
1495 distanceTransform1d( f, width, v, z, d );
1496 for (
int x = 0; x < width; x++ )
1498 im[ x + y * width ] = d[x];
1509 double *QgsShapeburstFillSymbolLayer::distanceTransform( QImage *im,
QgsRenderContext &context )
1511 int width = im->width();
1512 int height = im->height();
1514 double *dtArray =
new double[width * height];
1519 for (
int heightIndex = 0; heightIndex < height; ++heightIndex )
1524 const QRgb *scanLine =
reinterpret_cast< const QRgb *
>( im->constScanLine( heightIndex ) );
1525 for (
int widthIndex = 0; widthIndex < width; ++widthIndex )
1527 tmpRgb = scanLine[widthIndex];
1528 if ( qRed( tmpRgb ) == 0 )
1536 dtArray[ idx ] =
INF;
1543 distanceTransform2d( dtArray, width, height, context );
1548 void QgsShapeburstFillSymbolLayer::dtArrayToQImage(
double *array, QImage *im,
QgsColorRamp *ramp,
QgsRenderContext &context,
bool useWholeShape,
int maxPixelDistance )
1550 int width = im->width();
1551 int height = im->height();
1554 double maxDistanceValue;
1559 double dtMaxValue = array[0];
1560 for (
int i = 1; i < ( width * height ); ++i )
1562 if ( array[i] > dtMaxValue )
1564 dtMaxValue = array[i];
1569 maxDistanceValue = std::sqrt( dtMaxValue );
1574 maxDistanceValue = maxPixelDistance;
1579 double squaredVal = 0;
1582 for (
int heightIndex = 0; heightIndex < height; ++heightIndex )
1587 QRgb *scanLine =
reinterpret_cast< QRgb *
>( im->scanLine( heightIndex ) );
1588 for (
int widthIndex = 0; widthIndex < width; ++widthIndex )
1591 squaredVal = array[idx];
1594 if ( maxDistanceValue > 0 )
1596 pixVal = squaredVal > 0 ? std::min( ( std::sqrt( squaredVal ) / maxDistanceValue ), 1.0 ) : 0;
1605 scanLine[widthIndex] = qPremultiply( ramp->
color( pixVal ).rgba() );
1616 map[QStringLiteral(
"color_type" )] = QString::number(
static_cast< int >( mColorType ) );
1617 map[QStringLiteral(
"blur_radius" )] = QString::number( mBlurRadius );
1618 map[QStringLiteral(
"use_whole_shape" )] = QString::number( mUseWholeShape );
1619 map[QStringLiteral(
"max_distance" )] = QString::number( mMaxDistance );
1622 map[QStringLiteral(
"ignore_rings" )] = QString::number( mIgnoreRings );
1626 if ( mGradientRamp )
1628 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
1629 map.unite( mGradientRamp->properties() );
1631 map.insert( mGradientRamp->properties() );
1640 std::unique_ptr< QgsShapeburstFillSymbolLayer > sl = std::make_unique< QgsShapeburstFillSymbolLayer >(
mColor, mColor2, mColorType, mBlurRadius, mUseWholeShape, mMaxDistance );
1641 if ( mGradientRamp )
1643 sl->setColorRamp( mGradientRamp->clone() );
1645 sl->setDistanceUnit( mDistanceUnit );
1646 sl->setDistanceMapUnitScale( mDistanceMapUnitScale );
1647 sl->setIgnoreRings( mIgnoreRings );
1648 sl->setOffset( mOffset );
1649 sl->setOffsetUnit( mOffsetUnit );
1650 sl->setOffsetMapUnitScale( mOffsetMapUnitScale );
1653 return sl.release();
1658 double offsetBleed = context.
convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale );
1669 mDistanceUnit = unit;
1675 if ( mDistanceUnit == mOffsetUnit )
1677 return mDistanceUnit;
1690 mDistanceMapUnitScale = scale;
1691 mOffsetMapUnitScale = scale;
1696 if ( mDistanceMapUnitScale == mOffsetMapUnitScale )
1698 return mDistanceMapUnitScale;
1723 p->setPen( QPen( Qt::NoPen ) );
1725 QTransform bkTransform =
mBrush.transform();
1729 QTransform t =
mBrush.transform();
1730 t.translate( leftCorner.x(), leftCorner.y() );
1731 mBrush.setTransform( t );
1735 QTransform t =
mBrush.transform();
1736 t.translate( 0, 0 );
1737 mBrush.setTransform( t );
1743 p->setBrush( QBrush( selColor ) );
1749 QTransform t =
mBrush.transform();
1751 mBrush.setTransform( t );
1756 mBrush.setTransform( bkTransform );
1792 return Qt::SolidLine;
1796 return Qt::SolidLine;
1800 return mStroke->dxfPenStyle();
1836 , mPatternWidth( width )
1840 mColor = QColor( 255, 255, 255 );
1846 , mPatternWidth( width )
1847 , mSvgData( svgData )
1852 mColor = QColor( 255, 255, 255 );
1853 setDefaultSvgParams();
1861 mPatternWidthUnit = unit;
1862 mSvgStrokeWidthUnit = unit;
1865 mStroke->setOutputUnit( unit );
1871 if ( mPatternWidthUnit != unit || mSvgStrokeWidthUnit != unit ||
mStrokeWidthUnit != unit )
1881 mPatternWidthMapUnitScale = scale;
1882 mSvgStrokeWidthMapUnitScale = scale;
1888 mPatternWidthMapUnitScale == mSvgStrokeWidthMapUnitScale &&
1891 return mPatternWidthMapUnitScale;
1901 mSvgFilePath = svgPath;
1902 setDefaultSvgParams();
1912 if (
properties.contains( QStringLiteral(
"width" ) ) )
1914 width =
properties[QStringLiteral(
"width" )].toDouble();
1916 if (
properties.contains( QStringLiteral(
"svgFile" ) ) )
1920 if (
properties.contains( QStringLiteral(
"angle" ) ) )
1925 std::unique_ptr< QgsSVGFillSymbolLayer > symbolLayer;
1928 symbolLayer = std::make_unique< QgsSVGFillSymbolLayer >(
svgFilePath, width,
angle );
1932 if (
properties.contains( QStringLiteral(
"data" ) ) )
1934 data = QByteArray::fromHex(
properties[QStringLiteral(
"data" )].toString().toLocal8Bit() );
1936 symbolLayer = std::make_unique< QgsSVGFillSymbolLayer >( data, width,
angle );
1940 if (
properties.contains( QStringLiteral(
"svgFillColor" ) ) )
1945 else if (
properties.contains( QStringLiteral(
"color" ) ) )
1949 if (
properties.contains( QStringLiteral(
"svgOutlineColor" ) ) )
1954 else if (
properties.contains( QStringLiteral(
"outline_color" ) ) )
1958 else if (
properties.contains( QStringLiteral(
"line_color" ) ) )
1962 if (
properties.contains( QStringLiteral(
"svgOutlineWidth" ) ) )
1965 symbolLayer->setSvgStrokeWidth(
properties[QStringLiteral(
"svgOutlineWidth" )].toDouble() );
1967 else if (
properties.contains( QStringLiteral(
"outline_width" ) ) )
1969 symbolLayer->setSvgStrokeWidth(
properties[QStringLiteral(
"outline_width" )].toDouble() );
1971 else if (
properties.contains( QStringLiteral(
"line_width" ) ) )
1973 symbolLayer->setSvgStrokeWidth(
properties[QStringLiteral(
"line_width" )].toDouble() );
1977 if (
properties.contains( QStringLiteral(
"pattern_width_unit" ) ) )
1981 if (
properties.contains( QStringLiteral(
"pattern_width_map_unit_scale" ) ) )
1985 if (
properties.contains( QStringLiteral(
"svg_outline_width_unit" ) ) )
1989 if (
properties.contains( QStringLiteral(
"svg_outline_width_map_unit_scale" ) ) )
1993 if (
properties.contains( QStringLiteral(
"outline_width_unit" ) ) )
1997 if (
properties.contains( QStringLiteral(
"outline_width_map_unit_scale" ) ) )
2002 if (
properties.contains( QStringLiteral(
"parameters" ) ) )
2008 symbolLayer->restoreOldDataDefinedProperties(
properties );
2010 return symbolLayer.release();
2015 QVariantMap::iterator it =
properties.find( QStringLiteral(
"svgFile" ) );
2027 return QStringLiteral(
"SVGFill" );
2030 void QgsSVGFillSymbolLayer::applyPattern( QBrush &brush,
const QString &svgFilePath,
double patternWidth,
QgsUnitTypes::RenderUnit patternWidthUnit,
2031 const QColor &svgFillColor,
const QColor &svgStrokeColor,
double svgStrokeWidth,
2035 if ( mSvgViewBox.isNull() )
2042 if (
static_cast< int >( size ) < 1.0 || 10000.0 < size )
2044 brush.setTextureImage( QImage() );
2048 bool fitsInCache =
true;
2056 double hwRatio = 1.0;
2057 if ( patternPict.width() > 0 )
2059 hwRatio =
static_cast< double >( patternPict.height() ) /
static_cast< double >( patternPict.width() );
2061 patternImage = QImage(
static_cast< int >( size ),
static_cast< int >( size * hwRatio ), QImage::Format_ARGB32_Premultiplied );
2062 patternImage.fill( 0 );
2064 QPainter p( &patternImage );
2065 p.drawPicture( QPointF( size / 2, size * hwRatio / 2 ), patternPict );
2068 QTransform brushTransform;
2071 QImage transparentImage = patternImage.copy();
2073 brush.setTextureImage( transparentImage );
2077 brush.setTextureImage( patternImage );
2079 brush.setTransform( brushTransform );
2087 applyPattern(
mBrush, mSvgFilePath, mPatternWidth, mPatternWidthUnit,
mColor, mSvgStrokeColor, mSvgStrokeWidth, mSvgStrokeWidthUnit, context, mPatternWidthMapUnitScale, mSvgStrokeWidthMapUnitScale, evaluatedParameters );
2112 for (
auto ringIt = rings->constBegin(); ringIt != rings->constEnd(); ++ringIt )
2123 if ( !mSvgFilePath.isEmpty() )
2125 map.insert( QStringLiteral(
"svgFile" ), mSvgFilePath );
2129 map.insert( QStringLiteral(
"data" ), QString( mSvgData.toHex() ) );
2132 map.insert( QStringLiteral(
"width" ), QString::number( mPatternWidth ) );
2133 map.insert( QStringLiteral(
"angle" ), QString::number(
mAngle ) );
2138 map.insert( QStringLiteral(
"outline_width" ), QString::number( mSvgStrokeWidth ) );
2155 std::unique_ptr< QgsSVGFillSymbolLayer > clonedLayer;
2156 if ( !mSvgFilePath.isEmpty() )
2158 clonedLayer = std::make_unique< QgsSVGFillSymbolLayer >( mSvgFilePath, mPatternWidth,
mAngle );
2159 clonedLayer->setSvgFillColor(
mColor );
2160 clonedLayer->setSvgStrokeColor( mSvgStrokeColor );
2161 clonedLayer->setSvgStrokeWidth( mSvgStrokeWidth );
2165 clonedLayer = std::make_unique< QgsSVGFillSymbolLayer >( mSvgData, mPatternWidth,
mAngle );
2168 clonedLayer->setPatternWidthUnit( mPatternWidthUnit );
2169 clonedLayer->setPatternWidthMapUnitScale( mPatternWidthMapUnitScale );
2170 clonedLayer->setSvgStrokeWidthUnit( mSvgStrokeWidthUnit );
2171 clonedLayer->setSvgStrokeWidthMapUnitScale( mSvgStrokeWidthMapUnitScale );
2175 clonedLayer->setParameters( mParameters );
2179 clonedLayer->setSubSymbol( mStroke->clone() );
2183 return clonedLayer.release();
2188 QDomElement symbolizerElem = doc.createElement( QStringLiteral(
"se:PolygonSymbolizer" ) );
2189 if ( !props.value( QStringLiteral(
"uom" ), QString() ).toString().isEmpty() )
2190 symbolizerElem.setAttribute( QStringLiteral(
"uom" ), props.value( QStringLiteral(
"uom" ), QString() ).toString() );
2191 element.appendChild( symbolizerElem );
2195 QDomElement fillElem = doc.createElement( QStringLiteral(
"se:Fill" ) );
2196 symbolizerElem.appendChild( fillElem );
2198 QDomElement graphicFillElem = doc.createElement( QStringLiteral(
"se:GraphicFill" ) );
2199 fillElem.appendChild( graphicFillElem );
2201 QDomElement graphicElem = doc.createElement( QStringLiteral(
"se:Graphic" ) );
2202 graphicFillElem.appendChild( graphicElem );
2204 if ( !mSvgFilePath.isEmpty() )
2215 symbolizerElem.appendChild( doc.createComment( QStringLiteral(
"SVG from data not implemented yet" ) ) );
2221 double angle = props.value( QStringLiteral(
"angle" ), QStringLiteral(
"0" ) ).toDouble( &ok );
2224 angleFunc = QStringLiteral(
"%1 + %2" ).arg( props.value( QStringLiteral(
"angle" ), QStringLiteral(
"0" ) ).toString() ).arg(
mAngle );
2237 mStroke->toSld( doc, element, props );
2249 return mStroke.get();
2256 mStroke.reset(
nullptr );
2269 mStroke.reset( lineSymbol );
2279 if ( mStroke && mStroke->symbolLayer( 0 ) )
2281 double subLayerBleed = mStroke->symbolLayer( 0 )->estimateMaxBleed( context );
2282 return subLayerBleed;
2292 return QColor( Qt::black );
2294 return mStroke->color();
2301 attr.unite( mStroke->usedAttributes( context ) );
2309 if ( mStroke && mStroke->hasDataDefinedProperties() )
2316 QString path, mimeType;
2318 Qt::PenStyle penStyle;
2319 double size, strokeWidth;
2321 QDomElement fillElem = element.firstChildElement( QStringLiteral(
"Fill" ) );
2322 if ( fillElem.isNull() )
2325 QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral(
"GraphicFill" ) );
2326 if ( graphicFillElem.isNull() )
2329 QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral(
"Graphic" ) );
2330 if ( graphicElem.isNull() )
2336 if ( mimeType != QLatin1String(
"image/svg+xml" ) )
2341 double scaleFactor = 1.0;
2342 const QString uom = element.attribute( QStringLiteral(
"uom" ) );
2344 size = size * scaleFactor;
2345 strokeWidth = strokeWidth * scaleFactor;
2352 double d = angleFunc.toDouble( &ok );
2357 std::unique_ptr< QgsSVGFillSymbolLayer > sl = std::make_unique< QgsSVGFillSymbolLayer >( path, size,
angle );
2358 sl->setOutputUnit( sldUnitSize );
2361 sl->setSvgStrokeWidth( strokeWidth );
2364 QDomElement strokeElem = element.firstChildElement( QStringLiteral(
"Stroke" ) );
2365 if ( !strokeElem.isNull() )
2376 return sl.release();
2394 double width = mPatternWidth;
2400 QString svgFile = mSvgFilePath;
2419 double strokeWidth = mSvgStrokeWidth;
2428 mSvgStrokeWidthUnit, context, mPatternWidthMapUnitScale, mSvgStrokeWidthMapUnitScale, evaluatedParameters );
2432 void QgsSVGFillSymbolLayer::storeViewBox()
2434 if ( !mSvgData.isEmpty() )
2436 QSvgRenderer r( mSvgData );
2439 mSvgViewBox = r.viewBoxF();
2444 mSvgViewBox = QRectF();
2447 void QgsSVGFillSymbolLayer::setDefaultSvgParams()
2449 if ( mSvgFilePath.isEmpty() )
2454 bool hasFillParam, hasFillOpacityParam, hasStrokeParam, hasStrokeWidthParam, hasStrokeOpacityParam;
2455 bool hasDefaultFillColor, hasDefaultFillOpacity, hasDefaultStrokeColor, hasDefaultStrokeWidth, hasDefaultStrokeOpacity;
2456 QColor defaultFillColor, defaultStrokeColor;
2457 double defaultStrokeWidth, defaultFillOpacity, defaultStrokeOpacity;
2459 hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
2460 hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2461 hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
2462 hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity );
2464 double newFillOpacity = hasFillOpacityParam ?
mColor.alphaF() : 1.0;
2465 double newStrokeOpacity = hasStrokeOpacityParam ? mSvgStrokeColor.alphaF() : 1.0;
2467 if ( hasDefaultFillColor )
2469 mColor = defaultFillColor;
2470 mColor.setAlphaF( newFillOpacity );
2472 if ( hasDefaultFillOpacity )
2474 mColor.setAlphaF( defaultFillOpacity );
2476 if ( hasDefaultStrokeColor )
2478 mSvgStrokeColor = defaultStrokeColor;
2479 mSvgStrokeColor.setAlphaF( newStrokeOpacity );
2481 if ( hasDefaultStrokeOpacity )
2483 mSvgStrokeColor.setAlphaF( defaultStrokeOpacity );
2485 if ( hasDefaultStrokeWidth )
2487 mSvgStrokeWidth = defaultStrokeWidth;
2508 mFillLineSymbol->setWidth( w );
2514 mFillLineSymbol->setColor(
c );
2520 return mFillLineSymbol ? mFillLineSymbol->color() :
mColor;
2532 mFillLineSymbol.reset( qgis::down_cast<QgsLineSymbol *>( symbol ) );
2541 return mFillLineSymbol.get();
2547 if ( mFillLineSymbol )
2548 attr.unite( mFillLineSymbol->usedAttributes( context ) );
2556 if ( mFillLineSymbol && mFillLineSymbol->hasDataDefinedProperties() )
2579 mDistanceUnit = unit;
2580 mLineWidthUnit = unit;
2583 if ( mFillLineSymbol )
2584 mFillLineSymbol->setOutputUnit( unit );
2607 mDistanceMapUnitScale = scale;
2608 mLineWidthMapUnitScale = scale;
2609 mOffsetMapUnitScale = scale;
2615 mDistanceMapUnitScale == mLineWidthMapUnitScale &&
2616 mLineWidthMapUnitScale == mOffsetMapUnitScale )
2618 return mDistanceMapUnitScale;
2625 std::unique_ptr< QgsLinePatternFillSymbolLayer > patternLayer = std::make_unique< QgsLinePatternFillSymbolLayer >();
2631 QColor
color( Qt::black );
2634 if (
properties.contains( QStringLiteral(
"lineangle" ) ) )
2639 else if (
properties.contains( QStringLiteral(
"angle" ) ) )
2643 patternLayer->setLineAngle(
lineAngle );
2645 if (
properties.contains( QStringLiteral(
"distance" ) ) )
2649 patternLayer->setDistance(
distance );
2651 if (
properties.contains( QStringLiteral(
"linewidth" ) ) )
2656 else if (
properties.contains( QStringLiteral(
"outline_width" ) ) )
2660 else if (
properties.contains( QStringLiteral(
"line_width" ) ) )
2664 patternLayer->setLineWidth(
lineWidth );
2666 if (
properties.contains( QStringLiteral(
"color" ) ) )
2670 else if (
properties.contains( QStringLiteral(
"outline_color" ) ) )
2674 else if (
properties.contains( QStringLiteral(
"line_color" ) ) )
2678 patternLayer->setColor(
color );
2680 if (
properties.contains( QStringLiteral(
"offset" ) ) )
2684 patternLayer->setOffset(
offset );
2687 if (
properties.contains( QStringLiteral(
"distance_unit" ) ) )
2691 if (
properties.contains( QStringLiteral(
"distance_map_unit_scale" ) ) )
2695 if (
properties.contains( QStringLiteral(
"line_width_unit" ) ) )
2699 else if (
properties.contains( QStringLiteral(
"outline_width_unit" ) ) )
2703 if (
properties.contains( QStringLiteral(
"line_width_map_unit_scale" ) ) )
2707 if (
properties.contains( QStringLiteral(
"offset_unit" ) ) )
2711 if (
properties.contains( QStringLiteral(
"offset_map_unit_scale" ) ) )
2715 if (
properties.contains( QStringLiteral(
"outline_width_unit" ) ) )
2719 if (
properties.contains( QStringLiteral(
"outline_width_map_unit_scale" ) ) )
2723 if (
properties.contains( QStringLiteral(
"coordinate_reference" ) ) )
2727 if (
properties.contains( QStringLiteral(
"clip_mode" ) ) )
2732 patternLayer->restoreOldDataDefinedProperties(
properties );
2734 return patternLayer.release();
2739 return QStringLiteral(
"LinePatternFill" );
2742 void QgsLinePatternFillSymbolLayer::applyPattern(
const QgsSymbolRenderContext &context, QBrush &brush,
double lineAngle,
double distance )
2744 mBrush.setTextureImage( QImage() );
2746 if ( !mFillLineSymbol )
2751 std::unique_ptr< QgsLineSymbol > fillLineSymbol( mFillLineSymbol->clone() );
2752 if ( !fillLineSymbol )
2768 outputPixelOffset = std::fmod( outputPixelOffset, outputPixelDist );
2769 if ( outputPixelOffset > outputPixelDist / 2.0 )
2770 outputPixelOffset -= outputPixelDist;
2774 double outputPixelBleed = 0;
2775 double outputPixelInterval = 0;
2776 for (
int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2780 outputPixelBleed = std::max( outputPixelBleed, outputPixelLayerBleed );
2783 if ( markerLineLayer )
2792 outputPixelInterval = std::max( outputPixelInterval, outputPixelLayerInterval );
2796 if ( outputPixelInterval > 0 )
2800 double intervalScale = std::round( outputPixelInterval ) / outputPixelInterval;
2801 outputPixelInterval = std::round( outputPixelInterval );
2803 for (
int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2808 if ( markerLineLayer )
2822 height = outputPixelDist;
2823 width = outputPixelInterval > 0 ? outputPixelInterval : height;
2827 width = outputPixelDist;
2828 height = outputPixelInterval > 0 ? outputPixelInterval : width;
2832 height = outputPixelDist / std::cos(
lineAngle * M_PI / 180 );
2833 width = outputPixelDist / std::sin(
lineAngle * M_PI / 180 );
2836 lineAngle = 180 * std::atan2(
static_cast< double >( height ),
static_cast< double >( width ) ) / M_PI;
2842 height = std::abs( height );
2843 width = std::abs( width );
2845 outputPixelDist = std::abs( height * std::cos(
lineAngle * M_PI / 180 ) );
2849 int offsetHeight =
static_cast< int >( std::round( outputPixelOffset / std::cos(
lineAngle * M_PI / 180 ) ) );
2850 outputPixelOffset = offsetHeight * std::cos(
lineAngle * M_PI / 180 );
2859 int bufferMulti =
static_cast< int >( std::max( std::ceil( outputPixelBleed / width ), std::ceil( outputPixelBleed / width ) ) );
2863 bufferMulti = std::max( bufferMulti, 1 );
2865 int xBuffer = width * bufferMulti;
2866 int yBuffer = height * bufferMulti;
2867 int innerWidth = width;
2868 int innerHeight = height;
2869 width += 2 * xBuffer;
2870 height += 2 * yBuffer;
2873 if ( width > 10000 || height > 10000 || width == 0 || height == 0 )
2878 QImage patternImage( width, height, QImage::Format_ARGB32 );
2879 patternImage.fill( 0 );
2881 QPointF p1, p2, p3, p4, p5, p6;
2884 p1 = QPointF( 0, yBuffer );
2885 p2 = QPointF( width, yBuffer );
2886 p3 = QPointF( 0, yBuffer + innerHeight );
2887 p4 = QPointF( width, yBuffer + innerHeight );
2891 p1 = QPointF( xBuffer, height );
2892 p2 = QPointF( xBuffer, 0 );
2893 p3 = QPointF( xBuffer + innerWidth, height );
2894 p4 = QPointF( xBuffer + innerWidth, 0 );
2898 dx = outputPixelDist * std::cos( ( 90 -
lineAngle ) * M_PI / 180.0 );
2899 dy = outputPixelDist * std::sin( ( 90 -
lineAngle ) * M_PI / 180.0 );
2900 p1 = QPointF( 0, height );
2901 p2 = QPointF( width, 0 );
2902 p3 = QPointF( -dx, height - dy );
2903 p4 = QPointF( width - dx, -dy );
2904 p5 = QPointF( dx, height + dy );
2905 p6 = QPointF( width + dx, dy );
2909 dx = outputPixelDist * std::cos( ( 90 -
lineAngle ) * M_PI / 180.0 );
2910 dy = outputPixelDist * std::sin( ( 90 -
lineAngle ) * M_PI / 180.0 );
2911 p1 = QPointF( width, 0 );
2912 p2 = QPointF( 0, height );
2913 p3 = QPointF( width - dx, -dy );
2914 p4 = QPointF( -dx, height - dy );
2915 p5 = QPointF( width + dx, dy );
2916 p6 = QPointF( dx, height + dy );
2920 dy = outputPixelDist * std::cos( ( 180 -
lineAngle ) * M_PI / 180 );
2921 dx = outputPixelDist * std::sin( ( 180 -
lineAngle ) * M_PI / 180 );
2922 p1 = QPointF( 0, 0 );
2923 p2 = QPointF( width, height );
2924 p5 = QPointF( dx, -dy );
2925 p6 = QPointF( width + dx, height - dy );
2926 p3 = QPointF( -dx, dy );
2927 p4 = QPointF( width - dx, height + dy );
2931 dy = outputPixelDist * std::cos( ( 180 -
lineAngle ) * M_PI / 180 );
2932 dx = outputPixelDist * std::sin( ( 180 -
lineAngle ) * M_PI / 180 );
2933 p1 = QPointF( width, height );
2934 p2 = QPointF( 0, 0 );
2935 p5 = QPointF( width + dx, height - dy );
2936 p6 = QPointF( dx, -dy );
2937 p3 = QPointF( width - dx, height + dy );
2938 p4 = QPointF( -dx, dy );
2945 p3 = QPointF( tempPt.x(), tempPt.y() );
2947 p4 = QPointF( tempPt.x(), tempPt.y() );
2949 p5 = QPointF( tempPt.x(), tempPt.y() );
2951 p6 = QPointF( tempPt.x(), tempPt.y() );
2955 p1 = QPointF( tempPt.x(), tempPt.y() );
2957 p2 = QPointF( tempPt.x(), tempPt.y() );
2960 QPainter p( &patternImage );
2964 p.setRenderHint( QPainter::Antialiasing,
false );
2965 QPen pen( QColor( Qt::black ) );
2966 pen.setWidthF( 0.1 );
2967 pen.setCapStyle( Qt::FlatCap );
2972 QPolygon polygon = QPolygon() << QPoint( 0, 0 ) << QPoint( width - 1, 0 ) << QPoint( width - 1, height - 1 ) << QPoint( 0, height - 1 ) << QPoint( 0, 0 );
2973 p.drawPolygon( polygon );
2975 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 );
2976 p.drawPolygon( polygon );
2982 p.setRenderHint( QPainter::Antialiasing,
true );
2994 fillLineSymbol->startRender( lineRenderContext, context.
fields() );
2996 QVector<QPolygonF> polygons;
2997 polygons.append( QPolygonF() << p1 << p2 );
2998 polygons.append( QPolygonF() << p3 << p4 );
3001 polygons.append( QPolygonF() << p5 << p6 );
3004 for (
const QPolygonF &polygon : std::as_const( polygons ) )
3006 fillLineSymbol->renderPolyline( polygon, context.
feature(), lineRenderContext, -1, context.
selected() );
3009 fillLineSymbol->stopRender( lineRenderContext );
3013 patternImage = patternImage.copy( xBuffer, yBuffer, patternImage.width() - 2 * xBuffer, patternImage.height() - 2 * yBuffer );
3018 QImage transparentImage = patternImage.copy();
3020 brush.setTextureImage( transparentImage );
3024 brush.setTextureImage( patternImage );
3027 QTransform brushTransform;
3028 brush.setTransform( brushTransform );
3036 || mFillLineSymbol->hasDataDefinedProperties()
3040 if ( mRenderUsingLines )
3042 if ( mFillLineSymbol )
3048 applyPattern( context,
mBrush, mLineAngle, mDistance );
3054 if ( mRenderUsingLines && mFillLineSymbol )
3062 if ( !mRenderUsingLines )
3096 outputPixelOffset = std::fmod( outputPixelOffset, outputPixelDistance );
3097 if ( outputPixelOffset > outputPixelDistance / 2.0 )
3098 outputPixelOffset -= outputPixelDistance;
3100 p->setPen( QPen( Qt::NoPen ) );
3105 p->setBrush( QBrush( selColor ) );
3129 std::unique_ptr< QgsPolygon > shapePolygon;
3130 std::unique_ptr< QgsGeometryEngine > shapeEngine;
3138 shapePolygon = std::make_unique< QgsPolygon >();
3142 for (
const QPolygonF &ring : *rings )
3148 shapeEngine->prepareGeometry();
3155 path.addPolygon( points );
3158 for (
const QPolygonF &ring : *rings )
3160 path.addPolygon( ring );
3163 p->setClipPath( path, Qt::IntersectClip );
3169 const QRectF boundingRect = points.boundingRect();
3171 QTransform invertedRotateTransform;
3177 QTransform transform;
3178 if ( applyBrushTransform )
3181 transform.translate( -boundingRect.center().x(),
3182 -boundingRect.center().y() );
3184 transform.translate( boundingRect.center().x(),
3185 boundingRect.center().y() );
3193 const QRectF transformedBounds = transform.map( points ).boundingRect();
3197 left = transformedBounds.left() - buffer * 2;
3198 top = transformedBounds.top() - buffer * 2;
3199 right = transformedBounds.right() + buffer * 2;
3200 bottom = transformedBounds.bottom() + buffer * 2;
3201 invertedRotateTransform = transform.inverted();
3203 if ( !applyBrushTransform )
3205 top -= transformedBounds.top() - ( outputPixelDistance * std::floor( transformedBounds.top() / outputPixelDistance ) );
3210 const bool needsExpressionContext = mFillLineSymbol->hasDataDefinedProperties();
3215 int currentLine = 0;
3216 for (
double currentY = top; currentY <= bottom; currentY += outputPixelDistance )
3221 if ( needsExpressionContext )
3225 double y1 = currentY;
3227 double y2 = currentY;
3228 invertedRotateTransform.map( left, currentY - outputPixelOffset, &x1, &y1 );
3229 invertedRotateTransform.map( right, currentY - outputPixelOffset, &x2, &y2 );
3234 std::unique_ptr< QgsAbstractGeometry > intersection( shapeEngine->intersection( &ls ) );
3235 for (
auto it = intersection->const_parts_begin(); it != intersection->const_parts_end(); ++it )
3237 if (
const QgsLineString *ls = qgsgeometry_cast< const QgsLineString * >( *it ) )
3245 mFillLineSymbol->renderPolyline( QPolygonF() << QPointF( x1, y1 ) << QPointF( x2, y2 ), context.
feature(), context.
renderContext() );
3257 map.insert( QStringLiteral(
"angle" ), QString::number( mLineAngle ) );
3258 map.insert( QStringLiteral(
"distance" ), QString::number( mDistance ) );
3259 map.insert( QStringLiteral(
"line_width" ), QString::number( mLineWidth ) );
3261 map.insert( QStringLiteral(
"offset" ), QString::number( mOffset ) );
3277 if ( mFillLineSymbol )
3288 QDomElement symbolizerElem = doc.createElement( QStringLiteral(
"se:PolygonSymbolizer" ) );
3289 if ( !props.value( QStringLiteral(
"uom" ), QString() ).toString().isEmpty() )
3290 symbolizerElem.setAttribute( QStringLiteral(
"uom" ), props.value( QStringLiteral(
"uom" ), QString() ).toString() );
3291 element.appendChild( symbolizerElem );
3296 QDomElement fillElem = doc.createElement( QStringLiteral(
"se:Fill" ) );
3297 symbolizerElem.appendChild( fillElem );
3299 QDomElement graphicFillElem = doc.createElement( QStringLiteral(
"se:GraphicFill" ) );
3300 fillElem.appendChild( graphicFillElem );
3302 QDomElement graphicElem = doc.createElement( QStringLiteral(
"se:Graphic" ) );
3303 graphicFillElem.appendChild( graphicElem );
3306 QColor lineColor = mFillLineSymbol ? mFillLineSymbol->color() : QColor();
3307 double lineWidth = mFillLineSymbol ? mFillLineSymbol->width() : 0.0;
3315 double angle = props.value( QStringLiteral(
"angle" ), QStringLiteral(
"0" ) ).toDouble( &ok );
3318 angleFunc = QStringLiteral(
"%1 + %2" ).arg( props.value( QStringLiteral(
"angle" ), QStringLiteral(
"0" ) ).toString() ).arg( mLineAngle );
3322 angleFunc = QString::number(
angle + mLineAngle );
3327 QPointF lineOffset( std::sin( mLineAngle ) * mOffset, std::cos( mLineAngle ) * mOffset );
3334 QString featureStyle;
3335 featureStyle.append(
"Brush(" );
3336 featureStyle.append( QStringLiteral(
"fc:%1" ).arg(
mColor.name() ) );
3337 featureStyle.append( QStringLiteral(
",bc:%1" ).arg( QLatin1String(
"#00000000" ) ) );
3338 featureStyle.append(
",id:\"ogr-brush-2\"" );
3339 featureStyle.append( QStringLiteral(
",a:%1" ).arg( mLineAngle ) );
3340 featureStyle.append( QStringLiteral(
",s:%1" ).arg( mLineWidth * widthScaleFactor ) );
3341 featureStyle.append(
",dx:0mm" );
3342 featureStyle.append( QStringLiteral(
",dy:%1mm" ).arg( mDistance * widthScaleFactor ) );
3343 featureStyle.append(
')' );
3344 return featureStyle;
3350 && ( !mFillLineSymbol || !mFillLineSymbol->hasDataDefinedProperties() ) )
3375 Qt::PenStyle lineStyle;
3377 QDomElement fillElem = element.firstChildElement( QStringLiteral(
"Fill" ) );
3378 if ( fillElem.isNull() )
3381 QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral(
"GraphicFill" ) );
3382 if ( graphicFillElem.isNull() )
3385 QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral(
"Graphic" ) );
3386 if ( graphicElem.isNull() )
3392 if ( name != QLatin1String(
"horline" ) )
3400 double d = angleFunc.toDouble( &ok );
3409 offset = std::sqrt( std::pow( vectOffset.x(), 2 ) + std::pow( vectOffset.y(), 2 ) );
3412 double scaleFactor = 1.0;
3413 const QString uom = element.attribute( QStringLiteral(
"uom" ) );
3415 size = size * scaleFactor;
3418 std::unique_ptr< QgsLinePatternFillSymbolLayer > sl = std::make_unique< QgsLinePatternFillSymbolLayer >();
3419 sl->setOutputUnit( sldUnitSize );
3420 sl->setColor( lineColor );
3422 sl->setLineAngle(
angle );
3424 sl->setDistance( size );
3427 QDomElement strokeElem = element.firstChildElement( QStringLiteral(
"Stroke" ) );
3428 if ( !strokeElem.isNull() )
3439 return sl.release();
3539 std::unique_ptr< QgsPointPatternFillSymbolLayer > layer = std::make_unique< QgsPointPatternFillSymbolLayer >();
3540 if (
properties.contains( QStringLiteral(
"distance_x" ) ) )
3542 layer->setDistanceX(
properties[QStringLiteral(
"distance_x" )].toDouble() );
3544 if (
properties.contains( QStringLiteral(
"distance_y" ) ) )
3546 layer->setDistanceY(
properties[QStringLiteral(
"distance_y" )].toDouble() );
3548 if (
properties.contains( QStringLiteral(
"displacement_x" ) ) )
3550 layer->setDisplacementX(
properties[QStringLiteral(
"displacement_x" )].toDouble() );
3552 if (
properties.contains( QStringLiteral(
"displacement_y" ) ) )
3554 layer->setDisplacementY(
properties[QStringLiteral(
"displacement_y" )].toDouble() );
3556 if (
properties.contains( QStringLiteral(
"offset_x" ) ) )
3558 layer->setOffsetX(
properties[QStringLiteral(
"offset_x" )].toDouble() );
3560 if (
properties.contains( QStringLiteral(
"offset_y" ) ) )
3562 layer->setOffsetY(
properties[QStringLiteral(
"offset_y" )].toDouble() );
3565 if (
properties.contains( QStringLiteral(
"distance_x_unit" ) ) )
3569 if (
properties.contains( QStringLiteral(
"distance_x_map_unit_scale" ) ) )
3573 if (
properties.contains( QStringLiteral(
"distance_y_unit" ) ) )
3577 if (
properties.contains( QStringLiteral(
"distance_y_map_unit_scale" ) ) )
3581 if (
properties.contains( QStringLiteral(
"displacement_x_unit" ) ) )
3585 if (
properties.contains( QStringLiteral(
"displacement_x_map_unit_scale" ) ) )
3589 if (
properties.contains( QStringLiteral(
"displacement_y_unit" ) ) )
3593 if (
properties.contains( QStringLiteral(
"displacement_y_map_unit_scale" ) ) )
3597 if (
properties.contains( QStringLiteral(
"offset_x_unit" ) ) )
3601 if (
properties.contains( QStringLiteral(
"offset_x_map_unit_scale" ) ) )
3605 if (
properties.contains( QStringLiteral(
"offset_y_unit" ) ) )
3609 if (
properties.contains( QStringLiteral(
"offset_y_map_unit_scale" ) ) )
3614 if (
properties.contains( QStringLiteral(
"random_deviation_x" ) ) )
3616 layer->setMaximumRandomDeviationX(
properties[QStringLiteral(
"random_deviation_x" )].toDouble() );
3618 if (
properties.contains( QStringLiteral(
"random_deviation_y" ) ) )
3620 layer->setMaximumRandomDeviationY(
properties[QStringLiteral(
"random_deviation_y" )].toDouble() );
3622 if (
properties.contains( QStringLiteral(
"random_deviation_x_unit" ) ) )
3626 if (
properties.contains( QStringLiteral(
"random_deviation_x_map_unit_scale" ) ) )
3630 if (
properties.contains( QStringLiteral(
"random_deviation_y_unit" ) ) )
3634 if (
properties.contains( QStringLiteral(
"random_deviation_y_map_unit_scale" ) ) )
3638 unsigned long seed = 0;
3639 if (
properties.contains( QStringLiteral(
"seed" ) ) )
3645 std::random_device rd;
3646 std::mt19937 mt(
seed == 0 ? rd() :
seed );
3647 std::uniform_int_distribution<> uniformDist( 1, 999999999 );
3648 seed = uniformDist( mt );
3650 layer->setSeed(
seed );
3652 if (
properties.contains( QStringLiteral(
"outline_width_unit" ) ) )
3656 if (
properties.contains( QStringLiteral(
"outline_width_map_unit_scale" ) ) )
3660 if (
properties.contains( QStringLiteral(
"clip_mode" ) ) )
3664 if (
properties.contains( QStringLiteral(
"coordinate_reference" ) ) )
3669 if (
properties.contains( QStringLiteral(
"angle" ) ) )
3671 layer->setAngle(
properties[QStringLiteral(
"angle" )].toDouble() );
3674 layer->restoreOldDataDefinedProperties(
properties );
3676 return layer.release();
3681 return QStringLiteral(
"PointPatternFill" );
3684 void QgsPointPatternFillSymbolLayer::applyPattern(
const QgsSymbolRenderContext &context, QBrush &brush,
double distanceX,
double distanceY,
3685 double displacementX,
double displacementY,
double offsetX,
double offsetY )
3692 double widthOffset = std::fmod(
3695 double heightOffset = std::fmod(
3699 if ( width > 10000 || height > 10000 )
3702 brush.setTextureImage( img );
3706 QImage patternImage( width, height, QImage::Format_ARGB32 );
3707 patternImage.fill( 0 );
3708 if ( patternImage.isNull() )
3710 brush.setTextureImage( QImage() );
3715 QPainter p( &patternImage );
3733 for (
double currentX = -width; currentX <= width * 2.0; currentX += width )
3735 for (
double currentY = -height; currentY <= height * 2.0; currentY += height )
3737 mMarkerSymbol->renderPoint( QPointF( currentX + widthOffset, currentY + heightOffset ), context.
feature(), pointRenderContext );
3748 for (
double currentX = -width; currentX <= width * 2.0; currentX += width )
3750 for (
double currentY = -height / 2.0; currentY <= height * 2.0; currentY += height )
3752 mMarkerSymbol->renderPoint( QPointF( currentX + widthOffset + displacementPixelX, currentY + heightOffset ), context.
feature(), pointRenderContext );
3756 for (
double currentX = -width / 2.0; currentX <= width * 2.0; currentX += width )
3758 for (
double currentY = -height; currentY <= height * 2.0; currentY += height / 2.0 )
3760 mMarkerSymbol->renderPoint( QPointF( currentX + widthOffset + ( std::fmod( currentY, height ) != 0 ? displacementPixelX : 0 ), currentY + heightOffset - displacementPixelY ), context.
feature(), pointRenderContext );
3769 QImage transparentImage = patternImage.copy();
3771 brush.setTextureImage( transparentImage );
3775 brush.setTextureImage( patternImage );
3777 QTransform brushTransform;
3778 brush.setTransform( brushTransform );
3796 if ( mRenderUsingMarkers )
3809 if ( mRenderUsingMarkers )
3831 if ( !mRenderUsingMarkers )
3874 const double widthOffset = std::fmod(
3886 const double heightOffset = std::fmod(
3912 p->setPen( QPen( Qt::NoPen ) );
3917 p->setBrush( QBrush( selColor ) );
3941 std::unique_ptr< QgsPolygon > shapePolygon;
3942 std::unique_ptr< QgsGeometryEngine > shapeEngine;
3949 shapePolygon = std::make_unique< QgsPolygon >();
3953 for (
const QPolygonF &ring : *rings )
3959 shapeEngine->prepareGeometry();
3966 path.addPolygon( points );
3969 for (
const QPolygonF &ring : *rings )
3971 path.addPolygon( ring );
3974 p->setClipPath( path, Qt::IntersectClip );
3980 const QRectF boundingRect = points.boundingRect();
3982 QTransform invertedRotateTransform;
3990 QTransform transform;
3991 if ( applyBrushTransform )
3994 transform.translate( -boundingRect.center().x(),
3995 -boundingRect.center().y() );
3996 transform.rotate( -
angle );
3997 transform.translate( boundingRect.center().x(),
3998 boundingRect.center().y() );
4003 transform.rotate( -
angle );
4006 const QRectF transformedBounds = transform.map( points ).boundingRect();
4007 left = transformedBounds.left() - 2 * width;
4008 top = transformedBounds.top() - 2 * height;
4009 right = transformedBounds.right() + 2 * width;
4010 bottom = transformedBounds.bottom() + 2 * height;
4011 invertedRotateTransform = transform.inverted();
4013 if ( !applyBrushTransform )
4015 left -= transformedBounds.left() - ( width * std::floor( transformedBounds.left() / width ) );
4016 top -= transformedBounds.top() - ( height * std::floor( transformedBounds.top() / height ) );
4021 left = boundingRect.left() - 2 * width;
4022 top = boundingRect.top() - 2 * height;
4023 right = boundingRect.right() + 2 * width;
4024 bottom = boundingRect.bottom() + 2 * height;
4026 if ( !applyBrushTransform )
4028 left -= boundingRect.left() - ( width * std::floor( boundingRect.left() / width ) );
4029 top -= boundingRect.top() - ( height * std::floor( boundingRect.top() / height ) );
4058 std::random_device rd;
4059 std::mt19937 mt(
seed == 0 ? rd() :
seed );
4060 std::uniform_real_distribution<> uniformDist( 0, 1 );
4066 const bool needsExpressionContext =
mMarkerSymbol->hasDataDefinedProperties();
4071 bool alternateColumn =
false;
4072 int currentCol = -3;
4073 for (
double currentX = left; currentX <= right; currentX += width, alternateColumn = !alternateColumn )
4078 if ( needsExpressionContext )
4081 bool alternateRow =
false;
4082 const double columnX = currentX + widthOffset;
4083 int currentRow = -3;
4084 for (
double currentY = top; currentY <= bottom; currentY += height, alternateRow = !alternateRow )
4089 double y = currentY + heightOffset;
4092 x += displacementPixelX;
4094 if ( !alternateColumn )
4095 y -= displacementPixelY;
4101 invertedRotateTransform.map( xx, yy, &x, &y );
4104 if ( useRandomShift )
4106 x += ( 2 * uniformDist( mt ) - 1 ) * maxRandomDeviationPixelX;
4107 y += ( 2 * uniformDist( mt ) - 1 ) * maxRandomDeviationPixelY;
4110 if ( needsExpressionContext )
4118 bool renderPoint =
true;
4126 renderPoint = shapeEngine->intersects( &p );
4136 renderPoint = shapeEngine->contains( markerBounds.
constGet() );
4138 renderPoint = shapeEngine->intersects( markerBounds.
constGet() );
4162 map.insert( QStringLiteral(
"distance_x" ), QString::number(
mDistanceX ) );
4163 map.insert( QStringLiteral(
"distance_y" ), QString::number(
mDistanceY ) );
4164 map.insert( QStringLiteral(
"displacement_x" ), QString::number(
mDisplacementX ) );
4165 map.insert( QStringLiteral(
"displacement_y" ), QString::number(
mDisplacementY ) );
4166 map.insert( QStringLiteral(
"offset_x" ), QString::number(
mOffsetX ) );
4167 map.insert( QStringLiteral(
"offset_y" ), QString::number(
mOffsetY ) );
4183 map.insert( QStringLiteral(
"random_deviation_x" ), QString::number(
mRandomDeviationX ) );
4184 map.insert( QStringLiteral(
"random_deviation_y" ), QString::number(
mRandomDeviationY ) );
4189 map.insert( QStringLiteral(
"seed" ), QString::number(
mSeed ) );
4190 map.insert( QStringLiteral(
"angle" ),
mAngle );
4209 for (
int i = 0; i <
mMarkerSymbol->symbolLayerCount(); i++ )
4211 QDomElement symbolizerElem = doc.createElement( QStringLiteral(
"se:PolygonSymbolizer" ) );
4212 if ( !props.value( QStringLiteral(
"uom" ), QString() ).toString().isEmpty() )
4213 symbolizerElem.setAttribute( QStringLiteral(
"uom" ), props.value( QStringLiteral(
"uom" ), QString() ).toString() );
4214 element.appendChild( symbolizerElem );
4219 QDomElement fillElem = doc.createElement( QStringLiteral(
"se:Fill" ) );
4220 symbolizerElem.appendChild( fillElem );
4222 QDomElement graphicFillElem = doc.createElement( QStringLiteral(
"se:GraphicFill" ) );
4223 fillElem.appendChild( graphicFillElem );
4230 symbolizerElem.appendChild( distanceElem );
4235 markerLayer->writeSldMarker( doc, graphicFillElem, props );
4239 QString errorMsg = QStringLiteral(
"QgsMarkerSymbolLayer expected, %1 found. Skip it." ).arg( layer->
layerType() );
4240 graphicFillElem.appendChild( doc.createComment( errorMsg ) );
4244 QString errorMsg = QStringLiteral(
"Missing point pattern symbol layer. Skip it." );
4245 graphicFillElem.appendChild( doc.createComment( errorMsg ) );
4335 attributes.unite(
mMarkerSymbol->usedAttributes( context ) );
4373 std::unique_ptr< QgsCentroidFillSymbolLayer > sl = std::make_unique< QgsCentroidFillSymbolLayer >();
4375 if (
properties.contains( QStringLiteral(
"point_on_surface" ) ) )
4376 sl->setPointOnSurface(
properties[QStringLiteral(
"point_on_surface" )].toInt() != 0 );
4377 if (
properties.contains( QStringLiteral(
"point_on_all_parts" ) ) )
4378 sl->setPointOnAllParts(
properties[QStringLiteral(
"point_on_all_parts" )].toInt() != 0 );
4379 if (
properties.contains( QStringLiteral(
"clip_points" ) ) )
4380 sl->setClipPoints(
properties[QStringLiteral(
"clip_points" )].toInt() != 0 );
4381 if (
properties.contains( QStringLiteral(
"clip_on_current_part_only" ) ) )
4382 sl->setClipOnCurrentPartOnly(
properties[QStringLiteral(
"clip_on_current_part_only" )].toInt() != 0 );
4384 sl->restoreOldDataDefinedProperties(
properties );
4386 return sl.release();
4391 return QStringLiteral(
"CentroidFill" );
4418 part.exterior = points;
4420 part.rings = *rings;
4427 mCurrentParts << part;
4432 const double prevOpacity =
mMarker->opacity();
4435 mMarker->setOpacity( prevOpacity );
4442 mCurrentParts.clear();
4449 const double prevOpacity =
mMarker->opacity();
4452 render( context, mCurrentParts, feature,
false );
4454 mMarker->setOpacity( prevOpacity );
4457 void QgsCentroidFillSymbolLayer::render(
QgsRenderContext &context,
const QVector<QgsCentroidFillSymbolLayer::Part> &parts,
const QgsFeature &feature,
bool selected )
4466 QVector< QgsGeometry > geometryParts;
4467 geometryParts.reserve( parts.size() );
4468 QPainterPath globalPath;
4471 int maxAreaPartIdx = 0;
4473 for (
int i = 0; i < parts.size(); i++ )
4475 const Part part = parts[i];
4478 if ( !geom.
isNull() && !part.rings.empty() )
4480 QgsPolygon *poly = qgsgeometry_cast< QgsPolygon * >( geom.
get() );
4484 int area = poly->
area();
4486 if ( area > maxArea )
4496 globalPath.addPolygon( part.exterior );
4497 for (
const QPolygonF &ring : part.rings )
4499 globalPath.addPolygon( ring );
4504 for (
int i = 0; i < parts.size(); i++ )
4509 const Part part = parts[i];
4517 path.addPolygon( part.exterior );
4518 for (
const QPolygonF &ring : part.rings )
4520 path.addPolygon( ring );
4529 context.
painter()->setClipPath( path );
4549 map[QStringLiteral(
"point_on_surface" )] = QString::number(
mPointOnSurface );
4550 map[QStringLiteral(
"point_on_all_parts" )] = QString::number(
mPointOnAllParts );
4551 map[QStringLiteral(
"clip_points" )] = QString::number(
mClipPoints );
4558 std::unique_ptr< QgsCentroidFillSymbolLayer > x = std::make_unique< QgsCentroidFillSymbolLayer >();
4561 x->setSubSymbol(
mMarker->clone() );
4576 mMarker->toSld( doc, element, props );
4587 std::unique_ptr< QgsMarkerSymbol > marker(
new QgsMarkerSymbol( layers ) );
4589 std::unique_ptr< QgsCentroidFillSymbolLayer > sl = std::make_unique< QgsCentroidFillSymbolLayer >();
4590 sl->setSubSymbol( marker.release() );
4591 sl->setPointOnAllParts(
false );
4592 return sl.release();
4619 attributes.unite(
mMarker->usedAttributes( context ) );
4642 mMarker->setOutputUnit( unit );
4659 return mMarker->usesMapUnits();
4668 mMarker->setMapUnitScale( scale );
4676 return mMarker->mapUnitScale();
4686 , mImageFilePath( imageFilePath )
4703 if (
properties.contains( QStringLiteral(
"imageFile" ) ) )
4705 imagePath =
properties[QStringLiteral(
"imageFile" )].toString();
4707 if (
properties.contains( QStringLiteral(
"coordinate_mode" ) ) )
4711 if (
properties.contains( QStringLiteral(
"alpha" ) ) )
4713 alpha =
properties[QStringLiteral(
"alpha" )].toDouble();
4715 if (
properties.contains( QStringLiteral(
"offset" ) ) )
4719 if (
properties.contains( QStringLiteral(
"angle" ) ) )
4723 if (
properties.contains( QStringLiteral(
"width" ) ) )
4727 std::unique_ptr< QgsRasterFillSymbolLayer > symbolLayer = std::make_unique< QgsRasterFillSymbolLayer >( imagePath );
4728 symbolLayer->setCoordinateMode( mode );
4729 symbolLayer->setOpacity( alpha );
4730 symbolLayer->setOffset(
offset );
4731 symbolLayer->setAngle(
angle );
4732 symbolLayer->setWidth(
width );
4733 if (
properties.contains( QStringLiteral(
"offset_unit" ) ) )
4737 if (
properties.contains( QStringLiteral(
"offset_map_unit_scale" ) ) )
4741 if (
properties.contains( QStringLiteral(
"width_unit" ) ) )
4745 if (
properties.contains( QStringLiteral(
"width_map_unit_scale" ) ) )
4750 symbolLayer->restoreOldDataDefinedProperties(
properties );
4752 return symbolLayer.release();
4757 QVariantMap::iterator it =
properties.find( QStringLiteral(
"imageFile" ) );
4761 it.value() = pathResolver.
writePath( it.value().toString() );
4763 it.value() = pathResolver.
readPath( it.value().toString() );
4775 return QStringLiteral(
"RasterFill" );
4786 QPointF
offset = mOffset;
4804 QRectF boundingRect = points.boundingRect();
4805 mBrush.setTransform(
mBrush.transform().translate( boundingRect.left() -
mBrush.transform().dx(),
4806 boundingRect.top() -
mBrush.transform().dy() ) );
4818 applyPattern(
mBrush, mImageFilePath, mWidth, mOpacity * context.
opacity(), context );
4829 map[QStringLiteral(
"imageFile" )] = mImageFilePath;
4830 map[QStringLiteral(
"coordinate_mode" )] = QString::number(
static_cast< int >( mCoordinateMode ) );
4831 map[QStringLiteral(
"alpha" )] = QString::number( mOpacity );
4835 map[QStringLiteral(
"angle" )] = QString::number(
mAngle );
4836 map[QStringLiteral(
"width" )] = QString::number( mWidth );
4844 std::unique_ptr< QgsRasterFillSymbolLayer > sl = std::make_unique< QgsRasterFillSymbolLayer >( mImageFilePath );
4845 sl->setCoordinateMode( mCoordinateMode );
4846 sl->setOpacity( mOpacity );
4847 sl->setOffset( mOffset );
4848 sl->setOffsetUnit( mOffsetUnit );
4849 sl->setOffsetMapUnitScale( mOffsetMapUnitScale );
4851 sl->setWidth( mWidth );
4852 sl->setWidthUnit( mWidthUnit );
4853 sl->setWidthMapUnitScale( mWidthMapUnitScale );
4856 return sl.release();
4861 return context.
convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale );
4884 mImageFilePath = imagePath;
4889 mCoordinateMode = mode;
4907 if ( !hasWidthExpression && !hasAngleExpression && !hasOpacityExpression && !hasFileExpression )
4913 if ( hasAngleExpression )
4921 if ( !hasWidthExpression && !hasOpacityExpression && !hasFileExpression )
4926 double width = mWidth;
4927 if ( hasWidthExpression )
4933 if ( hasOpacityExpression )
4938 QString file = mImageFilePath;
4939 if ( hasFileExpression )
4952 void QgsRasterFillSymbolLayer::applyPattern( QBrush &brush,
const QString &imageFilePath,
const double width,
const double alpha,
const QgsSymbolRenderContext &context )
4965 if ( size.isEmpty() )
4968 size.setWidth( (
width * size.width() ) / 100.0 );
4971 if (
static_cast< int >( size.width() ) < 1 || 10000.0 < size.width() )
4975 size.setHeight( 0 );
4983 brush.setTextureImage( img );
4992 : mCountMethod( method )
4993 , mPointCount( pointCount )
4994 , mDensityArea( densityArea )
5005 const int pointCount =
properties.value( QStringLiteral(
"point_count" ), QStringLiteral(
"10" ) ).toInt();
5006 const double densityArea =
properties.value( QStringLiteral(
"density_area" ), QStringLiteral(
"250.0" ) ).toDouble();
5008 unsigned long seed = 0;
5009 if (
properties.contains( QStringLiteral(
"seed" ) ) )
5015 std::random_device rd;
5016 std::mt19937 mt(
seed == 0 ? rd() :
seed );
5017 std::uniform_int_distribution<> uniformDist( 1, 999999999 );
5018 seed = uniformDist( mt );
5023 if (
properties.contains( QStringLiteral(
"density_area_unit" ) ) )
5025 if (
properties.contains( QStringLiteral(
"density_area_unit_scale" ) ) )
5028 if (
properties.contains( QStringLiteral(
"clip_points" ) ) )
5030 sl->setClipPoints(
properties[QStringLiteral(
"clip_points" )].toInt() );
5033 return sl.release();
5038 return QStringLiteral(
"RandomMarkerFill" );
5043 mMarker->setColor(
color );
5049 return mMarker ? mMarker->color() :
mColor;
5065 part.exterior = points;
5067 part.rings = *rings;
5069 if ( mRenderingFeature )
5073 mFeatureSymbolOpacity = context.
opacity();
5074 mCurrentParts << part;
5079 const double prevOpacity = mMarker->opacity();
5080 mMarker->setOpacity( mMarker->opacity() * context.
opacity() );
5082 mMarker->setOpacity( prevOpacity );
5086 void QgsRandomMarkerFillSymbolLayer::render(
QgsRenderContext &context,
const QVector<QgsRandomMarkerFillSymbolLayer::Part> &parts,
const QgsFeature &feature,
bool selected )
5095 QVector< QgsGeometry > geometryParts;
5096 geometryParts.reserve( parts.size() );
5099 for (
const Part &part : parts )
5102 if ( !geom.
isNull() && !part.rings.empty() )
5104 QgsPolygon *poly = qgsgeometry_cast< QgsPolygon * >( geom.
get() );
5105 for (
const QPolygonF &ring : part.rings )
5112 geom = geom.
buffer( 0, 0 );
5114 geometryParts << geom;
5118 path.addPolygon( part.exterior );
5119 for (
const QPolygonF &ring : part.rings )
5121 path.addPolygon( ring );
5131 context.
painter()->setClipPath( path );
5135 int count = mPointCount;
5142 switch ( mCountMethod )
5144 case Qgis::PointCountMethod::DensityBased:
5154 count = std::max( 0.0, std::ceil( count * ( geom.
area() /
densityArea ) ) );
5157 case Qgis::PointCountMethod::Absolute:
5161 unsigned long seed = mSeed;
5172 std::sort( randomPoints.begin(), randomPoints.end(), [](
const QgsPointXY & a,
const QgsPointXY & b )->bool
5174 return a.y() < b.y();
5180 const bool needsExpressionContext = mMarker->hasDataDefinedProperties();
5185 for (
const QgsPointXY &p : std::as_const( randomPoints ) )
5187 if ( needsExpressionContext )
5189 mMarker->renderPoint( QPointF( p.x(), p.y() ), feature.
isValid() ? &feature :
nullptr, context, -1, selected );
5203 map.insert( QStringLiteral(
"count_method" ), QString::number(
static_cast< int >( mCountMethod ) ) );
5204 map.insert( QStringLiteral(
"point_count" ), QString::number( mPointCount ) );
5205 map.insert( QStringLiteral(
"density_area" ), QString::number( mDensityArea ) );
5208 map.insert( QStringLiteral(
"seed" ), QString::number( mSeed ) );
5209 map.insert( QStringLiteral(
"clip_points" ), QString::number( mClipPoints ) );
5215 std::unique_ptr< QgsRandomMarkerFillSymbolLayer > res = std::make_unique< QgsRandomMarkerFillSymbolLayer >( mPointCount, mCountMethod, mDensityArea, mSeed );
5218 res->setDensityAreaUnit( mDensityAreaUnit );
5219 res->setDensityAreaUnitScale( mDensityAreaUnitScale );
5220 res->mClipPoints = mClipPoints;
5221 res->setSubSymbol( mMarker->clone() );
5224 return res.release();
5234 return mMarker.get();
5246 mColor = mMarker->color();
5255 attributes.unite( mMarker->usedAttributes( context ) );
5264 if ( mMarker && mMarker->hasDataDefinedProperties() )
5301 return mCountMethod;
5306 mCountMethod = method;
5311 return mDensityArea;
5316 mDensityArea = area;
5321 mRenderingFeature =
true;
5322 mCurrentParts.clear();
5327 mRenderingFeature =
false;
5329 const double prevOpacity = mMarker->opacity();
5330 mMarker->setOpacity( mMarker->opacity() * mFeatureSymbolOpacity );
5332 render( context, mCurrentParts, feature,
false );
5334 mFeatureSymbolOpacity = 1;
5335 mMarker->setOpacity( prevOpacity );
5341 mDensityAreaUnit = unit;
5344 mMarker->setOutputUnit( unit );
5352 return mMarker->outputUnit();
5361 return mMarker->usesMapUnits();
5370 mMarker->setMapUnitScale( scale );
5378 return mMarker->mapUnitScale();