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 QT_FEATURE_timezone > 0
334 if ( details.creationDateTime.timeZone().isValid() )
336 int offsetFromUtc = details.creationDateTime.timeZone().offsetFromUtc( details.creationDateTime );
337 creationDateString += ( offsetFromUtc >= 0 ) ?
'+' :
'-';
338 offsetFromUtc = std::abs( offsetFromUtc );
339 int offsetHours = offsetFromUtc / 3600;
340 int offsetMins = ( offsetFromUtc % 3600 ) / 60;
341 creationDateString += QStringLiteral(
"%1'%2'" ).arg( offsetHours ).arg( offsetMins );
344 QgsDebugError( QStringLiteral(
"Qt is built without timezone support, skipping timezone for pdf export" ) );
346 creationDate.appendChild( doc.createTextNode( creationDateString ) );
347 metadata.appendChild( creationDate );
349 if ( !details.subject.isEmpty() )
351 QDomElement subject = doc.createElement( QStringLiteral(
"Subject" ) );
352 subject.appendChild( doc.createTextNode( details.subject ) );
353 metadata.appendChild( subject );
355 if ( !details.title.isEmpty() )
357 QDomElement title = doc.createElement( QStringLiteral(
"Title" ) );
358 title.appendChild( doc.createTextNode( details.title ) );
359 metadata.appendChild( title );
361 if ( !details.keywords.empty() )
363 QStringList allKeywords;
364 for (
auto it = details.keywords.constBegin(); it != details.keywords.constEnd(); ++it )
366 allKeywords.append( QStringLiteral(
"%1: %2" ).arg( it.key(), it.value().join(
',' ) ) );
368 QDomElement keywords = doc.createElement( QStringLiteral(
"Keywords" ) );
369 keywords.appendChild( doc.createTextNode( allKeywords.join(
';' ) ) );
370 metadata.appendChild( keywords );
372 compositionElem.appendChild( metadata );
374 QSet< QString > createdLayerIds;
375 std::vector< std::unique_ptr< TreeNode > > rootGroups;
376 std::vector< std::unique_ptr< TreeNode > > rootLayers;
377 QMap< QString, TreeNode * > groupNameMap;
379 QStringList layerTreeGroupOrder = details.layerTreeGroupOrder;
383 for (
auto it = details.customLayerTreeGroups.constBegin(); it != details.customLayerTreeGroups.constEnd(); ++it )
385 if ( layerTreeGroupOrder.contains( it.value() ) )
387 layerTreeGroupOrder.append( it.value() );
391 if ( details.includeFeatures )
395 if ( !component.group.isEmpty() && !layerTreeGroupOrder.contains( component.group ) )
397 layerTreeGroupOrder.append( component.group );
405 if ( !component.group.isEmpty() && !layerTreeGroupOrder.contains( component.group ) )
407 layerTreeGroupOrder.append( component.group );
411 QMap< QString, TreeNode * > groupNameToTreeNode;
412 QMap< QString, TreeNode * > layerIdToTreeNode;
414 auto createGroup = [&details, &groupNameToTreeNode](
const QString & groupName ) -> std::unique_ptr< TreeNode >
416 auto group = std::make_unique< TreeNode >();
417 const QString
id = QUuid::createUuid().toString();
419 groupNameToTreeNode[ groupName ] = group.get();
421 group->name = groupName;
422 group->initiallyVisible =
true;
423 if ( details.mutuallyExclusiveGroups.contains( groupName ) )
424 group->mutuallyExclusiveGroupId = QStringLiteral(
"__mutually_exclusive_groups__" );
428 if ( details.includeFeatures )
432 const QString destinationGroup = details.customLayerTreeGroups.value( component.mapLayerId, component.group );
434 auto layer = std::make_unique< TreeNode >();
435 layer->id = destinationGroup.isEmpty() ? component.mapLayerId : QStringLiteral(
"%1_%2" ).arg( destinationGroup, component.mapLayerId );
436 layer->name = details.layerIdToPdfLayerTreeNameMap.contains( component.mapLayerId ) ? details.layerIdToPdfLayerTreeNameMap.value( component.mapLayerId ) : component.name;
437 layer->initiallyVisible = details.initialLayerVisibility.value( component.mapLayerId,
true );
438 layer->mapLayerId = component.mapLayerId;
440 layerIdToTreeNode.insert( component.mapLayerId, layer.get() );
441 if ( !destinationGroup.isEmpty() )
443 if ( TreeNode *groupNode = groupNameMap.value( destinationGroup ) )
445 groupNode->addChild( std::move( layer ) );
449 std::unique_ptr< TreeNode > group = createGroup( destinationGroup );
450 group->addChild( std::move( layer ) );
451 groupNameMap.insert( destinationGroup, group.get() );
452 rootGroups.emplace_back( std::move( group ) );
457 rootLayers.emplace_back( std::move( layer ) );
460 createdLayerIds.insert( component.mapLayerId );
470 if ( !component.mapLayerId.isEmpty() && createdLayerIds.contains( component.mapLayerId ) )
473 const QString destinationGroup = details.customLayerTreeGroups.value( component.mapLayerId, component.group );
474 if ( destinationGroup.isEmpty() && component.mapLayerId.isEmpty() )
477 std::unique_ptr< TreeNode > mapLayerNode;
478 if ( !component.mapLayerId.isEmpty() )
480 mapLayerNode = std::make_unique< TreeNode >();
481 mapLayerNode->id = destinationGroup.isEmpty() ? component.mapLayerId : QStringLiteral(
"%1_%2" ).arg( destinationGroup, component.mapLayerId );
482 mapLayerNode->name = details.layerIdToPdfLayerTreeNameMap.value( component.mapLayerId, component.name );
483 mapLayerNode->initiallyVisible = details.initialLayerVisibility.value( component.mapLayerId,
true );
484 mapLayerNode->mapLayerId = component.mapLayerId;
486 layerIdToTreeNode.insert( component.mapLayerId, mapLayerNode.get() );
489 if ( !destinationGroup.isEmpty() )
491 if ( TreeNode *groupNode = groupNameMap.value( destinationGroup ) )
494 groupNode->addChild( std::move( mapLayerNode ) );
498 std::unique_ptr< TreeNode > group = createGroup( destinationGroup );
500 group->addChild( std::move( mapLayerNode ) );
501 groupNameMap.insert( destinationGroup, group.get() );
502 rootGroups.emplace_back( std::move( group ) );
508 rootLayers.emplace_back( std::move( mapLayerNode ) );
511 if ( !component.mapLayerId.isEmpty() )
513 createdLayerIds.insert( component.mapLayerId );
518 QDomElement page = doc.createElement( QStringLiteral(
"Page" ) );
519 QDomElement dpi = doc.createElement( QStringLiteral(
"DPI" ) );
522 page.appendChild( dpi );
524 QDomElement width = doc.createElement( QStringLiteral(
"Width" ) );
525 const double pageWidthPdfUnits = std::ceil( details.pageSizeMm.width() / 25.4 * 72 );
526 width.appendChild( doc.createTextNode(
qgsDoubleToString( pageWidthPdfUnits ) ) );
527 page.appendChild( width );
528 QDomElement height = doc.createElement( QStringLiteral(
"Height" ) );
529 const double pageHeightPdfUnits = std::ceil( details.pageSizeMm.height() / 25.4 * 72 );
530 height.appendChild( doc.createTextNode(
qgsDoubleToString( pageHeightPdfUnits ) ) );
531 page.appendChild( height );
535 for (
const QgsAbstractGeospatialPdfExporter::GeoReferencedSection §ion : details.georeferencedSections )
537 QDomElement georeferencing = doc.createElement( QStringLiteral(
"Georeferencing" ) );
538 georeferencing.setAttribute( QStringLiteral(
"id" ), QStringLiteral(
"georeferenced_%1" ).arg( i++ ) );
539 georeferencing.setAttribute( QStringLiteral(
"ISO32000ExtensionFormat" ), details.useIso32000ExtensionFormatGeoreferencing ? QStringLiteral(
"true" ) : QStringLiteral(
"false" ) );
543 QDomElement srs = doc.createElement( QStringLiteral(
"SRS" ) );
546 if ( !section.
crs.
authid().isEmpty() && !section.
crs.
authid().startsWith( QStringLiteral(
"user" ), Qt::CaseInsensitive ) )
548 srs.appendChild( doc.createTextNode( section.
crs.
authid() ) );
554 georeferencing.appendChild( srs );
566 QDomElement boundingPolygon = doc.createElement( QStringLiteral(
"BoundingPolygon" ) );
569 QTransform t = QTransform::fromTranslate( 0, pageHeightPdfUnits ).scale( pageWidthPdfUnits / details.pageSizeMm.width(),
570 -pageHeightPdfUnits / details.pageSizeMm.height() );
574 boundingPolygon.appendChild( doc.createTextNode( p.
asWkt() ) );
576 georeferencing.appendChild( boundingPolygon );
585 QDomElement boundingBox = doc.createElement( QStringLiteral(
"BoundingBox" ) );
590 georeferencing.appendChild( boundingBox );
595 QDomElement cp1 = doc.createElement( QStringLiteral(
"ControlPoint" ) );
596 cp1.setAttribute( QStringLiteral(
"x" ),
qgsDoubleToString( point.pagePoint.x() / 25.4 * 72 ) );
597 cp1.setAttribute( QStringLiteral(
"y" ),
qgsDoubleToString( ( details.pageSizeMm.height() - point.pagePoint.y() ) / 25.4 * 72 ) );
598 cp1.setAttribute( QStringLiteral(
"GeoX" ),
qgsDoubleToString( point.geoPoint.x() ) );
599 cp1.setAttribute( QStringLiteral(
"GeoY" ),
qgsDoubleToString( point.geoPoint.y() ) );
600 georeferencing.appendChild( cp1 );
603 page.appendChild( georeferencing );
608 QDomElement pdfDataset = doc.createElement( QStringLiteral(
"PDF" ) );
609 pdfDataset.setAttribute( QStringLiteral(
"dataset" ), component.sourcePdfPath );
610 if ( component.opacity != 1.0 || component.compositionMode != QPainter::CompositionMode_SourceOver )
612 QDomElement blendingElement = doc.createElement( QStringLiteral(
"Blending" ) );
613 blendingElement.setAttribute( QStringLiteral(
"opacity" ), component.opacity );
614 blendingElement.setAttribute( QStringLiteral(
"function" ), compositionModeToString( component.compositionMode ) );
616 pdfDataset.appendChild( blendingElement );
622 QDomElement content = doc.createElement( QStringLiteral(
"Content" ) );
625 if ( component.mapLayerId.isEmpty() && component.group.isEmpty() )
627 content.appendChild( createPdfDatasetElement( component ) );
629 else if ( !component.mapLayerId.isEmpty() )
631 if ( TreeNode *treeNode = layerIdToTreeNode.value( component.mapLayerId ) )
633 QDomElement ifLayerOnElement = treeNode->createNestedIfLayerOnElements( doc, content );
634 ifLayerOnElement.appendChild( createPdfDatasetElement( component ) );
637 else if ( TreeNode *groupNode = groupNameToTreeNode.value( component.group ) )
639 QDomElement ifGroupOn = groupNode->createIfLayerOnElement( doc, content );
640 ifGroupOn.appendChild( createPdfDatasetElement( component ) );
645 if ( details.includeFeatures )
649 if ( TreeNode *treeNode = layerIdToTreeNode.value( component.mapLayerId ) )
651 QDomElement ifLayerOnElement = treeNode->createNestedIfLayerOnElements( doc, content );
653 QDomElement vectorDataset = doc.createElement( QStringLiteral(
"Vector" ) );
654 vectorDataset.setAttribute( QStringLiteral(
"dataset" ), component.sourceVectorPath );
655 vectorDataset.setAttribute( QStringLiteral(
"layer" ), component.sourceVectorLayer );
656 vectorDataset.setAttribute( QStringLiteral(
"visible" ), QStringLiteral(
"false" ) );
657 QDomElement logicalStructure = doc.createElement( QStringLiteral(
"LogicalStructure" ) );
658 logicalStructure.setAttribute( QStringLiteral(
"displayLayerName" ), component.name );
659 if ( !component.displayAttribute.isEmpty() )
660 logicalStructure.setAttribute( QStringLiteral(
"fieldToDisplay" ), component.displayAttribute );
661 vectorDataset.appendChild( logicalStructure );
662 ifLayerOnElement.appendChild( vectorDataset );
667 page.appendChild( content );
670 QDomElement
layerTree = doc.createElement( QStringLiteral(
"LayerTree" ) );
676 std::sort( rootGroups.begin(), rootGroups.end(), [&layerTreeGroupOrder](
const std::unique_ptr< TreeNode > &a,
const std::unique_ptr< TreeNode > &b ) ->
bool
678 return layerTreeGroupOrder.indexOf( a->name ) < layerTreeGroupOrder.indexOf( b->name );
681 bool haveFoundMutuallyExclusiveGroup =
false;
682 for (
const auto &node : std::as_const( rootGroups ) )
684 if ( !node->mutuallyExclusiveGroupId.isEmpty() )
687 node->initiallyVisible = !haveFoundMutuallyExclusiveGroup;
688 haveFoundMutuallyExclusiveGroup =
true;
690 layerTree.appendChild( node->toElement( doc ) );
694 layerTreeGroupOrder.erase( std::remove_if( layerTreeGroupOrder.begin(), layerTreeGroupOrder.end(), [&details](
const QString & group )
696 return details.customLayerTreeGroups.key( group ).isEmpty();
697 } ), layerTreeGroupOrder.end() );
701 std::sort( rootLayers.begin(), rootLayers.end(), [&details](
const std::unique_ptr< TreeNode > &a,
const std::unique_ptr< TreeNode > &b ) ->
bool
703 const int indexA = details.layerOrder.indexOf( a->mapLayerId );
704 const int indexB = details.layerOrder.indexOf( b->mapLayerId );
706 if ( indexA >= 0 && indexB >= 0 )
707 return indexA < indexB;
708 else if ( indexA >= 0 )
710 else if ( indexB >= 0 )
713 return a->name.localeAwareCompare( b->name ) < 0;
716 for (
const auto &node : std::as_const( rootLayers ) )
718 layerTree.appendChild( node->toElement( doc ) );
721 compositionElem.appendChild( layerTree );
722 compositionElem.appendChild( page );
724 doc.appendChild( compositionElem );
727 QTextStream stream( &composition );
728 doc.save( stream, -1 );
733QString QgsAbstractGeospatialPdfExporter::compositionModeToString( QPainter::CompositionMode mode )
737 case QPainter::CompositionMode_SourceOver:
738 return QStringLiteral(
"Normal" );
740 case QPainter::CompositionMode_Multiply:
741 return QStringLiteral(
"Multiply" );
743 case QPainter::CompositionMode_Screen:
744 return QStringLiteral(
"Screen" );
746 case QPainter::CompositionMode_Overlay:
747 return QStringLiteral(
"Overlay" );
749 case QPainter::CompositionMode_Darken:
750 return QStringLiteral(
"Darken" );
752 case QPainter::CompositionMode_Lighten:
753 return QStringLiteral(
"Lighten" );
755 case QPainter::CompositionMode_ColorDodge:
756 return QStringLiteral(
"ColorDodge" );
758 case QPainter::CompositionMode_ColorBurn:
759 return QStringLiteral(
"ColorBurn" );
761 case QPainter::CompositionMode_HardLight:
762 return QStringLiteral(
"HardLight" );
764 case QPainter::CompositionMode_SoftLight:
765 return QStringLiteral(
"SoftLight" );
767 case QPainter::CompositionMode_Difference:
768 return QStringLiteral(
"Difference" );
770 case QPainter::CompositionMode_Exclusion:
771 return QStringLiteral(
"Exclusion" );
777 QgsDebugError( QStringLiteral(
"Unsupported PDF blend mode %1" ).arg( mode ) );
778 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.