mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-15 00:04:00 -04:00
[FEATURE] Random marker fill symbol layer type
This commit adds a new fill symbol layer type "Random marker fill". It allows polygon features to be rendered using a marker symbol placed at random locations within the polygon boundary. Options include: - number of marker symbols to render for the polygon - whether markers rendered near the edges of polygons should be clipped to the polygon boundary or not - an optional random number seed, to give consistent placement of markers whenever maps are refreshed (also allows random placement to play nice with QGIS server and tile-based rendering) Sponsored by the SLYR project
This commit is contained in:
parent
fc6d01b77a
commit
b5e92d41f5
@ -1891,6 +1891,117 @@ Returns the unit scale for the vertical offset between rows in the pattern.
|
||||
QgsPointPatternFillSymbolLayer( const QgsPointPatternFillSymbolLayer &other );
|
||||
};
|
||||
|
||||
class QgsRandomMarkerFillSymbolLayer : QgsFillSymbolLayer
|
||||
{
|
||||
%Docstring
|
||||
|
||||
A fill symbol layer which places markers at random locations within polygons.
|
||||
|
||||
.. versionadded:: 3.12
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgsfillsymbollayer.h"
|
||||
%End
|
||||
public:
|
||||
|
||||
QgsRandomMarkerFillSymbolLayer( int pointCount = 1, unsigned long seed = 0 );
|
||||
%Docstring
|
||||
Constructor for QgsRandomMarkerFillSymbolLayer, with the specified ``pointCount``.
|
||||
|
||||
Optionally a specific random number ``seed`` can be used when generating points. A ``seed`` of 0 indicates that
|
||||
a truely random sequence should be used.
|
||||
%End
|
||||
|
||||
static QgsSymbolLayer *create( const QgsStringMap &properties = QgsStringMap() ) /Factory/;
|
||||
|
||||
virtual QString layerType() const;
|
||||
|
||||
virtual void startRender( QgsSymbolRenderContext &context );
|
||||
|
||||
virtual void stopRender( QgsSymbolRenderContext &context );
|
||||
|
||||
virtual void renderPolygon( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context );
|
||||
|
||||
virtual QgsStringMap properties() const;
|
||||
|
||||
virtual QgsRandomMarkerFillSymbolLayer *clone() const /Factory/;
|
||||
|
||||
|
||||
virtual void setColor( const QColor &color );
|
||||
|
||||
virtual QColor color() const;
|
||||
|
||||
|
||||
virtual QgsSymbol *subSymbol();
|
||||
|
||||
virtual bool setSubSymbol( QgsSymbol *symbol /Transfer/ );
|
||||
|
||||
|
||||
virtual void setOutputUnit( QgsUnitTypes::RenderUnit unit );
|
||||
|
||||
virtual QgsUnitTypes::RenderUnit outputUnit() const;
|
||||
|
||||
|
||||
virtual void setMapUnitScale( const QgsMapUnitScale &scale );
|
||||
|
||||
virtual QgsMapUnitScale mapUnitScale() const;
|
||||
|
||||
|
||||
virtual QSet<QString> usedAttributes( const QgsRenderContext &context ) const;
|
||||
|
||||
virtual bool hasDataDefinedProperties() const;
|
||||
|
||||
|
||||
int pointCount() const;
|
||||
%Docstring
|
||||
Returns the count of random points to render in the fill.
|
||||
|
||||
.. seealso:: :py:func:`setPointCount`
|
||||
%End
|
||||
|
||||
void setPointCount( int count );
|
||||
%Docstring
|
||||
Sets the ``count`` of random points to render in the fill.
|
||||
|
||||
.. seealso:: :py:func:`pointCount`
|
||||
%End
|
||||
|
||||
unsigned long seed() const;
|
||||
%Docstring
|
||||
Returns the random number seed to use when generating points, or 0 if
|
||||
a truely random sequence should be used.
|
||||
|
||||
.. seealso:: :py:func:`setSeed`
|
||||
%End
|
||||
|
||||
void setSeed( unsigned long seed );
|
||||
%Docstring
|
||||
Sets the random number ``seed`` to use when generating points, or 0 if
|
||||
a truely random sequence should be used.
|
||||
|
||||
.. seealso:: :py:func:`seed`
|
||||
%End
|
||||
|
||||
bool clipPoints() const;
|
||||
%Docstring
|
||||
Returns ``True`` if point markers should be clipped to the polygon boundary.
|
||||
|
||||
.. seealso:: :py:func:`setClipPoints`
|
||||
%End
|
||||
|
||||
void setClipPoints( bool clipped );
|
||||
%Docstring
|
||||
Sets whether point markers should be ``clipped`` to the polygon boundary.
|
||||
|
||||
.. seealso:: :py:func:`clipPoints`
|
||||
%End
|
||||
|
||||
private:
|
||||
QgsRandomMarkerFillSymbolLayer( const QgsRandomMarkerFillSymbolLayer &other );
|
||||
};
|
||||
|
||||
|
||||
class QgsCentroidFillSymbolLayer : QgsFillSymbolLayer
|
||||
{
|
||||
|
||||
|
@ -135,6 +135,9 @@ class QgsSymbolLayer
|
||||
PropertyArrowType,
|
||||
PropertyOffsetX,
|
||||
PropertyOffsetY,
|
||||
PropertyPointCount,
|
||||
PropertyRandomSeed,
|
||||
PropertyClipPoints,
|
||||
};
|
||||
|
||||
static const QgsPropertiesDefinition &propertyDefinitions();
|
||||
|
@ -655,6 +655,40 @@ Creates a new QgsPointPatternFillSymbolLayerWidget.
|
||||
|
||||
|
||||
|
||||
|
||||
class QgsRandomMarkerFillSymbolLayerWidget: QgsSymbolLayerWidget
|
||||
{
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgssymbollayerwidget.h"
|
||||
%End
|
||||
public:
|
||||
|
||||
QgsRandomMarkerFillSymbolLayerWidget( QgsVectorLayer *vl, QWidget *parent /TransferThis/ = 0 );
|
||||
%Docstring
|
||||
Constructor for QgsRandomMarkerFillSymbolLayerWidget.
|
||||
|
||||
:param vl: associated vector layer
|
||||
:param parent: parent widget
|
||||
%End
|
||||
|
||||
static QgsSymbolLayerWidget *create( QgsVectorLayer *vl ) /Factory/;
|
||||
%Docstring
|
||||
Creates a new QgsRandomMarkerFillSymbolLayerWidget.
|
||||
|
||||
:param vl: associated vector layer
|
||||
%End
|
||||
|
||||
virtual void setSymbolLayer( QgsSymbolLayer *layer );
|
||||
|
||||
virtual QgsSymbolLayer *symbolLayer();
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class QgsFontMarkerSymbolLayerWidget : QgsSymbolLayerWidget
|
||||
{
|
||||
|
||||
|
@ -31,6 +31,8 @@
|
||||
#include "qgsmessagelog.h"
|
||||
#include "qgsapplication.h"
|
||||
#include "qgsimageoperation.h"
|
||||
#include "qgspolygon.h"
|
||||
#include "qgslinestring.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QFile>
|
||||
@ -3909,3 +3911,250 @@ void QgsRasterFillSymbolLayer::applyPattern( QBrush &brush, const QString &image
|
||||
|
||||
brush.setTextureImage( img );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// QgsRandomMarkerFillSymbolLayer
|
||||
//
|
||||
|
||||
QgsRandomMarkerFillSymbolLayer::QgsRandomMarkerFillSymbolLayer( int pointCount, unsigned long seed )
|
||||
: mPointCount( pointCount )
|
||||
, mSeed( seed )
|
||||
{
|
||||
setSubSymbol( new QgsMarkerSymbol() );
|
||||
}
|
||||
|
||||
QgsSymbolLayer *QgsRandomMarkerFillSymbolLayer::create( const QgsStringMap &properties )
|
||||
{
|
||||
const int pointCount = properties.value( QStringLiteral( "point_count" ), QStringLiteral( "1" ) ).toInt();
|
||||
const unsigned long seed = properties.value( QStringLiteral( "seed" ), QStringLiteral( "0" ) ).toULong();
|
||||
std::unique_ptr< QgsRandomMarkerFillSymbolLayer > sl = qgis::make_unique< QgsRandomMarkerFillSymbolLayer >( pointCount, seed );
|
||||
|
||||
if ( properties.contains( QStringLiteral( "clip_points" ) ) )
|
||||
{
|
||||
sl->setClipPoints( properties[QStringLiteral( "clip_points" )].toInt() );
|
||||
}
|
||||
|
||||
return sl.release();
|
||||
}
|
||||
|
||||
QString QgsRandomMarkerFillSymbolLayer::layerType() const
|
||||
{
|
||||
return QStringLiteral( "RandomMarkerFill" );
|
||||
}
|
||||
|
||||
void QgsRandomMarkerFillSymbolLayer::setColor( const QColor &color )
|
||||
{
|
||||
mMarker->setColor( color );
|
||||
mColor = color;
|
||||
}
|
||||
|
||||
QColor QgsRandomMarkerFillSymbolLayer::color() const
|
||||
{
|
||||
return mMarker ? mMarker->color() : mColor;
|
||||
}
|
||||
|
||||
void QgsRandomMarkerFillSymbolLayer::startRender( QgsSymbolRenderContext &context )
|
||||
{
|
||||
mMarker->setOpacity( context.opacity() );
|
||||
mMarker->startRender( context.renderContext(), context.fields() );
|
||||
}
|
||||
|
||||
void QgsRandomMarkerFillSymbolLayer::stopRender( QgsSymbolRenderContext &context )
|
||||
{
|
||||
mMarker->stopRender( context.renderContext() );
|
||||
}
|
||||
|
||||
void QgsRandomMarkerFillSymbolLayer::renderPolygon( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
|
||||
{
|
||||
QgsGeometry geom = QgsGeometry::fromQPolygonF( points );
|
||||
if ( !geom.isNull() && rings )
|
||||
{
|
||||
QgsPolygon *poly = qgsgeometry_cast< QgsPolygon * >( geom.get() );
|
||||
const QList< QPolygonF > constRings = *rings;
|
||||
for ( const QPolygonF &ring : constRings )
|
||||
{
|
||||
poly->addInteriorRing( QgsLineString::fromQPolygonF( ring ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !geom.isGeosValid() )
|
||||
{
|
||||
geom = geom.buffer( 0, 0 );
|
||||
}
|
||||
|
||||
bool clipPoints = mClipPoints;
|
||||
if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyClipPoints ) )
|
||||
{
|
||||
context.setOriginalValueVariable( clipPoints );
|
||||
clipPoints = mDataDefinedProperties.valueAsBool( QgsSymbolLayer::PropertyClipPoints, context.renderContext().expressionContext(), clipPoints );
|
||||
}
|
||||
if ( clipPoints )
|
||||
{
|
||||
QPainterPath path;
|
||||
path.addPolygon( points );
|
||||
if ( rings )
|
||||
{
|
||||
const QList< QPolygonF > constRings = *rings;
|
||||
for ( const QPolygonF &ring : constRings )
|
||||
{
|
||||
path.addPolygon( ring );
|
||||
}
|
||||
}
|
||||
context.renderContext().painter()->save();
|
||||
context.renderContext().painter()->setClipPath( path );
|
||||
}
|
||||
|
||||
int count = mPointCount;
|
||||
if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyPointCount ) )
|
||||
{
|
||||
context.setOriginalValueVariable( count );
|
||||
count = mDataDefinedProperties.valueAsInt( QgsSymbolLayer::PropertyPointCount, context.renderContext().expressionContext(), count );
|
||||
}
|
||||
unsigned long seed = mSeed;
|
||||
if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyRandomSeed ) )
|
||||
{
|
||||
context.setOriginalValueVariable( static_cast< unsigned long long >( seed ) );
|
||||
seed = mDataDefinedProperties.valueAsInt( QgsSymbolLayer::PropertyRandomSeed, context.renderContext().expressionContext(), seed );
|
||||
}
|
||||
|
||||
QVector< QgsPointXY > randomPoints = geom.randomPointsInPolygon( count, seed );
|
||||
#if 0
|
||||
std::sort( randomPoints.begin(), randomPoints.end(), []( const QgsPointXY & a, const QgsPointXY & b )->bool
|
||||
{
|
||||
return a.y() < b.y();
|
||||
} );
|
||||
#endif
|
||||
for ( const QgsPointXY &p : qgis::as_const( randomPoints ) )
|
||||
{
|
||||
mMarker->renderPoint( QPointF( p.x(), p.y() ), context.feature(), context.renderContext(), -1, context.selected() );
|
||||
}
|
||||
|
||||
if ( clipPoints )
|
||||
{
|
||||
context.renderContext().painter()->restore();
|
||||
}
|
||||
}
|
||||
|
||||
QgsStringMap QgsRandomMarkerFillSymbolLayer::properties() const
|
||||
{
|
||||
QgsStringMap map;
|
||||
map.insert( QStringLiteral( "point_count" ), QString::number( mPointCount ) );
|
||||
map.insert( QStringLiteral( "seed" ), QString::number( mSeed ) );
|
||||
map.insert( QStringLiteral( "clip_points" ), QString::number( mClipPoints ) );
|
||||
return map;
|
||||
}
|
||||
|
||||
QgsRandomMarkerFillSymbolLayer *QgsRandomMarkerFillSymbolLayer::clone() const
|
||||
{
|
||||
std::unique_ptr< QgsRandomMarkerFillSymbolLayer > x = qgis::make_unique< QgsRandomMarkerFillSymbolLayer >( mPointCount, mSeed );
|
||||
x->mAngle = mAngle;
|
||||
x->mColor = mColor;
|
||||
x->mClipPoints = mClipPoints;
|
||||
x->setSubSymbol( mMarker->clone() );
|
||||
copyDataDefinedProperties( x.get() );
|
||||
copyPaintEffect( x.get() );
|
||||
return x.release();
|
||||
}
|
||||
|
||||
QgsSymbol *QgsRandomMarkerFillSymbolLayer::subSymbol()
|
||||
{
|
||||
return mMarker.get();
|
||||
}
|
||||
|
||||
bool QgsRandomMarkerFillSymbolLayer::setSubSymbol( QgsSymbol *symbol )
|
||||
{
|
||||
if ( !symbol || symbol->type() != QgsSymbol::Marker )
|
||||
{
|
||||
delete symbol;
|
||||
return false;
|
||||
}
|
||||
|
||||
mMarker.reset( static_cast<QgsMarkerSymbol *>( symbol ) );
|
||||
mColor = mMarker->color();
|
||||
return true;
|
||||
}
|
||||
|
||||
QSet<QString> QgsRandomMarkerFillSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
|
||||
{
|
||||
QSet<QString> attributes = QgsFillSymbolLayer::usedAttributes( context );
|
||||
|
||||
if ( mMarker )
|
||||
attributes.unite( mMarker->usedAttributes( context ) );
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
bool QgsRandomMarkerFillSymbolLayer::hasDataDefinedProperties() const
|
||||
{
|
||||
if ( QgsSymbolLayer::hasDataDefinedProperties() )
|
||||
return true;
|
||||
if ( mMarker && mMarker->hasDataDefinedProperties() )
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
int QgsRandomMarkerFillSymbolLayer::pointCount() const
|
||||
{
|
||||
return mPointCount;
|
||||
}
|
||||
|
||||
void QgsRandomMarkerFillSymbolLayer::setPointCount( int pointCount )
|
||||
{
|
||||
mPointCount = pointCount;
|
||||
}
|
||||
|
||||
unsigned long QgsRandomMarkerFillSymbolLayer::seed() const
|
||||
{
|
||||
return mSeed;
|
||||
}
|
||||
|
||||
void QgsRandomMarkerFillSymbolLayer::setSeed( unsigned long seed )
|
||||
{
|
||||
mSeed = seed;
|
||||
}
|
||||
|
||||
bool QgsRandomMarkerFillSymbolLayer::clipPoints() const
|
||||
{
|
||||
return mClipPoints;
|
||||
}
|
||||
|
||||
void QgsRandomMarkerFillSymbolLayer::setClipPoints( bool clipPoints )
|
||||
{
|
||||
mClipPoints = clipPoints;
|
||||
}
|
||||
|
||||
void QgsRandomMarkerFillSymbolLayer::setOutputUnit( QgsUnitTypes::RenderUnit unit )
|
||||
{
|
||||
if ( mMarker )
|
||||
{
|
||||
mMarker->setOutputUnit( unit );
|
||||
}
|
||||
}
|
||||
|
||||
QgsUnitTypes::RenderUnit QgsRandomMarkerFillSymbolLayer::outputUnit() const
|
||||
{
|
||||
if ( mMarker )
|
||||
{
|
||||
return mMarker->outputUnit();
|
||||
}
|
||||
return QgsUnitTypes::RenderUnknownUnit; //mOutputUnit;
|
||||
}
|
||||
|
||||
void QgsRandomMarkerFillSymbolLayer::setMapUnitScale( const QgsMapUnitScale &scale )
|
||||
{
|
||||
if ( mMarker )
|
||||
{
|
||||
mMarker->setMapUnitScale( scale );
|
||||
}
|
||||
}
|
||||
|
||||
QgsMapUnitScale QgsRandomMarkerFillSymbolLayer::mapUnitScale() const
|
||||
{
|
||||
if ( mMarker )
|
||||
{
|
||||
return mMarker->mapUnitScale();
|
||||
}
|
||||
return QgsMapUnitScale();
|
||||
}
|
||||
|
||||
|
@ -1702,6 +1702,106 @@ class CORE_EXPORT QgsPointPatternFillSymbolLayer: public QgsImageFillSymbolLayer
|
||||
double displacementX, double displacementY, double offsetX, double offsetY );
|
||||
};
|
||||
|
||||
/**
|
||||
* \ingroup core
|
||||
* \class QgsRandomMarkerFillSymbolLayer
|
||||
*
|
||||
* A fill symbol layer which places markers at random locations within polygons.
|
||||
*
|
||||
* \since QGIS 3.12
|
||||
*/
|
||||
class CORE_EXPORT QgsRandomMarkerFillSymbolLayer : public QgsFillSymbolLayer
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for QgsRandomMarkerFillSymbolLayer, with the specified \a pointCount.
|
||||
*
|
||||
* Optionally a specific random number \a seed can be used when generating points. A \a seed of 0 indicates that
|
||||
* a truely random sequence should be used.
|
||||
*/
|
||||
QgsRandomMarkerFillSymbolLayer( int pointCount = 1, unsigned long seed = 0 );
|
||||
|
||||
static QgsSymbolLayer *create( const QgsStringMap &properties = QgsStringMap() ) SIP_FACTORY;
|
||||
|
||||
QString layerType() const override;
|
||||
void startRender( QgsSymbolRenderContext &context ) override;
|
||||
void stopRender( QgsSymbolRenderContext &context ) override;
|
||||
void renderPolygon( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context ) override;
|
||||
QgsStringMap properties() const override;
|
||||
QgsRandomMarkerFillSymbolLayer *clone() const override SIP_FACTORY;
|
||||
|
||||
void setColor( const QColor &color ) override;
|
||||
QColor color() const override;
|
||||
|
||||
QgsSymbol *subSymbol() override;
|
||||
bool setSubSymbol( QgsSymbol *symbol SIP_TRANSFER ) override;
|
||||
|
||||
void setOutputUnit( QgsUnitTypes::RenderUnit unit ) override;
|
||||
QgsUnitTypes::RenderUnit outputUnit() const override;
|
||||
|
||||
void setMapUnitScale( const QgsMapUnitScale &scale ) override;
|
||||
QgsMapUnitScale mapUnitScale() const override;
|
||||
|
||||
QSet<QString> usedAttributes( const QgsRenderContext &context ) const override;
|
||||
bool hasDataDefinedProperties() const override;
|
||||
|
||||
/**
|
||||
* Returns the count of random points to render in the fill.
|
||||
*
|
||||
* \see setPointCount()
|
||||
*/
|
||||
int pointCount() const;
|
||||
|
||||
/**
|
||||
* Sets the \a count of random points to render in the fill.
|
||||
*
|
||||
* \see pointCount()
|
||||
*/
|
||||
void setPointCount( int count );
|
||||
|
||||
/**
|
||||
* Returns the random number seed to use when generating points, or 0 if
|
||||
* a truely random sequence should be used.
|
||||
*
|
||||
* \see setSeed()
|
||||
*/
|
||||
unsigned long seed() const;
|
||||
|
||||
/**
|
||||
* Sets the random number \a seed to use when generating points, or 0 if
|
||||
* a truely random sequence should be used.
|
||||
*
|
||||
* \see seed()
|
||||
*/
|
||||
void setSeed( unsigned long seed );
|
||||
|
||||
/**
|
||||
* Returns TRUE if point markers should be clipped to the polygon boundary.
|
||||
*
|
||||
* \see setClipPoints()
|
||||
*/
|
||||
bool clipPoints() const;
|
||||
|
||||
/**
|
||||
* Sets whether point markers should be \a clipped to the polygon boundary.
|
||||
*
|
||||
* \see clipPoints()
|
||||
*/
|
||||
void setClipPoints( bool clipped );
|
||||
|
||||
private:
|
||||
#ifdef SIP_RUN
|
||||
QgsRandomMarkerFillSymbolLayer( const QgsRandomMarkerFillSymbolLayer &other );
|
||||
#endif
|
||||
|
||||
std::unique_ptr< QgsMarkerSymbol > mMarker;
|
||||
int mPointCount = 1;
|
||||
unsigned long mSeed = 0;
|
||||
bool mClipPoints = false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \ingroup core
|
||||
* \class QgsCentroidFillSymbolLayer
|
||||
|
@ -93,9 +93,11 @@ void QgsSymbolLayer::initPropertyDefinitions()
|
||||
{ QgsSymbolLayer::PropertyArrowStartWidth, QgsPropertyDefinition( "arrowStartWidth", QObject::tr( "Arrow line start width" ), QgsPropertyDefinition::StrokeWidth, origin )},
|
||||
{ QgsSymbolLayer::PropertyArrowHeadLength, QgsPropertyDefinition( "arrowHeadLength", QObject::tr( "Arrow head length" ), QgsPropertyDefinition::DoublePositive, origin )},
|
||||
{ QgsSymbolLayer::PropertyArrowHeadThickness, QgsPropertyDefinition( "arrowHeadThickness", QObject::tr( "Arrow head thickness" ), QgsPropertyDefinition::DoublePositive, origin )},
|
||||
{ QgsSymbolLayer::PropertyArrowHeadType, QgsPropertyDefinition( "arrowHeadType", QgsPropertyDefinition::DataTypeString, QObject::tr( "Arrow head type" ), QObject::tr( "string " ) + QLatin1String( "[<b>single</b>|<b>reversed</b>|<b>double</b>]" ) )},
|
||||
{ QgsSymbolLayer::PropertyArrowType, QgsPropertyDefinition( "arrowType", QgsPropertyDefinition::DataTypeString, QObject::tr( "Arrow type" ), QObject::tr( "string " ) + QLatin1String( "[<b>plain</b>|<b>lefthalf</b>|<b>righthalf</b>]" ) )},
|
||||
|
||||
{ QgsSymbolLayer::PropertyArrowHeadType, QgsPropertyDefinition( "arrowHeadType", QgsPropertyDefinition::DataTypeString, QObject::tr( "Arrow head type" ), QObject::tr( "string " ) + QLatin1String( "[<b>single</b>|<b>reversed</b>|<b>double</b>]" ), origin )},
|
||||
{ QgsSymbolLayer::PropertyArrowType, QgsPropertyDefinition( "arrowType", QgsPropertyDefinition::DataTypeString, QObject::tr( "Arrow type" ), QObject::tr( "string " ) + QLatin1String( "[<b>plain</b>|<b>lefthalf</b>|<b>righthalf</b>]" ), origin )},
|
||||
{ QgsSymbolLayer::PropertyPointCount, QgsPropertyDefinition( "pointCount", QObject::tr( "Point count" ), QgsPropertyDefinition::IntegerPositive, origin )},
|
||||
{ QgsSymbolLayer::PropertyRandomSeed, QgsPropertyDefinition( "randomSeed", QgsPropertyDefinition::DataTypeNumeric, QObject::tr( "Random number seed" ), QObject::tr( "integer > 0, or 0 for completely random sequence" ), origin )},
|
||||
{ QgsSymbolLayer::PropertyClipPoints, QgsPropertyDefinition( "clipPoints", QObject::tr( "Clip markers" ), QgsPropertyDefinition::Boolean, origin )},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -177,6 +177,9 @@ class CORE_EXPORT QgsSymbolLayer
|
||||
PropertyArrowType, //!< Arrow type
|
||||
PropertyOffsetX, //!< Horizontal offset
|
||||
PropertyOffsetY, //!< Vertical offset
|
||||
PropertyPointCount, //!< Point count
|
||||
PropertyRandomSeed, //!< Random number seed
|
||||
PropertyClipPoints, //!< Whether markers should be clipped to polygon boundaries
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -61,6 +61,8 @@ QgsSymbolLayerRegistry::QgsSymbolLayerRegistry()
|
||||
QgsSVGFillSymbolLayer::create, QgsSVGFillSymbolLayer::createFromSld, QgsSVGFillSymbolLayer::resolvePaths ) );
|
||||
addSymbolLayerType( new QgsSymbolLayerMetadata( QStringLiteral( "CentroidFill" ), QObject::tr( "Centroid fill" ), QgsSymbol::Fill,
|
||||
QgsCentroidFillSymbolLayer::create, QgsCentroidFillSymbolLayer::createFromSld ) );
|
||||
addSymbolLayerType( new QgsSymbolLayerMetadata( QStringLiteral( "RandomMarkerFill" ), QObject::tr( "Random marker fill" ), QgsSymbol::Fill,
|
||||
QgsRandomMarkerFillSymbolLayer::create ) );
|
||||
addSymbolLayerType( new QgsSymbolLayerMetadata( QStringLiteral( "LinePatternFill" ), QObject::tr( "Line pattern fill" ), QgsSymbol::Fill,
|
||||
QgsLinePatternFillSymbolLayer::create, QgsLinePatternFillSymbolLayer::createFromSld ) );
|
||||
addSymbolLayerType( new QgsSymbolLayerMetadata( QStringLiteral( "PointPatternFill" ), QObject::tr( "Point pattern fill" ), QgsSymbol::Fill,
|
||||
|
@ -87,6 +87,7 @@ static void _initWidgetFunctions()
|
||||
_initWidgetFunction( QStringLiteral( "CentroidFill" ), QgsCentroidFillSymbolLayerWidget::create );
|
||||
_initWidgetFunction( QStringLiteral( "LinePatternFill" ), QgsLinePatternFillSymbolLayerWidget::create );
|
||||
_initWidgetFunction( QStringLiteral( "PointPatternFill" ), QgsPointPatternFillSymbolLayerWidget::create );
|
||||
_initWidgetFunction( QStringLiteral( "RandomMarkerFill" ), QgsRandomMarkerFillSymbolLayerWidget::create );
|
||||
|
||||
_initWidgetFunction( QStringLiteral( "GeometryGenerator" ), QgsGeometryGeneratorSymbolLayerWidget::create );
|
||||
|
||||
|
@ -4109,3 +4109,68 @@ void QgsGeometryGeneratorSymbolLayerWidget::updateSymbolType()
|
||||
|
||||
emit symbolChanged();
|
||||
}
|
||||
|
||||
//
|
||||
// QgsRandomMarkerFillSymbolLayerWidget
|
||||
//
|
||||
|
||||
|
||||
QgsRandomMarkerFillSymbolLayerWidget::QgsRandomMarkerFillSymbolLayerWidget( QgsVectorLayer *vl, QWidget *parent ):
|
||||
QgsSymbolLayerWidget( parent, vl )
|
||||
{
|
||||
setupUi( this );
|
||||
mPointCountSpinBox->setShowClearButton( true );
|
||||
mPointCountSpinBox->setClearValue( 100 );
|
||||
mSeedSpinBox->setShowClearButton( true );
|
||||
mSeedSpinBox->setClearValue( 0 );
|
||||
connect( mPointCountSpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsRandomMarkerFillSymbolLayerWidget::countChanged );
|
||||
connect( mSeedSpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsRandomMarkerFillSymbolLayerWidget::seedChanged );
|
||||
connect( mClipPointsCheckBox, &QCheckBox::toggled, this, [ = ]( bool checked )
|
||||
{
|
||||
if ( mLayer )
|
||||
{
|
||||
mLayer->setClipPoints( checked );
|
||||
emit changed();
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
void QgsRandomMarkerFillSymbolLayerWidget::setSymbolLayer( QgsSymbolLayer *layer )
|
||||
{
|
||||
if ( !layer || layer->layerType() != QLatin1String( "RandomMarkerFill" ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
mLayer = static_cast<QgsRandomMarkerFillSymbolLayer *>( layer );
|
||||
whileBlocking( mPointCountSpinBox )->setValue( mLayer->pointCount() );
|
||||
whileBlocking( mSeedSpinBox )->setValue( mLayer->seed() );
|
||||
whileBlocking( mClipPointsCheckBox )->setChecked( mLayer->clipPoints() );
|
||||
|
||||
registerDataDefinedButton( mPointCountDdbtn, QgsSymbolLayer::PropertyPointCount );
|
||||
registerDataDefinedButton( mSeedDdbtn, QgsSymbolLayer::PropertyRandomSeed );
|
||||
registerDataDefinedButton( mClipPointsDdbtn, QgsSymbolLayer::PropertyClipPoints );
|
||||
}
|
||||
|
||||
QgsSymbolLayer *QgsRandomMarkerFillSymbolLayerWidget::symbolLayer()
|
||||
{
|
||||
return mLayer;
|
||||
}
|
||||
|
||||
void QgsRandomMarkerFillSymbolLayerWidget::countChanged( int d )
|
||||
{
|
||||
if ( mLayer )
|
||||
{
|
||||
mLayer->setPointCount( d );
|
||||
emit changed();
|
||||
}
|
||||
}
|
||||
|
||||
void QgsRandomMarkerFillSymbolLayerWidget::seedChanged( int d )
|
||||
{
|
||||
if ( mLayer )
|
||||
{
|
||||
mLayer->setSeed( d );
|
||||
emit changed();
|
||||
}
|
||||
}
|
||||
|
@ -888,6 +888,48 @@ class GUI_EXPORT QgsPointPatternFillSymbolLayerWidget: public QgsSymbolLayerWidg
|
||||
void mVerticalOffsetUnitWidget_changed();
|
||||
};
|
||||
|
||||
|
||||
//////////
|
||||
|
||||
#include "ui_widget_randommarkerfill.h"
|
||||
|
||||
class QgsRandomMarkerFillSymbolLayer;
|
||||
|
||||
/**
|
||||
* \ingroup gui
|
||||
* \class QgsRandomMarkerFillSymbolLayerWidget
|
||||
*/
|
||||
class GUI_EXPORT QgsRandomMarkerFillSymbolLayerWidget: public QgsSymbolLayerWidget, private Ui::WidgetRandomMarkerFill
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for QgsRandomMarkerFillSymbolLayerWidget.
|
||||
* \param vl associated vector layer
|
||||
* \param parent parent widget
|
||||
*/
|
||||
QgsRandomMarkerFillSymbolLayerWidget( QgsVectorLayer *vl, QWidget *parent SIP_TRANSFERTHIS = nullptr );
|
||||
|
||||
/**
|
||||
* Creates a new QgsRandomMarkerFillSymbolLayerWidget.
|
||||
* \param vl associated vector layer
|
||||
*/
|
||||
static QgsSymbolLayerWidget *create( QgsVectorLayer *vl ) SIP_FACTORY { return new QgsRandomMarkerFillSymbolLayerWidget( vl ); }
|
||||
|
||||
void setSymbolLayer( QgsSymbolLayer *layer ) override;
|
||||
QgsSymbolLayer *symbolLayer() override;
|
||||
|
||||
private:
|
||||
QgsRandomMarkerFillSymbolLayer *mLayer = nullptr;
|
||||
|
||||
private slots:
|
||||
|
||||
void countChanged( int d );
|
||||
void seedChanged( int d );
|
||||
};
|
||||
|
||||
/////////
|
||||
|
||||
#include "ui_widget_fontmarker.h"
|
||||
|
120
src/ui/symbollayer/widget_randommarkerfill.ui
Normal file
120
src/ui/symbollayer/widget_randommarkerfill.ui
Normal file
@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>WidgetRandomMarkerFill</class>
|
||||
<widget class="QWidget" name="WidgetRandomMarkerFill">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>401</width>
|
||||
<height>225</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout" columnstretch="0,0,0">
|
||||
<item row="1" column="2">
|
||||
<widget class="QgsPropertyOverrideButton" name="mSeedDdbtn">
|
||||
<property name="text">
|
||||
<string>…</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Seed</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="mPointCountLabel">
|
||||
<property name="text">
|
||||
<string>Point count</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="3">
|
||||
<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="0" column="1">
|
||||
<widget class="QgsSpinBox" name="mPointCountSpinBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>999999999</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QgsSpinBox" name="mSeedSpinBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="specialValueText">
|
||||
<string>randomised</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>999999999</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QgsPropertyOverrideButton" name="mPointCountDdbtn">
|
||||
<property name="text">
|
||||
<string>…</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="mClipPointsCheckBox">
|
||||
<property name="text">
|
||||
<string>Clip markers to polygon boundary</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QgsPropertyOverrideButton" name="mClipPointsDdbtn">
|
||||
<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>QgsSpinBox</class>
|
||||
<extends>QSpinBox</extends>
|
||||
<header>qgsspinbox.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>mPointCountDdbtn</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
Loading…
x
Reference in New Issue
Block a user