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