[needs-docs] Rework label engine "maximum line candidates" and "maximum polygon candidates"

settings and logic

The previous approach of a single fixed value which applied to ALL line and ALL polygon
features was... not ideal. It meant that all line features would be assigned the same
number of candidates, regardless of length. So a road of length 1 cm on the rendered
map would have an identical number of candidates as a 30cm road covering the length of the
whole map!! This resulted in both a lot of wasted calculations (generating a ridiculous
number of candidates for small lines at barely discernable distances from each other)
AND an insufficient number of candidates for lengthy features (resulting in worse label
placement for these features).

(The situation was similar, but even worse for polygons)

Now, the setting is reworked to "Number of line candidates per cm" and "number of
polygon candidates per cm2". This means that small features get much less candidates,
and large features get much more features! Both a win for map rendering speed in many
circumstances AND good cartography... now that's a nice Christmas gift for QGIS :)
This commit is contained in:
Nyall Dawson 2019-12-21 07:28:02 +10:00
parent df102a98da
commit 1899f90a04
12 changed files with 254 additions and 130 deletions

View File

@ -73,23 +73,60 @@ Test whether a particular flag is enabled
Sets whether a particual flag is enabled
%End
void numCandidatePositions( int &candPoint, int &candLine, int &candPolygon ) const;
double maximumLineCandidatesPerCm() const;
%Docstring
Returns the maximum number of line label candidate positions per centimeter.
.. seealso:: :py:func:`setMaximumLineCandidatesPerCm`
.. versionadded:: 3.12
%End
void setMaximumLineCandidatesPerCm( double candidates );
%Docstring
Sets the maximum number of line label ``candidates`` per centimeter.
.. seealso:: :py:func:`maximumLineCandidatesPerCm`
.. versionadded:: 3.12
%End
double maximumPolygonCandidatesPerCmSquared() const;
%Docstring
Returns the maximum number of polygon label candidate positions per centimeter squared.
.. seealso:: :py:func:`setMaximumPolygonCandidatesPerCmSquared`
.. versionadded:: 3.12
%End
void setMaximumPolygonCandidatesPerCmSquared( double candidates );
%Docstring
Sets the maximum number of polygon label ``candidates`` per centimeter squared.
.. seealso:: :py:func:`maximumPolygonCandidatesPerCmSquared`
.. versionadded:: 3.12
%End
void numCandidatePositions( int &candPoint, int &candLine, int &candPolygon ) const /Deprecated/;
%Docstring
Gets number of candidate positions that will be generated for each label feature.
.. deprecated:: QGIS 3.12
the ``candPoint`` argument is ignored.
use maximumPolygonCandidatesPerCmSquared() and
maximumLineCandidatesPerCm() instead.
%End
void setNumCandidatePositions( int candPoint, int candLine, int candPolygon );
void setNumCandidatePositions( int candPoint, int candLine, int candPolygon ) /Deprecated/;
%Docstring
Sets the number of candidate positions that will be generated for each label feature.
.. deprecated:: QGIS 3.12
the ``candPoint`` argument is ignored.
use setMaximumPolygonCandidatesPerCmSquared() and
setMaximumLineCandidatesPerCm() instead.
%End
void setSearchMethod( Search s ) /Deprecated/;
%Docstring
Used to set which search method to use for removal collisions between labels

View File

@ -55,11 +55,12 @@ QgsLabelEngineConfigWidget::QgsLabelEngineConfigWidget( QWidget *parent )
}
} );
spinCandLine->setClearValue( 5 );
spinCandPolygon->setClearValue( 10 );
// candidate numbers
int candPoint, candLine, candPolygon;
engineSettings.numCandidatePositions( candPoint, candLine, candPolygon );
spinCandLine->setValue( candLine );
spinCandPolygon->setValue( candPolygon );
spinCandLine->setValue( engineSettings.maximumLineCandidatesPerCm() );
spinCandPolygon->setValue( engineSettings.maximumPolygonCandidatesPerCmSquared() );
chkShowCandidates->setChecked( engineSettings.testFlag( QgsLabelingEngineSettings::DrawCandidates ) );
chkShowAllLabels->setChecked( engineSettings.testFlag( QgsLabelingEngineSettings::UseAllLabels ) );
@ -108,7 +109,8 @@ void QgsLabelEngineConfigWidget::apply()
QgsLabelingEngineSettings engineSettings;
// save
engineSettings.setNumCandidatePositions( 0, spinCandLine->value(), spinCandPolygon->value() );
engineSettings.setMaximumLineCandidatesPerCm( spinCandLine->value() );
engineSettings.setMaximumPolygonCandidatesPerCmSquared( spinCandPolygon->value() );
engineSettings.setFlag( QgsLabelingEngineSettings::DrawCandidates, chkShowCandidates->isChecked() );
engineSettings.setFlag( QgsLabelingEngineSettings::UseAllLabels, chkShowAllLabels->isChecked() );
@ -128,8 +130,8 @@ void QgsLabelEngineConfigWidget::apply()
void QgsLabelEngineConfigWidget::setDefaults()
{
pal::Pal p;
spinCandLine->setValue( p.maximumNumberOfLineCandidates() );
spinCandPolygon->setValue( p.maximumNumberOfPolygonCandidates() );
spinCandLine->setValue( 5 );
spinCandPolygon->setValue( 10 );
chkShowCandidates->setChecked( false );
chkShowAllLabels->setChecked( false );
chkShowPartialsLabels->setChecked( p.showPartialLabels() );

View File

@ -270,11 +270,8 @@ void QgsLabelingEngine::registerLabels( QgsRenderContext &context )
mPal = qgis::make_unique< pal::Pal >();
// set number of candidates generated per feature
int candPoint, candLine, candPolygon;
settings.numCandidatePositions( candPoint, candLine, candPolygon );
mPal->setMaximumNumberOfLineCandidates( candLine );
mPal->setMaximumNumberOfPolygonCandidates( candPolygon );
mPal->setMaximumLineCandidatesPerMapUnit( context.labelingEngine()->engineSettings().maximumLineCandidatesPerCm() / context.convertToMapUnits( 10, QgsUnitTypes::RenderMillimeters ) );
mPal->setMaximumPolygonCandidatesPerMapUnitSquared( context.labelingEngine()->engineSettings().maximumPolygonCandidatesPerCmSquared() / std::pow( context.convertToMapUnits( 10, QgsUnitTypes::RenderMillimeters ), 2 ) );
mPal->setShowPartialLabels( settings.testFlag( QgsLabelingEngineSettings::UsePartialCandidates ) );
mPal->setPlacementVersion( settings.placementVersion() );

View File

@ -32,8 +32,8 @@ void QgsLabelingEngineSettings::readSettingsFromProject( QgsProject *prj )
{
bool saved = false;
mSearchMethod = static_cast< Search >( prj->readNumEntry( QStringLiteral( "PAL" ), QStringLiteral( "/SearchMethod" ), static_cast< int >( Chain ), &saved ) );
mCandLine = prj->readNumEntry( QStringLiteral( "PAL" ), QStringLiteral( "/CandidatesLine" ), 50, &saved );
mCandPolygon = prj->readNumEntry( QStringLiteral( "PAL" ), QStringLiteral( "/CandidatesPolygon" ), 30, &saved );
mMaxLineCandidatesPerCm = prj->readDoubleEntry( QStringLiteral( "PAL" ), QStringLiteral( "/CandidatesLinePerCM" ), 5, &saved );
mMaxPolygonCandidatesPerCmSquared = prj->readDoubleEntry( QStringLiteral( "PAL" ), QStringLiteral( "/CandidatesPolygonPerCM" ), 10, &saved );
mFlags = nullptr;
if ( prj->readBoolEntry( QStringLiteral( "PAL" ), QStringLiteral( "/ShowingCandidates" ), false, &saved ) ) mFlags |= DrawCandidates;
@ -59,8 +59,8 @@ void QgsLabelingEngineSettings::readSettingsFromProject( QgsProject *prj )
void QgsLabelingEngineSettings::writeSettingsToProject( QgsProject *project )
{
project->writeEntry( QStringLiteral( "PAL" ), QStringLiteral( "/SearchMethod" ), static_cast< int >( mSearchMethod ) );
project->writeEntry( QStringLiteral( "PAL" ), QStringLiteral( "/CandidatesLine" ), mCandLine );
project->writeEntry( QStringLiteral( "PAL" ), QStringLiteral( "/CandidatesPolygon" ), mCandPolygon );
project->writeEntry( QStringLiteral( "PAL" ), QStringLiteral( "/CandidatesLinePerCM" ), mMaxLineCandidatesPerCm );
project->writeEntry( QStringLiteral( "PAL" ), QStringLiteral( "/CandidatesPolygonPerCM" ), mMaxPolygonCandidatesPerCmSquared );
project->writeEntry( QStringLiteral( "PAL" ), QStringLiteral( "/ShowingCandidates" ), mFlags.testFlag( DrawCandidates ) );
project->writeEntry( QStringLiteral( "PAL" ), QStringLiteral( "/DrawRectOnly" ), mFlags.testFlag( DrawLabelRectOnly ) );

View File

@ -84,28 +84,61 @@ class CORE_EXPORT QgsLabelingEngineSettings
void setFlag( Flag f, bool enabled = true ) { if ( enabled ) mFlags |= f; else mFlags &= ~f; }
/**
* Gets number of candidate positions that will be generated for each label feature.
* \deprecated Since QGIS 3.12 the \a candPoint argument is ignored.
* Returns the maximum number of line label candidate positions per centimeter.
*
* \see setMaximumLineCandidatesPerCm()
* \since QGIS 3.12
*/
void numCandidatePositions( int &candPoint, int &candLine, int &candPolygon ) const
double maximumLineCandidatesPerCm() const { return mMaxLineCandidatesPerCm; }
/**
* Sets the maximum number of line label \a candidates per centimeter.
*
* \see maximumLineCandidatesPerCm()
* \since QGIS 3.12
*/
void setMaximumLineCandidatesPerCm( double candidates ) { mMaxLineCandidatesPerCm = candidates; }
/**
* Returns the maximum number of polygon label candidate positions per centimeter squared.
*
* \see setMaximumPolygonCandidatesPerCmSquared()
* \since QGIS 3.12
*/
double maximumPolygonCandidatesPerCmSquared() const { return mMaxPolygonCandidatesPerCmSquared; }
/**
* Sets the maximum number of polygon label \a candidates per centimeter squared.
*
* \see maximumPolygonCandidatesPerCmSquared()
* \since QGIS 3.12
*/
void setMaximumPolygonCandidatesPerCmSquared( double candidates ) { mMaxPolygonCandidatesPerCmSquared = candidates; }
/**
* Gets number of candidate positions that will be generated for each label feature.
* \deprecated Since QGIS 3.12 use maximumPolygonCandidatesPerCmSquared() and
* maximumLineCandidatesPerCm() instead.
*/
Q_DECL_DEPRECATED void numCandidatePositions( int &candPoint, int &candLine, int &candPolygon ) const SIP_DEPRECATED
{
Q_UNUSED( candPoint )
candLine = mCandLine;
candPolygon = mCandPolygon;
Q_UNUSED( candLine )
Q_UNUSED( candPolygon )
}
/**
* Sets the number of candidate positions that will be generated for each label feature.
* \deprecated Since QGIS 3.12 the \a candPoint argument is ignored.
* \deprecated Since QGIS 3.12 use setMaximumPolygonCandidatesPerCmSquared() and
* setMaximumLineCandidatesPerCm() instead.
*/
void setNumCandidatePositions( int candPoint, int candLine, int candPolygon )
Q_DECL_DEPRECATED void setNumCandidatePositions( int candPoint, int candLine, int candPolygon ) SIP_DEPRECATED
{
Q_UNUSED( candPoint )
mCandLine = candLine;
mCandPolygon = candPolygon;
Q_UNUSED( candLine )
Q_UNUSED( candPolygon )
}
/**
* Used to set which search method to use for removal collisions between labels
* \deprecated since QGIS 3.10 - Chain is always used.
@ -186,10 +219,10 @@ class CORE_EXPORT QgsLabelingEngineSettings
Flags mFlags;
//! search method to use for removal collisions between labels
Search mSearchMethod = Chain;
//! Number of candedate positions that will be generated for features
int mCandLine = 50, mCandPolygon = 30;
// maximum density of line/polygon candidates per mm
double mMaxLineCandidatesPerCm = 5;
double mMaxPolygonCandidatesPerCmSquared = 10;
QColor mUnplacedLabelColor = QColor( 255, 0, 0 );

View File

@ -157,6 +157,60 @@ QgsFeatureId FeaturePart::featureId() const
return mLF->id();
}
std::size_t FeaturePart::maximumLineCandidates() const
{
if ( mCachedMaxLineCandidates > 0 )
return mCachedMaxLineCandidates;
GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
try
{
double length = 0;
if ( GEOSLength_r( geosctxt, geos(), &length ) == 1 )
{
const std::size_t candidatesForLineLength = static_cast< std::size_t >( std::ceil( mLF->layer()->pal->maximumLineCandidatesPerMapUnit() * length ) );
const std::size_t maxForLayer = mLF->layer()->maximumLineLabelCandidates();
if ( maxForLayer == 0 )
mCachedMaxLineCandidates = candidatesForLineLength;
else
mCachedMaxLineCandidates = std::min( candidatesForLineLength, maxForLayer );
return mCachedMaxLineCandidates;
}
}
catch ( GEOSException & )
{
}
mCachedMaxLineCandidates = 1;
return mCachedMaxLineCandidates;
}
std::size_t FeaturePart::maximumPolygonCandidates() const
{
if ( mCachedMaxPolygonCandidates > 0 )
return mCachedMaxPolygonCandidates;
GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler();
try
{
double area = 0;
if ( GEOSArea_r( geosctxt, geos(), &area ) == 1 )
{
const std::size_t candidatesForArea = static_cast< std::size_t >( std::ceil( mLF->layer()->pal->maximumPolygonCandidatesPerMapUnitSquared() * area ) );
const std::size_t maxForLayer = mLF->layer()->maximumPolygonLabelCandidates();
if ( maxForLayer == 0 )
mCachedMaxPolygonCandidates = candidatesForArea;
else
mCachedMaxPolygonCandidates = std::min( candidatesForArea, maxForLayer );
return mCachedMaxPolygonCandidates;
}
}
catch ( GEOSException & )
{
}
mCachedMaxPolygonCandidates = 1;
return mCachedMaxPolygonCandidates;
}
bool FeaturePart::hasSameLabelFeatureAs( FeaturePart *part ) const
{
if ( !part )
@ -635,7 +689,8 @@ std::size_t FeaturePart::createCandidatesAlongLine( std::vector< std::unique_ptr
//prefer to label along straightish segments:
std::size_t candidates = createCandidatesAlongLineNearStraightSegments( lPos, mapShape, pal );
if ( static_cast< int >( candidates ) < mLF->layer()->maximumLineLabelCandidates() )
const std::size_t candidateTargetCount = maximumLineCandidates();
if ( candidates < candidateTargetCount )
{
// but not enough candidates yet, so fallback to labeling near whole line's midpoint
candidates = createCandidatesAlongLineNearMidpoint( lPos, mapShape, candidates > 0 ? 0.01 : 0.0, pal );
@ -734,8 +789,9 @@ std::size_t FeaturePart::createCandidatesAlongLineNearStraightSegments( std::vec
return 0; //createCandidatesAlongLineNearMidpoint will be more appropriate
}
const std::size_t candidateTargetCount = maximumLineCandidates();
double lineStepDistance = ( totalLineLength - labelWidth ); // distance to move along line with each candidate
lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / mLF->layer()->maximumLineLabelCandidates() );
lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
double distanceToEndOfSegment = 0.0;
int lastNodeInSegment = 0;
@ -906,9 +962,11 @@ std::size_t FeaturePart::createCandidatesAlongLineNearMidpoint( std::vector< std
double lineStepDistance = ( totalLineLength - labelWidth ); // distance to move along line with each candidate
double currentDistanceAlongLine = 0;
const std::size_t candidateTargetCount = maximumLineCandidates();
if ( totalLineLength > labelWidth )
{
lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / mLF->layer()->maximumLineLabelCandidates() );
lineStepDistance = std::min( std::min( labelHeight, labelWidth ), lineStepDistance / candidateTargetCount );
}
else if ( !line->isClosed() ) // line length < label width => centering label position
{
@ -1272,7 +1330,8 @@ std::size_t FeaturePart::createCurvedCandidatesAlongLine( std::vector< std::uniq
return 0;
QLinkedList<LabelPosition *> positions;
double delta = std::max( li->label_height / 6, total_distance / mLF->layer()->maximumLineLabelCandidates() );
const std::size_t candidateTargetCount = maximumLineCandidates();
double delta = std::max( li->label_height / 6, total_distance / candidateTargetCount );
pal::LineArrangementFlags flags = mLF->arrangementFlags();
if ( flags == 0 )

View File

@ -126,6 +126,16 @@ namespace pal
*/
QgsFeatureId featureId() const;
/**
* Returns the maximum number of line candidates to generate for this feature.
*/
std::size_t maximumLineCandidates() const;
/**
* Returns the maximum number of polygon candidates to generate for this feature.
*/
std::size_t maximumPolygonCandidates() const;
/**
* Generates a list of candidate positions for labels for this feature.
*/
@ -349,6 +359,9 @@ namespace pal
LabelPosition::Quadrant quadrantFromOffset() const;
int mTotalRepeats = 0;
mutable std::size_t mCachedMaxLineCandidates = 0;
mutable std::size_t mCachedMaxPolygonCandidates = 0;
};
} // end namespace pal

View File

@ -103,7 +103,7 @@ namespace pal
* Returns the maximum number of point label candidates to generate for features
* in this layer.
*/
int maximumPointLabelCandidates() const
std::size_t maximumPointLabelCandidates() const
{
// when an extreme number of features exist in the layer, we limit the number of candidates
// to avoid the engine processing endlessly...
@ -124,42 +124,42 @@ namespace pal
* Returns the maximum number of line label candidates to generate for features
* in this layer.
*/
int maximumLineLabelCandidates() const
std::size_t maximumLineLabelCandidates() const
{
// when an extreme number of features exist in the layer, we limit the number of candidates
// to avoid the engine processing endlessly...
const int size = mHashtable.size();
if ( size > 1000 )
return std::min( pal->mMaxLineCandidates, 5 );
return static_cast< std::size_t >( 5 );
else if ( size > 500 )
return std::min( pal->mMaxLineCandidates, 10 );
return static_cast< std::size_t >( 10 );
else if ( size > 200 )
return std::min( pal->mMaxLineCandidates, 20 );
return static_cast< std::size_t >( 20 );
else if ( size > 100 )
return std::min( pal->mMaxLineCandidates, 40 );
return static_cast< std::size_t >( 40 );
else
return pal->mMaxLineCandidates;
return static_cast< std::size_t >( 0 );
}
/**
* Returns the maximum number of polygon label candidates to generate for features
* in this layer.
*/
int maximumPolygonLabelCandidates() const
std::size_t maximumPolygonLabelCandidates() const
{
// when an extreme number of features exist in the layer, we limit the number of candidates
// to avoid the engine processing endlessly...
const int size = mHashtable.size();
if ( size > 1000 )
return std::min( pal->mMaxPolyCandidates, 5 );
return static_cast< std::size_t >( 5 );
else if ( size > 500 )
return std::min( pal->mMaxPolyCandidates, 15 );
return static_cast< std::size_t >( 15 );
else if ( size > 200 )
return std::min( pal->mMaxPolyCandidates, 20 );
return static_cast< std::size_t >( 20 );
else if ( size > 100 )
return std::min( pal->mMaxPolyCandidates, 25 );
return static_cast< std::size_t >( 25 );
else
return pal->mMaxPolyCandidates;
return static_cast< std::size_t >( 0 );
}
//! Returns pointer to the associated provider

View File

@ -295,11 +295,11 @@ std::unique_ptr<Problem> Pal::extract( const QgsRectangle &extent, const QgsGeom
break;
case GEOS_LINESTRING:
max_p = feat->feature->layer()->maximumLineLabelCandidates();
max_p = feat->feature->maximumLineCandidates();
break;
case GEOS_POLYGON:
max_p = feat->feature->layer()->maximumPolygonLabelCandidates();
max_p = feat->feature->maximumPolygonCandidates();
break;
}
@ -443,19 +443,6 @@ QList<LabelPosition *> Pal::solveProblem( Problem *prob, bool displayAll, QList<
return prob->getSolution( displayAll, unlabeled );
}
void Pal::setMaximumNumberOfLineCandidates( int line_p )
{
if ( line_p > 0 )
this->mMaxLineCandidates = line_p;
}
void Pal::setMaximumNumberOfPolygonCandidates( int poly_p )
{
if ( poly_p > 0 )
this->mMaxPolyCandidates = poly_p;
}
void Pal::setMinIt( int min_it )
{
if ( min_it >= 0 )
@ -494,16 +481,6 @@ void Pal::setShowPartialLabels( bool show )
this->mShowPartialLabels = show;
}
int Pal::maximumNumberOfLineCandidates() const
{
return mMaxLineCandidates;
}
int Pal::maximumNumberOfPolygonCandidates() const
{
return mMaxPolyCandidates;
}
QgsLabelingEngineSettings::PlacementEngineVersion Pal::placementVersion() const
{
return mPlacementVersion;

View File

@ -174,28 +174,32 @@ namespace pal
bool showPartialLabels() const;
/**
* Sets the maximum number of candidates to generate for line features.
* Returns the maximum number of line label candidate positions per map unit.
*
* The larger the value, the longer the labeling solution will take to calculate.
* \see setMaximumLineCandidatesPerCm()
*/
void setMaximumNumberOfLineCandidates( int candidates );
double maximumLineCandidatesPerMapUnit() const { return mMaxLineCandidatesPerMapUnit; }
/**
* Sets the maximum number of candidates to generate for polygon features.
* Sets the maximum number of line label \a candidates per map unit.
*
* The larger the value, the longer the labeling solution will take to calculate.
* \see maximumLineCandidatesPerMapUnit()
*/
void setMaximumNumberOfPolygonCandidates( int candidates );
void setMaximumLineCandidatesPerMapUnit( double candidates ) { mMaxLineCandidatesPerMapUnit = candidates; }
/**
* Returns the number of candidates to generate for line features.
* Returns the maximum number of polygon label candidate positions per map unit squared.
*
* \see setMaximumPolygonCandidatesPerMapUnitSquared()
*/
int maximumNumberOfLineCandidates() const;
double maximumPolygonCandidatesPerMapUnitSquared() const { return mMaxPolygonCandidatesPerMapUnitSquared; }
/**
* Returns the number of candidates to generate for polygon features.
* Sets the maximum number of polygon label \a candidates per map unit squared.
*
* \see maximumPolygonCandidatesPerMapUnitSquared()
*/
int maximumNumberOfPolygonCandidates() const;
void setMaximumPolygonCandidatesPerMapUnitSquared( double candidates ) { mMaxPolygonCandidatesPerMapUnitSquared = candidates; }
/**
* Returns the placement engine version, which dictates how the label placement problem is solved.
@ -217,16 +221,6 @@ namespace pal
QMutex mMutex;
/**
* Maximum number of candidates for a line.
*/
int mMaxLineCandidates = 50;
/**
* Maximum number of candidates for a polygon.
*/
int mMaxPolyCandidates = 30;
/*
* POPMUSIC Tuning
*/
@ -244,6 +238,9 @@ namespace pal
*/
bool mShowPartialLabels = true;
double mMaxLineCandidatesPerMapUnit = 0;
double mMaxPolygonCandidatesPerMapUnitSquared = 0;
QgsLabelingEngineSettings::PlacementEngineVersion mPlacementVersion = QgsLabelingEngineSettings::PlacementEngineVersion2;
//! Callback that may be called from PAL to check whether the job has not been canceled in meanwhile

View File

@ -50,32 +50,6 @@
<string>Number of Candidates</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QSpinBox" name="spinCandLine">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>999</number>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="spinCandPolygon">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>999</number>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="sizePolicy">
@ -85,7 +59,7 @@
</sizepolicy>
</property>
<property name="text">
<string>Line</string>
<string>Line (per cm)</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
@ -101,13 +75,45 @@
</sizepolicy>
</property>
<property name="text">
<string>Polygon</string>
<string>Polygon (per cm²)</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QgsDoubleSpinBox" name="spinCandLine">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="decimals">
<number>1</number>
</property>
<property name="maximum">
<double>1000.000000000000000</double>
</property>
<property name="singleStep">
<double>1.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QgsDoubleSpinBox" name="spinCandPolygon">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="decimals">
<number>1</number>
</property>
<property name="maximum">
<double>1000.000000000000000</double>
</property>
<property name="singleStep">
<double>1.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -257,6 +263,11 @@
<header>qgspanelwidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsDoubleSpinBox</class>
<extends>QDoubleSpinBox</extends>
<header>qgsdoublespinbox.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>spinCandLine</tabstop>

View File

@ -122,13 +122,11 @@ void TestQgsMapSettings::testLabelingEngineSettings()
// test that setting labeling engine settings for QgsMapSettings works
QgsMapSettings ms;
QgsLabelingEngineSettings les;
les.setNumCandidatePositions( 4, 8, 15 ); // 23, 42... ;)
les.setMaximumLineCandidatesPerCm( 4 );
les.setMaximumPolygonCandidatesPerCmSquared( 8.0 );
ms.setLabelingEngineSettings( les );
int c1, c2, c3;
ms.labelingEngineSettings().numCandidatePositions( c1, c2, c3 );
QCOMPARE( c1, 4 );
QCOMPARE( c2, 8 );
QCOMPARE( c3, 15 );
QCOMPARE( ms.labelingEngineSettings().maximumLineCandidatesPerCm(), 4.0 );
QCOMPARE( ms.labelingEngineSettings().maximumPolygonCandidatesPerCmSquared(), 8.0 );
// ensure that setting labeling engine settings also sets text format
les.setDefaultTextRenderFormat( QgsRenderContext::TextFormatAlwaysText );