28 #include "cpl_string.h"
31 #include <QMutexLocker>
32 #include <QDomDocument>
33 #include <QDomElement>
36 #include <QTextStream>
40 #if GDAL_VERSION_NUM < GDAL_COMPUTE_VERSION(3,0,0)
45 GDALDriverH hDriverMem = GDALGetDriverByName(
"PDF" );
51 const char *pHavePoppler = GDALGetMetadataItem( hDriverMem,
"HAVE_POPPLER",
nullptr );
52 if ( pHavePoppler && strstr( pHavePoppler,
"YES" ) )
55 const char *pHavePdfium = GDALGetMetadataItem( hDriverMem,
"HAVE_PDFIUM",
nullptr );
56 if ( pHavePdfium && strstr( pHavePdfium,
"YES" ) )
65 #if GDAL_VERSION_NUM < GDAL_COMPUTE_VERSION(3,0,0)
66 return QObject::tr(
"GeoPDF creation requires GDAL version 3.0 or later." );
69 GDALDriverH hDriverMem = GDALGetDriverByName(
"PDF" );
72 return QObject::tr(
"No GDAL PDF driver available." );
75 const char *pHavePoppler = GDALGetMetadataItem( hDriverMem,
"HAVE_POPPLER",
nullptr );
76 if ( pHavePoppler && strstr( pHavePoppler,
"YES" ) )
79 const char *pHavePdfium = GDALGetMetadataItem( hDriverMem,
"HAVE_PDFIUM",
nullptr );
80 if ( pHavePdfium && strstr( pHavePdfium,
"YES" ) )
83 return QObject::tr(
"GDAL PDF driver was not built with PDF read support. A build with PDF read support is required for GeoPDF creation." );
92 #if GDAL_VERSION_NUM < GDAL_COMPUTE_VERSION(3,0,0)
93 Q_UNUSED( components )
94 Q_UNUSED( destinationFile )
97 const QString composition = createCompositionXml( components, details );
99 if ( composition.isEmpty() )
103 GDALDriverH driver = GDALGetDriverByName(
"PDF" );
106 mErrorMessage = QObject::tr(
"Cannot load GDAL PDF driver" );
111 QFile file( xmlFilePath );
112 if ( file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
114 QTextStream out( &file );
115 out.setCodec(
"UTF-8" );
120 mErrorMessage = QObject::tr(
"Could not create GeoPDF composition file" );
124 char **papszOptions = CSLSetNameValue(
nullptr,
"COMPOSITION_FILE", xmlFilePath.toUtf8().constData() );
127 gdal::dataset_unique_ptr outputDataset( GDALCreate( driver, destinationFile.toUtf8().constData(), 0, 0, 0, GDT_Unknown, papszOptions ) );
128 bool res = outputDataset.get();
129 outputDataset.reset();
131 CSLDestroy( papszOptions );
139 return mTemporaryDir.filePath( filename );
146 case QPainter::CompositionMode_SourceOver:
147 case QPainter::CompositionMode_Multiply:
148 case QPainter::CompositionMode_Screen:
149 case QPainter::CompositionMode_Overlay:
150 case QPainter::CompositionMode_Darken:
151 case QPainter::CompositionMode_Lighten:
152 case QPainter::CompositionMode_ColorDodge:
153 case QPainter::CompositionMode_ColorBurn:
154 case QPainter::CompositionMode_HardLight:
155 case QPainter::CompositionMode_SoftLight:
156 case QPainter::CompositionMode_Difference:
157 case QPainter::CompositionMode_Exclusion:
170 QMutexLocker locker( &mMutex );
175 mCollatedFeatures[ group ][ layerId ].append( f );
178 bool QgsAbstractGeoPdfExporter::saveTemporaryLayers()
180 for (
auto groupIt = mCollatedFeatures.constBegin(); groupIt != mCollatedFeatures.constEnd(); ++groupIt )
182 for (
auto it = groupIt->constBegin(); it != groupIt->constEnd(); ++it )
186 VectorComponentDetail detail = componentDetailForLayerId( it.key() );
187 detail.sourceVectorPath = filePath;
188 detail.group = groupIt.key();
194 saveOptions.
driverName = QStringLiteral(
"GPKG" );
197 if ( writer->hasError() )
199 mErrorMessage = writer->errorMessage();
208 mErrorMessage = writer->errorMessage();
213 detail.sourceVectorLayer = layerName;
214 mVectorComponents << detail;
220 QString QgsAbstractGeoPdfExporter::createCompositionXml(
const QList<ComponentLayerDetail> &components,
const ExportDetails &details )
224 QDomElement compositionElem = doc.createElement( QStringLiteral(
"PDFComposition" ) );
227 QDomElement metadata = doc.createElement( QStringLiteral(
"Metadata" ) );
228 if ( !details.author.isEmpty() )
230 QDomElement author = doc.createElement( QStringLiteral(
"Author" ) );
231 author.appendChild( doc.createTextNode( details.author ) );
232 metadata.appendChild( author );
234 if ( !details.producer.isEmpty() )
236 QDomElement producer = doc.createElement( QStringLiteral(
"Producer" ) );
237 producer.appendChild( doc.createTextNode( details.producer ) );
238 metadata.appendChild( producer );
240 if ( !details.creator.isEmpty() )
242 QDomElement creator = doc.createElement( QStringLiteral(
"Creator" ) );
243 creator.appendChild( doc.createTextNode( details.creator ) );
244 metadata.appendChild( creator );
246 if ( details.creationDateTime.isValid() )
248 QDomElement creationDate = doc.createElement( QStringLiteral(
"CreationDate" ) );
249 QString creationDateString = QStringLiteral(
"D:%1" ).arg( details.creationDateTime.toString( QStringLiteral(
"yyyyMMddHHmmss" ) ) );
250 if ( details.creationDateTime.timeZone().isValid() )
252 int offsetFromUtc = details.creationDateTime.timeZone().offsetFromUtc( details.creationDateTime );
253 creationDateString += ( offsetFromUtc >= 0 ) ?
'+' :
'-';
254 offsetFromUtc = std::abs( offsetFromUtc );
255 int offsetHours = offsetFromUtc / 3600;
256 int offsetMins = ( offsetFromUtc % 3600 ) / 60;
257 creationDateString += QStringLiteral(
"%1'%2'" ).arg( offsetHours ).arg( offsetMins );
259 creationDate.appendChild( doc.createTextNode( creationDateString ) );
260 metadata.appendChild( creationDate );
262 if ( !details.subject.isEmpty() )
264 QDomElement subject = doc.createElement( QStringLiteral(
"Subject" ) );
265 subject.appendChild( doc.createTextNode( details.subject ) );
266 metadata.appendChild( subject );
268 if ( !details.title.isEmpty() )
270 QDomElement title = doc.createElement( QStringLiteral(
"Title" ) );
271 title.appendChild( doc.createTextNode( details.title ) );
272 metadata.appendChild( title );
274 if ( !details.keywords.empty() )
276 QStringList allKeywords;
277 for (
auto it = details.keywords.constBegin(); it != details.keywords.constEnd(); ++it )
279 allKeywords.append( QStringLiteral(
"%1: %2" ).arg( it.key(), it.value().join(
',' ) ) );
281 QDomElement keywords = doc.createElement( QStringLiteral(
"Keywords" ) );
282 keywords.appendChild( doc.createTextNode( allKeywords.join(
';' ) ) );
283 metadata.appendChild( keywords );
285 compositionElem.appendChild( metadata );
287 QMap< QString, QSet< QString > > createdLayerIds;
288 QMap< QString, QDomElement > groupLayerMap;
289 QMap< QString, QString > customGroupNamesToIds;
291 QMultiMap< QString, QDomElement > pendingLayerTreeElements;
293 if ( details.includeFeatures )
295 for (
const VectorComponentDetail &component : qgis::as_const( mVectorComponents ) )
297 if ( details.customLayerTreeGroups.contains( component.mapLayerId ) )
300 QDomElement layer = doc.createElement( QStringLiteral(
"Layer" ) );
301 layer.setAttribute( QStringLiteral(
"id" ), component.group.isEmpty() ? component.mapLayerId : QStringLiteral(
"%1_%2" ).arg( component.group, component.mapLayerId ) );
302 layer.setAttribute( QStringLiteral(
"name" ), details.layerIdToPdfLayerTreeNameMap.contains( component.mapLayerId ) ? details.layerIdToPdfLayerTreeNameMap.value( component.mapLayerId ) : component.name );
303 layer.setAttribute( QStringLiteral(
"initiallyVisible" ), details.initialLayerVisibility.value( component.mapLayerId,
true ) ? QStringLiteral(
"true" ) : QStringLiteral(
"false" ) );
305 if ( !component.group.isEmpty() )
307 if ( groupLayerMap.contains( component.group ) )
309 groupLayerMap[ component.group ].appendChild( layer );
313 QDomElement group = doc.createElement( QStringLiteral(
"Layer" ) );
314 group.setAttribute( QStringLiteral(
"id" ), QStringLiteral(
"group_%1" ).arg( component.group ) );
315 group.setAttribute( QStringLiteral(
"name" ), component.group );
316 group.setAttribute( QStringLiteral(
"initiallyVisible" ), groupLayerMap.empty() ? QStringLiteral(
"true" ) : QStringLiteral(
"false" ) );
317 group.setAttribute( QStringLiteral(
"mutuallyExclusiveGroupId" ), QStringLiteral(
"__mutually_exclusive_groups__" ) );
318 pendingLayerTreeElements.insert( component.mapLayerId, group );
319 group.appendChild( layer );
320 groupLayerMap[ component.group ] = group;
325 pendingLayerTreeElements.insert( component.mapLayerId, layer );
328 createdLayerIds[ component.group ].insert( component.mapLayerId );
332 for (
const ComponentLayerDetail &component : components )
334 if ( component.mapLayerId.isEmpty() || createdLayerIds.value( component.group ).contains( component.mapLayerId ) )
337 if ( details.customLayerTreeGroups.contains( component.mapLayerId ) )
340 QDomElement layer = doc.createElement( QStringLiteral(
"Layer" ) );
341 layer.setAttribute( QStringLiteral(
"id" ), component.group.isEmpty() ? component.mapLayerId : QStringLiteral(
"%1_%2" ).arg( component.group, component.mapLayerId ) );
342 layer.setAttribute( QStringLiteral(
"name" ), details.layerIdToPdfLayerTreeNameMap.contains( component.mapLayerId ) ? details.layerIdToPdfLayerTreeNameMap.value( component.mapLayerId ) : component.name );
343 layer.setAttribute( QStringLiteral(
"initiallyVisible" ), details.initialLayerVisibility.value( component.mapLayerId,
true ) ? QStringLiteral(
"true" ) : QStringLiteral(
"false" ) );
345 if ( !component.group.isEmpty() )
347 if ( groupLayerMap.contains( component.group ) )
349 groupLayerMap[ component.group ].appendChild( layer );
353 QDomElement group = doc.createElement( QStringLiteral(
"Layer" ) );
354 group.setAttribute( QStringLiteral(
"id" ), QStringLiteral(
"group_%1" ).arg( component.group ) );
355 group.setAttribute( QStringLiteral(
"name" ), component.group );
356 group.setAttribute( QStringLiteral(
"initiallyVisible" ), groupLayerMap.empty() ? QStringLiteral(
"true" ) : QStringLiteral(
"false" ) );
357 group.setAttribute( QStringLiteral(
"mutuallyExclusiveGroupId" ), QStringLiteral(
"__mutually_exclusive_groups__" ) );
358 pendingLayerTreeElements.insert( component.mapLayerId, group );
359 group.appendChild( layer );
360 groupLayerMap[ component.group ] = group;
365 pendingLayerTreeElements.insert( component.mapLayerId, layer );
368 createdLayerIds[ component.group ].insert( component.mapLayerId );
372 QDomElement
layerTree = doc.createElement( QStringLiteral(
"LayerTree" ) );
376 for (
auto it = details.customLayerTreeGroups.constBegin(); it != details.customLayerTreeGroups.constEnd(); ++it )
378 if ( customGroupNamesToIds.contains( it.value() ) )
381 QDomElement layer = doc.createElement( QStringLiteral(
"Layer" ) );
382 const QString
id = QUuid::createUuid().toString();
383 customGroupNamesToIds[ it.value() ] = id;
384 layer.setAttribute( QStringLiteral(
"id" ),
id );
385 layer.setAttribute( QStringLiteral(
"name" ), it.value() );
386 layer.setAttribute( QStringLiteral(
"initiallyVisible" ), QStringLiteral(
"true" ) );
391 for (
const QString &layerId : details.layerOrder )
393 const QList< QDomElement> elements = pendingLayerTreeElements.values( layerId );
394 for (
const QDomElement &element : elements )
398 for (
auto it = pendingLayerTreeElements.constBegin(); it != pendingLayerTreeElements.constEnd(); ++it )
400 if ( details.layerOrder.contains( it.key() ) )
409 compositionElem.appendChild(
layerTree );
412 QDomElement page = doc.createElement( QStringLiteral(
"Page" ) );
413 QDomElement dpi = doc.createElement( QStringLiteral(
"DPI" ) );
416 page.appendChild( dpi );
418 QDomElement width = doc.createElement( QStringLiteral(
"Width" ) );
419 const double pageWidthPdfUnits = std::ceil( details.pageSizeMm.width() / 25.4 * 72 );
420 width.appendChild( doc.createTextNode(
qgsDoubleToString( pageWidthPdfUnits ) ) );
421 page.appendChild( width );
422 QDomElement height = doc.createElement( QStringLiteral(
"Height" ) );
423 const double pageHeightPdfUnits = std::ceil( details.pageSizeMm.height() / 25.4 * 72 );
424 height.appendChild( doc.createTextNode(
qgsDoubleToString( pageHeightPdfUnits ) ) );
425 page.appendChild( height );
432 QDomElement georeferencing = doc.createElement( QStringLiteral(
"Georeferencing" ) );
433 georeferencing.setAttribute( QStringLiteral(
"id" ), QStringLiteral(
"georeferenced_%1" ).arg( i++ ) );
434 georeferencing.setAttribute( QStringLiteral(
"OGCBestPracticeFormat" ), details.useOgcBestPracticeFormatGeoreferencing ? QStringLiteral(
"true" ) : QStringLiteral(
"false" ) );
435 georeferencing.setAttribute( QStringLiteral(
"ISO32000ExtensionFormat" ), details.useIso32000ExtensionFormatGeoreferencing ? QStringLiteral(
"true" ) : QStringLiteral(
"false" ) );
439 QDomElement srs = doc.createElement( QStringLiteral(
"SRS" ) );
442 if ( !section.
crs.
authid().startsWith( QStringLiteral(
"user" ), Qt::CaseInsensitive ) )
444 srs.appendChild( doc.createTextNode( section.
crs.
authid() ) );
450 georeferencing.appendChild( srs );
462 QDomElement boundingPolygon = doc.createElement( QStringLiteral(
"BoundingPolygon" ) );
465 QTransform t = QTransform::fromTranslate( 0, pageHeightPdfUnits ).scale( pageWidthPdfUnits / details.pageSizeMm.width(),
466 -pageHeightPdfUnits / details.pageSizeMm.height() );
470 boundingPolygon.appendChild( doc.createTextNode( p.
asWkt() ) );
472 georeferencing.appendChild( boundingPolygon );
481 QDomElement boundingBox = doc.createElement( QStringLiteral(
"BoundingBox" ) );
486 georeferencing.appendChild( boundingBox );
491 QDomElement cp1 = doc.createElement( QStringLiteral(
"ControlPoint" ) );
492 cp1.setAttribute( QStringLiteral(
"x" ),
qgsDoubleToString( point.pagePoint.x() / 25.4 * 72 ) );
493 cp1.setAttribute( QStringLiteral(
"y" ),
qgsDoubleToString( ( details.pageSizeMm.height() - point.pagePoint.y() ) / 25.4 * 72 ) );
494 cp1.setAttribute( QStringLiteral(
"GeoX" ),
qgsDoubleToString( point.geoPoint.x() ) );
495 cp1.setAttribute( QStringLiteral(
"GeoY" ),
qgsDoubleToString( point.geoPoint.y() ) );
496 georeferencing.appendChild( cp1 );
499 page.appendChild( georeferencing );
502 auto createPdfDatasetElement = [&doc](
const ComponentLayerDetail & component ) -> QDomElement
504 QDomElement pdfDataset = doc.createElement( QStringLiteral(
"PDF" ) );
505 pdfDataset.setAttribute( QStringLiteral(
"dataset" ), component.sourcePdfPath );
506 if ( component.opacity != 1.0 || component.compositionMode != QPainter::CompositionMode_SourceOver )
508 QDomElement blendingElement = doc.createElement( QStringLiteral(
"Blending" ) );
509 blendingElement.setAttribute( QStringLiteral(
"opacity" ), component.opacity );
510 blendingElement.setAttribute( QStringLiteral(
"function" ), compositionModeToString( component.compositionMode ) );
512 pdfDataset.appendChild( blendingElement );
518 QDomElement content = doc.createElement( QStringLiteral(
"Content" ) );
519 for (
const ComponentLayerDetail &component : components )
521 if ( component.mapLayerId.isEmpty() )
523 content.appendChild( createPdfDatasetElement( component ) );
525 else if ( !component.group.isEmpty() )
528 QDomElement ifGroupOn = doc.createElement( QStringLiteral(
"IfLayerOn" ) );
529 ifGroupOn.setAttribute( QStringLiteral(
"layerId" ), QStringLiteral(
"group_%1" ).arg( component.group ) );
530 QDomElement ifLayerOn = doc.createElement( QStringLiteral(
"IfLayerOn" ) );
531 if ( details.customLayerTreeGroups.contains( component.mapLayerId ) )
532 ifLayerOn.setAttribute( QStringLiteral(
"layerId" ), customGroupNamesToIds.value( details.customLayerTreeGroups.value( component.mapLayerId ) ) );
533 else if ( component.group.isEmpty() )
534 ifLayerOn.setAttribute( QStringLiteral(
"layerId" ), component.mapLayerId );
536 ifLayerOn.setAttribute( QStringLiteral(
"layerId" ), QStringLiteral(
"%1_%2" ).arg( component.group, component.mapLayerId ) );
538 ifLayerOn.appendChild( createPdfDatasetElement( component ) );
539 ifGroupOn.appendChild( ifLayerOn );
540 content.appendChild( ifGroupOn );
544 QDomElement ifLayerOn = doc.createElement( QStringLiteral(
"IfLayerOn" ) );
545 if ( details.customLayerTreeGroups.contains( component.mapLayerId ) )
546 ifLayerOn.setAttribute( QStringLiteral(
"layerId" ), customGroupNamesToIds.value( details.customLayerTreeGroups.value( component.mapLayerId ) ) );
547 else if ( component.group.isEmpty() )
548 ifLayerOn.setAttribute( QStringLiteral(
"layerId" ), component.mapLayerId );
550 ifLayerOn.setAttribute( QStringLiteral(
"layerId" ), QStringLiteral(
"%1_%2" ).arg( component.group, component.mapLayerId ) );
551 ifLayerOn.appendChild( createPdfDatasetElement( component ) );
552 content.appendChild( ifLayerOn );
557 if ( details.includeFeatures )
559 for (
const VectorComponentDetail &component : qgis::as_const( mVectorComponents ) )
561 QDomElement ifLayerOn = doc.createElement( QStringLiteral(
"IfLayerOn" ) );
562 if ( details.customLayerTreeGroups.contains( component.mapLayerId ) )
563 ifLayerOn.setAttribute( QStringLiteral(
"layerId" ), customGroupNamesToIds.value( details.customLayerTreeGroups.value( component.mapLayerId ) ) );
564 else if ( component.group.isEmpty() )
565 ifLayerOn.setAttribute( QStringLiteral(
"layerId" ), component.mapLayerId );
567 ifLayerOn.setAttribute( QStringLiteral(
"layerId" ), QStringLiteral(
"%1_%2" ).arg( component.group, component.mapLayerId ) );
568 QDomElement vectorDataset = doc.createElement( QStringLiteral(
"Vector" ) );
569 vectorDataset.setAttribute( QStringLiteral(
"dataset" ), component.sourceVectorPath );
570 vectorDataset.setAttribute( QStringLiteral(
"layer" ), component.sourceVectorLayer );
571 vectorDataset.setAttribute( QStringLiteral(
"visible" ), QStringLiteral(
"false" ) );
572 QDomElement logicalStructure = doc.createElement( QStringLiteral(
"LogicalStructure" ) );
573 logicalStructure.setAttribute( QStringLiteral(
"displayLayerName" ), component.name );
574 if ( !component.displayAttribute.isEmpty() )
575 logicalStructure.setAttribute( QStringLiteral(
"fieldToDisplay" ), component.displayAttribute );
576 vectorDataset.appendChild( logicalStructure );
577 ifLayerOn.appendChild( vectorDataset );
578 content.appendChild( ifLayerOn );
582 page.appendChild( content );
583 compositionElem.appendChild( page );
585 doc.appendChild( compositionElem );
588 QTextStream stream( &composition );
589 doc.save( stream, -1 );
594 QString QgsAbstractGeoPdfExporter::compositionModeToString( QPainter::CompositionMode mode )
598 case QPainter::CompositionMode_SourceOver:
599 return QStringLiteral(
"Normal" );
601 case QPainter::CompositionMode_Multiply:
602 return QStringLiteral(
"Multiply" );
604 case QPainter::CompositionMode_Screen:
605 return QStringLiteral(
"Screen" );
607 case QPainter::CompositionMode_Overlay:
608 return QStringLiteral(
"Overlay" );
610 case QPainter::CompositionMode_Darken:
611 return QStringLiteral(
"Darken" );
613 case QPainter::CompositionMode_Lighten:
614 return QStringLiteral(
"Lighten" );
616 case QPainter::CompositionMode_ColorDodge:
617 return QStringLiteral(
"ColorDodge" );
619 case QPainter::CompositionMode_ColorBurn:
620 return QStringLiteral(
"ColorBurn" );
622 case QPainter::CompositionMode_HardLight:
623 return QStringLiteral(
"HardLight" );
625 case QPainter::CompositionMode_SoftLight:
626 return QStringLiteral(
"SoftLight" );
628 case QPainter::CompositionMode_Difference:
629 return QStringLiteral(
"Difference" );
631 case QPainter::CompositionMode_Exclusion:
632 return QStringLiteral(
"Exclusion" );
638 QgsDebugMsg( QStringLiteral(
"Unsupported PDF blend mode %1" ).arg( mode ) );
639 return QStringLiteral(
"Normal" );
void pushRenderedFeature(const QString &layerId, const QgsAbstractGeoPdfExporter::RenderedFeature &feature, const QString &group=QString())
Called multiple times during the rendering operation, whenever a feature associated with the specifie...
QString generateTemporaryFilepath(const QString &filename) const
Returns a file path to use for temporary files required for GeoPDF creation.
static bool geoPDFCreationAvailable()
Returns true if the current QGIS build is capable of GeoPDF support.
static bool compositionModeSupported(QPainter::CompositionMode mode)
Returns true if the specified composition mode is supported for layers during GeoPDF exports.
static QString geoPDFAvailabilityExplanation()
Returns a user-friendly, translated string explaining why GeoPDF export support is not available on t...
bool finalize(const QList< QgsAbstractGeoPdfExporter::ComponentLayerDetail > &components, const QString &destinationFile, const ExportDetails &details)
To be called after the rendering operation is complete.
This class represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString authid() const
Returns the authority identifier for the CRS.
@ WKT_PREFERRED_GDAL
Preferred format for conversion of CRS to WKT for use with the GDAL library.
QString toWkt(WktVariant variant=WKT1_GDAL, 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 SIP_HOLDGIL
Returns true if the geometry is empty.
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
void transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) override SIP_THROW(QgsCsException)
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 id, geometry and a list of field/values...
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Options to pass to writeAsVectorFormat()
QString driverName
OGR driver to use.
QgsVectorFileWriter::SymbologyExport symbologyExport
Symbology to export.
static QgsVectorFileWriter * create(const QString &fileName, const QgsFields &fields, QgsWkbTypes::Type 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.
QList< QgsFeature > QgsFeatureList
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.
QgsPolygon pageBoundsPolygon
Bounds of the georeferenced section on the page, in millimeters, as a free-form polygon.
QList< QgsAbstractGeoPdfExporter::ControlPoint > controlPoints
List of control points corresponding to this georeferenced section.
Contains information about a feature rendered inside the PDF.
QgsFeature feature
Rendered feature.
QgsGeometry renderedBounds
Bounds, in PDF units, of rendered feature.