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