mirror of
https://github.com/qgis/QGIS.git
synced 2025-06-19 00:02:48 -04:00
[FEATURE][API] Add method to QgsMapSettings for specifying label blocking regions
These represent areas of the map where NO labels should be placed
This commit is contained in:
parent
ed25a3e2ee
commit
d8eac4797e
@ -11,6 +11,29 @@
|
||||
|
||||
|
||||
|
||||
class QgsLabelBlockingRegion
|
||||
{
|
||||
%Docstring
|
||||
|
||||
Label blocking region (in map coordinates and CRS).
|
||||
|
||||
.. versionadded:: 3.6
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgsmapsettings.h"
|
||||
%End
|
||||
public:
|
||||
|
||||
explicit QgsLabelBlockingRegion( const QgsGeometry &geometry );
|
||||
%Docstring
|
||||
Constructor for a label blocking region
|
||||
%End
|
||||
|
||||
QgsGeometry geometry;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class QgsMapSettings
|
||||
{
|
||||
@ -541,6 +564,8 @@ The geometry is specified using the map's destinationCrs().
|
||||
|
||||
.. seealso:: :py:func:`setLabelBoundaryGeometry`
|
||||
|
||||
.. seealso:: :py:func:`labelBlockingRegions`
|
||||
|
||||
.. versionadded:: 3.6
|
||||
%End
|
||||
|
||||
@ -556,7 +581,31 @@ The geometry is specified using the map's destinationCrs().
|
||||
|
||||
.. seealso:: :py:func:`labelBoundaryGeometry`
|
||||
|
||||
.. seealso:: :py:func:`setLabelBlockingRegions`
|
||||
|
||||
.. versionadded:: 3.6
|
||||
%End
|
||||
|
||||
void setLabelBlockingRegions( const QList< QgsLabelBlockingRegion > ®ions );
|
||||
%Docstring
|
||||
Sets a list of ``regions`` to avoid placing labels within.
|
||||
|
||||
.. versionadded:: 3.6
|
||||
|
||||
.. seealso:: :py:func:`labelBlockingRegions`
|
||||
|
||||
.. seealso:: :py:func:`setLabelBoundaryGeometry`
|
||||
%End
|
||||
|
||||
QList< QgsLabelBlockingRegion > labelBlockingRegions() const;
|
||||
%Docstring
|
||||
Returns the list of regions to avoid placing labels within.
|
||||
|
||||
.. versionadded:: 3.6
|
||||
|
||||
.. seealso:: :py:func:`setLabelBlockingRegions`
|
||||
|
||||
.. seealso:: :py:func:`labelBoundaryGeometry`
|
||||
%End
|
||||
|
||||
protected:
|
||||
@ -578,7 +627,9 @@ The geometry is specified using the map's destinationCrs().
|
||||
|
||||
|
||||
|
||||
|
||||
void updateDerived();
|
||||
|
||||
};
|
||||
|
||||
QFlags<QgsMapSettings::Flag> operator|(QgsMapSettings::Flag f1, QFlags<QgsMapSettings::Flag> f2);
|
||||
|
@ -247,6 +247,14 @@ void QgsLabelingEngine::run( QgsRenderContext &context )
|
||||
|
||||
// get map label boundary geometry - if one hasn't been explicitly set, we use the whole of the map's visible polygon
|
||||
QgsGeometry mapBoundaryGeom = !mMapSettings.labelBoundaryGeometry().isNull() ? mMapSettings.labelBoundaryGeometry() : QgsGeometry::fromQPolygonF( visiblePoly );
|
||||
|
||||
// label blocking regions work by "chopping away" those regions from the permissible labelling area
|
||||
const QList< QgsLabelBlockingRegion > blockingRegions = mMapSettings.labelBlockingRegions();
|
||||
for ( const QgsLabelBlockingRegion ®ion : blockingRegions )
|
||||
{
|
||||
mapBoundaryGeom = mapBoundaryGeom.difference( region.geometry );
|
||||
}
|
||||
|
||||
if ( settings.flags() & QgsLabelingEngineSettings::DrawCandidates )
|
||||
{
|
||||
// draw map boundary
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "qgsscalecalculator.h"
|
||||
#include "qgsexpressioncontext.h"
|
||||
#include "qgsmaplayer.h"
|
||||
#include "qgsgeometry.h"
|
||||
|
||||
class QPainter;
|
||||
|
||||
@ -39,6 +40,30 @@ class QgsCoordinateTransform;
|
||||
class QgsScaleCalculator;
|
||||
class QgsMapRendererJob;
|
||||
|
||||
/**
|
||||
* \class QgsLabelBlockingRegion
|
||||
* \ingroup core
|
||||
*
|
||||
* Label blocking region (in map coordinates and CRS).
|
||||
*
|
||||
* \since QGIS 3.6
|
||||
*/
|
||||
class CORE_EXPORT QgsLabelBlockingRegion
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for a label blocking region
|
||||
*/
|
||||
explicit QgsLabelBlockingRegion( const QgsGeometry &geometry )
|
||||
: geometry( geometry )
|
||||
{}
|
||||
|
||||
//! Geometry of region to avoid placing labels within (in destination map coordinates and CRS)
|
||||
QgsGeometry geometry;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \ingroup core
|
||||
@ -471,6 +496,7 @@ class CORE_EXPORT QgsMapSettings
|
||||
* The geometry is specified using the map's destinationCrs().
|
||||
*
|
||||
* \see setLabelBoundaryGeometry()
|
||||
* \see labelBlockingRegions()
|
||||
* \since QGIS 3.6
|
||||
*/
|
||||
QgsGeometry labelBoundaryGeometry() const;
|
||||
@ -485,10 +511,27 @@ class CORE_EXPORT QgsMapSettings
|
||||
* The geometry is specified using the map's destinationCrs().
|
||||
*
|
||||
* \see labelBoundaryGeometry()
|
||||
* \see setLabelBlockingRegions()
|
||||
* \since QGIS 3.6
|
||||
*/
|
||||
void setLabelBoundaryGeometry( const QgsGeometry &boundary );
|
||||
|
||||
/**
|
||||
* Sets a list of \a regions to avoid placing labels within.
|
||||
* \since QGIS 3.6
|
||||
* \see labelBlockingRegions()
|
||||
* \see setLabelBoundaryGeometry()
|
||||
*/
|
||||
void setLabelBlockingRegions( const QList< QgsLabelBlockingRegion > ®ions ) { mLabelBlockingRegions = regions; }
|
||||
|
||||
/**
|
||||
* Returns the list of regions to avoid placing labels within.
|
||||
* \since QGIS 3.6
|
||||
* \see setLabelBlockingRegions()
|
||||
* \see labelBoundaryGeometry()
|
||||
*/
|
||||
QList< QgsLabelBlockingRegion > labelBlockingRegions() const { return mLabelBlockingRegions; }
|
||||
|
||||
protected:
|
||||
|
||||
double mDpi;
|
||||
@ -546,6 +589,10 @@ class CORE_EXPORT QgsMapSettings
|
||||
#endif
|
||||
|
||||
void updateDerived();
|
||||
|
||||
private:
|
||||
|
||||
QList< QgsLabelBlockingRegion > mLabelBlockingRegions;
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsMapSettings::Flags )
|
||||
|
@ -53,6 +53,7 @@ class TestQgsLabelingEngine : public QObject
|
||||
void testRotateHidePartial();
|
||||
void testParallelLabelSmallFeature();
|
||||
void testLabelBoundary();
|
||||
void testLabelBlockingRegion();
|
||||
|
||||
private:
|
||||
QgsVectorLayer *vl = nullptr;
|
||||
@ -874,5 +875,89 @@ void TestQgsLabelingEngine::testLabelBoundary()
|
||||
QVERIFY( imageCheck( QStringLiteral( "rotated_label_boundary_geometry" ), img, 20 ) );
|
||||
}
|
||||
|
||||
void TestQgsLabelingEngine::testLabelBlockingRegion()
|
||||
{
|
||||
// test that no labels are drawn inside blocking regions
|
||||
QgsPalLayerSettings settings;
|
||||
setDefaultLabelParams( settings );
|
||||
|
||||
QgsTextFormat format = settings.format();
|
||||
format.setSize( 20 );
|
||||
format.setColor( QColor( 0, 0, 0 ) );
|
||||
settings.setFormat( format );
|
||||
|
||||
settings.fieldName = QStringLiteral( "'X'" );
|
||||
settings.isExpression = true;
|
||||
settings.placement = QgsPalLayerSettings::OverPoint;
|
||||
|
||||
std::unique_ptr< QgsVectorLayer> vl2( new QgsVectorLayer( QStringLiteral( "Point?crs=epsg:4326&field=id:integer" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) ) );
|
||||
vl2->setRenderer( new QgsNullSymbolRenderer() );
|
||||
|
||||
QgsFeature f( vl2->fields(), 1 );
|
||||
|
||||
for ( int x = 0; x < 15; x++ )
|
||||
{
|
||||
for ( int y = 0; y < 12; y++ )
|
||||
{
|
||||
f.setGeometry( qgis::make_unique< QgsPoint >( x, y ) );
|
||||
vl2->dataProvider()->addFeature( f );
|
||||
}
|
||||
}
|
||||
|
||||
vl2->setLabeling( new QgsVectorLayerSimpleLabeling( settings ) ); // TODO: this should not be necessary!
|
||||
vl2->setLabelsEnabled( true );
|
||||
|
||||
// make a fake render context
|
||||
QSize size( 640, 480 );
|
||||
QgsMapSettings mapSettings;
|
||||
QgsCoordinateReferenceSystem tgtCrs;
|
||||
tgtCrs.createFromString( QStringLiteral( "EPSG:4326" ) );
|
||||
mapSettings.setDestinationCrs( tgtCrs );
|
||||
|
||||
mapSettings.setOutputSize( size );
|
||||
mapSettings.setExtent( vl2->extent() );
|
||||
mapSettings.setLayers( QList<QgsMapLayer *>() << vl2.get() );
|
||||
mapSettings.setOutputDpi( 96 );
|
||||
|
||||
QList< QgsLabelBlockingRegion > regions;
|
||||
regions << QgsLabelBlockingRegion( QgsGeometry::fromWkt( QStringLiteral( "Polygon((6 1, 12 1, 12 9, 6 9, 6 1),(8 4, 10 4, 10 7, 8 7, 8 4))" ) ) );
|
||||
regions << QgsLabelBlockingRegion( QgsGeometry::fromWkt( QStringLiteral( "Polygon((0 0, 3 0, 3 3, 0 3, 0 0))" ) ) );
|
||||
mapSettings.setLabelBlockingRegions( regions );
|
||||
|
||||
QgsLabelingEngineSettings engineSettings = mapSettings.labelingEngineSettings();
|
||||
engineSettings.setFlag( QgsLabelingEngineSettings::UsePartialCandidates, false );
|
||||
engineSettings.setFlag( QgsLabelingEngineSettings::DrawLabelRectOnly, true );
|
||||
//engineSettings.setFlag( QgsLabelingEngineSettings::DrawCandidates, true );
|
||||
mapSettings.setLabelingEngineSettings( engineSettings );
|
||||
|
||||
QgsMapRendererSequentialJob job( mapSettings );
|
||||
job.start();
|
||||
job.waitForFinished();
|
||||
|
||||
QImage img = job.renderedImage();
|
||||
QVERIFY( imageCheck( QStringLiteral( "label_blocking_geometry" ), img, 20 ) );
|
||||
|
||||
// with rotation
|
||||
mapSettings.setRotation( 45 );
|
||||
QgsMapRendererSequentialJob job2( mapSettings );
|
||||
job2.start();
|
||||
job2.waitForFinished();
|
||||
|
||||
img = job2.renderedImage();
|
||||
QVERIFY( imageCheck( QStringLiteral( "rotated_label_blocking_geometry" ), img, 20 ) );
|
||||
|
||||
// blocking regions WITH label margin
|
||||
mapSettings.setRotation( 0 );
|
||||
mapSettings.setLabelBoundaryGeometry( QgsGeometry::fromWkt( QStringLiteral( "Polygon((1 1, 14 1, 14 9, 1 9, 1 1))" ) ) );
|
||||
|
||||
QgsMapRendererSequentialJob job3( mapSettings );
|
||||
job3.start();
|
||||
job3.waitForFinished();
|
||||
|
||||
img = job3.renderedImage();
|
||||
QVERIFY( imageCheck( QStringLiteral( "label_blocking_boundary_geometry" ), img, 20 ) );
|
||||
|
||||
}
|
||||
|
||||
QGSTEST_MAIN( TestQgsLabelingEngine )
|
||||
#include "testqgslabelingengine.moc"
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
Loading…
x
Reference in New Issue
Block a user