[FEATURE] Raster image marker

This commit is contained in:
nirvn 2018-12-01 16:03:10 +07:00 committed by Mathieu Pellerin
parent a8be1ff37c
commit c6425338ee
20 changed files with 1818 additions and 0 deletions

View File

@ -697,6 +697,166 @@ Calculates the marker aspect ratio between width and height.
class QgsRasterMarkerSymbolLayer : QgsMarkerSymbolLayer
{
%Docstring
Raster marker symbol layer class.
.. versionadded:: 3.6
%End
%TypeHeaderCode
#include "qgsmarkersymbollayer.h"
%End
public:
QgsRasterMarkerSymbolLayer( const QString &path = QString(),
double size = DEFAULT_SVGMARKER_SIZE,
double angle = DEFAULT_SVGMARKER_ANGLE,
QgsSymbol::ScaleMethod scaleMethod = DEFAULT_SCALE_METHOD );
%Docstring
Constructs raster marker symbol layer with picture from given absolute path to a raster image file
%End
static QgsSymbolLayer *create( const QgsStringMap &properties = QgsStringMap() ) /Factory/;
%Docstring
Creates a raster marker symbol layer from a string map of properties.
:param properties: QgsStringMap properties object
%End
static void resolvePaths( QgsStringMap &properties, const QgsPathResolver &pathResolver, bool saving );
%Docstring
Turns relative paths in properties map to absolute when reading and vice versa when writing.
Used internally when reading/writing symbols.
.. versionadded:: 3.0
%End
virtual QString layerType() const;
virtual void renderPoint( QPointF point, QgsSymbolRenderContext &context );
virtual QgsStringMap properties() const;
virtual QgsRasterMarkerSymbolLayer *clone() const /Factory/;
double calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const;
%Docstring
Calculates the marker aspect ratio between width and height.
:param context: symbol render context
:param scaledSize: size of symbol to render
:param hasDataDefinedAspectRatio: will be set to true if marker has data defined aspectRatio
%End
QString path() const;
%Docstring
Returns the marker raster image path.
.. seealso:: :py:func:`setPath`
%End
void setPath( const QString &path );
%Docstring
Set the marker raster image path.
:param path: raster image path
.. seealso:: :py:func:`path`
%End
double opacity() const;
%Docstring
Returns the marker opacity.
:return: opacity value between 0 (fully transparent) and 1 (fully opaque)
.. seealso:: :py:func:`setOpacity`
%End
void setOpacity( double opacity );
%Docstring
Set the marker opacity.
:param opacity: opacity value between 0 (fully transparent) and 1 (fully opaque)
.. seealso:: :py:func:`opacity`
%End
double defaultAspectRatio() const;
%Docstring
Returns the default marker aspect ratio between width and height, 0 if not yet calculated.
.. seealso:: :py:func:`updateDefaultAspectRatio`
%End
double updateDefaultAspectRatio();
%Docstring
Calculates the default marker aspect ratio between width and height.
:return: the default aspect ratio value
.. seealso:: :py:func:`defaultAspectRatio`
%End
bool preservedAspectRatio() const;
%Docstring
Returns the preserved aspect ratio value, true if fixed aspect ratio has been lower or equal to 0.
.. seealso:: :py:func:`setPreservedAspectRatio`
%End
bool setPreservedAspectRatio( bool par );
%Docstring
Set preserved the marker aspect ratio between width and height.
:param par: Preserved Aspect Ratio
:return: the preserved aspect ratio value, true if fixed aspect ratio has been lower or equal to 0
.. seealso:: :py:func:`preservedAspectRatio`
%End
double fixedAspectRatio() const;
%Docstring
Returns the marker aspect ratio between width and height to be used in rendering,
if the value set is lower or equal to 0 the aspect ratio will be preserved in rendering
.. seealso:: :py:func:`setFixedAspectRatio`
%End
void setFixedAspectRatio( double ratio );
%Docstring
Set the marker aspect ratio between width and height to be used in rendering,
if the value set is lower or equal to 0 the aspect ratio will be preserved in rendering
:param ratio: Fixed Aspect Ratio
.. seealso:: :py:func:`fixedAspectRatio`
%End
virtual void setMapUnitScale( const QgsMapUnitScale &scale );
virtual QgsMapUnitScale mapUnitScale() const;
virtual QRectF bounds( QPointF point, QgsSymbolRenderContext &context );
protected:
};
class QgsFontMarkerSymbolLayer : QgsMarkerSymbolLayer
{

View File

@ -33,6 +33,8 @@ class QgsSymbolLayer
sipType = sipType_QgsFilledMarkerSymbolLayer;
else if ( sipCpp->layerType() == "SvgMarker" )
sipType = sipType_QgsSvgMarkerSymbolLayer;
else if ( sipCpp->layerType() == "RasterMarker" )
sipType = sipType_QgsRasterMarkerSymbolLayer;
else if ( sipCpp->layerType() == "VectorField" )
sipType = sipType_QgsVectorFieldSymbolLayer;
else
@ -463,6 +465,9 @@ Abstract base class for marker symbol layers.
virtual void startRender( QgsSymbolRenderContext &context );
virtual void stopRender( QgsSymbolRenderContext &context );
virtual void renderPoint( QPointF point, QgsSymbolRenderContext &context ) = 0;
%Docstring
Renders a marker at the specified point. Derived classes must implement this to

View File

@ -417,6 +417,47 @@ Creates a new QgsSvgMarkerSymbolLayerWidget.
class QgsRasterMarkerSymbolLayerWidget : QgsSymbolLayerWidget
{
%Docstring
Widget for configuring QgsRasterMarkerSymbolLayer symbol layers.
.. versionadded:: 3.6
%End
%TypeHeaderCode
#include "qgssymbollayerwidget.h"
%End
public:
QgsRasterMarkerSymbolLayerWidget( QgsVectorLayer *vl, QWidget *parent /TransferThis/ = 0 );
%Docstring
Constructor for QgsRasterMarkerSymbolLayerWidget.
:param vl: associated vector layer
:param parent: parent widget
%End
static QgsSymbolLayerWidget *create( QgsVectorLayer *vl ) /Factory/;
%Docstring
Creates a new QgsRasterMarkerSymbolLayerWidget.
:param vl: associated vector layer
%End
virtual void setSymbolLayer( QgsSymbolLayer *layer );
virtual QgsSymbolLayer *symbolLayer();
protected:
};
class QgsRasterFillSymbolLayerWidget : QgsSymbolLayerWidget
{

View File

@ -132,6 +132,7 @@ QImage QgsImageCache::pathAsImage( const QString &file, const QSize size, const
}
else
{
QgsDebugMsg( "HITTTTTTTTTTTTTTTTTTTTTTTTT!!!!!!!!!!!!!!" );
result = currentEntry->image;
}

View File

@ -19,6 +19,8 @@
#include "qgsdxfexport.h"
#include "qgsdxfpaintdevice.h"
#include "qgsexpression.h"
#include "qgsimagecache.h"
#include "qgsimageoperation.h"
#include "qgsrendercontext.h"
#include "qgslogger.h"
#include "qgssvgcache.h"
@ -2545,6 +2547,377 @@ QRectF QgsSvgMarkerSymbolLayer::bounds( QPointF point, QgsSymbolRenderContext &c
//////////
QgsRasterMarkerSymbolLayer::QgsRasterMarkerSymbolLayer( const QString &path, double size, double angle, QgsSymbol::ScaleMethod scaleMethod )
: mPath( path )
{
mSize = size;
mAngle = angle;
mOffset = QPointF( 0, 0 );
mScaleMethod = scaleMethod;
updateDefaultAspectRatio();
}
QgsSymbolLayer *QgsRasterMarkerSymbolLayer::create( const QgsStringMap &props )
{
QString path;
double size = DEFAULT_RASTERMARKER_SIZE;
double angle = DEFAULT_RASTERMARKER_ANGLE;
QgsSymbol::ScaleMethod scaleMethod = DEFAULT_SCALE_METHOD;
if ( props.contains( QStringLiteral( "imageFile" ) ) )
path = props[QStringLiteral( "imageFile" )];
if ( props.contains( QStringLiteral( "size" ) ) )
size = props[QStringLiteral( "size" )].toDouble();
if ( props.contains( QStringLiteral( "angle" ) ) )
angle = props[QStringLiteral( "angle" )].toDouble();
if ( props.contains( QStringLiteral( "scale_method" ) ) )
scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )] );
QgsRasterMarkerSymbolLayer *m = new QgsRasterMarkerSymbolLayer( path, size, angle, scaleMethod );
if ( props.contains( QStringLiteral( "alpha" ) ) )
{
m->setOpacity( props[QStringLiteral( "alpha" )].toDouble() );
}
if ( props.contains( QStringLiteral( "size_unit" ) ) )
m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
if ( props.contains( QStringLiteral( "offset" ) ) )
m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
if ( props.contains( QStringLiteral( "offset_unit" ) ) )
m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
{
m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
}
if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
{
m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
}
m->restoreOldDataDefinedProperties( props );
m->updateDefaultAspectRatio();
return m;
}
void QgsRasterMarkerSymbolLayer::resolvePaths( QgsStringMap &properties, const QgsPathResolver &pathResolver, bool saving )
{
QgsStringMap::iterator it = properties.find( QStringLiteral( "name" ) );
if ( it != properties.end() )
{
if ( saving )
it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value(), pathResolver );
else
it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value(), pathResolver );
}
}
void QgsRasterMarkerSymbolLayer::setPath( const QString &path )
{
mPath = path;
updateDefaultAspectRatio();
}
bool QgsRasterMarkerSymbolLayer::setPreservedAspectRatio( bool par )
{
bool aPreservedAspectRatio = preservedAspectRatio();
if ( aPreservedAspectRatio && !par )
{
mFixedAspectRatio = mDefaultAspectRatio;
}
else if ( !aPreservedAspectRatio && par )
{
mFixedAspectRatio = 0.0;
}
return preservedAspectRatio();
}
double QgsRasterMarkerSymbolLayer::updateDefaultAspectRatio()
{
if ( mDefaultAspectRatio == 0.0 )
{
QSize size = QgsApplication::imageCache()->originalSize( mPath );
mDefaultAspectRatio = ( !size.isNull() && size.isValid() && size.width() > 0 ) ? static_cast< double >( size.height() ) / static_cast< double >( size.width() ) : 0.0;
}
return mDefaultAspectRatio;
}
QString QgsRasterMarkerSymbolLayer::layerType() const
{
return QStringLiteral( "RasterMarker" );
}
void QgsRasterMarkerSymbolLayer::renderPoint( QPointF point, QgsSymbolRenderContext &context )
{
QPainter *p = context.renderContext().painter();
if ( !p )
return;
bool hasDataDefinedSize = false;
double scaledSize = calculateSize( context, hasDataDefinedSize );
double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
bool hasDataDefinedAspectRatio = false;
double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
//don't render symbols with size below one or above 10,000 pixels
if ( static_cast< int >( width ) < 1 || 10000.0 < width )
{
return;
}
p->save();
QString path = mPath;
if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyName ) )
{
context.setOriginalValueVariable( mPath );
path = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyName, context.renderContext().expressionContext(), mPath );
if ( preservedAspectRatio() && path != mPath )
{
QSize size = QgsApplication::imageCache()->originalSize( path );
if ( !size.isNull() && size.isValid() && size.width() > 0 )
{
height = width * ( static_cast< double >( size.height() ) / static_cast< double >( size.width() ) );
}
}
}
QPointF outputOffset;
double angle = 0.0;
calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
p->translate( point + outputOffset );
bool rotated = !qgsDoubleNear( angle, 0 );
if ( rotated )
p->rotate( angle );
double opacity = mOpacity;
if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyOpacity ) )
{
context.setOriginalValueVariable( mOpacity );
opacity = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyOpacity, context.renderContext().expressionContext(), opacity * 100 ) / 100.0;
}
opacity *= context.opacity();
bool cached;
QImage img = QgsApplication::imageCache()->pathAsImage( path, QSize( width, preservedAspectRatio() ? 0 : width * aspectRatio ), preservedAspectRatio(), opacity, cached );
if ( !img.isNull() )
{
if ( context.selected() )
QgsImageOperation::adjustHueSaturation( img, 1.0, context.renderContext().selectionColor(), 1.0 );
p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
}
p->restore();
}
double QgsRasterMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
{
double scaledSize = mSize;
hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
bool ok = true;
if ( hasDataDefinedSize )
{
context.setOriginalValueVariable( mSize );
scaledSize = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertySize, context.renderContext().expressionContext(), mSize, &ok );
}
else
{
hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyWidth );
if ( hasDataDefinedSize )
{
context.setOriginalValueVariable( mSize );
scaledSize = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyWidth, context.renderContext().expressionContext(), mSize, &ok );
}
}
if ( hasDataDefinedSize && ok )
{
switch ( mScaleMethod )
{
case QgsSymbol::ScaleArea:
scaledSize = std::sqrt( scaledSize );
break;
case QgsSymbol::ScaleDiameter:
break;
}
}
return scaledSize;
}
double QgsRasterMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
{
hasDataDefinedAspectRatio = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyWidth ) || mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyHeight );
if ( !hasDataDefinedAspectRatio )
return mFixedAspectRatio;
if ( !mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyHeight ) && mFixedAspectRatio <= 0.0 )
return 0.0;
double scaledAspectRatio = mDefaultAspectRatio;
if ( mFixedAspectRatio > 0.0 )
scaledAspectRatio = mFixedAspectRatio;
double defaultHeight = mSize * scaledAspectRatio;
scaledAspectRatio = defaultHeight / scaledSize;
bool ok = true;
double scaledHeight = scaledSize * scaledAspectRatio;
if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyHeight ) )
{
context.setOriginalValueVariable( defaultHeight );
scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyHeight, context.renderContext().expressionContext(), defaultHeight, &ok );
}
if ( hasDataDefinedAspectRatio && ok )
{
switch ( mScaleMethod )
{
case QgsSymbol::ScaleArea:
scaledHeight = sqrt( scaledHeight );
break;
case QgsSymbol::ScaleDiameter:
break;
}
}
scaledAspectRatio = scaledHeight / scaledSize;
return scaledAspectRatio;
}
void QgsRasterMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
{
//offset
double offsetX = 0;
double offsetY = 0;
markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
offset = QPointF( offsetX, offsetY );
angle = mAngle + mLineAngle;
if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyAngle ) )
{
context.setOriginalValueVariable( mAngle );
angle = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyAngle, context.renderContext().expressionContext(), mAngle ) + mLineAngle;
}
bool hasDataDefinedRotation = context.renderHints() & QgsSymbol::DynamicRotation || mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyAngle );
if ( hasDataDefinedRotation )
{
const QgsFeature *f = context.feature();
if ( f )
{
if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
{
const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
angle += m2p.mapRotation();
}
}
}
if ( angle )
offset = _rotatedOffset( offset, angle );
}
QgsStringMap QgsRasterMarkerSymbolLayer::properties() const
{
QgsStringMap map;
map[QStringLiteral( "imageFile" )] = mPath;
map[QStringLiteral( "size" )] = QString::number( mSize );
map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
map[QStringLiteral( "angle" )] = QString::number( mAngle );
map[QStringLiteral( "alpha" )] = QString::number( mOpacity );
map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
return map;
}
QgsRasterMarkerSymbolLayer *QgsRasterMarkerSymbolLayer::clone() const
{
QgsRasterMarkerSymbolLayer *m = new QgsRasterMarkerSymbolLayer( mPath, mSize, mAngle );
m->setFixedAspectRatio( mFixedAspectRatio );
m->setOpacity( mOpacity );
m->setOffset( mOffset );
m->setOffsetUnit( mOffsetUnit );
m->setOffsetMapUnitScale( mOffsetMapUnitScale );
m->setSizeUnit( mSizeUnit );
m->setSizeMapUnitScale( mSizeMapUnitScale );
m->setHorizontalAnchorPoint( mHorizontalAnchorPoint );
m->setVerticalAnchorPoint( mVerticalAnchorPoint );
copyDataDefinedProperties( m );
copyPaintEffect( m );
return m;
}
void QgsRasterMarkerSymbolLayer::setMapUnitScale( const QgsMapUnitScale &scale )
{
QgsMarkerSymbolLayer::setMapUnitScale( scale );
}
QgsMapUnitScale QgsRasterMarkerSymbolLayer::mapUnitScale() const
{
return QgsMarkerSymbolLayer::mapUnitScale();
}
QRectF QgsRasterMarkerSymbolLayer::bounds( QPointF point, QgsSymbolRenderContext &context )
{
bool hasDataDefinedSize = false;
double scaledSize = calculateSize( context, hasDataDefinedSize );
double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
bool hasDataDefinedAspectRatio = false;
double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
//don't render symbols with size below one or above 10,000 pixels
if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
{
return QRectF();
}
QPointF outputOffset;
double angle = 0.0;
calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
QMatrix transform;
// move to the desired position
transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
if ( !qgsDoubleNear( angle, 0.0 ) )
transform.rotate( angle );
QRectF symbolBounds = transform.mapRect( QRectF( -width / 2.0,
-height / 2.0,
width,
height ) );
return symbolBounds;
}
//////////
QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QChar chr, double pointSize, const QColor &color, double angle )
{
mFontFamily = fontFamily;

View File

@ -636,6 +636,150 @@ class CORE_EXPORT QgsSvgMarkerSymbolLayer : public QgsMarkerSymbolLayer
};
//////////
#define DEFAULT_RASTERMARKER_SIZE 2*DEFAULT_POINT_SIZE
#define DEFAULT_RASTERMARKER_ANGLE 0
/**
* \ingroup core
* \class QgsRasterMarkerSymbolLayer
* \brief Raster marker symbol layer class.
* \since QGIS 3.6
*/
class CORE_EXPORT QgsRasterMarkerSymbolLayer : public QgsMarkerSymbolLayer
{
public:
//! Constructs raster marker symbol layer with picture from given absolute path to a raster image file
QgsRasterMarkerSymbolLayer( const QString &path = QString(),
double size = DEFAULT_SVGMARKER_SIZE,
double angle = DEFAULT_SVGMARKER_ANGLE,
QgsSymbol::ScaleMethod scaleMethod = DEFAULT_SCALE_METHOD );
// static stuff
/**
* Creates a raster marker symbol layer from a string map of properties.
* \param properties QgsStringMap properties object
*/
static QgsSymbolLayer *create( const QgsStringMap &properties = QgsStringMap() ) SIP_FACTORY;
/**
* Turns relative paths in properties map to absolute when reading and vice versa when writing.
* Used internally when reading/writing symbols.
* \since QGIS 3.0
*/
static void resolvePaths( QgsStringMap &properties, const QgsPathResolver &pathResolver, bool saving );
// implemented from base classes
QString layerType() const override;
void renderPoint( QPointF point, QgsSymbolRenderContext &context ) override;
QgsStringMap properties() const override;
QgsRasterMarkerSymbolLayer *clone() const override SIP_FACTORY;
/**
* Calculates the marker aspect ratio between width and height.
* \param context symbol render context
* \param scaledSize size of symbol to render
* \param hasDataDefinedAspectRatio will be set to true if marker has data defined aspectRatio
*/
double calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const;
/**
* Returns the marker raster image path.
* \see setPath()
*/
QString path() const { return mPath; }
/**
* Set the marker raster image path.
* \param path raster image path
* \see path()
*/
void setPath( const QString &path );
/**
* Returns the marker opacity.
* \returns opacity value between 0 (fully transparent) and 1 (fully opaque)
* \see setOpacity()
*/
double opacity() const { return mOpacity; }
/**
* Set the marker opacity.
* \param opacity opacity value between 0 (fully transparent) and 1 (fully opaque)
* \see opacity()
*/
void setOpacity( double opacity ) { mOpacity = opacity; }
/**
* Returns the default marker aspect ratio between width and height, 0 if not yet calculated.
* \see updateDefaultAspectRatio()
*/
double defaultAspectRatio() const { return mDefaultAspectRatio; }
/**
* Calculates the default marker aspect ratio between width and height.
* \returns the default aspect ratio value
* \see defaultAspectRatio()
*/
double updateDefaultAspectRatio();
/**
* Returns the preserved aspect ratio value, true if fixed aspect ratio has been lower or equal to 0.
* \see setPreservedAspectRatio()
*/
bool preservedAspectRatio() const { return mFixedAspectRatio <= 0.0; }
/**
* Set preserved the marker aspect ratio between width and height.
* \param par Preserved Aspect Ratio
* \returns the preserved aspect ratio value, true if fixed aspect ratio has been lower or equal to 0
* \see preservedAspectRatio()
*/
bool setPreservedAspectRatio( bool par );
/**
* Returns the marker aspect ratio between width and height to be used in rendering,
* if the value set is lower or equal to 0 the aspect ratio will be preserved in rendering
* \see setFixedAspectRatio() QgsSvgCache
*/
double fixedAspectRatio() const { return mFixedAspectRatio; }
/**
* Set the marker aspect ratio between width and height to be used in rendering,
* if the value set is lower or equal to 0 the aspect ratio will be preserved in rendering
* \param ratio Fixed Aspect Ratio
* \see fixedAspectRatio() QgsSvgCache
*/
void setFixedAspectRatio( double ratio ) { mFixedAspectRatio = ratio; }
void setMapUnitScale( const QgsMapUnitScale &scale ) override;
QgsMapUnitScale mapUnitScale() const override;
QRectF bounds( QPointF point, QgsSymbolRenderContext &context ) override;
protected:
QString mPath;
//! The marker default opacity
double mOpacity = 1.0;
//! The marker default aspect ratio
double mDefaultAspectRatio = 0.0;
//! The marker fixed aspect ratio
double mFixedAspectRatio = 0.0;
private:
double calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const;
void calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const;
};
//////////
#define POINT2MM(x) ( (x) * 25.4 / 72 ) // point is 1/72 of inch

View File

@ -411,6 +411,11 @@ void QgsMarkerSymbolLayer::startRender( QgsSymbolRenderContext &context )
Q_UNUSED( context );
}
void QgsMarkerSymbolLayer::stopRender( QgsSymbolRenderContext &context )
{
Q_UNUSED( context );
}
void QgsMarkerSymbolLayer::drawPreviewIcon( QgsSymbolRenderContext &context, QSize size )
{
startRender( context );

View File

@ -70,6 +70,8 @@ class CORE_EXPORT QgsSymbolLayer
sipType = sipType_QgsFilledMarkerSymbolLayer;
else if ( sipCpp->layerType() == "SvgMarker" )
sipType = sipType_QgsSvgMarkerSymbolLayer;
else if ( sipCpp->layerType() == "RasterMarker" )
sipType = sipType_QgsRasterMarkerSymbolLayer;
else if ( sipCpp->layerType() == "VectorField" )
sipType = sipType_QgsVectorFieldSymbolLayer;
else
@ -476,6 +478,8 @@ class CORE_EXPORT QgsMarkerSymbolLayer : public QgsSymbolLayer
void startRender( QgsSymbolRenderContext &context ) override;
void stopRender( QgsSymbolRenderContext &context ) override;
/**
* Renders a marker at the specified point. Derived classes must implement this to
* handle drawing the point.

View File

@ -38,6 +38,8 @@ QgsSymbolLayerRegistry::QgsSymbolLayerRegistry()
QgsFilledMarkerSymbolLayer::create ) );
addSymbolLayerType( new QgsSymbolLayerMetadata( QStringLiteral( "SvgMarker" ), QObject::tr( "SVG marker" ), QgsSymbol::Marker,
QgsSvgMarkerSymbolLayer::create, QgsSvgMarkerSymbolLayer::createFromSld, QgsSvgMarkerSymbolLayer::resolvePaths ) );
addSymbolLayerType( new QgsSymbolLayerMetadata( QStringLiteral( "RasterMarker" ), QObject::tr( "Raster image marker" ), QgsSymbol::Marker,
QgsRasterMarkerSymbolLayer::create, nullptr, QgsRasterFillSymbolLayer::resolvePaths ) );
addSymbolLayerType( new QgsSymbolLayerMetadata( QStringLiteral( "FontMarker" ), QObject::tr( "Font marker" ), QgsSymbol::Marker,
QgsFontMarkerSymbolLayer::create, QgsFontMarkerSymbolLayer::createFromSld ) );
addSymbolLayerType( new QgsSymbolLayerMetadata( QStringLiteral( "EllipseMarker" ), QObject::tr( "Ellipse marker" ), QgsSymbol::Marker,

View File

@ -70,6 +70,7 @@ static void _initWidgetFunctions()
_initWidgetFunction( QStringLiteral( "SimpleMarker" ), QgsSimpleMarkerSymbolLayerWidget::create );
_initWidgetFunction( QStringLiteral( "FilledMarker" ), QgsFilledMarkerSymbolLayerWidget::create );
_initWidgetFunction( QStringLiteral( "SvgMarker" ), QgsSvgMarkerSymbolLayerWidget::create );
_initWidgetFunction( QStringLiteral( "RasterMarker" ), QgsRasterMarkerSymbolLayerWidget::create );
_initWidgetFunction( QStringLiteral( "FontMarker" ), QgsFontMarkerSymbolLayerWidget::create );
_initWidgetFunction( QStringLiteral( "EllipseMarker" ), QgsEllipseSymbolLayerWidget::create );
_initWidgetFunction( QStringLiteral( "VectorField" ), QgsVectorFieldSymbolLayerWidget::create );

View File

@ -3072,6 +3072,324 @@ void QgsCentroidFillSymbolLayerWidget::mDrawAllPartsCheckBox_stateChanged( int s
emit changed();
}
///////////
QgsRasterMarkerSymbolLayerWidget::QgsRasterMarkerSymbolLayerWidget( QgsVectorLayer *vl, QWidget *parent )
: QgsSymbolLayerWidget( parent, vl )
{
mLayer = nullptr;
setupUi( this );
connect( mBrowseToolButton, &QToolButton::clicked, this, &QgsRasterMarkerSymbolLayerWidget::mBrowseToolButton_clicked );
connect( mImageLineEdit, &QLineEdit::editingFinished, this, &QgsRasterMarkerSymbolLayerWidget::mImageLineEdit_editingFinished );
connect( mOffsetUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsRasterMarkerSymbolLayerWidget::mOffsetUnitWidget_changed );
connect( mRotationSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsRasterMarkerSymbolLayerWidget::setAngle );
connect( mSizeUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsRasterMarkerSymbolLayerWidget::mSizeUnitWidget_changed );
connect( mWidthSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsRasterMarkerSymbolLayerWidget::setWidth );
connect( mHeightSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsRasterMarkerSymbolLayerWidget::setHeight );
connect( mLockAspectRatio, static_cast < void ( QgsRatioLockButton::* )( bool ) > ( &QgsRatioLockButton::lockChanged ), this, &QgsRasterMarkerSymbolLayerWidget::setLockAspectRatio );
mSizeUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderPixels << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits
<< QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
mOffsetUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
<< QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
mSpinOffsetX->setClearValue( 0.0 );
mSpinOffsetY->setClearValue( 0.0 );
mRotationSpinBox->setClearValue( 0.0 );
connect( mSpinOffsetX, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsRasterMarkerSymbolLayerWidget::setOffset );
connect( mSpinOffsetY, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsRasterMarkerSymbolLayerWidget::setOffset );
connect( mOpacityWidget, &QgsOpacityWidget::opacityChanged, this, &QgsRasterMarkerSymbolLayerWidget::setOpacity );
connect( mHorizontalAnchorComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsRasterMarkerSymbolLayerWidget::mHorizontalAnchorComboBox_currentIndexChanged );
connect( mVerticalAnchorComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsRasterMarkerSymbolLayerWidget::mVerticalAnchorComboBox_currentIndexChanged );
}
void QgsRasterMarkerSymbolLayerWidget::setSymbolLayer( QgsSymbolLayer *layer )
{
if ( !layer )
{
return;
}
if ( layer->layerType() != QLatin1String( "RasterMarker" ) )
return;
// layer type is correct, we can do the cast
mLayer = static_cast<QgsRasterMarkerSymbolLayer *>( layer );
// set values
whileBlocking( mImageLineEdit )->setText( mLayer->path() );
whileBlocking( mWidthSpinBox )->setValue( mLayer->size() );
bool preservedAspectRatio = mLayer->preservedAspectRatio();
mHeightSpinBox->blockSignals( true );
if ( preservedAspectRatio )
{
mHeightSpinBox->setValue( mLayer->size() );
}
else
{
mHeightSpinBox->setValue( mLayer->size() * mLayer->fixedAspectRatio() );
}
mHeightSpinBox->setEnabled( mLayer->defaultAspectRatio() > 0.0 );
mHeightSpinBox->blockSignals( false );
whileBlocking( mLockAspectRatio )->setLocked( preservedAspectRatio );
whileBlocking( mRotationSpinBox )->setValue( mLayer->angle() );
whileBlocking( mOpacityWidget )->setOpacity( mLayer->opacity() );
whileBlocking( mSpinOffsetX )->setValue( mLayer->offset().x() );
whileBlocking( mSpinOffsetY )->setValue( mLayer->offset().y() );
mSizeUnitWidget->blockSignals( true );
mSizeUnitWidget->setUnit( mLayer->sizeUnit() );
mSizeUnitWidget->setMapUnitScale( mLayer->sizeMapUnitScale() );
mSizeUnitWidget->blockSignals( false );
mOffsetUnitWidget->blockSignals( true );
mOffsetUnitWidget->setUnit( mLayer->offsetUnit() );
mOffsetUnitWidget->setMapUnitScale( mLayer->offsetMapUnitScale() );
mOffsetUnitWidget->blockSignals( false );
//anchor points
whileBlocking( mHorizontalAnchorComboBox )->setCurrentIndex( mLayer->horizontalAnchorPoint() );
whileBlocking( mVerticalAnchorComboBox )->setCurrentIndex( mLayer->verticalAnchorPoint() );
registerDataDefinedButton( mWidthDDBtn, QgsSymbolLayer::PropertyWidth );
registerDataDefinedButton( mHeightDDBtn, QgsSymbolLayer::PropertyHeight );
registerDataDefinedButton( mRotationDDBtn, QgsSymbolLayer::PropertyAngle );
registerDataDefinedButton( mOpacityDDBtn, QgsSymbolLayer::PropertyOpacity );
registerDataDefinedButton( mOffsetDDBtn, QgsSymbolLayer::PropertyOffset );
registerDataDefinedButton( mFilenameDDBtn, QgsSymbolLayer::PropertyName );
registerDataDefinedButton( mHorizontalAnchorDDBtn, QgsSymbolLayer::PropertyHorizontalAnchor );
registerDataDefinedButton( mVerticalAnchorDDBtn, QgsSymbolLayer::PropertyVerticalAnchor );
updatePreviewImage();
}
QgsSymbolLayer *QgsRasterMarkerSymbolLayerWidget::symbolLayer()
{
return mLayer;
}
void QgsRasterMarkerSymbolLayerWidget::mBrowseToolButton_clicked()
{
QgsSettings s;
QString openDir;
QString lineEditText = mImageLineEdit->text();
if ( !lineEditText.isEmpty() )
{
QFileInfo openDirFileInfo( lineEditText );
openDir = openDirFileInfo.path();
}
if ( openDir.isEmpty() )
{
openDir = s.value( QStringLiteral( "/UI/lastRasterMarkerImageDir" ), QDir::homePath() ).toString();
}
//show file dialog
QString filePath = QFileDialog::getOpenFileName( nullptr, tr( "Select Image File" ), openDir );
if ( !filePath.isNull() )
{
//check if file exists
QFileInfo fileInfo( filePath );
if ( !fileInfo.exists() || !fileInfo.isReadable() )
{
QMessageBox::critical( nullptr, QStringLiteral( "Select Image File" ), QStringLiteral( "Error, file does not exist or is not readable." ) );
return;
}
s.setValue( QStringLiteral( "/UI/lastRasterMarkerImageDir" ), fileInfo.absolutePath() );
mImageLineEdit->setText( filePath );
mImageLineEdit_editingFinished();
}
}
void QgsRasterMarkerSymbolLayerWidget::mImageLineEdit_editingFinished()
{
QFileInfo fi( mImageLineEdit->text() );
if ( !fi.exists() )
{
QUrl url( mImageLineEdit->text() );
if ( !url.isValid() )
{
return;
}
}
QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
mLayer->setPath( mImageLineEdit->text() );
updatePreviewImage();
QApplication::restoreOverrideCursor();
emit changed();
}
void QgsRasterMarkerSymbolLayerWidget::updatePreviewImage()
{
QImage image( mLayer->path() );
if ( image.isNull() )
{
mLabelImagePreview->setPixmap( QPixmap() );
return;
}
if ( image.height() > 150 || image.width() > 150 )
{
image = image.scaled( 150, 150, Qt::KeepAspectRatio, Qt::SmoothTransformation );
}
QImage previewImage( 150, 150, QImage::Format_ARGB32 );
previewImage.fill( Qt::transparent );
QRect imageRect( ( 150 - image.width() ) / 2.0, ( 150 - image.height() ) / 2.0, image.width(), image.height() );
QPainter p;
p.begin( &previewImage );
//draw a checkerboard background
uchar pixDataRGB[] = { 150, 150, 150, 150,
100, 100, 100, 150,
100, 100, 100, 150,
150, 150, 150, 150
};
QImage img( pixDataRGB, 2, 2, 8, QImage::Format_ARGB32 );
QPixmap pix = QPixmap::fromImage( img.scaled( 8, 8 ) );
QBrush checkerBrush;
checkerBrush.setTexture( pix );
p.fillRect( imageRect, checkerBrush );
if ( mLayer->opacity() < 1.0 )
{
p.setOpacity( mLayer->opacity() );
}
p.drawImage( imageRect.left(), imageRect.top(), image );
p.end();
mLabelImagePreview->setPixmap( QPixmap::fromImage( previewImage ) );
}
void QgsRasterMarkerSymbolLayerWidget::setWidth()
{
double defaultAspectRatio = mLayer->defaultAspectRatio();
double fixedAspectRatio = 0.0;
mHeightSpinBox->blockSignals( true );
if ( defaultAspectRatio <= 0.0 )
{
mHeightSpinBox->setValue( mWidthSpinBox->value() );
}
else if ( mLockAspectRatio->locked() )
{
mHeightSpinBox->setValue( mWidthSpinBox->value() * defaultAspectRatio );
}
else
{
fixedAspectRatio = mHeightSpinBox->value() / mWidthSpinBox->value();
}
mHeightSpinBox->blockSignals( false );
mLayer->setSize( mWidthSpinBox->value() );
mLayer->setFixedAspectRatio( fixedAspectRatio );
emit changed();
}
void QgsRasterMarkerSymbolLayerWidget::setHeight()
{
double defaultAspectRatio = mLayer->defaultAspectRatio();
double fixedAspectRatio = 0.0;
mWidthSpinBox->blockSignals( true );
if ( defaultAspectRatio <= 0.0 )
{
mWidthSpinBox->setValue( mHeightSpinBox->value() );
}
else if ( mLockAspectRatio->locked() )
{
mWidthSpinBox->setValue( mHeightSpinBox->value() / defaultAspectRatio );
}
else
{
fixedAspectRatio = mHeightSpinBox->value() / mWidthSpinBox->value();
}
mWidthSpinBox->blockSignals( false );
mLayer->setSize( mWidthSpinBox->value() );
mLayer->setFixedAspectRatio( fixedAspectRatio );
emit changed();
}
void QgsRasterMarkerSymbolLayerWidget::setLockAspectRatio( const bool locked )
{
double defaultAspectRatio = mLayer->defaultAspectRatio();
if ( defaultAspectRatio <= 0.0 )
{
whileBlocking( mLockAspectRatio )->setLocked( true );
}
else if ( locked )
{
mLayer->setFixedAspectRatio( 0.0 );
setWidth();
}
else
{
mLayer->setFixedAspectRatio( mHeightSpinBox->value() / mWidthSpinBox->value() );
}
}
void QgsRasterMarkerSymbolLayerWidget::setAngle()
{
mLayer->setAngle( mRotationSpinBox->value() );
emit changed();
}
void QgsRasterMarkerSymbolLayerWidget::setOpacity( double value )
{
mLayer->setOpacity( value );
emit changed();
updatePreviewImage();
}
void QgsRasterMarkerSymbolLayerWidget::setOffset()
{
mLayer->setOffset( QPointF( mSpinOffsetX->value(), mSpinOffsetY->value() ) );
emit changed();
}
void QgsRasterMarkerSymbolLayerWidget::mSizeUnitWidget_changed()
{
if ( mLayer )
{
mLayer->setSizeUnit( mSizeUnitWidget->unit() );
mLayer->setSizeMapUnitScale( mSizeUnitWidget->getMapUnitScale() );
emit changed();
}
}
void QgsRasterMarkerSymbolLayerWidget::mOffsetUnitWidget_changed()
{
if ( mLayer )
{
mLayer->setOffsetUnit( mOffsetUnitWidget->unit() );
mLayer->setOffsetMapUnitScale( mOffsetUnitWidget->getMapUnitScale() );
emit changed();
}
}
void QgsRasterMarkerSymbolLayerWidget::mHorizontalAnchorComboBox_currentIndexChanged( int index )
{
if ( mLayer )
{
mLayer->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( index ) );
emit changed();
}
}
void QgsRasterMarkerSymbolLayerWidget::mVerticalAnchorComboBox_currentIndexChanged( int index )
{
if ( mLayer )
{
mLayer->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( index ) );
emit changed();
}
}
///////////////
QgsRasterFillSymbolLayerWidget::QgsRasterFillSymbolLayerWidget( QgsVectorLayer *vl, QWidget *parent )

View File

@ -571,6 +571,62 @@ class GUI_EXPORT QgsSvgMarkerSymbolLayerWidget : public QgsSymbolLayerWidget, pr
///////////
#include "ui_widget_rastermarker.h"
class QgsRasterMarkerSymbolLayer;
/**
* \ingroup gui
* \class QgsRasterMarkerSymbolLayerWidget
* \brief Widget for configuring QgsRasterMarkerSymbolLayer symbol layers.
* \since QGIS 3.6
*/
class GUI_EXPORT QgsRasterMarkerSymbolLayerWidget : public QgsSymbolLayerWidget, private Ui::WidgetRasterMarker
{
Q_OBJECT
public:
/**
* Constructor for QgsRasterMarkerSymbolLayerWidget.
* \param vl associated vector layer
* \param parent parent widget
*/
QgsRasterMarkerSymbolLayerWidget( QgsVectorLayer *vl, QWidget *parent SIP_TRANSFERTHIS = nullptr );
/**
* Creates a new QgsRasterMarkerSymbolLayerWidget.
* \param vl associated vector layer
*/
static QgsSymbolLayerWidget *create( QgsVectorLayer *vl ) SIP_FACTORY { return new QgsRasterMarkerSymbolLayerWidget( vl ); }
// from base class
void setSymbolLayer( QgsSymbolLayer *layer ) override;
QgsSymbolLayer *symbolLayer() override;
protected:
QgsRasterMarkerSymbolLayer *mLayer = nullptr;
private slots:
void mBrowseToolButton_clicked();
void mImageLineEdit_editingFinished();
void mSizeUnitWidget_changed();
void mOffsetUnitWidget_changed();
void mHorizontalAnchorComboBox_currentIndexChanged( int index );
void mVerticalAnchorComboBox_currentIndexChanged( int index );
void setWidth();
void setHeight();
void setLockAspectRatio( bool locked );
void setAngle();
void setOffset();
void setOpacity( double value );
void updatePreviewImage();
};
///////////
#include "ui_widget_rasterfill.h"
class QgsRasterFillSymbolLayer;

View File

@ -0,0 +1,508 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WidgetRasterMarker</class>
<widget class="QWidget" name="WidgetRasterMarker">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>338</width>
<height>419</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="4" column="3">
<widget class="QgsPropertyOverrideButton" name="mRotationDDBtn">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="3">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="mImageLineEdit"/>
</item>
<item>
<widget class="QToolButton" name="mBrowseToolButton">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="3">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QgsPropertyOverrideButton" name="mWidthDDBtn">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
<item>
<widget class="QgsPropertyOverrideButton" name="mHeightDDBtn">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="7" column="2">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>x</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QgsDoubleSpinBox" name="mSpinOffsetX">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="decimals">
<number>6</number>
</property>
<property name="minimum">
<double>-99999999.989999994635582</double>
</property>
<property name="maximum">
<double>99999999.989999994635582</double>
</property>
<property name="singleStep">
<double>0.200000000000000</double>
</property>
</widget>
</item>
<item row="0" column="2" rowspan="2">
<widget class="QgsUnitSelectionWidget" name="mOffsetUnitWidget" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>y</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QgsDoubleSpinBox" name="mSpinOffsetY">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="decimals">
<number>6</number>
</property>
<property name="minimum">
<double>-99999999.989999994635582</double>
</property>
<property name="maximum">
<double>99999999.989999994635582</double>
</property>
<property name="singleStep">
<double>0.200000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item row="7" column="3">
<widget class="QgsPropertyOverrideButton" name="mOffsetDDBtn">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
<item row="8" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0">
<widget class="QLabel" name="mTextureWidthLabel">
<property name="text">
<string>Size</string>
</property>
<property name="buddy">
<cstring>mSizeSpinBox</cstring>
</property>
</widget>
</item>
<item row="0" column="0" colspan="4">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="mLabelImagePreview">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>150</width>
<height>150</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="lineWidth">
<number>1</number>
</property>
<property name="midLineWidth">
<number>0</number>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="4" column="2">
<widget class="QgsDoubleSpinBox" name="mRotationSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="wrapping">
<bool>true</bool>
</property>
<property name="suffix">
<string> °</string>
</property>
<property name="minimum">
<double>-360.000000000000000</double>
</property>
<property name="maximum">
<double>360.000000000000000</double>
</property>
<property name="singleStep">
<double>0.500000000000000</double>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Offset</string>
</property>
<property name="buddy">
<cstring>mSpinOffsetX</cstring>
</property>
</widget>
</item>
<item row="2" column="2">
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Width</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QgsDoubleSpinBox" name="mWidthSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="decimals">
<number>6</number>
</property>
<property name="maximum">
<double>100000.000000000000000</double>
</property>
<property name="singleStep">
<double>0.200000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
<property name="showClearButton" stdset="0">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Height</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QgsDoubleSpinBox" name="mHeightSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="decimals">
<number>6</number>
</property>
<property name="maximum">
<double>100000.000000000000000</double>
</property>
<property name="singleStep">
<double>0.200000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
<property name="showClearButton" stdset="0">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="2" rowspan="2">
<layout class="QHBoxLayout" name="_2">
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<widget class="QgsRatioLockButton" name="mLockAspectRatio" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Lock aspect ratio</string>
</property>
<property name="leftMargin" stdset="0">
<number>13</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="3" rowspan="2">
<widget class="QgsUnitSelectionWidget" name="mSizeUnitWidget" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0">
<widget class="QLabel" name="mRotationLabel">
<property name="text">
<string>Rotation</string>
</property>
<property name="buddy">
<cstring>mRotationSpinBox</cstring>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QgsPropertyOverrideButton" name="mFilenameDDBtn">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Opacity</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QgsOpacityWidget" name="mOpacityWidget" native="true">
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QgsPropertyOverrideButton" name="mOpacityDDBtn">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
<item row="9" column="0" rowspan="2">
<widget class="QLabel" name="mAnchorPointLabel">
<property name="text">
<string>Anchor point</string>
</property>
</widget>
</item>
<item row="9" column="2">
<widget class="QComboBox" name="mVerticalAnchorComboBox">
<item>
<property name="text">
<string>Top</string>
</property>
</item>
<item>
<property name="text">
<string>VCenter</string>
</property>
</item>
<item>
<property name="text">
<string>Bottom</string>
</property>
</item>
</widget>
</item>
<item row="9" column="3">
<widget class="QgsPropertyOverrideButton" name="mVerticalAnchorDDBtn">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
<item row="10" column="2">
<widget class="QComboBox" name="mHorizontalAnchorComboBox">
<item>
<property name="text">
<string>Left</string>
</property>
</item>
<item>
<property name="text">
<string>HCenter</string>
</property>
</item>
<item>
<property name="text">
<string>Right</string>
</property>
</item>
</widget>
</item>
<item row="10" column="3">
<widget class="QgsPropertyOverrideButton" name="mHorizontalAnchorDDBtn">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QgsPropertyOverrideButton</class>
<extends>QToolButton</extends>
<header>qgspropertyoverridebutton.h</header>
</customwidget>
<customwidget>
<class>QgsDoubleSpinBox</class>
<extends>QDoubleSpinBox</extends>
<header>qgsdoublespinbox.h</header>
</customwidget>
<customwidget>
<class>QgsUnitSelectionWidget</class>
<extends>QWidget</extends>
<header>qgsunitselectionwidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsOpacityWidget</class>
<extends>QWidget</extends>
<header>qgsopacitywidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsRatioLockButton</class>
<extends>QWidget</extends>
<header>qgsratiolockbutton.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>mImageLineEdit</tabstop>
<tabstop>mBrowseToolButton</tabstop>
<tabstop>mFilenameDDBtn</tabstop>
<tabstop>mWidgthSpinBox</tabstop>
<tabstop>mHeightSpinBox</tabstop>
<tabstop>mSizeUnitWidget</tabstop>
<tabstop>mWidthDDBtn</tabstop>
<tabstop>mHeightDDBtn</tabstop>
<tabstop>cboCoordinateMode</tabstop>
<tabstop>mOpacityWidget</tabstop>
<tabstop>mOpacityDDBtn</tabstop>
<tabstop>mRotationSpinBox</tabstop>
<tabstop>mRotationDDBtn</tabstop>
<tabstop>mSpinOffsetX</tabstop>
<tabstop>mSpinOffsetY</tabstop>
<tabstop>mOffsetUnitWidget</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@ -175,6 +175,7 @@ SET(TESTS
testqgis.cpp
testqgsrasterfilewriter.cpp
testqgsrasterfill.cpp
testqgsrastermarker.cpp
testqgsrasteriterator.cpp
testqgsrasterblock.cpp
testqgsrasterlayer.cpp

View File

@ -0,0 +1,199 @@
/***************************************************************************
testqgsrastermarker.cpp
---------------------
Date : December 2018
Copyright : (C) 2014 Mathieu Pellerin
Email : nirvn dot asia at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgstest.h"
#include <QObject>
#include <QString>
#include <QStringList>
#include <QApplication>
#include <QFileInfo>
#include <QDir>
#include <QDesktopServices>
#include <qgsmapsettings.h>
#include <qgsmaplayer.h>
#include <qgsvectorlayer.h>
#include <qgsapplication.h>
#include <qgsproviderregistry.h>
#include <qgsproject.h>
#include <qgssymbol.h>
#include <qgssinglesymbolrenderer.h>
#include <qgsmarkersymbollayer.h>
#include "qgsmultirenderchecker.h"
/**
* \ingroup UnitTests
* This is a unit test for raster marker types.
*/
class TestQgsRasterMarker : public QObject
{
Q_OBJECT
public:
TestQgsRasterMarker() = default;
private slots:
void initTestCase();// will be called before the first testfunction is executed.
void cleanupTestCase();// will be called after the last testfunction was executed.
void init(); // will be called before each testfunction is executed.
void cleanup();// will be called after every testfunction.
void rasterMarkerSymbol();
void anchor();
void alpha();
void rotation();
void fixedAspectRatio();
private:
bool mTestHasError = false ;
bool imageCheck( const QString &type );
QgsMapSettings mMapSettings;
QgsVectorLayer *mPointLayer = nullptr;
QgsRasterMarkerSymbolLayer *mRasterMarker = nullptr;
QgsMarkerSymbol *mMarkerSymbol = nullptr;
QgsSingleSymbolRenderer *mSymbolRenderer = nullptr;
QString mTestDataDir;
QString mReport;
};
void TestQgsRasterMarker::initTestCase()
{
mTestHasError = false;
// init QGIS's paths - true means that all path will be inited from prefix
QgsApplication::init();
QgsApplication::initQgis();
QgsApplication::showSettings();
//create some objects that will be used in all tests...
QString myDataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt
mTestDataDir = myDataDir + '/';
//create a marker layer that will be used in all tests
QString pointFileName = mTestDataDir + "points.shp";
QFileInfo pointFileInfo( pointFileName );
mPointLayer = new QgsVectorLayer( pointFileInfo.filePath(),
pointFileInfo.completeBaseName(), QStringLiteral( "ogr" ) );
// Register the layer with the registry
QgsProject::instance()->addMapLayers(
QList<QgsMapLayer *>() << mPointLayer );
//setup the raster marker symbol
mRasterMarker = new QgsRasterMarkerSymbolLayer();
mMarkerSymbol = new QgsMarkerSymbol();
mMarkerSymbol->changeSymbolLayer( 0, mRasterMarker );
mSymbolRenderer = new QgsSingleSymbolRenderer( mMarkerSymbol );
mPointLayer->setRenderer( mSymbolRenderer );
// We only need maprender instead of mapcanvas
// since maprender does not require a qui
// and is more light weight
mMapSettings.setLayers( QList<QgsMapLayer *>() << mPointLayer );
mReport += QLatin1String( "<h1>Raster Marker Renderer Tests</h1>\n" );
}
void TestQgsRasterMarker::cleanupTestCase()
{
QString myReportFile = QDir::tempPath() + "/qgistest.html";
QFile myFile( myReportFile );
if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) )
{
QTextStream myQTextStream( &myFile );
myQTextStream << mReport;
myFile.close();
}
QgsApplication::exitQgis();
}
void TestQgsRasterMarker::init()
{
mRasterMarker->setPath( mTestDataDir + QStringLiteral( "sample_image.png" ) );
mRasterMarker->setSize( 30.0 );
mRasterMarker->setSizeUnit( QgsUnitTypes::RenderPixels );
}
void TestQgsRasterMarker::cleanup()
{
}
void TestQgsRasterMarker::rasterMarkerSymbol()
{
mReport += QLatin1String( "<h2>Raster marker symbol renderer test</h2>\n" );
bool result = imageCheck( QStringLiteral( "rastermarker" ) );
QVERIFY( result );
}
void TestQgsRasterMarker::anchor()
{
mRasterMarker->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( 2 ) );
mRasterMarker->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( 2 ) );
bool result = imageCheck( QStringLiteral( "rastermarker_anchor" ) );
QVERIFY( result );
mRasterMarker->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( 1 ) );
mRasterMarker->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( 1 ) );
}
void TestQgsRasterMarker::alpha()
{
mReport += QLatin1String( "<h2>Raster marker alpha</h2>\n" );
mRasterMarker->setOpacity( 0.5 );
bool result = imageCheck( QStringLiteral( "rastermarker_alpha" ) );
QVERIFY( result );
}
void TestQgsRasterMarker::rotation()
{
mReport += QLatin1String( "<h2>Raster marker rotation</h2>\n" );
mRasterMarker->setAngle( 45.0 );
bool result = imageCheck( QStringLiteral( "rastermarker_rotation" ) );
QVERIFY( result );
}
void TestQgsRasterMarker::fixedAspectRatio()
{
mReport += QLatin1String( "<h2>Raster marker fixed aspect ratio</h2>\n" );
mRasterMarker->setFixedAspectRatio( 0.2 );
bool result = imageCheck( QStringLiteral( "rastermarker_fixedaspectratio" ) );
QVERIFY( result );
}
//
// Private helper functions not called directly by CTest
//
bool TestQgsRasterMarker::imageCheck( const QString &testType )
{
//use the QgsRenderChecker test utility class to
//ensure the rendered output matches our control image
mMapSettings.setExtent( mPointLayer->extent() );
mMapSettings.setOutputDpi( 96 );
QgsMultiRenderChecker myChecker;
myChecker.setControlPathPrefix( QStringLiteral( "symbol_rastermarker" ) );
myChecker.setControlName( "expected_" + testType );
myChecker.setMapSettings( mMapSettings );
myChecker.setColorTolerance( 20 );
bool myResultFlag = myChecker.runTest( testType, 500 );
mReport += myChecker.report();
return myResultFlag;
}
QGSTEST_MAIN( TestQgsRasterMarker )
#include "testqgsrastermarker.moc"

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 KiB