40 #include <QSvgRenderer>
41 #include <QDomDocument>
42 #include <QDomElement>
50 Qt::PenJoinStyle penJoinStyle )
51 : mBrushStyle( style )
52 , mStrokeColor( strokeColor )
53 , mStrokeStyle( strokeStyle )
54 , mStrokeWidth( strokeWidth )
55 , mPenJoinStyle( penJoinStyle )
91 void QgsSimpleFillSymbolLayer::applyDataDefinedSymbology(
QgsSymbolRenderContext &context, QBrush &brush, QPen &pen, QPen &selPen )
107 if ( exprVal.isValid() )
119 double width = exprVal.toDouble( &ok );
123 pen.setWidthF( width );
124 selPen.setWidthF( width );
160 if ( props.contains( QStringLiteral(
"color" ) ) )
162 if ( props.contains( QStringLiteral(
"style" ) ) )
164 if ( props.contains( QStringLiteral(
"color_border" ) ) )
169 else if ( props.contains( QStringLiteral(
"outline_color" ) ) )
173 else if ( props.contains( QStringLiteral(
"line_color" ) ) )
178 if ( props.contains( QStringLiteral(
"style_border" ) ) )
183 else if ( props.contains( QStringLiteral(
"outline_style" ) ) )
187 else if ( props.contains( QStringLiteral(
"line_style" ) ) )
191 if ( props.contains( QStringLiteral(
"width_border" ) ) )
194 strokeWidth = props[QStringLiteral(
"width_border" )].toDouble();
196 else if ( props.contains( QStringLiteral(
"outline_width" ) ) )
198 strokeWidth = props[QStringLiteral(
"outline_width" )].toDouble();
200 else if ( props.contains( QStringLiteral(
"line_width" ) ) )
202 strokeWidth = props[QStringLiteral(
"line_width" )].toDouble();
204 if ( props.contains( QStringLiteral(
"offset" ) ) )
206 if ( props.contains( QStringLiteral(
"joinstyle" ) ) )
211 if ( props.contains( QStringLiteral(
"border_width_unit" ) ) )
215 else if ( props.contains( QStringLiteral(
"outline_width_unit" ) ) )
219 else if ( props.contains( QStringLiteral(
"line_width_unit" ) ) )
223 if ( props.contains( QStringLiteral(
"offset_unit" ) ) )
226 if ( props.contains( QStringLiteral(
"border_width_map_unit_scale" ) ) )
228 if ( props.contains( QStringLiteral(
"offset_map_unit_scale" ) ) )
231 sl->restoreOldDataDefinedProperties( props );
239 return QStringLiteral(
"SimpleFill" );
251 selColor.setAlphaF( context.
opacity() );
290 #ifndef QT_NO_PRINTER
291 if (
mBrush.style() == Qt::SolidPattern ||
mBrush.style() == Qt::NoBrush || !
dynamic_cast<QPrinter *
>( p->device() ) )
298 #ifndef QT_NO_PRINTER
305 p->setPen( Qt::NoPen );
309 p->setBrush( Qt::NoBrush );
327 map[QStringLiteral(
"outline_width" )] = QString::number(
mStrokeWidth );
355 QDomElement symbolizerElem = doc.createElement( QStringLiteral(
"se:PolygonSymbolizer" ) );
356 if ( !props.value( QStringLiteral(
"uom" ), QString() ).isEmpty() )
357 symbolizerElem.setAttribute( QStringLiteral(
"uom" ), props.value( QStringLiteral(
"uom" ), QString() ) );
358 element.appendChild( symbolizerElem );
366 QDomElement fillElem = doc.createElement( QStringLiteral(
"se:Fill" ) );
367 symbolizerElem.appendChild( fillElem );
374 QDomElement strokeElem = doc.createElement( QStringLiteral(
"se:Stroke" ) );
375 symbolizerElem.appendChild( strokeElem );
390 symbolStyle.append(
';' );
399 Qt::BrushStyle fillStyle;
403 QDomElement fillElem = element.firstChildElement( QStringLiteral(
"Fill" ) );
406 QDomElement strokeElem = element.firstChildElement( QStringLiteral(
"Stroke" ) );
412 QString uom = element.attribute( QStringLiteral(
"uom" ), QString() );
418 sl->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
427 return penBleed + offsetBleed;
488 : mGradientColorType( colorType )
489 , mGradientType( gradientType )
490 , mCoordinateMode( coordinateMode )
491 , mGradientSpread( spread )
492 , mReferencePoint1( QPointF( 0.5, 0 ) )
493 , mReferencePoint2( QPointF( 0.5, 1 ) )
514 bool refPoint1IsCentroid =
false;
516 bool refPoint2IsCentroid =
false;
521 if ( props.contains( QStringLiteral(
"type" ) ) )
522 type =
static_cast< GradientType >( props[QStringLiteral(
"type" )].toInt() );
523 if ( props.contains( QStringLiteral(
"coordinate_mode" ) ) )
525 if ( props.contains( QStringLiteral(
"spread" ) ) )
527 if ( props.contains( QStringLiteral(
"color_type" ) ) )
528 colorType =
static_cast< GradientColorType >( props[QStringLiteral(
"color_type" )].toInt() );
529 if ( props.contains( QStringLiteral(
"gradient_color" ) ) )
534 else if ( props.contains( QStringLiteral(
"color" ) ) )
538 if ( props.contains( QStringLiteral(
"gradient_color2" ) ) )
543 if ( props.contains( QStringLiteral(
"reference_point1" ) ) )
545 if ( props.contains( QStringLiteral(
"reference_point1_iscentroid" ) ) )
546 refPoint1IsCentroid = props[QStringLiteral(
"reference_point1_iscentroid" )].toInt();
547 if ( props.contains( QStringLiteral(
"reference_point2" ) ) )
549 if ( props.contains( QStringLiteral(
"reference_point2_iscentroid" ) ) )
550 refPoint2IsCentroid = props[QStringLiteral(
"reference_point2_iscentroid" )].toInt();
551 if ( props.contains( QStringLiteral(
"angle" ) ) )
552 angle = props[QStringLiteral(
"angle" )].toDouble();
554 if ( props.contains( QStringLiteral(
"offset" ) ) )
559 if ( props.contains( QStringLiteral(
"rampType" ) ) && props[QStringLiteral(
"rampType" )] == QStringLiteral(
"cpt-city" ) )
571 if ( props.contains( QStringLiteral(
"offset_unit" ) ) )
573 if ( props.contains( QStringLiteral(
"offset_map_unit_scale" ) ) )
576 sl->setReferencePoint1IsCentroid( refPoint1IsCentroid );
578 sl->setReferencePoint2IsCentroid( refPoint2IsCentroid );
579 sl->setAngle(
angle );
581 sl->setColorRamp( gradientRamp );
583 sl->restoreOldDataDefinedProperties( props );
596 return QStringLiteral(
"GradientFill" );
599 void QgsGradientFillSymbolLayer::applyDataDefinedSymbology(
QgsSymbolRenderContext &context,
const QPolygonF &points )
642 if ( currentType == QObject::tr(
"linear" ) )
646 else if ( currentType == QObject::tr(
"radial" ) )
650 else if ( currentType == QObject::tr(
"conical" ) )
664 if ( currentCoordMode == QObject::tr(
"feature" ) )
668 else if ( currentCoordMode == QObject::tr(
"viewport" ) )
682 if ( currentSpread == QObject::tr(
"pad" ) )
686 else if ( currentSpread == QObject::tr(
"repeat" ) )
690 else if ( currentSpread == QObject::tr(
"reflect" ) )
737 if ( refPoint1IsCentroid || refPoint2IsCentroid )
742 QRectF bbox = points.boundingRect();
743 double centroidX = ( centroid.x() - bbox.left() ) / bbox.width();
744 double centroidY = ( centroid.y() - bbox.top() ) / bbox.height();
746 if ( refPoint1IsCentroid )
748 refPoint1X = centroidX;
749 refPoint1Y = centroidY;
751 if ( refPoint2IsCentroid )
753 refPoint2X = centroidX;
754 refPoint2Y = centroidY;
760 spread, QPointF( refPoint1X, refPoint1Y ), QPointF( refPoint2X, refPoint2Y ),
angle );
763 QPointF QgsGradientFillSymbolLayer::rotateReferencePoint( QPointF refPoint,
double angle )
768 QLineF refLine = QLineF( QPointF( 0.5, 0.5 ), refPoint );
770 refLine.setAngle( refLine.angle() +
angle );
772 QPointF rotatedReferencePoint = refLine.p2();
774 if ( rotatedReferencePoint.x() > 1 )
775 rotatedReferencePoint.setX( 1 );
776 if ( rotatedReferencePoint.x() < 0 )
777 rotatedReferencePoint.setX( 0 );
778 if ( rotatedReferencePoint.y() > 1 )
779 rotatedReferencePoint.setY( 1 );
780 if ( rotatedReferencePoint.y() < 0 )
781 rotatedReferencePoint.setY( 0 );
783 return rotatedReferencePoint;
787 const QColor &color,
const QColor &color2, GradientColorType gradientColorType,
789 GradientCoordinateMode coordinateMode, GradientSpread gradientSpread,
790 QPointF referencePoint1, QPointF referencePoint2,
const double angle )
795 QColor fillColor2 =
color2;
796 fillColor2.setAlphaF( context.
opacity() * fillColor2.alphaF() );
807 gradient = QLinearGradient( rotatedReferencePoint1, rotatedReferencePoint2 );
810 gradient = QRadialGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).length() );
813 gradient = QConicalGradient( rotatedReferencePoint1, QLineF( rotatedReferencePoint1, rotatedReferencePoint2 ).
angle() );
819 gradient.setCoordinateMode( QGradient::ObjectBoundingMode );
822 gradient.setCoordinateMode( QGradient::StretchToDeviceMode );
828 gradient.setSpread( QGradient::PadSpread );
831 gradient.setSpread( QGradient::ReflectSpread );
834 gradient.setSpread( QGradient::RepeatSpread );
840 ( gradientRamp->
type() == QLatin1String(
"gradient" ) || gradientRamp->
type() == QLatin1String(
"cpt-city" ) ) )
850 gradient.setColorAt( 1.0, fillColor2 );
854 brush = QBrush( gradient );
861 selColor.setAlphaF( context.
opacity() );
878 applyDataDefinedSymbology( context, points );
881 p->setPen( Qt::NoPen );
905 map[QStringLiteral(
"type" )] = QString::number(
mGradientType );
906 map[QStringLiteral(
"coordinate_mode" )] = QString::number(
mCoordinateMode );
912 map[QStringLiteral(
"angle" )] = QString::number(
mAngle );
970 int blurRadius,
bool useWholeShape,
double maxDistance )
971 : mBlurRadius( blurRadius )
972 , mUseWholeShape( useWholeShape )
973 , mMaxDistance( maxDistance )
974 , mColorType( colorType )
993 if ( props.contains( QStringLiteral(
"color_type" ) ) )
997 if ( props.contains( QStringLiteral(
"shapeburst_color" ) ) )
1002 else if ( props.contains( QStringLiteral(
"color" ) ) )
1007 if ( props.contains( QStringLiteral(
"shapeburst_color2" ) ) )
1012 else if ( props.contains( QStringLiteral(
"gradient_color2" ) ) )
1016 if ( props.contains( QStringLiteral(
"blur_radius" ) ) )
1018 blurRadius = props[QStringLiteral(
"blur_radius" )].toInt();
1020 if ( props.contains( QStringLiteral(
"use_whole_shape" ) ) )
1022 useWholeShape = props[QStringLiteral(
"use_whole_shape" )].toInt();
1024 if ( props.contains( QStringLiteral(
"max_distance" ) ) )
1026 maxDistance = props[QStringLiteral(
"max_distance" )].toDouble();
1028 if ( props.contains( QStringLiteral(
"offset" ) ) )
1035 if ( props.contains( QStringLiteral(
"rampType" ) ) && props[QStringLiteral(
"rampType" )] == QStringLiteral(
"cpt-city" ) )
1047 if ( props.contains( QStringLiteral(
"offset_unit" ) ) )
1051 if ( props.contains( QStringLiteral(
"distance_unit" ) ) )
1055 if ( props.contains( QStringLiteral(
"offset_map_unit_scale" ) ) )
1059 if ( props.contains( QStringLiteral(
"distance_map_unit_scale" ) ) )
1063 if ( props.contains( QStringLiteral(
"ignore_rings" ) ) )
1065 sl->setIgnoreRings( props[QStringLiteral(
"ignore_rings" )].toInt() );
1069 sl->setColorRamp( gradientRamp );
1072 sl->restoreOldDataDefinedProperties( props );
1074 return sl.release();
1079 return QStringLiteral(
"ShapeburstFill" );
1084 if ( mGradientRamp.get() == ramp )
1087 mGradientRamp.reset( ramp );
1090 void QgsShapeburstFillSymbolLayer::applyDataDefinedSymbology(
QgsSymbolRenderContext &context, QColor &color, QColor &color2,
int &blurRadius,
bool &useWholeShape,
1091 double &maxDistance,
bool &ignoreRings )
1148 selColor.setAlphaF( context.
opacity() );
1149 mSelBrush = QBrush( selColor );
1168 p->setBrush( mSelBrush );
1170 if ( !mOffset.isNull() )
1177 if ( !mOffset.isNull() )
1193 int outputPixelMaxDist = 0;
1201 std::unique_ptr< QgsGradientColorRamp > twoColorGradientRamp;
1204 twoColorGradientRamp = qgis::make_unique< QgsGradientColorRamp >( color1,
color2 );
1208 p->setPen( QPen( Qt::NoPen ) );
1213 int pointsWidth =
static_cast< int >( std::round( points.boundingRect().width() ) );
1214 int pointsHeight =
static_cast< int >( std::round( points.boundingRect().height() ) );
1215 int imWidth = pointsWidth + ( sideBuffer * 2 );
1216 int imHeight = pointsHeight + ( sideBuffer * 2 );
1217 std::unique_ptr< QImage > fillImage = qgis::make_unique< QImage >( imWidth,
1218 imHeight, QImage::Format_ARGB32_Premultiplied );
1219 if ( fillImage->isNull() )
1226 std::unique_ptr< QImage > alphaImage = qgis::make_unique< QImage >( fillImage->width(), fillImage->height(), QImage::Format_ARGB32_Premultiplied );
1227 if ( alphaImage->isNull() )
1236 fillImage->fill( Qt::black );
1239 alphaImage->fill( Qt::transparent );
1242 QPainter imgPainter;
1243 imgPainter.begin( alphaImage.get() );
1244 imgPainter.setRenderHint( QPainter::Antialiasing,
true );
1245 imgPainter.setBrush( QBrush( Qt::white ) );
1246 imgPainter.setPen( QPen( Qt::black ) );
1247 imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1253 imgPainter.begin( fillImage.get() );
1256 imgPainter.drawImage( 0, 0, *alphaImage );
1263 imgPainter.setBrush( QBrush( Qt::white ) );
1264 imgPainter.setPen( QPen( Qt::black ) );
1265 imgPainter.translate( -points.boundingRect().left() + sideBuffer, - points.boundingRect().top() + sideBuffer );
1271 double *dtArray = distanceTransform( fillImage.get(), context.
renderContext() );
1291 imgPainter.begin( fillImage.get() );
1292 imgPainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
1293 imgPainter.drawImage( 0, 0, *alphaImage );
1302 if ( !mOffset.isNull() )
1309 p->drawImage( points.boundingRect().left() - sideBuffer, points.boundingRect().top() - sideBuffer, *fillImage );
1311 if ( !mOffset.isNull() )
1322 void QgsShapeburstFillSymbolLayer::distanceTransform1d(
double *f,
int n,
int *v,
double *z,
double *d )
1328 for (
int q = 1; q <= n - 1; q++ )
1330 double s = ( ( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1334 s = ( ( f[q] + q * q ) - ( f[v[k]] + ( v[k] * v[k] ) ) ) / ( 2 * q - 2 * v[k] );
1343 for (
int q = 0; q <= n - 1; q++ )
1345 while ( z[k + 1] < q )
1347 d[q] = ( q - v[k] ) * ( q - v[k] ) + f[v[k]];
1352 void QgsShapeburstFillSymbolLayer::distanceTransform2d(
double *im,
int width,
int height,
QgsRenderContext &context )
1354 int maxDimension = std::max( width, height );
1355 double *f =
new double[ maxDimension ];
1356 int *v =
new int[ maxDimension ];
1357 double *z =
new double[ maxDimension + 1 ];
1358 double *d =
new double[ maxDimension ];
1361 for (
int x = 0; x < width; x++ )
1366 for (
int y = 0; y < height; y++ )
1368 f[y] = im[ x + y * width ];
1370 distanceTransform1d( f, height, v, z, d );
1371 for (
int y = 0; y < height; y++ )
1373 im[ x + y * width ] = d[y];
1378 for (
int y = 0; y < height; y++ )
1383 for (
int x = 0; x < width; x++ )
1385 f[x] = im[ x + y * width ];
1387 distanceTransform1d( f, width, v, z, d );
1388 for (
int x = 0; x < width; x++ )
1390 im[ x + y * width ] = d[x];
1401 double *QgsShapeburstFillSymbolLayer::distanceTransform( QImage *im,
QgsRenderContext &context )
1403 int width = im->width();
1404 int height = im->height();
1406 double *dtArray =
new double[width * height];
1411 for (
int heightIndex = 0; heightIndex < height; ++heightIndex )
1416 const QRgb *scanLine =
reinterpret_cast< const QRgb *
>( im->constScanLine( heightIndex ) );
1417 for (
int widthIndex = 0; widthIndex < width; ++widthIndex )
1419 tmpRgb = scanLine[widthIndex];
1420 if ( qRed( tmpRgb ) == 0 )
1428 dtArray[ idx ] =
INF;
1435 distanceTransform2d( dtArray, width, height, context );
1440 void QgsShapeburstFillSymbolLayer::dtArrayToQImage(
double *array, QImage *im,
QgsColorRamp *ramp,
QgsRenderContext &context,
bool useWholeShape,
int maxPixelDistance )
1442 int width = im->width();
1443 int height = im->height();
1446 double maxDistanceValue;
1451 double dtMaxValue = array[0];
1452 for (
int i = 1; i < ( width * height ); ++i )
1454 if ( array[i] > dtMaxValue )
1456 dtMaxValue = array[i];
1461 maxDistanceValue = std::sqrt( dtMaxValue );
1466 maxDistanceValue = maxPixelDistance;
1471 double squaredVal = 0;
1474 for (
int heightIndex = 0; heightIndex < height; ++heightIndex )
1479 QRgb *scanLine =
reinterpret_cast< QRgb *
>( im->scanLine( heightIndex ) );
1480 for (
int widthIndex = 0; widthIndex < width; ++widthIndex )
1483 squaredVal = array[idx];
1486 if ( maxDistanceValue > 0 )
1488 pixVal = squaredVal > 0 ? std::min( ( std::sqrt( squaredVal ) / maxDistanceValue ), 1.0 ) : 0;
1497 scanLine[widthIndex] = qPremultiply( ramp->
color( pixVal ).rgba() );
1508 map[QStringLiteral(
"color_type" )] = QString::number( mColorType );
1509 map[QStringLiteral(
"blur_radius" )] = QString::number( mBlurRadius );
1510 map[QStringLiteral(
"use_whole_shape" )] = QString::number( mUseWholeShape );
1511 map[QStringLiteral(
"max_distance" )] = QString::number( mMaxDistance );
1514 map[QStringLiteral(
"ignore_rings" )] = QString::number( mIgnoreRings );
1518 if ( mGradientRamp )
1520 map.unite( mGradientRamp->properties() );
1528 std::unique_ptr< QgsShapeburstFillSymbolLayer > sl = qgis::make_unique< QgsShapeburstFillSymbolLayer >(
mColor, mColor2, mColorType, mBlurRadius, mUseWholeShape, mMaxDistance );
1529 if ( mGradientRamp )
1531 sl->setColorRamp( mGradientRamp->clone() );
1533 sl->setDistanceUnit( mDistanceUnit );
1534 sl->setDistanceMapUnitScale( mDistanceMapUnitScale );
1535 sl->setIgnoreRings( mIgnoreRings );
1536 sl->setOffset( mOffset );
1537 sl->setOffsetUnit( mOffsetUnit );
1538 sl->setOffsetMapUnitScale( mOffsetMapUnitScale );
1541 return sl.release();
1546 double offsetBleed = context.
convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale );
1552 mDistanceUnit = unit;
1558 if ( mDistanceUnit == mOffsetUnit )
1560 return mDistanceUnit;
1567 mDistanceMapUnitScale = scale;
1568 mOffsetMapUnitScale = scale;
1573 if ( mDistanceMapUnitScale == mOffsetMapUnitScale )
1575 return mDistanceMapUnitScale;
1599 p->setPen( QPen( Qt::NoPen ) );
1601 QTransform bkTransform =
mBrush.transform();
1605 QPointF leftCorner = points.boundingRect().topLeft();
1606 QTransform t =
mBrush.transform();
1607 t.translate( leftCorner.x(), leftCorner.y() );
1608 mBrush.setTransform( t );
1614 p->setBrush( QBrush( selColor ) );
1620 QTransform t =
mBrush.transform();
1622 mBrush.setTransform( t );
1631 for (
auto ringIt = rings->constBegin(); ringIt != rings->constEnd(); ++ringIt )
1638 mBrush.setTransform( bkTransform );
1690 double subLayerBleed =
mStroke->symbolLayer( 0 )->estimateMaxBleed( context );
1691 return subLayerBleed;
1712 return QColor( Qt::black );
1719 return Qt::SolidLine;
1723 return Qt::SolidLine;
1727 return mStroke->dxfPenStyle();
1736 attr.unite(
mStroke->usedAttributes( context ) );
1754 , mPatternWidth( width )
1758 mColor = QColor( 255, 255, 255 );
1764 , mPatternWidth( width )
1765 , mSvgData( svgData )
1770 mColor = QColor( 255, 255, 255 );
1772 setDefaultSvgParams();
1778 mPatternWidthUnit = unit;
1779 mSvgStrokeWidthUnit = unit;
1781 mStroke->setOutputUnit( unit );
1787 if ( mPatternWidthUnit != unit || mSvgStrokeWidthUnit != unit ||
mStrokeWidthUnit != unit )
1797 mPatternWidthMapUnitScale = scale;
1798 mSvgStrokeWidthMapUnitScale = scale;
1805 mPatternWidthMapUnitScale == mSvgStrokeWidthMapUnitScale &&
1808 return mPatternWidthMapUnitScale;
1818 mSvgFilePath = svgPath;
1819 setDefaultSvgParams();
1829 if (
properties.contains( QStringLiteral(
"width" ) ) )
1831 width =
properties[QStringLiteral(
"width" )].toDouble();
1833 if (
properties.contains( QStringLiteral(
"svgFile" ) ) )
1837 if (
properties.contains( QStringLiteral(
"angle" ) ) )
1842 std::unique_ptr< QgsSVGFillSymbolLayer > symbolLayer;
1845 symbolLayer = qgis::make_unique< QgsSVGFillSymbolLayer >(
svgFilePath, width,
angle );
1849 if (
properties.contains( QStringLiteral(
"data" ) ) )
1851 data = QByteArray::fromHex(
properties[QStringLiteral(
"data" )].toLocal8Bit() );
1853 symbolLayer = qgis::make_unique< QgsSVGFillSymbolLayer >( data, width,
angle );
1857 if (
properties.contains( QStringLiteral(
"svgFillColor" ) ) )
1862 else if (
properties.contains( QStringLiteral(
"color" ) ) )
1866 if (
properties.contains( QStringLiteral(
"svgOutlineColor" ) ) )
1871 else if (
properties.contains( QStringLiteral(
"outline_color" ) ) )
1875 else if (
properties.contains( QStringLiteral(
"line_color" ) ) )
1879 if (
properties.contains( QStringLiteral(
"svgOutlineWidth" ) ) )
1882 symbolLayer->setSvgStrokeWidth(
properties[QStringLiteral(
"svgOutlineWidth" )].toDouble() );
1884 else if (
properties.contains( QStringLiteral(
"outline_width" ) ) )
1886 symbolLayer->setSvgStrokeWidth(
properties[QStringLiteral(
"outline_width" )].toDouble() );
1888 else if (
properties.contains( QStringLiteral(
"line_width" ) ) )
1890 symbolLayer->setSvgStrokeWidth(
properties[QStringLiteral(
"line_width" )].toDouble() );
1894 if (
properties.contains( QStringLiteral(
"pattern_width_unit" ) ) )
1898 if (
properties.contains( QStringLiteral(
"pattern_width_map_unit_scale" ) ) )
1902 if (
properties.contains( QStringLiteral(
"svg_outline_width_unit" ) ) )
1906 if (
properties.contains( QStringLiteral(
"svg_outline_width_map_unit_scale" ) ) )
1910 if (
properties.contains( QStringLiteral(
"outline_width_unit" ) ) )
1914 if (
properties.contains( QStringLiteral(
"outline_width_map_unit_scale" ) ) )
1919 symbolLayer->restoreOldDataDefinedProperties(
properties );
1921 return symbolLayer.release();
1926 QgsStringMap::iterator it =
properties.find( QStringLiteral(
"svgFile" ) );
1938 return QStringLiteral(
"SVGFill" );
1941 void QgsSVGFillSymbolLayer::applyPattern( QBrush &brush,
const QString &svgFilePath,
double patternWidth,
QgsUnitTypes::RenderUnit patternWidthUnit,
1942 const QColor &svgFillColor,
const QColor &svgStrokeColor,
double svgStrokeWidth,
1946 if ( mSvgViewBox.isNull() )
1953 if (
static_cast< int >( size ) < 1.0 || 10000.0 < size )
1955 brush.setTextureImage( QImage() );
1959 bool fitsInCache =
true;
1967 double hwRatio = 1.0;
1968 if ( patternPict.width() > 0 )
1970 hwRatio =
static_cast< double >( patternPict.height() ) /
static_cast< double >( patternPict.width() );
1972 patternImage = QImage(
static_cast< int >( size ),
static_cast< int >( size * hwRatio ), QImage::Format_ARGB32_Premultiplied );
1973 patternImage.fill( 0 );
1975 QPainter p( &patternImage );
1976 p.drawPicture( QPointF( size / 2, size * hwRatio / 2 ), patternPict );
1979 QTransform brushTransform;
1982 QImage transparentImage = patternImage.copy();
1984 brush.setTextureImage( transparentImage );
1988 brush.setTextureImage( patternImage );
1990 brush.setTransform( brushTransform );
1997 applyPattern(
mBrush, mSvgFilePath, mPatternWidth, mPatternWidthUnit,
mColor, mSvgStrokeColor, mSvgStrokeWidth, mSvgStrokeWidthUnit, context, mPatternWidthMapUnitScale, mSvgStrokeWidthMapUnitScale );
2016 if ( !mSvgFilePath.isEmpty() )
2018 map.insert( QStringLiteral(
"svgFile" ), mSvgFilePath );
2022 map.insert( QStringLiteral(
"data" ), QString( mSvgData.toHex() ) );
2025 map.insert( QStringLiteral(
"width" ), QString::number( mPatternWidth ) );
2026 map.insert( QStringLiteral(
"angle" ), QString::number(
mAngle ) );
2031 map.insert( QStringLiteral(
"outline_width" ), QString::number( mSvgStrokeWidth ) );
2045 std::unique_ptr< QgsSVGFillSymbolLayer > clonedLayer;
2046 if ( !mSvgFilePath.isEmpty() )
2048 clonedLayer = qgis::make_unique< QgsSVGFillSymbolLayer >( mSvgFilePath, mPatternWidth,
mAngle );
2049 clonedLayer->setSvgFillColor(
mColor );
2050 clonedLayer->setSvgStrokeColor( mSvgStrokeColor );
2051 clonedLayer->setSvgStrokeWidth( mSvgStrokeWidth );
2055 clonedLayer = qgis::make_unique< QgsSVGFillSymbolLayer >( mSvgData, mPatternWidth,
mAngle );
2058 clonedLayer->setPatternWidthUnit( mPatternWidthUnit );
2059 clonedLayer->setPatternWidthMapUnitScale( mPatternWidthMapUnitScale );
2060 clonedLayer->setSvgStrokeWidthUnit( mSvgStrokeWidthUnit );
2061 clonedLayer->setSvgStrokeWidthMapUnitScale( mSvgStrokeWidthMapUnitScale );
2067 clonedLayer->setSubSymbol(
mStroke->clone() );
2071 return clonedLayer.release();
2076 QDomElement symbolizerElem = doc.createElement( QStringLiteral(
"se:PolygonSymbolizer" ) );
2077 if ( !props.value( QStringLiteral(
"uom" ), QString() ).isEmpty() )
2078 symbolizerElem.setAttribute( QStringLiteral(
"uom" ), props.value( QStringLiteral(
"uom" ), QString() ) );
2079 element.appendChild( symbolizerElem );
2083 QDomElement fillElem = doc.createElement( QStringLiteral(
"se:Fill" ) );
2084 symbolizerElem.appendChild( fillElem );
2086 QDomElement graphicFillElem = doc.createElement( QStringLiteral(
"se:GraphicFill" ) );
2087 fillElem.appendChild( graphicFillElem );
2089 QDomElement graphicElem = doc.createElement( QStringLiteral(
"se:Graphic" ) );
2090 graphicFillElem.appendChild( graphicElem );
2092 if ( !mSvgFilePath.isEmpty() )
2103 symbolizerElem.appendChild( doc.createComment( QStringLiteral(
"SVG from data not implemented yet" ) ) );
2109 double angle = props.value( QStringLiteral(
"angle" ), QStringLiteral(
"0" ) ).toDouble( &ok );
2112 angleFunc = QStringLiteral(
"%1 + %2" ).arg( props.value( QStringLiteral(
"angle" ), QStringLiteral(
"0" ) ) ).arg(
mAngle );
2125 mStroke->toSld( doc, element, props );
2131 QString path, mimeType;
2133 Qt::PenStyle penStyle;
2134 double size, strokeWidth;
2136 QDomElement fillElem = element.firstChildElement( QStringLiteral(
"Fill" ) );
2137 if ( fillElem.isNull() )
2140 QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral(
"GraphicFill" ) );
2141 if ( graphicFillElem.isNull() )
2144 QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral(
"Graphic" ) );
2145 if ( graphicElem.isNull() )
2151 if ( mimeType != QLatin1String(
"image/svg+xml" ) )
2156 QString uom = element.attribute( QStringLiteral(
"uom" ) );
2165 double d = angleFunc.toDouble( &ok );
2170 std::unique_ptr< QgsSVGFillSymbolLayer > sl = qgis::make_unique< QgsSVGFillSymbolLayer >( path, size,
angle );
2171 sl->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
2174 sl->setSvgStrokeWidth( strokeWidth );
2177 QDomElement strokeElem = element.firstChildElement( QStringLiteral(
"Stroke" ) );
2178 if ( !strokeElem.isNull() )
2189 return sl.release();
2207 double width = mPatternWidth;
2213 QString svgFile = mSvgFilePath;
2232 double strokeWidth = mSvgStrokeWidth;
2239 mSvgStrokeWidthUnit, context, mPatternWidthMapUnitScale, mSvgStrokeWidthMapUnitScale );
2243 void QgsSVGFillSymbolLayer::storeViewBox()
2245 if ( !mSvgData.isEmpty() )
2247 QSvgRenderer r( mSvgData );
2250 mSvgViewBox = r.viewBoxF();
2255 mSvgViewBox = QRectF();
2258 void QgsSVGFillSymbolLayer::setDefaultSvgParams()
2260 if ( mSvgFilePath.isEmpty() )
2265 bool hasFillParam, hasFillOpacityParam, hasStrokeParam, hasStrokeWidthParam, hasStrokeOpacityParam;
2266 bool hasDefaultFillColor, hasDefaultFillOpacity, hasDefaultStrokeColor, hasDefaultStrokeWidth, hasDefaultStrokeOpacity;
2267 QColor defaultFillColor, defaultStrokeColor;
2268 double defaultStrokeWidth, defaultFillOpacity, defaultStrokeOpacity;
2270 hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
2271 hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2272 hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
2273 hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity );
2275 double newFillOpacity = hasFillOpacityParam ?
mColor.alphaF() : 1.0;
2276 double newStrokeOpacity = hasStrokeOpacityParam ? mSvgStrokeColor.alphaF() : 1.0;
2278 if ( hasDefaultFillColor )
2280 mColor = defaultFillColor;
2281 mColor.setAlphaF( newFillOpacity );
2283 if ( hasDefaultFillOpacity )
2285 mColor.setAlphaF( defaultFillOpacity );
2287 if ( hasDefaultStrokeColor )
2289 mSvgStrokeColor = defaultStrokeColor;
2290 mSvgStrokeColor.setAlphaF( newStrokeOpacity );
2292 if ( hasDefaultStrokeOpacity )
2294 mSvgStrokeColor.setAlphaF( defaultStrokeOpacity );
2296 if ( hasDefaultStrokeWidth )
2298 mSvgStrokeWidth = defaultStrokeWidth;
2324 return mFillLineSymbol ? mFillLineSymbol->
color() :
mColor;
2329 delete mFillLineSymbol;
2344 delete mFillLineSymbol;
2345 mFillLineSymbol = lineSymbol;
2356 return mFillLineSymbol;
2362 if ( mFillLineSymbol )
2384 mDistanceUnit = unit;
2385 mLineWidthUnit = unit;
2392 if ( mDistanceUnit != unit || mLineWidthUnit != unit || mOffsetUnit != unit )
2402 mDistanceMapUnitScale = scale;
2403 mLineWidthMapUnitScale = scale;
2404 mOffsetMapUnitScale = scale;
2410 mDistanceMapUnitScale == mLineWidthMapUnitScale &&
2411 mLineWidthMapUnitScale == mOffsetMapUnitScale )
2413 return mDistanceMapUnitScale;
2420 std::unique_ptr< QgsLinePatternFillSymbolLayer > patternLayer = qgis::make_unique< QgsLinePatternFillSymbolLayer >();
2426 QColor
color( Qt::black );
2429 if (
properties.contains( QStringLiteral(
"lineangle" ) ) )
2434 else if (
properties.contains( QStringLiteral(
"angle" ) ) )
2438 patternLayer->setLineAngle(
lineAngle );
2440 if (
properties.contains( QStringLiteral(
"distance" ) ) )
2444 patternLayer->setDistance(
distance );
2446 if (
properties.contains( QStringLiteral(
"linewidth" ) ) )
2451 else if (
properties.contains( QStringLiteral(
"outline_width" ) ) )
2455 else if (
properties.contains( QStringLiteral(
"line_width" ) ) )
2459 patternLayer->setLineWidth(
lineWidth );
2461 if (
properties.contains( QStringLiteral(
"color" ) ) )
2465 else if (
properties.contains( QStringLiteral(
"outline_color" ) ) )
2469 else if (
properties.contains( QStringLiteral(
"line_color" ) ) )
2473 patternLayer->setColor(
color );
2475 if (
properties.contains( QStringLiteral(
"offset" ) ) )
2479 patternLayer->setOffset(
offset );
2482 if (
properties.contains( QStringLiteral(
"distance_unit" ) ) )
2486 if (
properties.contains( QStringLiteral(
"distance_map_unit_scale" ) ) )
2490 if (
properties.contains( QStringLiteral(
"line_width_unit" ) ) )
2494 else if (
properties.contains( QStringLiteral(
"outline_width_unit" ) ) )
2498 if (
properties.contains( QStringLiteral(
"line_width_map_unit_scale" ) ) )
2502 if (
properties.contains( QStringLiteral(
"offset_unit" ) ) )
2506 if (
properties.contains( QStringLiteral(
"offset_map_unit_scale" ) ) )
2510 if (
properties.contains( QStringLiteral(
"outline_width_unit" ) ) )
2514 if (
properties.contains( QStringLiteral(
"outline_width_map_unit_scale" ) ) )
2519 patternLayer->restoreOldDataDefinedProperties(
properties );
2521 return patternLayer.release();
2526 return QStringLiteral(
"LinePatternFill" );
2529 void QgsLinePatternFillSymbolLayer::applyPattern(
const QgsSymbolRenderContext &context, QBrush &brush,
double lineAngle,
double distance )
2531 mBrush.setTextureImage( QImage() );
2533 if ( !mFillLineSymbol )
2538 std::unique_ptr< QgsLineSymbol > fillLineSymbol( mFillLineSymbol->
clone() );
2539 if ( !fillLineSymbol )
2554 outputPixelOffset = std::fmod( outputPixelOffset, outputPixelDist );
2555 if ( outputPixelOffset > outputPixelDist / 2.0 )
2556 outputPixelOffset -= outputPixelDist;
2560 double outputPixelBleed = 0;
2561 double outputPixelInterval = 0;
2562 for (
int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2566 outputPixelBleed = std::max( outputPixelBleed, outputPixelLayerBleed );
2569 if ( markerLineLayer )
2578 outputPixelInterval = std::max( outputPixelInterval, outputPixelLayerInterval );
2582 if ( outputPixelInterval > 0 )
2586 double intervalScale = std::round( outputPixelInterval ) / outputPixelInterval;
2587 outputPixelInterval = std::round( outputPixelInterval );
2589 for (
int i = 0; i < fillLineSymbol->symbolLayerCount(); i++ )
2594 if ( markerLineLayer )
2608 height = outputPixelDist;
2609 width = outputPixelInterval > 0 ? outputPixelInterval : height;
2613 width = outputPixelDist;
2614 height = outputPixelInterval > 0 ? outputPixelInterval : width;
2618 height = outputPixelDist / std::cos(
lineAngle * M_PI / 180 );
2619 width = outputPixelDist / std::sin(
lineAngle * M_PI / 180 );
2622 lineAngle = 180 * std::atan2(
static_cast< double >( height ),
static_cast< double >( width ) ) / M_PI;
2628 height = std::abs( height );
2629 width = std::abs( width );
2631 outputPixelDist = std::abs( height * std::cos(
lineAngle * M_PI / 180 ) );
2635 int offsetHeight =
static_cast< int >( std::round( outputPixelOffset / std::cos(
lineAngle * M_PI / 180 ) ) );
2636 outputPixelOffset = offsetHeight * std::cos(
lineAngle * M_PI / 180 );
2645 int bufferMulti =
static_cast< int >( std::max( std::ceil( outputPixelBleed / width ), std::ceil( outputPixelBleed / width ) ) );
2649 bufferMulti = std::max( bufferMulti, 1 );
2651 int xBuffer = width * bufferMulti;
2652 int yBuffer = height * bufferMulti;
2653 int innerWidth = width;
2654 int innerHeight = height;
2655 width += 2 * xBuffer;
2656 height += 2 * yBuffer;
2659 if ( width > 10000 || height > 10000 || width == 0 || height == 0 )
2664 QImage patternImage( width, height, QImage::Format_ARGB32 );
2665 patternImage.fill( 0 );
2667 QPointF p1, p2, p3, p4, p5, p6;
2670 p1 = QPointF( 0, yBuffer );
2671 p2 = QPointF( width, yBuffer );
2672 p3 = QPointF( 0, yBuffer + innerHeight );
2673 p4 = QPointF( width, yBuffer + innerHeight );
2677 p1 = QPointF( xBuffer, height );
2678 p2 = QPointF( xBuffer, 0 );
2679 p3 = QPointF( xBuffer + innerWidth, height );
2680 p4 = QPointF( xBuffer + innerWidth, 0 );
2684 dx = outputPixelDist * std::cos( ( 90 -
lineAngle ) * M_PI / 180.0 );
2685 dy = outputPixelDist * std::sin( ( 90 -
lineAngle ) * M_PI / 180.0 );
2686 p1 = QPointF( 0, height );
2687 p2 = QPointF( width, 0 );
2688 p3 = QPointF( -dx, height - dy );
2689 p4 = QPointF( width - dx, -dy );
2690 p5 = QPointF( dx, height + dy );
2691 p6 = QPointF( width + dx, dy );
2695 dx = outputPixelDist * std::cos( ( 90 -
lineAngle ) * M_PI / 180.0 );
2696 dy = outputPixelDist * std::sin( ( 90 -
lineAngle ) * M_PI / 180.0 );
2697 p1 = QPointF( width, 0 );
2698 p2 = QPointF( 0, height );
2699 p3 = QPointF( width - dx, -dy );
2700 p4 = QPointF( -dx, height - dy );
2701 p5 = QPointF( width + dx, dy );
2702 p6 = QPointF( dx, height + dy );
2706 dy = outputPixelDist * std::cos( ( 180 -
lineAngle ) * M_PI / 180 );
2707 dx = outputPixelDist * std::sin( ( 180 -
lineAngle ) * M_PI / 180 );
2708 p1 = QPointF( 0, 0 );
2709 p2 = QPointF( width, height );
2710 p5 = QPointF( dx, -dy );
2711 p6 = QPointF( width + dx, height - dy );
2712 p3 = QPointF( -dx, dy );
2713 p4 = QPointF( width - dx, height + dy );
2717 dy = outputPixelDist * std::cos( ( 180 -
lineAngle ) * M_PI / 180 );
2718 dx = outputPixelDist * std::sin( ( 180 -
lineAngle ) * M_PI / 180 );
2719 p1 = QPointF( width, height );
2720 p2 = QPointF( 0, 0 );
2721 p5 = QPointF( width + dx, height - dy );
2722 p6 = QPointF( dx, -dy );
2723 p3 = QPointF( width - dx, height + dy );
2724 p4 = QPointF( -dx, dy );
2731 p3 = QPointF( tempPt.x(), tempPt.y() );
2733 p4 = QPointF( tempPt.x(), tempPt.y() );
2735 p5 = QPointF( tempPt.x(), tempPt.y() );
2737 p6 = QPointF( tempPt.x(), tempPt.y() );
2741 p1 = QPointF( tempPt.x(), tempPt.y() );
2743 p2 = QPointF( tempPt.x(), tempPt.y() );
2746 QPainter p( &patternImage );
2750 p.setRenderHint( QPainter::Antialiasing,
false );
2751 QPen pen( QColor( Qt::black ) );
2752 pen.setWidthF( 0.1 );
2753 pen.setCapStyle( Qt::FlatCap );
2758 QPolygon polygon = QPolygon() << QPoint( 0, 0 ) << QPoint( width - 1, 0 ) << QPoint( width - 1, height - 1 ) << QPoint( 0, height - 1 ) << QPoint( 0, 0 );
2759 p.drawPolygon( polygon );
2761 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 );
2762 p.drawPolygon( polygon );
2768 p.setRenderHint( QPainter::Antialiasing,
true );
2779 fillLineSymbol->startRender( lineRenderContext, context.
fields() );
2781 QVector<QPolygonF> polygons;
2782 polygons.append( QPolygonF() << p1 << p2 );
2783 polygons.append( QPolygonF() << p3 << p4 );
2786 polygons.append( QPolygonF() << p5 << p6 );
2789 for (
const QPolygonF &polygon : qgis::as_const( polygons ) )
2791 fillLineSymbol->renderPolyline( polygon, context.
feature(), lineRenderContext, -1, context.
selected() );
2794 fillLineSymbol->stopRender( lineRenderContext );
2798 patternImage = patternImage.copy( xBuffer, yBuffer, patternImage.width() - 2 * xBuffer, patternImage.height() - 2 * yBuffer );
2803 QImage transparentImage = patternImage.copy();
2805 brush.setTextureImage( transparentImage );
2809 brush.setTextureImage( patternImage );
2812 QTransform brushTransform;
2813 brush.setTransform( brushTransform );
2818 applyPattern( context,
mBrush, mLineAngle, mDistance );
2820 if ( mFillLineSymbol )
2828 if ( mFillLineSymbol )
2837 map.insert( QStringLiteral(
"angle" ), QString::number( mLineAngle ) );
2838 map.insert( QStringLiteral(
"distance" ), QString::number( mDistance ) );
2839 map.insert( QStringLiteral(
"line_width" ), QString::number( mLineWidth ) );
2841 map.insert( QStringLiteral(
"offset" ), QString::number( mOffset ) );
2856 if ( mFillLineSymbol )
2867 QDomElement symbolizerElem = doc.createElement( QStringLiteral(
"se:PolygonSymbolizer" ) );
2868 if ( !props.value( QStringLiteral(
"uom" ), QString() ).isEmpty() )
2869 symbolizerElem.setAttribute( QStringLiteral(
"uom" ), props.value( QStringLiteral(
"uom" ), QString() ) );
2870 element.appendChild( symbolizerElem );
2875 QDomElement fillElem = doc.createElement( QStringLiteral(
"se:Fill" ) );
2876 symbolizerElem.appendChild( fillElem );
2878 QDomElement graphicFillElem = doc.createElement( QStringLiteral(
"se:GraphicFill" ) );
2879 fillElem.appendChild( graphicFillElem );
2881 QDomElement graphicElem = doc.createElement( QStringLiteral(
"se:Graphic" ) );
2882 graphicFillElem.appendChild( graphicElem );
2885 QColor lineColor = mFillLineSymbol ? mFillLineSymbol->
color() : QColor();
2886 double lineWidth = mFillLineSymbol ? mFillLineSymbol->
width() : 0.0;
2894 double angle = props.value( QStringLiteral(
"angle" ), QStringLiteral(
"0" ) ).toDouble( &ok );
2897 angleFunc = QStringLiteral(
"%1 + %2" ).arg( props.value( QStringLiteral(
"angle" ), QStringLiteral(
"0" ) ) ).arg( mLineAngle );
2901 angleFunc = QString::number(
angle + mLineAngle );
2906 QPointF lineOffset( std::sin( mLineAngle ) * mOffset, std::cos( mLineAngle ) * mOffset );
2913 QString featureStyle;
2914 featureStyle.append(
"Brush(" );
2915 featureStyle.append( QStringLiteral(
"fc:%1" ).arg(
mColor.name() ) );
2916 featureStyle.append( QStringLiteral(
",bc:%1" ).arg( QStringLiteral(
"#00000000" ) ) );
2917 featureStyle.append(
",id:\"ogr-brush-2\"" );
2918 featureStyle.append( QStringLiteral(
",a:%1" ).arg( mLineAngle ) );
2919 featureStyle.append( QStringLiteral(
",s:%1" ).arg( mLineWidth * widthScaleFactor ) );
2920 featureStyle.append(
",dx:0mm" );
2921 featureStyle.append( QStringLiteral(
",dy:%1mm" ).arg( mDistance * widthScaleFactor ) );
2922 featureStyle.append(
')' );
2923 return featureStyle;
2954 Qt::PenStyle lineStyle;
2956 QDomElement fillElem = element.firstChildElement( QStringLiteral(
"Fill" ) );
2957 if ( fillElem.isNull() )
2960 QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral(
"GraphicFill" ) );
2961 if ( graphicFillElem.isNull() )
2964 QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral(
"Graphic" ) );
2965 if ( graphicElem.isNull() )
2971 if ( name != QLatin1String(
"horline" ) )
2979 double d = angleFunc.toDouble( &ok );
2988 offset = std::sqrt( std::pow( vectOffset.x(), 2 ) + std::pow( vectOffset.y(), 2 ) );
2991 QString uom = element.attribute( QStringLiteral(
"uom" ) );
2995 std::unique_ptr< QgsLinePatternFillSymbolLayer > sl = qgis::make_unique< QgsLinePatternFillSymbolLayer >();
2996 sl->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
2997 sl->setColor( lineColor );
2999 sl->setLineAngle(
angle );
3001 sl->setDistance( size );
3004 QDomElement strokeElem = element.firstChildElement( QStringLiteral(
"Stroke" ) );
3005 if ( !strokeElem.isNull() )
3016 return sl.release();
3093 std::unique_ptr< QgsPointPatternFillSymbolLayer > layer = qgis::make_unique< QgsPointPatternFillSymbolLayer >();
3094 if (
properties.contains( QStringLiteral(
"distance_x" ) ) )
3096 layer->setDistanceX(
properties[QStringLiteral(
"distance_x" )].toDouble() );
3098 if (
properties.contains( QStringLiteral(
"distance_y" ) ) )
3100 layer->setDistanceY(
properties[QStringLiteral(
"distance_y" )].toDouble() );
3102 if (
properties.contains( QStringLiteral(
"displacement_x" ) ) )
3104 layer->setDisplacementX(
properties[QStringLiteral(
"displacement_x" )].toDouble() );
3106 if (
properties.contains( QStringLiteral(
"displacement_y" ) ) )
3108 layer->setDisplacementY(
properties[QStringLiteral(
"displacement_y" )].toDouble() );
3110 if (
properties.contains( QStringLiteral(
"offset_x" ) ) )
3112 layer->setOffsetX(
properties[QStringLiteral(
"offset_x" )].toDouble() );
3114 if (
properties.contains( QStringLiteral(
"offset_y" ) ) )
3116 layer->setOffsetY(
properties[QStringLiteral(
"offset_y" )].toDouble() );
3119 if (
properties.contains( QStringLiteral(
"distance_x_unit" ) ) )
3123 if (
properties.contains( QStringLiteral(
"distance_x_map_unit_scale" ) ) )
3127 if (
properties.contains( QStringLiteral(
"distance_y_unit" ) ) )
3131 if (
properties.contains( QStringLiteral(
"distance_y_map_unit_scale" ) ) )
3135 if (
properties.contains( QStringLiteral(
"displacement_x_unit" ) ) )
3139 if (
properties.contains( QStringLiteral(
"displacement_x_map_unit_scale" ) ) )
3143 if (
properties.contains( QStringLiteral(
"displacement_y_unit" ) ) )
3147 if (
properties.contains( QStringLiteral(
"displacement_y_map_unit_scale" ) ) )
3151 if (
properties.contains( QStringLiteral(
"offset_x_unit" ) ) )
3155 if (
properties.contains( QStringLiteral(
"offset_x_map_unit_scale" ) ) )
3159 if (
properties.contains( QStringLiteral(
"offset_y_unit" ) ) )
3163 if (
properties.contains( QStringLiteral(
"offset_y_map_unit_scale" ) ) )
3168 if (
properties.contains( QStringLiteral(
"outline_width_unit" ) ) )
3172 if (
properties.contains( QStringLiteral(
"outline_width_map_unit_scale" ) ) )
3177 layer->restoreOldDataDefinedProperties(
properties );
3179 return layer.release();
3184 return QStringLiteral(
"PointPatternFill" );
3187 void QgsPointPatternFillSymbolLayer::applyPattern(
const QgsSymbolRenderContext &context, QBrush &brush,
double distanceX,
double distanceY,
3188 double displacementX,
double displacementY,
double offsetX,
double offsetY )
3198 if ( width > 10000 || height > 10000 )
3201 brush.setTextureImage( img );
3205 QImage patternImage( width, height, QImage::Format_ARGB32 );
3206 patternImage.fill( 0 );
3207 if ( patternImage.isNull() )
3209 brush.setTextureImage( QImage() );
3214 QPainter p( &patternImage );
3224 p.setRenderHint( QPainter::Antialiasing,
true );
3234 for (
double currentX = -width; currentX <= width * 2.0; currentX += width )
3236 for (
double currentY = -height; currentY <= height * 2.0; currentY += height )
3245 for (
double currentX = -width; currentX <= width * 2.0; currentX += width )
3247 for (
double currentY = -height / 2.0; currentY <= height * 2.0; currentY += height )
3253 for (
double currentX = -width / 2.0; currentX <= width * 2.0; currentX += width )
3255 for (
double currentY = -height; currentY <= height * 2.0; currentY += height / 2.0 )
3257 mMarkerSymbol->
renderPoint( QPointF( currentX + widthOffset + ( std::fmod( currentY, height ) != 0 ? displacementPixelX : 0 ), currentY + heightOffset - displacementPixelY ), context.
feature(), pointRenderContext );
3266 QImage transparentImage = patternImage.copy();
3268 brush.setTextureImage( transparentImage );
3272 brush.setTextureImage( patternImage );
3274 QTransform brushTransform;
3275 brush.setTransform( brushTransform );
3284 if ( mRenderUsingMarkers )
3302 if ( mRenderUsingMarkers )
3315 if ( !mRenderUsingMarkers )
3377 p->setPen( QPen( Qt::NoPen ) );
3382 p->setBrush( QBrush( selColor ) );
3389 path.addPolygon( points );
3392 for (
const QPolygonF &ring : *rings )
3394 path.addPolygon( ring );
3397 p->setClipPath( path, Qt::IntersectClip );
3399 const double left = points.boundingRect().left();
3400 const double top = points.boundingRect().top();
3401 const double right = points.boundingRect().right();
3402 const double bottom = points.boundingRect().bottom();
3409 bool alternateColumn =
false;
3410 int currentCol = -3;
3411 for (
double currentX = ( std::floor( left / width ) - 2 ) * width; currentX <= right + 2 * width; currentX += width, alternateColumn = !alternateColumn )
3413 if ( needsExpressionContext )
3416 bool alternateRow =
false;
3417 const double columnX = currentX + widthOffset;
3418 int currentRow = -3;
3419 for (
double currentY = ( std::floor( top / height ) - 2 ) * height; currentY <= bottom + 2 * height; currentY += height, alternateRow = !alternateRow )
3421 double y = currentY + heightOffset;
3424 x += displacementPixelX;
3426 if ( !alternateColumn )
3427 y -= displacementPixelY;
3429 if ( needsExpressionContext )
3446 for (
auto ringIt = rings->constBegin(); ringIt != rings->constEnd(); ++ringIt )
3457 map.insert( QStringLiteral(
"distance_x" ), QString::number(
mDistanceX ) );
3458 map.insert( QStringLiteral(
"distance_y" ), QString::number(
mDistanceY ) );
3459 map.insert( QStringLiteral(
"displacement_x" ), QString::number(
mDisplacementX ) );
3460 map.insert( QStringLiteral(
"displacement_y" ), QString::number(
mDisplacementY ) );
3461 map.insert( QStringLiteral(
"offset_x" ), QString::number(
mOffsetX ) );
3462 map.insert( QStringLiteral(
"offset_y" ), QString::number(
mOffsetY ) );
3496 QDomElement symbolizerElem = doc.createElement( QStringLiteral(
"se:PolygonSymbolizer" ) );
3497 if ( !props.value( QStringLiteral(
"uom" ), QString() ).isEmpty() )
3498 symbolizerElem.setAttribute( QStringLiteral(
"uom" ), props.value( QStringLiteral(
"uom" ), QString() ) );
3499 element.appendChild( symbolizerElem );
3504 QDomElement fillElem = doc.createElement( QStringLiteral(
"se:Fill" ) );
3505 symbolizerElem.appendChild( fillElem );
3507 QDomElement graphicFillElem = doc.createElement( QStringLiteral(
"se:GraphicFill" ) );
3508 fillElem.appendChild( graphicFillElem );
3515 symbolizerElem.appendChild( distanceElem );
3521 QString errorMsg = QStringLiteral(
"MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( layer->
layerType() );
3522 graphicFillElem.appendChild( doc.createComment( errorMsg ) );
3648 std::unique_ptr< QgsCentroidFillSymbolLayer > sl = qgis::make_unique< QgsCentroidFillSymbolLayer >();
3650 if (
properties.contains( QStringLiteral(
"point_on_surface" ) ) )
3651 sl->setPointOnSurface(
properties[QStringLiteral(
"point_on_surface" )].toInt() != 0 );
3652 if (
properties.contains( QStringLiteral(
"point_on_all_parts" ) ) )
3653 sl->setPointOnAllParts(
properties[QStringLiteral(
"point_on_all_parts" )].toInt() != 0 );
3654 if (
properties.contains( QStringLiteral(
"clip_points" ) ) )
3655 sl->setClipPoints(
properties[QStringLiteral(
"clip_points" )].toInt() != 0 );
3656 if (
properties.contains( QStringLiteral(
"clip_on_current_part_only" ) ) )
3657 sl->setClipOnCurrentPartOnly(
properties[QStringLiteral(
"clip_on_current_part_only" )].toInt() != 0 );
3659 sl->restoreOldDataDefinedProperties(
properties );
3661 return sl.release();
3666 return QStringLiteral(
"CentroidFill" );
3697 part.exterior = points;
3699 part.rings = *rings;
3705 mCurrentParts << part;
3717 mCurrentParts.clear();
3723 render( context, mCurrentParts, feature,
false );
3726 void QgsCentroidFillSymbolLayer::render(
QgsRenderContext &context,
const QVector<QgsCentroidFillSymbolLayer::Part> &parts,
const QgsFeature &feature,
bool selected )
3735 QVector< QgsGeometry > geometryParts;
3736 geometryParts.reserve( parts.size() );
3737 QPainterPath globalPath;
3740 int maxAreaPartIdx = 0;
3742 for (
int i = 0; i < parts.size(); i++ )
3744 const Part part = parts[i];
3747 if ( !geom.
isNull() && !part.rings.empty() )
3749 QgsPolygon *poly = qgsgeometry_cast< QgsPolygon * >( geom.
get() );
3753 int area = poly->
area();
3755 if ( area > maxArea )
3765 globalPath.addPolygon( part.exterior );
3766 for (
const QPolygonF &ring : part.rings )
3768 globalPath.addPolygon( ring );
3773 for (
int i = 0; i < parts.size(); i++ )
3778 const Part part = parts[i];
3786 path.addPolygon( part.exterior );
3787 for (
const QPolygonF &ring : part.rings )
3789 path.addPolygon( ring );
3798 context.
painter()->setClipPath( path );
3802 mMarker->renderPoint( centroid, feature.
isValid() ? &feature :
nullptr, context, -1, selected );
3814 map[QStringLiteral(
"point_on_surface" )] = QString::number(
mPointOnSurface );
3815 map[QStringLiteral(
"point_on_all_parts" )] = QString::number(
mPointOnAllParts );
3816 map[QStringLiteral(
"clip_points" )] = QString::number(
mClipPoints );
3823 std::unique_ptr< QgsCentroidFillSymbolLayer > x = qgis::make_unique< QgsCentroidFillSymbolLayer >();
3826 x->setSubSymbol(
mMarker->clone() );
3841 mMarker->toSld( doc, element, props );
3852 std::unique_ptr< QgsMarkerSymbol > marker(
new QgsMarkerSymbol( layers ) );
3854 std::unique_ptr< QgsCentroidFillSymbolLayer > sl = qgis::make_unique< QgsCentroidFillSymbolLayer >();
3855 sl->setSubSymbol( marker.release() );
3856 sl->setPointOnAllParts(
false );
3857 return sl.release();
3884 attributes.unite(
mMarker->usedAttributes( context ) );
3902 mMarker->setOutputUnit( unit );
3919 mMarker->setMapUnitScale( scale );
3927 return mMarker->mapUnitScale();
3937 , mImageFilePath( imageFilePath )
3951 if (
properties.contains( QStringLiteral(
"imageFile" ) ) )
3953 imagePath =
properties[QStringLiteral(
"imageFile" )];
3955 if (
properties.contains( QStringLiteral(
"coordinate_mode" ) ) )
3959 if (
properties.contains( QStringLiteral(
"alpha" ) ) )
3961 alpha =
properties[QStringLiteral(
"alpha" )].toDouble();
3963 if (
properties.contains( QStringLiteral(
"offset" ) ) )
3967 if (
properties.contains( QStringLiteral(
"angle" ) ) )
3971 if (
properties.contains( QStringLiteral(
"width" ) ) )
3975 std::unique_ptr< QgsRasterFillSymbolLayer > symbolLayer = qgis::make_unique< QgsRasterFillSymbolLayer >( imagePath );
3976 symbolLayer->setCoordinateMode( mode );
3977 symbolLayer->setOpacity( alpha );
3978 symbolLayer->setOffset(
offset );
3979 symbolLayer->setAngle(
angle );
3980 symbolLayer->setWidth(
width );
3981 if (
properties.contains( QStringLiteral(
"offset_unit" ) ) )
3985 if (
properties.contains( QStringLiteral(
"offset_map_unit_scale" ) ) )
3989 if (
properties.contains( QStringLiteral(
"width_unit" ) ) )
3993 if (
properties.contains( QStringLiteral(
"width_map_unit_scale" ) ) )
3998 symbolLayer->restoreOldDataDefinedProperties(
properties );
4000 return symbolLayer.release();
4005 QgsStringMap::iterator it =
properties.find( QStringLiteral(
"imageFile" ) );
4009 it.value() = pathResolver.
writePath( it.value() );
4011 it.value() = pathResolver.
readPath( it.value() );
4023 return QStringLiteral(
"RasterFill" );
4035 if ( !mOffset.isNull() )
4041 if ( mCoordinateMode ==
Feature )
4043 QRectF boundingRect = points.boundingRect();
4044 mBrush.setTransform(
mBrush.transform().translate( boundingRect.left() -
mBrush.transform().dx(),
4045 boundingRect.top() -
mBrush.transform().dy() ) );
4049 if ( !mOffset.isNull() )
4057 applyPattern(
mBrush, mImageFilePath, mWidth, mOpacity, context );
4068 map[QStringLiteral(
"imageFile" )] = mImageFilePath;
4069 map[QStringLiteral(
"coordinate_mode" )] = QString::number( mCoordinateMode );
4070 map[QStringLiteral(
"alpha" )] = QString::number( mOpacity );
4074 map[QStringLiteral(
"angle" )] = QString::number(
mAngle );
4075 map[QStringLiteral(
"width" )] = QString::number( mWidth );
4083 std::unique_ptr< QgsRasterFillSymbolLayer > sl = qgis::make_unique< QgsRasterFillSymbolLayer >( mImageFilePath );
4084 sl->setCoordinateMode( mCoordinateMode );
4085 sl->setOpacity( mOpacity );
4086 sl->setOffset( mOffset );
4087 sl->setOffsetUnit( mOffsetUnit );
4088 sl->setOffsetMapUnitScale( mOffsetMapUnitScale );
4090 sl->setWidth( mWidth );
4091 sl->setWidthUnit( mWidthUnit );
4092 sl->setWidthMapUnitScale( mWidthMapUnitScale );
4095 return sl.release();
4100 return context.
convertToPainterUnits( std::max( std::fabs( mOffset.x() ), std::fabs( mOffset.y() ) ), mOffsetUnit, mOffsetMapUnitScale );
4105 mImageFilePath = imagePath;
4110 mCoordinateMode = mode;
4128 if ( !hasWidthExpression && !hasAngleExpression && !hasOpacityExpression && !hasFileExpression )
4134 if ( hasAngleExpression )
4142 if ( !hasWidthExpression && !hasOpacityExpression && !hasFileExpression )
4147 double width = mWidth;
4148 if ( hasWidthExpression )
4154 if ( hasOpacityExpression )
4159 QString file = mImageFilePath;
4160 if ( hasFileExpression )
4168 void QgsRasterFillSymbolLayer::applyPattern( QBrush &brush,
const QString &imageFilePath,
const double width,
const double alpha,
const QgsSymbolRenderContext &context )
4181 if ( size.isEmpty() )
4184 size.setWidth( (
width * size.width() ) / 100.0 );
4187 if (
static_cast< int >( size.width() ) < 1 || 10000.0 < size.width() )
4191 size.setHeight( 0 );
4199 brush.setTextureImage( img );
4208 : mCountMethod( method )
4209 , mPointCount( pointCount )
4210 , mDensityArea( densityArea )
4219 const int pointCount =
properties.value( QStringLiteral(
"point_count" ), QStringLiteral(
"10" ) ).toInt();
4220 const double densityArea =
properties.value( QStringLiteral(
"density_area" ), QStringLiteral(
"250.0" ) ).toDouble();
4222 unsigned long seed = 0;
4223 if (
properties.contains( QStringLiteral(
"seed" ) ) )
4229 std::random_device rd;
4230 std::mt19937 mt(
seed == 0 ? rd() :
seed );
4231 std::uniform_int_distribution<> uniformDist( 1, 999999999 );
4232 seed = uniformDist( mt );
4237 if (
properties.contains( QStringLiteral(
"density_area_unit" ) ) )
4239 if (
properties.contains( QStringLiteral(
"density_area_unit_scale" ) ) )
4242 if (
properties.contains( QStringLiteral(
"clip_points" ) ) )
4244 sl->setClipPoints(
properties[QStringLiteral(
"clip_points" )].toInt() );
4247 return sl.release();
4252 return QStringLiteral(
"RandomMarkerFill" );
4257 mMarker->setColor(
color );
4263 return mMarker ? mMarker->color() :
mColor;
4268 mMarker->setOpacity( context.
opacity() );
4280 part.exterior = points;
4282 part.rings = *rings;
4284 if ( mRenderingFeature )
4288 mCurrentParts << part;
4297 void QgsRandomMarkerFillSymbolLayer::render(
QgsRenderContext &context,
const QVector<QgsRandomMarkerFillSymbolLayer::Part> &parts,
const QgsFeature &feature,
bool selected )
4306 QVector< QgsGeometry > geometryParts;
4307 geometryParts.reserve( parts.size() );
4310 for (
const Part &part : parts )
4313 if ( !geom.
isNull() && !part.rings.empty() )
4315 QgsPolygon *poly = qgsgeometry_cast< QgsPolygon * >( geom.
get() );
4316 for (
const QPolygonF &ring : part.rings )
4323 geom = geom.
buffer( 0, 0 );
4325 geometryParts << geom;
4329 path.addPolygon( part.exterior );
4330 for (
const QPolygonF &ring : part.rings )
4332 path.addPolygon( ring );
4342 context.
painter()->setClipPath( path );
4346 int count = mPointCount;
4353 switch ( mCountMethod )
4365 count = std::max( 0.0, std::ceil( count * ( geom.
area() /
densityArea ) ) );
4372 unsigned long seed = mSeed;
4383 std::sort( randomPoints.begin(), randomPoints.end(), [](
const QgsPointXY & a,
const QgsPointXY & b )->bool
4385 return a.y() < b.y();
4393 for (
const QgsPointXY &p : qgis::as_const( randomPoints ) )
4395 if ( needsExpressionContext )
4397 mMarker->renderPoint( QPointF( p.x(), p.y() ), feature.
isValid() ? &feature :
nullptr, context, -1, selected );
4409 map.insert( QStringLiteral(
"count_method" ), QString::number(
static_cast< int >( mCountMethod ) ) );
4410 map.insert( QStringLiteral(
"point_count" ), QString::number( mPointCount ) );
4411 map.insert( QStringLiteral(
"density_area" ), QString::number( mDensityArea ) );
4414 map.insert( QStringLiteral(
"seed" ), QString::number( mSeed ) );
4415 map.insert( QStringLiteral(
"clip_points" ), QString::number( mClipPoints ) );
4421 std::unique_ptr< QgsRandomMarkerFillSymbolLayer > res = qgis::make_unique< QgsRandomMarkerFillSymbolLayer >( mPointCount, mCountMethod, mDensityArea, mSeed );
4424 res->setDensityAreaUnit( mDensityAreaUnit );
4425 res->setDensityAreaUnitScale( mDensityAreaUnitScale );
4426 res->mClipPoints = mClipPoints;
4427 res->setSubSymbol( mMarker->clone() );
4430 return res.release();
4435 return mMarker.get();
4447 mColor = mMarker->color();
4456 attributes.unite( mMarker->usedAttributes( context ) );
4465 if ( mMarker && mMarker->hasDataDefinedProperties() )
4502 return mCountMethod;
4507 mCountMethod = method;
4512 return mDensityArea;
4517 mDensityArea = area;
4522 mRenderingFeature =
true;
4523 mCurrentParts.clear();
4528 mRenderingFeature =
false;
4529 render( context, mCurrentParts, feature,
false );
4537 mMarker->setOutputUnit( unit );
4545 return mMarker->outputUnit();
4554 mMarker->setMapUnitScale( scale );
4562 return mMarker->mapUnitScale();