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