QGIS API Documentation  3.25.0-Master (dec16ba68b)
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 "qgslinestring.h"
22 #include "qgsmultipoint.h"
23 #include "qgsmultilinestring.h"
24 #include "qgsogrprovider.h"
25 #include "qgslinesymbollayer.h"
26 #include "qgspolygon.h"
27 #include "qgsmultipolygon.h"
29 #include "qgsfillsymbollayer.h"
30 #include "qgsmarkersymbollayer.h"
31 #include "qgssymbollayerutils.h"
32 #include "qgsfontutils.h"
33 #include "qgsmessagelog.h"
34 #include "qgssymbol.h"
35 #include "qgsfillsymbol.h"
36 #include "qgslinesymbol.h"
37 #include "qgsmarkersymbol.h"
38 #include "qgsfielddomain.h"
39 
40 #include <QTextCodec>
41 #include <QUuid>
42 #include <cpl_error.h>
43 #include <QJsonDocument>
44 #include <QFileInfo>
45 #include <QDir>
46 #include <QTextStream>
47 #include <QDataStream>
48 #include <QRegularExpression>
49 
50 #include "ogr_srs_api.h"
51 
52 
53 void gdal::OGRDataSourceDeleter::operator()( OGRDataSourceH source ) const
54 {
55  OGR_DS_Destroy( source );
56 }
57 
58 
59 void gdal::OGRGeometryDeleter::operator()( OGRGeometryH geometry ) const
60 {
61  OGR_G_DestroyGeometry( geometry );
62 }
63 
64 void gdal::OGRFldDeleter::operator()( OGRFieldDefnH definition ) const
65 {
66  OGR_Fld_Destroy( definition );
67 }
68 
69 void gdal::OGRFeatureDeleter::operator()( OGRFeatureH feature ) const
70 {
71  OGR_F_Destroy( feature );
72 }
73 
75 {
76  GDALClose( dataset );
77 }
78 
79 void gdal::fast_delete_and_close( gdal::dataset_unique_ptr &dataset, GDALDriverH driver, const QString &path )
80 {
81  // see https://github.com/qgis/QGIS/commit/d024910490a39e65e671f2055c5b6543e06c7042#commitcomment-25194282
82  // faster if we close the handle AFTER delete, but doesn't work for windows
83 #ifdef Q_OS_WIN
84  // close dataset handle
85  dataset.reset();
86 #endif
87 
88  CPLPushErrorHandler( CPLQuietErrorHandler );
89  GDALDeleteDataset( driver, path.toUtf8().constData() );
90  CPLPopErrorHandler();
91 
92 #ifndef Q_OS_WIN
93  // close dataset handle
94  dataset.reset();
95 #endif
96 }
97 
98 
99 void gdal::GDALWarpOptionsDeleter::operator()( GDALWarpOptions *options ) const
100 {
101  GDALDestroyWarpOptions( options );
102 }
103 
104 QVariant QgsOgrUtils::OGRFieldtoVariant( const OGRField *value, OGRFieldType type )
105 {
106  if ( !value || OGR_RawField_IsUnset( value ) || OGR_RawField_IsNull( value ) )
107  return QVariant();
108 
109  switch ( type )
110  {
111  case OFTInteger:
112  return value->Integer;
113 
114  case OFTInteger64:
115  return value->Integer64;
116 
117  case OFTReal:
118  return value->Real;
119 
120  case OFTString:
121  case OFTWideString:
122  return QString::fromUtf8( value->String );
123 
124  case OFTDate:
125  return QDate( value->Date.Year, value->Date.Month, value->Date.Day );
126 
127  case OFTTime:
128  {
129  float secondsPart = 0;
130  float millisecondPart = std::modf( value->Date.Second, &secondsPart );
131  return QTime( value->Date.Hour, value->Date.Minute, static_cast< int >( secondsPart ), static_cast< int >( 1000 * millisecondPart ) );
132  }
133 
134  case OFTDateTime:
135  {
136  float secondsPart = 0;
137  float millisecondPart = std::modf( value->Date.Second, &secondsPart );
138  return QDateTime( QDate( value->Date.Year, value->Date.Month, value->Date.Day ),
139  QTime( value->Date.Hour, value->Date.Minute, static_cast< int >( secondsPart ), static_cast< int >( 1000 * millisecondPart ) ) );
140  }
141 
142  case OFTBinary:
143  // not supported!
144  Q_ASSERT_X( false, "QgsOgrUtils::OGRFieldtoVariant", "OFTBinary type not supported" );
145  return QVariant();
146 
147  case OFTIntegerList:
148  {
149  QVariantList res;
150  res.reserve( value->IntegerList.nCount );
151  for ( int i = 0; i < value->IntegerList.nCount; ++i )
152  res << value->IntegerList.paList[ i ];
153  return res;
154  }
155 
156  case OFTInteger64List:
157  {
158  QVariantList res;
159  res.reserve( value->Integer64List.nCount );
160  for ( int i = 0; i < value->Integer64List.nCount; ++i )
161  res << value->Integer64List.paList[ i ];
162  return res;
163  }
164 
165  case OFTRealList:
166  {
167  QVariantList res;
168  res.reserve( value->RealList.nCount );
169  for ( int i = 0; i < value->RealList.nCount; ++i )
170  res << value->RealList.paList[ i ];
171  return res;
172  }
173 
174  case OFTStringList:
175  case OFTWideStringList:
176  {
177  QVariantList res;
178  res.reserve( value->StringList.nCount );
179  for ( int i = 0; i < value->StringList.nCount; ++i )
180  res << QString::fromUtf8( value->StringList.paList[ i ] );
181  return res;
182  }
183  }
184  return QVariant();
185 }
186 
187 std::unique_ptr< OGRField > QgsOgrUtils::variantToOGRField( const QVariant &value )
188 {
189  std::unique_ptr< OGRField > res = std::make_unique< OGRField >();
190 
191  switch ( value.type() )
192  {
193  case QVariant::Invalid:
194  OGR_RawField_SetUnset( res.get() );
195  break;
196  case QVariant::Bool:
197  res->Integer = value.toBool() ? 1 : 0;
198  break;
199  case QVariant::Int:
200  res->Integer = value.toInt();
201  break;
202  case QVariant::LongLong:
203  res->Integer64 = value.toLongLong();
204  break;
205  case QVariant::Double:
206  res->Real = value.toDouble();
207  break;
208  case QVariant::Char:
209  case QVariant::String:
210  res->String = CPLStrdup( value.toString().toUtf8().constData() );
211  break;
212  case QVariant::Date:
213  {
214  const QDate date = value.toDate();
215  res->Date.Day = date.day();
216  res->Date.Month = date.month();
217  res->Date.Year = date.year();
218  res->Date.TZFlag = 0;
219  break;
220  }
221  case QVariant::Time:
222  {
223  const QTime time = value.toTime();
224  res->Date.Hour = time.hour();
225  res->Date.Minute = time.minute();
226  res->Date.Second = time.second() + static_cast< double >( time.msec() ) / 1000;
227  res->Date.TZFlag = 0;
228  break;
229  }
230  case QVariant::DateTime:
231  {
232  const QDateTime dateTime = value.toDateTime();
233  res->Date.Day = dateTime.date().day();
234  res->Date.Month = dateTime.date().month();
235  res->Date.Year = dateTime.date().year();
236  res->Date.Hour = dateTime.time().hour();
237  res->Date.Minute = dateTime.time().minute();
238  res->Date.Second = dateTime.time().second() + static_cast< double >( dateTime.time().msec() ) / 1000;
239  res->Date.TZFlag = 0;
240  break;
241  }
242 
243  default:
244  QgsDebugMsg( "Unhandled variant type in variantToOGRField" );
245  OGR_RawField_SetUnset( res.get() );
246  break;
247  }
248 
249  return res;
250 }
251 
252 QgsFeature QgsOgrUtils::readOgrFeature( OGRFeatureH ogrFet, const QgsFields &fields, QTextCodec *encoding )
253 {
254  QgsFeature feature;
255  if ( !ogrFet )
256  {
257  feature.setValid( false );
258  return feature;
259  }
260 
261  feature.setId( OGR_F_GetFID( ogrFet ) );
262  feature.setValid( true );
263 
264  if ( !readOgrFeatureGeometry( ogrFet, feature ) )
265  {
266  feature.setValid( false );
267  }
268 
269  if ( !readOgrFeatureAttributes( ogrFet, fields, feature, encoding ) )
270  {
271  feature.setValid( false );
272  }
273 
274  return feature;
275 }
276 
277 QgsFields QgsOgrUtils::readOgrFields( OGRFeatureH ogrFet, QTextCodec *encoding )
278 {
279  QgsFields fields;
280 
281  if ( !ogrFet )
282  return fields;
283 
284  int fieldCount = OGR_F_GetFieldCount( ogrFet );
285  for ( int i = 0; i < fieldCount; ++i )
286  {
287  OGRFieldDefnH fldDef = OGR_F_GetFieldDefnRef( ogrFet, i );
288  if ( !fldDef )
289  {
290  fields.append( QgsField() );
291  continue;
292  }
293 
294  QString name = encoding ? encoding->toUnicode( OGR_Fld_GetNameRef( fldDef ) ) : QString::fromUtf8( OGR_Fld_GetNameRef( fldDef ) );
295  QVariant::Type varType;
296  switch ( OGR_Fld_GetType( fldDef ) )
297  {
298  case OFTInteger:
299  if ( OGR_Fld_GetSubType( fldDef ) == OFSTBoolean )
300  varType = QVariant::Bool;
301  else
302  varType = QVariant::Int;
303  break;
304  case OFTInteger64:
305  varType = QVariant::LongLong;
306  break;
307  case OFTReal:
308  varType = QVariant::Double;
309  break;
310  case OFTDate:
311  varType = QVariant::Date;
312  break;
313  case OFTTime:
314  varType = QVariant::Time;
315  break;
316  case OFTDateTime:
317  varType = QVariant::DateTime;
318  break;
319  case OFTString:
320  if ( OGR_Fld_GetSubType( fldDef ) == OFSTJSON )
321  varType = QVariant::Map;
322  else
323  varType = QVariant::String;
324  break;
325  default:
326  varType = QVariant::String; // other unsupported, leave it as a string
327  }
328  fields.append( QgsField( name, varType ) );
329  }
330  return fields;
331 }
332 
333 
334 QVariant QgsOgrUtils::getOgrFeatureAttribute( OGRFeatureH ogrFet, const QgsFields &fields, int attIndex, QTextCodec *encoding, bool *ok )
335 {
336  if ( attIndex < 0 || attIndex >= fields.count() )
337  {
338  if ( ok )
339  *ok = false;
340  return QVariant();
341  }
342 
343  const QgsField field = fields.at( attIndex );
344  return getOgrFeatureAttribute( ogrFet, field, attIndex, encoding, ok );
345 }
346 
347 QVariant QgsOgrUtils::getOgrFeatureAttribute( OGRFeatureH ogrFet, const QgsField &field, int attIndex, QTextCodec *encoding, bool *ok )
348 {
349  if ( !ogrFet || attIndex < 0 )
350  {
351  if ( ok )
352  *ok = false;
353  return QVariant();
354  }
355 
356  OGRFieldDefnH fldDef = OGR_F_GetFieldDefnRef( ogrFet, attIndex );
357 
358  if ( ! fldDef )
359  {
360  if ( ok )
361  *ok = false;
362 
363  QgsDebugMsg( QStringLiteral( "ogrFet->GetFieldDefnRef(attindex) returns NULL" ) );
364  return QVariant();
365  }
366 
367  QVariant value;
368 
369  if ( ok )
370  *ok = true;
371 
372  if ( OGR_F_IsFieldSetAndNotNull( ogrFet, attIndex ) )
373  {
374  switch ( field.type() )
375  {
376  case QVariant::String:
377  {
378  if ( encoding )
379  value = QVariant( encoding->toUnicode( OGR_F_GetFieldAsString( ogrFet, attIndex ) ) );
380  else
381  value = QVariant( QString::fromUtf8( OGR_F_GetFieldAsString( ogrFet, attIndex ) ) );
382 
383 #ifdef Q_OS_WIN
384  // Fixes GH #41076 (empty strings shown as NULL), because we have checked before that it was NOT NULL
385  // Note: QVariant( QString( ) ).isNull( ) is still true on windows so we really need string literal :(
386  if ( value.isNull() )
387  value = QVariant( QStringLiteral( "" ) ); // skip-keyword-check
388 #endif
389 
390  break;
391  }
392  case QVariant::Int:
393  value = QVariant( OGR_F_GetFieldAsInteger( ogrFet, attIndex ) );
394  break;
395  case QVariant::Bool:
396  value = QVariant( bool( OGR_F_GetFieldAsInteger( ogrFet, attIndex ) ) );
397  break;
398  case QVariant::LongLong:
399  value = QVariant( OGR_F_GetFieldAsInteger64( ogrFet, attIndex ) );
400  break;
401  case QVariant::Double:
402  value = QVariant( OGR_F_GetFieldAsDouble( ogrFet, attIndex ) );
403  break;
404  case QVariant::Date:
405  case QVariant::DateTime:
406  case QVariant::Time:
407  {
408  int year, month, day, hour, minute, tzf;
409  float second;
410  float secondsPart = 0;
411 
412  OGR_F_GetFieldAsDateTimeEx( ogrFet, attIndex, &year, &month, &day, &hour, &minute, &second, &tzf );
413  float millisecondPart = std::modf( second, &secondsPart );
414 
415  if ( field.type() == QVariant::Date )
416  value = QDate( year, month, day );
417  else if ( field.type() == QVariant::Time )
418  value = QTime( hour, minute, static_cast< int >( secondsPart ), static_cast< int >( 1000 * millisecondPart ) );
419  else
420  value = QDateTime( QDate( year, month, day ),
421  QTime( hour, minute, static_cast< int >( secondsPart ), static_cast< int >( 1000 * millisecondPart ) ) );
422  }
423  break;
424 
425  case QVariant::ByteArray:
426  {
427  int size = 0;
428  const GByte *b = OGR_F_GetFieldAsBinary( ogrFet, attIndex, &size );
429 
430  // QByteArray::fromRawData is funny. It doesn't take ownership of the data, so we have to explicitly call
431  // detach on it to force a copy which owns the data
432  QByteArray ba = QByteArray::fromRawData( reinterpret_cast<const char *>( b ), size );
433  ba.detach();
434 
435  value = ba;
436  break;
437  }
438 
439  case QVariant::StringList:
440  {
441  QStringList list;
442  char **lst = OGR_F_GetFieldAsStringList( ogrFet, attIndex );
443  const int count = CSLCount( lst );
444  if ( count > 0 )
445  {
446  list.reserve( count );
447  for ( int i = 0; i < count; i++ )
448  {
449  if ( encoding )
450  list << encoding->toUnicode( lst[i] );
451  else
452  list << QString::fromUtf8( lst[i] );
453  }
454  }
455  value = list;
456  break;
457  }
458 
459  case QVariant::List:
460  {
461  switch ( field.subType() )
462  {
463  case QVariant::String:
464  {
465  QStringList list;
466  char **lst = OGR_F_GetFieldAsStringList( ogrFet, attIndex );
467  const int count = CSLCount( lst );
468  if ( count > 0 )
469  {
470  list.reserve( count );
471  for ( int i = 0; i < count; i++ )
472  {
473  if ( encoding )
474  list << encoding->toUnicode( lst[i] );
475  else
476  list << QString::fromUtf8( lst[i] );
477  }
478  }
479  value = list;
480  break;
481  }
482 
483  case QVariant::Int:
484  {
485  QVariantList list;
486  int count = 0;
487  const int *lst = OGR_F_GetFieldAsIntegerList( ogrFet, attIndex, &count );
488  if ( count > 0 )
489  {
490  list.reserve( count );
491  for ( int i = 0; i < count; i++ )
492  {
493  list << lst[i];
494  }
495  }
496  value = list;
497  break;
498  }
499 
500  case QVariant::Double:
501  {
502  QVariantList list;
503  int count = 0;
504  const double *lst = OGR_F_GetFieldAsDoubleList( ogrFet, attIndex, &count );
505  if ( count > 0 )
506  {
507  list.reserve( count );
508  for ( int i = 0; i < count; i++ )
509  {
510  list << lst[i];
511  }
512  }
513  value = list;
514  break;
515  }
516 
517  case QVariant::LongLong:
518  {
519  QVariantList list;
520  int count = 0;
521  const long long *lst = OGR_F_GetFieldAsInteger64List( ogrFet, attIndex, &count );
522  if ( count > 0 )
523  {
524  list.reserve( count );
525  for ( int i = 0; i < count; i++ )
526  {
527  list << lst[i];
528  }
529  }
530  value = list;
531  break;
532  }
533 
534  default:
535  {
536  Q_ASSERT_X( false, "QgsOgrUtils::getOgrFeatureAttribute", "unsupported field type" );
537  if ( ok )
538  *ok = false;
539  break;
540  }
541  }
542  break;
543  }
544 
545  case QVariant::Map:
546  {
547  //it has to be JSON
548  //it's null if no json format
549  if ( encoding )
550  value = QJsonDocument::fromJson( encoding->toUnicode( OGR_F_GetFieldAsString( ogrFet, attIndex ) ).toUtf8() ).toVariant();
551  else
552  value = QJsonDocument::fromJson( QString::fromUtf8( OGR_F_GetFieldAsString( ogrFet, attIndex ) ).toUtf8() ).toVariant();
553  break;
554  }
555  default:
556  Q_ASSERT_X( false, "QgsOgrUtils::getOgrFeatureAttribute", "unsupported field type" );
557  if ( ok )
558  *ok = false;
559  }
560  }
561  else
562  {
563  value = QVariant( field.type() );
564  }
565 
566  return value;
567 }
568 
569 bool QgsOgrUtils::readOgrFeatureAttributes( OGRFeatureH ogrFet, const QgsFields &fields, QgsFeature &feature, QTextCodec *encoding )
570 {
571  // read all attributes
572  feature.initAttributes( fields.count() );
573  feature.setFields( fields );
574 
575  if ( !ogrFet )
576  return false;
577 
578  bool ok = false;
579  for ( int idx = 0; idx < fields.count(); ++idx )
580  {
581  QVariant value = getOgrFeatureAttribute( ogrFet, fields, idx, encoding, &ok );
582  if ( ok )
583  {
584  feature.setAttribute( idx, value );
585  }
586  }
587  return true;
588 }
589 
590 bool QgsOgrUtils::readOgrFeatureGeometry( OGRFeatureH ogrFet, QgsFeature &feature )
591 {
592  if ( !ogrFet )
593  return false;
594 
595  OGRGeometryH geom = OGR_F_GetGeometryRef( ogrFet );
596  if ( !geom )
597  feature.clearGeometry();
598  else
599  feature.setGeometry( ogrGeometryToQgsGeometry( geom ) );
600 
601  return true;
602 }
603 
604 std::unique_ptr< QgsPoint > ogrGeometryToQgsPoint( OGRGeometryH geom )
605 {
606  QgsWkbTypes::Type wkbType = static_cast<QgsWkbTypes::Type>( OGR_G_GetGeometryType( geom ) );
607 
608  double x, y, z, m;
609  OGR_G_GetPointZM( geom, 0, &x, &y, &z, &m );
610  return std::make_unique< QgsPoint >( wkbType, x, y, z, m );
611 }
612 
613 std::unique_ptr< QgsMultiPoint > ogrGeometryToQgsMultiPoint( OGRGeometryH geom )
614 {
615  std::unique_ptr< QgsMultiPoint > mp = std::make_unique< QgsMultiPoint >();
616 
617  const int count = OGR_G_GetGeometryCount( geom );
618  mp->reserve( count );
619  for ( int i = 0; i < count; ++i )
620  {
621  mp->addGeometry( ogrGeometryToQgsPoint( OGR_G_GetGeometryRef( geom, i ) ).release() );
622  }
623 
624  return mp;
625 }
626 
627 std::unique_ptr< QgsLineString > ogrGeometryToQgsLineString( OGRGeometryH geom )
628 {
629  QgsWkbTypes::Type wkbType = static_cast<QgsWkbTypes::Type>( OGR_G_GetGeometryType( geom ) );
630 
631  int count = OGR_G_GetPointCount( geom );
632  QVector< double > x( count );
633  QVector< double > y( count );
634  QVector< double > z;
635  double *pz = nullptr;
636  if ( QgsWkbTypes::hasZ( wkbType ) )
637  {
638  z.resize( count );
639  pz = z.data();
640  }
641  double *pm = nullptr;
642  QVector< double > m;
643  if ( QgsWkbTypes::hasM( wkbType ) )
644  {
645  m.resize( count );
646  pm = m.data();
647  }
648  OGR_G_GetPointsZM( geom, x.data(), sizeof( double ), y.data(), sizeof( double ), pz, sizeof( double ), pm, sizeof( double ) );
649 
650  return std::make_unique< QgsLineString>( x, y, z, m, wkbType == QgsWkbTypes::LineString25D );
651 }
652 
653 std::unique_ptr< QgsMultiLineString > ogrGeometryToQgsMultiLineString( OGRGeometryH geom )
654 {
655  std::unique_ptr< QgsMultiLineString > mp = std::make_unique< QgsMultiLineString >();
656 
657  const int count = OGR_G_GetGeometryCount( geom );
658  mp->reserve( count );
659  for ( int i = 0; i < count; ++i )
660  {
661  mp->addGeometry( ogrGeometryToQgsLineString( OGR_G_GetGeometryRef( geom, i ) ).release() );
662  }
663 
664  return mp;
665 }
666 
667 std::unique_ptr< QgsPolygon > ogrGeometryToQgsPolygon( OGRGeometryH geom )
668 {
669  std::unique_ptr< QgsPolygon > polygon = std::make_unique< QgsPolygon >();
670 
671  const int count = OGR_G_GetGeometryCount( geom );
672  if ( count >= 1 )
673  {
674  polygon->setExteriorRing( ogrGeometryToQgsLineString( OGR_G_GetGeometryRef( geom, 0 ) ).release() );
675  }
676 
677  for ( int i = 1; i < count; ++i )
678  {
679  polygon->addInteriorRing( ogrGeometryToQgsLineString( OGR_G_GetGeometryRef( geom, i ) ).release() );
680  }
681 
682  return polygon;
683 }
684 
685 std::unique_ptr< QgsMultiPolygon > ogrGeometryToQgsMultiPolygon( OGRGeometryH geom )
686 {
687  std::unique_ptr< QgsMultiPolygon > polygon = std::make_unique< QgsMultiPolygon >();
688 
689  const int count = OGR_G_GetGeometryCount( geom );
690  polygon->reserve( count );
691  for ( int i = 0; i < count; ++i )
692  {
693  polygon->addGeometry( ogrGeometryToQgsPolygon( OGR_G_GetGeometryRef( geom, i ) ).release() );
694  }
695 
696  return polygon;
697 }
698 
700 {
701  switch ( ogrGeomType )
702  {
703  case wkbUnknown: return QgsWkbTypes::Type::Unknown;
704  case wkbPoint: return QgsWkbTypes::Type::Point;
705  case wkbLineString: return QgsWkbTypes::Type::LineString;
706  case wkbPolygon: return QgsWkbTypes::Type::Polygon;
707  case wkbMultiPoint: return QgsWkbTypes::Type::MultiPoint;
708  case wkbMultiLineString: return QgsWkbTypes::Type::MultiLineString;
709  case wkbMultiPolygon: return QgsWkbTypes::Type::MultiPolygon;
710  case wkbGeometryCollection: return QgsWkbTypes::Type::GeometryCollection;
711  case wkbCircularString: return QgsWkbTypes::Type::CircularString;
712  case wkbCompoundCurve: return QgsWkbTypes::Type::CompoundCurve;
713  case wkbCurvePolygon: return QgsWkbTypes::Type::CurvePolygon;
714  case wkbMultiCurve: return QgsWkbTypes::Type::MultiCurve;
715  case wkbMultiSurface: return QgsWkbTypes::Type::MultiSurface;
716  case wkbCurve: return QgsWkbTypes::Type::Unknown; // not an actual concrete type
717  case wkbSurface: return QgsWkbTypes::Type::Unknown; // not an actual concrete type
718  case wkbPolyhedralSurface: return QgsWkbTypes::Type::Unknown; // no actual matching
719  case wkbTIN: return QgsWkbTypes::Type::Unknown; // no actual matching
720  case wkbTriangle: return QgsWkbTypes::Type::Triangle;
721 
722  case wkbNone: return QgsWkbTypes::Type::NoGeometry;
723  case wkbLinearRing: return QgsWkbTypes::Type::LineString; // approximate match
724 
725  case wkbCircularStringZ: return QgsWkbTypes::Type::CircularStringZ;
726  case wkbCompoundCurveZ: return QgsWkbTypes::Type::CompoundCurveZ;
727  case wkbCurvePolygonZ: return QgsWkbTypes::Type::CurvePolygonZ;
728  case wkbMultiCurveZ: return QgsWkbTypes::Type::MultiCurveZ;
729  case wkbMultiSurfaceZ: return QgsWkbTypes::Type::MultiSurfaceZ;
730  case wkbCurveZ: return QgsWkbTypes::Type::Unknown; // not an actual concrete type
731  case wkbSurfaceZ: return QgsWkbTypes::Type::Unknown; // not an actual concrete type
732  case wkbPolyhedralSurfaceZ: return QgsWkbTypes::Type::Unknown; // no actual matching
733  case wkbTINZ: return QgsWkbTypes::Type::Unknown; // no actual matching
734  case wkbTriangleZ: return QgsWkbTypes::Type::TriangleZ;
735 
736  case wkbPointM: return QgsWkbTypes::Type::PointM;
737  case wkbLineStringM: return QgsWkbTypes::Type::LineStringM;
738  case wkbPolygonM: return QgsWkbTypes::Type::PolygonM;
739  case wkbMultiPointM: return QgsWkbTypes::Type::MultiPointM;
740  case wkbMultiLineStringM: return QgsWkbTypes::Type::MultiLineStringM;
741  case wkbMultiPolygonM: return QgsWkbTypes::Type::MultiPolygonM;
742  case wkbGeometryCollectionM: return QgsWkbTypes::Type::GeometryCollectionM;
743  case wkbCircularStringM: return QgsWkbTypes::Type::CircularStringM;
744  case wkbCompoundCurveM: return QgsWkbTypes::Type::CompoundCurveM;
745  case wkbCurvePolygonM: return QgsWkbTypes::Type::CurvePolygonM;
746  case wkbMultiCurveM: return QgsWkbTypes::Type::MultiCurveM;
747  case wkbMultiSurfaceM: return QgsWkbTypes::Type::MultiSurfaceM;
748  case wkbCurveM: return QgsWkbTypes::Type::Unknown; // not an actual concrete type
749  case wkbSurfaceM: return QgsWkbTypes::Type::Unknown; // not an actual concrete type
750  case wkbPolyhedralSurfaceM: return QgsWkbTypes::Type::Unknown; // no actual matching
751  case wkbTINM: return QgsWkbTypes::Type::Unknown; // no actual matching
752  case wkbTriangleM: return QgsWkbTypes::Type::TriangleM;
753 
754  case wkbPointZM: return QgsWkbTypes::Type::PointZM;
755  case wkbLineStringZM: return QgsWkbTypes::Type::LineStringZM;
756  case wkbPolygonZM: return QgsWkbTypes::Type::PolygonZM;
757  case wkbMultiPointZM: return QgsWkbTypes::Type::MultiPointZM;
758  case wkbMultiLineStringZM: return QgsWkbTypes::Type::MultiLineStringZM;
759  case wkbMultiPolygonZM: return QgsWkbTypes::Type::MultiPolygonZM;
760  case wkbGeometryCollectionZM: return QgsWkbTypes::Type::GeometryCollectionZM;
761  case wkbCircularStringZM: return QgsWkbTypes::Type::CircularStringZM;
762  case wkbCompoundCurveZM: return QgsWkbTypes::Type::CompoundCurveZM;
763  case wkbCurvePolygonZM: return QgsWkbTypes::Type::CurvePolygonZM;
764  case wkbMultiCurveZM: return QgsWkbTypes::Type::MultiCurveZM;
765  case wkbMultiSurfaceZM: return QgsWkbTypes::Type::MultiSurfaceZM;
766  case wkbCurveZM: return QgsWkbTypes::Type::Unknown; // not an actual concrete type
767  case wkbSurfaceZM: return QgsWkbTypes::Type::Unknown; // not an actual concrete type
768  case wkbPolyhedralSurfaceZM: return QgsWkbTypes::Type::Unknown; // no actual matching
769  case wkbTINZM: return QgsWkbTypes::Type::Unknown; // no actual matching
770  case wkbTriangleZM: return QgsWkbTypes::Type::TriangleZM;
771 
772  case wkbPoint25D: return QgsWkbTypes::Type::PointZ;
773  case wkbLineString25D: return QgsWkbTypes::Type::LineStringZ;
774  case wkbPolygon25D: return QgsWkbTypes::Type::PolygonZ;
775  case wkbMultiPoint25D: return QgsWkbTypes::Type::MultiPointZ;
776  case wkbMultiLineString25D: return QgsWkbTypes::Type::MultiLineStringZ;
777  case wkbMultiPolygon25D: return QgsWkbTypes::Type::MultiPolygonZ;
778  case wkbGeometryCollection25D: return QgsWkbTypes::Type::GeometryCollectionZ;
779  }
780 
781  // should not reach that point normally
782  return QgsWkbTypes::Type::Unknown;
783 }
784 
786 {
787  if ( !geom )
788  return QgsGeometry();
789 
790  const auto ogrGeomType = OGR_G_GetGeometryType( geom );
791  QgsWkbTypes::Type wkbType = ogrGeometryTypeToQgsWkbType( ogrGeomType );
792 
793  // optimised case for some geometry classes, avoiding wkb conversion on OGR/QGIS sides
794  // TODO - extend to other classes!
795  switch ( QgsWkbTypes::flatType( wkbType ) )
796  {
797  case QgsWkbTypes::Point:
798  {
799  return QgsGeometry( ogrGeometryToQgsPoint( geom ) );
800  }
801 
803  {
804  return QgsGeometry( ogrGeometryToQgsMultiPoint( geom ) );
805  }
806 
808  {
809  return QgsGeometry( ogrGeometryToQgsLineString( geom ) );
810  }
811 
813  {
815  }
816 
818  {
819  return QgsGeometry( ogrGeometryToQgsPolygon( geom ) );
820  }
821 
823  {
824  return QgsGeometry( ogrGeometryToQgsMultiPolygon( geom ) );
825  }
826 
827  default:
828  break;
829  }
830 
831  // Fallback to inefficient WKB conversions
832 
833  if ( wkbFlatten( wkbType ) == wkbGeometryCollection )
834  {
835  // Shapefile MultiPatch can be reported as GeometryCollectionZ of TINZ
836  if ( OGR_G_GetGeometryCount( geom ) >= 1 &&
837  wkbFlatten( OGR_G_GetGeometryType( OGR_G_GetGeometryRef( geom, 0 ) ) ) == wkbTIN )
838  {
839  auto newGeom = OGR_G_ForceToMultiPolygon( OGR_G_Clone( geom ) );
840  auto ret = ogrGeometryToQgsGeometry( newGeom );
841  OGR_G_DestroyGeometry( newGeom );
842  return ret;
843  }
844  }
845 
846  // get the wkb representation
847  int memorySize = OGR_G_WkbSize( geom );
848  unsigned char *wkb = new unsigned char[memorySize];
849  OGR_G_ExportToWkb( geom, static_cast<OGRwkbByteOrder>( QgsApplication::endian() ), wkb );
850 
851  // Read original geometry type
852  uint32_t origGeomType;
853  memcpy( &origGeomType, wkb + 1, sizeof( uint32_t ) );
854  bool hasZ = ( origGeomType >= 1000 && origGeomType < 2000 ) || ( origGeomType >= 3000 && origGeomType < 4000 );
855  bool hasM = ( origGeomType >= 2000 && origGeomType < 3000 ) || ( origGeomType >= 3000 && origGeomType < 4000 );
856 
857  // PolyhedralSurface and TINs are not supported, map them to multipolygons...
858  if ( origGeomType % 1000 == 16 ) // is TIN, TINZ, TINM or TINZM
859  {
860  // TIN has the same wkb layout as a multipolygon, just need to overwrite the geom types...
861  int nDims = 2 + hasZ + hasM;
862  uint32_t newMultiType = static_cast<uint32_t>( QgsWkbTypes::zmType( QgsWkbTypes::MultiPolygon, hasZ, hasM ) );
863  uint32_t newSingleType = static_cast<uint32_t>( QgsWkbTypes::zmType( QgsWkbTypes::Polygon, hasZ, hasM ) );
864  unsigned char *wkbptr = wkb;
865 
866  // Endianness
867  wkbptr += 1;
868 
869  // Overwrite geom type
870  memcpy( wkbptr, &newMultiType, sizeof( uint32_t ) );
871  wkbptr += 4;
872 
873  // Geom count
874  uint32_t numGeoms;
875  memcpy( &numGeoms, wkb + 5, sizeof( uint32_t ) );
876  wkbptr += 4;
877 
878  // For each part, overwrite the geometry type to polygon (Z|M)
879  for ( uint32_t i = 0; i < numGeoms; ++i )
880  {
881  // Endianness
882  wkbptr += 1;
883 
884  // Overwrite geom type
885  memcpy( wkbptr, &newSingleType, sizeof( uint32_t ) );
886  wkbptr += sizeof( uint32_t );
887 
888  // skip coordinates
889  uint32_t nRings;
890  memcpy( &nRings, wkbptr, sizeof( uint32_t ) );
891  wkbptr += sizeof( uint32_t );
892 
893  for ( uint32_t j = 0; j < nRings; ++j )
894  {
895  uint32_t nPoints;
896  memcpy( &nPoints, wkbptr, sizeof( uint32_t ) );
897  wkbptr += sizeof( uint32_t ) + sizeof( double ) * nDims * nPoints;
898  }
899  }
900  }
901  else if ( origGeomType % 1000 == 15 ) // PolyhedralSurface, PolyhedralSurfaceZ, PolyhedralSurfaceM or PolyhedralSurfaceZM
902  {
903  // PolyhedralSurface has the same wkb layout as a MultiPolygon, just need to overwrite the geom type...
904  uint32_t newType = static_cast<uint32_t>( QgsWkbTypes::zmType( QgsWkbTypes::MultiPolygon, hasZ, hasM ) );
905  // Overwrite geom type
906  memcpy( wkb + 1, &newType, sizeof( uint32_t ) );
907  }
908 
909  QgsGeometry g;
910  g.fromWkb( wkb, memorySize );
911  return g;
912 }
913 
914 QgsFeatureList QgsOgrUtils::stringToFeatureList( const QString &string, const QgsFields &fields, QTextCodec *encoding )
915 {
916  QgsFeatureList features;
917  if ( string.isEmpty() )
918  return features;
919 
920  QString randomFileName = QStringLiteral( "/vsimem/%1" ).arg( QUuid::createUuid().toString() );
921 
922  // create memory file system object from string buffer
923  QByteArray ba = string.toUtf8();
924  VSIFCloseL( VSIFileFromMemBuffer( randomFileName.toUtf8().constData(), reinterpret_cast< GByte * >( ba.data() ),
925  static_cast< vsi_l_offset >( ba.size() ), FALSE ) );
926 
927  gdal::ogr_datasource_unique_ptr hDS( OGROpen( randomFileName.toUtf8().constData(), false, nullptr ) );
928  if ( !hDS )
929  {
930  VSIUnlink( randomFileName.toUtf8().constData() );
931  return features;
932  }
933 
934  OGRLayerH ogrLayer = OGR_DS_GetLayer( hDS.get(), 0 );
935  if ( !ogrLayer )
936  {
937  hDS.reset();
938  VSIUnlink( randomFileName.toUtf8().constData() );
939  return features;
940  }
941 
943  while ( oFeat.reset( OGR_L_GetNextFeature( ogrLayer ) ), oFeat )
944  {
945  QgsFeature feat = readOgrFeature( oFeat.get(), fields, encoding );
946  if ( feat.isValid() )
947  features << feat;
948  }
949 
950  hDS.reset();
951  VSIUnlink( randomFileName.toUtf8().constData() );
952 
953  return features;
954 }
955 
956 QgsFields QgsOgrUtils::stringToFields( const QString &string, QTextCodec *encoding )
957 {
958  QgsFields fields;
959  if ( string.isEmpty() )
960  return fields;
961 
962  QString randomFileName = QStringLiteral( "/vsimem/%1" ).arg( QUuid::createUuid().toString() );
963 
964  // create memory file system object from buffer
965  QByteArray ba = string.toUtf8();
966  VSIFCloseL( VSIFileFromMemBuffer( randomFileName.toUtf8().constData(), reinterpret_cast< GByte * >( ba.data() ),
967  static_cast< vsi_l_offset >( ba.size() ), FALSE ) );
968 
969  gdal::ogr_datasource_unique_ptr hDS( OGROpen( randomFileName.toUtf8().constData(), false, nullptr ) );
970  if ( !hDS )
971  {
972  VSIUnlink( randomFileName.toUtf8().constData() );
973  return fields;
974  }
975 
976  OGRLayerH ogrLayer = OGR_DS_GetLayer( hDS.get(), 0 );
977  if ( !ogrLayer )
978  {
979  hDS.reset();
980  VSIUnlink( randomFileName.toUtf8().constData() );
981  return fields;
982  }
983 
985  //read in the first feature only
986  if ( oFeat.reset( OGR_L_GetNextFeature( ogrLayer ) ), oFeat )
987  {
988  fields = readOgrFields( oFeat.get(), encoding );
989  }
990 
991  hDS.reset();
992  VSIUnlink( randomFileName.toUtf8().constData() );
993  return fields;
994 }
995 
996 QStringList QgsOgrUtils::cStringListToQStringList( char **stringList )
997 {
998  QStringList strings;
999 
1000  // presume null terminated string list
1001  for ( qgssize i = 0; stringList[i]; ++i )
1002  {
1003  strings.append( QString::fromUtf8( stringList[i] ) );
1004  }
1005 
1006  return strings;
1007 }
1008 
1010 {
1011  if ( !srs )
1012  return QString();
1013 
1014  char *pszWkt = nullptr;
1015  const QByteArray multiLineOption = QStringLiteral( "MULTILINE=NO" ).toLocal8Bit();
1016  const QByteArray formatOption = QStringLiteral( "FORMAT=WKT2" ).toLocal8Bit();
1017  const char *const options[] = {multiLineOption.constData(), formatOption.constData(), nullptr};
1018  OSRExportToWktEx( srs, &pszWkt, options );
1019 
1020  const QString res( pszWkt );
1021  CPLFree( pszWkt );
1022  return res;
1023 }
1024 
1026 {
1027  const QString wkt = OGRSpatialReferenceToWkt( srs );
1028  if ( wkt.isEmpty() )
1030 
1031  const char *authorityName = OSRGetAuthorityName( srs, nullptr );
1032  const char *authorityCode = OSRGetAuthorityCode( srs, nullptr );
1034  if ( authorityName && authorityCode )
1035  {
1036  QString authId = QString( authorityName ) + ':' + QString( authorityCode );
1037  OGRSpatialReferenceH ogrSrsTmp = OSRNewSpatialReference( nullptr );
1038  // Check that the CRS build from authId and the input one are the "same".
1039  if ( OSRSetFromUserInput( ogrSrsTmp, authId.toUtf8().constData() ) != OGRERR_NONE &&
1040  OSRIsSame( srs, ogrSrsTmp ) )
1041  {
1043  res.createFromUserInput( authId );
1044  }
1045  OSRDestroySpatialReference( ogrSrsTmp );
1046  }
1047  if ( !res.isValid() )
1049 
1050 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,4,0)
1051  const double coordinateEpoch = OSRGetCoordinateEpoch( srs );
1052  if ( coordinateEpoch > 0 )
1053  res.setCoordinateEpoch( coordinateEpoch );
1054 #endif
1055  return res;
1056 }
1057 
1059 {
1060  if ( crs.isValid() )
1061  {
1062  OGRSpatialReferenceH ogrSrs = nullptr;
1063 
1064  // First try instantiating the CRS from its authId. This will give a
1065  // more complete representation of the CRS for GDAL. In particular it might
1066  // help a few drivers to get the datum code, that would be missing in WKT-2.
1067  // See https://github.com/OSGeo/gdal/pull/5218
1068  const QString authId = crs.authid();
1070  if ( !authId.isEmpty() )
1071  {
1072  ogrSrs = OSRNewSpatialReference( nullptr );
1073  if ( OSRSetFromUserInput( ogrSrs, authId.toUtf8().constData() ) == OGRERR_NONE )
1074  {
1075  // Check that the CRS build from WKT and authId are the "same".
1076  OGRSpatialReferenceH ogrSrsFromWkt = OSRNewSpatialReference( srsWkt.toUtf8().constData() );
1077  if ( ogrSrsFromWkt )
1078  {
1079  if ( !OSRIsSame( ogrSrs, ogrSrsFromWkt ) )
1080  {
1081  OSRDestroySpatialReference( ogrSrs );
1082  ogrSrs = ogrSrsFromWkt;
1083  }
1084  else
1085  {
1086  OSRDestroySpatialReference( ogrSrsFromWkt );
1087  }
1088  }
1089  }
1090  else
1091  {
1092  OSRDestroySpatialReference( ogrSrs );
1093  ogrSrs = nullptr;
1094  }
1095  }
1096  if ( !ogrSrs )
1097  {
1098  ogrSrs = OSRNewSpatialReference( srsWkt.toUtf8().constData() );
1099  }
1100  if ( ogrSrs )
1101  {
1102  OSRSetAxisMappingStrategy( ogrSrs, OAMS_TRADITIONAL_GIS_ORDER );
1103 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,4,0)
1104  if ( !std::isnan( crs.coordinateEpoch() ) )
1105  {
1106  OSRSetCoordinateEpoch( ogrSrs, crs.coordinateEpoch() );
1107  }
1108 #endif
1109  return ogrSrs;
1110  }
1111  }
1112 
1113  return nullptr;
1114 }
1115 
1116 QString QgsOgrUtils::readShapefileEncoding( const QString &path )
1117 {
1118  const QString cpgEncoding = readShapefileEncodingFromCpg( path );
1119  if ( !cpgEncoding.isEmpty() )
1120  return cpgEncoding;
1121 
1122  return readShapefileEncodingFromLdid( path );
1123 }
1124 
1125 QString QgsOgrUtils::readShapefileEncodingFromCpg( const QString &path )
1126 {
1127 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,1,0)
1128  QString errCause;
1129  QgsOgrLayerUniquePtr layer = QgsOgrProviderUtils::getLayer( path, false, QStringList(), 0, errCause, false );
1130  return layer ? layer->GetMetadataItem( QStringLiteral( "ENCODING_FROM_CPG" ), QStringLiteral( "SHAPEFILE" ) ) : QString();
1131 #else
1132  if ( !QFileInfo::exists( path ) )
1133  return QString();
1134 
1135  // first try to read cpg file, if present
1136  const QFileInfo fi( path );
1137  const QString baseName = fi.completeBaseName();
1138  const QString cpgPath = fi.dir().filePath( QStringLiteral( "%1.%2" ).arg( baseName, fi.suffix() == QLatin1String( "SHP" ) ? QStringLiteral( "CPG" ) : QStringLiteral( "cpg" ) ) );
1139  if ( QFile::exists( cpgPath ) )
1140  {
1141  QFile cpgFile( cpgPath );
1142  if ( cpgFile.open( QIODevice::ReadOnly ) )
1143  {
1144  QTextStream cpgStream( &cpgFile );
1145  const QString cpgString = cpgStream.readLine();
1146  cpgFile.close();
1147 
1148  if ( !cpgString.isEmpty() )
1149  {
1150  // from OGRShapeLayer::ConvertCodePage
1151  // https://github.com/OSGeo/gdal/blob/master/gdal/ogr/ogrsf_frmts/shape/ogrshapelayer.cpp#L342
1152  bool ok = false;
1153  int cpgCodePage = cpgString.toInt( &ok );
1154  if ( ok && ( ( cpgCodePage >= 437 && cpgCodePage <= 950 )
1155  || ( cpgCodePage >= 1250 && cpgCodePage <= 1258 ) ) )
1156  {
1157  return QStringLiteral( "CP%1" ).arg( cpgCodePage );
1158  }
1159  else if ( cpgString.startsWith( QLatin1String( "8859" ) ) )
1160  {
1161  if ( cpgString.length() > 4 && cpgString.at( 4 ) == '-' )
1162  return QStringLiteral( "ISO-8859-%1" ).arg( cpgString.mid( 5 ) );
1163  else
1164  return QStringLiteral( "ISO-8859-%1" ).arg( cpgString.mid( 4 ) );
1165  }
1166  else if ( cpgString.startsWith( QLatin1String( "UTF-8" ), Qt::CaseInsensitive ) ||
1167  cpgString.startsWith( QLatin1String( "UTF8" ), Qt::CaseInsensitive ) )
1168  return QStringLiteral( "UTF-8" );
1169  else if ( cpgString.startsWith( QLatin1String( "ANSI 1251" ), Qt::CaseInsensitive ) )
1170  return QStringLiteral( "CP1251" );
1171 
1172  return cpgString;
1173  }
1174  }
1175  }
1176 
1177  return QString();
1178 #endif
1179 }
1180 
1181 QString QgsOgrUtils::readShapefileEncodingFromLdid( const QString &path )
1182 {
1183 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,1,0)
1184  QString errCause;
1185  QgsOgrLayerUniquePtr layer = QgsOgrProviderUtils::getLayer( path, false, QStringList(), 0, errCause, false );
1186  return layer ? layer->GetMetadataItem( QStringLiteral( "ENCODING_FROM_LDID" ), QStringLiteral( "SHAPEFILE" ) ) : QString();
1187 #else
1188  // from OGRShapeLayer::ConvertCodePage
1189  // https://github.com/OSGeo/gdal/blob/master/gdal/ogr/ogrsf_frmts/shape/ogrshapelayer.cpp#L342
1190 
1191  if ( !QFileInfo::exists( path ) )
1192  return QString();
1193 
1194  // first try to read cpg file, if present
1195  const QFileInfo fi( path );
1196  const QString baseName = fi.completeBaseName();
1197 
1198  // fallback to LDID value, read from DBF file
1199  const QString dbfPath = fi.dir().filePath( QStringLiteral( "%1.%2" ).arg( baseName, fi.suffix() == QLatin1String( "SHP" ) ? QStringLiteral( "DBF" ) : QStringLiteral( "dbf" ) ) );
1200  if ( QFile::exists( dbfPath ) )
1201  {
1202  QFile dbfFile( dbfPath );
1203  if ( dbfFile.open( QIODevice::ReadOnly ) )
1204  {
1205  dbfFile.read( 29 );
1206  QDataStream dbfIn( &dbfFile );
1207  dbfIn.setByteOrder( QDataStream::LittleEndian );
1208  quint8 ldid;
1209  dbfIn >> ldid;
1210  dbfFile.close();
1211 
1212  int nCP = -1; // Windows code page.
1213 
1214  // http://www.autopark.ru/ASBProgrammerGuide/DBFSTRUC.HTM
1215  switch ( ldid )
1216  {
1217  case 1: nCP = 437; break;
1218  case 2: nCP = 850; break;
1219  case 3: nCP = 1252; break;
1220  case 4: nCP = 10000; break;
1221  case 8: nCP = 865; break;
1222  case 10: nCP = 850; break;
1223  case 11: nCP = 437; break;
1224  case 13: nCP = 437; break;
1225  case 14: nCP = 850; break;
1226  case 15: nCP = 437; break;
1227  case 16: nCP = 850; break;
1228  case 17: nCP = 437; break;
1229  case 18: nCP = 850; break;
1230  case 19: nCP = 932; break;
1231  case 20: nCP = 850; break;
1232  case 21: nCP = 437; break;
1233  case 22: nCP = 850; break;
1234  case 23: nCP = 865; break;
1235  case 24: nCP = 437; break;
1236  case 25: nCP = 437; break;
1237  case 26: nCP = 850; break;
1238  case 27: nCP = 437; break;
1239  case 28: nCP = 863; break;
1240  case 29: nCP = 850; break;
1241  case 31: nCP = 852; break;
1242  case 34: nCP = 852; break;
1243  case 35: nCP = 852; break;
1244  case 36: nCP = 860; break;
1245  case 37: nCP = 850; break;
1246  case 38: nCP = 866; break;
1247  case 55: nCP = 850; break;
1248  case 64: nCP = 852; break;
1249  case 77: nCP = 936; break;
1250  case 78: nCP = 949; break;
1251  case 79: nCP = 950; break;
1252  case 80: nCP = 874; break;
1253  case 87: return QStringLiteral( "ISO-8859-1" );
1254  case 88: nCP = 1252; break;
1255  case 89: nCP = 1252; break;
1256  case 100: nCP = 852; break;
1257  case 101: nCP = 866; break;
1258  case 102: nCP = 865; break;
1259  case 103: nCP = 861; break;
1260  case 104: nCP = 895; break;
1261  case 105: nCP = 620; break;
1262  case 106: nCP = 737; break;
1263  case 107: nCP = 857; break;
1264  case 108: nCP = 863; break;
1265  case 120: nCP = 950; break;
1266  case 121: nCP = 949; break;
1267  case 122: nCP = 936; break;
1268  case 123: nCP = 932; break;
1269  case 124: nCP = 874; break;
1270  case 134: nCP = 737; break;
1271  case 135: nCP = 852; break;
1272  case 136: nCP = 857; break;
1273  case 150: nCP = 10007; break;
1274  case 151: nCP = 10029; break;
1275  case 200: nCP = 1250; break;
1276  case 201: nCP = 1251; break;
1277  case 202: nCP = 1254; break;
1278  case 203: nCP = 1253; break;
1279  case 204: nCP = 1257; break;
1280  default: break;
1281  }
1282 
1283  if ( nCP != -1 )
1284  {
1285  return QStringLiteral( "CP%1" ).arg( nCP );
1286  }
1287  }
1288  }
1289  return QString();
1290 #endif
1291 }
1292 
1293 QVariantMap QgsOgrUtils::parseStyleString( const QString &string )
1294 {
1295  QVariantMap styles;
1296 
1297  char **papszStyleString = CSLTokenizeString2( string.toUtf8().constData(), ";",
1298  CSLT_HONOURSTRINGS
1299  | CSLT_PRESERVEQUOTES
1300  | CSLT_PRESERVEESCAPES );
1301  for ( int i = 0; papszStyleString[i] != nullptr; ++i )
1302  {
1303  // style string format is:
1304  // <tool_name>([<tool_param>[,<tool_param>[,...]]])
1305 
1306  // first extract tool name
1307  const thread_local QRegularExpression sToolPartRx( QStringLiteral( "^(.*?)\\((.*)\\)$" ) );
1308  const QString stylePart( papszStyleString[i] );
1309  const QRegularExpressionMatch match = sToolPartRx.match( stylePart );
1310  if ( !match.hasMatch() )
1311  continue;
1312 
1313  const QString tool = match.captured( 1 );
1314  const QString params = match.captured( 2 );
1315 
1316  char **papszTokens = CSLTokenizeString2( params.toUtf8().constData(), ",", CSLT_HONOURSTRINGS
1317  | CSLT_PRESERVEESCAPES );
1318 
1319  QVariantMap toolParts;
1320  const thread_local QRegularExpression sToolParamRx( QStringLiteral( "^(.*?):(.*)$" ) );
1321  for ( int j = 0; papszTokens[j] != nullptr; ++j )
1322  {
1323  const QString toolPart( papszTokens[j] );
1324  const QRegularExpressionMatch toolMatch = sToolParamRx.match( toolPart );
1325  if ( !match.hasMatch() )
1326  continue;
1327 
1328  // note we always convert the keys to lowercase, just to be safe...
1329  toolParts.insert( toolMatch.captured( 1 ).toLower(), toolMatch.captured( 2 ) );
1330  }
1331  CSLDestroy( papszTokens );
1332 
1333  // note we always convert the keys to lowercase, just to be safe...
1334  styles.insert( tool.toLower(), toolParts );
1335  }
1336  CSLDestroy( papszStyleString );
1337  return styles;
1338 }
1339 
1340 std::unique_ptr<QgsSymbol> QgsOgrUtils::symbolFromStyleString( const QString &string, Qgis::SymbolType type )
1341 {
1342  const QVariantMap styles = parseStyleString( string );
1343 
1344  auto convertSize = []( const QString & size, double & value, QgsUnitTypes::RenderUnit & unit )->bool
1345  {
1346  const thread_local QRegularExpression sUnitRx = QRegularExpression( QStringLiteral( "^([\\d\\.]+)(g|px|pt|mm|cm|in)$" ) );
1347  const QRegularExpressionMatch match = sUnitRx.match( size );
1348  if ( match.hasMatch() )
1349  {
1350  value = match.captured( 1 ).toDouble();
1351  const QString unitString = match.captured( 2 );
1352  if ( unitString.compare( QLatin1String( "px" ), Qt::CaseInsensitive ) == 0 )
1353  {
1354  // pixels are a poor unit choice for QGIS -- they render badly in hidpi layouts. Convert to points instead, using
1355  // a 96 dpi conversion
1356  static constexpr double PT_TO_INCHES_FACTOR = 1 / 72.0;
1357  static constexpr double PX_TO_PT_FACTOR = 1 / ( 96.0 * PT_TO_INCHES_FACTOR );
1359  value *= PX_TO_PT_FACTOR;
1360  return true;
1361  }
1362  else if ( unitString.compare( QLatin1String( "pt" ), Qt::CaseInsensitive ) == 0 )
1363  {
1365  return true;
1366  }
1367  else if ( unitString.compare( QLatin1String( "mm" ), Qt::CaseInsensitive ) == 0 )
1368  {
1370  return true;
1371  }
1372  else if ( unitString.compare( QLatin1String( "cm" ), Qt::CaseInsensitive ) == 0 )
1373  {
1374  value *= 10;
1376  return true;
1377  }
1378  else if ( unitString.compare( QLatin1String( "in" ), Qt::CaseInsensitive ) == 0 )
1379  {
1381  return true;
1382  }
1383  else if ( unitString.compare( QLatin1String( "g" ), Qt::CaseInsensitive ) == 0 )
1384  {
1386  return true;
1387  }
1388  QgsDebugMsg( QStringLiteral( "Unknown unit %1" ).arg( unitString ) );
1389  }
1390  else
1391  {
1392  QgsDebugMsg( QStringLiteral( "Could not parse style size %1" ).arg( size ) );
1393  }
1394  return false;
1395  };
1396 
1397  auto convertColor = []( const QString & string ) -> QColor
1398  {
1399  if ( string.isEmpty() )
1400  return QColor();
1401 
1402  const thread_local QRegularExpression sColorWithAlphaRx = QRegularExpression( QStringLiteral( "^#([0-9a-fA-F]{6})([0-9a-fA-F]{2})$" ) );
1403  const QRegularExpressionMatch match = sColorWithAlphaRx.match( string );
1404  if ( match.hasMatch() )
1405  {
1406  // need to convert #RRGGBBAA to #AARRGGBB for QColor
1407  return QColor( QStringLiteral( "#%1%2" ).arg( match.captured( 2 ), match.captured( 1 ) ) );
1408  }
1409  else
1410  {
1411  return QColor( string );
1412  }
1413  };
1414 
1415  auto convertPen = [&convertColor, &convertSize, string]( const QVariantMap & lineStyle ) -> std::unique_ptr< QgsSymbol >
1416  {
1417  QColor color = convertColor( lineStyle.value( QStringLiteral( "c" ), QStringLiteral( "#000000" ) ).toString() );
1418 
1419  double lineWidth = DEFAULT_SIMPLELINE_WIDTH;
1421  convertSize( lineStyle.value( QStringLiteral( "w" ) ).toString(), lineWidth, lineWidthUnit );
1422 
1423  // if the pen is a mapinfo pen, use dedicated converter for more accurate results
1424  const thread_local QRegularExpression sMapInfoId = QRegularExpression( QStringLiteral( "mapinfo-pen-(\\d+)" ) );
1425  const QRegularExpressionMatch match = sMapInfoId.match( string );
1426  if ( match.hasMatch() )
1427  {
1428  const int penId = match.captured( 1 ).toInt();
1430  std::unique_ptr<QgsSymbol> res( QgsMapInfoSymbolConverter::convertLineSymbol( penId, context, color, lineWidth, lineWidthUnit ) );
1431  if ( res )
1432  return res;
1433  }
1434 
1435  std::unique_ptr< QgsSimpleLineSymbolLayer > simpleLine = std::make_unique< QgsSimpleLineSymbolLayer >( color, lineWidth );
1436  simpleLine->setWidthUnit( lineWidthUnit );
1437 
1438  // pattern
1439  const QString pattern = lineStyle.value( QStringLiteral( "p" ) ).toString();
1440  if ( !pattern.isEmpty() )
1441  {
1442  const thread_local QRegularExpression sPatternUnitRx = QRegularExpression( QStringLiteral( "^([\\d\\.\\s]+)(g|px|pt|mm|cm|in)$" ) );
1443  const QRegularExpressionMatch match = sPatternUnitRx.match( pattern );
1444  if ( match.hasMatch() )
1445  {
1446  const QStringList patternValues = match.captured( 1 ).split( ' ' );
1447  QVector< qreal > dashPattern;
1449  for ( const QString &val : patternValues )
1450  {
1451  double length;
1452  convertSize( val + match.captured( 2 ), length, patternUnits );
1453  dashPattern.push_back( length * lineWidth * 2 );
1454  }
1455 
1456  simpleLine->setCustomDashVector( dashPattern );
1457  simpleLine->setCustomDashPatternUnit( patternUnits );
1458  simpleLine->setUseCustomDashPattern( true );
1459  }
1460  }
1461 
1462  Qt::PenCapStyle capStyle = Qt::FlatCap;
1463  Qt::PenJoinStyle joinStyle = Qt::MiterJoin;
1464  // workaround https://github.com/OSGeo/gdal/pull/3509 in older GDAL versions
1465  const QString id = lineStyle.value( QStringLiteral( "id" ) ).toString();
1466  if ( id.contains( QLatin1String( "mapinfo-pen" ), Qt::CaseInsensitive ) )
1467  {
1468  // MapInfo renders all lines using a round pen cap and round pen join
1469  // which are not the default values for OGR pen cap/join styles. So we need to explicitly
1470  // override the OGR default values here on older GDAL versions
1471  capStyle = Qt::RoundCap;
1472  joinStyle = Qt::RoundJoin;
1473  }
1474 
1475  // pen cap
1476  const QString penCap = lineStyle.value( QStringLiteral( "cap" ) ).toString();
1477  if ( penCap.compare( QLatin1String( "b" ), Qt::CaseInsensitive ) == 0 )
1478  {
1479  capStyle = Qt::FlatCap;
1480  }
1481  else if ( penCap.compare( QLatin1String( "r" ), Qt::CaseInsensitive ) == 0 )
1482  {
1483  capStyle = Qt::RoundCap;
1484  }
1485  else if ( penCap.compare( QLatin1String( "p" ), Qt::CaseInsensitive ) == 0 )
1486  {
1487  capStyle = Qt::SquareCap;
1488  }
1489  simpleLine->setPenCapStyle( capStyle );
1490 
1491  // pen join
1492  const QString penJoin = lineStyle.value( QStringLiteral( "j" ) ).toString();
1493  if ( penJoin.compare( QLatin1String( "m" ), Qt::CaseInsensitive ) == 0 )
1494  {
1495  joinStyle = Qt::MiterJoin;
1496  }
1497  else if ( penJoin.compare( QLatin1String( "r" ), Qt::CaseInsensitive ) == 0 )
1498  {
1499  joinStyle = Qt::RoundJoin;
1500  }
1501  else if ( penJoin.compare( QLatin1String( "b" ), Qt::CaseInsensitive ) == 0 )
1502  {
1503  joinStyle = Qt::BevelJoin;
1504  }
1505  simpleLine->setPenJoinStyle( joinStyle );
1506 
1507  const QString priority = lineStyle.value( QStringLiteral( "l" ) ).toString();
1508  if ( !priority.isEmpty() )
1509  {
1510  simpleLine->setRenderingPass( priority.toInt() );
1511  }
1512  return std::make_unique< QgsLineSymbol >( QgsSymbolLayerList() << simpleLine.release() );
1513  };
1514 
1515  auto convertBrush = [&convertColor]( const QVariantMap & brushStyle ) -> std::unique_ptr< QgsSymbol >
1516  {
1517  const QColor foreColor = convertColor( brushStyle.value( QStringLiteral( "fc" ), QStringLiteral( "#000000" ) ).toString() );
1518  const QColor backColor = convertColor( brushStyle.value( QStringLiteral( "bc" ), QString() ).toString() );
1519 
1520  const QString id = brushStyle.value( QStringLiteral( "id" ) ).toString();
1521 
1522  // if the pen is a mapinfo brush, use dedicated converter for more accurate results
1523  const thread_local QRegularExpression sMapInfoId = QRegularExpression( QStringLiteral( "mapinfo-brush-(\\d+)" ) );
1524  const QRegularExpressionMatch match = sMapInfoId.match( id );
1525  if ( match.hasMatch() )
1526  {
1527  const int brushId = match.captured( 1 ).toInt();
1529  std::unique_ptr<QgsSymbol> res( QgsMapInfoSymbolConverter::convertFillSymbol( brushId, context, foreColor, backColor ) );
1530  if ( res )
1531  return res;
1532  }
1533 
1534  const thread_local QRegularExpression sOgrId = QRegularExpression( QStringLiteral( "ogr-brush-(\\d+)" ) );
1535  const QRegularExpressionMatch ogrMatch = sOgrId.match( id );
1536 
1537  Qt::BrushStyle style = Qt::SolidPattern;
1538  if ( ogrMatch.hasMatch() )
1539  {
1540  const int brushId = ogrMatch.captured( 1 ).toInt();
1541  switch ( brushId )
1542  {
1543  case 0:
1544  style = Qt::SolidPattern;
1545  break;
1546 
1547  case 1:
1548  style = Qt::NoBrush;
1549  break;
1550 
1551  case 2:
1552  style = Qt::HorPattern;
1553  break;
1554 
1555  case 3:
1556  style = Qt::VerPattern;
1557  break;
1558 
1559  case 4:
1560  style = Qt::FDiagPattern;
1561  break;
1562 
1563  case 5:
1564  style = Qt::BDiagPattern;
1565  break;
1566 
1567  case 6:
1568  style = Qt::CrossPattern;
1569  break;
1570 
1571  case 7:
1572  style = Qt::DiagCrossPattern;
1573  break;
1574  }
1575  }
1576 
1577  QgsSymbolLayerList layers;
1578  if ( backColor.isValid() && style != Qt::SolidPattern && style != Qt::NoBrush )
1579  {
1580  std::unique_ptr< QgsSimpleFillSymbolLayer > backgroundFill = std::make_unique< QgsSimpleFillSymbolLayer >( backColor );
1581  backgroundFill->setLocked( true );
1582  backgroundFill->setStrokeStyle( Qt::NoPen );
1583  layers << backgroundFill.release();
1584  }
1585 
1586  std::unique_ptr< QgsSimpleFillSymbolLayer > foregroundFill = std::make_unique< QgsSimpleFillSymbolLayer >( foreColor );
1587  foregroundFill->setBrushStyle( style );
1588  foregroundFill->setStrokeStyle( Qt::NoPen );
1589 
1590  const QString priority = brushStyle.value( QStringLiteral( "l" ) ).toString();
1591  if ( !priority.isEmpty() )
1592  {
1593  foregroundFill->setRenderingPass( priority.toInt() );
1594  }
1595  layers << foregroundFill.release();
1596  return std::make_unique< QgsFillSymbol >( layers );
1597  };
1598 
1599  auto convertSymbol = [&convertColor, &convertSize, string]( const QVariantMap & symbolStyle ) -> std::unique_ptr< QgsSymbol >
1600  {
1601  const QColor color = convertColor( symbolStyle.value( QStringLiteral( "c" ), QStringLiteral( "#000000" ) ).toString() );
1602 
1603  double symbolSize = DEFAULT_SIMPLEMARKER_SIZE;
1605  convertSize( symbolStyle.value( QStringLiteral( "s" ) ).toString(), symbolSize, symbolSizeUnit );
1606 
1607  const double angle = symbolStyle.value( QStringLiteral( "a" ), QStringLiteral( "0" ) ).toDouble();
1608 
1609  const QString id = symbolStyle.value( QStringLiteral( "id" ) ).toString();
1610 
1611  // if the symbol is a mapinfo symbol, use dedicated converter for more accurate results
1612  const thread_local QRegularExpression sMapInfoId = QRegularExpression( QStringLiteral( "mapinfo-sym-(\\d+)" ) );
1613  const QRegularExpressionMatch match = sMapInfoId.match( id );
1614  if ( match.hasMatch() )
1615  {
1616  const int symbolId = match.captured( 1 ).toInt();
1618 
1619  // ogr interpretations of mapinfo symbol sizes are too large -- scale these down
1620  symbolSize *= 0.61;
1621 
1622  std::unique_ptr<QgsSymbol> res( QgsMapInfoSymbolConverter::convertMarkerSymbol( symbolId, context, color, symbolSize, symbolSizeUnit ) );
1623  if ( res )
1624  return res;
1625  }
1626 
1627  std::unique_ptr< QgsMarkerSymbolLayer > markerLayer;
1628 
1629  const thread_local QRegularExpression sFontId = QRegularExpression( QStringLiteral( "font-sym-(\\d+)" ) );
1630  const QRegularExpressionMatch fontMatch = sFontId.match( id );
1631  if ( fontMatch.hasMatch() )
1632  {
1633  const int symId = fontMatch.captured( 1 ).toInt();
1634  const QStringList families = symbolStyle.value( QStringLiteral( "f" ), QString() ).toString().split( ',' );
1635 
1636  bool familyFound = false;
1637  QString fontFamily;
1638  for ( const QString &family : std::as_const( families ) )
1639  {
1640  if ( QgsFontUtils::fontFamilyMatchOnSystem( family ) )
1641  {
1642  familyFound = true;
1643  fontFamily = family;
1644  break;
1645  }
1646  }
1647 
1648  if ( familyFound )
1649  {
1650  std::unique_ptr< QgsFontMarkerSymbolLayer > fontMarker = std::make_unique< QgsFontMarkerSymbolLayer >( fontFamily, QChar( symId ), symbolSize );
1651  fontMarker->setSizeUnit( symbolSizeUnit );
1652  fontMarker->setAngle( -angle );
1653 
1654  fontMarker->setColor( color );
1655 
1656  const QColor strokeColor = convertColor( symbolStyle.value( QStringLiteral( "o" ), QString() ).toString() );
1657  if ( strokeColor.isValid() )
1658  {
1659  fontMarker->setStrokeColor( strokeColor );
1660  fontMarker->setStrokeWidth( 1 );
1661  fontMarker->setStrokeWidthUnit( QgsUnitTypes::RenderPoints );
1662  }
1663  else
1664  {
1665  fontMarker->setStrokeWidth( 0 );
1666  }
1667 
1668  markerLayer = std::move( fontMarker );
1669  }
1670  else if ( !families.empty() )
1671  {
1672  // couldn't even find a matching font in the backup list
1673  QgsMessageLog::logMessage( QObject::tr( "Font %1 not found on system" ).arg( families.at( 0 ) ) );
1674  }
1675  }
1676 
1677  if ( !markerLayer )
1678  {
1679  const thread_local QRegularExpression sOgrId = QRegularExpression( QStringLiteral( "ogr-sym-(\\d+)" ) );
1680  const QRegularExpressionMatch ogrMatch = sOgrId.match( id );
1681 
1682  Qgis::MarkerShape shape;
1683  bool isFilled = true;
1684  if ( ogrMatch.hasMatch() )
1685  {
1686  const int symId = ogrMatch.captured( 1 ).toInt();
1687  switch ( symId )
1688  {
1689  case 0:
1690  shape = Qgis::MarkerShape::Cross;
1691  break;
1692 
1693  case 1:
1694  shape = Qgis::MarkerShape::Cross2;
1695  break;
1696 
1697  case 2:
1698  isFilled = false;
1699  shape = Qgis::MarkerShape::Circle;
1700  break;
1701 
1702  case 3:
1703  shape = Qgis::MarkerShape::Circle;
1704  break;
1705 
1706  case 4:
1707  isFilled = false;
1708  shape = Qgis::MarkerShape::Square;
1709  break;
1710 
1711  case 5:
1712  shape = Qgis::MarkerShape::Square;
1713  break;
1714 
1715  case 6:
1716  isFilled = false;
1718  break;
1719 
1720  case 7:
1722  break;
1723 
1724  case 8:
1725  isFilled = false;
1726  shape = Qgis::MarkerShape::Star;
1727  break;
1728 
1729  case 9:
1730  shape = Qgis::MarkerShape::Star;
1731  break;
1732 
1733  case 10:
1734  shape = Qgis::MarkerShape::Line;
1735  break;
1736 
1737  default:
1738  isFilled = false;
1739  shape = Qgis::MarkerShape::Square; // to initialize the variable
1740  break;
1741  }
1742  }
1743  else
1744  {
1745  isFilled = false;
1746  shape = Qgis::MarkerShape::Square; // to initialize the variable
1747  }
1748 
1749  std::unique_ptr< QgsSimpleMarkerSymbolLayer > simpleMarker = std::make_unique< QgsSimpleMarkerSymbolLayer >( shape, symbolSize, -angle );
1750  simpleMarker->setSizeUnit( symbolSizeUnit );
1751 
1752  if ( isFilled && QgsSimpleMarkerSymbolLayer::shapeIsFilled( shape ) )
1753  {
1754  simpleMarker->setColor( color );
1755  simpleMarker->setStrokeStyle( Qt::NoPen );
1756  }
1757  else
1758  {
1759  simpleMarker->setFillColor( QColor( 0, 0, 0, 0 ) );
1760  simpleMarker->setStrokeColor( color );
1761  }
1762 
1763  const QColor strokeColor = convertColor( symbolStyle.value( QStringLiteral( "o" ), QString() ).toString() );
1764  if ( strokeColor.isValid() )
1765  {
1766  simpleMarker->setStrokeColor( strokeColor );
1767  simpleMarker->setStrokeStyle( Qt::SolidLine );
1768  }
1769 
1770  markerLayer = std::move( simpleMarker );
1771  }
1772 
1773  return std::make_unique< QgsMarkerSymbol >( QgsSymbolLayerList() << markerLayer.release() );
1774  };
1775 
1776  switch ( type )
1777  {
1779  if ( styles.contains( QStringLiteral( "symbol" ) ) )
1780  {
1781  const QVariantMap symbolStyle = styles.value( QStringLiteral( "symbol" ) ).toMap();
1782  return convertSymbol( symbolStyle );
1783  }
1784  else
1785  {
1786  return nullptr;
1787  }
1788 
1790  if ( styles.contains( QStringLiteral( "pen" ) ) )
1791  {
1792  // line symbol type
1793  const QVariantMap lineStyle = styles.value( QStringLiteral( "pen" ) ).toMap();
1794  return convertPen( lineStyle );
1795  }
1796  else
1797  {
1798  return nullptr;
1799  }
1800 
1802  {
1803  std::unique_ptr< QgsSymbol > fillSymbol = std::make_unique< QgsFillSymbol >();
1804  if ( styles.contains( QStringLiteral( "brush" ) ) )
1805  {
1806  const QVariantMap brushStyle = styles.value( QStringLiteral( "brush" ) ).toMap();
1807  fillSymbol = convertBrush( brushStyle );
1808  }
1809  else
1810  {
1811  std::unique_ptr< QgsSimpleFillSymbolLayer > emptyFill = std::make_unique< QgsSimpleFillSymbolLayer >();
1812  emptyFill->setBrushStyle( Qt::NoBrush );
1813  fillSymbol = std::make_unique< QgsFillSymbol >( QgsSymbolLayerList() << emptyFill.release() );
1814  }
1815 
1816  std::unique_ptr< QgsSymbol > penSymbol;
1817  if ( styles.contains( QStringLiteral( "pen" ) ) )
1818  {
1819  const QVariantMap lineStyle = styles.value( QStringLiteral( "pen" ) ).toMap();
1820  penSymbol = convertPen( lineStyle );
1821  }
1822 
1823  if ( penSymbol )
1824  {
1825  const int count = penSymbol->symbolLayerCount();
1826 
1827  if ( count == 1 )
1828  {
1829  // if only one pen symbol layer, let's try and combine it with the topmost brush layer, so that the resultant QGIS symbol is simpler
1830  if ( QgsSymbolLayerUtils::condenseFillAndOutline( dynamic_cast< QgsFillSymbolLayer * >( fillSymbol->symbolLayer( fillSymbol->symbolLayerCount() - 1 ) ),
1831  dynamic_cast< QgsLineSymbolLayer * >( penSymbol->symbolLayer( 0 ) ) ) )
1832  return fillSymbol;
1833  }
1834 
1835  for ( int i = 0; i < count; ++i )
1836  {
1837  std::unique_ptr< QgsSymbolLayer > layer( penSymbol->takeSymbolLayer( 0 ) );
1838  layer->setLocked( true );
1839  fillSymbol->appendSymbolLayer( layer.release() );
1840  }
1841  }
1842 
1843  return fillSymbol;
1844  }
1845 
1847  break;
1848  }
1849 
1850  return nullptr;
1851 }
1852 
1853 void QgsOgrUtils::ogrFieldTypeToQVariantType( OGRFieldType ogrType, OGRFieldSubType ogrSubType, QVariant::Type &variantType, QVariant::Type &variantSubType )
1854 {
1855  variantType = QVariant::Type::Invalid;
1856  variantSubType = QVariant::Type::Invalid;
1857 
1858  switch ( ogrType )
1859  {
1860  case OFTInteger:
1861  if ( ogrSubType == OFSTBoolean )
1862  {
1863  variantType = QVariant::Bool;
1864  ogrSubType = OFSTBoolean;
1865  }
1866  else
1867  variantType = QVariant::Int;
1868  break;
1869  case OFTInteger64:
1870  variantType = QVariant::LongLong;
1871  break;
1872  case OFTReal:
1873  variantType = QVariant::Double;
1874  break;
1875  case OFTDate:
1876  variantType = QVariant::Date;
1877  break;
1878  case OFTTime:
1879  variantType = QVariant::Time;
1880  break;
1881  case OFTDateTime:
1882  variantType = QVariant::DateTime;
1883  break;
1884 
1885  case OFTBinary:
1886  variantType = QVariant::ByteArray;
1887  break;
1888 
1889  case OFTString:
1890  case OFTWideString:
1891  if ( ogrSubType == OFSTJSON )
1892  {
1893  ogrSubType = OFSTJSON;
1894  variantType = QVariant::Map;
1895  variantSubType = QVariant::String;
1896  }
1897  else
1898  {
1899  variantType = QVariant::String;
1900  }
1901  break;
1902 
1903  case OFTStringList:
1904  case OFTWideStringList:
1905  variantType = QVariant::StringList;
1906  variantSubType = QVariant::String;
1907  break;
1908 
1909  case OFTIntegerList:
1910  variantType = QVariant::List;
1911  variantSubType = QVariant::Int;
1912  break;
1913 
1914  case OFTRealList:
1915  variantType = QVariant::List;
1916  variantSubType = QVariant::Double;
1917  break;
1918 
1919  case OFTInteger64List:
1920  variantType = QVariant::List;
1921  variantSubType = QVariant::LongLong;
1922  break;
1923  }
1924 }
1925 
1926 void QgsOgrUtils::variantTypeToOgrFieldType( QVariant::Type variantType, OGRFieldType &ogrType, OGRFieldSubType &ogrSubType )
1927 {
1928  ogrSubType = OFSTNone;
1929  switch ( variantType )
1930  {
1931  case QVariant::Bool:
1932  ogrType = OFTInteger;
1933  ogrSubType = OFSTBoolean;
1934  break;
1935 
1936  case QVariant::Int:
1937  ogrType = OFTInteger;
1938  break;
1939 
1940  case QVariant::LongLong:
1941  ogrType = OFTInteger64;
1942  break;
1943 
1944  case QVariant::Double:
1945  ogrType = OFTReal;
1946  break;
1947 
1948  case QVariant::Char:
1949  ogrType = OFTString;
1950  break;
1951 
1952  case QVariant::String:
1953  ogrType = OFTString;
1954  break;
1955 
1956  case QVariant::StringList:
1957  ogrType = OFTStringList;
1958  break;
1959 
1960  case QVariant::ByteArray:
1961  ogrType = OFTBinary;
1962  break;
1963 
1964  case QVariant::Date:
1965  ogrType = OFTDate;
1966  break;
1967 
1968  case QVariant::Time:
1969  ogrType = OFTTime;
1970  break;
1971  case QVariant::DateTime:
1972  ogrType = OFTDateTime;
1973  break;
1974 
1975  default:
1976  ogrType = OFTString;
1977  break;
1978  }
1979 }
1980 
1981 QVariant QgsOgrUtils::stringToVariant( OGRFieldType type, OGRFieldSubType, const QString &string )
1982 {
1983  if ( string.isEmpty() )
1984  return QVariant();
1985 
1986  bool ok = false;
1987  QVariant res;
1988  switch ( type )
1989  {
1990  case OFTInteger:
1991  res = string.toInt( &ok );
1992  break;
1993 
1994  case OFTInteger64:
1995  res = string.toLongLong( &ok );
1996  break;
1997 
1998  case OFTReal:
1999  res = string.toDouble( &ok );
2000  break;
2001 
2002  case OFTString:
2003  case OFTWideString:
2004  res = string;
2005  ok = true;
2006  break;
2007 
2008  case OFTDate:
2009  res = QDate::fromString( string, Qt::ISODate );
2010  ok = res.isValid();
2011  break;
2012 
2013  case OFTTime:
2014  res = QTime::fromString( string, Qt::ISODate );
2015  ok = res.isValid();
2016  break;
2017 
2018  case OFTDateTime:
2019  res = QDateTime::fromString( string, Qt::ISODate );
2020  ok = res.isValid();
2021  break;
2022 
2023  default:
2024  res = string;
2025  ok = true;
2026  break;
2027  }
2028 
2029  return ok ? res : QVariant();
2030 }
2031 
2032 
2033 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,3,0)
2034 std::unique_ptr< QgsFieldDomain > QgsOgrUtils::convertFieldDomain( OGRFieldDomainH domain )
2035 {
2036  if ( !domain )
2037  return nullptr;
2038 
2039  const QString name{ OGR_FldDomain_GetName( domain ) };
2040  const QString description{ OGR_FldDomain_GetDescription( domain ) };
2041 
2042  QVariant::Type fieldType = QVariant::Type::Invalid;
2043  QVariant::Type fieldSubType = QVariant::Type::Invalid;
2044  const OGRFieldType domainFieldType = OGR_FldDomain_GetFieldType( domain );
2045  const OGRFieldSubType domainFieldSubType = OGR_FldDomain_GetFieldSubType( domain );
2046  ogrFieldTypeToQVariantType( domainFieldType, domainFieldSubType, fieldType, fieldSubType );
2047 
2048  std::unique_ptr< QgsFieldDomain > res;
2049  switch ( OGR_FldDomain_GetDomainType( domain ) )
2050  {
2051  case OFDT_CODED:
2052  {
2053  QList< QgsCodedValue > values;
2054  const OGRCodedValue *codedValue = OGR_CodedFldDomain_GetEnumeration( domain );
2055  while ( codedValue && codedValue->pszCode )
2056  {
2057  const QString code( codedValue->pszCode );
2058 
2059  // if pszValue is null then it indicates we are working with a set of acceptable values which aren't
2060  // coded. In this case we copy the code as the value so that QGIS exposes the domain as a choice of
2061  // the valid code values.
2062  const QString value( codedValue->pszValue ? codedValue->pszValue : codedValue->pszCode );
2063  values.append( QgsCodedValue( stringToVariant( domainFieldType, domainFieldSubType, code ), value ) );
2064 
2065  codedValue++;
2066  }
2067 
2068  res = std::make_unique< QgsCodedFieldDomain >( name, description, fieldType, values );
2069  break;
2070  }
2071 
2072  case OFDT_RANGE:
2073  {
2074  QVariant minValue;
2075  bool minIsInclusive = false;
2076  if ( const OGRField *min = OGR_RangeFldDomain_GetMin( domain, &minIsInclusive ) )
2077  {
2078  minValue = QgsOgrUtils::OGRFieldtoVariant( min, domainFieldType );
2079  }
2080  QVariant maxValue;
2081  bool maxIsInclusive = false;
2082  if ( const OGRField *max = OGR_RangeFldDomain_GetMax( domain, &maxIsInclusive ) )
2083  {
2084  maxValue = QgsOgrUtils::OGRFieldtoVariant( max, domainFieldType );
2085  }
2086 
2087  res = std::make_unique< QgsRangeFieldDomain >( name, description, fieldType,
2088  minValue, minIsInclusive,
2089  maxValue, maxIsInclusive );
2090  break;
2091  }
2092 
2093  case OFDT_GLOB:
2094  res = std::make_unique< QgsGlobFieldDomain >( name, description, fieldType,
2095  QString( OGR_GlobFldDomain_GetGlob( domain ) ) );
2096  break;
2097  }
2098 
2099  switch ( OGR_FldDomain_GetMergePolicy( domain ) )
2100  {
2101  case OFDMP_DEFAULT_VALUE:
2102  res->setMergePolicy( Qgis::FieldDomainMergePolicy::DefaultValue );
2103  break;
2104  case OFDMP_SUM:
2105  res->setMergePolicy( Qgis::FieldDomainMergePolicy::Sum );
2106  break;
2107  case OFDMP_GEOMETRY_WEIGHTED:
2108  res->setMergePolicy( Qgis::FieldDomainMergePolicy::GeometryWeighted );
2109  break;
2110  }
2111 
2112  switch ( OGR_FldDomain_GetSplitPolicy( domain ) )
2113  {
2114  case OFDSP_DEFAULT_VALUE:
2115  res->setSplitPolicy( Qgis::FieldDomainSplitPolicy::DefaultValue );
2116  break;
2117  case OFDSP_DUPLICATE:
2118  res->setSplitPolicy( Qgis::FieldDomainSplitPolicy::Duplicate );
2119  break;
2120  case OFDSP_GEOMETRY_RATIO:
2121  res->setSplitPolicy( Qgis::FieldDomainSplitPolicy::GeometryRatio );
2122  break;
2123  }
2124  return res;
2125 }
2126 
2127 OGRFieldDomainH QgsOgrUtils::convertFieldDomain( const QgsFieldDomain *domain )
2128 {
2129  if ( !domain )
2130  return nullptr;
2131 
2132  OGRFieldType domainFieldType = OFTInteger;
2133  OGRFieldSubType domainFieldSubType = OFSTNone;
2134  variantTypeToOgrFieldType( domain->fieldType(), domainFieldType, domainFieldSubType );
2135 
2136  OGRFieldDomainH res = nullptr;
2137  switch ( domain->type() )
2138  {
2140  {
2141  std::vector< OGRCodedValue > enumeration;
2142  const QList< QgsCodedValue> values = qgis::down_cast< const QgsCodedFieldDomain * >( domain )->values();
2143  enumeration.reserve( values.size() );
2144  for ( const QgsCodedValue &value : values )
2145  {
2146  OGRCodedValue codedValue;
2147  codedValue.pszCode = CPLStrdup( value.code().toString().toUtf8().constData() );
2148  codedValue.pszValue = CPLStrdup( value.value().toUtf8().constData() );
2149  enumeration.push_back( codedValue );
2150  }
2151  OGRCodedValue last;
2152  last.pszCode = nullptr;
2153  last.pszValue = nullptr;
2154  enumeration.push_back( last );
2155  res = OGR_CodedFldDomain_Create(
2156  domain->name().toUtf8().constData(),
2157  domain->description().toUtf8().constData(),
2158  domainFieldType,
2159  domainFieldSubType,
2160  enumeration.data()
2161  );
2162 
2163  for ( const OGRCodedValue &value : std::as_const( enumeration ) )
2164  {
2165  CPLFree( value.pszCode );
2166  CPLFree( value.pszValue );
2167  }
2168  break;
2169  }
2170 
2172  {
2173  std::unique_ptr< OGRField > min = variantToOGRField( qgis::down_cast< const QgsRangeFieldDomain * >( domain )->minimum() );
2174  std::unique_ptr< OGRField > max = variantToOGRField( qgis::down_cast< const QgsRangeFieldDomain * >( domain )->maximum() );
2175  res = OGR_RangeFldDomain_Create(
2176  domain->name().toUtf8().constData(),
2177  domain->description().toUtf8().constData(),
2178  domainFieldType,
2179  domainFieldSubType,
2180  min.get(),
2181  qgis::down_cast< const QgsRangeFieldDomain * >( domain )->minimumIsInclusive(),
2182  max.get(),
2183  qgis::down_cast< const QgsRangeFieldDomain * >( domain )->maximumIsInclusive()
2184  );
2185  break;
2186  }
2187 
2189  {
2190  res = OGR_GlobFldDomain_Create(
2191  domain->name().toUtf8().constData(),
2192  domain->description().toUtf8().constData(),
2193  domainFieldType,
2194  domainFieldSubType,
2195  qgis::down_cast< const QgsGlobFieldDomain * >( domain )->glob().toUtf8().constData()
2196  );
2197  break;
2198  }
2199  }
2200 
2201  switch ( domain->mergePolicy() )
2202  {
2204  OGR_FldDomain_SetMergePolicy( res, OFDMP_DEFAULT_VALUE );
2205  break;
2207  OGR_FldDomain_SetMergePolicy( res, OFDMP_GEOMETRY_WEIGHTED );
2208  break;
2210  OGR_FldDomain_SetMergePolicy( res, OFDMP_SUM );
2211  break;
2212  }
2213 
2214  switch ( domain->splitPolicy() )
2215  {
2217  OGR_FldDomain_SetSplitPolicy( res, OFDSP_DEFAULT_VALUE );
2218  break;
2220  OGR_FldDomain_SetSplitPolicy( res, OFDSP_GEOMETRY_RATIO );
2221  break;
2223  OGR_FldDomain_SetSplitPolicy( res, OFDSP_DUPLICATE );
2224  break;
2225  }
2226 
2227  return res;
2228 }
2229 
2230 #endif
@ GeometryWeighted
New values are computed as the weighted average of the source values.
@ DefaultValue
Use default field value.
@ GeometryRatio
New values are computed by the ratio of their area/length compared to the area/length of the original...
@ DefaultValue
Use default field value.
@ Duplicate
Duplicate original value.
MarkerShape
Marker shapes.
Definition: qgis.h:1206
@ Line
Vertical line.
@ Triangle
Triangle.
@ Cross2
Rotated cross (lines only), 'x' shape.
@ Cross
Cross (lines only)
@ Coded
Coded field domain.
@ Range
Numeric range field domain (min/max)
@ Glob
Glob string pattern field domain.
SymbolType
Symbol types.
Definition: qgis.h:205
@ Marker
Marker symbol.
@ Line
Line symbol.
@ Fill
Fill symbol.
@ Hybrid
Hybrid symbol.
static endian_t endian()
Returns whether this machine uses big or little endian.
Associates a code and a value.
This class represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
bool createFromUserInput(const QString &definition)
Set up this CRS from various text formats.
void setCoordinateEpoch(double epoch)
Sets the coordinate epoch, as a decimal year.
@ WKT_PREFERRED_GDAL
Preferred format for conversion of CRS to WKT for use with the GDAL library.
static QgsCoordinateReferenceSystem fromWkt(const QString &wkt)
Creates a CRS from a WKT spatial ref sys definition string.
double coordinateEpoch() const
Returns the coordinate epoch, as a decimal year.
QString toWkt(WktVariant variant=WKT1_GDAL, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
bool setAttribute(int field, const QVariant &attr)
Sets an attribute's value by field index.
Definition: qgsfeature.cpp:255
void initAttributes(int fieldCount)
Initialize this feature with the given number of fields.
Definition: qgsfeature.cpp:228
void setFields(const QgsFields &fields, bool initAttributes=false)
Assigns a field map with the feature to allow attribute access by attribute name.
Definition: qgsfeature.cpp:188
void setId(QgsFeatureId id)
Sets the feature id for this feature.
Definition: qgsfeature.cpp:115
void clearGeometry()
Removes any geometry associated with the feature.
Definition: qgsfeature.cpp:177
void setValid(bool validity)
Sets the validity of the feature.
Definition: qgsfeature.cpp:214
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:209
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:163
Base class for field domains.
Qgis::FieldDomainMergePolicy mergePolicy() const
Returns the merge policy.
Qgis::FieldDomainSplitPolicy splitPolicy() const
Returns the split policy.
virtual Qgis::FieldDomainType type() const =0
Returns the type of field domain.
QString name() const
Returns the name of the field domain.
QVariant::Type fieldType() const
Returns the associated field type.
QString description() const
Returns the description of the field domain.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:51
QVariant::Type type
Definition: qgsfield.h:58
QVariant::Type subType() const
If the field is a collection, gets its element's type.
Definition: qgsfield.cpp:134
Container of fields for a vector layer.
Definition: qgsfields.h:45
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Appends a field. The field must have unique name, otherwise it is rejected (returns false)
Definition: qgsfields.cpp:59
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
static bool fontFamilyMatchOnSystem(const QString &family, QString *chosen=nullptr, bool *match=nullptr)
Check whether font family is on system.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:125
void fromWkb(unsigned char *wkb, int length)
Set the geometry, feeding in the buffer containing OGC Well-Known Binary and the buffer's length.
Context for a MapInfo symbol conversion operation.
static QgsFillSymbol * convertFillSymbol(int identifier, QgsMapInfoSymbolConversionContext &context, const QColor &foreColor, const QColor &backColor=QColor())
Converts the MapInfo fill symbol with the specified identifier to a QgsFillSymbol.
static QgsLineSymbol * convertLineSymbol(int identifier, QgsMapInfoSymbolConversionContext &context, const QColor &foreColor, double size, QgsUnitTypes::RenderUnit sizeUnit, bool interleaved=false)
Converts the MapInfo line symbol with the specified identifier to a QgsLineSymbol.
static QgsMarkerSymbol * convertMarkerSymbol(int identifier, QgsMapInfoSymbolConversionContext &context, const QColor &color, double size, QgsUnitTypes::RenderUnit sizeUnit)
Converts the MapInfo marker symbol with the specified identifier to a QgsMarkerSymbol.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
static QString readShapefileEncoding(const QString &path)
Reads the encoding of the shapefile at the specified path (where path is the location of the "....
static QVariant stringToVariant(OGRFieldType type, OGRFieldSubType subType, const QString &string)
Converts a string to a variant, using the provider OGR field type and subType to determine the most a...
static bool readOgrFeatureAttributes(OGRFeatureH ogrFet, const QgsFields &fields, QgsFeature &feature, QTextCodec *encoding)
Reads all attributes from an OGR feature into a QgsFeature.
static QgsGeometry ogrGeometryToQgsGeometry(OGRGeometryH geom)
Converts an OGR geometry representation to a QgsGeometry object.
static void variantTypeToOgrFieldType(QVariant::Type variantType, OGRFieldType &ogrType, OGRFieldSubType &ogrSubType)
Converts an QVariant type to the best matching OGR field type and sub type.
static QString OGRSpatialReferenceToWkt(OGRSpatialReferenceH srs)
Returns a WKT string corresponding to the specified OGR srs object.
static OGRSpatialReferenceH crsToOGRSpatialReference(const QgsCoordinateReferenceSystem &crs)
Returns a OGRSpatialReferenceH corresponding to the specified crs object.
static QVariant OGRFieldtoVariant(const OGRField *value, OGRFieldType type)
Converts an OGRField value of the specified type into a QVariant.
static std::unique_ptr< OGRField > variantToOGRField(const QVariant &value)
Converts a QVariant to an OGRField value.
static QgsFeature readOgrFeature(OGRFeatureH ogrFet, const QgsFields &fields, QTextCodec *encoding)
Reads an OGR feature and converts it to a QgsFeature.
static QStringList cStringListToQStringList(char **stringList)
Converts a c string list to a QStringList.
static QgsFields readOgrFields(OGRFeatureH ogrFet, QTextCodec *encoding)
Reads an OGR feature and returns a corresponding fields collection.
static QgsWkbTypes::Type ogrGeometryTypeToQgsWkbType(OGRwkbGeometryType ogrGeomType)
Converts a OGRwkbGeometryType to QgsWkbTypes::Type.
static std::unique_ptr< QgsFieldDomain > convertFieldDomain(OGRFieldDomainH domain)
Converts an OGR field domain definition to a QgsFieldDomain equivalent.
static QgsCoordinateReferenceSystem OGRSpatialReferenceToCrs(OGRSpatialReferenceH srs)
Returns a QgsCoordinateReferenceSystem corresponding to the specified OGR srs object,...
static QgsFeatureList stringToFeatureList(const QString &string, const QgsFields &fields, QTextCodec *encoding)
Attempts to parse a string representing a collection of features using OGR.
static QString readShapefileEncodingFromCpg(const QString &path)
Reads the encoding of the shapefile at the specified path (where path is the location of the "....
static bool readOgrFeatureGeometry(OGRFeatureH ogrFet, QgsFeature &feature)
Reads the geometry from an OGR feature into a QgsFeature.
static QgsFields stringToFields(const QString &string, QTextCodec *encoding)
Attempts to retrieve the fields from a string representing a collection of features using OGR.
static QString readShapefileEncodingFromLdid(const QString &path)
Reads the encoding of the shapefile at the specified path (where path is the location of the "....
static std::unique_ptr< QgsSymbol > symbolFromStyleString(const QString &string, Qgis::SymbolType type)
Creates a new QgsSymbol matching an OGR style string.
static void ogrFieldTypeToQVariantType(OGRFieldType ogrType, OGRFieldSubType ogrSubType, QVariant::Type &variantType, QVariant::Type &variantSubType)
Converts an OGR field type and sub type to the best matching QVariant::Type equivalent.
static QVariantMap parseStyleString(const QString &string)
Parses an OGR style string to a variant map containing the style string components.
static QVariant getOgrFeatureAttribute(OGRFeatureH ogrFet, const QgsFields &fields, int attIndex, QTextCodec *encoding, bool *ok=nullptr)
Retrieves an attribute value from an OGR feature.
static bool shapeIsFilled(Qgis::MarkerShape shape)
Returns true if a symbol shape has a fill.
static bool condenseFillAndOutline(QgsFillSymbolLayer *fill, QgsLineSymbolLayer *outline)
Attempts to condense a fill and outline layer, by moving the outline layer to the fill symbol's strok...
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:168
@ RenderPoints
Points (e.g., for font sizes)
Definition: qgsunittypes.h:173
@ RenderInches
Inches.
Definition: qgsunittypes.h:174
@ RenderMillimeters
Millimeters.
Definition: qgsunittypes.h:169
@ RenderMapUnits
Map units.
Definition: qgsunittypes.h:170
static bool hasM(Type type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1130
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
static Type zmType(Type type, bool hasZ, bool hasM) SIP_HOLDGIL
Returns the modified input geometry type according to hasZ / hasM.
Definition: qgswkbtypes.h:831
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:732
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1080
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:786
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:79
std::unique_ptr< std::remove_pointer< OGRFeatureH >::type, OGRFeatureDeleter > ogr_feature_unique_ptr
Scoped OGR feature.
Definition: qgsogrutils.h:134
std::unique_ptr< std::remove_pointer< GDALDatasetH >::type, GDALDatasetCloser > dataset_unique_ptr
Scoped GDAL dataset.
Definition: qgsogrutils.h:139
std::unique_ptr< std::remove_pointer< OGRDataSourceH >::type, OGRDataSourceDeleter > ogr_datasource_unique_ptr
Scoped OGR data source.
Definition: qgsogrutils.h:119
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:2516
void * GDALDatasetH
void * OGRSpatialReferenceH
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:882
const QgsField & field
Definition: qgsfield.h:463
#define DEFAULT_SIMPLELINE_WIDTH
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
#define DEFAULT_SIMPLEMARKER_SIZE
std::unique_ptr< QgsLineString > ogrGeometryToQgsLineString(OGRGeometryH geom)
std::unique_ptr< QgsMultiLineString > ogrGeometryToQgsMultiLineString(OGRGeometryH geom)
std::unique_ptr< QgsMultiPoint > ogrGeometryToQgsMultiPoint(OGRGeometryH geom)
std::unique_ptr< QgsPolygon > ogrGeometryToQgsPolygon(OGRGeometryH geom)
std::unique_ptr< QgsPoint > ogrGeometryToQgsPoint(OGRGeometryH geom)
std::unique_ptr< QgsMultiPolygon > ogrGeometryToQgsMultiPolygon(OGRGeometryH geom)
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition: qgssymbol.h:27
const QgsCoordinateReferenceSystem & crs
void CORE_EXPORT operator()(GDALDatasetH datasource) const
Destroys an gdal dataset, using the correct gdal calls.
Definition: qgsogrutils.cpp:74
void CORE_EXPORT operator()(GDALWarpOptions *options) const
Destroys GDAL warp options, using the correct gdal calls.
Definition: qgsogrutils.cpp:99
void CORE_EXPORT operator()(OGRDataSourceH source) const
Destroys an OGR data source, using the correct gdal calls.
Definition: qgsogrutils.cpp:53
void CORE_EXPORT operator()(OGRFeatureH feature) const
Destroys an OGR feature, using the correct gdal calls.
Definition: qgsogrutils.cpp:69
void CORE_EXPORT operator()(OGRFieldDefnH definition) const
Destroys an OGR field definition, using the correct gdal calls.
Definition: qgsogrutils.cpp:64
void CORE_EXPORT operator()(OGRGeometryH geometry) const
Destroys an OGR geometry, using the correct gdal calls.
Definition: qgsogrutils.cpp:59