19#include <cpl_string.h>
28#include <QDomDocument>
31#include <QMutexLocker>
39 GDALDriverH hDriverMem = GDALGetDriverByName(
"PDF" );
45 const char *pHavePoppler = GDALGetMetadataItem( hDriverMem,
"HAVE_POPPLER",
nullptr );
46 if ( pHavePoppler && strstr( pHavePoppler,
"YES" ) )
49 const char *pHavePdfium = GDALGetMetadataItem( hDriverMem,
"HAVE_PDFIUM",
nullptr );
50 if ( pHavePdfium && strstr( pHavePdfium,
"YES" ) )
59 GDALDriverH hDriverMem = GDALGetDriverByName(
"PDF" );
62 return QObject::tr(
"No GDAL PDF driver available." );
65 const char *pHavePoppler = GDALGetMetadataItem( hDriverMem,
"HAVE_POPPLER",
nullptr );
66 if ( pHavePoppler && strstr( pHavePoppler,
"YES" ) )
69 const char *pHavePdfium = GDALGetMetadataItem( hDriverMem,
"HAVE_PDFIUM",
nullptr );
70 if ( pHavePdfium && strstr( pHavePdfium,
"YES" ) )
73 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." );
78 QgsDebugError( QStringLiteral(
"GDAL PDF creation error: %1 " ).arg( msg ) );
79 if ( QStringList *errorList =
static_cast< QStringList *
>( CPLGetErrorHandlerUserData() ) )
81 errorList->append( QString( msg ) );
90 const QString composition = createCompositionXml( components, details );
92 if ( composition.isEmpty() )
96 GDALDriverH driver = GDALGetDriverByName(
"PDF" );
99 mErrorMessage = QObject::tr(
"Cannot load GDAL PDF driver" );
104 QFile file( xmlFilePath );
105 if ( file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
107 QTextStream out( &file );
108#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
109 out.setCodec(
"UTF-8" );
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() ? QStringLiteral(
"\n" ) : QString() ) + error;
147 mErrorMessage = QObject::tr(
"Could not create PDF file, but no error details are available" );
150 outputDataset.reset();
152 CSLDestroy( papszOptions );
166 case QPainter::CompositionMode_SourceOver:
167 case QPainter::CompositionMode_Multiply:
168 case QPainter::CompositionMode_Screen:
169 case QPainter::CompositionMode_Overlay:
170 case QPainter::CompositionMode_Darken:
171 case QPainter::CompositionMode_Lighten:
172 case QPainter::CompositionMode_ColorDodge:
173 case QPainter::CompositionMode_ColorBurn:
174 case QPainter::CompositionMode_HardLight:
175 case QPainter::CompositionMode_SoftLight:
176 case QPainter::CompositionMode_Difference:
177 case QPainter::CompositionMode_Exclusion:
190 QMutexLocker locker( &mMutex );
195 mCollatedFeatures[ group ][ layerId ].append( f );
198bool QgsAbstractGeospatialPdfExporter::saveTemporaryLayers()
200 for (
auto groupIt = mCollatedFeatures.constBegin(); groupIt != mCollatedFeatures.constEnd(); ++groupIt )
202 for (
auto it = groupIt->constBegin(); it != groupIt->constEnd(); ++it )
206 VectorComponentDetail detail = componentDetailForLayerId( it.key() );
207 detail.sourceVectorPath = filePath;
208 detail.group = groupIt.key();
214 saveOptions.
driverName = QStringLiteral(
"GPKG" );
217 if ( writer->hasError() )
219 mErrorMessage = writer->errorMessage();
223 for (
const QgsFeature &feature : features )
225 QgsFeature f = feature;
228 mErrorMessage = writer->errorMessage();
233 detail.sourceVectorLayer = layerName;
234 mVectorComponents << detail;
244 bool initiallyVisible =
false;
246 QString mutuallyExclusiveGroupId;
248 std::vector< std::unique_ptr< TreeNode > > children;
249 TreeNode *parent =
nullptr;
251 void addChild( std::unique_ptr< TreeNode > child )
253 child->parent =
this;
254 children.emplace_back( std::move( child ) );
257 QDomElement toElement( QDomDocument &doc )
const
259 QDomElement layerElement = doc.createElement( QStringLiteral(
"Layer" ) );
260 layerElement.setAttribute( QStringLiteral(
"id" ),
id );
261 layerElement.setAttribute( QStringLiteral(
"name" ), name );
262 layerElement.setAttribute( QStringLiteral(
"initiallyVisible" ), initiallyVisible ? QStringLiteral(
"true" ) : QStringLiteral(
"false" ) );
263 if ( !mutuallyExclusiveGroupId.isEmpty() )
264 layerElement.setAttribute( QStringLiteral(
"mutuallyExclusiveGroupId" ), mutuallyExclusiveGroupId );
266 for (
const auto &child : children )
268 layerElement.appendChild( child->toElement( doc ) );
274 QDomElement createIfLayerOnElement( QDomDocument &doc, QDomElement &contentElement )
const
276 QDomElement element = doc.createElement( QStringLiteral(
"IfLayerOn" ) );
277 element.setAttribute( QStringLiteral(
"layerId" ),
id );
278 contentElement.appendChild( element );
282 QDomElement createNestedIfLayerOnElements( QDomDocument &doc, QDomElement &contentElement )
const
284 TreeNode *currentParent = parent;
285 QDomElement finalElement = doc.createElement( QStringLiteral(
"IfLayerOn" ) );
286 finalElement.setAttribute( QStringLiteral(
"layerId" ),
id );
288 QDomElement currentElement = finalElement;
289 while ( currentParent )
291 QDomElement ifGroupOn = doc.createElement( QStringLiteral(
"IfLayerOn" ) );
292 ifGroupOn.setAttribute( QStringLiteral(
"layerId" ), currentParent->id );
293 ifGroupOn.appendChild( currentElement );
294 currentElement = ifGroupOn;
295 currentParent = currentParent->parent;
297 contentElement.appendChild( currentElement );
303QString QgsAbstractGeospatialPdfExporter::createCompositionXml(
const QList<ComponentLayerDetail> &components,
const ExportDetails &details )
307 QDomElement compositionElem = doc.createElement( QStringLiteral(
"PDFComposition" ) );
310 QDomElement metadata = doc.createElement( QStringLiteral(
"Metadata" ) );
311 if ( !details.author.isEmpty() )
313 QDomElement author = doc.createElement( QStringLiteral(
"Author" ) );
314 author.appendChild( doc.createTextNode( details.author ) );
315 metadata.appendChild( author );
317 if ( !details.producer.isEmpty() )
319 QDomElement producer = doc.createElement( QStringLiteral(
"Producer" ) );
320 producer.appendChild( doc.createTextNode( details.producer ) );
321 metadata.appendChild( producer );
323 if ( !details.creator.isEmpty() )
325 QDomElement creator = doc.createElement( QStringLiteral(
"Creator" ) );
326 creator.appendChild( doc.createTextNode( details.creator ) );
327 metadata.appendChild( creator );
329 if ( details.creationDateTime.isValid() )
331 QDomElement creationDate = doc.createElement( QStringLiteral(
"CreationDate" ) );
332 QString creationDateString = QStringLiteral(
"D:%1" ).arg( details.creationDateTime.toString( QStringLiteral(
"yyyyMMddHHmmss" ) ) );
333 if ( details.creationDateTime.timeZone().isValid() )
335 int offsetFromUtc = details.creationDateTime.timeZone().offsetFromUtc( details.creationDateTime );
336 creationDateString += ( offsetFromUtc >= 0 ) ?
'+' :
'-';
337 offsetFromUtc = std::abs( offsetFromUtc );
338 int offsetHours = offsetFromUtc / 3600;
339 int offsetMins = ( offsetFromUtc % 3600 ) / 60;
340 creationDateString += QStringLiteral(
"%1'%2'" ).arg( offsetHours ).arg( offsetMins );
342 creationDate.appendChild( doc.createTextNode( creationDateString ) );
343 metadata.appendChild( creationDate );
345 if ( !details.subject.isEmpty() )
347 QDomElement subject = doc.createElement( QStringLiteral(
"Subject" ) );
348 subject.appendChild( doc.createTextNode( details.subject ) );
349 metadata.appendChild( subject );
351 if ( !details.title.isEmpty() )
353 QDomElement title = doc.createElement( QStringLiteral(
"Title" ) );
354 title.appendChild( doc.createTextNode( details.title ) );
355 metadata.appendChild( title );
357 if ( !details.keywords.empty() )
359 QStringList allKeywords;
360 for (
auto it = details.keywords.constBegin(); it != details.keywords.constEnd(); ++it )
362 allKeywords.append( QStringLiteral(
"%1: %2" ).arg( it.key(), it.value().join(
',' ) ) );
364 QDomElement keywords = doc.createElement( QStringLiteral(
"Keywords" ) );
365 keywords.appendChild( doc.createTextNode( allKeywords.join(
';' ) ) );
366 metadata.appendChild( keywords );
368 compositionElem.appendChild( metadata );
370 QSet< QString > createdLayerIds;
371 std::vector< std::unique_ptr< TreeNode > > rootGroups;
372 std::vector< std::unique_ptr< TreeNode > > rootLayers;
373 QMap< QString, TreeNode * > groupNameMap;
375 QStringList layerTreeGroupOrder = details.layerTreeGroupOrder;
379 for (
auto it = details.customLayerTreeGroups.constBegin(); it != details.customLayerTreeGroups.constEnd(); ++it )
381 if ( layerTreeGroupOrder.contains( it.value() ) )
383 layerTreeGroupOrder.append( it.value() );
387 if ( details.includeFeatures )
391 if ( !component.group.isEmpty() && !layerTreeGroupOrder.contains( component.group ) )
393 layerTreeGroupOrder.append( component.group );
401 if ( !component.group.isEmpty() && !layerTreeGroupOrder.contains( component.group ) )
403 layerTreeGroupOrder.append( component.group );
407 QMap< QString, TreeNode * > groupNameToTreeNode;
408 QMap< QString, TreeNode * > layerIdToTreeNode;
410 auto createGroup = [&details, &groupNameToTreeNode](
const QString & groupName ) -> std::unique_ptr< TreeNode >
412 auto group = std::make_unique< TreeNode >();
413 const QString
id = QUuid::createUuid().toString();
415 groupNameToTreeNode[ groupName ] = group.get();
417 group->name = groupName;
418 group->initiallyVisible =
true;
419 if ( details.mutuallyExclusiveGroups.contains( groupName ) )
420 group->mutuallyExclusiveGroupId = QStringLiteral(
"__mutually_exclusive_groups__" );
424 if ( details.includeFeatures )
428 const QString destinationGroup = details.customLayerTreeGroups.value( component.mapLayerId, component.group );
430 auto layer = std::make_unique< TreeNode >();
431 layer->id = destinationGroup.isEmpty() ? component.mapLayerId : QStringLiteral(
"%1_%2" ).arg( destinationGroup, component.mapLayerId );
432 layer->name = details.layerIdToPdfLayerTreeNameMap.contains( component.mapLayerId ) ? details.layerIdToPdfLayerTreeNameMap.value( component.mapLayerId ) : component.name;
433 layer->initiallyVisible = details.initialLayerVisibility.value( component.mapLayerId,
true );
434 layer->mapLayerId = component.mapLayerId;
436 layerIdToTreeNode.insert( component.mapLayerId, layer.get() );
437 if ( !destinationGroup.isEmpty() )
439 if ( TreeNode *groupNode = groupNameMap.value( destinationGroup ) )
441 groupNode->addChild( std::move( layer ) );
445 std::unique_ptr< TreeNode > group = createGroup( destinationGroup );
446 group->addChild( std::move( layer ) );
447 groupNameMap.insert( destinationGroup, group.get() );
448 rootGroups.emplace_back( std::move( group ) );
453 rootLayers.emplace_back( std::move( layer ) );
456 createdLayerIds.insert( component.mapLayerId );
466 if ( !component.mapLayerId.isEmpty() && createdLayerIds.contains( component.mapLayerId ) )
469 const QString destinationGroup = details.customLayerTreeGroups.value( component.mapLayerId, component.group );
470 if ( destinationGroup.isEmpty() && component.mapLayerId.isEmpty() )
473 std::unique_ptr< TreeNode > mapLayerNode;
474 if ( !component.mapLayerId.isEmpty() )
476 mapLayerNode = std::make_unique< TreeNode >();
477 mapLayerNode->id = destinationGroup.isEmpty() ? component.mapLayerId : QStringLiteral(
"%1_%2" ).arg( destinationGroup, component.mapLayerId );
478 mapLayerNode->name = details.layerIdToPdfLayerTreeNameMap.value( component.mapLayerId, component.name );
479 mapLayerNode->initiallyVisible = details.initialLayerVisibility.value( component.mapLayerId,
true );
480 mapLayerNode->mapLayerId = component.mapLayerId;
482 layerIdToTreeNode.insert( component.mapLayerId, mapLayerNode.get() );
485 if ( !destinationGroup.isEmpty() )
487 if ( TreeNode *groupNode = groupNameMap.value( destinationGroup ) )
490 groupNode->addChild( std::move( mapLayerNode ) );
494 std::unique_ptr< TreeNode > group = createGroup( destinationGroup );
496 group->addChild( std::move( mapLayerNode ) );
497 groupNameMap.insert( destinationGroup, group.get() );
498 rootGroups.emplace_back( std::move( group ) );
504 rootLayers.emplace_back( std::move( mapLayerNode ) );
507 if ( !component.mapLayerId.isEmpty() )
509 createdLayerIds.insert( component.mapLayerId );
514 QDomElement page = doc.createElement( QStringLiteral(
"Page" ) );
515 QDomElement dpi = doc.createElement( QStringLiteral(
"DPI" ) );
518 page.appendChild( dpi );
520 QDomElement width = doc.createElement( QStringLiteral(
"Width" ) );
521 const double pageWidthPdfUnits = std::ceil( details.pageSizeMm.width() / 25.4 * 72 );
522 width.appendChild( doc.createTextNode(
qgsDoubleToString( pageWidthPdfUnits ) ) );
523 page.appendChild( width );
524 QDomElement height = doc.createElement( QStringLiteral(
"Height" ) );
525 const double pageHeightPdfUnits = std::ceil( details.pageSizeMm.height() / 25.4 * 72 );
526 height.appendChild( doc.createTextNode(
qgsDoubleToString( pageHeightPdfUnits ) ) );
527 page.appendChild( height );
531 for (
const QgsAbstractGeospatialPdfExporter::GeoReferencedSection §ion : details.georeferencedSections )
533 QDomElement georeferencing = doc.createElement( QStringLiteral(
"Georeferencing" ) );
534 georeferencing.setAttribute( QStringLiteral(
"id" ), QStringLiteral(
"georeferenced_%1" ).arg( i++ ) );
535 georeferencing.setAttribute( QStringLiteral(
"ISO32000ExtensionFormat" ), details.useIso32000ExtensionFormatGeoreferencing ? QStringLiteral(
"true" ) : QStringLiteral(
"false" ) );
539 QDomElement srs = doc.createElement( QStringLiteral(
"SRS" ) );
542 if ( !section.
crs.
authid().isEmpty() && !section.
crs.
authid().startsWith( QStringLiteral(
"user" ), Qt::CaseInsensitive ) )
544 srs.appendChild( doc.createTextNode( section.
crs.
authid() ) );
550 georeferencing.appendChild( srs );
562 QDomElement boundingPolygon = doc.createElement( QStringLiteral(
"BoundingPolygon" ) );
565 QTransform t = QTransform::fromTranslate( 0, pageHeightPdfUnits ).scale( pageWidthPdfUnits / details.pageSizeMm.width(),
566 -pageHeightPdfUnits / details.pageSizeMm.height() );
570 boundingPolygon.appendChild( doc.createTextNode( p.
asWkt() ) );
572 georeferencing.appendChild( boundingPolygon );
581 QDomElement boundingBox = doc.createElement( QStringLiteral(
"BoundingBox" ) );
586 georeferencing.appendChild( boundingBox );
591 QDomElement cp1 = doc.createElement( QStringLiteral(
"ControlPoint" ) );
592 cp1.setAttribute( QStringLiteral(
"x" ),
qgsDoubleToString( point.pagePoint.x() / 25.4 * 72 ) );
593 cp1.setAttribute( QStringLiteral(
"y" ),
qgsDoubleToString( ( details.pageSizeMm.height() - point.pagePoint.y() ) / 25.4 * 72 ) );
594 cp1.setAttribute( QStringLiteral(
"GeoX" ),
qgsDoubleToString( point.geoPoint.x() ) );
595 cp1.setAttribute( QStringLiteral(
"GeoY" ),
qgsDoubleToString( point.geoPoint.y() ) );
596 georeferencing.appendChild( cp1 );
599 page.appendChild( georeferencing );
604 QDomElement pdfDataset = doc.createElement( QStringLiteral(
"PDF" ) );
605 pdfDataset.setAttribute( QStringLiteral(
"dataset" ), component.sourcePdfPath );
606 if ( component.opacity != 1.0 || component.compositionMode != QPainter::CompositionMode_SourceOver )
608 QDomElement blendingElement = doc.createElement( QStringLiteral(
"Blending" ) );
609 blendingElement.setAttribute( QStringLiteral(
"opacity" ), component.opacity );
610 blendingElement.setAttribute( QStringLiteral(
"function" ), compositionModeToString( component.compositionMode ) );
612 pdfDataset.appendChild( blendingElement );
618 QDomElement content = doc.createElement( QStringLiteral(
"Content" ) );
621 if ( component.mapLayerId.isEmpty() && component.group.isEmpty() )
623 content.appendChild( createPdfDatasetElement( component ) );
625 else if ( !component.mapLayerId.isEmpty() )
627 if ( TreeNode *treeNode = layerIdToTreeNode.value( component.mapLayerId ) )
629 QDomElement ifLayerOnElement = treeNode->createNestedIfLayerOnElements( doc, content );
630 ifLayerOnElement.appendChild( createPdfDatasetElement( component ) );
633 else if ( TreeNode *groupNode = groupNameToTreeNode.value( component.group ) )
635 QDomElement ifGroupOn = groupNode->createIfLayerOnElement( doc, content );
636 ifGroupOn.appendChild( createPdfDatasetElement( component ) );
641 if ( details.includeFeatures )
645 if ( TreeNode *treeNode = layerIdToTreeNode.value( component.mapLayerId ) )
647 QDomElement ifLayerOnElement = treeNode->createNestedIfLayerOnElements( doc, content );
649 QDomElement vectorDataset = doc.createElement( QStringLiteral(
"Vector" ) );
650 vectorDataset.setAttribute( QStringLiteral(
"dataset" ), component.sourceVectorPath );
651 vectorDataset.setAttribute( QStringLiteral(
"layer" ), component.sourceVectorLayer );
652 vectorDataset.setAttribute( QStringLiteral(
"visible" ), QStringLiteral(
"false" ) );
653 QDomElement logicalStructure = doc.createElement( QStringLiteral(
"LogicalStructure" ) );
654 logicalStructure.setAttribute( QStringLiteral(
"displayLayerName" ), component.name );
655 if ( !component.displayAttribute.isEmpty() )
656 logicalStructure.setAttribute( QStringLiteral(
"fieldToDisplay" ), component.displayAttribute );
657 vectorDataset.appendChild( logicalStructure );
658 ifLayerOnElement.appendChild( vectorDataset );
663 page.appendChild( content );
666 QDomElement
layerTree = doc.createElement( QStringLiteral(
"LayerTree" ) );
672 std::sort( rootGroups.begin(), rootGroups.end(), [&layerTreeGroupOrder](
const std::unique_ptr< TreeNode > &a,
const std::unique_ptr< TreeNode > &b ) ->
bool
674 return layerTreeGroupOrder.indexOf( a->name ) < layerTreeGroupOrder.indexOf( b->name );
677 bool haveFoundMutuallyExclusiveGroup =
false;
678 for (
const auto &node : std::as_const( rootGroups ) )
680 if ( !node->mutuallyExclusiveGroupId.isEmpty() )
683 node->initiallyVisible = !haveFoundMutuallyExclusiveGroup;
684 haveFoundMutuallyExclusiveGroup =
true;
686 layerTree.appendChild( node->toElement( doc ) );
690 layerTreeGroupOrder.erase( std::remove_if( layerTreeGroupOrder.begin(), layerTreeGroupOrder.end(), [&details](
const QString & group )
692 return details.customLayerTreeGroups.key( group ).isEmpty();
693 } ), layerTreeGroupOrder.end() );
697 std::sort( rootLayers.begin(), rootLayers.end(), [&details](
const std::unique_ptr< TreeNode > &a,
const std::unique_ptr< TreeNode > &b ) ->
bool
699 const int indexA = details.layerOrder.indexOf( a->mapLayerId );
700 const int indexB = details.layerOrder.indexOf( b->mapLayerId );
702 if ( indexA >= 0 && indexB >= 0 )
703 return indexA < indexB;
704 else if ( indexA >= 0 )
706 else if ( indexB >= 0 )
709 return a->name.localeAwareCompare( b->name ) < 0;
712 for (
const auto &node : std::as_const( rootLayers ) )
714 layerTree.appendChild( node->toElement( doc ) );
717 compositionElem.appendChild( layerTree );
718 compositionElem.appendChild( page );
720 doc.appendChild( compositionElem );
723 QTextStream stream( &composition );
724 doc.save( stream, -1 );
729QString QgsAbstractGeospatialPdfExporter::compositionModeToString( QPainter::CompositionMode mode )
733 case QPainter::CompositionMode_SourceOver:
734 return QStringLiteral(
"Normal" );
736 case QPainter::CompositionMode_Multiply:
737 return QStringLiteral(
"Multiply" );
739 case QPainter::CompositionMode_Screen:
740 return QStringLiteral(
"Screen" );
742 case QPainter::CompositionMode_Overlay:
743 return QStringLiteral(
"Overlay" );
745 case QPainter::CompositionMode_Darken:
746 return QStringLiteral(
"Darken" );
748 case QPainter::CompositionMode_Lighten:
749 return QStringLiteral(
"Lighten" );
751 case QPainter::CompositionMode_ColorDodge:
752 return QStringLiteral(
"ColorDodge" );
754 case QPainter::CompositionMode_ColorBurn:
755 return QStringLiteral(
"ColorBurn" );
757 case QPainter::CompositionMode_HardLight:
758 return QStringLiteral(
"HardLight" );
760 case QPainter::CompositionMode_SoftLight:
761 return QStringLiteral(
"SoftLight" );
763 case QPainter::CompositionMode_Difference:
764 return QStringLiteral(
"Difference" );
766 case QPainter::CompositionMode_Exclusion:
767 return QStringLiteral(
"Exclusion" );
773 QgsDebugError( QStringLiteral(
"Unsupported PDF blend mode %1" ).arg( mode ) );
774 return QStringLiteral(
"Normal" );
@ 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.
static QgsVectorFileWriter * create(const QString &fileName, const QgsFields &fields, Qgis::WkbType geometryType, const QgsCoordinateReferenceSystem &srs, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QgsFeatureSink::SinkFlags sinkFlags=QgsFeatureSink::SinkFlags(), QString *newFilename=nullptr, QString *newLayer=nullptr)
Create a new vector file writer.
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.