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