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