QGIS API Documentation 3.99.0-Master (8e76e220402)
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.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( *this, 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( mSymbologyScale, ml->sizeUnit(), mMapUnits, ctx.renderContext().mapToPixel().mapUnitsPerPixel() ), 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 &&
761 ( job->renderer->capabilities() & QgsFeatureRenderer::SymbolLevels ) &&
762 job->renderer->usingSymbolLevels() )
763 {
764 writeEntitiesSymbolLevels( job );
765
766 continue;
767 }
768
769 const QgsCoordinateTransform ct( job->crs, mMapSettings.destinationCrs(), mMapSettings.transformContext() );
770
771 QgsFeatureRequest request = QgsFeatureRequest().setSubsetOfAttributes( job->attributes, job->fields ).setExpressionContext( job->renderContext.expressionContext() );
772 QgsCoordinateTransform extentTransform = ct;
773 extentTransform.setBallparkTransformsAreAppropriate( true );
774
775 try
776 {
777 request.setFilterRect( extentTransform.transformBoundingBox( mMapSettings.extent(), Qgis::TransformDirection::Reverse ) );
778 }
779 catch ( QgsCsException &e )
780 {
781 QgsDebugError( u"Error transforming DXF layer extent: %1"_s.arg( e.what() ) );
782 continue;
783 }
784
785 if ( mFlags & FlagOnlySelectedFeatures )
786 {
787 request.setFilterFids( job->selectedFeatureIds );
788 }
789
790 QgsFeatureIterator featureIt = job->featureSource.getFeatures( request );
791
792 QgsFeature fet;
793 while ( featureIt.nextFeature( fet ) )
794 {
795 job->renderContext.expressionContext().setFeature( fet );
796 QString lName( dxfLayerName( job->splitLayerAttribute.isNull() ? job->layerDerivedName : fet.attribute( job->splitLayerAttribute ).toString() ) );
797
798 sctx.setFeature( &fet );
799
800 if ( !job->renderer->willRenderFeature( fet, job->renderContext ) )
801 continue;
802
803 if ( mSymbologyExport == Qgis::FeatureSymbologyExport::NoSymbology )
804 {
805 addFeature( sctx, ct, lName, nullptr, nullptr ); // no symbology at all
806 }
807 else
808 {
809 const QgsSymbolList symbolList = job->renderer->symbolsForFeature( fet, job->renderContext );
810 bool hasSymbology = symbolList.size() > 0;
811
812 if ( hasSymbology && mSymbologyExport == Qgis::FeatureSymbologyExport::PerSymbolLayer ) // symbol layer symbology, but layer does not use symbol levels
813 {
814 for ( QgsSymbol *symbol : symbolList )
815 {
816 const QgsSymbolLayerList symbolLayers = symbol->symbolLayers();
817 for ( QgsSymbolLayer *symbolLayer : symbolLayers )
818 {
819 if ( !symbolLayer )
820 continue;
821
822 bool isGeometryGenerator = ( symbolLayer->layerType() == "GeometryGenerator"_L1 );
823 if ( isGeometryGenerator )
824 {
825 addGeometryGeneratorSymbolLayer( sctx, ct, lName, symbolLayer, true );
826 }
827 else
828 {
829 addFeature( sctx, ct, lName, symbolLayer, symbol );
830 }
831 }
832 }
833 }
834 else if ( hasSymbology )
835 {
836 // take first symbollayer from first symbol
837 QgsSymbol *s = symbolList.first();
838 if ( !s || s->symbolLayerCount() < 1 )
839 {
840 continue;
841 }
842
843 if ( s->symbolLayer( 0 )->layerType() == "GeometryGenerator"_L1 )
844 {
845 addGeometryGeneratorSymbolLayer( sctx, ct, lName, s->symbolLayer( 0 ), false );
846 }
847 else
848 {
849 addFeature( sctx, ct, lName, s->symbolLayer( 0 ), s );
850 }
851 }
852
853 if ( job->labelProvider )
854 {
855 job->labelProvider->registerFeature( fet, job->renderContext );
857 registerDxfLayer( job->featureSource.id(), fet.id(), lName );
859 }
860 else if ( job->ruleBasedLabelProvider )
861 {
862 job->ruleBasedLabelProvider->registerFeature( fet, job->renderContext );
864 registerDxfLayer( job->featureSource.id(), fet.id(), lName );
866 }
867 }
868 }
869 }
870
871 QImage image( 10, 10, QImage::Format_ARGB32_Premultiplied );
872 image.setDotsPerMeterX( 96 / 25.4 * 1000 );
873 image.setDotsPerMeterY( 96 / 25.4 * 1000 );
874 QPainter painter( &image );
875 mRenderContext.setPainter( &painter );
876
877 mRenderContext.labelingEngine()->run( mRenderContext );
878
879 endSection();
880}
881
882void QgsDxfExport::prepareRenderers()
883{
884 Q_ASSERT( mJobs.empty() ); // If this fails, stopRenderers() was not called after the last job
885
886 mRenderContext = QgsRenderContext();
887 mRenderContext.setRendererScale( mSymbologyScale );
888 mRenderContext.setExtent( mMapSettings.extent() );
889
890 mRenderContext.setScaleFactor( 96.0 / 25.4 );
891 mRenderContext.setMapToPixel( QgsMapToPixel( 1.0 / mFactor,
892 mMapSettings.extent().center().x(),
893 mMapSettings.extent().center().y(),
894 std::floor( mMapSettings.extent().width() * mFactor ),
895 std::floor( mMapSettings.extent().height() * mFactor ), 0 ) );
896
897 mRenderContext.expressionContext().appendScope( QgsExpressionContextUtils::projectScope( QgsProject::instance() ) ); // skip-keyword-check
898 mRenderContext.expressionContext().appendScope( QgsExpressionContextUtils::globalScope() );
899 mRenderContext.expressionContext().appendScope( QgsExpressionContextUtils::mapSettingsScope( mMapSettings ) );
900
901 mLabelingEngine = std::make_unique<QgsDefaultLabelingEngine>();
902 mLabelingEngine->setMapSettings( mMapSettings );
903 mRenderContext.setLabelingEngine( mLabelingEngine.get() );
904
905 const QList< QgsMapLayer * > layers = mMapSettings.layers();
906 for ( QgsMapLayer *ml : layers )
907 {
908 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
909 if ( !vl )
910 continue;
911
912 if ( !vl->renderer() )
913 continue;
914
915 if ( !layerIsScaleBasedVisible( vl ) )
916 continue;
917
918 QString splitLayerAttribute;
919 int splitLayerAttributeIndex = mLayerNameAttribute.value( vl->id(), -1 );
920 const QgsFields fields = vl->fields();
921 if ( splitLayerAttributeIndex >= 0 && splitLayerAttributeIndex < fields.size() )
922 splitLayerAttribute = fields.at( splitLayerAttributeIndex ).name();
923 DxfLayerJob *job = new DxfLayerJob( vl, mMapSettings.layerStyleOverrides().value( vl->id() ), mRenderContext, this, splitLayerAttribute, layerName( vl ) );
924 mJobs.append( job );
925 }
926}
927
928void QgsDxfExport::writeEntitiesSymbolLevels( DxfLayerJob *job )
929{
930 QHash< QgsSymbol *, QList<QgsFeature> > features;
931
932 QgsRenderContext ctx = renderContext();
933 const QList<QgsExpressionContextScope *> scopes = job->renderContext.expressionContext().scopes();
934 for ( QgsExpressionContextScope *scope : scopes )
935 ctx.expressionContext().appendScope( new QgsExpressionContextScope( *scope ) );
936 QgsSymbolRenderContext sctx( ctx, Qgis::RenderUnit::Millimeters, 1.0, false, Qgis::SymbolRenderHints(), nullptr );
937
938 // get iterator
939 QgsFeatureRequest req;
940 req.setSubsetOfAttributes( job->renderer->usedAttributes( ctx ), job->featureSource.fields() );
941 QgsCoordinateTransform ct( mMapSettings.destinationCrs(), job->crs, mMapSettings.transformContext() );
942 try
943 {
944 req.setFilterRect( ct.transform( mMapSettings.extent() ) );
945 }
946 catch ( const QgsCsException & )
947 {
948 QgsDebugError( u"QgsDxfExport::writeEntitiesSymbolLevels(): extent reprojection failed"_s );
949 return;
950 }
951 if ( mFlags & FlagOnlySelectedFeatures )
952 {
954 }
955
956 QgsFeatureIterator fit = job->featureSource.getFeatures( req );
957
958 // fetch features
959 QgsFeature fet;
960 QgsSymbol *featureSymbol = nullptr;
961 while ( fit.nextFeature( fet ) )
962 {
963 ctx.expressionContext().setFeature( fet );
964 featureSymbol = job->renderer->symbolForFeature( fet, ctx );
965 if ( !featureSymbol )
966 {
967 continue;
968 }
969
970 QHash< QgsSymbol *, QList<QgsFeature> >::iterator it = features.find( featureSymbol );
971 if ( it == features.end() )
972 {
973 it = features.insert( featureSymbol, QList<QgsFeature>() );
974 }
975 it.value().append( fet );
976 }
977
978 // find out order
979 QgsSymbolLevelOrder levels;
980 const QgsSymbolList symbols = job->renderer->symbols( ctx );
981 for ( QgsSymbol *symbol : symbols )
982 {
983 for ( int j = 0; j < symbol->symbolLayerCount(); j++ )
984 {
985 int level = symbol->symbolLayer( j )->renderingPass();
986 if ( level < 0 || level >= 1000 ) // ignore invalid levels
987 continue;
988 QgsSymbolLevelItem item( symbol, j );
989 while ( level >= levels.count() ) // append new empty levels
990 levels.append( QgsSymbolLevel() );
991 levels[level].append( item );
992 }
993 }
994
995 // export symbol layers and symbology
996 for ( const QgsSymbolLevel &level : std::as_const( levels ) )
997 {
998 for ( const QgsSymbolLevelItem &item : level )
999 {
1000 QHash< QgsSymbol *, QList<QgsFeature> >::iterator levelIt = features.find( item.symbol() );
1001 if ( levelIt == features.end() )
1002 {
1003 continue;
1004 }
1005
1006 int llayer = item.layer();
1007 const QList<QgsFeature> &featureList = levelIt.value();
1008 for ( const QgsFeature &feature : featureList )
1009 {
1010 sctx.setFeature( &feature );
1011 addFeature( sctx, ct, job->layerName, levelIt.key()->symbolLayer( llayer ), levelIt.key() );
1012 }
1013 }
1014 }
1015}
1016
1017void QgsDxfExport::stopRenderers()
1018{
1019 qDeleteAll( mJobs );
1020 mJobs.clear();
1021}
1022
1023void QgsDxfExport::writeEndFile()
1024{
1025 mTextStream << DXF_TRAILER;
1026
1027 writeGroup( 0, u"EOF"_s );
1028}
1029
1030void QgsDxfExport::startSection()
1031{
1032 writeGroup( 0, u"SECTION"_s );
1033}
1034
1035void QgsDxfExport::endSection()
1036{
1037 writeGroup( 0, u"ENDSEC"_s );
1038}
1039
1040void QgsDxfExport::writePoint( const QgsPoint &pt, const QString &layer, const QColor &color, QgsSymbolRenderContext &ctx, const QgsSymbolLayer *symbolLayer, const QgsSymbol *symbol, double angle )
1041{
1042#if 0
1043 // debug: draw rectangle for debugging
1044 const QgsMarkerSymbolLayer *msl = dynamic_cast< const QgsMarkerSymbolLayer * >( symbolLayer );
1045 if ( msl )
1046 {
1047 double halfSize = msl->size() * mapUnitScaleFactor( mSymbologyScale,
1048 msl->sizeUnit(), mMapUnits ) / 2.0;
1049 writeGroup( 0, "SOLID" );
1050 writeGroup( 8, layer );
1051 writeGroup( 62, 1 );
1052 writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, pt.x() - halfSize, pt.y() - halfSize ) );
1053 writeGroup( 1, QgsPoint( QgsWkbTypes::PointZ, pt.x() + halfSize, pt.y() - halfSize ) );
1054 writeGroup( 2, QgsPoint( QgsWkbTypes::PointZ, pt.x() - halfSize, pt.y() + halfSize ) );
1055 writeGroup( 3, QgsPoint( QgsWkbTypes::PointZ, pt.x() + halfSize, pt.y() + halfSize ) );
1056 }
1057#endif // 0
1058
1059 //there is a global block for the point layer
1060 QHash< const QgsSymbolLayer *, QString >::const_iterator blockIt = mPointSymbolBlocks.constFind( symbolLayer );
1061 if ( symbolLayer && blockIt != mPointSymbolBlocks.constEnd() )
1062 {
1063 writePointBlockReference( pt, symbolLayer, ctx, layer, angle, blockIt.value(), mPointSymbolBlockAngles.value( symbolLayer ), mPointSymbolBlockSizes.value( symbolLayer ) );
1064 return;
1065 }
1066
1067 //If there is a data defined block for the point layer, check if the feature falls into a data defined category
1068 QHash< const QgsSymbolLayer *, QHash <uint, DataDefinedBlockInfo> >::const_iterator ddBlockIt = mDataDefinedBlockInfo.constFind( symbolLayer );
1069 if ( symbolLayer && ctx.feature() && ddBlockIt != mDataDefinedBlockInfo.constEnd() )
1070 {
1071 const QHash <uint, DataDefinedBlockInfo> &symbolLayerDDBlocks = ddBlockIt.value();
1072
1073 QgsPropertyCollection props = symbolLayer->dataDefinedProperties();
1074
1075 uint ddSymbolHash = dataDefinedSymbolClassHash( *( ctx.feature() ), props );
1076 if ( symbolLayerDDBlocks.contains( ddSymbolHash ) )
1077 {
1078 const DataDefinedBlockInfo &info = symbolLayerDDBlocks[ddSymbolHash];
1079 writePointBlockReference( pt, symbolLayer, ctx, layer, angle, info.blockName, info.angle, info.size );
1080 return;
1081 }
1082 }
1083
1084 //no block has been created for the symbol. Write it directly here
1085 const QgsMarkerSymbolLayer *msl = dynamic_cast< const QgsMarkerSymbolLayer * >( symbolLayer );
1086 if ( msl && symbol )
1087 {
1088 if ( msl->writeDxf( *this, mapUnitScaleFactor( mSymbologyScale, msl->sizeUnit(), mMapUnits, ctx.renderContext().mapToPixel().mapUnitsPerPixel() ), layer, ctx, QPointF( pt.x(), pt.y() ) ) )
1089 {
1090 return;
1091 }
1092 }
1093 writePoint( layer, color, pt ); // write default point symbol
1094}
1095
1096void QgsDxfExport::writePointBlockReference( const QgsPoint &pt, const QgsSymbolLayer *symbolLayer, QgsSymbolRenderContext &ctx, const QString &layer, double angle, const QString &blockName, double blockAngle, double blockSize )
1097{
1098 const double scale = symbolLayer->dxfSize( *this, ctx ) / blockSize;
1099
1100 // insert block reference
1101 writeGroup( 0, u"INSERT"_s );
1102 writeHandle();
1103 writeGroup( 100, u"AcDbEntity"_s );
1104 writeGroup( 100, u"AcDbBlockReference"_s );
1105 writeGroup( 8, layer );
1106 writeGroup( 2, blockName ); // Block name
1107 writeGroup( 50, blockAngle - angle );
1108 if ( std::isfinite( scale ) && scale != 1.0 )
1109 {
1110 writeGroup( 41, scale );
1111 writeGroup( 42, scale );
1112 }
1113 writeGroup( 0, pt ); // Insertion point (in OCS)
1114}
1115
1116uint QgsDxfExport::dataDefinedSymbolClassHash( const QgsFeature &fet, const QgsPropertyCollection &prop )
1117{
1118 uint hashValue = 0;
1119
1120 QgsPropertyCollection dxfProp = prop;
1121 dxfProp.setProperty( QgsSymbolLayer::Property::Size, QgsProperty() );
1122 dxfProp.setProperty( QgsSymbolLayer::Property::Angle, QgsProperty() );
1123 QList< QString > fields = dxfProp.referencedFields().values();
1124 std::sort( fields.begin(), fields.end() );
1125 int i = 0;
1126 for ( const auto &field : std::as_const( fields ) ) //convert set to list to have a well defined order
1127 {
1128 QVariant attValue = fet.attribute( field );
1129 if ( i == 0 )
1130 {
1131 hashValue = qHash( attValue );
1132 }
1133 else
1134 {
1135 hashValue = hashValue ^ qHash( attValue );
1136 }
1137 ++i;
1138 }
1139
1140 return hashValue;
1141}
1142
1143void QgsDxfExport::writePolyline( const QgsPointSequence &line, const QString &layer, const QString &lineStyleName, const QColor &color, double width )
1144{
1145 int n = line.size();
1146 if ( n == 0 )
1147 {
1148 QgsDebugError( u"writePolyline: empty line layer=%1 lineStyleName=%2"_s.arg( layer, lineStyleName ) );
1149 return;
1150 }
1151
1152 if ( n < 2 )
1153 {
1154 QgsDebugError( u"writePolyline: line too short layer=%1 lineStyleName=%2"_s.arg( layer, lineStyleName ) );
1155 return;
1156 }
1157
1158 if ( mForce2d || !line.at( 0 ).is3D() )
1159 {
1160 bool polygon = line[0] == line[ line.size() - 1 ];
1161 if ( polygon )
1162 --n;
1163
1164 writeGroup( 0, u"LWPOLYLINE"_s );
1165 writeHandle();
1166 writeGroup( 8, layer );
1167 writeGroup( 100, u"AcDbEntity"_s );
1168 writeGroup( 100, u"AcDbPolyline"_s );
1169 writeGroup( 6, lineStyleName );
1170 writeGroup( color );
1171
1172 writeGroup( 90, n );
1173 writeGroup( 70, polygon ? 1 : 0 );
1174 writeGroup( 43, width );
1175
1176 for ( int i = 0; i < n; i++ )
1177 writeGroup( 0, line[i] );
1178 }
1179 else
1180 {
1181 writeGroup( 0, u"POLYLINE"_s );
1182 int plHandle = writeHandle();
1183 writeGroup( 330, mBlockHandle );
1184 writeGroup( 100, u"AcDbEntity"_s );
1185 writeGroup( 8, layer );
1186 writeGroup( 6, lineStyleName );
1187 writeGroup( color );
1188 writeGroup( 100, u"AcDb3dPolyline"_s );
1189 writeGroup( 0, QgsPoint( Qgis::WkbType::PointZ, 0.0, 0.0, 0.0 ) );
1190 writeGroup( 70, 8 );
1191
1192 for ( int i = 0; i < n; i++ )
1193 {
1194 writeGroup( 0, u"VERTEX"_s );
1195 writeHandle();
1196 writeGroup( 330, plHandle );
1197 writeGroup( 100, u"AcDbEntity"_s );
1198 writeGroup( 8, layer );
1199 writeGroup( color );
1200 writeGroup( 100, u"AcDbVertex"_s );
1201 writeGroup( 100, u"AcDb3dPolylineVertex"_s );
1202 writeGroup( 0, line[i] );
1203 writeGroup( 70, 32 );
1204 }
1205
1206 writeGroup( 0, u"SEQEND"_s );
1207 writeHandle();
1208 writeGroup( 330, plHandle );
1209 writeGroup( 100, u"AcDbEntity"_s );
1210 writeGroup( 8, layer );
1211 writeGroup( color );
1212 }
1213}
1214
1215void QgsDxfExport::appendCurve( const QgsCurve &c, QVector<QgsPoint> &points, QVector<double> &bulges )
1216{
1217 switch ( QgsWkbTypes::flatType( c.wkbType() ) )
1218 {
1220 appendLineString( *qgis::down_cast<const QgsLineString *>( &c ), points, bulges );
1221 break;
1222
1224 appendCircularString( *qgis::down_cast<const QgsCircularString *>( &c ), points, bulges );
1225 break;
1226
1228 appendCompoundCurve( *qgis::down_cast<const QgsCompoundCurve *>( &c ), points, bulges );
1229 break;
1230
1231 default:
1232 QgsDebugError( u"Unexpected curve type %1"_s.arg( c.wktTypeStr() ) );
1233 break;
1234 }
1235}
1236
1237void QgsDxfExport::appendLineString( const QgsLineString &ls, QVector<QgsPoint> &points, QVector<double> &bulges )
1238{
1239 for ( int i = 0; i < ls.numPoints(); i++ )
1240 {
1241 const QgsPoint &p = ls.pointN( i );
1242 if ( !points.isEmpty() && points.last() == p )
1243 continue;
1244
1245 points << p;
1246 bulges << 0.0;
1247 }
1248}
1249
1250void QgsDxfExport::appendCircularString( const QgsCircularString &cs, QVector<QgsPoint> &points, QVector<double> &bulges )
1251{
1252 for ( int i = 0; i < cs.numPoints() - 2; i += 2 )
1253 {
1254 const QgsPoint &p1 = cs.pointN( i );
1255 const QgsPoint &p2 = cs.pointN( i + 1 );
1256 const QgsPoint &p3 = cs.pointN( i + 2 );
1257
1258 if ( points.isEmpty() || points.last() != p1 )
1259 points << p1;
1260 else if ( !bulges.isEmpty() )
1261 bulges.removeLast();
1262
1263 double a = ( M_PI - ( p1 - p2 ).angle() + ( p3 - p2 ).angle() ) / 2.0;
1264 bulges << sin( a ) / cos( a );
1265
1266 points << p3;
1267 bulges << 0.0;
1268 }
1269}
1270
1271void QgsDxfExport::appendCompoundCurve( const QgsCompoundCurve &cc, QVector<QgsPoint> &points, QVector<double> &bulges )
1272{
1273 for ( int i = 0; i < cc.nCurves(); i++ )
1274 {
1275 const QgsCurve *c = cc.curveAt( i );
1276 Q_ASSERT( c );
1277 appendCurve( *c, points, bulges );
1278 }
1279}
1280
1281void QgsDxfExport::writePolyline( const QgsCurve &curve, const QString &layer, const QString &lineStyleName, const QColor &color, double width )
1282{
1283 int n = curve.numPoints();
1284 if ( n == 0 )
1285 {
1286 QgsDebugError( u"writePolyline: empty line layer=%1 lineStyleName=%2"_s.arg( layer, lineStyleName ) );
1287 return;
1288 }
1289
1290 if ( n < 2 )
1291 {
1292 QgsDebugError( u"writePolyline: line too short layer=%1 lineStyleName=%2"_s.arg( layer, lineStyleName ) );
1293 return;
1294 }
1295
1296 QVector<QgsPoint> points;
1297 QVector<double> bulges;
1298 appendCurve( curve, points, bulges );
1299
1300 if ( mForce2d || !curve.is3D() )
1301 {
1302 writeGroup( 0, u"LWPOLYLINE"_s );
1303 writeHandle();
1304 writeGroup( 8, layer );
1305 writeGroup( 100, u"AcDbEntity"_s );
1306 writeGroup( 100, u"AcDbPolyline"_s );
1307 writeGroup( 6, lineStyleName );
1308 writeGroup( color );
1309
1310 writeGroup( 90, points.size() );
1311 QgsDxfExport::DxfPolylineFlags polylineFlags;
1312 if ( curve.isClosed() )
1313 polylineFlags.setFlag( QgsDxfExport::DxfPolylineFlag::Closed );
1314 if ( curve.hasCurvedSegments() )
1315 polylineFlags.setFlag( QgsDxfExport::DxfPolylineFlag::Curve );
1316
1317 // Might need to conditional once this feature is implemented
1318 // https://github.com/qgis/QGIS/issues/32468
1319 polylineFlags.setFlag( QgsDxfExport::DxfPolylineFlag::ContinuousPattern );
1320
1321 writeGroup( 70, static_cast<int>( polylineFlags ) );
1322 writeGroup( 43, width );
1323
1324 for ( int i = 0; i < points.size(); i++ )
1325 {
1326 writeGroup( 0, points[i] );
1327 if ( bulges[i] != 0.0 )
1328 writeGroup( 42, bulges[i] );
1329 }
1330 }
1331 else
1332 {
1333 writeGroup( 0, u"POLYLINE"_s );
1334 int plHandle = writeHandle();
1335 writeGroup( 330, mBlockHandle );
1336 writeGroup( 100, u"AcDbEntity"_s );
1337 writeGroup( 8, layer );
1338 writeGroup( 6, lineStyleName );
1339 writeGroup( color );
1340 writeGroup( 100, u"AcDb3dPolyline"_s );
1341 writeGroup( 0, QgsPoint( Qgis::WkbType::PointZ, 0.0, 0.0, 0.0 ) );
1342 writeGroup( 70, 8 );
1343
1344 for ( int i = 0; i < points.size(); i++ )
1345 {
1346 writeGroup( 0, u"VERTEX"_s );
1347 writeHandle();
1348 writeGroup( 330, plHandle );
1349 writeGroup( 100, u"AcDbEntity"_s );
1350 writeGroup( 8, layer );
1351 writeGroup( color );
1352 writeGroup( 100, u"AcDbVertex"_s );
1353 writeGroup( 100, u"AcDb3dPolylineVertex"_s );
1354 writeGroup( 0, points[i] );
1355 if ( bulges[i] != 0.0 )
1356 writeGroup( 42, bulges[i] );
1357 writeGroup( 70, 32 );
1358 }
1359
1360 writeGroup( 0, u"SEQEND"_s );
1361 writeHandle();
1362 writeGroup( 330, plHandle );
1363 writeGroup( 100, u"AcDbEntity"_s );
1364 writeGroup( 8, layer );
1365 writeGroup( color );
1366 }
1367}
1368
1369void QgsDxfExport::writePolygon( const QgsRingSequence &polygon, const QString &layer, const QString &hatchPattern, const QColor &color )
1370{
1371 writeGroup( 0, u"HATCH"_s ); // Entity type
1372 writeHandle();
1373 writeGroup( 330, mBlockHandle );
1374 writeGroup( 100, u"AcDbEntity"_s );
1375 writeGroup( 8, layer ); // Layer name
1376 writeGroup( color ); // Color
1377 writeGroup( 100, u"AcDbHatch"_s );
1378
1379 writeGroup( 0, QgsPoint( Qgis::WkbType::PointZ, 0.0, 0.0, 0.0 ) ); // Elevation point (in OCS)
1380 writeGroup( 200, QgsPoint( Qgis::WkbType::PointZ, 0.0, 0.0, 1.0 ) );
1381
1382 writeGroup( 2, hatchPattern ); // Hatch pattern name
1383 writeGroup( 70, hatchPattern == "SOLID"_L1 ); // Solid fill flag (solid fill = 1; pattern fill = 0)
1384 writeGroup( 71, 0 ); // Associativity flag (associative = 1; non-associative = 0)
1385
1386 writeGroup( 91, polygon.size() ); // Number of boundary paths (loops)
1387 for ( int i = 0; i < polygon.size(); ++i )
1388 {
1389 writeGroup( 92, 2 ); // Boundary path type flag (bit coded): 0 = Default; 1 = External; 2 = Polyline 4 = Derived; 8 = Textbox; 16 = Outermost
1390 writeGroup( 72, 0 ); // Has bulge flag
1391 writeGroup( 73, 1 ); // Is closed flag
1392 writeGroup( 93, polygon[i].size() ); // Number of edges in this boundary path (only if boundary is not a polyline)
1393
1394 for ( int j = 0; j < polygon[i].size(); ++j )
1395 {
1396 writeGroup( 0, polygon[i][j] ); // Vertex location (in OCS)
1397 }
1398
1399 writeGroup( 97, 0 ); // Number of source boundary objects
1400 }
1401
1402 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)
1403 writeGroup( 76, 1 ); // Hatch pattern type: 0 = User-defined; 1 = Predefined; 2 = Custom
1404
1405 writeGroup( 98, 0 ); // Number of seed points
1406}
1407
1408void QgsDxfExport::writePolygon( const QgsCurvePolygon &polygon, const QString &layer, const QString &hatchPattern, const QColor &color )
1409{
1410 writeGroup( 0, u"HATCH"_s ); // Entity type
1411 writeHandle();
1412 writeGroup( 330, mBlockHandle );
1413 writeGroup( 100, u"AcDbEntity"_s );
1414 writeGroup( 8, layer ); // Layer name
1415 writeGroup( color ); // Color
1416 writeGroup( 100, u"AcDbHatch"_s );
1417
1418 writeGroup( 0, QgsPoint( Qgis::WkbType::PointZ, 0.0, 0.0, 0.0 ) ); // Elevation point (in OCS)
1419 writeGroup( 200, QgsPoint( Qgis::WkbType::PointZ, 0.0, 0.0, 1.0 ) );
1420
1421 writeGroup( 2, hatchPattern ); // Hatch pattern name
1422 writeGroup( 70, hatchPattern == "SOLID"_L1 ); // Solid fill flag (solid fill = 1; pattern fill = 0)
1423 writeGroup( 71, 0 ); // Associativity flag (associative = 1; non-associative = 0)
1424
1425 QVector<QVector<QgsPoint>> points;
1426 QVector<QVector<double>> bulges;
1427
1428 const int ringCount = polygon.numInteriorRings();
1429 points.reserve( ringCount + 1 );
1430 bulges.reserve( ringCount + 1 );
1431
1432 points << QVector<QgsPoint>();
1433 bulges << QVector<double>();
1434 appendCurve( *polygon.exteriorRing(), points.last(), bulges.last() );
1435
1436 for ( int i = 0; i < ringCount; i++ )
1437 {
1438 points << QVector<QgsPoint>();
1439 bulges << QVector<double>();
1440 appendCurve( *polygon.interiorRing( i ), points.last(), bulges.last() );
1441 }
1442
1443 bool hasBulges = false;
1444 for ( int i = 0; i < points.size() && !hasBulges; ++i )
1445 for ( int j = 0; j < points[i].size() && !hasBulges; ++j )
1446 hasBulges = bulges[i][j] != 0.0;
1447
1448 writeGroup( 91, points.size() ); // Number of boundary paths (loops)
1449
1450 for ( int i = 0; i < points.size(); ++i )
1451 {
1452 writeGroup( 92, 2 ); // Boundary path type flag (bit coded): 0 = Default; 1 = External; 2 = Polyline 4 = Derived; 8 = Textbox; 16 = Outermost
1453 writeGroup( 72, hasBulges ? 1 : 0 ); // Has bulge flag
1454 writeGroup( 73, 1 ); // Is closed flag
1455 writeGroup( 93, points[i].size() ); // Number of edges in this boundary path (only if boundary is not a polyline)
1456
1457 for ( int j = 0; j < points[i].size(); ++j )
1458 {
1459 writeGroup( 0, points[i][j] ); // Vertex location (in OCS)
1460 if ( hasBulges )
1461 writeGroup( 42, bulges[i][j] );
1462 }
1463
1464 writeGroup( 97, 0 ); // Number of source boundary objects
1465 }
1466
1467 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)
1468 writeGroup( 76, 1 ); // Hatch pattern type: 0 = User-defined; 1 = Predefined; 2 = Custom
1469
1470 writeGroup( 98, 0 ); // Number of seed points
1471}
1472
1473void QgsDxfExport::writeLine( const QgsPoint &pt1, const QgsPoint &pt2, const QString &layer, const QString &lineStyleName, const QColor &color, double width )
1474{
1475 writePolyline( QgsPointSequence() << pt1 << pt2, layer, lineStyleName, color, width );
1476}
1477
1478void QgsDxfExport::writeText( const QString &layer, const QString &text, pal::LabelPosition *label, const QgsPalLayerSettings &layerSettings, const QgsExpressionContext &expressionContext )
1479{
1480
1481 double lblX = label->getX();
1482 double lblY = label->getY();
1483
1484 QgsLabelFeature *labelFeature = label->getFeaturePart()->feature();
1485
1488
1489 const QgsPropertyCollection &props = layerSettings.dataDefinedProperties();
1490
1491 if ( layerSettings.placement == Qgis::LabelPlacement::OverPoint )
1492 {
1493 lblX = labelFeature->anchorPosition().x();
1494 lblY = labelFeature->anchorPosition().y();
1495
1496 Qgis::LabelQuadrantPosition offsetQuad = layerSettings.pointSettings().quadrant();
1497
1499 {
1500 const QVariant exprVal = props.value( QgsPalLayerSettings::Property::OffsetQuad, expressionContext );
1501 if ( !QgsVariantUtils::isNull( exprVal ) )
1502 {
1503 offsetQuad = static_cast<Qgis::LabelQuadrantPosition>( exprVal.toInt() );
1504 }
1505 }
1506
1507 switch ( offsetQuad )
1508 {
1510 hali = HAlign::HRight;
1511 vali = VAlign::VBottom;
1512 break;
1514 hali = HAlign::HCenter;
1515 vali = VAlign::VBottom;
1516 break;
1518 hali = HAlign::HLeft;
1519 vali = VAlign::VBottom;
1520 break;
1522 hali = HAlign::HRight;
1523 vali = VAlign::VMiddle;
1524 break;
1526 hali = HAlign::HCenter;
1527 vali = VAlign::VMiddle;
1528 break;
1530 hali = HAlign::HLeft;
1531 vali = VAlign::VMiddle;
1532 break;
1534 hali = HAlign::HRight;
1535 vali = VAlign::VTop;
1536 break;
1538 hali = HAlign::HCenter;
1539 vali = VAlign::VTop;
1540 break;
1542 hali = HAlign::HLeft;
1543 vali = VAlign::VTop;
1544 break;
1545 }
1546 }
1547
1549 {
1550 lblX = labelFeature->anchorPosition().x();
1551 lblY = labelFeature->anchorPosition().y();
1552
1553 hali = HAlign::HLeft;
1554 QVariant exprVal = props.value( QgsPalLayerSettings::Property::Hali, expressionContext );
1555 if ( !QgsVariantUtils::isNull( exprVal ) )
1556 {
1557 const QString haliString = exprVal.toString();
1558 if ( haliString.compare( "Center"_L1, Qt::CaseInsensitive ) == 0 )
1559 {
1560 hali = HAlign::HCenter;
1561 }
1562 else if ( haliString.compare( "Right"_L1, Qt::CaseInsensitive ) == 0 )
1563 {
1564 hali = HAlign::HRight;
1565 }
1566 }
1567 }
1568
1569 //vertical alignment
1571 {
1572 vali = VAlign::VBottom;
1573 QVariant exprVal = props.value( QgsPalLayerSettings::Property::Vali, expressionContext );
1574 if ( !QgsVariantUtils::isNull( exprVal ) )
1575 {
1576 const QString valiString = exprVal.toString();
1577 if ( valiString.compare( "Bottom"_L1, Qt::CaseInsensitive ) != 0 )
1578 {
1579 if ( valiString.compare( "Base"_L1, Qt::CaseInsensitive ) == 0 )
1580 {
1581 vali = VAlign::VBaseLine;
1582 }
1583 else if ( valiString.compare( "Half"_L1, Qt::CaseInsensitive ) == 0 )
1584 {
1585 vali = VAlign::VMiddle;
1586 }
1587 else //'Cap' or 'Top'
1588 {
1589 vali = VAlign::VTop;
1590 }
1591 }
1592 }
1593 }
1594
1595 writeText( layer, text, QgsPoint( lblX, lblY ), label->getHeight(), label->getAlpha() * 180.0 / M_PI, layerSettings.format().color(), hali, vali );
1596}
1597
1598void QgsDxfExport::writePoint( const QString &layer, const QColor &color, const QgsPoint &pt )
1599{
1600 writeGroup( 0, u"POINT"_s );
1601 writeHandle();
1602 writeGroup( 100, u"AcDbEntity"_s );
1603 writeGroup( 100, u"AcDbPoint"_s );
1604 writeGroup( 8, layer );
1605 writeGroup( color );
1606 writeGroup( 0, pt );
1607}
1608
1609void QgsDxfExport::writeFilledCircle( const QString &layer, const QColor &color, const QgsPoint &pt, double radius )
1610{
1611 writeGroup( 0, u"HATCH"_s ); // Entity type
1612 writeHandle();
1613 writeGroup( 330, mBlockHandle );
1614 writeGroup( 100, u"AcDbEntity"_s );
1615 writeGroup( 8, layer ); // Layer name
1616 writeGroup( color ); // Color (0 by block, 256 by layer)
1617 writeGroup( 100, u"AcDbHatch"_s );
1618
1619 writeGroup( 0, QgsPoint( Qgis::WkbType::PointZ, 0.0, 0.0, 0.0 ) ); // Elevation point (in OCS)
1620 writeGroup( 200, QgsPoint( Qgis::WkbType::PointZ, 0.0, 0.0, 1.0 ) );
1621
1622 writeGroup( 2, u"SOLID"_s ); // Hatch pattern name
1623 writeGroup( 70, 1 ); // Solid fill flag (solid fill = 1; pattern fill = 0)
1624 writeGroup( 71, 0 ); // Associativity flag (associative = 1; non-associative = 0)
1625
1626 writeGroup( 91, 1 ); // Number of boundary paths (loops)
1627
1628 writeGroup( 92, 3 ); // Boundary path type flag (bit coded): 0 = Default; 1 = External; 2 = Polyline 4 = Derived; 8 = Textbox; 16 = Outermost
1629 writeGroup( 72, 1 );
1630 writeGroup( 73, 1 ); // Is closed flag
1631 writeGroup( 93, 2 ); // Number of polyline vertices
1632
1633 writeGroup( 0, QgsPoint( Qgis::WkbType::Point, pt.x() - radius, pt.y() ) );
1634 writeGroup( 42, 1.0 );
1635
1636 writeGroup( 0, QgsPoint( Qgis::WkbType::Point, pt.x() + radius, pt.y() ) );
1637 writeGroup( 42, 1.0 );
1638
1639 writeGroup( 97, 0 ); // Number of source boundary objects
1640
1641 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)
1642 writeGroup( 76, 1 ); // Hatch pattern type: 0 = User-defined; 1 = Predefined; 2 = Custom
1643 writeGroup( 98, 0 ); // Number of seed points
1644}
1645
1646void QgsDxfExport::writeCircle( const QString &layer, const QColor &color, const QgsPoint &pt, double radius, const QString &lineStyleName, double width )
1647{
1648 writeGroup( 0, u"LWPOLYLINE"_s );
1649 writeHandle();
1650 writeGroup( 330, mBlockHandle );
1651 writeGroup( 8, layer );
1652 writeGroup( 100, u"AcDbEntity"_s );
1653 writeGroup( 100, u"AcDbPolyline"_s );
1654 writeGroup( 6, lineStyleName );
1655 writeGroup( color );
1656
1657 writeGroup( 90, 2 );
1658
1659 writeGroup( 70, 1 );
1660 writeGroup( 43, width );
1661
1662 writeGroup( 0, QgsPoint( pt.x() - radius, pt.y() ) );
1663 writeGroup( 42, 1.0 );
1664 writeGroup( 0, QgsPoint( pt.x() + radius, pt.y() ) );
1665 writeGroup( 42, 1.0 );
1666}
1667
1668void QgsDxfExport::writeText( const QString &layer, const QString &text, const QgsPoint &pt, double size, double angle, const QColor &color, HAlign hali, VAlign vali )
1669{
1670 writeGroup( 0, u"TEXT"_s );
1671 writeHandle();
1672 writeGroup( 100, u"AcDbEntity"_s );
1673 // writeGroup( 6, "Continuous" ); // Line style
1674 // writeGroup( 370, 18 ); // Line weight
1675 writeGroup( 100, u"AcDbText"_s );
1676 writeGroup( 8, layer );
1677 writeGroup( color );
1678 writeGroup( 0, pt );
1679 if ( hali != HAlign::Undefined || vali != VAlign::Undefined )
1680 writeGroup( 1, pt ); // Second alignment point
1681 writeGroup( 40, size );
1682 writeGroup( 1, text );
1683 writeGroup( 50, fmod( angle, 360 ) );
1684 if ( hali != HAlign::Undefined )
1685 writeGroup( 72, static_cast<int>( hali ) );
1686 writeGroup( 7, u"STANDARD"_s ); // so far only support for standard font
1687 writeGroup( 100, u"AcDbText"_s );
1688 if ( vali != VAlign::Undefined )
1689 {
1690 writeGroup( 73, static_cast<int>( vali ) );
1691 }
1692}
1693
1694void QgsDxfExport::writeMText( const QString &layer, const QString &text, const QgsPoint &pt, double width, double angle, const QColor &color )
1695{
1696#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1697 if ( !mTextStream.codec()->canEncode( text ) )
1698 {
1699 // TODO return error
1700 QgsDebugError( u"could not encode:%1"_s.arg( text ) );
1701 return;
1702 }
1703#endif
1704
1705 writeGroup( 0, u"MTEXT"_s );
1706 writeHandle();
1707 writeGroup( 100, u"AcDbEntity"_s );
1708 writeGroup( 100, u"AcDbMText"_s );
1709 writeGroup( 8, layer );
1710 writeGroup( color );
1711
1712 writeGroup( 0, pt );
1713
1714 QString t( text );
1715 while ( t.length() > 250 )
1716 {
1717 writeGroup( 3, t.left( 250 ) );
1718 t = t.mid( 250 );
1719 }
1720 writeGroup( 1, t );
1721
1722 writeGroup( 50, angle ); // Rotation angle in radians
1723 writeGroup( 41, width * 1.1 ); // Reference rectangle width
1724
1725 // Attachment point:
1726 // 1 2 3
1727 // 4 5 6
1728 // 7 8 9
1729 writeGroup( 71, 7 );
1730
1731 writeGroup( 7, u"STANDARD"_s ); // so far only support for standard font
1732}
1733
1734void QgsDxfExport::addFeature( QgsSymbolRenderContext &ctx, const QgsCoordinateTransform &ct, const QString &layer, const QgsSymbolLayer *symbolLayer, const QgsSymbol *symbol )
1735{
1736 const QgsFeature *fet = ctx.feature();
1737 if ( !fet )
1738 return;
1739
1740 if ( !fet->hasGeometry() )
1741 return;
1742
1743 QgsGeometry geom( fet->geometry() );
1744 if ( ct.isValid() )
1745 {
1746 geom.transform( ct );
1747 }
1748
1749 Qgis::WkbType geometryType = geom.wkbType();
1750
1751 QColor penColor;
1752 QColor brushColor;
1753 if ( mSymbologyExport != Qgis::FeatureSymbologyExport::NoSymbology && symbolLayer )
1754 {
1755 penColor = colorFromSymbolLayer( symbolLayer, ctx );
1756 brushColor = symbolLayer->dxfBrushColor( ctx );
1757 }
1758
1759 Qt::PenStyle penStyle( Qt::SolidLine );
1760 Qt::BrushStyle brushStyle( Qt::NoBrush );
1761 double width = -1;
1762 double offset = 0.0;
1763 double angle = 0.0;
1764 if ( mSymbologyExport != Qgis::FeatureSymbologyExport::NoSymbology && symbolLayer )
1765 {
1766 width = symbolLayer->dxfWidth( *this, ctx );
1767 offset = symbolLayer->dxfOffset( *this, ctx );
1768 angle = symbolLayer->dxfAngle( ctx );
1769 penStyle = symbolLayer->dxfPenStyle();
1770 brushStyle = symbolLayer->dxfBrushStyle();
1771
1772 if ( qgsDoubleNear( offset, 0.0 ) )
1773 offset = 0.0;
1774 }
1775
1776 if ( mFlags & FlagHairlineWidthExport )
1777 {
1778 width = 0;
1779 }
1780
1781 QString lineStyleName = u"CONTINUOUS"_s;
1782 if ( mSymbologyExport != Qgis::FeatureSymbologyExport::NoSymbology )
1783 {
1784 lineStyleName = lineStyleFromSymbolLayer( symbolLayer );
1785 }
1786
1787 // single point
1788 if ( QgsWkbTypes::flatType( geometryType ) == Qgis::WkbType::Point )
1789 {
1790 writePoint( geom.constGet()->coordinateSequence().at( 0 ).at( 0 ).at( 0 ), layer, penColor, ctx, symbolLayer, symbol, angle );
1791 return;
1792 }
1793
1794 if ( QgsWkbTypes::flatType( geometryType ) == Qgis::WkbType::MultiPoint )
1795 {
1796 const QgsCoordinateSequence &cs = geom.constGet()->coordinateSequence();
1797 for ( int i = 0; i < cs.size(); i++ )
1798 {
1799 writePoint( cs.at( i ).at( 0 ).at( 0 ), layer, penColor, ctx, symbolLayer, symbol, angle );
1800 }
1801 return;
1802 }
1803
1804 if ( penStyle != Qt::NoPen )
1805 {
1806 const QgsAbstractGeometry *sourceGeom = geom.constGet();
1807 std::unique_ptr< QgsAbstractGeometry > tempGeom;
1808
1809 switch ( QgsWkbTypes::flatType( geometryType ) )
1810 {
1816 {
1817 if ( !qgsDoubleNear( offset, 0.0 ) )
1818 {
1819 QgsGeos geos( sourceGeom );
1820 tempGeom.reset( geos.offsetCurve( offset, 0, Qgis::JoinStyle::Miter, 2.0 ) ); //#spellok
1821 if ( tempGeom )
1822 sourceGeom = tempGeom.get();
1823 else
1824 sourceGeom = geom.constGet();
1825 }
1826
1827 const QgsCurve *curve = dynamic_cast<const QgsCurve *>( sourceGeom );
1828 if ( curve )
1829 {
1830 writePolyline( *curve, layer, lineStyleName, penColor, width );
1831 }
1832 else
1833 {
1834 const QgsGeometryCollection *gc = dynamic_cast<const QgsGeometryCollection *>( sourceGeom );
1835 Q_ASSERT( gc );
1836 if ( gc )
1837 {
1838 for ( int i = 0; i < gc->numGeometries(); i++ )
1839 {
1840 const QgsCurve *curve = dynamic_cast<const QgsCurve *>( gc->geometryN( i ) );
1841 Q_ASSERT( curve );
1842 writePolyline( *curve, layer, lineStyleName, penColor, width );
1843 }
1844 }
1845 }
1846 break;
1847 }
1848
1853 {
1854 if ( !qgsDoubleNear( offset, 0.0 ) )
1855 {
1856 QgsGeos geos( sourceGeom );
1857 tempGeom.reset( geos.buffer( offset, 0, Qgis::EndCapStyle::Flat, Qgis::JoinStyle::Miter, 2.0 ) ); //#spellok
1858 if ( tempGeom )
1859 sourceGeom = tempGeom.get();
1860 else
1861 sourceGeom = geom.constGet();
1862 }
1863
1864 const QgsCurvePolygon *polygon = dynamic_cast<const QgsCurvePolygon *>( sourceGeom );
1865 if ( polygon )
1866 {
1867 writePolyline( *polygon->exteriorRing(), layer, lineStyleName, penColor, width );
1868 for ( int i = 0; i < polygon->numInteriorRings(); i++ )
1869 writePolyline( *polygon->interiorRing( i ), layer, lineStyleName, penColor, width );
1870 }
1871 else
1872 {
1873 const QgsGeometryCollection *gc = dynamic_cast<const QgsGeometryCollection *>( sourceGeom );
1874 Q_ASSERT( gc );
1875 if ( gc )
1876 {
1877 for ( int i = 0; i < gc->numGeometries(); i++ )
1878 {
1879 const QgsCurvePolygon *polygon = dynamic_cast<const QgsCurvePolygon *>( gc->geometryN( i ) );
1880 Q_ASSERT( polygon );
1881
1882 writePolyline( *polygon->exteriorRing(), layer, lineStyleName, penColor, width );
1883 for ( int j = 0; j < polygon->numInteriorRings(); j++ )
1884 writePolyline( *polygon->interiorRing( j ), layer, lineStyleName, penColor, width );
1885 }
1886 }
1887 }
1888
1889 break;
1890 }
1891
1892 default:
1893 break;
1894 }
1895
1896 }
1897
1898 if ( brushStyle != Qt::NoBrush )
1899 {
1900 const QgsAbstractGeometry *sourceGeom = geom.constGet();
1901
1902 switch ( QgsWkbTypes::flatType( geometryType ) )
1903 {
1906 {
1907 const QgsCurvePolygon *polygon = dynamic_cast<const QgsCurvePolygon *>( sourceGeom );
1908 Q_ASSERT( polygon );
1909 writePolygon( *polygon, layer, u"SOLID"_s, brushColor );
1910 break;
1911 }
1912
1915 {
1916 const QgsGeometryCollection *gc = dynamic_cast<const QgsGeometryCollection *>( sourceGeom );
1917 Q_ASSERT( gc );
1918
1919 for ( int i = 0; i < gc->numGeometries(); i++ )
1920 {
1921 const QgsCurvePolygon *polygon = dynamic_cast<const QgsCurvePolygon *>( gc->geometryN( i ) );
1922 Q_ASSERT( polygon );
1923 writePolygon( *polygon, layer, u"SOLID"_s, brushColor );
1924 }
1925 break;
1926 }
1927
1928 default:
1929 break;
1930
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 if ( symbolUnits == Qgis::RenderUnit::MapUnits )
2016 {
2017 return 1.0;
2018 }
2019 else if ( symbolUnits == Qgis::RenderUnit::Millimeters )
2020 {
2022 }
2023 else if ( symbolUnits == Qgis::RenderUnit::Pixels )
2024 {
2025 return mapUnitsPerPixel;
2026 }
2027 return 1.0;
2028}
2029
2030void QgsDxfExport::clipValueToMapUnitScale( double &value, const QgsMapUnitScale &scale, double pixelToMMFactor ) const
2031{
2032 if ( !scale.minSizeMMEnabled && !scale.maxSizeMMEnabled )
2033 {
2034 return;
2035 }
2036
2037 double mapUnitsPerPixel = mMapSettings.mapToPixel().mapUnitsPerPixel();
2038
2039 double minSizeMU = std::numeric_limits<double>::lowest();
2040 if ( scale.minSizeMMEnabled )
2041 {
2042 minSizeMU = scale.minSizeMM * pixelToMMFactor * mapUnitsPerPixel;
2043 }
2044 if ( !qgsDoubleNear( scale.minScale, 0.0 ) )
2045 {
2046 minSizeMU = std::max( minSizeMU, value );
2047 }
2048 value = std::max( value, minSizeMU );
2049
2050 double maxSizeMU = std::numeric_limits<double>::max();
2051 if ( scale.maxSizeMMEnabled )
2052 {
2053 maxSizeMU = scale.maxSizeMM * pixelToMMFactor * mapUnitsPerPixel;
2054 }
2055 if ( !qgsDoubleNear( scale.maxScale, 0.0 ) )
2056 {
2057 maxSizeMU = std::min( maxSizeMU, value );
2058 }
2059 value = std::min( value, maxSizeMU );
2060}
2061
2062QList< QPair< QgsSymbolLayer *, QgsSymbol * > > QgsDxfExport::symbolLayers( QgsRenderContext &context )
2063{
2064 QList< QPair< QgsSymbolLayer *, QgsSymbol * > > symbolLayers;
2065
2066 for ( DxfLayerJob *job : std::as_const( mJobs ) )
2067 {
2068 const QgsSymbolList symbols = job->renderer->symbols( context );
2069
2070 for ( QgsSymbol *symbol : symbols )
2071 {
2072 int maxSymbolLayers = symbol->symbolLayerCount();
2073 if ( mSymbologyExport != Qgis::FeatureSymbologyExport::PerSymbolLayer )
2074 {
2075 maxSymbolLayers = 1;
2076 }
2077 for ( int i = 0; i < maxSymbolLayers; ++i )
2078 {
2079 symbolLayers.append( qMakePair( symbol->symbolLayer( i ), symbol ) );
2080 }
2081 }
2082 }
2083
2084 return symbolLayers;
2085}
2086
2087void QgsDxfExport::writeDefaultLinetypes()
2088{
2089 // continuous (Qt solid line)
2090 for ( const QString &ltype : { u"ByLayer"_s, u"ByBlock"_s, u"CONTINUOUS"_s } )
2091 {
2092 writeGroup( 0, u"LTYPE"_s );
2093 writeHandle();
2094 writeGroup( 100, u"AcDbSymbolTableRecord"_s );
2095 writeGroup( 100, u"AcDbLinetypeTableRecord"_s );
2096 writeGroup( 2, ltype );
2097 writeGroup( 70, 64 );
2098 writeGroup( 3, u"Defaultstyle"_s );
2099 writeGroup( 72, 65 );
2100 writeGroup( 73, 0 );
2101 writeGroup( 40, 0.0 );
2102 }
2103
2104 double das = dashSize();
2105 double dss = dashSeparatorSize();
2106 double dos = dotSize();
2107
2108 QVector<qreal> dashVector( 2 );
2109 dashVector[0] = das;
2110 dashVector[1] = dss;
2111 writeLinetype( u"DASH"_s, dashVector, Qgis::RenderUnit::MapUnits );
2112
2113 QVector<qreal> dotVector( 2 );
2114 dotVector[0] = dos;
2115 dotVector[1] = dss;
2116 writeLinetype( u"DOT"_s, dotVector, Qgis::RenderUnit::MapUnits );
2117
2118 QVector<qreal> dashDotVector( 4 );
2119 dashDotVector[0] = das;
2120 dashDotVector[1] = dss;
2121 dashDotVector[2] = dos;
2122 dashDotVector[3] = dss;
2123 writeLinetype( u"DASHDOT"_s, dashDotVector, Qgis::RenderUnit::MapUnits );
2124
2125 QVector<qreal> dashDotDotVector( 6 );
2126 dashDotDotVector[0] = das;
2127 dashDotDotVector[1] = dss;
2128 dashDotDotVector[2] = dos;
2129 dashDotDotVector[3] = dss;
2130 dashDotDotVector[4] = dos;
2131 dashDotDotVector[5] = dss;
2132 writeLinetype( u"DASHDOTDOT"_s, dashDotDotVector, Qgis::RenderUnit::MapUnits );
2133}
2134
2135void QgsDxfExport::writeSymbolLayerLinetype( const QgsSymbolLayer *symbolLayer )
2136{
2137 if ( !symbolLayer )
2138 {
2139 return;
2140 }
2141
2142 Qgis::RenderUnit unit;
2143 QVector<qreal> customLinestyle = symbolLayer->dxfCustomDashPattern( unit );
2144 if ( !customLinestyle.isEmpty() )
2145 {
2146 QString name = u"symbolLayer%1"_s.arg( mSymbolLayerCounter++ );
2147 writeLinetype( name, customLinestyle, unit );
2148 mLineStyles.insert( symbolLayer, name );
2149 }
2150}
2151
2152int QgsDxfExport::nLineTypes( const QList< QPair< QgsSymbolLayer *, QgsSymbol * > > &symbolLayers )
2153{
2154 int nLineTypes = 0;
2155 for ( const auto &symbolLayer : symbolLayers )
2156 {
2157 const QgsSimpleLineSymbolLayer *simpleLine = dynamic_cast< const QgsSimpleLineSymbolLayer * >( symbolLayer.first );
2158 if ( simpleLine )
2159 {
2160 if ( simpleLine->useCustomDashPattern() )
2161 {
2162 ++nLineTypes;
2163 }
2164 }
2165 }
2166 return nLineTypes;
2167}
2168
2169void QgsDxfExport::writeLinetype( const QString &styleName, const QVector<qreal> &pattern, Qgis::RenderUnit u )
2170{
2171 double length = 0;
2172 for ( qreal size : pattern )
2173 {
2174 length += ( size * mapUnitScaleFactor( mSymbologyScale, u, mMapUnits, mMapSettings.mapToPixel().mapUnitsPerPixel() ) );
2175 }
2176
2177 writeGroup( 0, u"LTYPE"_s );
2178 writeHandle();
2179 // 330 5
2180 writeGroup( 100, u"AcDbSymbolTableRecord"_s );
2181 writeGroup( 100, u"AcDbLinetypeTableRecord"_s );
2182 writeGroup( 2, styleName );
2183 writeGroup( 70, 64 ); // 0?
2184 writeGroup( 3, QString() );
2185 writeGroup( 72, 65 );
2186 writeGroup( 73, pattern.size() );
2187 writeGroup( 40, length );
2188
2189 bool isGap = false;
2190 for ( qreal size : pattern )
2191 {
2192 // map units or mm?
2193 double segmentLength = ( isGap ? -size : size );
2194 segmentLength *= mapUnitScaleFactor( mSymbologyScale, u, mMapUnits, mMapSettings.mapToPixel().mapUnitsPerPixel() );
2195 writeGroup( 49, segmentLength );
2196 writeGroup( 74, 0 );
2197 isGap = !isGap;
2198 }
2199}
2200
2201void QgsDxfExport::addGeometryGeneratorSymbolLayer( QgsSymbolRenderContext &ctx, const QgsCoordinateTransform &ct, const QString &layer, QgsSymbolLayer *symbolLayer, bool allSymbolLayers )
2202{
2203 QgsGeometryGeneratorSymbolLayer *gg = dynamic_cast<QgsGeometryGeneratorSymbolLayer *>( symbolLayer );
2204 if ( !gg )
2205 {
2206 return;
2207 }
2208
2209 const QgsFeature *fet = ctx.feature();
2210 if ( !fet )
2211 {
2212 return;
2213 }
2214
2215 QgsFeature f = *fet;
2216
2217 QgsExpressionContext &expressionContext = ctx.renderContext().expressionContext();
2218 QgsExpression geomExpr( gg->geometryExpression() );
2219 geomExpr.prepare( &expressionContext );
2220 QgsGeometry geom = geomExpr.evaluate( &expressionContext ).value<QgsGeometry>();
2221 f.setGeometry( geom );
2222
2223 QgsSymbol *symbol = gg->subSymbol();
2224 if ( symbol && symbol->symbolLayerCount() > 0 )
2225 {
2226 QgsExpressionContextScope *symbolExpressionContextScope = symbol->symbolRenderContext()->expressionContextScope();
2227 symbolExpressionContextScope->setFeature( f );
2228
2229 ctx.setFeature( &f );
2230
2231 int nSymbolLayers = allSymbolLayers ? symbol->symbolLayerCount() : 1;
2232 for ( int i = 0; i < nSymbolLayers; ++i )
2233 {
2234 addFeature( ctx, ct, layer, symbol->symbolLayer( i ), symbol );
2235 }
2236
2237 ctx.setFeature( fet );
2238 }
2239}
2240
2241bool QgsDxfExport::hasBlockBreakingDataDefinedProperties( const QgsSymbolLayer *sl, const QgsSymbol *symbol )
2242{
2243 if ( !sl || !symbol )
2244 {
2245 return false;
2246 }
2247
2248 bool blockBreak = false;
2249 if ( sl->hasDataDefinedProperties() )
2250 {
2251 QSet<int> properties = sl->dataDefinedProperties().propertyKeys();
2252 // Remove data defined properties handled through DXF property codes
2253 properties.remove( static_cast< int >( QgsSymbolLayer::Property::Size ) );
2254 properties.remove( static_cast< int >( QgsSymbolLayer::Property::Angle ) );
2255 blockBreak = !properties.isEmpty();
2256 }
2257
2258 return blockBreak;
2259}
2260
2261double QgsDxfExport::dashSize() const
2262{
2263 double size = mSymbologyScale * 0.002;
2264 return sizeToMapUnits( size );
2265}
2266
2267double QgsDxfExport::dotSize() const
2268{
2269 double size = mSymbologyScale * 0.0006;
2270 return sizeToMapUnits( size );
2271}
2272
2273double QgsDxfExport::dashSeparatorSize() const
2274{
2275 double size = mSymbologyScale * 0.0006;
2276 return sizeToMapUnits( size );
2277}
2278
2279double QgsDxfExport::sizeToMapUnits( double s ) const
2280{
2281 double size = s * QgsUnitTypes::fromUnitToUnitFactor( Qgis::DistanceUnit::Meters, mMapUnits );
2282 return size;
2283}
2284
2285QString QgsDxfExport::lineNameFromPenStyle( Qt::PenStyle style )
2286{
2287 switch ( style )
2288 {
2289 case Qt::DashLine:
2290 return u"DASH"_s;
2291 case Qt::DotLine:
2292 return u"DOT"_s;
2293 case Qt::DashDotLine:
2294 return u"DASHDOT"_s;
2295 case Qt::DashDotDotLine:
2296 return u"DASHDOTDOT"_s;
2297 case Qt::SolidLine:
2298 default:
2299 return u"CONTINUOUS"_s;
2300 }
2301}
2302
2303QString QgsDxfExport::dxfLayerName( const QString &name )
2304{
2305 if ( name.isEmpty() )
2306 return u"0"_s;
2307
2308 // dxf layers can be max 255 characters long
2309 QString layerName = name.left( 255 );
2310
2311 // replaced restricted characters with underscore
2312 // < > / \ " : ; ? * | = '
2313 // See http://docs.autodesk.com/ACD/2010/ENU/AutoCAD%202010%20User%20Documentation/index.html?url=WS1a9193826455f5ffa23ce210c4a30acaf-7345.htm,topicNumber=d0e41665
2314 layerName.replace( '<', '_' );
2315 layerName.replace( '>', '_' );
2316 layerName.replace( '/', '_' );
2317 layerName.replace( '\\', '_' );
2318 layerName.replace( '\"', '_' );
2319 layerName.replace( ':', '_' );
2320 layerName.replace( ';', '_' );
2321 layerName.replace( '?', '_' );
2322 layerName.replace( '*', '_' );
2323 layerName.replace( '|', '_' );
2324 layerName.replace( '=', '_' );
2325 layerName.replace( '\'', '_' );
2326 // if layer name contains comma, resulting file is unreadable in AutoCAD
2327 // see https://github.com/qgis/QGIS/issues/47381
2328 layerName.replace( ',', '_' );
2329
2330 // also remove newline characters (#15067)
2331 layerName.replace( "\r\n"_L1, "_"_L1 );
2332 layerName.replace( '\r', '_' );
2333 layerName.replace( '\n', '_' );
2334
2335 return layerName.trimmed();
2336}
2337
2338bool QgsDxfExport::layerIsScaleBasedVisible( const QgsMapLayer *layer ) const
2339{
2340 if ( !layer )
2341 return false;
2342
2343 if ( mSymbologyExport == Qgis::FeatureSymbologyExport::NoSymbology )
2344 return true;
2345
2346 return layer->isInScaleRange( mSymbologyScale );
2347}
2348
2349QString QgsDxfExport::layerName( const QString &id, const QgsFeature &f ) const
2350{
2351 // TODO: make this thread safe
2352 for ( QgsMapLayer *ml : std::as_const( mLayerList ) )
2353 {
2354 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
2355 if ( vl && vl->id() == id )
2356 {
2357 int attrIdx = mLayerNameAttribute.value( vl->id(), -1 );
2358 return dxfLayerName( attrIdx < 0 ? layerName( vl ) : f.attribute( attrIdx ).toString() );
2359 }
2360 }
2361
2362 return u"0"_s;
2363}
2364
2365QString QgsDxfExport::dxfEncoding( const QString &name )
2366{
2367 const QByteArray codec = name.toLocal8Bit();
2368 if ( QTextCodec::codecForName( codec ) )
2369 {
2370 int i;
2371 for ( i = 0; i < static_cast< int >( sizeof( DXF_ENCODINGS ) / sizeof( *DXF_ENCODINGS ) ) && strcasecmp( codec.data(), DXF_ENCODINGS[i][1] ) != 0; ++i )
2372 ;
2373
2374 if ( i != static_cast< int >( sizeof( DXF_ENCODINGS ) / sizeof( *DXF_ENCODINGS ) ) )
2375 {
2376 return DXF_ENCODINGS[i][0];
2377 }
2378 }
2379
2380 return QString();
2381}
2382
2384{
2385 QStringList encodings;
2386 const QList< QByteArray > codecs = QTextCodec::availableCodecs();
2387 encodings.reserve( codecs.size() );
2388 for ( const QByteArray &codec : codecs )
2389 {
2390 int i;
2391 for ( i = 0; i < static_cast< int >( sizeof( DXF_ENCODINGS ) / sizeof( *DXF_ENCODINGS ) ) && strcasecmp( codec.data(), DXF_ENCODINGS[i][1] ) != 0; ++i )
2392 ;
2393
2394 if ( i < static_cast< int >( sizeof( DXF_ENCODINGS ) / sizeof( *DXF_ENCODINGS ) ) )
2395 encodings << codec.data();
2396 }
2397
2398 encodings.removeDuplicates();
2399
2400 return encodings;
2401}
2402
2404{
2405 Q_ASSERT( vl );
2406 auto overriddenNameIt = mLayerOverriddenName.constFind( vl->id() );
2407 if ( overriddenNameIt != mLayerOverriddenName.constEnd() && !overriddenNameIt.value().isEmpty() )
2408 {
2409 return overriddenNameIt.value();
2410 }
2411 else if ( mLayerTitleAsName && ( !vl->metadata().title().isEmpty() || !vl->serverProperties()->title().isEmpty() ) )
2412 {
2413 return !vl->metadata().title().isEmpty() ? vl->metadata().title() : vl->serverProperties()->title();
2414 }
2415 else
2416 {
2417 return vl->name();
2418 }
2419}
2420
2421void QgsDxfExport::drawLabel( const QString &layerId, QgsRenderContext &context, pal::LabelPosition *label, const QgsPalLayerSettings &settings )
2422{
2423 if ( !settings.drawLabels )
2424 return;
2425
2426 QgsTextLabelFeature *lf = dynamic_cast<QgsTextLabelFeature *>( label->getFeaturePart()->feature() );
2427 if ( !lf )
2428 return;
2429
2430 // Copy to temp, editable layer settings
2431 // these settings will be changed by any data defined values, then used for rendering label components
2432 // settings may be adjusted during rendering of components
2433 QgsPalLayerSettings tmpLyr( settings );
2434
2435 // apply any previously applied data defined settings for the label
2436 const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues = lf->dataDefinedValues();
2437
2438 //font
2439 QFont dFont = lf->definedFont();
2440 QgsDebugMsgLevel( u"PAL font tmpLyr: %1, Style: %2"_s.arg( tmpLyr.format().font().toString(), tmpLyr.format().font().styleName() ), 4 );
2441 QgsDebugMsgLevel( u"PAL font definedFont: %1, Style: %2"_s.arg( dFont.toString(), dFont.styleName() ), 4 );
2442
2443 QgsTextFormat format = tmpLyr.format();
2444 format.setFont( dFont );
2445 tmpLyr.setFormat( format );
2446
2448 {
2449 //calculate font alignment based on label quadrant
2450 switch ( label->quadrant() )
2451 {
2456 break;
2461 break;
2466 break;
2467 }
2468 }
2469
2470 // update tmpLyr with any data defined text style values
2471 QgsPalLabeling::dataDefinedTextStyle( tmpLyr, ddValues );
2472
2473 // update tmpLyr with any data defined text buffer values
2474 QgsPalLabeling::dataDefinedTextBuffer( tmpLyr, ddValues );
2475
2476 // update tmpLyr with any data defined text formatting values
2477 QgsPalLabeling::dataDefinedTextFormatting( tmpLyr, ddValues );
2478
2479 // add to the results
2480 QString txt = label->getFeaturePart()->feature()->labelText();
2481
2482 QgsFeatureId fid = label->getFeaturePart()->featureId();
2483 QString dxfLayer = mDxfLayerNames[layerId][fid];
2484
2485 QString wrapchr = tmpLyr.wrapChar.isEmpty() ? u"\n"_s : tmpLyr.wrapChar;
2486
2487 //add the direction symbol if needed
2488 if ( !txt.isEmpty() && tmpLyr.placement == Qgis::LabelPlacement::Line && tmpLyr.lineSettings().addDirectionSymbol() )
2489 {
2490 bool prependSymb = false;
2491 QString symb = tmpLyr.lineSettings().rightDirectionSymbol();
2492
2493 if ( label->isReversedFromLineDirection() )
2494 {
2495 prependSymb = true;
2496 symb = tmpLyr.lineSettings().leftDirectionSymbol();
2497 }
2498
2499 if ( tmpLyr.lineSettings().reverseDirectionSymbol() )
2500 {
2501 if ( symb == tmpLyr.lineSettings().rightDirectionSymbol() )
2502 {
2503 prependSymb = true;
2504 symb = tmpLyr.lineSettings().leftDirectionSymbol();
2505 }
2506 else
2507 {
2508 prependSymb = false;
2509 symb = tmpLyr.lineSettings().rightDirectionSymbol();
2510 }
2511 }
2512
2513 switch ( tmpLyr.lineSettings().directionSymbolPlacement() )
2514 {
2516 prependSymb = true;
2517 symb = symb + wrapchr;
2518 break;
2519
2521 prependSymb = false;
2522 symb = wrapchr + symb;
2523 break;
2524
2526 break;
2527 }
2528
2529 if ( prependSymb )
2530 {
2531 txt.prepend( symb );
2532 }
2533 else
2534 {
2535 txt.append( symb );
2536 }
2537 }
2538
2539 if ( mFlags & FlagNoMText )
2540 {
2541 txt.replace( QChar( QChar::LineFeed ), ' ' );
2542 txt.replace( QChar( QChar::CarriageReturn ), ' ' );
2543 writeText( dxfLayer, txt, label, tmpLyr, context.expressionContext() );
2544 }
2545 else
2546 {
2547 txt.replace( QString( QChar( QChar::CarriageReturn ) ) + QString( QChar( QChar::LineFeed ) ), u"\\P"_s );
2548 txt.replace( QChar( QChar::CarriageReturn ), u"\\P"_s );
2549 txt = txt.replace( wrapchr, "\\P"_L1 );
2550 txt.replace( " "_L1, "\\~"_L1 );
2551
2552 if ( tmpLyr.format().font().underline() )
2553 {
2554 txt.prepend( "\\L" ).append( "\\l" );
2555 }
2556
2557 if ( tmpLyr.format().font().overline() )
2558 {
2559 txt.prepend( "\\O" ).append( "\\o" );
2560 }
2561
2562 if ( tmpLyr.format().font().strikeOut() )
2563 {
2564 txt.prepend( "\\K" ).append( "\\k" );
2565 }
2566
2567 txt.prepend( u"\\f%1|i%2|b%3;\\H%4;"_s
2568 .arg( tmpLyr.format().font().family() )
2569 .arg( tmpLyr.format().font().italic() ? 1 : 0 )
2570 .arg( tmpLyr.format().font().bold() ? 1 : 0 )
2571 .arg( label->getHeight() / ( 1 + txt.count( u"\\P"_s ) ) * 0.75 ) );
2572 writeMText( dxfLayer, txt, QgsPoint( label->getX(), label->getY() ), label->getWidth(), label->getAlpha() * 180.0 / M_PI, tmpLyr.format().color() );
2573 }
2574}
2575
2576
2577void QgsDxfExport::registerDxfLayer( const QString &layerId, QgsFeatureId fid, const QString &layerName )
2578{
2579 if ( !mDxfLayerNames.contains( layerId ) )
2580 mDxfLayerNames[ layerId ] = QMap<QgsFeatureId, QString>();
2581
2582 mDxfLayerNames[layerId][fid] = layerName;
2583}
2584
2586{
2587 mCrs = crs;
2588 mMapUnits = crs.mapUnits();
2589}
2590
2595
2597{
2598 QString splitLayerFieldName;
2599 const QgsFields fields = mLayer->fields();
2600 if ( mLayerOutputAttributeIndex >= 0 && mLayerOutputAttributeIndex < fields.size() )
2601 {
2602 splitLayerFieldName = fields.at( mLayerOutputAttributeIndex ).name();
2603 }
2604
2605 return splitLayerFieldName;
2606}
2607
2608void QgsDxfExport::createDDBlockInfo()
2609{
2610 int symbolLayerNr = 0;
2611 for ( DxfLayerJob *job : std::as_const( mJobs ) )
2612 {
2613 int ddMaxNumberOfClasses = -1;
2614 bool createDDBlocks = mLayerDDBlockMaxNumberOfClasses.contains( job->featureSource.id() );
2615 if ( createDDBlocks )
2616 {
2617 ddMaxNumberOfClasses = mLayerDDBlockMaxNumberOfClasses[job->featureSource.id()];
2618 }
2619 else
2620 {
2621 continue;
2622 }
2623
2624 const QgsSymbolList symbols = job->renderer->symbols( mRenderContext );
2625
2626 for ( const QgsSymbol *symbol : symbols )
2627 {
2628 //Create blocks only for marker symbols
2629 if ( symbol->type() != Qgis::SymbolType::Marker )
2630 {
2631 continue;
2632 }
2633
2634 int maxSymbolLayers = symbol->symbolLayerCount();
2635 if ( mSymbologyExport != Qgis::FeatureSymbologyExport::PerSymbolLayer )
2636 {
2637 maxSymbolLayers = 1;
2638 }
2639
2640 for ( int i = 0; i < maxSymbolLayers; ++i )
2641 {
2642
2643 const QgsSymbolLayer *sl = symbol->symbolLayer( i );
2644 if ( !sl )
2645 {
2646 continue;
2647 }
2648 QgsPropertyCollection properties = sl->dataDefinedProperties();
2649
2650 if ( !hasBlockBreakingDataDefinedProperties( sl, symbol ) || !createDDBlocks )
2651 {
2652 ++symbolLayerNr;
2653 continue;
2654 }
2655
2656 //iterate layer, evaluate value and get symbology hash groups
2657 QgsSymbolRenderContext sctx( mRenderContext, Qgis::RenderUnit::Millimeters, 1.0, false, Qgis::SymbolRenderHints(), nullptr );
2658 const QgsCoordinateTransform ct( job->crs, mMapSettings.destinationCrs(), mMapSettings.transformContext() );
2659 QgsFeatureRequest request = QgsFeatureRequest().setSubsetOfAttributes( job->attributes, job->fields ).setFlags( Qgis::FeatureRequestFlag::NoGeometry ).setExpressionContext( job->renderContext.expressionContext() );
2660 QgsCoordinateTransform extentTransform = ct;
2661 extentTransform.setBallparkTransformsAreAppropriate( true );
2662 try
2663 {
2664 request.setFilterRect( extentTransform.transformBoundingBox( mExtent, Qgis::TransformDirection::Reverse ) );
2665 }
2666 catch ( QgsCsException &e )
2667 {
2668 QgsDebugError( u"Could not transform extent to layer extent: %1"_s.arg( e.what() ) );
2669 continue;
2670 }
2671
2672 QgsFeatureIterator featureIt = job->featureSource.getFeatures( request );
2673
2674 QHash <uint, QPair<int, DataDefinedBlockInfo> > blockSymbolMap; //symbolHash/occurrences/block Text
2675
2676 QgsFeature fet;
2677 while ( featureIt.nextFeature( fet ) )
2678 {
2679 uint symbolHash = dataDefinedSymbolClassHash( fet, properties );
2680 if ( blockSymbolMap.contains( symbolHash ) )
2681 {
2682 blockSymbolMap[symbolHash].first += 1;
2683 continue;
2684 }
2685
2686 sctx.setFeature( &fet );
2687 sctx.renderContext().expressionContext().setFeature( fet );
2688
2689 DataDefinedBlockInfo blockInfo;
2690 blockInfo.blockName = u"symbolLayer%1class%2"_s.arg( symbolLayerNr ).arg( symbolHash );
2691 blockInfo.angle = sl->dxfAngle( sctx );
2692 blockInfo.size = sl->dxfSize( *this, sctx );
2693 blockInfo.feature = fet;
2694
2695 blockSymbolMap.insert( symbolHash, qMakePair( 1, blockInfo ) );
2696 }
2697 ++symbolLayerNr;
2698
2699 //keep the entries with the most frequent occurrences
2700 QMultiMap<int, uint> occurrences;
2701 QHash <uint, QPair<int, DataDefinedBlockInfo> >::const_iterator blockSymbolIt = blockSymbolMap.constBegin();
2702 for ( ; blockSymbolIt != blockSymbolMap.constEnd(); ++blockSymbolIt )
2703 {
2704 occurrences.insert( blockSymbolIt.value().first, blockSymbolIt.key() );
2705 }
2706
2707 QHash <uint, DataDefinedBlockInfo > applyBlockSymbolMap;
2708 int nInsertedClasses = 0;
2709 QMultiMap<int, uint>::const_iterator occIt = occurrences.constEnd();
2710 while ( occurrences.size() > 0 && occIt != occurrences.constBegin() )
2711 {
2712 --occIt;
2713 applyBlockSymbolMap.insert( occIt.value(), blockSymbolMap[occIt.value()].second );
2714 ++nInsertedClasses;
2715 if ( ddMaxNumberOfClasses != -1 && nInsertedClasses >= ddMaxNumberOfClasses )
2716 {
2717 break;
2718 }
2719 }
2720
2721 //add to mDataDefinedBlockInfo
2722 mDataDefinedBlockInfo.insert( sl, applyBlockSymbolMap );
2723 }
2724 }
2725 }
2726}
@ OverPoint
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point....
Definition qgis.h:1227
@ Line
Arranges candidates parallel to a generalised line representing the feature or parallel to a polygon'...
Definition qgis.h:1228
DistanceUnit
Units of distance.
Definition qgis.h:5120
@ Meters
Meters.
Definition qgis.h:5121
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2254
LabelQuadrantPosition
Label quadrant positions.
Definition qgis.h:1313
@ AboveRight
Above right.
Definition qgis.h:1316
@ BelowLeft
Below left.
Definition qgis.h:1320
@ Above
Above center.
Definition qgis.h:1315
@ BelowRight
Below right.
Definition qgis.h:1322
@ Right
Right middle.
Definition qgis.h:1319
@ AboveLeft
Above left.
Definition qgis.h:1314
@ Below
Below center.
Definition qgis.h:1321
@ Over
Center middle.
Definition qgis.h:1318
@ Miter
Use mitered joins.
Definition qgis.h:2181
@ Center
Center align.
Definition qgis.h:1398
@ FollowPlacement
Alignment follows placement of label, e.g., labels to the left of a feature will be drawn with right ...
Definition qgis.h:1400
RenderUnit
Rendering size units.
Definition qgis.h:5290
@ Millimeters
Millimeters.
Definition qgis.h:5291
@ MapUnits
Map units.
Definition qgis.h:5292
@ Pixels
Pixels.
Definition qgis.h:5293
@ Flat
Flat cap (in line with start/end of line).
Definition qgis.h:2168
QFlags< SymbolRenderHint > SymbolRenderHints
Symbol render hints.
Definition qgis.h:796
@ Marker
Marker symbol.
Definition qgis.h:630
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:280
@ CompoundCurve
CompoundCurve.
Definition qgis.h:291
@ Point
Point.
Definition qgis.h:282
@ LineString
LineString.
Definition qgis.h:283
@ MultiPoint
MultiPoint.
Definition qgis.h:286
@ Polygon
Polygon.
Definition qgis.h:284
@ MultiPolygon
MultiPolygon.
Definition qgis.h:288
@ MultiLineString
MultiLineString.
Definition qgis.h:287
@ CircularString
CircularString.
Definition qgis.h:290
@ PointZ
PointZ.
Definition qgis.h:299
@ MultiCurve
MultiCurve.
Definition qgis.h:293
@ CurvePolygon
CurvePolygon.
Definition qgis.h:292
@ MultiSurface
MultiSurface.
Definition qgis.h:294
@ PerFeature
Keeps the number of features and export symbology per feature.
Definition qgis.h:5860
@ PerSymbolLayer
Exports one feature per symbol layer (considering symbol levels).
Definition qgis.h:5861
@ NoSymbology
Export only data.
Definition qgis.h:5859
@ Reverse
Reverse/inverse transform (from destination to source).
Definition qgis.h:2731
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.
QString title() const
Returns the human readable name of the resource, typically displayed in search results.
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:78
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:54
void writeFilledCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius)
Write filled circle (as hatch).
ExportResult
The result of an export as dxf operation.
@ DeviceNotWritableError
Device not writable error.
@ Success
Successful export.
@ EmptyExtentError
Empty extent, no extent given and no extent could be derived from layers.
@ InvalidDeviceError
Invalid device error.
void writeCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius, const QString &lineStyleName, double width)
Write circle (as polyline).
~QgsDxfExport() override
static double mapUnitScaleFactor(double scale, Qgis::RenderUnit symbolUnits, Qgis::DistanceUnit mapUnits, double mapUnitsPerPixel=1.0)
Returns scale factor for conversion to map units.
ExportResult writeToFile(QIODevice *d, const QString &codec)
Export to a dxf file in the given encoding.
void writeLine(const QgsPoint &pt1, const QgsPoint &pt2, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Write line (as a polyline).
void writeGroup(int code, int i)
Write a tuple of group code and integer value.
QString layerName(const QString &id, const QgsFeature &f) const
Gets layer name for feature.
void setFlags(QgsDxfExport::Flags flags)
Sets the export flags.
QgsRectangle extent() const
Gets extent of area to export.
@ 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:68
QgsGeometry geometry
Definition qgsfeature.h:71
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.
double mapUnitsPerPixel() const
Returns the current map units per pixel.
Struct for storing maximum and minimum scales for measurements in map units.
bool minSizeMMEnabled
Whether the minimum size in mm should be respected.
double maxScale
The maximum scale, or 0.0 if unset.
double minScale
The minimum scale, or 0.0 if unset.
double maxSizeMM
The maximum size in millimeters, or 0.0 if unset.
bool maxSizeMMEnabled
Whether the maximum size in mm should be respected.
double minSizeMM
The minimum size in millimeters, or 0.0 if unset.
Abstract base class for marker symbol layers.
double size() const
Returns the symbol size.
double dxfAngle(QgsSymbolRenderContext &context) const override
Gets angle.
double dxfSize(const QgsDxfExport &e, QgsSymbolRenderContext &context) const override
Gets marker size.
Qgis::RenderUnit sizeUnit() const
Returns the units for the symbol's size.
Contains settings for how a map layer will be labeled.
void setFormat(const QgsTextFormat &format)
Sets the label text formatting settings, e.g., font settings, buffer settings, etc.
QString wrapChar
Wrapping character string.
Qgis::LabelPlacement placement
Label placement mode.
bool drawLabels
Whether to draw labels for this layer.
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.
void setProperty(int key, const QgsProperty &property)
Adds a property to the collection and takes ownership of it.
bool isActive(int key) const final
Returns true if the collection contains an active property with the specified key.
QSet< QString > referencedFields(const QgsExpressionContext &context=QgsExpressionContext(), bool ignoreContext=false) const final
Returns the set of any fields referenced by the active properties from the collection.
A rectangle specified with double values.
double xMinimum
double yMinimum
double xMaximum
double yMaximum
Contains information about the context of a rendering operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
bool useCustomDashPattern() const
Returns true if the line uses a custom dash pattern.
Abstract base class for symbol layers.
virtual double dxfSize(const QgsDxfExport &e, QgsSymbolRenderContext &context) const
Gets marker size.
virtual double dxfOffset(const QgsDxfExport &e, QgsSymbolRenderContext &context) const
Gets offset.
virtual QColor dxfBrushColor(QgsSymbolRenderContext &context) const
Gets brush/fill color.
virtual Qt::PenStyle dxfPenStyle() const
Gets pen style.
virtual QColor dxfColor(QgsSymbolRenderContext &context) const
Gets color.
virtual QString layerType() const =0
Returns a string that represents this layer type.
virtual double dxfWidth(const QgsDxfExport &e, QgsSymbolRenderContext &context) const
Gets line width.
virtual double dxfAngle(QgsSymbolRenderContext &context) const
Gets angle.
virtual bool writeDxf(QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift=QPointF(0.0, 0.0)) const
write as DXF
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the symbol layer's property collection, used for data defined overrides.
virtual Qt::BrushStyle dxfBrushStyle() const
Gets brush/fill style.
virtual QVector< qreal > dxfCustomDashPattern(Qgis::RenderUnit &unit) const
Gets dash pattern.
virtual bool hasDataDefinedProperties() const
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
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:231
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:353
Qgis::SymbolType type() const
Returns the symbol's type.
Definition qgssymbol.h:294
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.
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:169
QgsLabelFeature * feature()
Returns the parent feature.
Definition feature.h:89
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:7486
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition qgis.h:6852
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7485
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6935
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:63
#define QgsDebugError(str)
Definition qgslogger.h:59
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...