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