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