Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
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 7c7ef6d
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 91 deletions.
20 changes: 20 additions & 0 deletions python/core/qgspallabeling.sip
Expand Up @@ -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
Expand All @@ -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 );
};
222 changes: 131 additions & 91 deletions src/core/qgspallabeling.cpp
Expand Up @@ -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 ) )
Expand All @@ -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
Expand Down Expand Up @@ -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 ) )
{
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
21 changes: 21 additions & 0 deletions src/core/qgspallabeling.h
Expand Up @@ -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,
Expand All @@ -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)
Expand All @@ -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

Expand Down

0 comments on commit 7c7ef6d

Please sign in to comment.