QGIS API Documentation 3.41.0-Master (45a0abf3bec)
Loading...
Searching...
No Matches
qgsprofileexporter.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsprofileexporter.cpp
3 ---------------
4 begin : May 2023
5 copyright : (C) 2023 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17#include "qgsprofileexporter.h"
18#include "moc_qgsprofileexporter.cpp"
21#include "qgsdxfexport.h"
22#include "qgsprofilerenderer.h"
24#include "qgsvectorlayer.h"
25#include "qgsvectorfilewriter.h"
26
27#include <QThread>
28#include <QFileInfo>
29
30QgsProfileExporter::QgsProfileExporter( const QList<QgsAbstractProfileSource *> &sources, const QgsProfileRequest &request, Qgis::ProfileExportType type )
31 : mType( type )
32 , mRequest( request )
33{
34 for ( QgsAbstractProfileSource *source : sources )
35 {
36 if ( source )
37 {
38 if ( std::unique_ptr< QgsAbstractProfileGenerator > generator{ source->createProfileGenerator( mRequest ) } )
39 mGenerators.emplace_back( std::move( generator ) );
40 }
41 }
42}
43
45
47{
48 if ( mGenerators.empty() )
49 return;
50
51 QgsProfilePlotRenderer renderer( std::move( mGenerators ), mRequest );
52 renderer.startGeneration();
53 renderer.waitForFinished();
54
55 mFeatures = renderer.asFeatures( mType, feedback );
56}
57
58QList< QgsVectorLayer *> QgsProfileExporter::toLayers()
59{
60 if ( mFeatures.empty() )
61 return {};
62
63 // collect all features with the same geometry types together
64 QHash< quint32, QVector< QgsAbstractProfileResults::Feature > > featuresByGeometryType;
65 for ( const QgsAbstractProfileResults::Feature &feature : std::as_const( mFeatures ) )
66 {
67 featuresByGeometryType[static_cast< quint32 >( feature.geometry.wkbType() )].append( feature );
68 }
69
70 // generate a new memory provider layer for each geometry type
71 QList< QgsVectorLayer * > res;
72 for ( auto wkbTypeIt = featuresByGeometryType.constBegin(); wkbTypeIt != featuresByGeometryType.constEnd(); ++wkbTypeIt )
73 {
74 // first collate a master list of fields for this geometry type
75 QgsFields outputFields;
76 outputFields.append( QgsField( QStringLiteral( "layer" ), QMetaType::Type::QString ) );
77
78 for ( const QgsAbstractProfileResults::Feature &feature : std::as_const( wkbTypeIt.value() ) )
79 {
80 for ( auto attributeIt = feature.attributes.constBegin(); attributeIt != feature.attributes.constEnd(); ++attributeIt )
81 {
82 const int existingFieldIndex = outputFields.lookupField( attributeIt.key() );
83 if ( existingFieldIndex < 0 )
84 {
85 outputFields.append( QgsField( attributeIt.key(), static_cast<QMetaType::Type>( attributeIt.value().userType() ) ) );
86 }
87 else
88 {
89 if ( outputFields.at( existingFieldIndex ).type() != QMetaType::Type::QString && outputFields.at( existingFieldIndex ).type() != attributeIt.value().userType() )
90 {
91 // attribute type mismatch across fields, just promote to string types to be flexible
92 outputFields[ existingFieldIndex ].setType( QMetaType::Type::QString );
93 }
94 }
95 }
96 }
97
98 // note -- 2d profiles have no CRS associated, the coordinate values are not location based!
99 std::unique_ptr< QgsVectorLayer > outputLayer( QgsMemoryProviderUtils::createMemoryLayer(
100 QStringLiteral( "profile" ),
101 outputFields,
102 static_cast< Qgis::WkbType >( wkbTypeIt.key() ),
104 false ) );
105
106 QList< QgsFeature > featuresToAdd;
107 featuresToAdd.reserve( wkbTypeIt.value().size() );
108 for ( const QgsAbstractProfileResults::Feature &feature : std::as_const( wkbTypeIt.value() ) )
109 {
110 QgsFeature out( outputFields );
111 out.setAttribute( 0, feature.layerIdentifier );
112 out.setGeometry( feature.geometry );
113 for ( auto attributeIt = feature.attributes.constBegin(); attributeIt != feature.attributes.constEnd(); ++attributeIt )
114 {
115 const int outputFieldIndex = outputFields.lookupField( attributeIt.key() );
116 const QgsField &targetField = outputFields.at( outputFieldIndex );
117 QVariant value = attributeIt.value();
118 targetField.convertCompatible( value );
119 out.setAttribute( outputFieldIndex, value );
120 }
121 featuresToAdd << out;
122 }
123
124 outputLayer->dataProvider()->addFeatures( featuresToAdd, QgsFeatureSink::FastInsert );
125 res << outputLayer.release();
126 }
127 return res;
128}
129
130//
131// QgsProfileExporterTask
132//
133
134QgsProfileExporterTask::QgsProfileExporterTask( const QList<QgsAbstractProfileSource *> &sources,
135 const QgsProfileRequest &request,
137 const QString &destination,
138 const QgsCoordinateTransformContext &transformContext
139 )
140 : QgsTask( tr( "Exporting elevation profile" ), QgsTask::CanCancel )
141 , mDestination( destination )
142 , mTransformContext( transformContext )
143{
144 mExporter = std::make_unique< QgsProfileExporter >( sources, request, type );
145}
146
148{
149 mFeedback = std::make_unique< QgsFeedback >();
150
151 mExporter->run( mFeedback.get() );
152
153 mLayers = mExporter->toLayers();
154
155 if ( mFeedback->isCanceled() )
156 {
157 mResult = ExportResult::Canceled;
158 return true;
159 }
160
161 if ( !mDestination.isEmpty() && !mLayers.empty() )
162 {
163 const QFileInfo destinationFileInfo( mDestination );
164 const QString fileExtension = destinationFileInfo.completeSuffix();
165 const QString driverName = QgsVectorFileWriter::driverForExtension( fileExtension );
166
167 if ( driverName == QLatin1String( "DXF" ) )
168 {
169 // DXF gets special handling -- we use the inbuilt QgsDxfExport class
170 QgsDxfExport dxf;
171 QList< QgsDxfExport::DxfLayer > dxfLayers;
172 for ( QgsVectorLayer *layer : std::as_const( mLayers ) )
173 {
174 QgsDxfExport::DxfLayer dxfLayer( layer );
175 dxfLayers.append( dxfLayer );
176 if ( layer->crs().isValid() )
177 dxf.setDestinationCrs( layer->crs() );
178 }
179 dxf.addLayers( dxfLayers );
180 QFile dxfFile( mDestination );
181 switch ( dxf.writeToFile( &dxfFile, QStringLiteral( "UTF-8" ) ) )
182 {
184 mResult = ExportResult::Success;
185 mCreatedFiles.append( mDestination );
186 break;
187
191 break;
192
195 break;
196 }
197 }
198 else
199 {
200 // use vector file writer
201 const bool outputFormatIsMultiLayer = QgsVectorFileWriter::supportedFormatExtensions( QgsVectorFileWriter::SupportsMultipleLayers ).contains( fileExtension );
202
203 int layerCount = 1;
204 for ( QgsVectorLayer *layer : std::as_const( mLayers ) )
205 {
206 QString thisLayerFilename;
208 if ( outputFormatIsMultiLayer )
209 {
210 thisLayerFilename = mDestination;
213 if ( mLayers.size() > 1 )
214 options.layerName = QStringLiteral( "profile_%1" ).arg( layerCount );
215 }
216 else
217 {
219 if ( mLayers.size() > 1 )
220 {
221 thisLayerFilename = QStringLiteral( "%1/%2_%3.%4" ).arg( destinationFileInfo.path(), destinationFileInfo.baseName() ).arg( layerCount ).arg( fileExtension );
222 }
223 else
224 {
225 thisLayerFilename = mDestination;
226 }
227 }
228 options.driverName = driverName;
229 options.feedback = mFeedback.get();
230 options.fileEncoding = QStringLiteral( "UTF-8" );
231 QString newFileName;
233 layer,
234 thisLayerFilename,
235 mTransformContext,
236 options,
237 &mError,
238 &newFileName
239 );
240 switch ( result )
241 {
243 mResult = ExportResult::Success;
244 if ( !mCreatedFiles.contains( newFileName ) )
245 mCreatedFiles.append( newFileName );
246 break;
247
252 break;
253
261 break;
262
263
265 mResult = ExportResult::Canceled;
266 break;
267 }
268
269 if ( mResult != ExportResult::Success )
270 break;
271 layerCount += 1;
272 }
273 }
274 }
275 else if ( mLayers.empty() )
276 {
277 mResult = ExportResult::Empty;
278 }
279
280 for ( QgsVectorLayer *layer : std::as_const( mLayers ) )
281 {
282 layer->moveToThread( nullptr );
283 }
284
285 mExporter.reset();
286 return true;
287}
288
290{
291 if ( mFeedback )
292 mFeedback->cancel();
293
295}
296
297QList<QgsVectorLayer *> QgsProfileExporterTask::takeLayers()
298{
299 QList<QgsVectorLayer *> res;
300 res.reserve( mLayers.size() );
301 for ( QgsVectorLayer *layer : std::as_const( mLayers ) )
302 {
303 layer->moveToThread( QThread::currentThread() );
304 res.append( layer );
305 }
306 mLayers.clear();
307 return res;
308}
309
ProfileExportType
Types of export for elevation profiles.
Definition qgis.h:3937
@ Profile2D
Export profiles as 2D profile lines, with elevation stored in exported geometry Y dimension and dista...
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:256
Interface for classes which can generate elevation profiles.
This class represents a coordinate reference system (CRS).
Contains information about the context in which a coordinate transform is executed.
Exports QGIS layers to the DXF format.
@ DeviceNotWritableError
Device not writable error.
@ Success
Successful export.
@ EmptyExtentError
Empty extent, no extent given and no extent could be derived from layers.
@ InvalidDeviceError
Invalid device error.
ExportResult writeToFile(QIODevice *d, const QString &codec)
Export to a dxf file in the given encoding.
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Set destination CRS.
void addLayers(const QList< QgsDxfExport::DxfLayer > &layers)
Add layers to export.
@ FastInsert
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
Q_INVOKABLE bool setAttribute(int field, const QVariant &attr)
Sets an attribute's value by field index.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:53
QMetaType::Type type
Definition qgsfield.h:60
bool convertCompatible(QVariant &v, QString *errorMessage=nullptr) const
Converts the provided variant to a compatible format.
Definition qgsfield.cpp:473
Container of fields for a vector layer.
Definition qgsfields.h:46
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:70
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
static QgsVectorLayer * createMemoryLayer(const QString &name, const QgsFields &fields, Qgis::WkbType geometryType=Qgis::WkbType::NoGeometry, const QgsCoordinateReferenceSystem &crs=QgsCoordinateReferenceSystem(), bool loadDefaultStyle=true) SIP_FACTORY
Creates a new memory layer using the specified parameters.
ExportResult
Results of exporting the profile.
@ LayerExportFailed
Generic error when outputting to files.
@ DxfExportFailed
Generic error when outputting to DXF.
@ DeviceError
Could not open output file device.
QgsProfileExporterTask(const QList< QgsAbstractProfileSource * > &sources, const QgsProfileRequest &request, Qgis::ProfileExportType type, const QString &destination, const QgsCoordinateTransformContext &transformContext)
Constructor for QgsProfileExporterTask, saving results to the specified destination file.
bool run() override
Performs the task's operation.
QgsProfileExporterTask::ExportResult result() const
Returns the result of the export operation.
void cancel() override
Notifies the task that it should terminate.
QList< QgsVectorLayer * > takeLayers()
Returns a list of vector layer containing the exported profile results.
void run(QgsFeedback *feedback=nullptr)
Runs the profile generation.
QgsProfileExporter(const QList< QgsAbstractProfileSource * > &sources, const QgsProfileRequest &request, Qgis::ProfileExportType type)
Constructor for QgsProfileExporter, using the provided list of profile sources to generate the result...
QList< QgsVectorLayer * > toLayers()
Returns a list of vector layer containing the exported profile results.
Generates and renders elevation profile plots.
QVector< QgsAbstractProfileResults::Feature > asFeatures(Qgis::ProfileExportType type, QgsFeedback *feedback=nullptr)
Exports the profile results as a set of features.
void startGeneration()
Start the generation job and immediately return.
void waitForFinished()
Block until the current job has finished.
Encapsulates properties and constraints relating to fetching elevation profiles from different source...
QgsCoordinateReferenceSystem crs() const
Returns the desired Coordinate Reference System for the profile.
Abstract base class for long running background tasks.
virtual void cancel()
Notifies the task that it should terminate.
Options to pass to writeAsVectorFormat()
QString layerName
Layer name. If let empty, it will be derived from the filename.
QgsVectorFileWriter::ActionOnExistingFile actionOnExistingFile
Action on existing file.
QgsFeedback * feedback
Optional feedback object allowing cancellation of layer save.
static QgsVectorFileWriter::WriterError writeAsVectorFormatV3(QgsVectorLayer *layer, const QString &fileName, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QString *errorMessage=nullptr, QString *newFilename=nullptr, QString *newLayer=nullptr)
Writes a layer out to a vector file.
@ Canceled
Writing was interrupted by manual cancellation.
@ ErrSavingMetadata
Metadata saving failed.
static QString driverForExtension(const QString &extension)
Returns the OGR driver name for a specified file extension.
@ SupportsMultipleLayers
Filter to only formats which support multiple layers.
static QStringList supportedFormatExtensions(VectorFormatOptions options=SortRecommended)
Returns a list of file extensions for supported formats, e.g "shp", "gpkg".
@ CreateOrOverwriteLayer
Create or overwrite layer.
@ CreateOrOverwriteFile
Create or overwrite file.
Represents a vector layer which manages a vector based data sets.
Encapsulates information about a feature exported from the profile results.
Layers and optional attribute index to split into multiple layers using attribute value as layer name...