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