Skip to content

Commit 7c7ef6d

Browse files
committed
Fix diagrams not respecting canvas rotation
Also fixes diagrams not showing for invalid polygons (fix #11955)
1 parent 437fc82 commit 7c7ef6d

File tree

3 files changed

+172
-91
lines changed

3 files changed

+172
-91
lines changed

python/core/qgspallabeling.sip

+20
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,17 @@ class QgsPalLabeling : QgsLabelingEngineInterface
742742
bool isStoredWithProject() const /Deprecated/;
743743
//! @deprecated since 2.4 - settings are always stored in project
744744
void setStoredWithProject( bool store ) /Deprecated/;
745+
746+
/** Prepares a geometry for registration with PAL. Handles reprojection, rotation, clipping, etc.
747+
* @param geometry geometry to prepare
748+
* @param context render context
749+
* @param ct coordinate transform
750+
* @param minSize minimum allowable size for feature for registration with PAL
751+
* @param clipGeometry geometry to clip features to, if applicable
752+
* @returns prepared geometry
753+
* @note added in QGIS 2.9
754+
*/
755+
static QgsGeometry* prepareGeometry( QgsGeometry *geometry, const QgsRenderContext &context, const QgsCoordinateTransform *ct, double minSize = 0, QgsGeometry *clipGeometry = 0 ) /Factory/;
745756

746757
protected:
747758
// update temporary QgsPalLayerSettings with any data defined text style values
@@ -765,4 +776,13 @@ class QgsPalLabeling : QgsLabelingEngineInterface
765776
const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues );
766777

767778
void deleteTemporaryData();
779+
780+
/** Checks whether a geometry exceeds the minimum required size for a geometry to be labeled.
781+
* @param context render context
782+
* @param geom geometry
783+
* @param minSize minimum size for geometry
784+
* @returns true if geometry exceeds minimum size
785+
* @note added in QGIS 2.9
786+
*/
787+
static bool checkMinimumSizeMM( const QgsRenderContext &context, QgsGeometry *geom, double minSize );
768788
};

src/core/qgspallabeling.cpp

+131-91
Original file line numberDiff line numberDiff line change
@@ -1723,42 +1723,6 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext
17231723
maxcharangleout = -( qAbs( maxcharangleout ) );
17241724
}
17251725

1726-
QgsGeometry* geom = f.geometry();
1727-
if ( !geom )
1728-
{
1729-
return;
1730-
}
1731-
1732-
// reproject the geometry if necessary (but don't modify the features
1733-
// geometry so that geometry based expression keep working)
1734-
QScopedPointer<QgsGeometry> clonedGeometry;
1735-
if ( ct )
1736-
{
1737-
geom = new QgsGeometry( *geom );
1738-
clonedGeometry.reset( geom );
1739-
1740-
try
1741-
{
1742-
geom->transform( *ct );
1743-
}
1744-
catch ( QgsCsException &cse )
1745-
{
1746-
Q_UNUSED( cse );
1747-
QgsDebugMsgLevel( QString( "Ignoring feature %1 due transformation exception" ).arg( f.id() ), 4 );
1748-
return;
1749-
}
1750-
}
1751-
1752-
if ( !checkMinimumSizeMM( context, geom, minFeatureSize ) )
1753-
{
1754-
return;
1755-
}
1756-
1757-
// whether we're going to create a centroid for polygon
1758-
bool centroidPoly = (( placement == QgsPalLayerSettings::AroundPoint
1759-
|| placement == QgsPalLayerSettings::OverPoint )
1760-
&& geom->type() == QGis::Polygon );
1761-
17621726
// data defined centroid whole or clipped?
17631727
bool wholeCentroid = centroidWhole;
17641728
if ( dataDefinedEvaluate( QgsPalLayerSettings::CentroidWhole, exprVal ) )
@@ -1779,51 +1743,30 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext
17791743
}
17801744
}
17811745

1782-
if ( !geom->asGeos() )
1783-
return; // there is something really wrong with the geometry
1784-
1785-
// fix invalid polygons
1786-
if ( geom->type() == QGis::Polygon && !geom->isGeosValid() )
1746+
QgsGeometry* geom = f.geometry();
1747+
if ( !geom )
17871748
{
1788-
QgsGeometry* bufferGeom = geom->buffer( 0, 0 );
1789-
if ( !bufferGeom )
1790-
{
1791-
return;
1792-
}
1793-
geom = bufferGeom;
1794-
clonedGeometry.reset( geom );
1749+
return;
17951750
}
17961751

1797-
// Rotate the geometry if needed, before clipping
1798-
const QgsMapToPixel& m2p = context.mapToPixel();
1799-
if ( m2p.mapRotation() )
1800-
{
1801-
if ( geom->rotate( m2p.mapRotation(), context.extent().center() ) )
1802-
{
1803-
QgsDebugMsg( QString( "Error rotating geometry" ).arg( geom->exportToWkt() ) );
1804-
return; // really ?
1805-
}
1806-
}
1752+
// whether we're going to create a centroid for polygon
1753+
bool centroidPoly = (( placement == QgsPalLayerSettings::AroundPoint
1754+
|| placement == QgsPalLayerSettings::OverPoint )
1755+
&& geom->type() == QGis::Polygon );
18071756

18081757
// CLIP the geometry if it is bigger than the extent
18091758
// don't clip if centroid is requested for whole feature
1810-
bool do_clip = false;
1759+
bool doClip = false;
18111760
if ( !centroidPoly || ( centroidPoly && !wholeCentroid ) )
18121761
{
1813-
do_clip = !extentGeom->contains( geom );
1814-
if ( do_clip )
1815-
{
1816-
QgsGeometry* clipGeom = geom->intersection( extentGeom ); // creates new geometry
1817-
if ( !clipGeom )
1818-
{
1819-
return;
1820-
}
1821-
geom = clipGeom;
1822-
clonedGeometry.reset( geom );
1823-
}
1762+
doClip = true;
18241763
}
18251764

1826-
const GEOSGeometry* geos_geom = geom->asGeos();
1765+
QScopedPointer<QgsGeometry> preparedGeom( QgsPalLabeling::prepareGeometry( geom, context, ct, minFeatureSize, doClip ? extentGeom : 0 ) );
1766+
if ( !preparedGeom.data() )
1767+
return;
1768+
1769+
const GEOSGeometry* geos_geom = preparedGeom.data()->asGeos();
18271770

18281771
if ( geos_geom == NULL )
18291772
return; // invalid geometry
@@ -1983,6 +1926,7 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext
19831926
angle = angleOffset * M_PI / 180; // convert to radians
19841927
}
19851928

1929+
const QgsMapToPixel& m2p = context.mapToPixel();
19861930
//data defined rotation?
19871931
if ( dataDefinedEvaluate( QgsPalLayerSettings::Rotation, exprVal ) )
19881932
{
@@ -3414,6 +3358,117 @@ void QgsPalLabeling::registerFeature( const QString& layerID, QgsFeature& f, con
34143358
lyr.registerFeature( f, context, dxfLayer );
34153359
}
34163360

3361+
QgsGeometry* QgsPalLabeling::prepareGeometry( QgsGeometry* geometry, const QgsRenderContext& context, const QgsCoordinateTransform* ct, double minSize, QgsGeometry* clipGeometry )
3362+
{
3363+
QgsGeometry* geom = geometry;
3364+
if ( !geom )
3365+
{
3366+
return 0;
3367+
}
3368+
3369+
// reproject the geometry if necessary (but don't modify the features
3370+
// geometry so that geometry based expression keep working)
3371+
QScopedPointer<QgsGeometry> clonedGeometry;
3372+
if ( ct )
3373+
{
3374+
geom = new QgsGeometry( *geom );
3375+
clonedGeometry.reset( geom );
3376+
3377+
try
3378+
{
3379+
geom->transform( *ct );
3380+
}
3381+
catch ( QgsCsException &cse )
3382+
{
3383+
Q_UNUSED( cse );
3384+
QgsDebugMsgLevel( QString( "Ignoring feature due to transformation exception" ), 4 );
3385+
return 0;
3386+
}
3387+
}
3388+
3389+
if ( minSize > 0 && !checkMinimumSizeMM( context, geom, minSize ) )
3390+
{
3391+
return 0;
3392+
}
3393+
3394+
if ( !geom->asGeos() )
3395+
return 0; // there is something really wrong with the geometry
3396+
3397+
// fix invalid polygons
3398+
if ( geom->type() == QGis::Polygon && !geom->isGeosValid() )
3399+
{
3400+
QgsGeometry* bufferGeom = geom->buffer( 0, 0 );
3401+
if ( !bufferGeom )
3402+
{
3403+
return 0;
3404+
}
3405+
geom = bufferGeom;
3406+
clonedGeometry.reset( geom );
3407+
}
3408+
3409+
// Rotate the geometry if needed, before clipping
3410+
const QgsMapToPixel& m2p = context.mapToPixel();
3411+
if ( m2p.mapRotation() )
3412+
{
3413+
if ( geom->rotate( m2p.mapRotation(), context.extent().center() ) )
3414+
{
3415+
QgsDebugMsg( QString( "Error rotating geometry" ).arg( geom->exportToWkt() ) );
3416+
return 0;
3417+
}
3418+
}
3419+
3420+
if ( clipGeometry && !clipGeometry->contains( geom ) )
3421+
{
3422+
QgsGeometry* clipGeom = geom->intersection( clipGeometry ); // creates new geometry
3423+
if ( !clipGeom )
3424+
{
3425+
return 0;
3426+
}
3427+
geom = clipGeom;
3428+
clonedGeometry.reset( geom );
3429+
}
3430+
3431+
return clonedGeometry.take();
3432+
}
3433+
3434+
bool QgsPalLabeling::checkMinimumSizeMM( const QgsRenderContext& context, QgsGeometry* geom, double minSize )
3435+
{
3436+
if ( minSize <= 0 )
3437+
{
3438+
return true;
3439+
}
3440+
3441+
if ( !geom )
3442+
{
3443+
return false;
3444+
}
3445+
3446+
QGis::GeometryType featureType = geom->type();
3447+
if ( featureType == QGis::Point ) //minimum size does not apply to point features
3448+
{
3449+
return true;
3450+
}
3451+
3452+
double mapUnitsPerMM = context.mapToPixel().mapUnitsPerPixel() * context.scaleFactor();
3453+
if ( featureType == QGis::Line )
3454+
{
3455+
double length = geom->length();
3456+
if ( length >= 0.0 )
3457+
{
3458+
return ( length >= ( minSize * mapUnitsPerMM ) );
3459+
}
3460+
}
3461+
else if ( featureType == QGis::Polygon )
3462+
{
3463+
double area = geom->area();
3464+
if ( area >= 0.0 )
3465+
{
3466+
return ( sqrt( area ) >= ( minSize * mapUnitsPerMM ) );
3467+
}
3468+
}
3469+
return true; //should never be reached. Return true in this case to label such geometries anyway.
3470+
}
3471+
34173472
void QgsPalLabeling::registerDiagramFeature( const QString& layerID, QgsFeature& feat, const QgsRenderContext& context )
34183473
{
34193474
//get diagram layer settings, diagram renderer
@@ -3445,28 +3500,13 @@ void QgsPalLabeling::registerDiagramFeature( const QString& layerID, QgsFeature&
34453500

34463501
//convert geom to geos
34473502
QgsGeometry* geom = feat.geometry();
3503+
QScopedPointer<QgsGeometry> extentGeom( QgsGeometry::fromRect( mMapSettings->visibleExtent() ) );
34483504

3449-
// reproject the geometry if necessary (but don't modify the features
3450-
// geometry so that geometry based expression keep working)
3451-
QScopedPointer<QgsGeometry> clonedGeometry;
3452-
if ( layerIt.value().ct )
3453-
{
3454-
geom = new QgsGeometry( *geom );
3455-
clonedGeometry.reset( geom );
3456-
3457-
try
3458-
{
3459-
geom->transform( *( layerIt.value().ct ) );
3460-
}
3461-
catch ( QgsCsException &cse )
3462-
{
3463-
Q_UNUSED( cse );
3464-
QgsDebugMsgLevel( QString( "Ignoring feature %1 due transformation exception" ).arg( feat.id() ), 4 );
3465-
return;
3466-
}
3467-
}
3505+
QScopedPointer<QgsGeometry> preparedGeom( QgsPalLabeling::prepareGeometry( geom, context, layerIt.value().ct, -1, extentGeom.data() ) );
3506+
if ( !preparedGeom.data() )
3507+
return;
34683508

3469-
const GEOSGeometry* geos_geom = geom->asGeos();
3509+
const GEOSGeometry* geos_geom = preparedGeom.data()->asGeos();
34703510
if ( geos_geom == 0 )
34713511
{
34723512
return; // invalid geometry

src/core/qgspallabeling.h

+21
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,17 @@ class CORE_EXPORT QgsPalLabeling : public QgsLabelingEngineInterface
812812
//! @deprecated since 2.4 - settings are always stored in project
813813
Q_DECL_DEPRECATED void setStoredWithProject( bool store ) { Q_UNUSED( store ); }
814814

815+
/** Prepares a geometry for registration with PAL. Handles reprojection, rotation, clipping, etc.
816+
* @param geometry geometry to prepare
817+
* @param context render context
818+
* @param ct coordinate transform
819+
* @param minSize minimum allowable size for feature for registration with PAL
820+
* @param clipGeometry geometry to clip features to, if applicable
821+
* @returns prepared geometry
822+
* @note added in QGIS 2.9
823+
*/
824+
static QgsGeometry* prepareGeometry( QgsGeometry *geometry, const QgsRenderContext &context, const QgsCoordinateTransform *ct, double minSize = 0, QgsGeometry *clipGeometry = 0 );
825+
815826
protected:
816827
// update temporary QgsPalLayerSettings with any data defined text style values
817828
void dataDefinedTextStyle( QgsPalLayerSettings& tmpLyr,
@@ -835,6 +846,15 @@ class CORE_EXPORT QgsPalLabeling : public QgsLabelingEngineInterface
835846

836847
void deleteTemporaryData();
837848

849+
/** Checks whether a geometry exceeds the minimum required size for a geometry to be labeled.
850+
* @param context render context
851+
* @param geom geometry
852+
* @param minSize minimum size for geometry
853+
* @returns true if geometry exceeds minimum size
854+
* @note added in QGIS 2.9
855+
*/
856+
static bool checkMinimumSizeMM( const QgsRenderContext &context, QgsGeometry *geom, double minSize );
857+
838858
// hashtable of layer settings, being filled during labeling (key = layer ID)
839859
QHash<QString, QgsPalLayerSettings> mActiveLayers;
840860
// hashtable of active diagram layers (key = layer ID)
@@ -856,6 +876,7 @@ class CORE_EXPORT QgsPalLabeling : public QgsLabelingEngineInterface
856876
bool mDrawOutlineLabels; // whether to draw labels as text or outlines
857877

858878
QgsLabelingResults* mResults;
879+
859880
};
860881
Q_NOWARN_DEPRECATED_POP
861882

0 commit comments

Comments
 (0)