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