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