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