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