Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add tests
  • Loading branch information
elpaso committed Jan 13, 2023
1 parent bf1c4f8 commit 7a3abbd
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 87 deletions.
Expand Up @@ -186,7 +186,7 @@ Returns the map unit scale for the fill's offset.

virtual Qt::BrushStyle dxfBrushStyle() const;

virtual QImage toTiledPattern( ) const;
virtual QImage toTiledPatternImage( ) const;


protected:
Expand Down Expand Up @@ -1549,7 +1549,7 @@ ownership of the returned object.

virtual void toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const;

virtual QImage toTiledPattern( ) const;
virtual QImage toTiledPatternImage( ) const;

virtual double estimateMaxBleed( const QgsRenderContext &context ) const;

Expand Down Expand Up @@ -1846,7 +1846,7 @@ Caller takes ownership of the returned symbol layer.

virtual void toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const;

virtual QImage toTiledPattern( ) const;
virtual QImage toTiledPatternImage( ) const;

virtual double estimateMaxBleed( const QgsRenderContext &context ) const;

Expand Down
7 changes: 5 additions & 2 deletions python/core/auto_generated/symbology/qgssymbollayer.sip.in
Expand Up @@ -1313,9 +1313,12 @@ The ``rings`` argument optionally specifies a list of polygon rings to render as
void setAngle( double angle );
double angle() const;

virtual QImage toTiledPattern( ) const;
virtual QImage toTiledPatternImage( ) const;
%Docstring
Renders the symbol layer to an image that can be used as a seamless pattern fill.
Renders the symbol layer to an image that can be used as a seamless pattern fill
for polygons, this method is used by SLD export to generate image tiles for
ExternalGraphic polygon fills.

The default implementation returns a null image.

:return: the tile image (not necessarily a square) or a null image if not implemented.
Expand Down
152 changes: 80 additions & 72 deletions src/core/symbology/qgsfillsymbollayer.cpp
Expand Up @@ -413,7 +413,7 @@ void QgsSimpleFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, c
bool exportOk { false };
if ( ! context.exportFilePath().isEmpty() && context.exportOptions().testFlag( Qgis::SldExportOption::Png ) && mBrush.style() != Qt::NoBrush )
{
const QImage image { toTiledPattern( ) };
const QImage image { toTiledPatternImage( ) };
if ( ! image.isNull() )
{
QDomElement graphicFillElem = doc.createElement( QStringLiteral( "se:GraphicFill" ) );
Expand Down Expand Up @@ -570,10 +570,8 @@ Qt::BrushStyle QgsSimpleFillSymbolLayer::dxfBrushStyle() const
return mBrushStyle;
}

QImage QgsSimpleFillSymbolLayer::toTiledPattern( ) const
QImage QgsSimpleFillSymbolLayer::toTiledPatternImage( ) const
{
// TODO: calculate size, e.g. for solid brush a 1x1 image is sufficient
// for other brush styles must be calculated.
QPixmap pixmap( QSize( 32, 32 ) );
pixmap.fill( Qt::transparent );
QPainter painter;
Expand Down Expand Up @@ -2640,7 +2638,7 @@ void QgsLinePatternFillSymbolLayer::stopFeatureRender( const QgsFeature &, QgsRe
// deliberately don't pass this on to subsymbol here
}

QImage QgsLinePatternFillSymbolLayer::toTiledPattern() const
QImage QgsLinePatternFillSymbolLayer::toTiledPatternImage() const
{

double lineAngleRads { qDegreesToRadians( mLineAngle ) };
Expand Down Expand Up @@ -3413,7 +3411,7 @@ void QgsLinePatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &eleme
bool exportOk { false };
if ( ! context.exportFilePath().isEmpty() && context.exportOptions().testFlag( Qgis::SldExportOption::Png ) )
{
const QImage image { toTiledPattern( ) };
const QImage image { toTiledPatternImage() };
if ( ! image.isNull() )
{
const QFileInfo info { context.exportFilePath() };
Expand Down Expand Up @@ -4361,7 +4359,7 @@ void QgsPointPatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &elem
bool exportOk { false };
if ( ! context.exportFilePath().isEmpty() && context.exportOptions().testFlag( Qgis::SldExportOption::Png ) )
{
const QImage image { toTiledPattern( ) };
const QImage image { toTiledPatternImage( ) };
if ( ! image.isNull() )
{
QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
Expand Down Expand Up @@ -4408,7 +4406,7 @@ void QgsPointPatternFillSymbolLayer::toSld( QDomDocument &doc, QDomElement &elem
}
}

QImage QgsPointPatternFillSymbolLayer::toTiledPattern() const
QImage QgsPointPatternFillSymbolLayer::toTiledPatternImage() const
{

double angleRads { qDegreesToRadians( mAngle ) };
Expand Down Expand Up @@ -4472,92 +4470,102 @@ QgsSymbolLayer *QgsPointPatternFillSymbolLayer::createFromSld( QDomElement &elem
std::unique_ptr< QgsPointPatternFillSymbolLayer > pointPatternFillSl = std::make_unique< QgsPointPatternFillSymbolLayer >();
pointPatternFillSl->setSubSymbol( marker.release() );

// Set distance X and Y from vendor options
QgsStringMap vendorOptions = QgsSymbolLayerUtils::getVendorOptionList( element );
for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
auto distanceParser = [ & ]( const QStringList & values )
{
if ( it.key() == QLatin1String( "graphic-margin" ) )
{

// This may not be correct in all cases, TODO: check "uom"
pointPatternFillSl->setDistanceXUnit( QgsUnitTypes::RenderUnit::RenderPixels );
pointPatternFillSl->setDistanceYUnit( QgsUnitTypes::RenderUnit::RenderPixels );

const QStringList values = it.value().split( ' ' );
// This may not be correct in all cases, TODO: check "uom"
pointPatternFillSl->setDistanceXUnit( QgsUnitTypes::RenderUnit::RenderPixels );
pointPatternFillSl->setDistanceYUnit( QgsUnitTypes::RenderUnit::RenderPixels );

switch ( values.count( ) )
switch ( values.count( ) )
{
case 1: // top-right-bottom-left (single value for all four margins)
{
case 1: // top-right-bottom-left (single value for all four margins)
bool ok;
const double v { values.at( 0 ).toDouble( &ok ) };
if ( ok )
{
bool ok;
const double v { values.at( 0 ).toDouble( &ok ) };
if ( ok )
{
pointPatternFillSl->setDistanceX( v * 2 + markerSize );
pointPatternFillSl->setDistanceY( v * 2 + markerSize );
}
break;
pointPatternFillSl->setDistanceX( v * 2 + markerSize );
pointPatternFillSl->setDistanceY( v * 2 + markerSize );
}
case 2: // top-bottom,right-left (two values, top and bottom sharing the same value)
break;
}
case 2: // top-bottom,right-left (two values, top and bottom sharing the same value)
{
bool ok;
const double vX { values.at( 1 ).toDouble( &ok ) };
if ( ok )
{
bool ok;
const double vX { values.at( 1 ).toDouble( &ok ) };
if ( ok )
{
pointPatternFillSl->setDistanceX( vX * 2 + markerSize );
}
const double vY { values.at( 0 ).toDouble( &ok ) };
if ( ok )
{
pointPatternFillSl->setDistanceY( vY * 2 + markerSize );
}
break;
pointPatternFillSl->setDistanceX( vX * 2 + markerSize );
}
case 3: // top,right-left,bottom (three values, with right and left sharing the same value)
const double vY { values.at( 0 ).toDouble( &ok ) };
if ( ok )
{
bool ok;
const double vX { values.at( 1 ).toDouble( &ok ) };
if ( ok )
{
pointPatternFillSl->setDistanceX( vX * 2 + markerSize );
}
const double vYt { values.at( 0 ).toDouble( &ok ) };
pointPatternFillSl->setDistanceY( vY * 2 + markerSize );
}
break;
}
case 3: // top,right-left,bottom (three values, with right and left sharing the same value)
{
bool ok;
const double vX { values.at( 1 ).toDouble( &ok ) };
if ( ok )
{
pointPatternFillSl->setDistanceX( vX * 2 + markerSize );
}
const double vYt { values.at( 0 ).toDouble( &ok ) };
if ( ok )
{
const double vYb { values.at( 2 ).toDouble( &ok ) };
if ( ok )
{
const double vYb { values.at( 2 ).toDouble( &ok ) };
if ( ok )
{
pointPatternFillSl->setDistanceY( ( vYt + vYb ) + markerSize );
}
pointPatternFillSl->setDistanceY( ( vYt + vYb ) + markerSize );
}
break;
}
case 4: // top,right,bottom,left (one explicit value per margin)
break;
}
case 4: // top,right,bottom,left (one explicit value per margin)
{
bool ok;
const double vYt { values.at( 0 ).toDouble( &ok ) };
if ( ok )
{
bool ok;
const double vYt { values.at( 0 ).toDouble( &ok ) };
const double vYb { values.at( 2 ).toDouble( &ok ) };
if ( ok )
{
const double vYb { values.at( 2 ).toDouble( &ok ) };
if ( ok )
{
pointPatternFillSl->setDistanceY( ( vYt + vYb ) + markerSize );
}
pointPatternFillSl->setDistanceY( ( vYt + vYb ) + markerSize );
}
const double vXr { values.at( 1 ).toDouble( &ok ) };
}
const double vXr { values.at( 1 ).toDouble( &ok ) };
if ( ok )
{
const double vXl { values.at( 3 ).toDouble( &ok ) };
if ( ok )
{
const double vXl { values.at( 3 ).toDouble( &ok ) };
if ( ok )
{
pointPatternFillSl->setDistanceX( ( vXr + vXl ) + markerSize );
}
pointPatternFillSl->setDistanceX( ( vXr + vXl ) + markerSize );
}
break;
}
default:
break;
break;
}
default:
break;
}
};

// Set distance X and Y from vendor options
QgsStringMap vendorOptions = QgsSymbolLayerUtils::getVendorOptionList( element );
for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
{
// Legacy
if ( it.key() == QLatin1String( "distance" ) )
{
distanceParser( it.value().split( ',' ) );
}
// GeoServer
else if ( it.key() == QLatin1String( "graphic-margin" ) )
{
distanceParser( it.value().split( ' ' ) );

}
}
return pointPatternFillSl.release();
Expand Down
6 changes: 3 additions & 3 deletions src/core/symbology/qgsfillsymbollayer.h
Expand Up @@ -179,7 +179,7 @@ class CORE_EXPORT QgsSimpleFillSymbolLayer : public QgsFillSymbolLayer
Qt::PenStyle dxfPenStyle() const override;
QColor dxfBrushColor( QgsSymbolRenderContext &context ) const override;
Qt::BrushStyle dxfBrushStyle() const override;
QImage toTiledPattern( ) const override;
QImage toTiledPatternImage( ) const override;

protected:
QBrush mBrush;
Expand Down Expand Up @@ -1419,7 +1419,7 @@ class CORE_EXPORT QgsLinePatternFillSymbolLayer: public QgsImageFillSymbolLayer
QVariantMap properties() const override;
QgsLinePatternFillSymbolLayer *clone() const override SIP_FACTORY;
void toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const override;
QImage toTiledPattern( ) const override;
QImage toTiledPatternImage( ) const override;
double estimateMaxBleed( const QgsRenderContext &context ) const override;

QString ogrFeatureStyleWidth( double widthScaleFactor ) const;
Expand Down Expand Up @@ -1691,7 +1691,7 @@ class CORE_EXPORT QgsPointPatternFillSymbolLayer: public QgsImageFillSymbolLayer
QVariantMap properties() const override;
QgsPointPatternFillSymbolLayer *clone() const override SIP_FACTORY;
void toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const override;
QImage toTiledPattern( ) const override;
QImage toTiledPatternImage( ) const override;
double estimateMaxBleed( const QgsRenderContext &context ) const override;
bool setSubSymbol( QgsSymbol *symbol SIP_TRANSFER ) override;
QgsSymbol *subSymbol() override;
Expand Down
2 changes: 1 addition & 1 deletion src/core/symbology/qgssymbollayer.cpp
Expand Up @@ -844,7 +844,7 @@ void QgsFillSymbolLayer::drawPreviewIcon( QgsSymbolRenderContext &context, QSize
stopRender( context );
}

QImage QgsFillSymbolLayer::toTiledPattern() const
QImage QgsFillSymbolLayer::toTiledPatternImage() const
{
return QImage();
}
Expand Down
7 changes: 5 additions & 2 deletions src/core/symbology/qgssymbollayer.h
Expand Up @@ -1242,13 +1242,16 @@ class CORE_EXPORT QgsFillSymbolLayer : public QgsSymbolLayer
double angle() const { return mAngle; }

/**
* Renders the symbol layer to an image that can be used as a seamless pattern fill.
* Renders the symbol layer to an image that can be used as a seamless pattern fill
* for polygons, this method is used by SLD export to generate image tiles for
* ExternalGraphic polygon fills.
*
* The default implementation returns a null image.
*
* \return the tile image (not necessarily a square) or a null image if not implemented.
* \since QGIS 3.30
*/
virtual QImage toTiledPattern( ) const;
virtual QImage toTiledPatternImage( ) const;

protected:
QgsFillSymbolLayer( bool locked = false );
Expand Down

0 comments on commit 7a3abbd

Please sign in to comment.