QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
qgsdxfexport.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsdxfexport.cpp
3 ----------------
4 begin : September 2013
5 copyright : (C) 2013 by Marco Hugentobler
6 email : marco at sourcepole dot ch
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18// Specs:
19// AutoCAD 2000: http://www.autodesk.com/techpubs/autocad/acad2000/dxf/
20// AutoCAD 2002: http://www.autodesk.com/techpubs/autocad/dxf/dxf2002.pdf
21// AutoCAD 2004: http://atrey.karlin.mff.cuni.cz/projekty/vrr/doc/dxf14.pdf
22// AutoCAD 2006: http://images.autodesk.com/adsk/files/dxf_format.pdf
23// AutoCAD 2008: http://images.autodesk.com/adsk/files/acad_dxf0.pdf
24// AutoCAD 2009: http://images.autodesk.com/adsk/files/acad_dxf.pdf
25// AutoCAD 2011: http://images.autodesk.com/adsk/files/acad_dxf2.pdf
26// AutoCAD 2012: http://images.autodesk.com/adsk/files/autocad_2012_pdf_dxf-reference_enu.pdf
27// AutoCAD 2014: http://images.autodesk.com/adsk/files/autocad_2014_pdf_dxf_reference_enu.pdf
28
29#include "qgsdxfexport.h"
32#include "qgscurvepolygon.h"
33#include "qgscompoundcurve.h"
34#include "qgscircularstring.h"
35#include "qgslinestring.h"
37#include "qgspointxy.h"
38#include "qgsproject.h"
39#include "qgsrenderer.h"
40#include "qgssymbollayer.h"
41#include "qgsfillsymbollayer.h"
42#include "qgsfeatureiterator.h"
43#include "qgslinesymbollayer.h"
44#include "qgsvectorlayer.h"
45#include "qgsunittypes.h"
46#include "qgstextlabelfeature.h"
47#include "qgslogger.h"
48#include "qgsmaplayerstyle.h"
51#include "qgsdxfexport_p.h"
52#include "qgssymbol.h"
53#include "qgsvariantutils.h"
54
55#include "qgswkbtypes.h"
56#include "qgspoint.h"
57#include "qgsgeos.h"
58
59#include "pal/feature.h"
60#include "pal/pointset.h"
61#include "pal/labelposition.h"
62
63#include <QIODevice>
64#include <QTextCodec>
65
66#ifdef _MSC_VER
67#define strcasecmp( a, b ) stricmp( a, b )
68#endif
69
71
73{
74 qDeleteAll( mJobs );
75}
76
78{
79 mMapSettings = settings;
80}
81
82void QgsDxfExport::setFlags( QgsDxfExport::Flags flags )
83{
84 mFlags = flags;
85}
86
87QgsDxfExport::Flags QgsDxfExport::flags() const
88{
89 return mFlags;
90}
91
92void QgsDxfExport::addLayers( const QList<DxfLayer> &layers )
93{
94 QList<QgsMapLayer *> layerList;
95
96 mLayerNameAttribute.clear();
97
98 layerList.reserve( layers.size() );
99 for ( const DxfLayer &dxfLayer : layers )
100 {
101 layerList << dxfLayer.layer();
102 if ( dxfLayer.layerOutputAttributeIndex() >= 0 )
103 mLayerNameAttribute.insert( dxfLayer.layer()->id(), dxfLayer.layerOutputAttributeIndex() );
104 }
105
106 mMapSettings.setLayers( layerList );
107}
108
109void QgsDxfExport::writeGroup( int code, int i )
110{
111 writeGroupCode( code );
112 writeInt( i );
113}
114
115void QgsDxfExport::writeGroup( int code, long long i )
116{
117 writeGroupCode( code );
118 writeInt( i );
119}
120
121void QgsDxfExport::writeGroup( int code, double d )
122{
123 writeGroupCode( code );
124 writeDouble( d );
125}
126
127void QgsDxfExport::writeGroup( int code, const QString &s )
128{
129 writeGroupCode( code );
130 writeString( s );
131}
132
133void QgsDxfExport::writeGroup( int code, const QgsPoint &p )
134{
135 writeGroup( code + 10, p.x() );
136 writeGroup( code + 20, p.y() );
137 if ( !mForce2d && p.is3D() && std::isfinite( p.z() ) )
138 writeGroup( code + 30, p.z() );
139}
140
141void QgsDxfExport::writeGroup( const QColor &color, int exactMatchCode, int rgbCode, int transparencyCode )
142{
143 int minDistAt = -1;
144 int minDist = std::numeric_limits<int>::max();
145
146 for ( int i = 1; i < static_cast< int >( sizeof( sDxfColors ) / sizeof( *sDxfColors ) ) && minDist > 0; ++i )
147 {
148 int dist = color_distance( color.rgba(), i );
149 if ( dist >= minDist )
150 continue;
151
152 minDistAt = i;
153 minDist = dist;
154 }
155
156 if ( minDist == 0 && minDistAt != 7 )
157 {
158 // exact full opaque match, not black/white
159 writeGroup( exactMatchCode, minDistAt );
160 if ( color.alpha() == 255 )
161 return;
162 }
163
164 int c = ( color.red() & 0xff ) * 0x10000 + ( color.green() & 0xff ) * 0x100 + ( color.blue() & 0xff );
165 writeGroup( rgbCode, c );
166 if ( transparencyCode != -1 && color.alpha() < 255 )
167 writeGroup( transparencyCode, 0x2000000 | color.alpha() );
168}
169
171{
172 mTextStream << QStringLiteral( "%1\n" ).arg( code, 3, 10, QChar( ' ' ) );
173}
174
176{
177 mTextStream << QStringLiteral( "%1\n" ).arg( i, 6, 10, QChar( ' ' ) );
178}
179
181{
182 QString s( qgsDoubleToString( d ) );
183 if ( !s.contains( '.' ) )
184 s += QLatin1String( ".0" );
185 mTextStream << s << '\n';
186}
187
188void QgsDxfExport::writeString( const QString &s )
189{
190 mTextStream << s << '\n';
191}
192
193QgsDxfExport::ExportResult QgsDxfExport::writeToFile( QIODevice *d, const QString &encoding )
194{
195 if ( !d )
196 {
198 }
199
200 if ( !d->isOpen() && !d->open( QIODevice::WriteOnly | QIODevice::Truncate ) )
201 {
203 }
204
205 mTextStream.setDevice( d );
206#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
207 mTextStream.setCodec( encoding.toLocal8Bit() );
208#else
209 mTextStream.setEncoding( QStringConverter::encodingForName( encoding.toLocal8Bit() ).value_or( QStringConverter::Utf8 ) );
210#endif
211
212 if ( mCrs.isValid() )
213 mMapSettings.setDestinationCrs( mCrs );
214
215 if ( mExtent.isEmpty() )
216 {
217 const QList< QgsMapLayer * > layers = mMapSettings.layers();
218 for ( QgsMapLayer *ml : layers )
219 {
220 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
221 if ( !vl )
222 continue;
223
224 QgsRectangle layerExtent = vl->extent();
225 if ( layerExtent.isEmpty() )
226 continue;
227
228 layerExtent = mMapSettings.layerToMapCoordinates( vl, layerExtent );
229
230 if ( mExtent.isEmpty() )
231 {
232 mExtent = layerExtent;
233 }
234 else
235 {
236 mExtent.combineExtentWith( layerExtent );
237 }
238 }
239 }
240
241 if ( mExtent.isEmpty() )
243
245 mMapSettings.setExtent( mExtent );
246
247 int dpi = 96;
248 mFactor = 1000 * dpi / mSymbologyScale / 25.4 * QgsUnitTypes::fromUnitToUnitFactor( mapUnits, QgsUnitTypes::DistanceMeters );
249 mMapSettings.setOutputSize( QSize( mExtent.width() * mFactor, mExtent.height() * mFactor ) );
250 mMapSettings.setOutputDpi( dpi );
251
252 writeHeader( dxfEncoding( encoding ) );
253 prepareRenderers();
254 writeTables();
255 writeBlocks();
256 writeEntities();
257 writeEndFile();
258 stopRenderers();
259
261}
262
264{
265 return mMapUnits;
266}
267
268void QgsDxfExport::writeHeader( const QString &codepage )
269{
270 writeGroup( 999, QStringLiteral( "DXF created from QGIS" ) );
271
272 startSection();
273 writeGroup( 2, QStringLiteral( "HEADER" ) );
274
275 // ACADVER
276 writeGroup( 9, QStringLiteral( "$ACADVER" ) );
277 writeGroup( 1, QStringLiteral( "AC1015" ) );
278
279 // EXTMIN
280 writeGroup( 9, QStringLiteral( "$EXTMIN" ) );
281 writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, mExtent.xMinimum(), mExtent.yMinimum(), 0.0 ) );
282
283 // EXTMAX
284 writeGroup( 9, QStringLiteral( "$EXTMAX" ) );
285 writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, mExtent.xMaximum(), mExtent.yMaximum(), 0.0 ) );
286
287 // Global linetype scale
288 writeGroup( 9, QStringLiteral( "$LTSCALE" ) );
289 writeGroup( 40, 1.0 );
290
291 // Point display mode (33 = circle)
292 writeGroup( 9, QStringLiteral( "$PDMODE" ) );
293 writeGroup( 70, 33 );
294
295 // Point display size
296 writeGroup( 9, QStringLiteral( "$PDSIZE" ) );
297 writeGroup( 40, 1 );
298
299 // Controls paper space linetype scaling (1 = No special linetype scaling, 0 = Viewport scaling governs linetype scaling)
300 writeGroup( 9, QStringLiteral( "$PSLTSCALE" ) );
301 writeGroup( 70, 0 );
302
303 writeGroup( 9, QStringLiteral( "$HANDSEED" ) );
305
306 writeGroup( 9, QStringLiteral( "$DWGCODEPAGE" ) );
307 writeGroup( 3, codepage );
308
309 endSection();
310}
311
312int QgsDxfExport::writeHandle( int code, int handle )
313{
314 if ( handle == 0 )
315 handle = mNextHandleId++;
316
317 Q_ASSERT_X( handle < DXF_HANDMAX, "QgsDxfExport::writeHandle(int, int)", "DXF handle too large" );
318
319 writeGroup( code, QString::number( handle, 16 ) );
320 return handle;
321}
322
323void QgsDxfExport::writeTables()
324{
325 startSection();
326 writeGroup( 2, QStringLiteral( "TABLES" ) );
327
328 // Iterate through all layers and get symbol layer pointers
329 QgsRenderContext context = renderContext();
330 QList< QPair< QgsSymbolLayer *, QgsSymbol * > > slList;
331 if ( mSymbologyExport != NoSymbology )
332 {
333 slList = symbolLayers( context );
334 }
335
336 // Line types
337 mLineStyles.clear();
338 writeGroup( 0, QStringLiteral( "TABLE" ) );
339 writeGroup( 2, QStringLiteral( "LTYPE" ) );
340 writeHandle();
341 writeGroup( 100, QStringLiteral( "AcDbSymbolTable" ) );
342 writeGroup( 70, nLineTypes( slList ) + 5 );
343
344 writeDefaultLinetypes();
345
346 // Add custom linestyles
347 for ( const auto &symbolLayer : std::as_const( slList ) )
348 {
349 writeSymbolLayerLinetype( symbolLayer.first );
350 }
351
352 writeGroup( 0, QStringLiteral( "ENDTAB" ) );
353
354 // BLOCK_RECORD
355 writeGroup( 0, QStringLiteral( "TABLE" ) );
356 writeGroup( 2, QStringLiteral( "BLOCK_RECORD" ) );
357 writeHandle();
358
359 writeGroup( 100, QStringLiteral( "AcDbSymbolTable" ) );
360 writeGroup( 70, 0 );
361
362 const QStringList blockStrings = QStringList() << QStringLiteral( "*Model_Space" ) << QStringLiteral( "*Paper_Space" ) << QStringLiteral( "*Paper_Space0" );
363 for ( const QString &block : blockStrings )
364 {
365 writeGroup( 0, QStringLiteral( "BLOCK_RECORD" ) );
366 mBlockHandles.insert( block, writeHandle() );
367 writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
368 writeGroup( 100, QStringLiteral( "AcDbBlockTableRecord" ) );
369 writeGroup( 2, block );
370 }
371
372 int i = 0;
373 for ( const auto &symbolLayer : std::as_const( slList ) )
374 {
375 QgsMarkerSymbolLayer *ml = dynamic_cast< QgsMarkerSymbolLayer *>( symbolLayer.first );
376 if ( !ml )
377 continue;
378
379 if ( hasDataDefinedProperties( ml, symbolLayer.second ) )
380 continue;
381
382 QString name = QStringLiteral( "symbolLayer%1" ).arg( i++ );
383 writeGroup( 0, QStringLiteral( "BLOCK_RECORD" ) );
384 mBlockHandles.insert( name, writeHandle() );
385 writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
386 writeGroup( 100, QStringLiteral( "AcDbBlockTableRecord" ) );
387 writeGroup( 2, name );
388 }
389
390 writeGroup( 0, QStringLiteral( "ENDTAB" ) );
391
392 // APPID
393 writeGroup( 0, QStringLiteral( "TABLE" ) );
394 writeGroup( 2, QStringLiteral( "APPID" ) );
395 writeHandle();
396 writeGroup( 100, QStringLiteral( "AcDbSymbolTable" ) );
397 writeGroup( 70, 1 );
398 writeGroup( 0, QStringLiteral( "APPID" ) );
399 writeHandle();
400 writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
401 writeGroup( 100, QStringLiteral( "AcDbRegAppTableRecord" ) );
402 writeGroup( 2, QStringLiteral( "ACAD" ) );
403 writeGroup( 70, 0 );
404 writeGroup( 0, QStringLiteral( "ENDTAB" ) );
405
406 // VIEW
407 writeGroup( 0, QStringLiteral( "TABLE" ) );
408 writeGroup( 2, QStringLiteral( "VIEW" ) );
409 writeHandle();
410 writeGroup( 100, QStringLiteral( "AcDbSymbolTable" ) );
411 writeGroup( 70, 0 );
412 writeGroup( 0, QStringLiteral( "ENDTAB" ) );
413
414 // UCS
415 writeGroup( 0, QStringLiteral( "TABLE" ) );
416 writeGroup( 2, QStringLiteral( "UCS" ) );
417 writeHandle();
418 writeGroup( 100, QStringLiteral( "AcDbSymbolTable" ) );
419 writeGroup( 70, 0 );
420 writeGroup( 0, QStringLiteral( "ENDTAB" ) );
421
422 // VPORT
423 writeGroup( 0, QStringLiteral( "TABLE" ) );
424 writeGroup( 2, QStringLiteral( "VPORT" ) );
425 writeHandle();
426 writeGroup( 100, QStringLiteral( "AcDbSymbolTable" ) );
427
428 writeGroup( 0, QStringLiteral( "VPORT" ) );
429 writeHandle();
430 writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
431 writeGroup( 100, QStringLiteral( "AcDbViewportTableRecord" ) );
432 writeGroup( 2, QStringLiteral( "*ACTIVE" ) );
433 writeGroup( 70, 0 ); // flags
434 writeGroup( 0, QgsPoint( 0.0, 0.0 ) ); // lower left
435 writeGroup( 1, QgsPoint( 1.0, 1.0 ) ); // upper right
436 writeGroup( 2, QgsPoint( 0.0, 0.0 ) ); // view center point
437 writeGroup( 3, QgsPoint( 0.0, 0.0 ) ); // snap base point
438 writeGroup( 4, QgsPoint( 1.0, 1.0 ) ); // snap spacing
439 writeGroup( 5, QgsPoint( 1.0, 1.0 ) ); // grid spacing
440 writeGroup( 6, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 1.0 ) ); // view direction from target point
441 writeGroup( 7, QgsPoint( mExtent.center() ) ); // view target point
442 writeGroup( 40, mExtent.height() ); // view height
443 writeGroup( 41, mExtent.width() / mExtent.height() ); // view aspect ratio
444 writeGroup( 42, 50.0 ); // lens length
445 writeGroup( 43, 0.0 ); // front clipping plane
446 writeGroup( 44, 0.0 ); // back clipping plane
447 writeGroup( 50, 0.0 ); // snap rotation
448 writeGroup( 51, 0.0 ); // view twist angle
449 writeGroup( 71, 0 ); // view mode (0 = deactivates)
450 writeGroup( 72, 100 ); // circle zoom percent
451 writeGroup( 73, 1 ); // fast zoom setting
452 writeGroup( 74, 1 ); // UCSICON setting
453 writeGroup( 75, 0 ); // snapping off
454 writeGroup( 76, 0 ); // grid off
455 writeGroup( 77, 0 ); // snap style
456 writeGroup( 78, 0 ); // snap isopair
457 writeGroup( 281, 0 ); // render mode (0 = 2D optimized)
458 writeGroup( 65, 1 ); // value of UCSVP for this viewport
459 writeGroup( 100, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 0.0 ) );// UCS origin
460 writeGroup( 101, QgsPoint( QgsWkbTypes::PointZ, 1.0, 0.0, 0.0 ) );// UCS x axis
461 writeGroup( 102, QgsPoint( QgsWkbTypes::PointZ, 0.0, 1.0, 0.0 ) );// UCS y axis
462 writeGroup( 79, 0 ); // Orthographic type of UCS (0 = UCS is not orthographic)
463 writeGroup( 146, 0.0 ); // Elevation
464
465 writeGroup( 70, 0 );
466 writeGroup( 0, QStringLiteral( "ENDTAB" ) );
467
468 // DIMSTYLE
469 writeGroup( 0, QStringLiteral( "TABLE" ) );
470 writeGroup( 2, QStringLiteral( "DIMSTYLE" ) );
471 writeHandle();
472 writeGroup( 100, QStringLiteral( "AcDbSymbolTable" ) );
473 writeGroup( 100, QStringLiteral( "AcDbDimStyleTable" ) );
474 writeGroup( 70, 0 );
475 writeGroup( 0, QStringLiteral( "ENDTAB" ) );
476
477 QSet<QString> layerNames;
478 const QList< QgsMapLayer * > layers = mMapSettings.layers();
479 for ( QgsMapLayer *ml : layers )
480 {
481 if ( !layerIsScaleBasedVisible( ml ) )
482 continue;
483
484 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
485 if ( !vl )
486 continue;
487
488 int attrIdx = mLayerNameAttribute.value( vl->id(), -1 );
489 if ( attrIdx < 0 )
490 {
491 layerNames << dxfLayerName( layerName( vl ) );
492 }
493 else
494 {
495 const QSet<QVariant> values = vl->uniqueValues( attrIdx );
496 for ( const QVariant &v : values )
497 {
498 layerNames << dxfLayerName( v.toString() );
499 }
500 }
501 }
502
503 // Layers
504 // TODO: iterate features of all layer to produce a data-defined layer list
505 writeGroup( 0, QStringLiteral( "TABLE" ) );
506 writeGroup( 2, QStringLiteral( "LAYER" ) );
507 writeHandle();
508 writeGroup( 100, QStringLiteral( "AcDbSymbolTable" ) );
509 writeGroup( 70, layerNames.size() + 1 );
510
511 writeGroup( 0, QStringLiteral( "LAYER" ) );
512 writeHandle();
513 writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
514 writeGroup( 100, QStringLiteral( "AcDbLayerTableRecord" ) );
515 writeGroup( 2, QStringLiteral( "0" ) );
516 writeGroup( 70, 64 );
517 writeGroup( 62, 1 );
518 writeGroup( 6, QStringLiteral( "CONTINUOUS" ) );
520
521 for ( const QString &layerName : std::as_const( layerNames ) )
522 {
523 writeGroup( 0, QStringLiteral( "LAYER" ) );
524 writeHandle();
525 writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
526 writeGroup( 100, QStringLiteral( "AcDbLayerTableRecord" ) );
527 writeGroup( 2, layerName );
528 writeGroup( 70, 64 );
529 writeGroup( 62, 1 );
530 writeGroup( 6, QStringLiteral( "CONTINUOUS" ) );
532 }
533 writeGroup( 0, QStringLiteral( "ENDTAB" ) );
534
535 // Text styles
536 writeGroup( 0, QStringLiteral( "TABLE" ) );
537 writeGroup( 2, QStringLiteral( "STYLE" ) );
538 writeHandle();
539 writeGroup( 100, QStringLiteral( "AcDbSymbolTable" ) );
540 writeGroup( 70, 1 );
541
542 // Provide only standard font for the moment
543 writeGroup( 0, QStringLiteral( "STYLE" ) );
544 writeHandle();
545 writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
546 writeGroup( 100, QStringLiteral( "AcDbTextStyleTableRecord" ) );
547 writeGroup( 2, QStringLiteral( "STANDARD" ) );
548 writeGroup( 70, 64 );
549 writeGroup( 40, 0.0 );
550 writeGroup( 41, 1.0 );
551 writeGroup( 50, 0.0 );
552 writeGroup( 71, 0 );
553 writeGroup( 42, 5.0 );
554 writeGroup( 3, QStringLiteral( "romans.shx" ) );
555 writeGroup( 4, QString() );
556
557 writeGroup( 0, QStringLiteral( "ENDTAB" ) );
558
559 endSection();
560}
561
562void QgsDxfExport::writeBlocks()
563{
564 startSection();
565 writeGroup( 2, QStringLiteral( "BLOCKS" ) );
566
567 static const QStringList blockStrings = QStringList() << QStringLiteral( "*Model_Space" ) << QStringLiteral( "*Paper_Space" ) << QStringLiteral( "*Paper_Space0" );
568 for ( const QString &block : blockStrings )
569 {
570 writeGroup( 0, QStringLiteral( "BLOCK" ) );
571 writeHandle();
572 writeGroup( 330, QString::number( mBlockHandles[ block ], 16 ) );
573 writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
574 writeGroup( 8, QStringLiteral( "0" ) );
575 writeGroup( 100, QStringLiteral( "AcDbBlockBegin" ) );
576 writeGroup( 2, block );
577 writeGroup( 70, 0 );
578 writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 0.0 ) );
579 writeGroup( 3, block );
580 writeGroup( 1, QString() );
581 writeGroup( 0, QStringLiteral( "ENDBLK" ) );
582 writeHandle();
583 writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
584 writeGroup( 8, QStringLiteral( "0" ) );
585 writeGroup( 100, QStringLiteral( "AcDbBlockEnd" ) );
586 }
587
588 QgsRenderContext ct = renderContext();
589
590 // Iterate through all layers and get symbol layer pointers
591 QList< QPair< QgsSymbolLayer *, QgsSymbol * > > slList;
592 if ( mSymbologyExport != NoSymbology )
593 {
594 slList = symbolLayers( ct );
595 }
596
597 for ( const auto &symbolLayer : std::as_const( slList ) )
598 {
599 QgsMarkerSymbolLayer *ml = dynamic_cast< QgsMarkerSymbolLayer *>( symbolLayer.first );
600 if ( !ml )
601 continue;
602
603 // if point symbol layer and no data defined properties: write block
604 QgsSymbolRenderContext ctx( ct, QgsUnitTypes::RenderMapUnits, symbolLayer.second->opacity(), false, symbolLayer.second->renderHints(), nullptr );
605
606 // markers with data defined properties are inserted inline
607 if ( hasDataDefinedProperties( ml, symbolLayer.second ) )
608 {
609 continue;
610 }
611
612 QString block( QStringLiteral( "symbolLayer%1" ).arg( mBlockCounter++ ) );
613 mBlockHandle = QString::number( mBlockHandles[ block ], 16 );
614
615 writeGroup( 0, QStringLiteral( "BLOCK" ) );
616 writeHandle();
617 writeGroup( 330, mBlockHandle );
618 writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
619 writeGroup( 8, QStringLiteral( "0" ) );
620 writeGroup( 100, QStringLiteral( "AcDbBlockBegin" ) );
621 writeGroup( 2, block );
622 writeGroup( 70, 0 );
623
624 // x/y/z coordinates of reference point
625 // todo: consider anchor point
626 // double size = ml->size();
627 // size *= mapUnitScaleFactor( mSymbologyScale, ml->sizeUnit(), mMapUnits );
628 writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 0.0 ) );
629 writeGroup( 3, block );
630 writeGroup( 1, QString() );
631
632 // maplayer 0 -> block receives layer from INSERT statement
633 ml->writeDxf( *this, mapUnitScaleFactor( mSymbologyScale, ml->sizeUnit(), mMapUnits, ctx.renderContext().mapToPixel().mapUnitsPerPixel() ), QStringLiteral( "0" ), ctx );
634
635 writeGroup( 0, QStringLiteral( "ENDBLK" ) );
636 writeHandle();
637 writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
638 writeGroup( 8, QStringLiteral( "0" ) );
639 writeGroup( 100, QStringLiteral( "AcDbBlockEnd" ) );
640
641 mPointSymbolBlocks.insert( ml, block );
642 }
643 endSection();
644}
645
646
647void QgsDxfExport::writeEntities()
648{
649 startSection();
650 writeGroup( 2, QStringLiteral( "ENTITIES" ) );
651
652 mBlockHandle = QString::number( mBlockHandles[ QStringLiteral( "*Model_Space" )], 16 );
653
654 // iterate through the maplayers
655 for ( DxfLayerJob *job : std::as_const( mJobs ) )
656 {
657 QgsSymbolRenderContext sctx( mRenderContext, QgsUnitTypes::RenderMillimeters, 1.0, false, Qgis::SymbolRenderHints(), nullptr );
658
659 if ( mSymbologyExport == QgsDxfExport::SymbolLayerSymbology &&
660 ( job->renderer->capabilities() & QgsFeatureRenderer::SymbolLevels ) &&
661 job->renderer->usingSymbolLevels() )
662 {
663 writeEntitiesSymbolLevels( job );
664
665 continue;
666 }
667
668 const QgsCoordinateTransform ct( job->crs, mMapSettings.destinationCrs(), mMapSettings.transformContext() );
669
670 QgsFeatureRequest request = QgsFeatureRequest().setSubsetOfAttributes( job->attributes, job->fields ).setExpressionContext( job->renderContext.expressionContext() );
671 QgsCoordinateTransform extentTransform = ct;
672 extentTransform.setBallparkTransformsAreAppropriate( true );
673 request.setFilterRect( extentTransform.transformBoundingBox( mExtent, Qgis::TransformDirection::Reverse ) );
674
675 QgsFeatureIterator featureIt = job->featureSource.getFeatures( request );
676
677 QgsFeature fet;
678 while ( featureIt.nextFeature( fet ) )
679 {
680 mRenderContext.expressionContext().setFeature( fet );
681 QString lName( dxfLayerName( job->splitLayerAttribute.isNull() ? job->layerTitle : fet.attribute( job->splitLayerAttribute ).toString() ) );
682
683 sctx.setFeature( &fet );
684
685 if ( !job->renderer->willRenderFeature( fet, mRenderContext ) )
686 continue;
687
688 if ( mSymbologyExport == NoSymbology )
689 {
690 addFeature( sctx, ct, lName, nullptr, nullptr ); // no symbology at all
691 }
692 else
693 {
694 const QgsSymbolList symbolList = job->renderer->symbolsForFeature( fet, mRenderContext );
695 bool hasSymbology = symbolList.size() > 0;
696
697 if ( hasSymbology && mSymbologyExport == QgsDxfExport::SymbolLayerSymbology ) // symbol layer symbology, but layer does not use symbol levels
698 {
699 for ( QgsSymbol *symbol : symbolList )
700 {
701 const QgsSymbolLayerList symbolLayers = symbol->symbolLayers();
702 for ( QgsSymbolLayer *symbolLayer : symbolLayers )
703 {
704 if ( !symbolLayer )
705 continue;
706
707 bool isGeometryGenerator = ( symbolLayer->layerType() == QLatin1String( "GeometryGenerator" ) );
708 if ( isGeometryGenerator )
709 {
710 addGeometryGeneratorSymbolLayer( sctx, ct, lName, symbolLayer, true );
711 }
712 else
713 {
714 addFeature( sctx, ct, lName, symbolLayer, symbol );
715 }
716 }
717 }
718 }
719 else if ( hasSymbology )
720 {
721 // take first symbollayer from first symbol
722 QgsSymbol *s = symbolList.first();
723 if ( !s || s->symbolLayerCount() < 1 )
724 {
725 continue;
726 }
727
728 if ( s->symbolLayer( 0 )->layerType() == QLatin1String( "GeometryGenerator" ) )
729 {
730 addGeometryGeneratorSymbolLayer( sctx, ct, lName, s->symbolLayer( 0 ), false );
731 }
732 else
733 {
734 addFeature( sctx, ct, lName, s->symbolLayer( 0 ), s );
735 }
736 }
737
738 if ( job->labelProvider )
739 {
740 job->labelProvider->registerFeature( fet, mRenderContext );
742 registerDxfLayer( job->featureSource.id(), fet.id(), lName );
744 }
745 else if ( job->ruleBasedLabelProvider )
746 {
747 job->ruleBasedLabelProvider->registerFeature( fet, mRenderContext );
749 registerDxfLayer( job->featureSource.id(), fet.id(), lName );
751 }
752 }
753 }
754 }
755
756 QImage image( 10, 10, QImage::Format_ARGB32_Premultiplied );
757 image.setDotsPerMeterX( 96 / 25.4 * 1000 );
758 image.setDotsPerMeterY( 96 / 25.4 * 1000 );
759 QPainter painter( &image );
760 mRenderContext.setPainter( &painter );
761
762 mRenderContext.labelingEngine()->run( mRenderContext );
763
764 endSection();
765}
766
767void QgsDxfExport::prepareRenderers()
768{
769 Q_ASSERT( mJobs.empty() ); // If this fails, stopRenderers() was not called after the last job
770
771 mRenderContext = QgsRenderContext();
772 mRenderContext.setRendererScale( mSymbologyScale );
773 mRenderContext.setExtent( mExtent );
774
775 mRenderContext.setScaleFactor( 96.0 / 25.4 );
776 mRenderContext.setMapToPixel( QgsMapToPixel( 1.0 / mFactor, mExtent.center().x(), mExtent.center().y(), mExtent.width() * mFactor,
777 mExtent.height() * mFactor, 0 ) );
778
782
783 mLabelingEngine = std::make_unique<QgsDefaultLabelingEngine>();
784 mLabelingEngine->setMapSettings( mMapSettings );
785 mRenderContext.setLabelingEngine( mLabelingEngine.get() );
786
787 const QList< QgsMapLayer * > layers = mMapSettings.layers();
788 for ( QgsMapLayer *ml : layers )
789 {
790 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
791 if ( !vl )
792 continue;
793
794 if ( !vl->renderer() )
795 continue;
796
797 if ( !layerIsScaleBasedVisible( vl ) )
798 continue;
799
800 QString splitLayerAttribute;
801 int splitLayerAttributeIndex = mLayerNameAttribute.value( vl->id(), -1 );
802 const QgsFields fields = vl->fields();
803 if ( splitLayerAttributeIndex >= 0 && splitLayerAttributeIndex < fields.size() )
804 splitLayerAttribute = fields.at( splitLayerAttributeIndex ).name();
805 DxfLayerJob *job = new DxfLayerJob( vl, mMapSettings.layerStyleOverrides().value( vl->id() ), mRenderContext, this, splitLayerAttribute );
806 mJobs.append( job );
807 }
808}
809
810void QgsDxfExport::writeEntitiesSymbolLevels( DxfLayerJob *job )
811{
812 QHash< QgsSymbol *, QList<QgsFeature> > features;
813
814 QgsRenderContext ctx = renderContext();
815 const QList<QgsExpressionContextScope *> scopes = job->renderContext.expressionContext().scopes();
816 for ( QgsExpressionContextScope *scope : scopes )
818 QgsSymbolRenderContext sctx( ctx, QgsUnitTypes::RenderMillimeters, 1.0, false, Qgis::SymbolRenderHints(), nullptr );
819
820 // get iterator
822 req.setSubsetOfAttributes( job->renderer->usedAttributes( ctx ), job->featureSource.fields() );
823 QgsCoordinateTransform ct( mMapSettings.destinationCrs(), job->crs, mMapSettings.transformContext() );
824 req.setFilterRect( ct.transform( mExtent ) );
825
827
828 // fetch features
829 QgsFeature fet;
830 QgsSymbol *featureSymbol = nullptr;
831 while ( fit.nextFeature( fet ) )
832 {
833 ctx.expressionContext().setFeature( fet );
834 featureSymbol = job->renderer->symbolForFeature( fet, ctx );
835 if ( !featureSymbol )
836 {
837 continue;
838 }
839
840 QHash< QgsSymbol *, QList<QgsFeature> >::iterator it = features.find( featureSymbol );
841 if ( it == features.end() )
842 {
843 it = features.insert( featureSymbol, QList<QgsFeature>() );
844 }
845 it.value().append( fet );
846 }
847
848 // find out order
849 QgsSymbolLevelOrder levels;
850 const QgsSymbolList symbols = job->renderer->symbols( ctx );
851 for ( QgsSymbol *symbol : symbols )
852 {
853 for ( int j = 0; j < symbol->symbolLayerCount(); j++ )
854 {
855 int level = symbol->symbolLayer( j )->renderingPass();
856 if ( level < 0 || level >= 1000 ) // ignore invalid levels
857 continue;
858 QgsSymbolLevelItem item( symbol, j );
859 while ( level >= levels.count() ) // append new empty levels
860 levels.append( QgsSymbolLevel() );
861 levels[level].append( item );
862 }
863 }
864
865 // export symbol layers and symbology
866 for ( const QgsSymbolLevel &level : std::as_const( levels ) )
867 {
868 for ( const QgsSymbolLevelItem &item : level )
869 {
870 QHash< QgsSymbol *, QList<QgsFeature> >::iterator levelIt = features.find( item.symbol() );
871 if ( levelIt == features.end() )
872 {
873 continue;
874 }
875
876 int llayer = item.layer();
877 const QList<QgsFeature> &featureList = levelIt.value();
878 for ( const QgsFeature &feature : featureList )
879 {
880 sctx.setFeature( &feature );
881 addFeature( sctx, ct, job->layerName, levelIt.key()->symbolLayer( llayer ), levelIt.key() );
882 }
883 }
884 }
885}
886
887void QgsDxfExport::stopRenderers()
888{
889 qDeleteAll( mJobs );
890 mJobs.clear();
891}
892
893void QgsDxfExport::writeEndFile()
894{
895 mTextStream << DXF_TRAILER;
896
897 writeGroup( 0, QStringLiteral( "EOF" ) );
898}
899
900void QgsDxfExport::startSection()
901{
902 writeGroup( 0, QStringLiteral( "SECTION" ) );
903}
904
905void QgsDxfExport::endSection()
906{
907 writeGroup( 0, QStringLiteral( "ENDSEC" ) );
908}
909
910void QgsDxfExport::writePoint( const QgsPoint &pt, const QString &layer, const QColor &color, QgsSymbolRenderContext &ctx, const QgsSymbolLayer *symbolLayer, const QgsSymbol *symbol, double angle )
911{
912#if 0
913 // debug: draw rectangle for debugging
914 const QgsMarkerSymbolLayer *msl = dynamic_cast< const QgsMarkerSymbolLayer * >( symbolLayer );
915 if ( msl )
916 {
917 double halfSize = msl->size() * mapUnitScaleFactor( mSymbologyScale,
918 msl->sizeUnit(), mMapUnits ) / 2.0;
919 writeGroup( 0, "SOLID" );
920 writeGroup( 8, layer );
921 writeGroup( 62, 1 );
922 writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, pt.x() - halfSize, pt.y() - halfSize ) );
923 writeGroup( 1, QgsPoint( QgsWkbTypes::PointZ, pt.x() + halfSize, pt.y() - halfSize ) );
924 writeGroup( 2, QgsPoint( QgsWkbTypes::PointZ, pt.x() - halfSize, pt.y() + halfSize ) );
925 writeGroup( 3, QgsPoint( QgsWkbTypes::PointZ, pt.x() + halfSize, pt.y() + halfSize ) );
926 }
927#endif // 0
928
929 // insert block or write point directly?
930 QHash< const QgsSymbolLayer *, QString >::const_iterator blockIt = mPointSymbolBlocks.constFind( symbolLayer );
931 if ( !symbolLayer || blockIt == mPointSymbolBlocks.constEnd() )
932 {
933 // write symbol directly here
934 const QgsMarkerSymbolLayer *msl = dynamic_cast< const QgsMarkerSymbolLayer * >( symbolLayer );
935 if ( msl && symbol )
936 {
937 if ( msl->writeDxf( *this, mapUnitScaleFactor( mSymbologyScale, msl->sizeUnit(), mMapUnits, ctx.renderContext().mapToPixel().mapUnitsPerPixel() ), layer, ctx, QPointF( pt.x(), pt.y() ) ) )
938 {
939 return;
940 }
941 }
942 writePoint( layer, color, pt ); // write default point symbol
943 }
944 else
945 {
946 // insert block reference
947 writeGroup( 0, QStringLiteral( "INSERT" ) );
948 writeHandle();
949 writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
950 writeGroup( 100, QStringLiteral( "AcDbBlockReference" ) );
951 writeGroup( 8, layer );
952 writeGroup( 2, blockIt.value() ); // Block name
953 writeGroup( 50, angle ); // angle
954 writeGroup( 0, pt ); // Insertion point (in OCS)
955 }
956}
957
958void QgsDxfExport::writePolyline( const QgsPointSequence &line, const QString &layer, const QString &lineStyleName, const QColor &color, double width )
959{
960 int n = line.size();
961 if ( n == 0 )
962 {
963 QgsDebugMsg( QStringLiteral( "writePolyline: empty line layer=%1 lineStyleName=%2" ).arg( layer, lineStyleName ) );
964 return;
965 }
966
967 if ( n < 2 )
968 {
969 QgsDebugMsg( QStringLiteral( "writePolyline: line too short layer=%1 lineStyleName=%2" ).arg( layer, lineStyleName ) );
970 return;
971 }
972
973 if ( mForce2d || !line.at( 0 ).is3D() )
974 {
975 bool polygon = line[0] == line[ line.size() - 1 ];
976 if ( polygon )
977 --n;
978
979 writeGroup( 0, QStringLiteral( "LWPOLYLINE" ) );
980 writeHandle();
981 writeGroup( 8, layer );
982 writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
983 writeGroup( 100, QStringLiteral( "AcDbPolyline" ) );
984 writeGroup( 6, lineStyleName );
985 writeGroup( color );
986
987 writeGroup( 90, n );
988 writeGroup( 70, polygon ? 1 : 0 );
989 writeGroup( 43, width );
990
991 for ( int i = 0; i < n; i++ )
992 writeGroup( 0, line[i] );
993 }
994 else
995 {
996 writeGroup( 0, QStringLiteral( "POLYLINE" ) );
997 int plHandle = writeHandle();
998 writeGroup( 330, mBlockHandle );
999 writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1000 writeGroup( 8, layer );
1001 writeGroup( 6, lineStyleName );
1002 writeGroup( color );
1003 writeGroup( 100, QStringLiteral( "AcDb3dPolyline" ) );
1004 writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 0.0 ) );
1005 writeGroup( 70, 8 );
1006
1007 for ( int i = 0; i < n; i++ )
1008 {
1009 writeGroup( 0, QStringLiteral( "VERTEX" ) );
1010 writeHandle();
1011 writeGroup( 330, plHandle );
1012 writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1013 writeGroup( 8, layer );
1014 writeGroup( color );
1015 writeGroup( 100, QStringLiteral( "AcDbVertex" ) );
1016 writeGroup( 100, QStringLiteral( "AcDb3dPolylineVertex" ) );
1017 writeGroup( 0, line[i] );
1018 writeGroup( 70, 32 );
1019 }
1020
1021 writeGroup( 0, QStringLiteral( "SEQEND" ) );
1022 writeHandle();
1023 writeGroup( 330, plHandle );
1024 writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1025 writeGroup( 8, layer );
1026 writeGroup( color );
1027 }
1028}
1029
1030void QgsDxfExport::appendCurve( const QgsCurve &c, QVector<QgsPoint> &points, QVector<double> &bulges )
1031{
1032 switch ( QgsWkbTypes::flatType( c.wkbType() ) )
1033 {
1035 appendLineString( *dynamic_cast<const QgsLineString *>( &c ), points, bulges );
1036 break;
1037
1039 appendCircularString( *dynamic_cast<const QgsCircularString *>( &c ), points, bulges );
1040 break;
1041
1043 appendCompoundCurve( *dynamic_cast<const QgsCompoundCurve *>( &c ), points, bulges );
1044 break;
1045
1046 default:
1047 QgsDebugMsg( QStringLiteral( "Unexpected curve type %1" ).arg( c.wktTypeStr() ) );
1048 break;
1049 }
1050}
1051
1052void QgsDxfExport::appendLineString( const QgsLineString &ls, QVector<QgsPoint> &points, QVector<double> &bulges )
1053{
1054 for ( int i = 0; i < ls.numPoints(); i++ )
1055 {
1056 const QgsPoint &p = ls.pointN( i );
1057 if ( !points.isEmpty() && points.last() == p )
1058 continue;
1059
1060 points << p;
1061 bulges << 0.0;
1062 }
1063}
1064
1065void QgsDxfExport::appendCircularString( const QgsCircularString &cs, QVector<QgsPoint> &points, QVector<double> &bulges )
1066{
1067 for ( int i = 0; i < cs.numPoints() - 2; i += 2 )
1068 {
1069 const QgsPoint &p1 = cs.pointN( i );
1070 const QgsPoint &p2 = cs.pointN( i + 1 );
1071 const QgsPoint &p3 = cs.pointN( i + 2 );
1072
1073 if ( points.isEmpty() || points.last() != p1 )
1074 points << p1;
1075 else if ( !bulges.isEmpty() )
1076 bulges.removeLast();
1077
1078 double a = ( M_PI - ( p1 - p2 ).angle() + ( p3 - p2 ).angle() ) / 2.0;
1079 bulges << sin( a ) / cos( a );
1080
1081 points << p3;
1082 bulges << 0.0;
1083 }
1084}
1085
1086void QgsDxfExport::appendCompoundCurve( const QgsCompoundCurve &cc, QVector<QgsPoint> &points, QVector<double> &bulges )
1087{
1088 for ( int i = 0; i < cc.nCurves(); i++ )
1089 {
1090 const QgsCurve *c = cc.curveAt( i );
1091 Q_ASSERT( c );
1092 appendCurve( *c, points, bulges );
1093 }
1094}
1095
1096void QgsDxfExport::writePolyline( const QgsCurve &curve, const QString &layer, const QString &lineStyleName, const QColor &color, double width )
1097{
1098 int n = curve.numPoints();
1099 if ( n == 0 )
1100 {
1101 QgsDebugMsg( QStringLiteral( "writePolyline: empty line layer=%1 lineStyleName=%2" ).arg( layer, lineStyleName ) );
1102 return;
1103 }
1104
1105 if ( n < 2 )
1106 {
1107 QgsDebugMsg( QStringLiteral( "writePolyline: line too short layer=%1 lineStyleName=%2" ).arg( layer, lineStyleName ) );
1108 return;
1109 }
1110
1111 QVector<QgsPoint> points;
1112 QVector<double> bulges;
1113 appendCurve( curve, points, bulges );
1114
1115 if ( mForce2d || !curve.is3D() )
1116 {
1117 writeGroup( 0, QStringLiteral( "LWPOLYLINE" ) );
1118 writeHandle();
1119 writeGroup( 8, layer );
1120 writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1121 writeGroup( 100, QStringLiteral( "AcDbPolyline" ) );
1122 writeGroup( 6, lineStyleName );
1123 writeGroup( color );
1124
1125 writeGroup( 90, points.size() );
1126 QgsDxfExport::DxfPolylineFlags polylineFlags;
1127 if ( curve.isClosed() )
1128 polylineFlags.setFlag( QgsDxfExport::DxfPolylineFlag::Closed );
1129 if ( curve.hasCurvedSegments() )
1130 polylineFlags.setFlag( QgsDxfExport::DxfPolylineFlag::Curve );
1131
1132 // Might need to conditional once this feature is implemented
1133 // https://github.com/qgis/QGIS/issues/32468
1134 polylineFlags.setFlag( QgsDxfExport::DxfPolylineFlag::ContinuousPattern );
1135
1136 writeGroup( 70, static_cast<int>( polylineFlags ) );
1137 writeGroup( 43, width );
1138
1139 for ( int i = 0; i < points.size(); i++ )
1140 {
1141 writeGroup( 0, points[i] );
1142 if ( bulges[i] != 0.0 )
1143 writeGroup( 42, bulges[i] );
1144 }
1145 }
1146 else
1147 {
1148 writeGroup( 0, QStringLiteral( "POLYLINE" ) );
1149 int plHandle = writeHandle();
1150 writeGroup( 330, mBlockHandle );
1151 writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1152 writeGroup( 8, layer );
1153 writeGroup( 6, lineStyleName );
1154 writeGroup( color );
1155 writeGroup( 100, QStringLiteral( "AcDb3dPolyline" ) );
1156 writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 0.0 ) );
1157 writeGroup( 70, 8 );
1158
1159 for ( int i = 0; i < points.size(); i++ )
1160 {
1161 writeGroup( 0, QStringLiteral( "VERTEX" ) );
1162 writeHandle();
1163 writeGroup( 330, plHandle );
1164 writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1165 writeGroup( 8, layer );
1166 writeGroup( color );
1167 writeGroup( 100, QStringLiteral( "AcDbVertex" ) );
1168 writeGroup( 100, QStringLiteral( "AcDb3dPolylineVertex" ) );
1169 writeGroup( 0, points[i] );
1170 if ( bulges[i] != 0.0 )
1171 writeGroup( 42, bulges[i] );
1172 writeGroup( 70, 32 );
1173 }
1174
1175 writeGroup( 0, QStringLiteral( "SEQEND" ) );
1176 writeHandle();
1177 writeGroup( 330, plHandle );
1178 writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1179 writeGroup( 8, layer );
1180 writeGroup( color );
1181 }
1182}
1183
1184void QgsDxfExport::writePolygon( const QgsRingSequence &polygon, const QString &layer, const QString &hatchPattern, const QColor &color )
1185{
1186 writeGroup( 0, QStringLiteral( "HATCH" ) ); // Entity type
1187 writeHandle();
1188 writeGroup( 330, mBlockHandle );
1189 writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1190 writeGroup( 8, layer ); // Layer name
1191 writeGroup( color ); // Color
1192 writeGroup( 100, QStringLiteral( "AcDbHatch" ) );
1193
1194 writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 0.0 ) ); // Elevation point (in OCS)
1195 writeGroup( 200, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 1.0 ) );
1196
1197 writeGroup( 2, hatchPattern ); // Hatch pattern name
1198 writeGroup( 70, hatchPattern == QLatin1String( "SOLID" ) ); // Solid fill flag (solid fill = 1; pattern fill = 0)
1199 writeGroup( 71, 0 ); // Associativity flag (associative = 1; non-associative = 0)
1200
1201 writeGroup( 91, polygon.size() ); // Number of boundary paths (loops)
1202 for ( int i = 0; i < polygon.size(); ++i )
1203 {
1204 writeGroup( 92, 2 ); // Boundary path type flag (bit coded): 0 = Default; 1 = External; 2 = Polyline 4 = Derived; 8 = Textbox; 16 = Outermost
1205 writeGroup( 72, 0 ); // Has bulge flag
1206 writeGroup( 73, 1 ); // Is closed flag
1207 writeGroup( 93, polygon[i].size() ); // Number of edges in this boundary path (only if boundary is not a polyline)
1208
1209 for ( int j = 0; j < polygon[i].size(); ++j )
1210 {
1211 writeGroup( 0, polygon[i][j] ); // Vertex location (in OCS)
1212 }
1213
1214 writeGroup( 97, 0 ); // Number of source boundary objects
1215 }
1216
1217 writeGroup( 75, 0 ); // Hatch style: 0 = Hatch "odd parity" area (Normal style), 1 = Hatch outermost area only (Outer style), 2 = Hatch through entire area (Ignore style)
1218 writeGroup( 76, 1 ); // Hatch pattern type: 0 = User-defined; 1 = Predefined; 2 = Custom
1219
1220 writeGroup( 98, 0 ); // Number of seed points
1221}
1222
1223void QgsDxfExport::writePolygon( const QgsCurvePolygon &polygon, const QString &layer, const QString &hatchPattern, const QColor &color )
1224{
1225 writeGroup( 0, QStringLiteral( "HATCH" ) ); // Entity type
1226 writeHandle();
1227 writeGroup( 330, mBlockHandle );
1228 writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1229 writeGroup( 8, layer ); // Layer name
1230 writeGroup( color ); // Color
1231 writeGroup( 100, QStringLiteral( "AcDbHatch" ) );
1232
1233 writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 0.0 ) ); // Elevation point (in OCS)
1234 writeGroup( 200, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 1.0 ) );
1235
1236 writeGroup( 2, hatchPattern ); // Hatch pattern name
1237 writeGroup( 70, hatchPattern == QLatin1String( "SOLID" ) ); // Solid fill flag (solid fill = 1; pattern fill = 0)
1238 writeGroup( 71, 0 ); // Associativity flag (associative = 1; non-associative = 0)
1239
1240 QVector<QVector<QgsPoint>> points;
1241 QVector<QVector<double>> bulges;
1242
1243 const int ringCount = polygon.numInteriorRings();
1244 points.reserve( ringCount + 1 );
1245 bulges.reserve( ringCount + 1 );
1246
1247 points << QVector<QgsPoint>();
1248 bulges << QVector<double>();
1249 appendCurve( *polygon.exteriorRing(), points.last(), bulges.last() );
1250
1251 for ( int i = 0; i < ringCount; i++ )
1252 {
1253 points << QVector<QgsPoint>();
1254 bulges << QVector<double>();
1255 appendCurve( *polygon.interiorRing( i ), points.last(), bulges.last() );
1256 }
1257
1258 bool hasBulges = false;
1259 for ( int i = 0; i < points.size() && !hasBulges; ++i )
1260 for ( int j = 0; j < points[i].size() && !hasBulges; ++j )
1261 hasBulges = bulges[i][j] != 0.0;
1262
1263 writeGroup( 91, points.size() ); // Number of boundary paths (loops)
1264
1265 for ( int i = 0; i < points.size(); ++i )
1266 {
1267 writeGroup( 92, 2 ); // Boundary path type flag (bit coded): 0 = Default; 1 = External; 2 = Polyline 4 = Derived; 8 = Textbox; 16 = Outermost
1268 writeGroup( 72, hasBulges ? 1 : 0 ); // Has bulge flag
1269 writeGroup( 73, 1 ); // Is closed flag
1270 writeGroup( 93, points[i].size() ); // Number of edges in this boundary path (only if boundary is not a polyline)
1271
1272 for ( int j = 0; j < points[i].size(); ++j )
1273 {
1274 writeGroup( 0, points[i][j] ); // Vertex location (in OCS)
1275 if ( hasBulges )
1276 writeGroup( 42, bulges[i][j] );
1277 }
1278
1279 writeGroup( 97, 0 ); // Number of source boundary objects
1280 }
1281
1282 writeGroup( 75, 0 ); // Hatch style: 0 = Hatch "odd parity" area (Normal style), 1 = Hatch outermost area only (Outer style), 2 = Hatch through entire area (Ignore style)
1283 writeGroup( 76, 1 ); // Hatch pattern type: 0 = User-defined; 1 = Predefined; 2 = Custom
1284
1285 writeGroup( 98, 0 ); // Number of seed points
1286}
1287
1288void QgsDxfExport::writeLine( const QgsPoint &pt1, const QgsPoint &pt2, const QString &layer, const QString &lineStyleName, const QColor &color, double width )
1289{
1290 writePolyline( QgsPointSequence() << pt1 << pt2, layer, lineStyleName, color, width );
1291}
1292
1293void QgsDxfExport::writeText( const QString &layer, const QString &text, pal::LabelPosition *label, const QgsPalLayerSettings &layerSettings, const QgsExpressionContext &expressionContext )
1294{
1295
1296 double lblX = label->getX();
1297 double lblY = label->getY();
1298
1299 QgsLabelFeature *labelFeature = label->getFeaturePart()->feature();
1300
1303
1304 const QgsPropertyCollection &props = layerSettings.dataDefinedProperties();
1305
1306 if ( layerSettings.placement == Qgis::LabelPlacement::OverPoint )
1307 {
1308 lblX = labelFeature->anchorPosition().x();
1309 lblY = labelFeature->anchorPosition().y();
1310
1311 Qgis::LabelQuadrantPosition offsetQuad = layerSettings.quadOffset;
1312
1314 {
1315 const QVariant exprVal = props.value( QgsPalLayerSettings::OffsetQuad, expressionContext );
1316 if ( !QgsVariantUtils::isNull( exprVal ) )
1317 {
1318 offsetQuad = static_cast<Qgis::LabelQuadrantPosition>( exprVal.toInt() );
1319 }
1320 }
1321
1322 switch ( offsetQuad )
1323 {
1324 case Qgis::LabelQuadrantPosition::AboveLeft:
1325 hali = HAlign::HRight;
1326 vali = VAlign::VBottom;
1327 break;
1328 case Qgis::LabelQuadrantPosition::Above:
1329 hali = HAlign::HCenter;
1330 vali = VAlign::VBottom;
1331 break;
1332 case Qgis::LabelQuadrantPosition::AboveRight:
1333 hali = HAlign::HLeft;
1334 vali = VAlign::VBottom;
1335 break;
1336 case Qgis::LabelQuadrantPosition::Left:
1337 hali = HAlign::HRight;
1338 vali = VAlign::VMiddle;
1339 break;
1340 case Qgis::LabelQuadrantPosition::Over:
1341 hali = HAlign::HCenter;
1342 vali = VAlign::VMiddle;
1343 break;
1344 case Qgis::LabelQuadrantPosition::Right:
1345 hali = HAlign::HLeft;
1346 vali = VAlign::VMiddle;
1347 break;
1348 case Qgis::LabelQuadrantPosition::BelowLeft:
1349 hali = HAlign::HRight;
1350 vali = VAlign::VTop;
1351 break;
1352 case Qgis::LabelQuadrantPosition::Below:
1353 hali = HAlign::HCenter;
1354 vali = VAlign::VTop;
1355 break;
1356 case Qgis::LabelQuadrantPosition::BelowRight:
1357 hali = HAlign::HLeft;
1358 vali = VAlign::VTop;
1359 break;
1360 }
1361 }
1362
1363 if ( props.isActive( QgsPalLayerSettings::Hali ) )
1364 {
1365 lblX = labelFeature->anchorPosition().x();
1366 lblY = labelFeature->anchorPosition().y();
1367
1368 hali = HAlign::HLeft;
1369 QVariant exprVal = props.value( QgsPalLayerSettings::Hali, expressionContext );
1370 if ( !QgsVariantUtils::isNull( exprVal ) )
1371 {
1372 const QString haliString = exprVal.toString();
1373 if ( haliString.compare( QLatin1String( "Center" ), Qt::CaseInsensitive ) == 0 )
1374 {
1375 hali = HAlign::HCenter;
1376 }
1377 else if ( haliString.compare( QLatin1String( "Right" ), Qt::CaseInsensitive ) == 0 )
1378 {
1379 hali = HAlign::HRight;
1380 }
1381 }
1382 }
1383
1384 //vertical alignment
1385 if ( props.isActive( QgsPalLayerSettings::Vali ) )
1386 {
1387 vali = VAlign::VBottom;
1388 QVariant exprVal = props.value( QgsPalLayerSettings::Vali, expressionContext );
1389 if ( !QgsVariantUtils::isNull( exprVal ) )
1390 {
1391 const QString valiString = exprVal.toString();
1392 if ( valiString.compare( QLatin1String( "Bottom" ), Qt::CaseInsensitive ) != 0 )
1393 {
1394 if ( valiString.compare( QLatin1String( "Base" ), Qt::CaseInsensitive ) == 0 )
1395 {
1396 vali = VAlign::VBaseLine;
1397 }
1398 else if ( valiString.compare( QLatin1String( "Half" ), Qt::CaseInsensitive ) == 0 )
1399 {
1400 vali = VAlign::VMiddle;
1401 }
1402 else //'Cap' or 'Top'
1403 {
1404 vali = VAlign::VTop;
1405 }
1406 }
1407 }
1408 }
1409
1410 writeText( layer, text, QgsPoint( lblX, lblY ), label->getHeight(), label->getAlpha() * 180.0 / M_PI, layerSettings.format().color(), hali, vali );
1411}
1412
1413void QgsDxfExport::writePoint( const QString &layer, const QColor &color, const QgsPoint &pt )
1414{
1415 writeGroup( 0, QStringLiteral( "POINT" ) );
1416 writeHandle();
1417 writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1418 writeGroup( 100, QStringLiteral( "AcDbPoint" ) );
1419 writeGroup( 8, layer );
1420 writeGroup( color );
1421 writeGroup( 0, pt );
1422}
1423
1424void QgsDxfExport::writeFilledCircle( const QString &layer, const QColor &color, const QgsPoint &pt, double radius )
1425{
1426 writeGroup( 0, QStringLiteral( "HATCH" ) ); // Entity type
1427 writeHandle();
1428 writeGroup( 330, mBlockHandle );
1429 writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1430 writeGroup( 8, layer ); // Layer name
1431 writeGroup( color ); // Color (0 by block, 256 by layer)
1432 writeGroup( 100, QStringLiteral( "AcDbHatch" ) );
1433
1434 writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 0.0 ) ); // Elevation point (in OCS)
1435 writeGroup( 200, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 1.0 ) );
1436
1437 writeGroup( 2, QStringLiteral( "SOLID" ) ); // Hatch pattern name
1438 writeGroup( 70, 1 ); // Solid fill flag (solid fill = 1; pattern fill = 0)
1439 writeGroup( 71, 0 ); // Associativity flag (associative = 1; non-associative = 0)
1440
1441 writeGroup( 91, 1 ); // Number of boundary paths (loops)
1442
1443 writeGroup( 92, 3 ); // Boundary path type flag (bit coded): 0 = Default; 1 = External; 2 = Polyline 4 = Derived; 8 = Textbox; 16 = Outermost
1444 writeGroup( 72, 1 );
1445 writeGroup( 73, 1 ); // Is closed flag
1446 writeGroup( 93, 2 ); // Number of polyline vertices
1447
1448 writeGroup( 0, QgsPoint( QgsWkbTypes::Point, pt.x() - radius, pt.y() ) );
1449 writeGroup( 42, 1.0 );
1450
1451 writeGroup( 0, QgsPoint( QgsWkbTypes::Point, pt.x() + radius, pt.y() ) );
1452 writeGroup( 42, 1.0 );
1453
1454 writeGroup( 97, 0 ); // Number of source boundary objects
1455
1456 writeGroup( 75, 0 ); // Hatch style: 0 = Hatch "odd parity" area (Normal style), 1 = Hatch outermost area only (Outer style), 2 = Hatch through entire area (Ignore style)
1457 writeGroup( 76, 1 ); // Hatch pattern type: 0 = User-defined; 1 = Predefined; 2 = Custom
1458 writeGroup( 98, 0 ); // Number of seed points
1459}
1460
1461void QgsDxfExport::writeCircle( const QString &layer, const QColor &color, const QgsPoint &pt, double radius, const QString &lineStyleName, double width )
1462{
1463 writeGroup( 0, QStringLiteral( "LWPOLYLINE" ) );
1464 writeHandle();
1465 writeGroup( 330, mBlockHandle );
1466 writeGroup( 8, layer );
1467 writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1468 writeGroup( 100, QStringLiteral( "AcDbPolyline" ) );
1469 writeGroup( 6, lineStyleName );
1470 writeGroup( color );
1471
1472 writeGroup( 90, 2 );
1473
1474 writeGroup( 70, 1 );
1475 writeGroup( 43, width );
1476
1477 writeGroup( 0, QgsPoint( pt.x() - radius, pt.y() ) );
1478 writeGroup( 42, 1.0 );
1479 writeGroup( 0, QgsPoint( pt.x() + radius, pt.y() ) );
1480 writeGroup( 42, 1.0 );
1481}
1482
1483void QgsDxfExport::writeText( const QString &layer, const QString &text, const QgsPoint &pt, double size, double angle, const QColor &color, HAlign hali, VAlign vali )
1484{
1485 writeGroup( 0, QStringLiteral( "TEXT" ) );
1486 writeHandle();
1487 writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1488 // writeGroup( 6, "Continuous" ); // Line style
1489 // writeGroup( 370, 18 ); // Line weight
1490 writeGroup( 100, QStringLiteral( "AcDbText" ) );
1491 writeGroup( 8, layer );
1492 writeGroup( color );
1493 writeGroup( 0, pt );
1494 if ( hali != HAlign::Undefined || vali != VAlign::Undefined )
1495 writeGroup( 1, pt ); // Second alignment point
1496 writeGroup( 40, size );
1497 writeGroup( 1, text );
1498 writeGroup( 50, fmod( angle, 360 ) );
1499 if ( hali != HAlign::Undefined )
1500 writeGroup( 72, static_cast<int>( hali ) );
1501 writeGroup( 7, QStringLiteral( "STANDARD" ) ); // so far only support for standard font
1502 writeGroup( 100, QStringLiteral( "AcDbText" ) );
1503 if ( vali != VAlign::Undefined )
1504 {
1505 writeGroup( 73, static_cast<int>( vali ) );
1506 }
1507}
1508
1509void QgsDxfExport::writeMText( const QString &layer, const QString &text, const QgsPoint &pt, double width, double angle, const QColor &color )
1510{
1511#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1512 if ( !mTextStream.codec()->canEncode( text ) )
1513 {
1514 // TODO return error
1515 QgsDebugMsg( QStringLiteral( "could not encode:%1" ).arg( text ) );
1516 return;
1517 }
1518#endif
1519
1520 writeGroup( 0, QStringLiteral( "MTEXT" ) );
1521 writeHandle();
1522 writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1523 writeGroup( 100, QStringLiteral( "AcDbMText" ) );
1524 writeGroup( 8, layer );
1525 writeGroup( color );
1526
1527 writeGroup( 0, pt );
1528
1529 QString t( text );
1530 while ( t.length() > 250 )
1531 {
1532 writeGroup( 3, t.left( 250 ) );
1533 t = t.mid( 250 );
1534 }
1535 writeGroup( 1, t );
1536
1537 writeGroup( 50, angle ); // Rotation angle in radians
1538 writeGroup( 41, width * 1.1 ); // Reference rectangle width
1539
1540 // Attachment point:
1541 // 1 2 3
1542 // 4 5 6
1543 // 7 8 9
1544 writeGroup( 71, 7 );
1545
1546 writeGroup( 7, QStringLiteral( "STANDARD" ) ); // so far only support for standard font
1547}
1548
1549void QgsDxfExport::addFeature( QgsSymbolRenderContext &ctx, const QgsCoordinateTransform &ct, const QString &layer, const QgsSymbolLayer *symbolLayer, const QgsSymbol *symbol )
1550{
1551 const QgsFeature *fet = ctx.feature();
1552 if ( !fet )
1553 return;
1554
1555 if ( !fet->hasGeometry() )
1556 return;
1557
1558 QgsGeometry geom( fet->geometry() );
1559 if ( ct.isValid() )
1560 {
1561 geom.transform( ct );
1562 }
1563
1564 QgsWkbTypes::Type geometryType = geom.wkbType();
1565
1566 QColor penColor;
1567 QColor brushColor;
1568 if ( mSymbologyExport != NoSymbology && symbolLayer )
1569 {
1570 penColor = colorFromSymbolLayer( symbolLayer, ctx );
1571 brushColor = symbolLayer->dxfBrushColor( ctx );
1572 }
1573
1574 Qt::PenStyle penStyle( Qt::SolidLine );
1575 Qt::BrushStyle brushStyle( Qt::NoBrush );
1576 double width = -1;
1577 double offset = 0.0;
1578 double angle = 0.0;
1579 if ( mSymbologyExport != NoSymbology && symbolLayer )
1580 {
1581 width = symbolLayer->dxfWidth( *this, ctx );
1582 offset = symbolLayer->dxfOffset( *this, ctx );
1583 angle = symbolLayer->dxfAngle( ctx );
1584 penStyle = symbolLayer->dxfPenStyle();
1585 brushStyle = symbolLayer->dxfBrushStyle();
1586
1587 if ( qgsDoubleNear( offset, 0.0 ) )
1588 offset = 0.0;
1589 }
1590
1591 QString lineStyleName = QStringLiteral( "CONTINUOUS" );
1592 if ( mSymbologyExport != NoSymbology )
1593 {
1594 lineStyleName = lineStyleFromSymbolLayer( symbolLayer );
1595 }
1596
1597 // single point
1598 if ( QgsWkbTypes::flatType( geometryType ) == QgsWkbTypes::Point )
1599 {
1600 writePoint( geom.constGet()->coordinateSequence().at( 0 ).at( 0 ).at( 0 ), layer, penColor, ctx, symbolLayer, symbol, angle );
1601 return;
1602 }
1603
1604 if ( QgsWkbTypes::flatType( geometryType ) == QgsWkbTypes::MultiPoint )
1605 {
1606 const QgsCoordinateSequence &cs = geom.constGet()->coordinateSequence();
1607 for ( int i = 0; i < cs.size(); i++ )
1608 {
1609 writePoint( cs.at( i ).at( 0 ).at( 0 ), layer, penColor, ctx, symbolLayer, symbol, angle );
1610 }
1611 return;
1612 }
1613
1614 if ( penStyle != Qt::NoPen )
1615 {
1616 const QgsAbstractGeometry *sourceGeom = geom.constGet();
1617 std::unique_ptr< QgsAbstractGeometry > tempGeom;
1618
1619 switch ( QgsWkbTypes::flatType( geometryType ) )
1620 {
1626 {
1627 if ( !qgsDoubleNear( offset, 0.0 ) )
1628 {
1629 QgsGeos geos( sourceGeom );
1630 tempGeom.reset( geos.offsetCurve( offset, 0, Qgis::JoinStyle::Miter, 2.0 ) ); //#spellok
1631 if ( tempGeom )
1632 sourceGeom = tempGeom.get();
1633 else
1634 sourceGeom = geom.constGet();
1635 }
1636
1637 const QgsCurve *curve = dynamic_cast<const QgsCurve *>( sourceGeom );
1638 if ( curve )
1639 {
1640 writePolyline( *curve, layer, lineStyleName, penColor, width );
1641 }
1642 else
1643 {
1644 const QgsGeometryCollection *gc = dynamic_cast<const QgsGeometryCollection *>( sourceGeom );
1645 Q_ASSERT( gc );
1646 if ( gc )
1647 {
1648 for ( int i = 0; i < gc->numGeometries(); i++ )
1649 {
1650 const QgsCurve *curve = dynamic_cast<const QgsCurve *>( gc->geometryN( i ) );
1651 Q_ASSERT( curve );
1652 writePolyline( *curve, layer, lineStyleName, penColor, width );
1653 }
1654 }
1655 }
1656 break;
1657 }
1658
1663 {
1664 if ( !qgsDoubleNear( offset, 0.0 ) )
1665 {
1666 QgsGeos geos( sourceGeom );
1667 tempGeom.reset( geos.buffer( offset, 0, Qgis::EndCapStyle::Flat, Qgis::JoinStyle::Miter, 2.0 ) ); //#spellok
1668 if ( tempGeom )
1669 sourceGeom = tempGeom.get();
1670 else
1671 sourceGeom = geom.constGet();
1672 }
1673
1674 const QgsCurvePolygon *polygon = dynamic_cast<const QgsCurvePolygon *>( sourceGeom );
1675 if ( polygon )
1676 {
1677 writePolyline( *polygon->exteriorRing(), layer, lineStyleName, penColor, width );
1678 for ( int i = 0; i < polygon->numInteriorRings(); i++ )
1679 writePolyline( *polygon->interiorRing( i ), layer, lineStyleName, penColor, width );
1680 }
1681 else
1682 {
1683 const QgsGeometryCollection *gc = dynamic_cast<const QgsGeometryCollection *>( sourceGeom );
1684 Q_ASSERT( gc );
1685 if ( gc )
1686 {
1687 for ( int i = 0; i < gc->numGeometries(); i++ )
1688 {
1689 const QgsCurvePolygon *polygon = dynamic_cast<const QgsCurvePolygon *>( gc->geometryN( i ) );
1690 Q_ASSERT( polygon );
1691
1692 writePolyline( *polygon->exteriorRing(), layer, lineStyleName, penColor, width );
1693 for ( int j = 0; j < polygon->numInteriorRings(); j++ )
1694 writePolyline( *polygon->interiorRing( j ), layer, lineStyleName, penColor, width );
1695 }
1696 }
1697 }
1698
1699 break;
1700 }
1701
1702 default:
1703 break;
1704 }
1705
1706 }
1707
1708 if ( brushStyle != Qt::NoBrush )
1709 {
1710 const QgsAbstractGeometry *sourceGeom = geom.constGet();
1711 std::unique_ptr< QgsAbstractGeometry > tempGeom;
1712
1713 switch ( QgsWkbTypes::flatType( geometryType ) )
1714 {
1717 {
1718 const QgsCurvePolygon *polygon = dynamic_cast<const QgsCurvePolygon *>( sourceGeom );
1719 Q_ASSERT( polygon );
1720 writePolygon( *polygon, layer, QStringLiteral( "SOLID" ), brushColor );
1721 break;
1722 }
1723
1726 {
1727 const QgsGeometryCollection *gc = dynamic_cast<const QgsGeometryCollection *>( sourceGeom );
1728 Q_ASSERT( gc );
1729
1730 for ( int i = 0; i < gc->numGeometries(); i++ )
1731 {
1732 const QgsCurvePolygon *polygon = dynamic_cast<const QgsCurvePolygon *>( gc->geometryN( i ) );
1733 Q_ASSERT( polygon );
1734 writePolygon( *polygon, layer, QStringLiteral( "SOLID" ), brushColor );
1735 }
1736 break;
1737 }
1738
1739 default:
1740 break;
1741
1742 }
1743 }
1744}
1745
1746QColor QgsDxfExport::colorFromSymbolLayer( const QgsSymbolLayer *symbolLayer, QgsSymbolRenderContext &ctx )
1747{
1748 if ( !symbolLayer )
1749 return QColor();
1750
1751 return symbolLayer->dxfColor( ctx );
1752}
1753
1754QString QgsDxfExport::lineStyleFromSymbolLayer( const QgsSymbolLayer *symbolLayer )
1755{
1756 QString lineStyleName = QStringLiteral( "CONTINUOUS" );
1757 if ( !symbolLayer )
1758 {
1759 return lineStyleName;
1760 }
1761
1762 QHash< const QgsSymbolLayer *, QString >::const_iterator lineTypeIt = mLineStyles.constFind( symbolLayer );
1763 if ( lineTypeIt != mLineStyles.constEnd() )
1764 {
1765 lineStyleName = lineTypeIt.value();
1766 return lineStyleName;
1767 }
1768 else
1769 {
1770 return lineNameFromPenStyle( symbolLayer->dxfPenStyle() );
1771 }
1772}
1773
1775{
1776 int idx = 0;
1777 int current_distance = std::numeric_limits<int>::max();
1778 for ( int i = 1; i < static_cast< int >( sizeof( sDxfColors ) / sizeof( *sDxfColors ) ); ++i )
1779 {
1780 int dist = color_distance( pixel, i );
1781 if ( dist < current_distance )
1782 {
1783 current_distance = dist;
1784 idx = i;
1785 if ( dist == 0 )
1786 break;
1787 }
1788 }
1789 return idx;
1790}
1791
1792int QgsDxfExport::color_distance( QRgb p1, int index )
1793{
1794 if ( index > 255 || index < 0 )
1795 {
1796 return 0;
1797 }
1798
1799 double redDiff = qRed( p1 ) - sDxfColors[index][0];
1800 double greenDiff = qGreen( p1 ) - sDxfColors[index][1];
1801 double blueDiff = qBlue( p1 ) - sDxfColors[index][2];
1802#if 0
1803 QgsDebugMsg( QStringLiteral( "color_distance( r:%1 g:%2 b:%3 <=> i:%4 r:%5 g:%6 b:%7 ) => %8" )
1804 .arg( qRed( p1 ) ).arg( qGreen( p1 ) ).arg( qBlue( p1 ) )
1805 .arg( index )
1806 .arg( mDxfColors[index][0] )
1807 .arg( mDxfColors[index][1] )
1808 .arg( mDxfColors[index][2] )
1809 .arg( redDiff * redDiff + greenDiff * greenDiff + blueDiff * blueDiff ) );
1810#endif
1811 return redDiff * redDiff + greenDiff * greenDiff + blueDiff * blueDiff;
1812}
1813
1814QRgb QgsDxfExport::createRgbEntry( qreal r, qreal g, qreal b )
1815{
1816 return QColor::fromRgbF( r, g, b ).rgb();
1817}
1818
1819QgsRenderContext QgsDxfExport::renderContext() const
1820{
1821 return mRenderContext;
1822}
1823
1824double QgsDxfExport::mapUnitScaleFactor( double scale, QgsUnitTypes::RenderUnit symbolUnits, QgsUnitTypes::DistanceUnit mapUnits, double mapUnitsPerPixel )
1825{
1826 if ( symbolUnits == QgsUnitTypes::RenderMapUnits )
1827 {
1828 return 1.0;
1829 }
1830 else if ( symbolUnits == QgsUnitTypes::RenderMillimeters )
1831 {
1833 }
1834 else if ( symbolUnits == QgsUnitTypes::RenderPixels )
1835 {
1836 return mapUnitsPerPixel;
1837 }
1838 return 1.0;
1839}
1840
1841void QgsDxfExport::clipValueToMapUnitScale( double &value, const QgsMapUnitScale &scale, double pixelToMMFactor ) const
1842{
1843 if ( !scale.minSizeMMEnabled && !scale.maxSizeMMEnabled )
1844 {
1845 return;
1846 }
1847
1848 double mapUnitsPerPixel = mMapSettings.mapToPixel().mapUnitsPerPixel();
1849
1850 double minSizeMU = std::numeric_limits<double>::lowest();
1851 if ( scale.minSizeMMEnabled )
1852 {
1853 minSizeMU = scale.minSizeMM * pixelToMMFactor * mapUnitsPerPixel;
1854 }
1855 if ( !qgsDoubleNear( scale.minScale, 0.0 ) )
1856 {
1857 minSizeMU = std::max( minSizeMU, value );
1858 }
1859 value = std::max( value, minSizeMU );
1860
1861 double maxSizeMU = std::numeric_limits<double>::max();
1862 if ( scale.maxSizeMMEnabled )
1863 {
1864 maxSizeMU = scale.maxSizeMM * pixelToMMFactor * mapUnitsPerPixel;
1865 }
1866 if ( !qgsDoubleNear( scale.maxScale, 0.0 ) )
1867 {
1868 maxSizeMU = std::min( maxSizeMU, value );
1869 }
1870 value = std::min( value, maxSizeMU );
1871}
1872
1873QList< QPair< QgsSymbolLayer *, QgsSymbol * > > QgsDxfExport::symbolLayers( QgsRenderContext &context )
1874{
1875 QList< QPair< QgsSymbolLayer *, QgsSymbol * > > symbolLayers;
1876
1877 for ( DxfLayerJob *job : std::as_const( mJobs ) )
1878 {
1879 const QgsSymbolList symbols = job->renderer->symbols( context );
1880
1881 for ( QgsSymbol *symbol : symbols )
1882 {
1883 int maxSymbolLayers = symbol->symbolLayerCount();
1884 if ( mSymbologyExport != SymbolLayerSymbology )
1885 {
1886 maxSymbolLayers = 1;
1887 }
1888 for ( int i = 0; i < maxSymbolLayers; ++i )
1889 {
1890 symbolLayers.append( qMakePair( symbol->symbolLayer( i ), symbol ) );
1891 }
1892 }
1893 }
1894
1895 return symbolLayers;
1896}
1897
1898void QgsDxfExport::writeDefaultLinetypes()
1899{
1900 // continuous (Qt solid line)
1901 for ( const QString &ltype : { QStringLiteral( "ByLayer" ), QStringLiteral( "ByBlock" ), QStringLiteral( "CONTINUOUS" ) } )
1902 {
1903 writeGroup( 0, QStringLiteral( "LTYPE" ) );
1904 writeHandle();
1905 writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
1906 writeGroup( 100, QStringLiteral( "AcDbLinetypeTableRecord" ) );
1907 writeGroup( 2, ltype );
1908 writeGroup( 70, 64 );
1909 writeGroup( 3, QStringLiteral( "Defaultstyle" ) );
1910 writeGroup( 72, 65 );
1911 writeGroup( 73, 0 );
1912 writeGroup( 40, 0.0 );
1913 }
1914
1915 double das = dashSize();
1916 double dss = dashSeparatorSize();
1917 double dos = dotSize();
1918
1919 QVector<qreal> dashVector( 2 );
1920 dashVector[0] = das;
1921 dashVector[1] = dss;
1922 writeLinetype( QStringLiteral( "DASH" ), dashVector, QgsUnitTypes::RenderMapUnits );
1923
1924 QVector<qreal> dotVector( 2 );
1925 dotVector[0] = dos;
1926 dotVector[1] = dss;
1927 writeLinetype( QStringLiteral( "DOT" ), dotVector, QgsUnitTypes::RenderMapUnits );
1928
1929 QVector<qreal> dashDotVector( 4 );
1930 dashDotVector[0] = das;
1931 dashDotVector[1] = dss;
1932 dashDotVector[2] = dos;
1933 dashDotVector[3] = dss;
1934 writeLinetype( QStringLiteral( "DASHDOT" ), dashDotVector, QgsUnitTypes::RenderMapUnits );
1935
1936 QVector<qreal> dashDotDotVector( 6 );
1937 dashDotDotVector[0] = das;
1938 dashDotDotVector[1] = dss;
1939 dashDotDotVector[2] = dos;
1940 dashDotDotVector[3] = dss;
1941 dashDotDotVector[4] = dos;
1942 dashDotDotVector[5] = dss;
1943 writeLinetype( QStringLiteral( "DASHDOTDOT" ), dashDotDotVector, QgsUnitTypes::RenderMapUnits );
1944}
1945
1946void QgsDxfExport::writeSymbolLayerLinetype( const QgsSymbolLayer *symbolLayer )
1947{
1948 if ( !symbolLayer )
1949 {
1950 return;
1951 }
1952
1954 QVector<qreal> customLinestyle = symbolLayer->dxfCustomDashPattern( unit );
1955 if ( !customLinestyle.isEmpty() )
1956 {
1957 QString name = QStringLiteral( "symbolLayer%1" ).arg( mSymbolLayerCounter++ );
1958 writeLinetype( name, customLinestyle, unit );
1959 mLineStyles.insert( symbolLayer, name );
1960 }
1961}
1962
1963int QgsDxfExport::nLineTypes( const QList< QPair< QgsSymbolLayer *, QgsSymbol * > > &symbolLayers )
1964{
1965 int nLineTypes = 0;
1966 for ( const auto &symbolLayer : symbolLayers )
1967 {
1968 const QgsSimpleLineSymbolLayer *simpleLine = dynamic_cast< const QgsSimpleLineSymbolLayer * >( symbolLayer.first );
1969 if ( simpleLine )
1970 {
1971 if ( simpleLine->useCustomDashPattern() )
1972 {
1973 ++nLineTypes;
1974 }
1975 }
1976 }
1977 return nLineTypes;
1978}
1979
1980void QgsDxfExport::writeLinetype( const QString &styleName, const QVector<qreal> &pattern, QgsUnitTypes::RenderUnit u )
1981{
1982 double length = 0;
1983 for ( qreal size : pattern )
1984 {
1985 length += ( size * mapUnitScaleFactor( mSymbologyScale, u, mMapUnits, mMapSettings.mapToPixel().mapUnitsPerPixel() ) );
1986 }
1987
1988 writeGroup( 0, QStringLiteral( "LTYPE" ) );
1989 writeHandle();
1990 // 330 5
1991 writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
1992 writeGroup( 100, QStringLiteral( "AcDbLinetypeTableRecord" ) );
1993 writeGroup( 2, styleName );
1994 writeGroup( 70, 64 ); // 0?
1995 writeGroup( 3, QString() );
1996 writeGroup( 72, 65 );
1997 writeGroup( 73, pattern.size() );
1998 writeGroup( 40, length );
1999
2000 bool isGap = false;
2001 for ( qreal size : pattern )
2002 {
2003 // map units or mm?
2004 double segmentLength = ( isGap ? -size : size );
2005 segmentLength *= mapUnitScaleFactor( mSymbologyScale, u, mMapUnits, mMapSettings.mapToPixel().mapUnitsPerPixel() );
2006 writeGroup( 49, segmentLength );
2007 writeGroup( 74, 0 );
2008 isGap = !isGap;
2009 }
2010}
2011
2012void QgsDxfExport::addGeometryGeneratorSymbolLayer( QgsSymbolRenderContext &ctx, const QgsCoordinateTransform &ct, const QString &layer, QgsSymbolLayer *symbolLayer, bool allSymbolLayers )
2013{
2014 QgsGeometryGeneratorSymbolLayer *gg = dynamic_cast<QgsGeometryGeneratorSymbolLayer *>( symbolLayer );
2015 if ( !gg )
2016 {
2017 return;
2018 }
2019
2020 const QgsFeature *fet = ctx.feature();
2021 if ( !fet )
2022 {
2023 return;
2024 }
2025
2026 QgsFeature f = *fet;
2027
2028 QgsExpressionContext &expressionContext = ctx.renderContext().expressionContext();
2029 QgsExpression geomExpr( gg->geometryExpression() );
2030 geomExpr.prepare( &expressionContext );
2031 QgsGeometry geom = geomExpr.evaluate( &expressionContext ).value<QgsGeometry>();
2032 f.setGeometry( geom );
2033
2034 QgsSymbol *symbol = gg->subSymbol();
2035 if ( symbol && symbol->symbolLayerCount() > 0 )
2036 {
2037 QgsExpressionContextScope *symbolExpressionContextScope = symbol->symbolRenderContext()->expressionContextScope();
2038 symbolExpressionContextScope->setFeature( f );
2039
2040 ctx.setFeature( &f );
2041
2042 int nSymbolLayers = allSymbolLayers ? symbol->symbolLayerCount() : 1;
2043 for ( int i = 0; i < nSymbolLayers; ++i )
2044 {
2045 addFeature( ctx, ct, layer, symbol->symbolLayer( i ), symbol );
2046 }
2047
2048 ctx.setFeature( fet );
2049 }
2050}
2051
2052bool QgsDxfExport::hasDataDefinedProperties( const QgsSymbolLayer *sl, const QgsSymbol *symbol )
2053{
2054 if ( !sl || !symbol )
2055 {
2056 return false;
2057 }
2058
2060 {
2061 return true;
2062 }
2063
2064 return sl->hasDataDefinedProperties();
2065}
2066
2067double QgsDxfExport::dashSize() const
2068{
2069 double size = mSymbologyScale * 0.002;
2070 return sizeToMapUnits( size );
2071}
2072
2073double QgsDxfExport::dotSize() const
2074{
2075 double size = mSymbologyScale * 0.0006;
2076 return sizeToMapUnits( size );
2077}
2078
2079double QgsDxfExport::dashSeparatorSize() const
2080{
2081 double size = mSymbologyScale * 0.0006;
2082 return sizeToMapUnits( size );
2083}
2084
2085double QgsDxfExport::sizeToMapUnits( double s ) const
2086{
2088 return size;
2089}
2090
2091QString QgsDxfExport::lineNameFromPenStyle( Qt::PenStyle style )
2092{
2093 switch ( style )
2094 {
2095 case Qt::DashLine:
2096 return QStringLiteral( "DASH" );
2097 case Qt::DotLine:
2098 return QStringLiteral( "DOT" );
2099 case Qt::DashDotLine:
2100 return QStringLiteral( "DASHDOT" );
2101 case Qt::DashDotDotLine:
2102 return QStringLiteral( "DASHDOTDOT" );
2103 case Qt::SolidLine:
2104 default:
2105 return QStringLiteral( "CONTINUOUS" );
2106 }
2107}
2108
2109QString QgsDxfExport::dxfLayerName( const QString &name )
2110{
2111 if ( name.isEmpty() )
2112 return QStringLiteral( "0" );
2113
2114 // dxf layers can be max 255 characters long
2115 QString layerName = name.left( 255 );
2116
2117 // replaced restricted characters with underscore
2118 // < > / \ " : ; ? * | = '
2119 // See http://docs.autodesk.com/ACD/2010/ENU/AutoCAD%202010%20User%20Documentation/index.html?url=WS1a9193826455f5ffa23ce210c4a30acaf-7345.htm,topicNumber=d0e41665
2120 layerName.replace( '<', '_' );
2121 layerName.replace( '>', '_' );
2122 layerName.replace( '/', '_' );
2123 layerName.replace( '\\', '_' );
2124 layerName.replace( '\"', '_' );
2125 layerName.replace( ':', '_' );
2126 layerName.replace( ';', '_' );
2127 layerName.replace( '?', '_' );
2128 layerName.replace( '*', '_' );
2129 layerName.replace( '|', '_' );
2130 layerName.replace( '=', '_' );
2131 layerName.replace( '\'', '_' );
2132 // if layer name contains comma, resulting file is unreadable in AutoCAD
2133 // see https://github.com/qgis/QGIS/issues/47381
2134 layerName.replace( ',', '_' );
2135
2136 // also remove newline characters (#15067)
2137 layerName.replace( QLatin1String( "\r\n" ), QLatin1String( "_" ) );
2138 layerName.replace( '\r', '_' );
2139 layerName.replace( '\n', '_' );
2140
2141 return layerName.trimmed();
2142}
2143
2144bool QgsDxfExport::layerIsScaleBasedVisible( const QgsMapLayer *layer ) const
2145{
2146 if ( !layer )
2147 return false;
2148
2149 if ( mSymbologyExport == QgsDxfExport::NoSymbology )
2150 return true;
2151
2152 return layer->isInScaleRange( mSymbologyScale );
2153}
2154
2155QString QgsDxfExport::layerName( const QString &id, const QgsFeature &f ) const
2156{
2157 // TODO: make this thread safe
2158 const QList< QgsMapLayer * > layers = mMapSettings.layers();
2159 for ( QgsMapLayer *ml : layers )
2160 {
2161 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
2162 if ( vl && vl->id() == id )
2163 {
2164 int attrIdx = mLayerNameAttribute.value( vl->id(), -1 );
2165 return dxfLayerName( attrIdx < 0 ? layerName( vl ) : f.attribute( attrIdx ).toString() );
2166 }
2167 }
2168
2169 return QStringLiteral( "0" );
2170}
2171
2172QString QgsDxfExport::dxfEncoding( const QString &name )
2173{
2174 const QList< QByteArray > codecs = QTextCodec::availableCodecs();
2175 for ( const QByteArray &codec : codecs )
2176 {
2177 if ( name != codec )
2178 continue;
2179
2180 int i;
2181 for ( i = 0; i < static_cast< int >( sizeof( DXF_ENCODINGS ) / sizeof( *DXF_ENCODINGS ) ) && name != DXF_ENCODINGS[i][1]; ++i )
2182 ;
2183
2184 if ( i == static_cast< int >( sizeof( DXF_ENCODINGS ) / sizeof( *DXF_ENCODINGS ) ) )
2185 continue;
2186
2187 return DXF_ENCODINGS[i][0];
2188 }
2189
2190 return QString();
2191}
2192
2194{
2195 QStringList encodings;
2196 const QList< QByteArray > codecs = QTextCodec::availableCodecs();
2197 encodings.reserve( codecs.size() );
2198 for ( const QByteArray &codec : codecs )
2199 {
2200 int i;
2201 for ( i = 0; i < static_cast< int >( sizeof( DXF_ENCODINGS ) / sizeof( *DXF_ENCODINGS ) ) && strcasecmp( codec.data(), DXF_ENCODINGS[i][1] ) != 0; ++i )
2202 ;
2203
2204 if ( i < static_cast< int >( sizeof( DXF_ENCODINGS ) / sizeof( *DXF_ENCODINGS ) ) )
2205 encodings << codec.data();
2206 }
2207
2208 encodings.removeDuplicates();
2209
2210 return encodings;
2211}
2212
2214{
2215 Q_ASSERT( vl );
2216 return mLayerTitleAsName && !vl->title().isEmpty() ? vl->title() : vl->name();
2217}
2218
2219void QgsDxfExport::drawLabel( const QString &layerId, QgsRenderContext &context, pal::LabelPosition *label, const QgsPalLayerSettings &settings )
2220{
2221 Q_UNUSED( context )
2222
2223 if ( !settings.drawLabels )
2224 return;
2225
2226 QgsTextLabelFeature *lf = dynamic_cast<QgsTextLabelFeature *>( label->getFeaturePart()->feature() );
2227
2228 // Copy to temp, editable layer settings
2229 // these settings will be changed by any data defined values, then used for rendering label components
2230 // settings may be adjusted during rendering of components
2231 QgsPalLayerSettings tmpLyr( settings );
2232
2233 // apply any previously applied data defined settings for the label
2234 const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues = lf->dataDefinedValues();
2235
2236 //font
2237 QFont dFont = lf->definedFont();
2238 QgsDebugMsgLevel( QStringLiteral( "PAL font tmpLyr: %1, Style: %2" ).arg( tmpLyr.format().font().toString(), tmpLyr.format().font().styleName() ), 4 );
2239 QgsDebugMsgLevel( QStringLiteral( "PAL font definedFont: %1, Style: %2" ).arg( dFont.toString(), dFont.styleName() ), 4 );
2240
2241 QgsTextFormat format = tmpLyr.format();
2242 format.setFont( dFont );
2243 tmpLyr.setFormat( format );
2244
2245 if ( tmpLyr.multilineAlign == Qgis::LabelMultiLineAlignment::FollowPlacement )
2246 {
2247 //calculate font alignment based on label quadrant
2248 switch ( label->getQuadrant() )
2249 {
2253 tmpLyr.multilineAlign = Qgis::LabelMultiLineAlignment::Right;
2254 break;
2258 tmpLyr.multilineAlign = Qgis::LabelMultiLineAlignment::Center;
2259 break;
2263 tmpLyr.multilineAlign = Qgis::LabelMultiLineAlignment::Left;
2264 break;
2265 }
2266 }
2267
2268 // update tmpLyr with any data defined text style values
2269 QgsPalLabeling::dataDefinedTextStyle( tmpLyr, ddValues );
2270
2271 // update tmpLyr with any data defined text buffer values
2272 QgsPalLabeling::dataDefinedTextBuffer( tmpLyr, ddValues );
2273
2274 // update tmpLyr with any data defined text formatting values
2275 QgsPalLabeling::dataDefinedTextFormatting( tmpLyr, ddValues );
2276
2277 // add to the results
2278 QString txt = label->getFeaturePart()->feature()->labelText();
2279
2280 QgsFeatureId fid = label->getFeaturePart()->featureId();
2281 QString dxfLayer = mDxfLayerNames[layerId][fid];
2282
2283 QString wrapchr = tmpLyr.wrapChar.isEmpty() ? QStringLiteral( "\n" ) : tmpLyr.wrapChar;
2284
2285 //add the direction symbol if needed
2286 if ( !txt.isEmpty() && tmpLyr.placement == Qgis::LabelPlacement::Line && tmpLyr.lineSettings().addDirectionSymbol() )
2287 {
2288 bool prependSymb = false;
2289 QString symb = tmpLyr.lineSettings().rightDirectionSymbol();
2290
2291 if ( label->getReversed() )
2292 {
2293 prependSymb = true;
2294 symb = tmpLyr.lineSettings().leftDirectionSymbol();
2295 }
2296
2297 if ( tmpLyr.lineSettings().reverseDirectionSymbol() )
2298 {
2299 if ( symb == tmpLyr.lineSettings().rightDirectionSymbol() )
2300 {
2301 prependSymb = true;
2302 symb = tmpLyr.lineSettings().leftDirectionSymbol();
2303 }
2304 else
2305 {
2306 prependSymb = false;
2307 symb = tmpLyr.lineSettings().rightDirectionSymbol();
2308 }
2309 }
2310
2311 switch ( tmpLyr.lineSettings().directionSymbolPlacement() )
2312 {
2314 prependSymb = true;
2315 symb = symb + wrapchr;
2316 break;
2317
2319 prependSymb = false;
2320 symb = wrapchr + symb;
2321 break;
2322
2324 break;
2325 }
2326
2327 if ( prependSymb )
2328 {
2329 txt.prepend( symb );
2330 }
2331 else
2332 {
2333 txt.append( symb );
2334 }
2335 }
2336
2337 if ( mFlags & FlagNoMText )
2338 {
2339 txt.replace( QChar( QChar::LineFeed ), ' ' );
2340 txt.replace( QChar( QChar::CarriageReturn ), ' ' );
2341 writeText( dxfLayer, txt, label, tmpLyr, context.expressionContext() );
2342 }
2343 else
2344 {
2345 txt.replace( QString( QChar( QChar::CarriageReturn ) ) + QString( QChar( QChar::LineFeed ) ), QStringLiteral( "\\P" ) );
2346 txt.replace( QChar( QChar::CarriageReturn ), QStringLiteral( "\\P" ) );
2347 txt = txt.replace( wrapchr, QLatin1String( "\\P" ) );
2348 txt.replace( QLatin1String( " " ), QLatin1String( "\\~" ) );
2349
2350 if ( tmpLyr.format().font().underline() )
2351 {
2352 txt.prepend( "\\L" ).append( "\\l" );
2353 }
2354
2355 if ( tmpLyr.format().font().overline() )
2356 {
2357 txt.prepend( "\\O" ).append( "\\o" );
2358 }
2359
2360 if ( tmpLyr.format().font().strikeOut() )
2361 {
2362 txt.prepend( "\\K" ).append( "\\k" );
2363 }
2364
2365 txt.prepend( QStringLiteral( "\\f%1|i%2|b%3;\\H%4;" )
2366 .arg( tmpLyr.format().font().family() )
2367 .arg( tmpLyr.format().font().italic() ? 1 : 0 )
2368 .arg( tmpLyr.format().font().bold() ? 1 : 0 )
2369 .arg( label->getHeight() / ( 1 + txt.count( QStringLiteral( "\\P" ) ) ) * 0.75 ) );
2370 writeMText( dxfLayer, txt, QgsPoint( label->getX(), label->getY() ), label->getWidth(), label->getAlpha() * 180.0 / M_PI, tmpLyr.format().color() );
2371 }
2372}
2373
2374
2375void QgsDxfExport::registerDxfLayer( const QString &layerId, QgsFeatureId fid, const QString &layerName )
2376{
2377 if ( !mDxfLayerNames.contains( layerId ) )
2378 mDxfLayerNames[ layerId ] = QMap<QgsFeatureId, QString>();
2379
2380 mDxfLayerNames[layerId][fid] = layerName;
2381}
2382
2384{
2385 mCrs = crs;
2386 mMapUnits = crs.mapUnits();
2387}
2388
2390{
2391 return mCrs;
2392}
2393
2395{
2396 QString splitLayerFieldName;
2397 const QgsFields fields = mLayer->fields();
2398 if ( mLayerOutputAttributeIndex >= 0 && mLayerOutputAttributeIndex < fields.size() )
2399 {
2400 splitLayerFieldName = fields.at( mLayerOutputAttributeIndex ).name();
2401 }
2402
2403 return splitLayerFieldName;
2404}
@ DynamicRotation
Rotation of symbol may be changed during rendering and symbol should not be cached.
@ OverPoint
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point....
@ Line
Arranges candidates parallel to a generalised line representing the feature or parallel to a polygon'...
LabelQuadrantPosition
Label quadrant positions.
Definition: qgis.h:622
Abstract base class for all geometries.
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
virtual bool hasCurvedSegments() const
Returns true if the geometry contains curved segments.
Circular string geometry type.
QgsPoint pointN(int i) const SIP_HOLDGIL
Returns the point at index i within the circular string.
int numPoints() const override SIP_HOLDGIL
Returns the number of points in the curve.
Compound curve geometry type.
const QgsCurve * curveAt(int i) const SIP_HOLDGIL
Returns the curve at the specified index.
int nCurves() const SIP_HOLDGIL
Returns the number of curves in the geometry.
This class represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Q_GADGET QgsUnitTypes::DistanceUnit mapUnits
Class for doing transforms between two map coordinate systems.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const SIP_THROW(QgsCsException)
Transforms a rectangle from the source CRS to the destination CRS.
Curve polygon geometry type.
const QgsCurve * interiorRing(int i) const SIP_HOLDGIL
Retrieves an interior ring from the curve polygon.
const QgsCurve * exteriorRing() const SIP_HOLDGIL
Returns the curve polygon's exterior ring.
int numInteriorRings() const SIP_HOLDGIL
Returns the number of interior rings contained with the curve polygon.
Abstract base class for curved geometry type.
Definition: qgscurve.h:36
QgsCoordinateSequence coordinateSequence() const override
Retrieves the sequence of geometries, rings and nodes.
Definition: qgscurve.cpp:77
virtual int numPoints() const =0
Returns the number of points in the curve.
virtual bool isClosed() const SIP_HOLDGIL
Returns true if the curve is closed.
Definition: qgscurve.cpp:53
void writeFilledCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius)
Write filled circle (as hatch)
ExportResult
The result of an export as dxf operation.
Definition: qgsdxfexport.h:123
@ DeviceNotWritableError
Device not writable error.
@ Success
Successful export.
@ EmptyExtentError
Empty extent, no extent given and no extent could be derived from layers.
@ InvalidDeviceError
Invalid device error.
@ SymbolLayerSymbology
Exports one feature per symbol layer (considering symbol levels)
Definition: qgsdxfexport.h:107
@ NoSymbology
Export only data.
Definition: qgsdxfexport.h:105
void writeCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius, const QString &lineStyleName, double width)
Write circle (as polyline)
~QgsDxfExport() override
ExportResult writeToFile(QIODevice *d, const QString &codec)
Export to a dxf file in the given encoding.
void writeLine(const QgsPoint &pt1, const QgsPoint &pt2, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Write line (as a polyline)
static double mapUnitScaleFactor(double scale, QgsUnitTypes::RenderUnit symbolUnits, QgsUnitTypes::DistanceUnit mapUnits, double mapUnitsPerPixel=1.0)
Returns scale factor for conversion to map units.
void writeGroup(int code, int i)
Write a tuple of group code and integer value.
QString layerName(const QString &id, const QgsFeature &f) const
Gets layer name for feature.
void setFlags(QgsDxfExport::Flags flags)
Sets the export flags.
@ FlagNoMText
Export text as TEXT elements. If not set, text will be exported as MTEXT elements.
Definition: qgsdxfexport.h:113
void writeInt(int i)
Write an integer value.
void writeMText(const QString &layer, const QString &text, const QgsPoint &pt, double width, double angle, const QColor &color)
Write mtext (MTEXT)
QgsDxfExport()
Constructor for QgsDxfExport.
QgsUnitTypes::DistanceUnit mapUnits() const
Retrieve map units.
int writeHandle(int code=5, int handle=0)
Write a tuple of group code and a handle.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination CRS, or an invalid CRS if no reprojection will be done.
HAlign
Horizontal alignments.
Definition: qgsdxfexport.h:144
@ HCenter
Centered (1)
@ Undefined
Undefined.
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Set destination CRS.
void addLayers(const QList< QgsDxfExport::DxfLayer > &layers)
Add layers to export.
static QString dxfLayerName(const QString &name)
Returns cleaned layer name for use in DXF.
void writeDouble(double d)
Write a floating point value.
void writeText(const QString &layer, const QString &text, const QgsPoint &pt, double size, double angle, const QColor &color, QgsDxfExport::HAlign hali=QgsDxfExport::HAlign::Undefined, QgsDxfExport::VAlign vali=QgsDxfExport::VAlign::Undefined)
Write text (TEXT)
void writeString(const QString &s)
Write a string value.
void writePolygon(const QgsRingSequence &polygon, const QString &layer, const QString &hatchPattern, const QColor &color)
Draw dxf filled polygon (HATCH)
void drawLabel(const QString &layerId, QgsRenderContext &context, pal::LabelPosition *label, const QgsPalLayerSettings &settings) override
Add a label to the dxf output.
static QString dxfEncoding(const QString &name)
Returns DXF encoding for Qt encoding.
static int closestColorMatch(QRgb color)
Gets DXF palette index of nearest entry for given color.
void writePoint(const QString &layer, const QColor &color, const QgsPoint &pt)
Write point.
Q_DECL_DEPRECATED void registerDxfLayer(const QString &layerId, QgsFeatureId fid, const QString &layer)
Register name of layer for feature.
QgsDxfExport::Flags flags() const
Returns the export flags.
VAlign
Vertical alignments.
Definition: qgsdxfexport.h:134
@ Undefined
Undefined.
void clipValueToMapUnitScale(double &value, const QgsMapUnitScale &scale, double pixelToMMFactor) const
Clips value to scale minimum/maximum.
void writePolyline(const QgsPointSequence &line, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Draw dxf primitives (LWPOLYLINE)
static QStringList encodings()
Returns list of available DXF encodings.
void setMapSettings(const QgsMapSettings &settings)
Set map settings and assign layer name attributes.
void writeGroupCode(int code)
Write a group code.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the scope.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QList< QgsExpressionContextScope * > scopes()
Returns a list of scopes contained within the stack.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Class for parsing and evaluation of expressions (formerly called "search strings").
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
@ SymbolLevels
Rendering with symbol levels (i.e. implements symbols(), symbolForFeature())
Definition: qgsrenderer.h:263
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setExpressionContext(const QgsExpressionContext &context)
Sets the expression context used to evaluate filter expressions.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsGeometry geometry
Definition: qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:233
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:338
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:170
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
QString name
Definition: qgsfield.h:60
Container of fields for a vector layer.
Definition: qgsfields.h:45
int size() const
Returns number of items.
Definition: qgsfields.cpp:138
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
Geometry collection.
int numGeometries() const SIP_HOLDGIL
Returns the number of geometries within the collection.
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
QString geometryExpression() const
Gets the expression to generate this geometry.
QgsSymbol * subSymbol() override
Returns the symbol's sub symbol, if present.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:164
Does vector analysis using the geos library and handles import, export, exception handling*.
Definition: qgsgeos.h:99
The QgsLabelFeature class describes a feature that should be used within the labeling engine.
QgsPointXY anchorPosition() const
In case of quadrand or aligned positioning, this is set to the anchor point.
QString labelText() const
Text of the label.
bool reverseDirectionSymbol() const
Returns true if direction symbols should be reversed.
DirectionSymbolPlacement directionSymbolPlacement() const
Returns the placement for direction symbols.
QString leftDirectionSymbol() const
Returns the string to use for left direction arrows.
@ SymbolLeftRight
Place direction symbols on left/right of label.
@ SymbolAbove
Place direction symbols on above label.
@ SymbolBelow
Place direction symbols on below label.
QString rightDirectionSymbol() const
Returns the string to use for right direction arrows.
bool addDirectionSymbol() const
Returns true if '<' or '>' (or custom strings set via leftDirectionSymbol and rightDirectionSymbol) w...
virtual void run(QgsRenderContext &context)=0
Runs the labeling job.
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:45
int numPoints() const override SIP_HOLDGIL
Returns the number of points in the curve.
QgsPoint pointN(int i) const
Returns the specified point from inside the line string.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
QString name
Definition: qgsmaplayer.h:76
bool isInScaleRange(double scale) const
Tests whether the layer should be visible at the specified scale.
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
QString title() const
Returns the title of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:310
The QgsMapSettings class contains configuration for rendering of the map.
QgsPointXY layerToMapCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from layer's CRS to output CRS
QList< QgsMapLayer * > layers(bool expandGroupLayers=false) const
Returns the list of layers which will be rendered in the map.
void setLayers(const QList< QgsMapLayer * > &layers)
Sets the list of layers to render in the map.
void setOutputDpi(double dpi)
Sets the dpi (dots per inch) used for conversion between real world units (e.g.
const QgsMapToPixel & mapToPixel() const
void setExtent(const QgsRectangle &rect, bool magnified=true)
Sets the coordinates of the rectangle which should be rendered.
QMap< QString, QString > layerStyleOverrides() const
Returns the map of map layer style overrides (key: layer ID, value: style name) where a different sty...
void setOutputSize(QSize size)
Sets the size of the resulting map image, in pixels.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Sets the destination crs (coordinate reference system) for the map render.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context, which stores various information regarding which datum tran...
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:39
double mapUnitsPerPixel() const
Returns the current map units per pixel.
Struct for storing maximum and minimum scales for measurements in map units.
bool minSizeMMEnabled
Whether the minimum size in mm should be respected.
double maxScale
The maximum scale, or 0.0 if unset.
double minScale
The minimum scale, or 0.0 if unset.
double maxSizeMM
The maximum size in millimeters, or 0.0 if unset.
bool maxSizeMMEnabled
Whether the maximum size in mm should be respected.
double minSizeMM
The minimum size in millimeters, or 0.0 if unset.
Abstract base class for marker symbol layers.
QgsUnitTypes::RenderUnit sizeUnit() const
Returns the units for the symbol's size.
double size() const
Returns the symbol size.
Contains settings for how a map layer will be labeled.
void setFormat(const QgsTextFormat &format)
Sets the label text formatting settings, e.g., font settings, buffer settings, etc.
QString wrapChar
Wrapping character string.
Qgis::LabelPlacement placement
Label placement mode.
bool drawLabels
Whether to draw labels for this layer.
Qgis::LabelQuadrantPosition quadOffset
Sets the quadrant in which to offset labels from feature.
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the label's property collection, used for data defined overrides.
Qgis::LabelMultiLineAlignment multilineAlign
Horizontal alignment of multi-line labels.
@ Hali
Horizontal alignment for data defined label position (Left, Center, Right)
@ Vali
Vertical alignment for data defined label position (Bottom, Base, Half, Cap, Top)
const QgsLabelLineSettings & lineSettings() const
Returns the label line settings, which contain settings related to how the label engine places and fo...
const QgsTextFormat & format() const
Returns the label text formatting settings, e.g., font settings, buffer settings, etc.
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
Q_GADGET double x
Definition: qgspoint.h:52
double z
Definition: qgspoint.h:54
double y
Definition: qgspoint.h:53
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:477
A grouped map of multiple QgsProperty objects, each referenced by a integer key value.
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const override
Returns the calculated value of the property with the specified key from within the collection.
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
Definition: qgsrectangle.h:391
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:469
QgsPointXY center() const SIP_HOLDGIL
Returns the center point of the rectangle.
Definition: qgsrectangle.h:251
Contains information about the context of a rendering operation.
void setScaleFactor(double factor)
Sets the scaling factor for the render to convert painter units to physical sizes.
QgsExpressionContext & expressionContext()
Gets the expression context.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
void setExtent(const QgsRectangle &extent)
When rendering a map layer, calling this method sets the "clipping" extent for the layer (in the laye...
void setMapToPixel(const QgsMapToPixel &mtp)
Sets the context's map to pixel transform, which transforms between map coordinates and device coordi...
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
void setLabelingEngine(QgsLabelingEngine *engine)
Assigns the labeling engine.
void setRendererScale(double scale)
Sets the renderer map scale.
QgsLabelingEngine * labelingEngine() const
Gets access to new labeling engine (may be nullptr).
A simple line symbol layer, which renders lines using a line in a variety of styles (e....
bool useCustomDashPattern() const
Returns true if the line uses a custom dash pattern.
virtual double dxfOffset(const QgsDxfExport &e, QgsSymbolRenderContext &context) const
Gets offset.
virtual QColor dxfBrushColor(QgsSymbolRenderContext &context) const
Gets brush/fill color.
virtual QVector< qreal > dxfCustomDashPattern(QgsUnitTypes::RenderUnit &unit) const
Gets dash pattern.
virtual Qt::PenStyle dxfPenStyle() const
Gets pen style.
virtual QColor dxfColor(QgsSymbolRenderContext &context) const
Gets color.
virtual QString layerType() const =0
Returns a string that represents this layer type.
virtual double dxfWidth(const QgsDxfExport &e, QgsSymbolRenderContext &context) const
Gets line width.
virtual double dxfAngle(QgsSymbolRenderContext &context) const
Gets angle.
virtual bool writeDxf(QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift=QPointF(0.0, 0.0)) const
write as DXF
virtual Qt::BrushStyle dxfBrushStyle() const
Gets brush/fill style.
virtual bool hasDataDefinedProperties() const
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
const QgsFeature * feature() const
Returns the current feature being rendered.
QgsExpressionContextScope * expressionContextScope()
This scope is always available when a symbol of this type is being rendered.
void setFeature(const QgsFeature *f)
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:93
QgsSymbolRenderContext * symbolRenderContext()
Returns the symbol render context.
Definition: qgssymbol.cpp:1824
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
Definition: qgssymbol.cpp:756
Qgis::SymbolRenderHints renderHints() const
Returns the rendering hint flags for the symbol.
Definition: qgssymbol.h:514
int symbolLayerCount() const
Returns the total number of symbol layers contained in the symbol.
Definition: qgssymbol.h:215
Container for all settings relating to text rendering.
Definition: qgstextformat.h:41
void setFont(const QFont &font)
Sets the font used for rendering text.
QColor color() const
Returns the color that text will be rendered in.
QFont font() const
Returns the font used for rendering text.
Class that adds extra information to QgsLabelFeature for text labels.
QFont definedFont() const
Font to be used for rendering.
const QMap< QgsPalLayerSettings::Property, QVariant > & dataDefinedValues() const
Gets data-defined values.
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:68
@ DistanceMeters
Meters.
Definition: qgsunittypes.h:69
static Q_INVOKABLE double fromUnitToUnitFactor(QgsUnitTypes::DistanceUnit fromUnit, QgsUnitTypes::DistanceUnit toUnit)
Returns the conversion factor between the specified distance units.
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:168
@ RenderPixels
Pixels.
Definition: qgsunittypes.h:171
@ RenderMillimeters
Millimeters.
Definition: qgsunittypes.h:169
@ RenderMapUnits
Map units.
Definition: qgsunittypes.h:170
static bool isNull(const QVariant &variant)
Returns true if the specified variant should be considered a NULL value.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) override
Gets an iterator for features matching the specified request.
QgsFields fields() const
Returns the fields that will be available for features that are retrieved from this source.
Represents a vector layer which manages a vector based data sets.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QgsFeatureRenderer * renderer()
Returns the feature renderer used for rendering the features in the layer in 2D map views.
QgsRectangle extent() const FINAL
Returns the extent of the layer.
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const FINAL
Calculates a list of unique values contained within an attribute in the layer.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:732
QgsFeatureId featureId() const
Returns the unique ID of the feature.
Definition: feature.cpp:164
QgsLabelFeature * feature()
Returns the parent feature.
Definition: feature.h:94
LabelPosition is a candidate feature label position.
Definition: labelposition.h:56
double getAlpha() const
Returns the angle to rotate text (in rad).
double getHeight() const
bool getReversed() const
Quadrant getQuadrant() const
double getWidth() const
FeaturePart * getFeaturePart() const
Returns the feature corresponding to this labelposition.
double getX(int i=0) const
Returns the down-left x coordinate.
double getY(int i=0) const
Returns the down-left y coordinate.
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:786
Contains geos related utilities and functions.
Definition: qgsgeos.h:37
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:3061
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:2466
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:3060
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2527
QVector< QgsRingSequence > QgsCoordinateSequence
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
#define DXF_HANDMAX
Definition: qgsdxfexport.h:46
#define DXF_HANDPLOTSTYLE
Definition: qgsdxfexport.h:47
#define DXF_TRAILER
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QList< QgsSymbolLevel > QgsSymbolLevelOrder
Definition: qgsrenderer.h:88
QList< QgsSymbolLevelItem > QgsSymbolLevel
Definition: qgsrenderer.h:84
QList< QgsSymbol * > QgsSymbolList
Definition: qgsrenderer.h:44
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition: qgssymbol.h:29
const QgsCoordinateReferenceSystem & crs
Holds information about each layer in a DXF job.
QString layerName
std::unique_ptr< QgsFeatureRenderer > renderer
QgsRenderContext renderContext
QgsCoordinateReferenceSystem crs
QgsVectorLayerFeatureSource featureSource
Layers and optional attribute index to split into multiple layers using attribute value as layer name...
Definition: qgsdxfexport.h:74
QString splitLayerAttribute() const
If the split layer attribute is set, the vector layer will be split into several dxf layers,...