Skip to content
Permalink
Browse files

Fix diagrams not respecting canvas rotation

Also fixes diagrams not showing for invalid polygons (fix #11955)
  • Loading branch information
nyalldawson committed Apr 13, 2015
1 parent 437fc82 commit 7c7ef6d8cdfc547c8bafc9b36c2848c17239a259
Showing with 172 additions and 91 deletions.
  1. +20 −0 python/core/qgspallabeling.sip
  2. +131 −91 src/core/qgspallabeling.cpp
  3. +21 −0 src/core/qgspallabeling.h
@@ -742,6 +742,17 @@ class QgsPalLabeling : QgsLabelingEngineInterface
bool isStoredWithProject() const /Deprecated/;
//! @deprecated since 2.4 - settings are always stored in project
void setStoredWithProject( bool store ) /Deprecated/;

/** Prepares a geometry for registration with PAL. Handles reprojection, rotation, clipping, etc.
* @param geometry geometry to prepare
* @param context render context
* @param ct coordinate transform
* @param minSize minimum allowable size for feature for registration with PAL
* @param clipGeometry geometry to clip features to, if applicable
* @returns prepared geometry
* @note added in QGIS 2.9
*/
static QgsGeometry* prepareGeometry( QgsGeometry *geometry, const QgsRenderContext &context, const QgsCoordinateTransform *ct, double minSize = 0, QgsGeometry *clipGeometry = 0 ) /Factory/;

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

void deleteTemporaryData();

/** Checks whether a geometry exceeds the minimum required size for a geometry to be labeled.
* @param context render context
* @param geom geometry
* @param minSize minimum size for geometry
* @returns true if geometry exceeds minimum size
* @note added in QGIS 2.9
*/
static bool checkMinimumSizeMM( const QgsRenderContext &context, QgsGeometry *geom, double minSize );
};
@@ -1723,42 +1723,6 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext
maxcharangleout = -( qAbs( maxcharangleout ) );
}

QgsGeometry* geom = f.geometry();
if ( !geom )
{
return;
}

// reproject the geometry if necessary (but don't modify the features
// geometry so that geometry based expression keep working)
QScopedPointer<QgsGeometry> clonedGeometry;
if ( ct )
{
geom = new QgsGeometry( *geom );
clonedGeometry.reset( geom );

try
{
geom->transform( *ct );
}
catch ( QgsCsException &cse )
{
Q_UNUSED( cse );
QgsDebugMsgLevel( QString( "Ignoring feature %1 due transformation exception" ).arg( f.id() ), 4 );
return;
}
}

if ( !checkMinimumSizeMM( context, geom, minFeatureSize ) )
{
return;
}

// whether we're going to create a centroid for polygon
bool centroidPoly = (( placement == QgsPalLayerSettings::AroundPoint
|| placement == QgsPalLayerSettings::OverPoint )
&& geom->type() == QGis::Polygon );

// data defined centroid whole or clipped?
bool wholeCentroid = centroidWhole;
if ( dataDefinedEvaluate( QgsPalLayerSettings::CentroidWhole, exprVal ) )
@@ -1779,51 +1743,30 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext
}
}

if ( !geom->asGeos() )
return; // there is something really wrong with the geometry

// fix invalid polygons
if ( geom->type() == QGis::Polygon && !geom->isGeosValid() )
QgsGeometry* geom = f.geometry();
if ( !geom )
{
QgsGeometry* bufferGeom = geom->buffer( 0, 0 );
if ( !bufferGeom )
{
return;
}
geom = bufferGeom;
clonedGeometry.reset( geom );
return;
}

// Rotate the geometry if needed, before clipping
const QgsMapToPixel& m2p = context.mapToPixel();
if ( m2p.mapRotation() )
{
if ( geom->rotate( m2p.mapRotation(), context.extent().center() ) )
{
QgsDebugMsg( QString( "Error rotating geometry" ).arg( geom->exportToWkt() ) );
return; // really ?
}
}
// whether we're going to create a centroid for polygon
bool centroidPoly = (( placement == QgsPalLayerSettings::AroundPoint
|| placement == QgsPalLayerSettings::OverPoint )
&& geom->type() == QGis::Polygon );

// CLIP the geometry if it is bigger than the extent
// don't clip if centroid is requested for whole feature
bool do_clip = false;
bool doClip = false;
if ( !centroidPoly || ( centroidPoly && !wholeCentroid ) )
{
do_clip = !extentGeom->contains( geom );
if ( do_clip )
{
QgsGeometry* clipGeom = geom->intersection( extentGeom ); // creates new geometry
if ( !clipGeom )
{
return;
}
geom = clipGeom;
clonedGeometry.reset( geom );
}
doClip = true;
}

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

const GEOSGeometry* geos_geom = preparedGeom.data()->asGeos();

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

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

QgsGeometry* QgsPalLabeling::prepareGeometry( QgsGeometry* geometry, const QgsRenderContext& context, const QgsCoordinateTransform* ct, double minSize, QgsGeometry* clipGeometry )
{
QgsGeometry* geom = geometry;
if ( !geom )
{
return 0;
}

// reproject the geometry if necessary (but don't modify the features
// geometry so that geometry based expression keep working)
QScopedPointer<QgsGeometry> clonedGeometry;
if ( ct )
{
geom = new QgsGeometry( *geom );
clonedGeometry.reset( geom );

try
{
geom->transform( *ct );
}
catch ( QgsCsException &cse )
{
Q_UNUSED( cse );
QgsDebugMsgLevel( QString( "Ignoring feature due to transformation exception" ), 4 );
return 0;
}
}

if ( minSize > 0 && !checkMinimumSizeMM( context, geom, minSize ) )
{
return 0;
}

if ( !geom->asGeos() )
return 0; // there is something really wrong with the geometry

// fix invalid polygons
if ( geom->type() == QGis::Polygon && !geom->isGeosValid() )
{
QgsGeometry* bufferGeom = geom->buffer( 0, 0 );
if ( !bufferGeom )
{
return 0;
}
geom = bufferGeom;
clonedGeometry.reset( geom );
}

// Rotate the geometry if needed, before clipping
const QgsMapToPixel& m2p = context.mapToPixel();
if ( m2p.mapRotation() )
{
if ( geom->rotate( m2p.mapRotation(), context.extent().center() ) )
{
QgsDebugMsg( QString( "Error rotating geometry" ).arg( geom->exportToWkt() ) );
return 0;
}
}

if ( clipGeometry && !clipGeometry->contains( geom ) )
{
QgsGeometry* clipGeom = geom->intersection( clipGeometry ); // creates new geometry
if ( !clipGeom )
{
return 0;
}
geom = clipGeom;
clonedGeometry.reset( geom );
}

return clonedGeometry.take();
}

bool QgsPalLabeling::checkMinimumSizeMM( const QgsRenderContext& context, QgsGeometry* geom, double minSize )
{
if ( minSize <= 0 )
{
return true;
}

if ( !geom )
{
return false;
}

QGis::GeometryType featureType = geom->type();
if ( featureType == QGis::Point ) //minimum size does not apply to point features
{
return true;
}

double mapUnitsPerMM = context.mapToPixel().mapUnitsPerPixel() * context.scaleFactor();
if ( featureType == QGis::Line )
{
double length = geom->length();
if ( length >= 0.0 )
{
return ( length >= ( minSize * mapUnitsPerMM ) );
}
}
else if ( featureType == QGis::Polygon )
{
double area = geom->area();
if ( area >= 0.0 )
{
return ( sqrt( area ) >= ( minSize * mapUnitsPerMM ) );
}
}
return true; //should never be reached. Return true in this case to label such geometries anyway.
}

void QgsPalLabeling::registerDiagramFeature( const QString& layerID, QgsFeature& feat, const QgsRenderContext& context )
{
//get diagram layer settings, diagram renderer
@@ -3445,28 +3500,13 @@ void QgsPalLabeling::registerDiagramFeature( const QString& layerID, QgsFeature&

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

// reproject the geometry if necessary (but don't modify the features
// geometry so that geometry based expression keep working)
QScopedPointer<QgsGeometry> clonedGeometry;
if ( layerIt.value().ct )
{
geom = new QgsGeometry( *geom );
clonedGeometry.reset( geom );

try
{
geom->transform( *( layerIt.value().ct ) );
}
catch ( QgsCsException &cse )
{
Q_UNUSED( cse );
QgsDebugMsgLevel( QString( "Ignoring feature %1 due transformation exception" ).arg( feat.id() ), 4 );
return;
}
}
QScopedPointer<QgsGeometry> preparedGeom( QgsPalLabeling::prepareGeometry( geom, context, layerIt.value().ct, -1, extentGeom.data() ) );
if ( !preparedGeom.data() )
return;

const GEOSGeometry* geos_geom = geom->asGeos();
const GEOSGeometry* geos_geom = preparedGeom.data()->asGeos();
if ( geos_geom == 0 )
{
return; // invalid geometry
@@ -812,6 +812,17 @@ class CORE_EXPORT QgsPalLabeling : public QgsLabelingEngineInterface
//! @deprecated since 2.4 - settings are always stored in project
Q_DECL_DEPRECATED void setStoredWithProject( bool store ) { Q_UNUSED( store ); }

/** Prepares a geometry for registration with PAL. Handles reprojection, rotation, clipping, etc.
* @param geometry geometry to prepare
* @param context render context
* @param ct coordinate transform
* @param minSize minimum allowable size for feature for registration with PAL
* @param clipGeometry geometry to clip features to, if applicable
* @returns prepared geometry
* @note added in QGIS 2.9
*/
static QgsGeometry* prepareGeometry( QgsGeometry *geometry, const QgsRenderContext &context, const QgsCoordinateTransform *ct, double minSize = 0, QgsGeometry *clipGeometry = 0 );

protected:
// update temporary QgsPalLayerSettings with any data defined text style values
void dataDefinedTextStyle( QgsPalLayerSettings& tmpLyr,
@@ -835,6 +846,15 @@ class CORE_EXPORT QgsPalLabeling : public QgsLabelingEngineInterface

void deleteTemporaryData();

/** Checks whether a geometry exceeds the minimum required size for a geometry to be labeled.
* @param context render context
* @param geom geometry
* @param minSize minimum size for geometry
* @returns true if geometry exceeds minimum size
* @note added in QGIS 2.9
*/
static bool checkMinimumSizeMM( const QgsRenderContext &context, QgsGeometry *geom, double minSize );

// hashtable of layer settings, being filled during labeling (key = layer ID)
QHash<QString, QgsPalLayerSettings> mActiveLayers;
// hashtable of active diagram layers (key = layer ID)
@@ -856,6 +876,7 @@ class CORE_EXPORT QgsPalLabeling : public QgsLabelingEngineInterface
bool mDrawOutlineLabels; // whether to draw labels as text or outlines

QgsLabelingResults* mResults;

};
Q_NOWARN_DEPRECATED_POP

0 comments on commit 7c7ef6d

Please sign in to comment.
You can’t perform that action at this time.