Skip to content

Commit

Permalink
dxf export: support rule based labeling (fixes #13757)
Browse files Browse the repository at this point in the history
(cherry picked from commit f19a35c)
  • Loading branch information
jef-n committed Jun 26, 2016
1 parent 7025fba commit 5ce8a45
Show file tree
Hide file tree
Showing 15 changed files with 264 additions and 166 deletions.
14 changes: 7 additions & 7 deletions python/core/qgsmaprenderer.sip
Expand Up @@ -35,7 +35,7 @@ class QgsLabelingEngineInterface

//! called when we're going to start with rendering
//! @deprecated since 2.4 - use override with QgsMapSettings
virtual void init( QgsMapRenderer* mp ) = 0 /Deprecated/;
virtual void init( QgsMapRenderer *mp ) = 0 /Deprecated/;
//! called when we're going to start with rendering
virtual void init( const QgsMapSettings& mapSettings ) = 0;
//! called to find out whether the layer is used for labeling
Expand All @@ -48,17 +48,17 @@ class QgsLabelingEngineInterface
virtual int prepareLayer( QgsVectorLayer* layer, QStringList& attrNames, QgsRenderContext& ctx ) = 0;
//! returns PAL layer settings for a registered layer
//! @deprecated since 2.12 - if direct access to QgsPalLayerSettings is necessary, use QgsPalLayerSettings::fromLayer()
virtual QgsPalLayerSettings& layer( const QString& layerName ) = 0 /Deprecated/;
virtual QgsPalLayerSettings &layer( const QString &layerName ) = 0 /Deprecated/;
//! adds a diagram layer to the labeling engine
//! @note added in QGIS 2.12
virtual int prepareDiagramLayer( QgsVectorLayer* layer, QStringList& attrNames, QgsRenderContext& ctx );
virtual int prepareDiagramLayer( QgsVectorLayer *layer, QStringList &attrNames, QgsRenderContext &ctx );
//! adds a diagram layer to the labeling engine
//! @deprecated since 2.12 - use prepareDiagramLayer()
virtual int addDiagramLayer( QgsVectorLayer* layer, const QgsDiagramLayerSettings* s ) /Deprecated/;
virtual int addDiagramLayer( QgsVectorLayer *layer, const QgsDiagramLayerSettings *s ) /Deprecated/;
//! called for every feature
virtual void registerFeature( const QString& layerID, QgsFeature& feat, QgsRenderContext& context, const QString& dxfLayer = QString::null ) = 0;
virtual void registerFeature( const QString &layerID, QgsFeature &feat, QgsRenderContext &context ) = 0;
//! called for every diagram feature
virtual void registerDiagramFeature( const QString& layerID, QgsFeature& feat, QgsRenderContext& context );
virtual void registerDiagramFeature( const QString &layerID, QgsFeature &feat, QgsRenderContext &context );
//! called when the map is drawn and labels should be placed
virtual void drawLabeling( QgsRenderContext& context ) = 0;
//! called when we're done with rendering
Expand Down Expand Up @@ -268,7 +268,7 @@ class QgsMapRenderer : QObject
//! Accessor for render context
QgsRenderContext* rendererContext();

//! Labeling engine (NULL if there's no custom engine)
//! Labeling engine (nullptr if there's no custom engine)
QgsLabelingEngineInterface* labelingEngine();

//! Set labeling engine. Previous engine (if any) is deleted.
Expand Down
6 changes: 2 additions & 4 deletions python/core/qgspallabeling.sip
Expand Up @@ -551,7 +551,6 @@ class QgsPalLayerSettings
* @param f feature to label
* @param context render context. The QgsExpressionContext contained within the render context
* must have already had the feature and fields sets prior to calling this method.
* @param dxfLayer dxfLayer name
* @param labelFeature if using QgsLabelingEngineV2, this will receive the label feature. Not available
* in Python bindings.
* @param obstacleGeometry optional obstacle geometry, if a different geometry to the feature's geometry
Expand All @@ -560,7 +559,7 @@ class QgsPalLayerSettings
* the feature's original geometry will be used as an obstacle for labels. Not available
* in Python bindings.
*/
void registerFeature( QgsFeature& f, QgsRenderContext& context, const QString& dxfLayer );
void registerFeature( QgsFeature& f, QgsRenderContext& context );

void readFromLayer( QgsVectorLayer* layer );
void writeToLayer( QgsVectorLayer* layer );
Expand Down Expand Up @@ -867,9 +866,8 @@ class QgsPalLabeling : QgsLabelingEngineInterface
* @param feat feature to label
* @param context render context. The QgsExpressionContext contained within the render context
* must have already had the feature and fields sets prior to calling this method.
* @param dxfLayer dxfLayer name
*/
virtual void registerFeature( const QString& layerID, QgsFeature& feat, QgsRenderContext& context, const QString& dxfLayer = QString::null );
virtual void registerFeature( const QString& layerID, QgsFeature& feat, QgsRenderContext& context );

virtual void registerDiagramFeature( const QString& layerID, QgsFeature& feat, QgsRenderContext& context );
//! called when the map is drawn and labels should be placed
Expand Down
148 changes: 143 additions & 5 deletions src/core/dxf/qgsdxfexport.cpp
Expand Up @@ -37,6 +37,11 @@
#include "qgsvectorlayer.h"
#include "qgsmaplayerregistry.h"
#include "qgsunittypes.h"
#include "qgstextlabelfeature.h"

#include "pal/feature.h"
#include "pal/pointset.h"
#include "pal/labelposition.h"

#include <QIODevice>

Expand Down Expand Up @@ -942,12 +947,32 @@ void QgsDxfExport::writeEntities()
attributes << layerAttr;
}

QgsDxfLabelProvider* lp = new QgsDxfLabelProvider( vl, this );
engine.addProvider( lp );
if ( !lp->prepare( ctx, attributes ) )
const QgsAbstractVectorLayerLabeling *labeling = vl->labeling();
QgsDxfLabelProvider *lp = nullptr;
QgsDxfRuleBasedLabelProvider *rblp = nullptr;
if ( dynamic_cast<const QgsRuleBasedLabeling*>( labeling ) )
{
const QgsRuleBasedLabeling *rbl = dynamic_cast<const QgsRuleBasedLabeling*>( labeling );
rblp = new QgsDxfRuleBasedLabelProvider( *rbl, vl, this );
rblp->reinit( vl );
engine.addProvider( rblp );

if ( !rblp->prepare( ctx, attributes ) )
{
engine.removeProvider( rblp );
rblp = nullptr;
}
}
else
{
engine.removeProvider( lp );
lp = nullptr;
lp = new QgsDxfLabelProvider( vl, this, nullptr );
engine.addProvider( lp );

if ( !lp->prepare( ctx, attributes ) )
{
engine.removeProvider( lp );
lp = nullptr;
}
}

if ( mSymbologyExport == QgsDxfExport::SymbolLayerSymbology &&
Expand Down Expand Up @@ -1012,6 +1037,10 @@ void QgsDxfExport::writeEntities()
{
lp->registerDxfFeature( fet, ctx, lName );
}
else if ( rblp )
{
rblp->registerDxfFeature( fet, ctx, lName );
}
}
}

Expand Down Expand Up @@ -4175,3 +4204,112 @@ QString QgsDxfExport::layerName( QgsVectorLayer *vl ) const
Q_ASSERT( vl );
return mLayerTitleAsName && !vl->title().isEmpty() ? vl->title() : vl->name();
}

void QgsDxfExport::drawLabel( QString layerId, QgsRenderContext& context, pal::LabelPosition* label, const QgsPalLayerSettings &settings )
{
Q_UNUSED( context );
QgsTextLabelFeature* lf = dynamic_cast<QgsTextLabelFeature*>( label->getFeaturePart()->feature() );
if ( !lf )
return;

//label text
QString txt = lf->text( label->getPartId() );

//angle
double angle = label->getAlpha() * 180 / M_PI;

QgsFeatureId fid = label->getFeaturePart()->featureId();
QString dxfLayer = mDxfLayerNames[layerId][fid];

//debug: show label rectangle
#if 0
QgsPolyline line;
for ( int i = 0; i < 4; ++i )
{
line.append( QgsPoint( label->getX( i ), label->getY( i ) ) );
}
writePolyline( line, dxfLayer, "CONTINUOUS", 1, 0.01, true );
#endif

QString wrapchr = settings.wrapChar.isEmpty() ? "\n" : settings.wrapChar;

//add the direction symbol if needed
if ( !txt.isEmpty() && settings.placement == QgsPalLayerSettings::Line && settings.addDirectionSymbol )
{
bool prependSymb = false;
QString symb = settings.rightDirectionSymbol;

if ( label->getReversed() )
{
prependSymb = true;
symb = settings.leftDirectionSymbol;
}

if ( settings.reverseDirectionSymbol )
{
if ( symb == settings.rightDirectionSymbol )
{
prependSymb = true;
symb = settings.leftDirectionSymbol;
}
else
{
prependSymb = false;
symb = settings.rightDirectionSymbol;
}
}

if ( settings.placeDirectionSymbol == QgsPalLayerSettings::SymbolAbove )
{
prependSymb = true;
symb = symb + wrapchr;
}
else if ( settings.placeDirectionSymbol == QgsPalLayerSettings::SymbolBelow )
{
prependSymb = false;
symb = wrapchr + symb;
}

if ( prependSymb )
{
txt.prepend( symb );
}
else
{
txt.append( symb );
}
}

txt = txt.replace( wrapchr, "\\P" );

if ( settings.textFont.underline() )
{
txt.prepend( "\\L" ).append( "\\l" );
}

if ( settings.textFont.overline() )
{
txt.prepend( "\\O" ).append( "\\o" );
}

if ( settings.textFont.strikeOut() )
{
txt.prepend( "\\K" ).append( "\\k" );
}

txt.prepend( QString( "\\f%1|i%2|b%3;\\H%4;\\W0.75;" )
.arg( settings.textFont.family() )
.arg( settings.textFont.italic() ? 1 : 0 )
.arg( settings.textFont.bold() ? 1 : 0 )
.arg( label->getHeight() / ( 1 + txt.count( "\\P" ) ) * 0.75 ) );

writeMText( dxfLayer, txt, QgsPoint( label->getX(), label->getY() ), label->getWidth() * 1.1, angle, settings.textColor );
}

void QgsDxfExport::registerDxfLayer( QString layerId, QgsFeatureId fid, QString layerName )
{
if ( !mDxfLayerNames.contains( layerId ) )
mDxfLayerNames[ layerId ] = QMap<QgsFeatureId, QString>();

mDxfLayerNames[layerId][fid] = layerName;
}
13 changes: 13 additions & 0 deletions src/core/dxf/qgsdxfexport.h
Expand Up @@ -20,6 +20,7 @@

#include "qgsgeometry.h"
#include "qgssymbolv2.h"

#include <QColor>
#include <QList>
#include <QTextStream>
Expand All @@ -28,6 +29,12 @@ class QgsMapLayer;
class QgsPoint;
class QgsSymbolLayerV2;
class QIODevice;
class QgsPalLayerSettings;

namespace pal
{
class LabelPosition;
};

class CORE_EXPORT QgsDxfExport
{
Expand Down Expand Up @@ -283,6 +290,9 @@ class CORE_EXPORT QgsDxfExport
//! return list of available DXF encodings
static QStringList encodings();

void drawLabel( QString layerId, QgsRenderContext& context, pal::LabelPosition* label, const QgsPalLayerSettings &settings );
void registerDxfLayer( QString layerId, QgsFeatureId fid, QString layer );

private:
QList< QPair<QgsVectorLayer*, int> > mLayers;

Expand Down Expand Up @@ -350,6 +360,9 @@ class CORE_EXPORT QgsDxfExport

QHash<QString, int> mBlockHandles;
QString mBlockHandle;

//! DXF layer name for each label feature
QMap< QString, QMap<QgsFeatureId, QString> > mDxfLayerNames;
};

#endif // QGSDXFEXPORT_H

0 comments on commit 5ce8a45

Please sign in to comment.