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