Skip to content

Commit f19a35c

Browse files
committed
dxf export: support rule based labeling (fixes #13757)
1 parent b7a4e20 commit f19a35c

15 files changed

+264
-166
lines changed

python/core/qgsmaprenderer.sip

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class QgsLabelingEngineInterface
3535

3636
//! called when we're going to start with rendering
3737
//! @deprecated since 2.4 - use override with QgsMapSettings
38-
virtual void init( QgsMapRenderer* mp ) = 0 /Deprecated/;
38+
virtual void init( QgsMapRenderer *mp ) = 0 /Deprecated/;
3939
//! called when we're going to start with rendering
4040
virtual void init( const QgsMapSettings& mapSettings ) = 0;
4141
//! called to find out whether the layer is used for labeling
@@ -48,17 +48,17 @@ class QgsLabelingEngineInterface
4848
virtual int prepareLayer( QgsVectorLayer* layer, QStringList& attrNames, QgsRenderContext& ctx ) = 0;
4949
//! returns PAL layer settings for a registered layer
5050
//! @deprecated since 2.12 - if direct access to QgsPalLayerSettings is necessary, use QgsPalLayerSettings::fromLayer()
51-
virtual QgsPalLayerSettings& layer( const QString& layerName ) = 0 /Deprecated/;
51+
virtual QgsPalLayerSettings &layer( const QString &layerName ) = 0 /Deprecated/;
5252
//! adds a diagram layer to the labeling engine
5353
//! @note added in QGIS 2.12
54-
virtual int prepareDiagramLayer( QgsVectorLayer* layer, QStringList& attrNames, QgsRenderContext& ctx );
54+
virtual int prepareDiagramLayer( QgsVectorLayer *layer, QStringList &attrNames, QgsRenderContext &ctx );
5555
//! adds a diagram layer to the labeling engine
5656
//! @deprecated since 2.12 - use prepareDiagramLayer()
57-
virtual int addDiagramLayer( QgsVectorLayer* layer, const QgsDiagramLayerSettings* s ) /Deprecated/;
57+
virtual int addDiagramLayer( QgsVectorLayer *layer, const QgsDiagramLayerSettings *s ) /Deprecated/;
5858
//! called for every feature
59-
virtual void registerFeature( const QString& layerID, QgsFeature& feat, QgsRenderContext& context, const QString& dxfLayer = QString::null ) = 0;
59+
virtual void registerFeature( const QString &layerID, QgsFeature &feat, QgsRenderContext &context ) = 0;
6060
//! called for every diagram feature
61-
virtual void registerDiagramFeature( const QString& layerID, QgsFeature& feat, QgsRenderContext& context );
61+
virtual void registerDiagramFeature( const QString &layerID, QgsFeature &feat, QgsRenderContext &context );
6262
//! called when the map is drawn and labels should be placed
6363
virtual void drawLabeling( QgsRenderContext& context ) = 0;
6464
//! called when we're done with rendering
@@ -268,7 +268,7 @@ class QgsMapRenderer : QObject
268268
//! Accessor for render context
269269
QgsRenderContext* rendererContext();
270270

271-
//! Labeling engine (NULL if there's no custom engine)
271+
//! Labeling engine (nullptr if there's no custom engine)
272272
QgsLabelingEngineInterface* labelingEngine();
273273

274274
//! Set labeling engine. Previous engine (if any) is deleted.

python/core/qgspallabeling.sip

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,6 @@ class QgsPalLayerSettings
551551
* @param f feature to label
552552
* @param context render context. The QgsExpressionContext contained within the render context
553553
* must have already had the feature and fields sets prior to calling this method.
554-
* @param dxfLayer dxfLayer name
555554
* @param labelFeature if using QgsLabelingEngineV2, this will receive the label feature. Not available
556555
* in Python bindings.
557556
* @param obstacleGeometry optional obstacle geometry, if a different geometry to the feature's geometry
@@ -560,7 +559,7 @@ class QgsPalLayerSettings
560559
* the feature's original geometry will be used as an obstacle for labels. Not available
561560
* in Python bindings.
562561
*/
563-
void registerFeature( QgsFeature& f, QgsRenderContext& context, const QString& dxfLayer );
562+
void registerFeature( QgsFeature& f, QgsRenderContext& context );
564563

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

874872
virtual void registerDiagramFeature( const QString& layerID, QgsFeature& feat, QgsRenderContext& context );
875873
//! called when the map is drawn and labels should be placed

src/core/dxf/qgsdxfexport.cpp

Lines changed: 143 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@
3737
#include "qgsvectorlayer.h"
3838
#include "qgsmaplayerregistry.h"
3939
#include "qgsunittypes.h"
40+
#include "qgstextlabelfeature.h"
41+
42+
#include "pal/feature.h"
43+
#include "pal/pointset.h"
44+
#include "pal/labelposition.h"
4045

4146
#include <QIODevice>
4247

@@ -942,12 +947,32 @@ void QgsDxfExport::writeEntities()
942947
attributes << layerAttr;
943948
}
944949

945-
QgsDxfLabelProvider* lp = new QgsDxfLabelProvider( vl, this );
946-
engine.addProvider( lp );
947-
if ( !lp->prepare( ctx, attributes ) )
950+
const QgsAbstractVectorLayerLabeling *labeling = vl->labeling();
951+
QgsDxfLabelProvider *lp = nullptr;
952+
QgsDxfRuleBasedLabelProvider *rblp = nullptr;
953+
if ( dynamic_cast<const QgsRuleBasedLabeling*>( labeling ) )
954+
{
955+
const QgsRuleBasedLabeling *rbl = dynamic_cast<const QgsRuleBasedLabeling*>( labeling );
956+
rblp = new QgsDxfRuleBasedLabelProvider( *rbl, vl, this );
957+
rblp->reinit( vl );
958+
engine.addProvider( rblp );
959+
960+
if ( !rblp->prepare( ctx, attributes ) )
961+
{
962+
engine.removeProvider( rblp );
963+
rblp = nullptr;
964+
}
965+
}
966+
else
948967
{
949-
engine.removeProvider( lp );
950-
lp = nullptr;
968+
lp = new QgsDxfLabelProvider( vl, this, nullptr );
969+
engine.addProvider( lp );
970+
971+
if ( !lp->prepare( ctx, attributes ) )
972+
{
973+
engine.removeProvider( lp );
974+
lp = nullptr;
975+
}
951976
}
952977

953978
if ( mSymbologyExport == QgsDxfExport::SymbolLayerSymbology &&
@@ -1012,6 +1037,10 @@ void QgsDxfExport::writeEntities()
10121037
{
10131038
lp->registerDxfFeature( fet, ctx, lName );
10141039
}
1040+
else if ( rblp )
1041+
{
1042+
rblp->registerDxfFeature( fet, ctx, lName );
1043+
}
10151044
}
10161045
}
10171046

@@ -4169,3 +4198,112 @@ QString QgsDxfExport::layerName( QgsVectorLayer *vl ) const
41694198
Q_ASSERT( vl );
41704199
return mLayerTitleAsName && !vl->title().isEmpty() ? vl->title() : vl->name();
41714200
}
4201+
4202+
void QgsDxfExport::drawLabel( QString layerId, QgsRenderContext& context, pal::LabelPosition* label, const QgsPalLayerSettings &settings )
4203+
{
4204+
Q_UNUSED( context );
4205+
QgsTextLabelFeature* lf = dynamic_cast<QgsTextLabelFeature*>( label->getFeaturePart()->feature() );
4206+
if ( !lf )
4207+
return;
4208+
4209+
//label text
4210+
QString txt = lf->text( label->getPartId() );
4211+
4212+
//angle
4213+
double angle = label->getAlpha() * 180 / M_PI;
4214+
4215+
QgsFeatureId fid = label->getFeaturePart()->featureId();
4216+
QString dxfLayer = mDxfLayerNames[layerId][fid];
4217+
4218+
//debug: show label rectangle
4219+
#if 0
4220+
QgsPolyline line;
4221+
for ( int i = 0; i < 4; ++i )
4222+
{
4223+
line.append( QgsPoint( label->getX( i ), label->getY( i ) ) );
4224+
}
4225+
writePolyline( line, dxfLayer, "CONTINUOUS", 1, 0.01, true );
4226+
#endif
4227+
4228+
QString wrapchr = settings.wrapChar.isEmpty() ? "\n" : settings.wrapChar;
4229+
4230+
//add the direction symbol if needed
4231+
if ( !txt.isEmpty() && settings.placement == QgsPalLayerSettings::Line && settings.addDirectionSymbol )
4232+
{
4233+
bool prependSymb = false;
4234+
QString symb = settings.rightDirectionSymbol;
4235+
4236+
if ( label->getReversed() )
4237+
{
4238+
prependSymb = true;
4239+
symb = settings.leftDirectionSymbol;
4240+
}
4241+
4242+
if ( settings.reverseDirectionSymbol )
4243+
{
4244+
if ( symb == settings.rightDirectionSymbol )
4245+
{
4246+
prependSymb = true;
4247+
symb = settings.leftDirectionSymbol;
4248+
}
4249+
else
4250+
{
4251+
prependSymb = false;
4252+
symb = settings.rightDirectionSymbol;
4253+
}
4254+
}
4255+
4256+
if ( settings.placeDirectionSymbol == QgsPalLayerSettings::SymbolAbove )
4257+
{
4258+
prependSymb = true;
4259+
symb = symb + wrapchr;
4260+
}
4261+
else if ( settings.placeDirectionSymbol == QgsPalLayerSettings::SymbolBelow )
4262+
{
4263+
prependSymb = false;
4264+
symb = wrapchr + symb;
4265+
}
4266+
4267+
if ( prependSymb )
4268+
{
4269+
txt.prepend( symb );
4270+
}
4271+
else
4272+
{
4273+
txt.append( symb );
4274+
}
4275+
}
4276+
4277+
txt = txt.replace( wrapchr, "\\P" );
4278+
4279+
if ( settings.textFont.underline() )
4280+
{
4281+
txt.prepend( "\\L" ).append( "\\l" );
4282+
}
4283+
4284+
if ( settings.textFont.overline() )
4285+
{
4286+
txt.prepend( "\\O" ).append( "\\o" );
4287+
}
4288+
4289+
if ( settings.textFont.strikeOut() )
4290+
{
4291+
txt.prepend( "\\K" ).append( "\\k" );
4292+
}
4293+
4294+
txt.prepend( QString( "\\f%1|i%2|b%3;\\H%4;\\W0.75;" )
4295+
.arg( settings.textFont.family() )
4296+
.arg( settings.textFont.italic() ? 1 : 0 )
4297+
.arg( settings.textFont.bold() ? 1 : 0 )
4298+
.arg( label->getHeight() / ( 1 + txt.count( "\\P" ) ) * 0.75 ) );
4299+
4300+
writeMText( dxfLayer, txt, QgsPoint( label->getX(), label->getY() ), label->getWidth() * 1.1, angle, settings.textColor );
4301+
}
4302+
4303+
void QgsDxfExport::registerDxfLayer( QString layerId, QgsFeatureId fid, QString layerName )
4304+
{
4305+
if ( !mDxfLayerNames.contains( layerId ) )
4306+
mDxfLayerNames[ layerId ] = QMap<QgsFeatureId, QString>();
4307+
4308+
mDxfLayerNames[layerId][fid] = layerName;
4309+
}

src/core/dxf/qgsdxfexport.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include "qgsgeometry.h"
2222
#include "qgssymbolv2.h"
23+
2324
#include <QColor>
2425
#include <QList>
2526
#include <QTextStream>
@@ -28,6 +29,12 @@ class QgsMapLayer;
2829
class QgsPoint;
2930
class QgsSymbolLayerV2;
3031
class QIODevice;
32+
class QgsPalLayerSettings;
33+
34+
namespace pal
35+
{
36+
class LabelPosition;
37+
};
3138

3239
class CORE_EXPORT QgsDxfExport
3340
{
@@ -283,6 +290,9 @@ class CORE_EXPORT QgsDxfExport
283290
//! return list of available DXF encodings
284291
static QStringList encodings();
285292

293+
void drawLabel( QString layerId, QgsRenderContext& context, pal::LabelPosition* label, const QgsPalLayerSettings &settings );
294+
void registerDxfLayer( QString layerId, QgsFeatureId fid, QString layer );
295+
286296
private:
287297
QList< QPair<QgsVectorLayer*, int> > mLayers;
288298

@@ -350,6 +360,9 @@ class CORE_EXPORT QgsDxfExport
350360

351361
QHash<QString, int> mBlockHandles;
352362
QString mBlockHandle;
363+
364+
//! DXF layer name for each label feature
365+
QMap< QString, QMap<QgsFeatureId, QString> > mDxfLayerNames;
353366
};
354367

355368
#endif // QGSDXFEXPORT_H

0 commit comments

Comments
 (0)