QGIS API Documentation  3.2.0-Bonn (bc43194)
qgsogrutils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsogrutils.cpp
3  ---------------
4  begin : February 2016
5  copyright : (C) 2016 Nyall Dawson
6  email : nyall dot dawson at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgsogrutils.h"
17 #include "qgsapplication.h"
18 #include "qgslogger.h"
19 #include "qgsgeometry.h"
20 #include "qgsfields.h"
21 #include <QTextCodec>
22 #include <QUuid>
23 #include <cpl_error.h>
24 
25 // Starting with GDAL 2.2, there are 2 concepts: unset fields and null fields
26 // whereas previously there was only unset fields. For QGIS purposes, both
27 // states (unset/null) are equivalent.
28 #ifndef OGRNullMarker
29 #define OGR_F_IsFieldSetAndNotNull OGR_F_IsFieldSet
30 #endif
31 
32 
33 
34 void gdal::OGRDataSourceDeleter::operator()( OGRDataSourceH source )
35 {
36  OGR_DS_Destroy( source );
37 }
38 
39 
40 void gdal::OGRGeometryDeleter::operator()( OGRGeometryH geometry )
41 {
42  OGR_G_DestroyGeometry( geometry );
43 }
44 
45 void gdal::OGRFldDeleter::operator()( OGRFieldDefnH definition )
46 {
47  OGR_Fld_Destroy( definition );
48 }
49 
50 void gdal::OGRFeatureDeleter::operator()( OGRFeatureH feature )
51 {
52  OGR_F_Destroy( feature );
53 }
54 
56 {
57  GDALClose( dataset );
58 }
59 
60 void gdal::fast_delete_and_close( gdal::dataset_unique_ptr &dataset, GDALDriverH driver, const QString &path )
61 {
62  // see https://github.com/qgis/QGIS/commit/d024910490a39e65e671f2055c5b6543e06c7042#commitcomment-25194282
63  // faster if we close the handle AFTER delete, but doesn't work for windows
64 #ifdef Q_OS_WIN
65  // close dataset handle
66  dataset.reset();
67 #endif
68 
69  CPLPushErrorHandler( CPLQuietErrorHandler );
70  GDALDeleteDataset( driver, path.toUtf8().constData() );
71  CPLPopErrorHandler();
72 
73 #ifndef Q_OS_WIN
74  // close dataset handle
75  dataset.reset();
76 #endif
77 }
78 
79 
80 void gdal::GDALWarpOptionsDeleter::operator()( GDALWarpOptions *options )
81 {
82  GDALDestroyWarpOptions( options );
83 }
84 
85 QgsFeature QgsOgrUtils::readOgrFeature( OGRFeatureH ogrFet, const QgsFields &fields, QTextCodec *encoding )
86 {
87  QgsFeature feature;
88  if ( !ogrFet )
89  {
90  feature.setValid( false );
91  return feature;
92  }
93 
94  feature.setId( OGR_F_GetFID( ogrFet ) );
95  feature.setValid( true );
96 
97  if ( !readOgrFeatureGeometry( ogrFet, feature ) )
98  {
99  feature.setValid( false );
100  }
101 
102  if ( !readOgrFeatureAttributes( ogrFet, fields, feature, encoding ) )
103  {
104  feature.setValid( false );
105  }
106 
107  return feature;
108 }
109 
110 QgsFields QgsOgrUtils::readOgrFields( OGRFeatureH ogrFet, QTextCodec *encoding )
111 {
112  QgsFields fields;
113 
114  if ( !ogrFet )
115  return fields;
116 
117  int fieldCount = OGR_F_GetFieldCount( ogrFet );
118  for ( int i = 0; i < fieldCount; ++i )
119  {
120  OGRFieldDefnH fldDef = OGR_F_GetFieldDefnRef( ogrFet, i );
121  if ( !fldDef )
122  {
123  fields.append( QgsField() );
124  continue;
125  }
126 
127  QString name = encoding ? encoding->toUnicode( OGR_Fld_GetNameRef( fldDef ) ) : QString::fromUtf8( OGR_Fld_GetNameRef( fldDef ) );
128  QVariant::Type varType;
129  switch ( OGR_Fld_GetType( fldDef ) )
130  {
131  case OFTInteger:
132  if ( OGR_Fld_GetSubType( fldDef ) == OFSTBoolean )
133  varType = QVariant::Bool;
134  else
135  varType = QVariant::Int;
136  break;
137  case OFTInteger64:
138  varType = QVariant::LongLong;
139  break;
140  case OFTReal:
141  varType = QVariant::Double;
142  break;
143  case OFTDate:
144  varType = QVariant::Date;
145  break;
146  case OFTTime:
147  varType = QVariant::Time;
148  break;
149  case OFTDateTime:
150  varType = QVariant::DateTime;
151  break;
152  case OFTString:
153  default:
154  varType = QVariant::String; // other unsupported, leave it as a string
155  }
156  fields.append( QgsField( name, varType ) );
157  }
158  return fields;
159 }
160 
161 QVariant QgsOgrUtils::getOgrFeatureAttribute( OGRFeatureH ogrFet, const QgsFields &fields, int attIndex, QTextCodec *encoding, bool *ok )
162 {
163  if ( !ogrFet || attIndex < 0 || attIndex >= fields.count() )
164  {
165  if ( ok )
166  *ok = false;
167  return QVariant();
168  }
169 
170  OGRFieldDefnH fldDef = OGR_F_GetFieldDefnRef( ogrFet, attIndex );
171 
172  if ( ! fldDef )
173  {
174  if ( ok )
175  *ok = false;
176 
177  QgsDebugMsg( "ogrFet->GetFieldDefnRef(attindex) returns NULL" );
178  return QVariant();
179  }
180 
181  QVariant value;
182 
183  if ( ok )
184  *ok = true;
185 
186  if ( OGR_F_IsFieldSetAndNotNull( ogrFet, attIndex ) )
187  {
188  switch ( fields.at( attIndex ).type() )
189  {
190  case QVariant::String:
191  {
192  if ( encoding )
193  value = QVariant( encoding->toUnicode( OGR_F_GetFieldAsString( ogrFet, attIndex ) ) );
194  else
195  value = QVariant( QString::fromUtf8( OGR_F_GetFieldAsString( ogrFet, attIndex ) ) );
196  break;
197  }
198  case QVariant::Int:
199  case QVariant::Bool:
200  value = QVariant( OGR_F_GetFieldAsInteger( ogrFet, attIndex ) );
201  break;
202  case QVariant::LongLong:
203  value = QVariant( OGR_F_GetFieldAsInteger64( ogrFet, attIndex ) );
204  break;
205  case QVariant::Double:
206  value = QVariant( OGR_F_GetFieldAsDouble( ogrFet, attIndex ) );
207  break;
208  case QVariant::Date:
209  case QVariant::DateTime:
210  case QVariant::Time:
211  {
212  int year, month, day, hour, minute, second, tzf;
213 
214  OGR_F_GetFieldAsDateTime( ogrFet, attIndex, &year, &month, &day, &hour, &minute, &second, &tzf );
215  if ( fields.at( attIndex ).type() == QVariant::Date )
216  value = QDate( year, month, day );
217  else if ( fields.at( attIndex ).type() == QVariant::Time )
218  value = QTime( hour, minute, second );
219  else
220  value = QDateTime( QDate( year, month, day ), QTime( hour, minute, second ) );
221  }
222  break;
223  default:
224  Q_ASSERT_X( false, "QgsOgrUtils::getOgrFeatureAttribute", "unsupported field type" );
225  if ( ok )
226  *ok = false;
227  }
228  }
229  else
230  {
231  value = QVariant( QString() );
232  }
233 
234  return value;
235 }
236 
237 bool QgsOgrUtils::readOgrFeatureAttributes( OGRFeatureH ogrFet, const QgsFields &fields, QgsFeature &feature, QTextCodec *encoding )
238 {
239  // read all attributes
240  feature.initAttributes( fields.count() );
241  feature.setFields( fields );
242 
243  if ( !ogrFet )
244  return false;
245 
246  bool ok = false;
247  for ( int idx = 0; idx < fields.count(); ++idx )
248  {
249  QVariant value = getOgrFeatureAttribute( ogrFet, fields, idx, encoding, &ok );
250  if ( ok )
251  {
252  feature.setAttribute( idx, value );
253  }
254  }
255  return true;
256 }
257 
258 bool QgsOgrUtils::readOgrFeatureGeometry( OGRFeatureH ogrFet, QgsFeature &feature )
259 {
260  if ( !ogrFet )
261  return false;
262 
263  OGRGeometryH geom = OGR_F_GetGeometryRef( ogrFet );
264  if ( !geom )
265  feature.clearGeometry();
266  else
267  feature.setGeometry( ogrGeometryToQgsGeometry( geom ) );
268 
269  return true;
270 }
271 
273 {
274  if ( !geom )
275  return QgsGeometry();
276 
277  // get the wkb representation
278  int memorySize = OGR_G_WkbSize( geom );
279  unsigned char *wkb = new unsigned char[memorySize];
280  OGR_G_ExportToWkb( geom, ( OGRwkbByteOrder ) QgsApplication::endian(), wkb );
281 
282  // Read original geometry type
283  uint32_t origGeomType;
284  memcpy( &origGeomType, wkb + 1, sizeof( uint32_t ) );
285  bool hasZ = ( origGeomType >= 1000 && origGeomType < 2000 ) || ( origGeomType >= 3000 && origGeomType < 4000 );
286  bool hasM = ( origGeomType >= 2000 && origGeomType < 3000 ) || ( origGeomType >= 3000 && origGeomType < 4000 );
287 
288  // PolyhedralSurface and TINs are not supported, map them to multipolygons...
289  if ( origGeomType % 1000 == 16 ) // is TIN, TINZ, TINM or TINZM
290  {
291  // TIN has the same wkb layout as a multipolygon, just need to overwrite the geom types...
292  int nDims = 2 + hasZ + hasM;
293  uint32_t newMultiType = static_cast<uint32_t>( QgsWkbTypes::zmType( QgsWkbTypes::MultiPolygon, hasZ, hasM ) );
294  uint32_t newSingleType = static_cast<uint32_t>( QgsWkbTypes::zmType( QgsWkbTypes::Polygon, hasZ, hasM ) );
295  unsigned char *wkbptr = wkb;
296 
297  // Endianness
298  wkbptr += 1;
299 
300  // Overwrite geom type
301  memcpy( wkbptr, &newMultiType, sizeof( uint32_t ) );
302  wkbptr += 4;
303 
304  // Geom count
305  uint32_t numGeoms;
306  memcpy( &numGeoms, wkb + 5, sizeof( uint32_t ) );
307  wkbptr += 4;
308 
309  // For each part, overwrite the geometry type to polygon (Z|M)
310  for ( uint32_t i = 0; i < numGeoms; ++i )
311  {
312  // Endianness
313  wkbptr += 1;
314 
315  // Overwrite geom type
316  memcpy( wkbptr, &newSingleType, sizeof( uint32_t ) );
317  wkbptr += sizeof( uint32_t );
318 
319  // skip coordinates
320  uint32_t nRings;
321  memcpy( &nRings, wkbptr, sizeof( uint32_t ) );
322  wkbptr += sizeof( uint32_t );
323 
324  for ( uint32_t j = 0; j < nRings; ++j )
325  {
326  uint32_t nPoints;
327  memcpy( &nPoints, wkbptr, sizeof( uint32_t ) );
328  wkbptr += sizeof( uint32_t ) + sizeof( double ) * nDims * nPoints;
329  }
330  }
331  }
332  else if ( origGeomType % 1000 == 15 ) // PolyhedralSurface, PolyhedralSurfaceZ, PolyhedralSurfaceM or PolyhedralSurfaceZM
333  {
334  // PolyhedralSurface has the same wkb layout as a MultiPolygon, just need to overwrite the geom type...
335  uint32_t newType = static_cast<uint32_t>( QgsWkbTypes::zmType( QgsWkbTypes::MultiPolygon, hasZ, hasM ) );
336  // Overwrite geom type
337  memcpy( wkb + 1, &newType, sizeof( uint32_t ) );
338  }
339 
340  QgsGeometry g;
341  g.fromWkb( wkb, memorySize );
342  return g;
343 }
344 
345 QgsFeatureList QgsOgrUtils::stringToFeatureList( const QString &string, const QgsFields &fields, QTextCodec *encoding )
346 {
347  QgsFeatureList features;
348  if ( string.isEmpty() )
349  return features;
350 
351  QString randomFileName = QStringLiteral( "/vsimem/%1" ).arg( QUuid::createUuid().toString() );
352 
353  // create memory file system object from string buffer
354  QByteArray ba = string.toUtf8();
355  VSIFCloseL( VSIFileFromMemBuffer( randomFileName.toUtf8().constData(), reinterpret_cast< GByte * >( ba.data() ),
356  static_cast< vsi_l_offset >( ba.size() ), FALSE ) );
357 
358  gdal::ogr_datasource_unique_ptr hDS( OGROpen( randomFileName.toUtf8().constData(), false, nullptr ) );
359  if ( !hDS )
360  {
361  VSIUnlink( randomFileName.toUtf8().constData() );
362  return features;
363  }
364 
365  OGRLayerH ogrLayer = OGR_DS_GetLayer( hDS.get(), 0 );
366  if ( !ogrLayer )
367  {
368  hDS.reset();
369  VSIUnlink( randomFileName.toUtf8().constData() );
370  return features;
371  }
372 
374  while ( oFeat.reset( OGR_L_GetNextFeature( ogrLayer ) ), oFeat )
375  {
376  QgsFeature feat = readOgrFeature( oFeat.get(), fields, encoding );
377  if ( feat.isValid() )
378  features << feat;
379  }
380 
381  hDS.reset();
382  VSIUnlink( randomFileName.toUtf8().constData() );
383 
384  return features;
385 }
386 
387 QgsFields QgsOgrUtils::stringToFields( const QString &string, QTextCodec *encoding )
388 {
389  QgsFields fields;
390  if ( string.isEmpty() )
391  return fields;
392 
393  QString randomFileName = QStringLiteral( "/vsimem/%1" ).arg( QUuid::createUuid().toString() );
394 
395  // create memory file system object from buffer
396  QByteArray ba = string.toUtf8();
397  VSIFCloseL( VSIFileFromMemBuffer( randomFileName.toUtf8().constData(), reinterpret_cast< GByte * >( ba.data() ),
398  static_cast< vsi_l_offset >( ba.size() ), FALSE ) );
399 
400  gdal::ogr_datasource_unique_ptr hDS( OGROpen( randomFileName.toUtf8().constData(), false, nullptr ) );
401  if ( !hDS )
402  {
403  VSIUnlink( randomFileName.toUtf8().constData() );
404  return fields;
405  }
406 
407  OGRLayerH ogrLayer = OGR_DS_GetLayer( hDS.get(), 0 );
408  if ( !ogrLayer )
409  {
410  hDS.reset();
411  VSIUnlink( randomFileName.toUtf8().constData() );
412  return fields;
413  }
414 
416  //read in the first feature only
417  if ( oFeat.reset( OGR_L_GetNextFeature( ogrLayer ) ), oFeat )
418  {
419  fields = readOgrFields( oFeat.get(), encoding );
420  }
421 
422  hDS.reset();
423  VSIUnlink( randomFileName.toUtf8().constData() );
424  return fields;
425 }
426 
427 QStringList QgsOgrUtils::cStringListToQStringList( char **stringList )
428 {
429  QStringList strings;
430 
431  // presume null terminated string list
432  for ( qgssize i = 0; stringList[i]; ++i )
433  {
434  strings.append( QString::fromUtf8( stringList[i] ) );
435  }
436 
437  return strings;
438 }
439 
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:176
void CORE_EXPORT operator()(OGRFeatureH feature)
Destroys an OGR feature, using the correct gdal calls.
Definition: qgsogrutils.cpp:50
static bool readOgrFeatureAttributes(OGRFeatureH ogrFet, const QgsFields &fields, QgsFeature &feature, QTextCodec *encoding)
Reads all attributes from an OGR feature into a QgsFeature.
void setFields(const QgsFields &fields, bool initAttributes=false)
Assign a field map with the feature to allow attribute access by attribute name.
Definition: qgsfeature.cpp:155
void fromWkb(unsigned char *wkb, int length)
Set the geometry, feeding in the buffer containing OGC Well-Known Binary and the buffer&#39;s length...
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:549
static bool readOgrFeatureGeometry(OGRFeatureH ogrFet, QgsFeature &feature)
Reads the geometry from an OGR feature into a QgsFeature.
Container of fields for a vector layer.
Definition: qgsfields.h:42
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:104
bool setAttribute(int field, const QVariant &attr)
Set an attribute&#39;s value by field index.
Definition: qgsfeature.cpp:204
void CORE_EXPORT operator()(OGRDataSourceH source)
Destroys an OGR data source, using the correct gdal calls.
Definition: qgsogrutils.cpp:34
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:62
static QgsFields readOgrFields(OGRFeatureH ogrFet, QTextCodec *encoding)
Reads an OGR feature and returns a corresponding fields collection.
#define OGR_F_IsFieldSetAndNotNull
Definition: qgsogrutils.cpp:29
static endian_t endian()
Returns whether this machine uses big or little endian.
int count() const
Returns number of items.
Definition: qgsfields.cpp:115
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:145
static QgsFeatureList stringToFeatureList(const QString &string, const QgsFields &fields, QTextCodec *encoding)
Attempts to parse a string representing a collection of features using OGR.
void CORE_EXPORT fast_delete_and_close(dataset_unique_ptr &dataset, GDALDriverH driver, const QString &path)
Performs a fast close of an unwanted GDAL dataset handle by deleting the underlying data store...
Definition: qgsogrutils.cpp:60
std::unique_ptr< std::remove_pointer< OGRFeatureH >::type, OGRFeatureDeleter > ogr_feature_unique_ptr
Scoped OGR feature.
Definition: qgsogrutils.h:129
void CORE_EXPORT operator()(GDALDatasetH datasource)
Destroys an gdal dataset, using the correct gdal calls.
Definition: qgsogrutils.cpp:55
void initAttributes(int fieldCount)
Initialize this feature with the given number of fields.
Definition: qgsfeature.cpp:195
static QgsFeature readOgrFeature(OGRFeatureH ogrFet, const QgsFields &fields, QTextCodec *encoding)
Reads an OGR feature and converts it to a QgsFeature.
Definition: qgsogrutils.cpp:85
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Append a field. The field must have unique name, otherwise it is rejected (returns false) ...
Definition: qgsfields.cpp:59
static QgsFields stringToFields(const QString &string, QTextCodec *encoding)
Attempts to retrieve the fields from a string representing a collection of features using OGR...
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:48
void setId(QgsFeatureId id)
Sets the feature ID for this feature.
Definition: qgsfeature.cpp:112
void * GDALDatasetH
static QgsGeometry ogrGeometryToQgsGeometry(OGRGeometryH geom)
Converts an OGR geometry representation to a QgsGeometry object.
unsigned long long qgssize
Qgssize is used instead of size_t, because size_t is stdlib type, unknown by SIP, and it would be har...
Definition: qgis.h:510
void CORE_EXPORT operator()(GDALWarpOptions *options)
Destroys GDAL warp options, using the correct gdal calls.
Definition: qgsogrutils.cpp:80
void setValid(bool validity)
Sets the validity of the feature.
Definition: qgsfeature.cpp:181
void clearGeometry()
Removes any geometry associated with the feature.
Definition: qgsfeature.cpp:144
static QVariant getOgrFeatureAttribute(OGRFeatureH ogrFet, const QgsFields &fields, int attIndex, QTextCodec *encoding, bool *ok=nullptr)
Retrieves an attribute value from an OGR feature.
void CORE_EXPORT operator()(OGRGeometryH geometry)
Destroys an OGR geometry, using the correct gdal calls.
Definition: qgsogrutils.cpp:40
void CORE_EXPORT operator()(OGRFieldDefnH definition)
Destroys an OGR field definition, using the correct gdal calls.
Definition: qgsogrutils.cpp:45
void setGeometry(const QgsGeometry &geometry)
Set the feature&#39;s geometry.
Definition: qgsfeature.cpp:137
static Type zmType(Type type, bool hasZ, bool hasM)
Returns the modified input geometry type according to hasZ / hasM.
Definition: qgswkbtypes.h:526
std::unique_ptr< std::remove_pointer< GDALDatasetH >::type, GDALDatasetCloser > dataset_unique_ptr
Scoped GDAL dataset.
Definition: qgsogrutils.h:134
std::unique_ptr< std::remove_pointer< OGRDataSourceH >::type, OGRDataSourceDeleter > ogr_datasource_unique_ptr
Scoped OGR data source.
Definition: qgsogrutils.h:114
static QStringList cStringListToQStringList(char **stringList)
Converts a c string list to a QStringList.
QVariant::Type type
Definition: qgsfield.h:55