19#include <cpl_string.h>
28#include <QDomDocument>
31#include <QMutexLocker>
37using namespace Qt::StringLiterals;
42 GDALDriverH hDriverMem = GDALGetDriverByName(
"PDF" );
48 const char *pHavePoppler = GDALGetMetadataItem( hDriverMem,
"HAVE_POPPLER",
nullptr );
49 if ( pHavePoppler && strstr( pHavePoppler,
"YES" ) )
52 const char *pHavePdfium = GDALGetMetadataItem( hDriverMem,
"HAVE_PDFIUM",
nullptr );
53 if ( pHavePdfium && strstr( pHavePdfium,
"YES" ) )
62 GDALDriverH hDriverMem = GDALGetDriverByName(
"PDF" );
65 return QObject::tr(
"No GDAL PDF driver available." );
68 const char *pHavePoppler = GDALGetMetadataItem( hDriverMem,
"HAVE_POPPLER",
nullptr );
69 if ( pHavePoppler && strstr( pHavePoppler,
"YES" ) )
72 const char *pHavePdfium = GDALGetMetadataItem( hDriverMem,
"HAVE_PDFIUM",
nullptr );
73 if ( pHavePdfium && strstr( pHavePdfium,
"YES" ) )
76 return QObject::tr(
"GDAL PDF driver was not built with PDF read support. A build with PDF read support is required for geospatial PDF creation." );
81 QgsDebugError( u
"GDAL PDF creation error: %1 "_s.arg( msg ) );
82 if ( QStringList *errorList =
static_cast< QStringList *
>( CPLGetErrorHandlerUserData() ) )
84 errorList->append( QString( msg ) );
93 const QString composition = createCompositionXml( components, details );
95 if ( composition.isEmpty() )
99 GDALDriverH driver = GDALGetDriverByName(
"PDF" );
102 mErrorMessage = QObject::tr(
"Cannot load GDAL PDF driver" );
107 QFile file( xmlFilePath );
108 if ( file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
110 QTextStream out( &file );
115 mErrorMessage = QObject::tr(
"Could not create geospatial PDF composition file" );
119 char **papszOptions = CSLSetNameValue(
nullptr,
"COMPOSITION_FILE", xmlFilePath.toUtf8().constData() );
121 QStringList creationErrors;
125 gdal::dataset_unique_ptr outputDataset( GDALCreate( driver, destinationFile.toUtf8().constData(), 0, 0, 0, GDT_Unknown, papszOptions ) );
127 CPLPopErrorHandler();
129 const bool res = outputDataset.get() !=
nullptr;
132 if ( creationErrors.size() == 1 )
134 mErrorMessage = QObject::tr(
"Could not create PDF file: %1" ).arg( creationErrors.at( 0 ) );
136 else if ( !creationErrors.empty() )
138 mErrorMessage = QObject::tr(
"Could not create PDF file. Received errors:\n" );
139 for (
const QString &error : std::as_const( creationErrors ) )
141 mErrorMessage += ( !mErrorMessage.isEmpty() ? u
"\n"_s : QString() ) + error;
146 mErrorMessage = QObject::tr(
"Could not create PDF file, but no error details are available" );
149 outputDataset.reset();
151 CSLDestroy( papszOptions );
165 case QPainter::CompositionMode_SourceOver:
166 case QPainter::CompositionMode_Multiply:
167 case QPainter::CompositionMode_Screen:
168 case QPainter::CompositionMode_Overlay:
169 case QPainter::CompositionMode_Darken:
170 case QPainter::CompositionMode_Lighten:
171 case QPainter::CompositionMode_ColorDodge:
172 case QPainter::CompositionMode_ColorBurn:
173 case QPainter::CompositionMode_HardLight:
174 case QPainter::CompositionMode_SoftLight:
175 case QPainter::CompositionMode_Difference:
176 case QPainter::CompositionMode_Exclusion:
189 QMutexLocker locker( &mMutex );
194 mCollatedFeatures[group][layerId].append( f );
197bool QgsAbstractGeospatialPdfExporter::saveTemporaryLayers()
199 for (
auto groupIt = mCollatedFeatures.constBegin(); groupIt != mCollatedFeatures.constEnd(); ++groupIt )
201 for (
auto it = groupIt->constBegin(); it != groupIt->constEnd(); ++it )
205 VectorComponentDetail detail = componentDetailForLayerId( it.key() );
206 detail.sourceVectorPath = filePath;
207 detail.group = groupIt.key();
215 std::unique_ptr< QgsVectorFileWriter > writer(
219 if ( writer->hasError() )
221 mErrorMessage = writer->errorMessage();
225 for (
const QgsFeature &feature : features )
227 QgsFeature f = feature;
230 mErrorMessage = writer->errorMessage();
235 detail.sourceVectorLayer = layerName;
236 mVectorComponents << detail;
246 bool initiallyVisible =
false;
248 QString mutuallyExclusiveGroupId;
250 std::vector< std::unique_ptr< TreeNode > > children;
251 TreeNode *parent =
nullptr;
253 void addChild( std::unique_ptr< TreeNode > child )
255 child->parent =
this;
256 children.emplace_back( std::move( child ) );
259 QDomElement toElement( QDomDocument &doc )
const
261 QDomElement layerElement = doc.createElement( u
"Layer"_s );
262 layerElement.setAttribute( u
"id"_s,
id );
263 layerElement.setAttribute( u
"name"_s, name );
264 layerElement.setAttribute( u
"initiallyVisible"_s, initiallyVisible ? u
"true"_s : u
"false"_s );
265 if ( !mutuallyExclusiveGroupId.isEmpty() )
266 layerElement.setAttribute( u
"mutuallyExclusiveGroupId"_s, mutuallyExclusiveGroupId );
268 for (
const auto &child : children )
270 layerElement.appendChild( child->toElement( doc ) );
276 QDomElement createIfLayerOnElement( QDomDocument &doc, QDomElement &contentElement )
const
278 QDomElement element = doc.createElement( u
"IfLayerOn"_s );
279 element.setAttribute( u
"layerId"_s,
id );
280 contentElement.appendChild( element );
284 QDomElement createNestedIfLayerOnElements( QDomDocument &doc, QDomElement &contentElement )
const
286 TreeNode *currentParent = parent;
287 QDomElement finalElement = doc.createElement( u
"IfLayerOn"_s );
288 finalElement.setAttribute( u
"layerId"_s,
id );
290 QDomElement currentElement = finalElement;
291 while ( currentParent )
293 QDomElement ifGroupOn = doc.createElement( u
"IfLayerOn"_s );
294 ifGroupOn.setAttribute( u
"layerId"_s, currentParent->id );
295 ifGroupOn.appendChild( currentElement );
296 currentElement = ifGroupOn;
297 currentParent = currentParent->parent;
299 contentElement.appendChild( currentElement );
305QString QgsAbstractGeospatialPdfExporter::createCompositionXml(
const QList<ComponentLayerDetail> &components,
const ExportDetails &details )
309 QDomElement compositionElem = doc.createElement( u
"PDFComposition"_s );
312 QDomElement metadata = doc.createElement( u
"Metadata"_s );
313 if ( !details.author.isEmpty() )
315 QDomElement author = doc.createElement( u
"Author"_s );
316 author.appendChild( doc.createTextNode( details.author ) );
317 metadata.appendChild( author );
319 if ( !details.producer.isEmpty() )
321 QDomElement producer = doc.createElement( u
"Producer"_s );
322 producer.appendChild( doc.createTextNode( details.producer ) );
323 metadata.appendChild( producer );
325 if ( !details.creator.isEmpty() )
327 QDomElement creator = doc.createElement( u
"Creator"_s );
328 creator.appendChild( doc.createTextNode( details.creator ) );
329 metadata.appendChild( creator );
331 if ( details.creationDateTime.isValid() )
333 QDomElement creationDate = doc.createElement( u
"CreationDate"_s );
334 QString creationDateString = u
"D:%1"_s.arg( details.creationDateTime.toString( u
"yyyyMMddHHmmss"_s ) );
335#if QT_FEATURE_timezone > 0
336 if ( details.creationDateTime.timeZone().isValid() )
338 int offsetFromUtc = details.creationDateTime.timeZone().offsetFromUtc( details.creationDateTime );
339 creationDateString += ( offsetFromUtc >= 0 ) ?
'+' :
'-';
340 offsetFromUtc = std::abs( offsetFromUtc );
341 int offsetHours = offsetFromUtc / 3600;
342 int offsetMins = ( offsetFromUtc % 3600 ) / 60;
343 creationDateString += u
"%1'%2'"_s.arg( offsetHours ).arg( offsetMins );
346 QgsDebugError( u
"Qt is built without timezone support, skipping timezone for pdf export"_s );
348 creationDate.appendChild( doc.createTextNode( creationDateString ) );
349 metadata.appendChild( creationDate );
351 if ( !details.subject.isEmpty() )
353 QDomElement subject = doc.createElement( u
"Subject"_s );
354 subject.appendChild( doc.createTextNode( details.subject ) );
355 metadata.appendChild( subject );
357 if ( !details.title.isEmpty() )
359 QDomElement title = doc.createElement( u
"Title"_s );
360 title.appendChild( doc.createTextNode( details.title ) );
361 metadata.appendChild( title );
363 if ( !details.keywords.empty() )
365 QStringList allKeywords;
366 for (
auto it = details.keywords.constBegin(); it != details.keywords.constEnd(); ++it )
368 allKeywords.append( u
"%1: %2"_s.arg( it.key(), it.value().join(
',' ) ) );
370 QDomElement keywords = doc.createElement( u
"Keywords"_s );
371 keywords.appendChild( doc.createTextNode( allKeywords.join(
';' ) ) );
372 metadata.appendChild( keywords );
374 compositionElem.appendChild( metadata );
376 QSet< QString > createdLayerIds;
377 std::vector< std::unique_ptr< TreeNode > > rootGroups;
378 std::vector< std::unique_ptr< TreeNode > > rootLayers;
379 QMap< QString, TreeNode * > groupNameMap;
381 QStringList layerTreeGroupOrder = details.layerTreeGroupOrder;
385 for (
auto it = details.customLayerTreeGroups.constBegin(); it != details.customLayerTreeGroups.constEnd(); ++it )
387 if ( layerTreeGroupOrder.contains( it.value() ) )
389 layerTreeGroupOrder.append( it.value() );
393 if ( details.includeFeatures )
397 if ( !component.group.isEmpty() && !layerTreeGroupOrder.contains( component.group ) )
399 layerTreeGroupOrder.append( component.group );
407 if ( !component.group.isEmpty() && !layerTreeGroupOrder.contains( component.group ) )
409 layerTreeGroupOrder.append( component.group );
413 QMap< QString, TreeNode * > groupNameToTreeNode;
414 QMap< QString, TreeNode * > layerIdToTreeNode;
416 auto createGroup = [&details, &groupNameToTreeNode](
const QString &groupName ) -> std::unique_ptr< TreeNode > {
417 auto group = std::make_unique< TreeNode >();
418 const QString
id = QUuid::createUuid().toString();
420 groupNameToTreeNode[groupName] = group.get();
422 group->name = groupName;
423 group->initiallyVisible =
true;
424 if ( details.mutuallyExclusiveGroups.contains( groupName ) )
425 group->mutuallyExclusiveGroupId = u
"__mutually_exclusive_groups__"_s;
429 if ( details.includeFeatures )
433 const QString destinationGroup = details.customLayerTreeGroups.value( component.mapLayerId, component.group );
434 const QString
id = destinationGroup.isEmpty() ? component.mapLayerId : u
"%1_%2"_s.arg( destinationGroup, component.mapLayerId );
436 auto layer = std::make_unique< TreeNode >();
438 layer->name = details.layerIdToPdfLayerTreeNameMap.contains( component.mapLayerId ) ? details.layerIdToPdfLayerTreeNameMap.value( component.mapLayerId ) : component.name;
439 layer->initiallyVisible = details.initialLayerVisibility.value( component.mapLayerId,
true );
440 layer->mapLayerId = component.mapLayerId;
442 layerIdToTreeNode.insert( layer->id, layer.get() );
443 if ( !destinationGroup.isEmpty() )
445 if ( TreeNode *groupNode = groupNameMap.value( destinationGroup ) )
447 groupNode->addChild( std::move( layer ) );
451 std::unique_ptr< TreeNode > group = createGroup( destinationGroup );
452 group->addChild( std::move( layer ) );
453 groupNameMap.insert( destinationGroup, group.get() );
454 rootGroups.emplace_back( std::move( group ) );
459 rootLayers.emplace_back( std::move( layer ) );
462 createdLayerIds.insert(
id );
472 const QString destinationGroup = details.customLayerTreeGroups.value( component.mapLayerId, component.group );
473 const QString
id = destinationGroup.isEmpty() ? component.mapLayerId : u
"%1_%2"_s.arg( destinationGroup, component.mapLayerId );
475 if ( !component.mapLayerId.isEmpty() && createdLayerIds.contains(
id ) )
478 if ( destinationGroup.isEmpty() && component.mapLayerId.isEmpty() )
481 std::unique_ptr< TreeNode > mapLayerNode;
482 if ( !component.mapLayerId.isEmpty() )
484 mapLayerNode = std::make_unique< TreeNode >();
485 mapLayerNode->id = id;
486 mapLayerNode->name = details.layerIdToPdfLayerTreeNameMap.value( component.mapLayerId, component.name );
487 mapLayerNode->initiallyVisible = details.initialLayerVisibility.value( component.mapLayerId,
true );
488 mapLayerNode->mapLayerId = component.mapLayerId;
490 layerIdToTreeNode.insert( mapLayerNode->id, mapLayerNode.get() );
493 if ( !destinationGroup.isEmpty() )
495 if ( TreeNode *groupNode = groupNameMap.value( destinationGroup ) )
498 groupNode->addChild( std::move( mapLayerNode ) );
502 std::unique_ptr< TreeNode > group = createGroup( destinationGroup );
504 group->addChild( std::move( mapLayerNode ) );
505 groupNameMap.insert( destinationGroup, group.get() );
506 rootGroups.emplace_back( std::move( group ) );
512 rootLayers.emplace_back( std::move( mapLayerNode ) );
515 if ( !component.mapLayerId.isEmpty() )
517 createdLayerIds.insert(
id );
522 QDomElement page = doc.createElement( u
"Page"_s );
523 QDomElement dpi = doc.createElement( u
"DPI"_s );
526 page.appendChild( dpi );
528 QDomElement width = doc.createElement( u
"Width"_s );
529 const double pageWidthPdfUnits = std::ceil( details.pageSizeMm.width() / 25.4 * 72 );
530 width.appendChild( doc.createTextNode(
qgsDoubleToString( pageWidthPdfUnits ) ) );
531 page.appendChild( width );
532 QDomElement height = doc.createElement( u
"Height"_s );
533 const double pageHeightPdfUnits = std::ceil( details.pageSizeMm.height() / 25.4 * 72 );
534 height.appendChild( doc.createTextNode(
qgsDoubleToString( pageHeightPdfUnits ) ) );
535 page.appendChild( height );
539 for (
const QgsAbstractGeospatialPdfExporter::GeoReferencedSection §ion : details.georeferencedSections )
541 QDomElement georeferencing = doc.createElement( u
"Georeferencing"_s );
542 georeferencing.setAttribute( u
"id"_s, u
"georeferenced_%1"_s.arg( i++ ) );
543 georeferencing.setAttribute( u
"ISO32000ExtensionFormat"_s, details.useIso32000ExtensionFormatGeoreferencing ? u
"true"_s : u
"false"_s );
547 QDomElement srs = doc.createElement( u
"SRS"_s );
550 if ( !section.
crs.
authid().isEmpty() && !section.
crs.
authid().startsWith( u
"user"_s, Qt::CaseInsensitive ) )
552 srs.appendChild( doc.createTextNode( section.
crs.
authid() ) );
558 georeferencing.appendChild( srs );
570 QDomElement boundingPolygon = doc.createElement( u
"BoundingPolygon"_s );
573 QTransform t = QTransform::fromTranslate( 0, pageHeightPdfUnits ).scale( pageWidthPdfUnits / details.pageSizeMm.width(), -pageHeightPdfUnits / details.pageSizeMm.height() );
577 boundingPolygon.appendChild( doc.createTextNode( p.
asWkt() ) );
579 georeferencing.appendChild( boundingPolygon );
588 QDomElement boundingBox = doc.createElement( u
"BoundingBox"_s );
593 georeferencing.appendChild( boundingBox );
598 QDomElement cp1 = doc.createElement( u
"ControlPoint"_s );
599 cp1.setAttribute( u
"x"_s,
qgsDoubleToString( point.pagePoint.x() / 25.4 * 72 ) );
600 cp1.setAttribute( u
"y"_s,
qgsDoubleToString( ( details.pageSizeMm.height() - point.pagePoint.y() ) / 25.4 * 72 ) );
603 georeferencing.appendChild( cp1 );
606 page.appendChild( georeferencing );
610 QDomElement pdfDataset = doc.createElement( u
"PDF"_s );
611 pdfDataset.setAttribute( u
"dataset"_s, component.sourcePdfPath );
612 if ( component.opacity != 1.0 || component.compositionMode != QPainter::CompositionMode_SourceOver )
614 QDomElement blendingElement = doc.createElement( u
"Blending"_s );
615 blendingElement.setAttribute( u
"opacity"_s, component.opacity );
616 blendingElement.setAttribute( u
"function"_s, compositionModeToString( component.compositionMode ) );
618 pdfDataset.appendChild( blendingElement );
624 QDomElement content = doc.createElement( u
"Content"_s );
627 if ( component.mapLayerId.isEmpty() && component.group.isEmpty() )
629 content.appendChild( createPdfDatasetElement( component ) );
631 else if ( !component.mapLayerId.isEmpty() )
633 const QString destinationGroup = details.customLayerTreeGroups.value( component.mapLayerId, component.group );
634 const QString
id = destinationGroup.isEmpty() ? component.mapLayerId : u
"%1_%2"_s.arg( destinationGroup, component.mapLayerId );
635 if ( TreeNode *treeNode = layerIdToTreeNode.value(
id ) )
637 QDomElement ifLayerOnElement = treeNode->createNestedIfLayerOnElements( doc, content );
638 ifLayerOnElement.appendChild( createPdfDatasetElement( component ) );
641 else if ( TreeNode *groupNode = groupNameToTreeNode.value( component.group ) )
643 QDomElement ifGroupOn = groupNode->createIfLayerOnElement( doc, content );
644 ifGroupOn.appendChild( createPdfDatasetElement( component ) );
649 if ( details.includeFeatures )
653 const QString destinationGroup = details.customLayerTreeGroups.value( component.mapLayerId, component.group );
654 const QString
id = destinationGroup.isEmpty() ? component.mapLayerId : u
"%1_%2"_s.arg( destinationGroup, component.mapLayerId );
655 if ( TreeNode *treeNode = layerIdToTreeNode.value(
id ) )
657 QDomElement ifLayerOnElement = treeNode->createNestedIfLayerOnElements( doc, content );
659 QDomElement vectorDataset = doc.createElement( u
"Vector"_s );
660 vectorDataset.setAttribute( u
"dataset"_s, component.sourceVectorPath );
661 vectorDataset.setAttribute( u
"layer"_s, component.sourceVectorLayer );
662 vectorDataset.setAttribute( u
"visible"_s, u
"false"_s );
663 QDomElement logicalStructure = doc.createElement( u
"LogicalStructure"_s );
664 logicalStructure.setAttribute( u
"displayLayerName"_s, component.name );
665 if ( !component.displayAttribute.isEmpty() )
666 logicalStructure.setAttribute( u
"fieldToDisplay"_s, component.displayAttribute );
667 vectorDataset.appendChild( logicalStructure );
668 ifLayerOnElement.appendChild( vectorDataset );
673 page.appendChild( content );
676 QDomElement
layerTree = doc.createElement( u
"LayerTree"_s );
682 std::sort( rootGroups.begin(), rootGroups.end(), [&layerTreeGroupOrder](
const std::unique_ptr< TreeNode > &a,
const std::unique_ptr< TreeNode > &b ) ->
bool {
683 return layerTreeGroupOrder.indexOf( a->name ) < layerTreeGroupOrder.indexOf( b->name );
686 bool haveFoundMutuallyExclusiveGroup =
false;
687 for (
const auto &node : std::as_const( rootGroups ) )
689 if ( !node->mutuallyExclusiveGroupId.isEmpty() )
692 node->initiallyVisible = !haveFoundMutuallyExclusiveGroup;
693 haveFoundMutuallyExclusiveGroup =
true;
695 layerTree.appendChild( node->toElement( doc ) );
699 layerTreeGroupOrder.erase(
700 std::remove_if( layerTreeGroupOrder.begin(), layerTreeGroupOrder.end(), [&details](
const QString &group ) { return details.customLayerTreeGroups.key( group ).isEmpty(); } ),
701 layerTreeGroupOrder.end()
706 std::sort( rootLayers.begin(), rootLayers.end(), [&details](
const std::unique_ptr< TreeNode > &a,
const std::unique_ptr< TreeNode > &b ) ->
bool {
707 const int indexA = details.layerOrder.indexOf( a->mapLayerId );
708 const int indexB = details.layerOrder.indexOf( b->mapLayerId );
710 if ( indexA >= 0 && indexB >= 0 )
711 return indexA < indexB;
712 else if ( indexA >= 0 )
714 else if ( indexB >= 0 )
717 return a->name.localeAwareCompare( b->name ) < 0;
720 for (
const auto &node : std::as_const( rootLayers ) )
722 layerTree.appendChild( node->toElement( doc ) );
725 compositionElem.appendChild( layerTree );
726 compositionElem.appendChild( page );
728 doc.appendChild( compositionElem );
731 QTextStream stream( &composition );
732 doc.save( stream, -1 );
737QString QgsAbstractGeospatialPdfExporter::compositionModeToString( QPainter::CompositionMode mode )
741 case QPainter::CompositionMode_SourceOver:
744 case QPainter::CompositionMode_Multiply:
745 return u
"Multiply"_s;
747 case QPainter::CompositionMode_Screen:
750 case QPainter::CompositionMode_Overlay:
753 case QPainter::CompositionMode_Darken:
756 case QPainter::CompositionMode_Lighten:
759 case QPainter::CompositionMode_ColorDodge:
760 return u
"ColorDodge"_s;
762 case QPainter::CompositionMode_ColorBurn:
763 return u
"ColorBurn"_s;
765 case QPainter::CompositionMode_HardLight:
766 return u
"HardLight"_s;
768 case QPainter::CompositionMode_SoftLight:
769 return u
"SoftLight"_s;
771 case QPainter::CompositionMode_Difference:
772 return u
"Difference"_s;
774 case QPainter::CompositionMode_Exclusion:
775 return u
"Exclusion"_s;
781 QgsDebugError( u
"Unsupported PDF blend mode %1"_s.arg( mode ) );
@ PreferredGdal
Preferred format for conversion of CRS to WKT for use with the GDAL library.
@ NoSymbology
Export only data.
static bool compositionModeSupported(QPainter::CompositionMode mode)
Returns true if the specified composition mode is supported for layers during Geospatial PDF exports.
static QString geospatialPDFAvailabilityExplanation()
Returns a user-friendly, translated string explaining why Geospatial PDF export support is not availa...
bool finalize(const QList< QgsAbstractGeospatialPdfExporter::ComponentLayerDetail > &components, const QString &destinationFile, const ExportDetails &details)
To be called after the rendering operation is complete.
void pushRenderedFeature(const QString &layerId, const QgsAbstractGeospatialPdfExporter::RenderedFeature &feature, const QString &group=QString())
Called multiple times during the rendering operation, whenever a feature associated with the specifie...
static bool geospatialPDFCreationAvailable()
Returns true if the current QGIS build is capable of Geospatial PDF support.
QString generateTemporaryFilepath(const QString &filename) const
Returns a file path to use for temporary files required for Geospatial PDF creation.
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString toWkt(Qgis::CrsWktVariant variant=Qgis::CrsWktVariant::Wkt1Gdal, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
Contains information about the context in which a coordinate transform is executed.
bool isEmpty() const override
Returns true if the geometry is empty.
void transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection d=Qgis::TransformDirection::Forward, bool transformZ=false) override
Transforms the geometry using a coordinate transform.
@ FastInsert
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
@ RegeneratePrimaryKey
This flag indicates, that a primary key field cannot be guaranteed to be unique and the sink should i...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
static QString stringToSafeFilename(const QString &string)
Converts a string to a safe filename, replacing characters which are not safe for filenames with an '...
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
Options to pass to QgsVectorFileWriter::writeAsVectorFormat().
QString driverName
OGR driver to use.
Qgis::FeatureSymbologyExport symbologyExport
Symbology to export.
A convenience class for writing vector layers to disk based formats (e.g.
QgsLayerTree * layerTree(const QgsWmsRenderContext &context)
std::unique_ptr< std::remove_pointer< GDALDatasetH >::type, GDALDatasetCloser > dataset_unique_ptr
Scoped GDAL dataset.
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
void CPL_STDCALL collectErrors(CPLErr, int, const char *msg)
QList< QgsFeature > QgsFeatureList
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)
Contains details of a particular input component to be used during PDF composition.
Contains details of a control point used during georeferencing Geospatial PDF outputs.
bool includeFeatures
true if feature vector information (such as attributes) should be exported.
QgsRectangle pageBoundsMm
Bounds of the georeferenced section on the page, in millimeters.
QgsCoordinateReferenceSystem crs
Coordinate reference system for georeferenced section.
QList< QgsAbstractGeospatialPdfExporter::ControlPoint > controlPoints
List of control points corresponding to this georeferenced section.
QgsPolygon pageBoundsPolygon
Bounds of the georeferenced section on the page, in millimeters, as a free-form polygon.
Contains information about a feature rendered inside the PDF.
QgsGeometry renderedBounds
Bounds, in PDF units, of rendered feature.
QgsFeature feature
Rendered feature.
Contains information relating to a single PDF layer in the Geospatial PDF export.