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