Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsabstractgeopdfexporter.h
3 : : --------------------------
4 : : begin : August 2019
5 : : copyright : (C) 2019 by Nyall Dawson
6 : : email : nyall dot dawson at gmail dot com
7 : : ***************************************************************************/
8 : : /***************************************************************************
9 : : * *
10 : : * This program is free software; you can redistribute it and/or modify *
11 : : * it under the terms of the GNU General Public License as published by *
12 : : * the Free Software Foundation; either version 2 of the License, or *
13 : : * (at your option) any later version. *
14 : : * *
15 : : ***************************************************************************/
16 : : #ifndef QGSABSTRACTGEOPDFEXPORTER_H
17 : : #define QGSABSTRACTGEOPDFEXPORTER_H
18 : :
19 : : #include "qgis_core.h"
20 : : #include <QList>
21 : : #include <QTemporaryDir>
22 : : #include <QMutex>
23 : : #include <QDateTime>
24 : : #include <QPainter>
25 : :
26 : : #include "qgsfeature.h"
27 : : #include "qgsabstractmetadatabase.h"
28 : : #include "qgspolygon.h"
29 : :
30 : : #define SIP_NO_FILE
31 : :
32 : :
33 : : class QgsGeoPdfRenderedFeatureHandler;
34 : :
35 : : /**
36 : : * \class QgsAbstractGeoPdfExporter
37 : : * \ingroup core
38 : : *
39 : : * \brief Abstract base class for GeoPDF exporters.
40 : : *
41 : : * The base class handles generic GeoPDF export setup, cleanup and processing steps.
42 : : *
43 : : * This class is a low level implementation detail only. Generally, you should use the high level interface exposed by
44 : : * classes like QgsLayoutExporter instead.
45 : : *
46 : : * \warning QgsAbstractGeoPdfExporter is designed to be a short lived object. It should be created for a
47 : : * single export operation only, and then immediately destroyed. Failure to correctly
48 : : * destroy the object after exporting a PDF will leave the application in an inconsistent, unstable state.
49 : : *
50 : : * \note Not available in Python bindings
51 : : *
52 : : * \since QGIS 3.10
53 : : */
54 : : class CORE_EXPORT QgsAbstractGeoPdfExporter
55 : : {
56 : : public:
57 : :
58 : : /**
59 : : * Returns TRUE if the current QGIS build is capable of GeoPDF support.
60 : : *
61 : : * If FALSE is returned, a user-friendly explanation why can be retrieved via
62 : : * geoPDFAvailabilityExplanation().
63 : : */
64 : : static bool geoPDFCreationAvailable();
65 : :
66 : : /**
67 : : * Returns a user-friendly, translated string explaining why GeoPDF export
68 : : * support is not available on the current QGIS build (or an empty string if
69 : : * GeoPDF support IS available).
70 : : * \see geoPDFCreationAvailable()
71 : : */
72 : : static QString geoPDFAvailabilityExplanation();
73 : :
74 : : /**
75 : : * Constructor for QgsAbstractGeoPdfExporter.
76 : : */
77 : 0 : QgsAbstractGeoPdfExporter() = default;
78 : :
79 : 0 : virtual ~QgsAbstractGeoPdfExporter() = default;
80 : :
81 : : /**
82 : : * Contains information about a feature rendered inside the PDF.
83 : : */
84 : 0 : struct RenderedFeature
85 : : {
86 : :
87 : : /**
88 : : * Constructor for RenderedFeature.
89 : : */
90 : : RenderedFeature() = default;
91 : :
92 : : /**
93 : : * Constructor for RenderedFeature.
94 : : */
95 : 0 : RenderedFeature( const QgsFeature &feature, const QgsGeometry &renderedBounds )
96 : 0 : : feature( feature )
97 : 0 : , renderedBounds( renderedBounds )
98 : 0 : {}
99 : :
100 : : /**
101 : : * Rendered feature.
102 : : */
103 : : QgsFeature feature;
104 : :
105 : : /**
106 : : * Bounds, in PDF units, of rendered feature. (Multi)LineString or Polygon types only.
107 : : */
108 : : QgsGeometry renderedBounds;
109 : : };
110 : :
111 : : /**
112 : : * \brief Contains details of a particular input component to be used during PDF composition.
113 : : * \ingroup core
114 : : * \since QGIS 3.10
115 : : */
116 : 0 : struct CORE_EXPORT ComponentLayerDetail
117 : : {
118 : :
119 : : //! User-friendly name for the generated PDF layer
120 : : QString name;
121 : :
122 : : //! Associated map layer ID, or an empty string if this component layer is not associated with a map layer
123 : : QString mapLayerId;
124 : :
125 : : //! Optional group name, for arranging layers in top-level groups
126 : : QString group;
127 : :
128 : : //! File path to the (already created) PDF to use as the source for this component layer
129 : : QString sourcePdfPath;
130 : :
131 : : //! Component composition mode
132 : 0 : QPainter::CompositionMode compositionMode = QPainter::CompositionMode_SourceOver;
133 : :
134 : : //! Component opacity
135 : 0 : double opacity = 1.0;
136 : :
137 : : };
138 : :
139 : : /**
140 : : * \brief Contains details of a control point used during georeferencing GeoPDF outputs.
141 : : * \ingroup core
142 : : * \since QGIS 3.10
143 : : */
144 : 0 : struct ControlPoint
145 : : {
146 : :
147 : : /**
148 : : * Constructor for ControlPoint, at the specified \a pagePoint (in millimeters)
149 : : * and \a geoPoint (in CRS units).
150 : : */
151 : 0 : ControlPoint( const QgsPointXY &pagePoint, const QgsPointXY &geoPoint )
152 : 0 : : pagePoint( pagePoint )
153 : 0 : , geoPoint( geoPoint )
154 : 0 : {}
155 : :
156 : : //! Coordinate on the page of the control point, in millimeters
157 : : QgsPointXY pagePoint;
158 : :
159 : : //! Georeferenced coordinate of the control point, in CRS units
160 : : QgsPointXY geoPoint;
161 : : };
162 : :
163 : 0 : struct GeoReferencedSection
164 : : {
165 : :
166 : : /**
167 : : * Bounds of the georeferenced section on the page, in millimeters.
168 : : *
169 : : * \note if pageBoundsPolygon is specified then this setting is ignored.
170 : : */
171 : : QgsRectangle pageBoundsMm;
172 : :
173 : : /**
174 : : * Bounds of the georeferenced section on the page, in millimeters, as a free-form polygon.
175 : : *
176 : : * If specified, this will be used instead of pageBoundsMm.
177 : : */
178 : : QgsPolygon pageBoundsPolygon;
179 : :
180 : : //! Coordinate reference system for georeferenced section
181 : : QgsCoordinateReferenceSystem crs;
182 : :
183 : : //! List of control points corresponding to this georeferenced section
184 : : QList< QgsAbstractGeoPdfExporter::ControlPoint > controlPoints;
185 : :
186 : : };
187 : :
188 : : /**
189 : : * Called multiple times during the rendering operation, whenever a \a feature associated with the specified
190 : : * \a layerId is rendered.
191 : : *
192 : : * The optional \a group argument can be used to differentiate features from the same layer exported
193 : : * multiple times as part of different layer groups.
194 : : */
195 : : void pushRenderedFeature( const QString &layerId, const QgsAbstractGeoPdfExporter::RenderedFeature &feature, const QString &group = QString() );
196 : :
197 : 0 : struct ExportDetails
198 : : {
199 : : //! Page size, in millimeters
200 : : QSizeF pageSizeMm;
201 : :
202 : : //! Output DPI
203 : 0 : double dpi = 300;
204 : :
205 : : //! List of georeferenced sections
206 : : QList< QgsAbstractGeoPdfExporter::GeoReferencedSection > georeferencedSections;
207 : :
208 : : //! Metadata author tag
209 : : QString author;
210 : :
211 : : //! Metadata producer tag
212 : : QString producer;
213 : :
214 : : //! Metadata creator tag
215 : : QString creator;
216 : :
217 : : //! Metadata creation datetime
218 : : QDateTime creationDateTime;
219 : :
220 : : //! Metadata subject tag
221 : : QString subject;
222 : :
223 : : //! Metadata title tag
224 : : QString title;
225 : :
226 : : //! Metadata keyword map
227 : : QgsAbstractMetadataBase::KeywordMap keywords;
228 : :
229 : : /**
230 : : * TRUE if ISO3200 extension format georeferencing should be used.
231 : : *
232 : : * This is a recommended setting which results in Geospatial PDF files compatible
233 : : * with the built-in Acrobat geospatial tools.
234 : : */
235 : 0 : bool useIso32000ExtensionFormatGeoreferencing = true;
236 : :
237 : : /**
238 : : * TRUE if OGC "best practice" format georeferencing should be used.
239 : : *
240 : : * \warning This results in GeoPDF files compatible with the TerraGo suite of tools, but
241 : : * can break compatibility with the built-in Acrobat geospatial tools (yes, GeoPDF
242 : : * format is a mess!).
243 : : */
244 : 0 : bool useOgcBestPracticeFormatGeoreferencing = false;
245 : :
246 : : /**
247 : : * TRUE if feature vector information (such as attributes) should be exported.
248 : : */
249 : 0 : bool includeFeatures = true;
250 : :
251 : : /**
252 : : * Optional map of map layer ID to custom logical layer tree group in created PDF file.
253 : : *
254 : : * E.g. if the map contains "layer1": "Environment", "layer2": "Environment", "layer3": "Transport"
255 : : * then the created PDF file will have entries in its layer tree for "Environment" and "Transport",
256 : : * and toggling "Environment" will toggle BOTH layer1 and layer2.
257 : : *
258 : : * Layers which are not included in this group will always have their own individual layer tree entry
259 : : * created for them automatically.
260 : : */
261 : : QMap< QString, QString > customLayerTreeGroups;
262 : :
263 : : /**
264 : : * Optional map of map layer ID to custom layer tree name to show in the created PDF file.
265 : : *
266 : : * \since QGIS 3.14
267 : : */
268 : : QMap< QString, QString > layerIdToPdfLayerTreeNameMap;
269 : :
270 : : /**
271 : : * Optional map of map layer ID to initial visibility state. If a layer ID is not present in this,
272 : : * it will default to being initially visible when opening the PDF.
273 : : *
274 : : * \since QGIS 3.14
275 : : */
276 : : QMap< QString, bool > initialLayerVisibility;
277 : :
278 : : /**
279 : : * Optional list of layer IDs, in the order desired to appear in the generated GeoPDF file.
280 : : *
281 : : * Layers appearing earlier in the list will show earlier in the GeoPDF layer tree list.
282 : : *
283 : : * \since QGIS 3.14
284 : : */
285 : : QStringList layerOrder;
286 : :
287 : : };
288 : :
289 : : /**
290 : : * To be called after the rendering operation is complete.
291 : : *
292 : : * Will export the list of PDF layer \a components to a new PDF file at \a destinationFile. The \a components
293 : : * argument gives a list of additional layers to include in the generated PDF file. These must have already
294 : : * been created, e.g. as a result of rendering layers to separate PDF source files.
295 : : *
296 : : * Any features previously collected by calls to pushRenderedFeature() will be included automatically in the GeoPDF
297 : : * export.
298 : : *
299 : : * Returns TRUE if the operation was successful, or FALSE if an error occurred. If an error occurred, it
300 : : * can be retrieved by calling errorMessage().
301 : : */
302 : : bool finalize( const QList< QgsAbstractGeoPdfExporter::ComponentLayerDetail > &components, const QString &destinationFile, const ExportDetails &details );
303 : :
304 : : /**
305 : : * Returns the last error message encountered during the export.
306 : : */
307 : : QString errorMessage() { return mErrorMessage; }
308 : :
309 : : /**
310 : : * Returns a file path to use for temporary files required for GeoPDF creation.
311 : : */
312 : : QString generateTemporaryFilepath( const QString &filename ) const;
313 : :
314 : : /**
315 : : * Returns TRUE if the specified composition \a mode is supported for layers
316 : : * during GeoPDF exports.
317 : : *
318 : : * \since QGIS 3.14
319 : : */
320 : : static bool compositionModeSupported( QPainter::CompositionMode mode );
321 : :
322 : : protected:
323 : :
324 : : /**
325 : : * Contains information relating to a single PDF layer in the GeoPDF export.
326 : : */
327 : 0 : struct VectorComponentDetail
328 : : {
329 : : //! User-friendly name for the generated PDF layer
330 : : QString name;
331 : :
332 : : //! Associated map layer ID
333 : : QString mapLayerId;
334 : :
335 : : //! Optional layer group name
336 : : QString group;
337 : :
338 : : //! Field name for display
339 : : QString displayAttribute;
340 : :
341 : : //! File path to the (already created) vector dataset to use as the source for this component layer
342 : : QString sourceVectorPath;
343 : :
344 : : //! Layer name in vector dataset to use as the source
345 : : QString sourceVectorLayer;
346 : :
347 : : };
348 : :
349 : : private:
350 : :
351 : : QMutex mMutex;
352 : : QMap< QString, QMap< QString, QgsFeatureList > > mCollatedFeatures;
353 : :
354 : : /**
355 : : * Returns the PDF output component details for the layer with given \a layerId.
356 : : */
357 : : virtual VectorComponentDetail componentDetailForLayerId( const QString &layerId ) = 0;
358 : :
359 : : QList< VectorComponentDetail > mVectorComponents;
360 : :
361 : : QString mErrorMessage;
362 : : QTemporaryDir mTemporaryDir;
363 : :
364 : :
365 : : bool saveTemporaryLayers();
366 : :
367 : : QString createCompositionXml( const QList< QgsAbstractGeoPdfExporter::ComponentLayerDetail > &components, const ExportDetails &details );
368 : :
369 : : /**
370 : : * Returns the GDAL string representation of the specified QPainter composition \a mode.
371 : : */
372 : : static QString compositionModeToString( QPainter::CompositionMode mode );
373 : :
374 : : friend class TestQgsLayoutGeoPdfExport;
375 : : friend class TestQgsGeoPdfExport;
376 : : };
377 : :
378 : : #endif //QGSABSTRACTGEOPDFEXPORTER_H
379 : :
380 : :
381 : :
|