QGIS API Documentation 3.39.0-Master (d85f3c2a281)
Loading...
Searching...
No Matches
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 <limits>
50#include <QTextCodec>
51#include <QUuid>
52#include <cpl_error.h>
53#include <QJsonDocument>
54#include <QFileInfo>
55#include <QDir>
56#include <QTextStream>
57#include <QDataStream>
58#include <QRegularExpression>
59
60#include "ogr_srs_api.h"
61
62
63void gdal::OGRDataSourceDeleter::operator()( OGRDataSourceH source ) const
64{
65 OGR_DS_Destroy( source );
66}
67
68
69void gdal::OGRGeometryDeleter::operator()( OGRGeometryH geometry ) const
70{
71 OGR_G_DestroyGeometry( geometry );
72}
73
74void gdal::OGRFldDeleter::operator()( OGRFieldDefnH definition ) const
75{
76 OGR_Fld_Destroy( definition );
77}
78
79void gdal::OGRFeatureDeleter::operator()( OGRFeatureH feature ) const
80{
81 OGR_F_Destroy( feature );
82}
83
85{
86 GDALClose( dataset );
87}
88
89void gdal::fast_delete_and_close( gdal::dataset_unique_ptr &dataset, GDALDriverH driver, const QString &path )
90{
91 // see https://github.com/qgis/QGIS/commit/d024910490a39e65e671f2055c5b6543e06c7042#commitcomment-25194282
92 // faster if we close the handle AFTER delete, but doesn't work for windows
93#ifdef Q_OS_WIN
94 // close dataset handle
95 dataset.reset();
96#endif
97
98 CPLPushErrorHandler( CPLQuietErrorHandler );
99 GDALDeleteDataset( driver, path.toUtf8().constData() );
100 CPLPopErrorHandler();
101
102#ifndef Q_OS_WIN
103 // close dataset handle
104 dataset.reset();
105#endif
106}
107
108
109void gdal::GDALWarpOptionsDeleter::operator()( GDALWarpOptions *options ) const
110{
111 GDALDestroyWarpOptions( options );
112}
113
114#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,6,0)
115void gdal::GDALRelationshipDeleter::operator()( GDALRelationshipH relationship ) const
116{
117 GDALDestroyRelationship( relationship );
118}
119#endif
120
121static void setQTTimeZoneFromOGRTZFlag( QDateTime &dt, int nTZFlag )
122{
123 // Take into account time zone
124 if ( nTZFlag == 0 )
125 {
126 // unknown time zone
127 }
128 else if ( nTZFlag == 1 )
129 {
130 dt.setTimeSpec( Qt::LocalTime );
131 }
132 else if ( nTZFlag == 100 )
133 {
134 dt.setTimeSpec( Qt::UTC );
135 }
136 else
137 {
138 // TZFlag = 101 ==> UTC+00:15
139 // TZFlag = 99 ==> UTC-00:15
140 dt.setOffsetFromUtc( ( nTZFlag - 100 ) * 15 * 60 );
141 }
142}
143
144QVariant QgsOgrUtils::OGRFieldtoVariant( const OGRField *value, OGRFieldType type )
145{
146 if ( !value || OGR_RawField_IsUnset( value ) || OGR_RawField_IsNull( value ) )
147 return QVariant();
148
149 switch ( type )
150 {
151 case OFTInteger:
152 return value->Integer;
153
154 case OFTInteger64:
155 return value->Integer64;
156
157 case OFTReal:
158 return value->Real;
159
160 case OFTString:
161 case OFTWideString:
162 return QString::fromUtf8( value->String );
163
164 case OFTDate:
165 return QDate( value->Date.Year, value->Date.Month, value->Date.Day );
166
167 case OFTTime:
168 {
169 float secondsPart = 0;
170 float millisecondPart = std::modf( value->Date.Second, &secondsPart );
171 return QTime( value->Date.Hour, value->Date.Minute, static_cast< int >( secondsPart ), static_cast< int >( std::round( 1000 * millisecondPart ) ) );
172 }
173
174 case OFTDateTime:
175 {
176 float secondsPart = 0;
177 float millisecondPart = std::modf( value->Date.Second, &secondsPart );
178 QDateTime dt = QDateTime( QDate( value->Date.Year, value->Date.Month, value->Date.Day ),
179 QTime( value->Date.Hour, value->Date.Minute, static_cast< int >( secondsPart ), static_cast< int >( std::round( 1000 * millisecondPart ) ) ) );
180 setQTTimeZoneFromOGRTZFlag( dt, value->Date.TZFlag );
181 return dt;
182 }
183
184 case OFTBinary:
185 // not supported!
186 Q_ASSERT_X( false, "QgsOgrUtils::OGRFieldtoVariant", "OFTBinary type not supported" );
187 return QVariant();
188
189 case OFTIntegerList:
190 {
191 QVariantList res;
192 res.reserve( value->IntegerList.nCount );
193 for ( int i = 0; i < value->IntegerList.nCount; ++i )
194 res << value->IntegerList.paList[ i ];
195 return res;
196 }
197
198 case OFTInteger64List:
199 {
200 QVariantList res;
201 res.reserve( value->Integer64List.nCount );
202 for ( int i = 0; i < value->Integer64List.nCount; ++i )
203 res << value->Integer64List.paList[ i ];
204 return res;
205 }
206
207 case OFTRealList:
208 {
209 QVariantList res;
210 res.reserve( value->RealList.nCount );
211 for ( int i = 0; i < value->RealList.nCount; ++i )
212 res << value->RealList.paList[ i ];
213 return res;
214 }
215
216 case OFTStringList:
217 case OFTWideStringList:
218 {
219 QVariantList res;
220 res.reserve( value->StringList.nCount );
221 for ( int i = 0; i < value->StringList.nCount; ++i )
222 res << QString::fromUtf8( value->StringList.paList[ i ] );
223 return res;
224 }
225 }
226 return QVariant();
227}
228
229int QgsOgrUtils::OGRTZFlagFromQt( const QDateTime &datetime )
230{
231 if ( datetime.timeSpec() == Qt::LocalTime )
232 return 1;
233 return 100 + datetime.offsetFromUtc() / ( 60 * 15 );
234}
235
236std::unique_ptr< OGRField > QgsOgrUtils::variantToOGRField( const QVariant &value, OGRFieldType type )
237{
238 std::unique_ptr< OGRField > res = std::make_unique< OGRField >();
239
240 switch ( value.userType() )
241 {
242 case QMetaType::Type::UnknownType:
243 OGR_RawField_SetUnset( res.get() );
244 break;
245 case QMetaType::Type::Bool:
246 {
247 const int val = value.toBool() ? 1 : 0;
248 if ( type == OFTInteger )
249 res->Integer = val;
250 else if ( type == OFTInteger64 )
251 res->Integer64 = val;
252 else if ( type == OFTReal )
253 res->Real = val;
254 else
255 {
256 QgsDebugError( "Unsupported output data type for Bool" );
257 return nullptr;
258 }
259 break;
260 }
261 case QMetaType::Type::Int:
262 {
263 const int val = value.toInt();
264 if ( type == OFTInteger )
265 res->Integer = val;
266 else if ( type == OFTInteger64 )
267 res->Integer64 = val;
268 else if ( type == OFTReal )
269 res->Real = val;
270 else
271 {
272 QgsDebugError( "Unsupported output data type for Int" );
273 return nullptr;
274 }
275 break;
276 }
277 case QMetaType::Type::LongLong:
278 {
279 const qint64 val = value.toLongLong();
280 if ( type == OFTInteger )
281 {
282 if ( val <= std::numeric_limits<int>::max() &&
283 val >= std::numeric_limits<int>::min() )
284 {
285 res->Integer = static_cast<int>( val );
286 }
287 else
288 {
289 QgsDebugError( "Value does not fit on Integer" );
290 return nullptr;
291 }
292 }
293 else if ( type == OFTInteger64 )
294 res->Integer64 = val;
295 else if ( type == OFTReal )
296 {
297 res->Real = static_cast<double>( val );
298 }
299 else
300 {
301 QgsDebugError( "Unsupported output data type for LongLong" );
302 return nullptr;
303 }
304 break;
305 }
306 case QMetaType::Type::Double:
307 {
308 double val = value.toDouble();
309 if ( type == OFTInteger )
310 {
311 if ( val <= std::numeric_limits<int>::max() &&
312 val >= std::numeric_limits<int>::min() )
313 {
314 res->Integer = static_cast<int>( val );
315 }
316 else
317 {
318 QgsDebugError( "Value does not fit on Integer" );
319 return nullptr;
320 }
321 }
322 else if ( type == OFTInteger64 )
323 {
324 if ( val <= static_cast<double>( std::numeric_limits<qint64>::max() ) &&
325 val >= static_cast<double>( std::numeric_limits<qint64>::min() ) )
326 {
327 res->Integer64 = static_cast<qint64>( val );
328 }
329 else
330 {
331 QgsDebugError( "Value does not fit on Integer64" );
332 return nullptr;
333 }
334 }
335 else if ( type == OFTReal )
336 {
337 res->Real = val;
338 }
339 else
340 {
341 QgsDebugError( "Unsupported output data type for LongLong" );
342 return nullptr;
343 }
344 break;
345 }
346 case QMetaType::Type::QChar:
347 case QMetaType::Type::QString:
348 {
349 if ( type == OFTString )
350 res->String = CPLStrdup( value.toString().toUtf8().constData() );
351 else
352 {
353 QgsDebugError( "Unsupported output data type for String" );
354 return nullptr;
355 }
356 break;
357 }
358 case QMetaType::Type::QDate:
359 {
360 if ( type == OFTDate )
361 {
362 const QDate date = value.toDate();
363 res->Date.Day = date.day();
364 res->Date.Month = date.month();
365 res->Date.Year = static_cast<GInt16>( date.year() );
366 res->Date.TZFlag = 0;
367 }
368 else
369 {
370 QgsDebugError( "Unsupported output data type for Date" );
371 return nullptr;
372 }
373 break;
374 }
375 case QMetaType::Type::QTime:
376 {
377 if ( type == OFTTime )
378 {
379 const QTime time = value.toTime();
380 res->Date.Hour = time.hour();
381 res->Date.Minute = time.minute();
382 res->Date.Second = static_cast<float>( time.second() + static_cast< double >( time.msec() ) / 1000 );
383 res->Date.TZFlag = 0;
384 }
385 else
386 {
387 QgsDebugError( "Unsupported output data type for Time" );
388 return nullptr;
389 }
390 break;
391 }
392 case QMetaType::Type::QDateTime:
393 {
394 if ( type == OFTDateTime )
395 {
396 const QDateTime dt = value.toDateTime();
397 const QDate date = dt.date();
398 res->Date.Day = date.day();
399 res->Date.Month = date.month();
400 res->Date.Year = static_cast<GInt16>( date.year() );
401 const QTime time = dt.time();
402 res->Date.Hour = time.hour();
403 res->Date.Minute = time.minute();
404 res->Date.Second = static_cast<float>( time.second() + static_cast< double >( time.msec() ) / 1000 );
405 res->Date.TZFlag = OGRTZFlagFromQt( dt );
406 }
407 else
408 {
409 QgsDebugError( "Unsupported output data type for DateTime" );
410 return nullptr;
411 }
412 break;
413 }
414
415 default:
416 QgsDebugError( "Unhandled variant type in variantToOGRField" );
417 OGR_RawField_SetUnset( res.get() );
418 break;
419 }
420
421 return res;
422}
423
424QgsFeature QgsOgrUtils::readOgrFeature( OGRFeatureH ogrFet, const QgsFields &fields, QTextCodec *encoding )
425{
426 QgsFeature feature;
427 if ( !ogrFet )
428 {
429 feature.setValid( false );
430 return feature;
431 }
432
433 feature.setId( OGR_F_GetFID( ogrFet ) );
434 feature.setValid( true );
435
436 if ( !readOgrFeatureGeometry( ogrFet, feature ) )
437 {
438 feature.setValid( false );
439 }
440
441 if ( !readOgrFeatureAttributes( ogrFet, fields, feature, encoding ) )
442 {
443 feature.setValid( false );
444 }
445
446 return feature;
447}
448
449QgsFields QgsOgrUtils::readOgrFields( OGRFeatureH ogrFet, QTextCodec *encoding )
450{
451 QgsFields fields;
452
453 if ( !ogrFet )
454 return fields;
455
456 int fieldCount = OGR_F_GetFieldCount( ogrFet );
457 for ( int i = 0; i < fieldCount; ++i )
458 {
459 OGRFieldDefnH fldDef = OGR_F_GetFieldDefnRef( ogrFet, i );
460 if ( !fldDef )
461 {
462 fields.append( QgsField() );
463 continue;
464 }
465
466 QString name = encoding ? encoding->toUnicode( OGR_Fld_GetNameRef( fldDef ) ) : QString::fromUtf8( OGR_Fld_GetNameRef( fldDef ) );
467 QMetaType::Type varType;
468 switch ( OGR_Fld_GetType( fldDef ) )
469 {
470 case OFTInteger:
471 if ( OGR_Fld_GetSubType( fldDef ) == OFSTBoolean )
472 varType = QMetaType::Type::Bool;
473 else
474 varType = QMetaType::Type::Int;
475 break;
476 case OFTInteger64:
477 varType = QMetaType::Type::LongLong;
478 break;
479 case OFTReal:
480 varType = QMetaType::Type::Double;
481 break;
482 case OFTDate:
483 varType = QMetaType::Type::QDate;
484 break;
485 case OFTTime:
486 varType = QMetaType::Type::QTime;
487 break;
488 case OFTDateTime:
489 varType = QMetaType::Type::QDateTime;
490 break;
491 case OFTString:
492 if ( OGR_Fld_GetSubType( fldDef ) == OFSTJSON )
493 varType = QMetaType::Type::QVariantMap;
494 else
495 varType = QMetaType::Type::QString;
496 break;
497 default:
498 varType = QMetaType::Type::QString; // other unsupported, leave it as a string
499 }
500 fields.append( QgsField( name, varType ) );
501 }
502 return fields;
503}
504
505
506QVariant QgsOgrUtils::getOgrFeatureAttribute( OGRFeatureH ogrFet, const QgsFields &fields, int attIndex, QTextCodec *encoding, bool *ok )
507{
508 if ( attIndex < 0 || attIndex >= fields.count() )
509 {
510 if ( ok )
511 *ok = false;
512 return QVariant();
513 }
514
515 const QgsField field = fields.at( attIndex );
516 return getOgrFeatureAttribute( ogrFet, field, attIndex, encoding, ok );
517}
518
519QVariant QgsOgrUtils::getOgrFeatureAttribute( OGRFeatureH ogrFet, const QgsField &field, int attIndex, QTextCodec *encoding, bool *ok )
520{
521 if ( !ogrFet || attIndex < 0 )
522 {
523 if ( ok )
524 *ok = false;
525 return QVariant();
526 }
527
528 OGRFieldDefnH fldDef = OGR_F_GetFieldDefnRef( ogrFet, attIndex );
529
530 if ( ! fldDef )
531 {
532 if ( ok )
533 *ok = false;
534
535 QgsDebugError( QStringLiteral( "ogrFet->GetFieldDefnRef(attindex) returns NULL" ) );
536 return QVariant();
537 }
538
539 QVariant value;
540
541 if ( ok )
542 *ok = true;
543
544 if ( OGR_F_IsFieldSetAndNotNull( ogrFet, attIndex ) )
545 {
546 switch ( field.type() )
547 {
548 case QMetaType::Type::QString:
549 {
550 if ( encoding )
551 value = QVariant( encoding->toUnicode( OGR_F_GetFieldAsString( ogrFet, attIndex ) ) );
552 else
553 value = QVariant( QString::fromUtf8( OGR_F_GetFieldAsString( ogrFet, attIndex ) ) );
554
555#ifdef Q_OS_WIN
556 // Fixes GH #41076 (empty strings shown as NULL), because we have checked before that it was NOT NULL
557 // Note: QVariant( QString( ) ).isNull( ) is still true on windows so we really need string literal :(
558 if ( value.isNull() )
559 value = QVariant( QStringLiteral( "" ) ); // skip-keyword-check
560#endif
561
562 break;
563 }
564 case QMetaType::Type::Int:
565 value = QVariant( OGR_F_GetFieldAsInteger( ogrFet, attIndex ) );
566 break;
567 case QMetaType::Type::Bool:
568 value = QVariant( bool( OGR_F_GetFieldAsInteger( ogrFet, attIndex ) ) );
569 break;
570 case QMetaType::Type::LongLong:
571 value = QVariant( OGR_F_GetFieldAsInteger64( ogrFet, attIndex ) );
572 break;
573 case QMetaType::Type::Double:
574 value = QVariant( OGR_F_GetFieldAsDouble( ogrFet, attIndex ) );
575 break;
576 case QMetaType::Type::QDate:
577 case QMetaType::Type::QDateTime:
578 case QMetaType::Type::QTime:
579 {
580 int year, month, day, hour, minute, tzf;
581 float second;
582 float secondsPart = 0;
583
584 OGR_F_GetFieldAsDateTimeEx( ogrFet, attIndex, &year, &month, &day, &hour, &minute, &second, &tzf );
585 float millisecondPart = std::modf( second, &secondsPart );
586
587 if ( field.type() == QMetaType::Type::QDate )
588 value = QDate( year, month, day );
589 else if ( field.type() == QMetaType::Type::QTime )
590 value = QTime( hour, minute, static_cast< int >( secondsPart ), static_cast< int >( std::round( 1000 * millisecondPart ) ) );
591 else
592 {
593 QDateTime dt = QDateTime( QDate( year, month, day ),
594 QTime( hour, minute, static_cast< int >( secondsPart ), static_cast< int >( std::round( 1000 * millisecondPart ) ) ) );
595 setQTTimeZoneFromOGRTZFlag( dt, tzf );
596 value = dt;
597 }
598 }
599 break;
600
601 case QMetaType::Type::QByteArray:
602 {
603 int size = 0;
604 const GByte *b = OGR_F_GetFieldAsBinary( ogrFet, attIndex, &size );
605
606 // QByteArray::fromRawData is funny. It doesn't take ownership of the data, so we have to explicitly call
607 // detach on it to force a copy which owns the data
608 QByteArray ba = QByteArray::fromRawData( reinterpret_cast<const char *>( b ), size );
609 ba.detach();
610
611 value = ba;
612 break;
613 }
614
615 case QMetaType::Type::QStringList:
616 {
617 QStringList list;
618 char **lst = OGR_F_GetFieldAsStringList( ogrFet, attIndex );
619 const int count = CSLCount( lst );
620 if ( count > 0 )
621 {
622 list.reserve( count );
623 for ( int i = 0; i < count; i++ )
624 {
625 if ( encoding )
626 list << encoding->toUnicode( lst[i] );
627 else
628 list << QString::fromUtf8( lst[i] );
629 }
630 }
631 value = list;
632 break;
633 }
634
635 case QMetaType::Type::QVariantList:
636 {
637 switch ( field.subType() )
638 {
639 case QMetaType::Type::QString:
640 {
641 QStringList list;
642 char **lst = OGR_F_GetFieldAsStringList( ogrFet, attIndex );
643 const int count = CSLCount( lst );
644 if ( count > 0 )
645 {
646 list.reserve( count );
647 for ( int i = 0; i < count; i++ )
648 {
649 if ( encoding )
650 list << encoding->toUnicode( lst[i] );
651 else
652 list << QString::fromUtf8( lst[i] );
653 }
654 }
655 value = list;
656 break;
657 }
658
659 case QMetaType::Type::Int:
660 {
661 QVariantList list;
662 int count = 0;
663 const int *lst = OGR_F_GetFieldAsIntegerList( ogrFet, attIndex, &count );
664 if ( count > 0 )
665 {
666 list.reserve( count );
667 for ( int i = 0; i < count; i++ )
668 {
669 list << lst[i];
670 }
671 }
672 value = list;
673 break;
674 }
675
676 case QMetaType::Type::Double:
677 {
678 QVariantList list;
679 int count = 0;
680 const double *lst = OGR_F_GetFieldAsDoubleList( ogrFet, attIndex, &count );
681 if ( count > 0 )
682 {
683 list.reserve( count );
684 for ( int i = 0; i < count; i++ )
685 {
686 list << lst[i];
687 }
688 }
689 value = list;
690 break;
691 }
692
693 case QMetaType::Type::LongLong:
694 {
695 QVariantList list;
696 int count = 0;
697 const long long *lst = OGR_F_GetFieldAsInteger64List( ogrFet, attIndex, &count );
698 if ( count > 0 )
699 {
700 list.reserve( count );
701 for ( int i = 0; i < count; i++ )
702 {
703 list << lst[i];
704 }
705 }
706 value = list;
707 break;
708 }
709
710 default:
711 {
712 Q_ASSERT_X( false, "QgsOgrUtils::getOgrFeatureAttribute", "unsupported field type" );
713 if ( ok )
714 *ok = false;
715 break;
716 }
717 }
718 break;
719 }
720
721 case QMetaType::Type::QVariantMap:
722 {
723 //it has to be JSON
724 //it's null if no json format
725 if ( encoding )
726 value = QJsonDocument::fromJson( encoding->toUnicode( OGR_F_GetFieldAsString( ogrFet, attIndex ) ).toUtf8() ).toVariant();
727 else
728 value = QJsonDocument::fromJson( QString::fromUtf8( OGR_F_GetFieldAsString( ogrFet, attIndex ) ).toUtf8() ).toVariant();
729 break;
730 }
731 default:
732 Q_ASSERT_X( false, "QgsOgrUtils::getOgrFeatureAttribute", "unsupported field type" );
733 if ( ok )
734 *ok = false;
735 }
736 }
737 else
738 {
739 value = QgsVariantUtils::createNullVariant( field.type() );
740 }
741
742 return value;
743}
744
745bool QgsOgrUtils::readOgrFeatureAttributes( OGRFeatureH ogrFet, const QgsFields &fields, QgsFeature &feature, QTextCodec *encoding )
746{
747 // read all attributes
748 feature.initAttributes( fields.count() );
749 feature.setFields( fields );
750
751 if ( !ogrFet )
752 return false;
753
754 bool ok = false;
755 for ( int idx = 0; idx < fields.count(); ++idx )
756 {
757 QVariant value = getOgrFeatureAttribute( ogrFet, fields, idx, encoding, &ok );
758 if ( ok )
759 {
760 feature.setAttribute( idx, value );
761 }
762 }
763 return true;
764}
765
766bool QgsOgrUtils::readOgrFeatureGeometry( OGRFeatureH ogrFet, QgsFeature &feature )
767{
768 if ( !ogrFet )
769 return false;
770
771 OGRGeometryH geom = OGR_F_GetGeometryRef( ogrFet );
772 if ( !geom )
773 feature.clearGeometry();
774 else
775 feature.setGeometry( ogrGeometryToQgsGeometry( geom ) );
776
777 return true;
778}
779
780std::unique_ptr< QgsPoint > ogrGeometryToQgsPoint( OGRGeometryH geom )
781{
782 Qgis::WkbType wkbType = QgsOgrUtils::ogrGeometryTypeToQgsWkbType( OGR_G_GetGeometryType( geom ) );
783
784 double x, y, z, m;
785 OGR_G_GetPointZM( geom, 0, &x, &y, &z, &m );
786 return std::make_unique< QgsPoint >( wkbType, x, y, z, m );
787}
788
789std::unique_ptr< QgsMultiPoint > ogrGeometryToQgsMultiPoint( OGRGeometryH geom )
790{
791 std::unique_ptr< QgsMultiPoint > mp = std::make_unique< QgsMultiPoint >();
792
793 const int count = OGR_G_GetGeometryCount( geom );
794 mp->reserve( count );
795 for ( int i = 0; i < count; ++i )
796 {
797 mp->addGeometry( ogrGeometryToQgsPoint( OGR_G_GetGeometryRef( geom, i ) ).release() );
798 }
799
800 return mp;
801}
802
803std::unique_ptr< QgsLineString > ogrGeometryToQgsLineString( OGRGeometryH geom )
804{
805 Qgis::WkbType wkbType = QgsOgrUtils::ogrGeometryTypeToQgsWkbType( OGR_G_GetGeometryType( geom ) );
806
807 int count = OGR_G_GetPointCount( geom );
808 QVector< double > x( count );
809 QVector< double > y( count );
810 QVector< double > z;
811 double *pz = nullptr;
812 if ( QgsWkbTypes::hasZ( wkbType ) )
813 {
814 z.resize( count );
815 pz = z.data();
816 }
817 double *pm = nullptr;
818 QVector< double > m;
819 if ( QgsWkbTypes::hasM( wkbType ) )
820 {
821 m.resize( count );
822 pm = m.data();
823 }
824 OGR_G_GetPointsZM( geom, x.data(), sizeof( double ), y.data(), sizeof( double ), pz, sizeof( double ), pm, sizeof( double ) );
825
826 return std::make_unique< QgsLineString>( x, y, z, m, wkbType == Qgis::WkbType::LineString25D );
827}
828
829std::unique_ptr< QgsMultiLineString > ogrGeometryToQgsMultiLineString( OGRGeometryH geom )
830{
831 std::unique_ptr< QgsMultiLineString > mp = std::make_unique< QgsMultiLineString >();
832
833 const int count = OGR_G_GetGeometryCount( geom );
834 mp->reserve( count );
835 for ( int i = 0; i < count; ++i )
836 {
837 mp->addGeometry( ogrGeometryToQgsLineString( OGR_G_GetGeometryRef( geom, i ) ).release() );
838 }
839
840 return mp;
841}
842
843std::unique_ptr< QgsPolygon > ogrGeometryToQgsPolygon( OGRGeometryH geom )
844{
845 std::unique_ptr< QgsPolygon > polygon = std::make_unique< QgsPolygon >();
846
847 const int count = OGR_G_GetGeometryCount( geom );
848 if ( count >= 1 )
849 {
850 polygon->setExteriorRing( ogrGeometryToQgsLineString( OGR_G_GetGeometryRef( geom, 0 ) ).release() );
851 }
852
853 for ( int i = 1; i < count; ++i )
854 {
855 polygon->addInteriorRing( ogrGeometryToQgsLineString( OGR_G_GetGeometryRef( geom, i ) ).release() );
856 }
857
858 return polygon;
859}
860
861std::unique_ptr< QgsMultiPolygon > ogrGeometryToQgsMultiPolygon( OGRGeometryH geom )
862{
863 std::unique_ptr< QgsMultiPolygon > polygon = std::make_unique< QgsMultiPolygon >();
864
865 const int count = OGR_G_GetGeometryCount( geom );
866 polygon->reserve( count );
867 for ( int i = 0; i < count; ++i )
868 {
869 polygon->addGeometry( ogrGeometryToQgsPolygon( OGR_G_GetGeometryRef( geom, i ) ).release() );
870 }
871
872 return polygon;
873}
874
876{
877 switch ( ogrGeomType )
878 {
879 case wkbUnknown: return Qgis::WkbType::Unknown;
880 case wkbPoint: return Qgis::WkbType::Point;
881 case wkbLineString: return Qgis::WkbType::LineString;
882 case wkbPolygon: return Qgis::WkbType::Polygon;
883 case wkbMultiPoint: return Qgis::WkbType::MultiPoint;
884 case wkbMultiLineString: return Qgis::WkbType::MultiLineString;
885 case wkbMultiPolygon: return Qgis::WkbType::MultiPolygon;
886 case wkbGeometryCollection: return Qgis::WkbType::GeometryCollection;
887 case wkbCircularString: return Qgis::WkbType::CircularString;
888 case wkbCompoundCurve: return Qgis::WkbType::CompoundCurve;
889 case wkbCurvePolygon: return Qgis::WkbType::CurvePolygon;
890 case wkbMultiCurve: return Qgis::WkbType::MultiCurve;
891 case wkbMultiSurface: return Qgis::WkbType::MultiSurface;
892 case wkbCurve: return Qgis::WkbType::Unknown; // not an actual concrete type
893 case wkbSurface: return Qgis::WkbType::Unknown; // not an actual concrete type
894 case wkbPolyhedralSurface: return Qgis::WkbType::PolyhedralSurface;
895 case wkbTIN: return Qgis::WkbType::TIN;
896 case wkbTriangle: return Qgis::WkbType::Triangle;
897
898 case wkbNone: return Qgis::WkbType::NoGeometry;
899 case wkbLinearRing: return Qgis::WkbType::LineString; // approximate match
900
901 case wkbCircularStringZ: return Qgis::WkbType::CircularStringZ;
902 case wkbCompoundCurveZ: return Qgis::WkbType::CompoundCurveZ;
903 case wkbCurvePolygonZ: return Qgis::WkbType::CurvePolygonZ;
904 case wkbMultiCurveZ: return Qgis::WkbType::MultiCurveZ;
905 case wkbMultiSurfaceZ: return Qgis::WkbType::MultiSurfaceZ;
906 case wkbCurveZ: return Qgis::WkbType::Unknown; // not an actual concrete type
907 case wkbSurfaceZ: return Qgis::WkbType::Unknown; // not an actual concrete type
908 case wkbPolyhedralSurfaceZ: return Qgis::WkbType::PolyhedralSurfaceZ;
909 case wkbTINZ: return Qgis::WkbType::TINZ;
910 case wkbTriangleZ: return Qgis::WkbType::TriangleZ;
911
912 case wkbPointM: return Qgis::WkbType::PointM;
913 case wkbLineStringM: return Qgis::WkbType::LineStringM;
914 case wkbPolygonM: return Qgis::WkbType::PolygonM;
915 case wkbMultiPointM: return Qgis::WkbType::MultiPointM;
916 case wkbMultiLineStringM: return Qgis::WkbType::MultiLineStringM;
917 case wkbMultiPolygonM: return Qgis::WkbType::MultiPolygonM;
918 case wkbGeometryCollectionM: return Qgis::WkbType::GeometryCollectionM;
919 case wkbCircularStringM: return Qgis::WkbType::CircularStringM;
920 case wkbCompoundCurveM: return Qgis::WkbType::CompoundCurveM;
921 case wkbCurvePolygonM: return Qgis::WkbType::CurvePolygonM;
922 case wkbMultiCurveM: return Qgis::WkbType::MultiCurveM;
923 case wkbMultiSurfaceM: return Qgis::WkbType::MultiSurfaceM;
924 case wkbCurveM: return Qgis::WkbType::Unknown; // not an actual concrete type
925 case wkbSurfaceM: return Qgis::WkbType::Unknown; // not an actual concrete type
926 case wkbPolyhedralSurfaceM: return Qgis::WkbType::PolyhedralSurfaceM;
927 case wkbTINM: return Qgis::WkbType::TINM;
928 case wkbTriangleM: return Qgis::WkbType::TriangleM;
929
930 case wkbPointZM: return Qgis::WkbType::PointZM;
931 case wkbLineStringZM: return Qgis::WkbType::LineStringZM;
932 case wkbPolygonZM: return Qgis::WkbType::PolygonZM;
933 case wkbMultiPointZM: return Qgis::WkbType::MultiPointZM;
934 case wkbMultiLineStringZM: return Qgis::WkbType::MultiLineStringZM;
935 case wkbMultiPolygonZM: return Qgis::WkbType::MultiPolygonZM;
936 case wkbGeometryCollectionZM: return Qgis::WkbType::GeometryCollectionZM;
937 case wkbCircularStringZM: return Qgis::WkbType::CircularStringZM;
938 case wkbCompoundCurveZM: return Qgis::WkbType::CompoundCurveZM;
939 case wkbCurvePolygonZM: return Qgis::WkbType::CurvePolygonZM;
940 case wkbMultiCurveZM: return Qgis::WkbType::MultiCurveZM;
941 case wkbMultiSurfaceZM: return Qgis::WkbType::MultiSurfaceZM;
942 case wkbCurveZM: return Qgis::WkbType::Unknown; // not an actual concrete type
943 case wkbSurfaceZM: return Qgis::WkbType::Unknown; // not an actual concrete type
944 case wkbPolyhedralSurfaceZM: return Qgis::WkbType::PolyhedralSurfaceZM;
945 case wkbTINZM: return Qgis::WkbType::TINZM;
946 case wkbTriangleZM: return Qgis::WkbType::TriangleZM;
947
948 case wkbPoint25D: return Qgis::WkbType::PointZ;
949 case wkbLineString25D: return Qgis::WkbType::LineStringZ;
950 case wkbPolygon25D: return Qgis::WkbType::PolygonZ;
951 case wkbMultiPoint25D: return Qgis::WkbType::MultiPointZ;
952 case wkbMultiLineString25D: return Qgis::WkbType::MultiLineStringZ;
953 case wkbMultiPolygon25D: return Qgis::WkbType::MultiPolygonZ;
954 case wkbGeometryCollection25D: return Qgis::WkbType::GeometryCollectionZ;
955 }
956
957 // should not reach that point normally
959}
960
962{
963 if ( !geom )
964 return QgsGeometry();
965
966 const auto ogrGeomType = OGR_G_GetGeometryType( geom );
967 Qgis::WkbType wkbType = ogrGeometryTypeToQgsWkbType( ogrGeomType );
968
969 // optimised case for some geometry classes, avoiding wkb conversion on OGR/QGIS sides
970 // TODO - extend to other classes!
971 switch ( QgsWkbTypes::flatType( wkbType ) )
972 {
974 {
975 return QgsGeometry( ogrGeometryToQgsPoint( geom ) );
976 }
977
979 {
980 return QgsGeometry( ogrGeometryToQgsMultiPoint( geom ) );
981 }
982
984 {
985 return QgsGeometry( ogrGeometryToQgsLineString( geom ) );
986 }
987
989 {
991 }
992
994 {
995 return QgsGeometry( ogrGeometryToQgsPolygon( geom ) );
996 }
997
999 {
1000 return QgsGeometry( ogrGeometryToQgsMultiPolygon( geom ) );
1001 }
1002
1003 default:
1004 break;
1005 }
1006
1007 // Fallback to inefficient WKB conversions
1008
1009 if ( wkbFlatten( wkbType ) == wkbGeometryCollection )
1010 {
1011 // Shapefile MultiPatch can be reported as GeometryCollectionZ of TINZ
1012 if ( OGR_G_GetGeometryCount( geom ) >= 1 &&
1013 wkbFlatten( OGR_G_GetGeometryType( OGR_G_GetGeometryRef( geom, 0 ) ) ) == wkbTIN )
1014 {
1015 auto newGeom = OGR_G_ForceToMultiPolygon( OGR_G_Clone( geom ) );
1016 auto ret = ogrGeometryToQgsGeometry( newGeom );
1017 OGR_G_DestroyGeometry( newGeom );
1018 return ret;
1019 }
1020 }
1021
1022 // get the wkb representation
1023 int memorySize = OGR_G_WkbSize( geom );
1024 unsigned char *wkb = new unsigned char[memorySize];
1025 OGR_G_ExportToWkb( geom, static_cast<OGRwkbByteOrder>( QgsApplication::endian() ), wkb );
1026
1027 QgsGeometry g;
1028 g.fromWkb( wkb, memorySize );
1029 return g;
1030}
1031
1032QgsFeatureList QgsOgrUtils::stringToFeatureList( const QString &string, const QgsFields &fields, QTextCodec *encoding )
1033{
1034 QgsFeatureList features;
1035 if ( string.isEmpty() )
1036 return features;
1037
1038 QString randomFileName = QStringLiteral( "/vsimem/%1" ).arg( QUuid::createUuid().toString() );
1039
1040 // create memory file system object from string buffer
1041 QByteArray ba = string.toUtf8();
1042 VSIFCloseL( VSIFileFromMemBuffer( randomFileName.toUtf8().constData(), reinterpret_cast< GByte * >( ba.data() ),
1043 static_cast< vsi_l_offset >( ba.size() ), FALSE ) );
1044
1045 gdal::ogr_datasource_unique_ptr hDS( OGROpen( randomFileName.toUtf8().constData(), false, nullptr ) );
1046 if ( !hDS )
1047 {
1048 VSIUnlink( randomFileName.toUtf8().constData() );
1049 return features;
1050 }
1051
1052 OGRLayerH ogrLayer = OGR_DS_GetLayer( hDS.get(), 0 );
1053 if ( !ogrLayer )
1054 {
1055 hDS.reset();
1056 VSIUnlink( randomFileName.toUtf8().constData() );
1057 return features;
1058 }
1059
1061 while ( oFeat.reset( OGR_L_GetNextFeature( ogrLayer ) ), oFeat )
1062 {
1063 QgsFeature feat = readOgrFeature( oFeat.get(), fields, encoding );
1064 if ( feat.isValid() )
1065 features << feat;
1066 }
1067
1068 hDS.reset();
1069 VSIUnlink( randomFileName.toUtf8().constData() );
1070
1071 return features;
1072}
1073
1074QgsFields QgsOgrUtils::stringToFields( const QString &string, QTextCodec *encoding )
1075{
1076 QgsFields fields;
1077 if ( string.isEmpty() )
1078 return fields;
1079
1080 QString randomFileName = QStringLiteral( "/vsimem/%1" ).arg( QUuid::createUuid().toString() );
1081
1082 // create memory file system object from buffer
1083 QByteArray ba = string.toUtf8();
1084 VSIFCloseL( VSIFileFromMemBuffer( randomFileName.toUtf8().constData(), reinterpret_cast< GByte * >( ba.data() ),
1085 static_cast< vsi_l_offset >( ba.size() ), FALSE ) );
1086
1087 gdal::ogr_datasource_unique_ptr hDS( OGROpen( randomFileName.toUtf8().constData(), false, nullptr ) );
1088 if ( !hDS )
1089 {
1090 VSIUnlink( randomFileName.toUtf8().constData() );
1091 return fields;
1092 }
1093
1094 OGRLayerH ogrLayer = OGR_DS_GetLayer( hDS.get(), 0 );
1095 if ( !ogrLayer )
1096 {
1097 hDS.reset();
1098 VSIUnlink( randomFileName.toUtf8().constData() );
1099 return fields;
1100 }
1101
1103 //read in the first feature only
1104 if ( oFeat.reset( OGR_L_GetNextFeature( ogrLayer ) ), oFeat )
1105 {
1106 fields = readOgrFields( oFeat.get(), encoding );
1107 }
1108
1109 hDS.reset();
1110 VSIUnlink( randomFileName.toUtf8().constData() );
1111 return fields;
1112}
1113
1114QStringList QgsOgrUtils::cStringListToQStringList( char **stringList )
1115{
1116 if ( !stringList )
1117 return {};
1118
1119 QStringList strings;
1120 // presume null terminated string list
1121 for ( qgssize i = 0; stringList[i]; ++i )
1122 {
1123 strings.append( QString::fromUtf8( stringList[i] ) );
1124 }
1125
1126 return strings;
1127}
1128
1130{
1131 if ( !srs )
1132 return QString();
1133
1134 char *pszWkt = nullptr;
1135 const QByteArray multiLineOption = QStringLiteral( "MULTILINE=NO" ).toLocal8Bit();
1136 const QByteArray formatOption = QStringLiteral( "FORMAT=WKT2" ).toLocal8Bit();
1137 const char *const options[] = {multiLineOption.constData(), formatOption.constData(), nullptr};
1138 OSRExportToWktEx( srs, &pszWkt, options );
1139
1140 const QString res( pszWkt );
1141 CPLFree( pszWkt );
1142 return res;
1143}
1144
1146{
1147 const QString wkt = OGRSpatialReferenceToWkt( srs );
1148 if ( wkt.isEmpty() )
1150
1151 const char *authorityName = OSRGetAuthorityName( srs, nullptr );
1152 const char *authorityCode = OSRGetAuthorityCode( srs, nullptr );
1154 if ( authorityName && authorityCode )
1155 {
1156 QString authId = QString( authorityName ) + ':' + QString( authorityCode );
1157 OGRSpatialReferenceH ogrSrsTmp = OSRNewSpatialReference( nullptr );
1158 // Check that the CRS build from authId and the input one are the "same".
1159 if ( OSRSetFromUserInput( ogrSrsTmp, authId.toUtf8().constData() ) != OGRERR_NONE &&
1160 OSRIsSame( srs, ogrSrsTmp ) )
1161 {
1163 res.createFromUserInput( authId );
1164 }
1165 OSRDestroySpatialReference( ogrSrsTmp );
1166 }
1167 if ( !res.isValid() )
1169
1170#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,4,0)
1171 const double coordinateEpoch = OSRGetCoordinateEpoch( srs );
1172 if ( coordinateEpoch > 0 )
1173 res.setCoordinateEpoch( coordinateEpoch );
1174#endif
1175 return res;
1176}
1177
1179{
1180 if ( crs.isValid() )
1181 {
1182 OGRSpatialReferenceH ogrSrs = nullptr;
1183
1184 // First try instantiating the CRS from its authId. This will give a
1185 // more complete representation of the CRS for GDAL. In particular it might
1186 // help a few drivers to get the datum code, that would be missing in WKT-2.
1187 // See https://github.com/OSGeo/gdal/pull/5218
1188 const QString authId = crs.authid();
1189 const QString srsWkt = crs.toWkt( Qgis::CrsWktVariant::PreferredGdal );
1190 if ( !authId.isEmpty() )
1191 {
1192 ogrSrs = OSRNewSpatialReference( nullptr );
1193 if ( OSRSetFromUserInput( ogrSrs, authId.toUtf8().constData() ) == OGRERR_NONE )
1194 {
1195 // Check that the CRS build from WKT and authId are the "same".
1196 OGRSpatialReferenceH ogrSrsFromWkt = OSRNewSpatialReference( srsWkt.toUtf8().constData() );
1197 if ( ogrSrsFromWkt )
1198 {
1199 if ( !OSRIsSame( ogrSrs, ogrSrsFromWkt ) )
1200 {
1201 OSRDestroySpatialReference( ogrSrs );
1202 ogrSrs = ogrSrsFromWkt;
1203 }
1204 else
1205 {
1206 OSRDestroySpatialReference( ogrSrsFromWkt );
1207 }
1208 }
1209 }
1210 else
1211 {
1212 OSRDestroySpatialReference( ogrSrs );
1213 ogrSrs = nullptr;
1214 }
1215 }
1216 if ( !ogrSrs )
1217 {
1218 ogrSrs = OSRNewSpatialReference( srsWkt.toUtf8().constData() );
1219 }
1220 if ( ogrSrs )
1221 {
1222 OSRSetAxisMappingStrategy( ogrSrs, OAMS_TRADITIONAL_GIS_ORDER );
1223#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,4,0)
1224 if ( !std::isnan( crs.coordinateEpoch() ) )
1225 {
1226 OSRSetCoordinateEpoch( ogrSrs, crs.coordinateEpoch() );
1227 }
1228#endif
1229 return ogrSrs;
1230 }
1231 }
1232
1233 return nullptr;
1234}
1235
1236QString QgsOgrUtils::readShapefileEncoding( const QString &path )
1237{
1238 const QString cpgEncoding = readShapefileEncodingFromCpg( path );
1239 if ( !cpgEncoding.isEmpty() )
1240 return cpgEncoding;
1241
1242 return readShapefileEncodingFromLdid( path );
1243}
1244
1245QString QgsOgrUtils::readShapefileEncodingFromCpg( const QString &path )
1246{
1247 QString errCause;
1248 QgsOgrLayerUniquePtr layer = QgsOgrProviderUtils::getLayer( path, false, QStringList(), 0, errCause, false );
1249 return layer ? layer->GetMetadataItem( QStringLiteral( "ENCODING_FROM_CPG" ), QStringLiteral( "SHAPEFILE" ) ) : QString();
1250}
1251
1253{
1254 QString errCause;
1255 QgsOgrLayerUniquePtr layer = QgsOgrProviderUtils::getLayer( path, false, QStringList(), 0, errCause, false );
1256 return layer ? layer->GetMetadataItem( QStringLiteral( "ENCODING_FROM_LDID" ), QStringLiteral( "SHAPEFILE" ) ) : QString();
1257}
1258
1259QVariantMap QgsOgrUtils::parseStyleString( const QString &string )
1260{
1261 QVariantMap styles;
1262
1263 char **papszStyleString = CSLTokenizeString2( string.toUtf8().constData(), ";",
1264 CSLT_HONOURSTRINGS
1265 | CSLT_PRESERVEQUOTES
1266 | CSLT_PRESERVEESCAPES );
1267 for ( int i = 0; papszStyleString[i] != nullptr; ++i )
1268 {
1269 // style string format is:
1270 // <tool_name>([<tool_param>[,<tool_param>[,...]]])
1271
1272 // first extract tool name
1273 const thread_local QRegularExpression sToolPartRx( QStringLiteral( "^(.*?)\\((.*)\\)$" ) );
1274 const QString stylePart( papszStyleString[i] );
1275 const QRegularExpressionMatch match = sToolPartRx.match( stylePart );
1276 if ( !match.hasMatch() )
1277 continue;
1278
1279 const QString tool = match.captured( 1 );
1280 const QString params = match.captured( 2 );
1281
1282 char **papszTokens = CSLTokenizeString2( params.toUtf8().constData(), ",", CSLT_HONOURSTRINGS
1283 | CSLT_PRESERVEESCAPES );
1284
1285 QVariantMap toolParts;
1286 const thread_local QRegularExpression sToolParamRx( QStringLiteral( "^(.*?):(.*)$" ) );
1287 for ( int j = 0; papszTokens[j] != nullptr; ++j )
1288 {
1289 const QString toolPart( papszTokens[j] );
1290 const QRegularExpressionMatch toolMatch = sToolParamRx.match( toolPart );
1291 if ( !match.hasMatch() )
1292 continue;
1293
1294 // note we always convert the keys to lowercase, just to be safe...
1295 toolParts.insert( toolMatch.captured( 1 ).toLower(), toolMatch.captured( 2 ) );
1296 }
1297 CSLDestroy( papszTokens );
1298
1299 // note we always convert the keys to lowercase, just to be safe...
1300 styles.insert( tool.toLower(), toolParts );
1301 }
1302 CSLDestroy( papszStyleString );
1303 return styles;
1304}
1305
1306std::unique_ptr<QgsSymbol> QgsOgrUtils::symbolFromStyleString( const QString &string, Qgis::SymbolType type )
1307{
1308 const QVariantMap styles = parseStyleString( string );
1309
1310 auto convertSize = []( const QString & size, double & value, Qgis::RenderUnit & unit )->bool
1311 {
1312 const thread_local QRegularExpression sUnitRx = QRegularExpression( QStringLiteral( "^([\\d\\.]+)(g|px|pt|mm|cm|in)$" ) );
1313 const QRegularExpressionMatch match = sUnitRx.match( size );
1314 if ( match.hasMatch() )
1315 {
1316 value = match.captured( 1 ).toDouble();
1317 const QString unitString = match.captured( 2 );
1318 if ( unitString.compare( QLatin1String( "px" ), Qt::CaseInsensitive ) == 0 )
1319 {
1320 // pixels are a poor unit choice for QGIS -- they render badly in hidpi layouts. Convert to points instead, using
1321 // a 96 dpi conversion
1322 static constexpr double PT_TO_INCHES_FACTOR = 1 / 72.0;
1323 static constexpr double PX_TO_PT_FACTOR = 1 / ( 96.0 * PT_TO_INCHES_FACTOR );
1325 value *= PX_TO_PT_FACTOR;
1326 return true;
1327 }
1328 else if ( unitString.compare( QLatin1String( "pt" ), Qt::CaseInsensitive ) == 0 )
1329 {
1331 return true;
1332 }
1333 else if ( unitString.compare( QLatin1String( "mm" ), Qt::CaseInsensitive ) == 0 )
1334 {
1336 return true;
1337 }
1338 else if ( unitString.compare( QLatin1String( "cm" ), Qt::CaseInsensitive ) == 0 )
1339 {
1340 value *= 10;
1342 return true;
1343 }
1344 else if ( unitString.compare( QLatin1String( "in" ), Qt::CaseInsensitive ) == 0 )
1345 {
1347 return true;
1348 }
1349 else if ( unitString.compare( QLatin1String( "g" ), Qt::CaseInsensitive ) == 0 )
1350 {
1352 return true;
1353 }
1354 QgsDebugError( QStringLiteral( "Unknown unit %1" ).arg( unitString ) );
1355 }
1356 else
1357 {
1358 QgsDebugError( QStringLiteral( "Could not parse style size %1" ).arg( size ) );
1359 }
1360 return false;
1361 };
1362
1363 auto convertColor = []( const QString & string ) -> QColor
1364 {
1365 if ( string.isEmpty() )
1366 return QColor();
1367
1368 const thread_local QRegularExpression sColorWithAlphaRx = QRegularExpression( QStringLiteral( "^#([0-9a-fA-F]{6})([0-9a-fA-F]{2})$" ) );
1369 const QRegularExpressionMatch match = sColorWithAlphaRx.match( string );
1370 if ( match.hasMatch() )
1371 {
1372 // need to convert #RRGGBBAA to #AARRGGBB for QColor
1373 return QColor( QStringLiteral( "#%1%2" ).arg( match.captured( 2 ), match.captured( 1 ) ) );
1374 }
1375 else
1376 {
1377 return QColor( string );
1378 }
1379 };
1380
1381 auto convertPen = [&convertColor, &convertSize, string]( const QVariantMap & lineStyle ) -> std::unique_ptr< QgsSymbol >
1382 {
1383 QColor color = convertColor( lineStyle.value( QStringLiteral( "c" ), QStringLiteral( "#000000" ) ).toString() );
1384
1385 double lineWidth = DEFAULT_SIMPLELINE_WIDTH;
1387 convertSize( lineStyle.value( QStringLiteral( "w" ) ).toString(), lineWidth, lineWidthUnit );
1388
1389 // if the pen is a mapinfo pen, use dedicated converter for more accurate results
1390 const thread_local QRegularExpression sMapInfoId = QRegularExpression( QStringLiteral( "mapinfo-pen-(\\d+)" ) );
1391 const QRegularExpressionMatch match = sMapInfoId.match( string );
1392 if ( match.hasMatch() )
1393 {
1394 const int penId = match.captured( 1 ).toInt();
1396 std::unique_ptr<QgsSymbol> res( QgsMapInfoSymbolConverter::convertLineSymbol( penId, context, color, lineWidth, lineWidthUnit ) );
1397 if ( res )
1398 return res;
1399 }
1400
1401 std::unique_ptr< QgsSimpleLineSymbolLayer > simpleLine = std::make_unique< QgsSimpleLineSymbolLayer >( color, lineWidth );
1402 simpleLine->setWidthUnit( lineWidthUnit );
1403
1404 // pattern
1405 const QString pattern = lineStyle.value( QStringLiteral( "p" ) ).toString();
1406 if ( !pattern.isEmpty() )
1407 {
1408 const thread_local QRegularExpression sPatternUnitRx = QRegularExpression( QStringLiteral( "^([\\d\\.\\s]+)(g|px|pt|mm|cm|in)$" ) );
1409 const QRegularExpressionMatch match = sPatternUnitRx.match( pattern );
1410 if ( match.hasMatch() )
1411 {
1412 const QStringList patternValues = match.captured( 1 ).split( ' ' );
1413 QVector< qreal > dashPattern;
1415 for ( const QString &val : patternValues )
1416 {
1417 double length;
1418 convertSize( val + match.captured( 2 ), length, patternUnits );
1419 dashPattern.push_back( length * lineWidth * 2 );
1420 }
1421
1422 simpleLine->setCustomDashVector( dashPattern );
1423 simpleLine->setCustomDashPatternUnit( patternUnits );
1424 simpleLine->setUseCustomDashPattern( true );
1425 }
1426 }
1427
1428 Qt::PenCapStyle capStyle = Qt::FlatCap;
1429 Qt::PenJoinStyle joinStyle = Qt::MiterJoin;
1430 // workaround https://github.com/OSGeo/gdal/pull/3509 in older GDAL versions
1431 const QString id = lineStyle.value( QStringLiteral( "id" ) ).toString();
1432 if ( id.contains( QLatin1String( "mapinfo-pen" ), Qt::CaseInsensitive ) )
1433 {
1434 // MapInfo renders all lines using a round pen cap and round pen join
1435 // which are not the default values for OGR pen cap/join styles. So we need to explicitly
1436 // override the OGR default values here on older GDAL versions
1437 capStyle = Qt::RoundCap;
1438 joinStyle = Qt::RoundJoin;
1439 }
1440
1441 // pen cap
1442 const QString penCap = lineStyle.value( QStringLiteral( "cap" ) ).toString();
1443 if ( penCap.compare( QLatin1String( "b" ), Qt::CaseInsensitive ) == 0 )
1444 {
1445 capStyle = Qt::FlatCap;
1446 }
1447 else if ( penCap.compare( QLatin1String( "r" ), Qt::CaseInsensitive ) == 0 )
1448 {
1449 capStyle = Qt::RoundCap;
1450 }
1451 else if ( penCap.compare( QLatin1String( "p" ), Qt::CaseInsensitive ) == 0 )
1452 {
1453 capStyle = Qt::SquareCap;
1454 }
1455 simpleLine->setPenCapStyle( capStyle );
1456
1457 // pen join
1458 const QString penJoin = lineStyle.value( QStringLiteral( "j" ) ).toString();
1459 if ( penJoin.compare( QLatin1String( "m" ), Qt::CaseInsensitive ) == 0 )
1460 {
1461 joinStyle = Qt::MiterJoin;
1462 }
1463 else if ( penJoin.compare( QLatin1String( "r" ), Qt::CaseInsensitive ) == 0 )
1464 {
1465 joinStyle = Qt::RoundJoin;
1466 }
1467 else if ( penJoin.compare( QLatin1String( "b" ), Qt::CaseInsensitive ) == 0 )
1468 {
1469 joinStyle = Qt::BevelJoin;
1470 }
1471 simpleLine->setPenJoinStyle( joinStyle );
1472
1473 const QString priority = lineStyle.value( QStringLiteral( "l" ) ).toString();
1474 if ( !priority.isEmpty() )
1475 {
1476 simpleLine->setRenderingPass( priority.toInt() );
1477 }
1478 return std::make_unique< QgsLineSymbol >( QgsSymbolLayerList() << simpleLine.release() );
1479 };
1480
1481 auto convertBrush = [&convertColor]( const QVariantMap & brushStyle ) -> std::unique_ptr< QgsSymbol >
1482 {
1483 const QColor foreColor = convertColor( brushStyle.value( QStringLiteral( "fc" ), QStringLiteral( "#000000" ) ).toString() );
1484 const QColor backColor = convertColor( brushStyle.value( QStringLiteral( "bc" ), QString() ).toString() );
1485
1486 const QString id = brushStyle.value( QStringLiteral( "id" ) ).toString();
1487
1488 // if the pen is a mapinfo brush, use dedicated converter for more accurate results
1489 const thread_local QRegularExpression sMapInfoId = QRegularExpression( QStringLiteral( "mapinfo-brush-(\\d+)" ) );
1490 const QRegularExpressionMatch match = sMapInfoId.match( id );
1491 if ( match.hasMatch() )
1492 {
1493 const int brushId = match.captured( 1 ).toInt();
1495 std::unique_ptr<QgsSymbol> res( QgsMapInfoSymbolConverter::convertFillSymbol( brushId, context, foreColor, backColor ) );
1496 if ( res )
1497 return res;
1498 }
1499
1500 const thread_local QRegularExpression sOgrId = QRegularExpression( QStringLiteral( "ogr-brush-(\\d+)" ) );
1501 const QRegularExpressionMatch ogrMatch = sOgrId.match( id );
1502
1503 Qt::BrushStyle style = Qt::SolidPattern;
1504 if ( ogrMatch.hasMatch() )
1505 {
1506 const int brushId = ogrMatch.captured( 1 ).toInt();
1507 switch ( brushId )
1508 {
1509 case 0:
1510 style = Qt::SolidPattern;
1511 break;
1512
1513 case 1:
1514 style = Qt::NoBrush;
1515 break;
1516
1517 case 2:
1518 style = Qt::HorPattern;
1519 break;
1520
1521 case 3:
1522 style = Qt::VerPattern;
1523 break;
1524
1525 case 4:
1526 style = Qt::FDiagPattern;
1527 break;
1528
1529 case 5:
1530 style = Qt::BDiagPattern;
1531 break;
1532
1533 case 6:
1534 style = Qt::CrossPattern;
1535 break;
1536
1537 case 7:
1538 style = Qt::DiagCrossPattern;
1539 break;
1540 }
1541 }
1542
1543 QgsSymbolLayerList layers;
1544 if ( backColor.isValid() && style != Qt::SolidPattern && style != Qt::NoBrush )
1545 {
1546 std::unique_ptr< QgsSimpleFillSymbolLayer > backgroundFill = std::make_unique< QgsSimpleFillSymbolLayer >( backColor );
1547 backgroundFill->setLocked( true );
1548 backgroundFill->setStrokeStyle( Qt::NoPen );
1549 layers << backgroundFill.release();
1550 }
1551
1552 std::unique_ptr< QgsSimpleFillSymbolLayer > foregroundFill = std::make_unique< QgsSimpleFillSymbolLayer >( foreColor );
1553 foregroundFill->setBrushStyle( style );
1554 foregroundFill->setStrokeStyle( Qt::NoPen );
1555
1556 const QString priority = brushStyle.value( QStringLiteral( "l" ) ).toString();
1557 if ( !priority.isEmpty() )
1558 {
1559 foregroundFill->setRenderingPass( priority.toInt() );
1560 }
1561 layers << foregroundFill.release();
1562 return std::make_unique< QgsFillSymbol >( layers );
1563 };
1564
1565 auto convertSymbol = [&convertColor, &convertSize, string]( const QVariantMap & symbolStyle ) -> std::unique_ptr< QgsSymbol >
1566 {
1567 const QColor color = convertColor( symbolStyle.value( QStringLiteral( "c" ), QStringLiteral( "#000000" ) ).toString() );
1568
1569 double symbolSize = DEFAULT_SIMPLEMARKER_SIZE;
1571 convertSize( symbolStyle.value( QStringLiteral( "s" ) ).toString(), symbolSize, symbolSizeUnit );
1572
1573 const double angle = symbolStyle.value( QStringLiteral( "a" ), QStringLiteral( "0" ) ).toDouble();
1574
1575 const QString id = symbolStyle.value( QStringLiteral( "id" ) ).toString();
1576
1577 // if the symbol is a mapinfo symbol, use dedicated converter for more accurate results
1578 const thread_local QRegularExpression sMapInfoId = QRegularExpression( QStringLiteral( "mapinfo-sym-(\\d+)" ) );
1579 const QRegularExpressionMatch match = sMapInfoId.match( id );
1580 if ( match.hasMatch() )
1581 {
1582 const int symbolId = match.captured( 1 ).toInt();
1584
1585 // ogr interpretations of mapinfo symbol sizes are too large -- scale these down
1586 symbolSize *= 0.61;
1587
1588 std::unique_ptr<QgsSymbol> res( QgsMapInfoSymbolConverter::convertMarkerSymbol( symbolId, context, color, symbolSize, symbolSizeUnit ) );
1589 if ( res )
1590 return res;
1591 }
1592
1593 std::unique_ptr< QgsMarkerSymbolLayer > markerLayer;
1594
1595 const thread_local QRegularExpression sFontId = QRegularExpression( QStringLiteral( "font-sym-(\\d+)" ) );
1596 const QRegularExpressionMatch fontMatch = sFontId.match( id );
1597 if ( fontMatch.hasMatch() )
1598 {
1599 const int symId = fontMatch.captured( 1 ).toInt();
1600 const QStringList families = symbolStyle.value( QStringLiteral( "f" ), QString() ).toString().split( ',' );
1601
1602 bool familyFound = false;
1603 QString fontFamily;
1604 QString matched;
1605 for ( const QString &family : std::as_const( families ) )
1606 {
1607 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( family );
1608
1609 if ( QgsFontUtils::fontFamilyMatchOnSystem( processedFamily ) ||
1610 QgsApplication::fontManager()->tryToDownloadFontFamily( processedFamily, matched ) )
1611 {
1612 familyFound = true;
1613 fontFamily = processedFamily;
1614 break;
1615 }
1616 }
1617
1618 if ( familyFound )
1619 {
1620 std::unique_ptr< QgsFontMarkerSymbolLayer > fontMarker = std::make_unique< QgsFontMarkerSymbolLayer >( fontFamily, QChar( symId ), symbolSize );
1621 fontMarker->setSizeUnit( symbolSizeUnit );
1622 fontMarker->setAngle( -angle );
1623
1624 fontMarker->setColor( color );
1625
1626 const QColor strokeColor = convertColor( symbolStyle.value( QStringLiteral( "o" ), QString() ).toString() );
1627 if ( strokeColor.isValid() )
1628 {
1629 fontMarker->setStrokeColor( strokeColor );
1630 fontMarker->setStrokeWidth( 1 );
1631 fontMarker->setStrokeWidthUnit( Qgis::RenderUnit::Points );
1632 }
1633 else
1634 {
1635 fontMarker->setStrokeWidth( 0 );
1636 }
1637
1638 markerLayer = std::move( fontMarker );
1639 }
1640 else if ( !families.empty() )
1641 {
1642 // couldn't even find a matching font in the backup list
1643 QgsMessageLog::logMessage( QObject::tr( "Font %1 not found on system" ).arg( families.at( 0 ) ) );
1644 }
1645 }
1646
1647 if ( !markerLayer )
1648 {
1649 const thread_local QRegularExpression sOgrId = QRegularExpression( QStringLiteral( "ogr-sym-(\\d+)" ) );
1650 const QRegularExpressionMatch ogrMatch = sOgrId.match( id );
1651
1652 Qgis::MarkerShape shape;
1653 bool isFilled = true;
1654 if ( ogrMatch.hasMatch() )
1655 {
1656 const int symId = ogrMatch.captured( 1 ).toInt();
1657 switch ( symId )
1658 {
1659 case 0:
1661 break;
1662
1663 case 1:
1665 break;
1666
1667 case 2:
1668 isFilled = false;
1670 break;
1671
1672 case 3:
1674 break;
1675
1676 case 4:
1677 isFilled = false;
1679 break;
1680
1681 case 5:
1683 break;
1684
1685 case 6:
1686 isFilled = false;
1688 break;
1689
1690 case 7:
1692 break;
1693
1694 case 8:
1695 isFilled = false;
1697 break;
1698
1699 case 9:
1701 break;
1702
1703 case 10:
1705 break;
1706
1707 default:
1708 isFilled = false;
1709 shape = Qgis::MarkerShape::Square; // to initialize the variable
1710 break;
1711 }
1712 }
1713 else
1714 {
1715 isFilled = false;
1716 shape = Qgis::MarkerShape::Square; // to initialize the variable
1717 }
1718
1719 std::unique_ptr< QgsSimpleMarkerSymbolLayer > simpleMarker = std::make_unique< QgsSimpleMarkerSymbolLayer >( shape, symbolSize, -angle );
1720 simpleMarker->setSizeUnit( symbolSizeUnit );
1721 simpleMarker->setStrokeWidth( 1.0 );
1722 simpleMarker->setStrokeWidthUnit( Qgis::RenderUnit::Points );
1723
1724 if ( isFilled && QgsSimpleMarkerSymbolLayer::shapeIsFilled( shape ) )
1725 {
1726 simpleMarker->setColor( color );
1727 simpleMarker->setStrokeStyle( Qt::NoPen );
1728 }
1729 else
1730 {
1731 simpleMarker->setFillColor( QColor( 0, 0, 0, 0 ) );
1732 simpleMarker->setStrokeColor( color );
1733 }
1734
1735 const QColor strokeColor = convertColor( symbolStyle.value( QStringLiteral( "o" ), QString() ).toString() );
1736 if ( strokeColor.isValid() )
1737 {
1738 simpleMarker->setStrokeColor( strokeColor );
1739 simpleMarker->setStrokeStyle( Qt::SolidLine );
1740 }
1741
1742 markerLayer = std::move( simpleMarker );
1743 }
1744
1745 return std::make_unique< QgsMarkerSymbol >( QgsSymbolLayerList() << markerLayer.release() );
1746 };
1747
1748 switch ( type )
1749 {
1751 if ( styles.contains( QStringLiteral( "symbol" ) ) )
1752 {
1753 const QVariantMap symbolStyle = styles.value( QStringLiteral( "symbol" ) ).toMap();
1754 return convertSymbol( symbolStyle );
1755 }
1756 else
1757 {
1758 return nullptr;
1759 }
1760
1762 if ( styles.contains( QStringLiteral( "pen" ) ) )
1763 {
1764 // line symbol type
1765 const QVariantMap lineStyle = styles.value( QStringLiteral( "pen" ) ).toMap();
1766 return convertPen( lineStyle );
1767 }
1768 else
1769 {
1770 return nullptr;
1771 }
1772
1774 {
1775 std::unique_ptr< QgsSymbol > fillSymbol = std::make_unique< QgsFillSymbol >();
1776 if ( styles.contains( QStringLiteral( "brush" ) ) )
1777 {
1778 const QVariantMap brushStyle = styles.value( QStringLiteral( "brush" ) ).toMap();
1779 fillSymbol = convertBrush( brushStyle );
1780 }
1781 else
1782 {
1783 std::unique_ptr< QgsSimpleFillSymbolLayer > emptyFill = std::make_unique< QgsSimpleFillSymbolLayer >();
1784 emptyFill->setBrushStyle( Qt::NoBrush );
1785 fillSymbol = std::make_unique< QgsFillSymbol >( QgsSymbolLayerList() << emptyFill.release() );
1786 }
1787
1788 std::unique_ptr< QgsSymbol > penSymbol;
1789 if ( styles.contains( QStringLiteral( "pen" ) ) )
1790 {
1791 const QVariantMap lineStyle = styles.value( QStringLiteral( "pen" ) ).toMap();
1792 penSymbol = convertPen( lineStyle );
1793 }
1794
1795 if ( penSymbol )
1796 {
1797 const int count = penSymbol->symbolLayerCount();
1798
1799 if ( count == 1 )
1800 {
1801 // 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
1802 if ( QgsSymbolLayerUtils::condenseFillAndOutline( dynamic_cast< QgsFillSymbolLayer * >( fillSymbol->symbolLayer( fillSymbol->symbolLayerCount() - 1 ) ),
1803 dynamic_cast< QgsLineSymbolLayer * >( penSymbol->symbolLayer( 0 ) ) ) )
1804 return fillSymbol;
1805 }
1806
1807 for ( int i = 0; i < count; ++i )
1808 {
1809 std::unique_ptr< QgsSymbolLayer > layer( penSymbol->takeSymbolLayer( 0 ) );
1810 layer->setLocked( true );
1811 fillSymbol->appendSymbolLayer( layer.release() );
1812 }
1813 }
1814
1815 return fillSymbol;
1816 }
1817
1819 break;
1820 }
1821
1822 return nullptr;
1823}
1824
1825void QgsOgrUtils::ogrFieldTypeToQVariantType( OGRFieldType ogrType, OGRFieldSubType ogrSubType, QMetaType::Type &variantType, QMetaType::Type &variantSubType )
1826{
1827 variantType = QMetaType::Type::UnknownType;
1828 variantSubType = QMetaType::Type::UnknownType;
1829
1830 switch ( ogrType )
1831 {
1832 case OFTInteger:
1833 if ( ogrSubType == OFSTBoolean )
1834 {
1835 variantType = QMetaType::Type::Bool;
1836 ogrSubType = OFSTBoolean;
1837 }
1838 else
1839 variantType = QMetaType::Type::Int;
1840 break;
1841 case OFTInteger64:
1842 variantType = QMetaType::Type::LongLong;
1843 break;
1844 case OFTReal:
1845 variantType = QMetaType::Type::Double;
1846 break;
1847 case OFTDate:
1848 variantType = QMetaType::Type::QDate;
1849 break;
1850 case OFTTime:
1851 variantType = QMetaType::Type::QTime;
1852 break;
1853 case OFTDateTime:
1854 variantType = QMetaType::Type::QDateTime;
1855 break;
1856
1857 case OFTBinary:
1858 variantType = QMetaType::Type::QByteArray;
1859 break;
1860
1861 case OFTString:
1862 case OFTWideString:
1863 if ( ogrSubType == OFSTJSON )
1864 {
1865 ogrSubType = OFSTJSON;
1866 variantType = QMetaType::Type::QVariantMap;
1867 variantSubType = QMetaType::Type::QString;
1868 }
1869 else
1870 {
1871 variantType = QMetaType::Type::QString;
1872 }
1873 break;
1874
1875 case OFTStringList:
1876 case OFTWideStringList:
1877 variantType = QMetaType::Type::QStringList;
1878 variantSubType = QMetaType::Type::QString;
1879 break;
1880
1881 case OFTIntegerList:
1882 variantType = QMetaType::Type::QVariantList;
1883 variantSubType = QMetaType::Type::Int;
1884 break;
1885
1886 case OFTRealList:
1887 variantType = QMetaType::Type::QVariantList;
1888 variantSubType = QMetaType::Type::Double;
1889 break;
1890
1891 case OFTInteger64List:
1892 variantType = QMetaType::Type::QVariantList;
1893 variantSubType = QMetaType::Type::LongLong;
1894 break;
1895 }
1896}
1897
1898void QgsOgrUtils::variantTypeToOgrFieldType( QMetaType::Type variantType, OGRFieldType &ogrType, OGRFieldSubType &ogrSubType )
1899{
1900 ogrSubType = OFSTNone;
1901 switch ( variantType )
1902 {
1903 case QMetaType::Type::Bool:
1904 ogrType = OFTInteger;
1905 ogrSubType = OFSTBoolean;
1906 break;
1907
1908 case QMetaType::Type::Int:
1909 ogrType = OFTInteger;
1910 break;
1911
1912 case QMetaType::Type::LongLong:
1913 ogrType = OFTInteger64;
1914 break;
1915
1916 case QMetaType::Type::Double:
1917 ogrType = OFTReal;
1918 break;
1919
1920 case QMetaType::Type::QChar:
1921 case QMetaType::Type::QString:
1922 ogrType = OFTString;
1923 break;
1924
1925 case QMetaType::Type::QStringList:
1926 ogrType = OFTStringList;
1927 break;
1928
1929 case QMetaType::Type::QByteArray:
1930 ogrType = OFTBinary;
1931 break;
1932
1933 case QMetaType::Type::QDate:
1934 ogrType = OFTDate;
1935 break;
1936
1937 case QMetaType::Type::QTime:
1938 ogrType = OFTTime;
1939 break;
1940 case QMetaType::Type::QDateTime:
1941 ogrType = OFTDateTime;
1942 break;
1943
1944 default:
1945 ogrType = OFTString;
1946 break;
1947 }
1948}
1949
1950QVariant QgsOgrUtils::stringToVariant( OGRFieldType type, OGRFieldSubType, const QString &string )
1951{
1952 if ( string.isEmpty() )
1953 return QVariant();
1954
1955 bool ok = false;
1956 QVariant res;
1957 switch ( type )
1958 {
1959 case OFTInteger:
1960 res = string.toInt( &ok );
1961 break;
1962
1963 case OFTInteger64:
1964 res = string.toLongLong( &ok );
1965 break;
1966
1967 case OFTReal:
1968 res = string.toDouble( &ok );
1969 break;
1970
1971 case OFTString:
1972 case OFTWideString:
1973 res = string;
1974 ok = true;
1975 break;
1976
1977 case OFTDate:
1978 res = QDate::fromString( string, Qt::ISODate );
1979 ok = res.isValid();
1980 break;
1981
1982 case OFTTime:
1983 res = QTime::fromString( string, Qt::ISODate );
1984 ok = res.isValid();
1985 break;
1986
1987 case OFTDateTime:
1988 res = QDateTime::fromString( string, Qt::ISODate );
1989 ok = res.isValid();
1990 break;
1991
1992 default:
1993 res = string;
1994 ok = true;
1995 break;
1996 }
1997
1998 return ok ? res : QVariant();
1999}
2000
2001QList<QgsVectorDataProvider::NativeType> QgsOgrUtils::nativeFieldTypesForDriver( GDALDriverH driver )
2002{
2003 if ( !driver )
2004 return {};
2005
2006 const QString driverName = QString::fromUtf8( GDALGetDriverShortName( driver ) );
2007
2008 int nMaxIntLen = 11;
2009 int nMaxInt64Len = 21;
2010 int nMaxDoubleLen = 20;
2011 int nMaxDoublePrec = 15;
2012 int nDateLen = 8;
2013 if ( driverName == QLatin1String( "GPKG" ) )
2014 {
2015 // GPKG only supports field length for text (and binary)
2016 nMaxIntLen = 0;
2017 nMaxInt64Len = 0;
2018 nMaxDoubleLen = 0;
2019 nMaxDoublePrec = 0;
2020 nDateLen = 0;
2021 }
2022
2023 QList<QgsVectorDataProvider::NativeType> nativeTypes;
2024 nativeTypes
2025 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::Int ), QStringLiteral( "integer" ), QMetaType::Type::Int, 0, nMaxIntLen )
2026 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::LongLong ), QStringLiteral( "integer64" ), QMetaType::Type::LongLong, 0, nMaxInt64Len )
2027 << QgsVectorDataProvider::NativeType( QObject::tr( "Decimal number (real)" ), QStringLiteral( "double" ), QMetaType::Type::Double, 0, nMaxDoubleLen, 0, nMaxDoublePrec )
2028 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QString ), QStringLiteral( "string" ), QMetaType::Type::QString, 0, 65535 );
2029
2030 if ( driverName == QLatin1String( "GPKG" ) )
2031 nativeTypes << QgsVectorDataProvider::NativeType( QObject::tr( "JSON (string)" ), QStringLiteral( "JSON" ), QMetaType::Type::QVariantMap, 0, 0, 0, 0, QMetaType::Type::QString );
2032
2033 bool supportsDate = true;
2034 bool supportsTime = true;
2035 bool supportsDateTime = true;
2036 bool supportsBinary = false;
2037 bool supportIntegerList = false;
2038 bool supportInteger64List = false;
2039 bool supportRealList = false;
2040 bool supportsStringList = false;
2041
2042 // For drivers that advertise their data type, use that instead of the
2043 // above hardcoded defaults.
2044 if ( const char *pszDataTypes = GDALGetMetadataItem( driver, GDAL_DMD_CREATIONFIELDDATATYPES, nullptr ) )
2045 {
2046 char **papszTokens = CSLTokenizeString2( pszDataTypes, " ", 0 );
2047 supportsDate = CSLFindString( papszTokens, "Date" ) >= 0;
2048 supportsTime = CSLFindString( papszTokens, "Time" ) >= 0;
2049 supportsDateTime = CSLFindString( papszTokens, "DateTime" ) >= 0;
2050 supportsBinary = CSLFindString( papszTokens, "Binary" ) >= 0;
2051 supportIntegerList = CSLFindString( papszTokens, "IntegerList" ) >= 0;
2052 supportInteger64List = CSLFindString( papszTokens, "Integer64List" ) >= 0;
2053 supportRealList = CSLFindString( papszTokens, "RealList" ) >= 0;
2054 supportsStringList = CSLFindString( papszTokens, "StringList" ) >= 0;
2055 CSLDestroy( papszTokens );
2056 }
2057
2058 // Older versions of GDAL incorrectly report that shapefiles support
2059 // DateTime.
2060#if GDAL_VERSION_NUM < GDAL_COMPUTE_VERSION(3,2,0)
2061 if ( driverName == QLatin1String( "ESRI Shapefile" ) )
2062 {
2063 supportsDateTime = false;
2064 }
2065#endif
2066
2067 if ( supportsDate )
2068 {
2069 nativeTypes
2070 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QDate ), QStringLiteral( "date" ), QMetaType::Type::QDate, nDateLen, nDateLen );
2071 }
2072 if ( supportsTime )
2073 {
2074 nativeTypes
2075 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QTime ), QStringLiteral( "time" ), QMetaType::Type::QTime );
2076 }
2077 if ( supportsDateTime )
2078 {
2079 nativeTypes
2080 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QDateTime ), QStringLiteral( "datetime" ), QMetaType::Type::QDateTime );
2081 }
2082 if ( supportsBinary )
2083 {
2084 nativeTypes
2085 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QByteArray ), QStringLiteral( "binary" ), QMetaType::Type::QByteArray );
2086 }
2087 if ( supportIntegerList )
2088 {
2089 nativeTypes
2090 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QVariantList, QMetaType::Type::Int ), QStringLiteral( "integerlist" ), QMetaType::Type::QVariantList, 0, 0, 0, 0, QMetaType::Type::Int );
2091 }
2092 if ( supportInteger64List )
2093 {
2094 nativeTypes
2095 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QVariantList, QMetaType::Type::LongLong ), QStringLiteral( "integer64list" ), QMetaType::Type::QVariantList, 0, 0, 0, 0, QMetaType::Type::LongLong );
2096 }
2097 if ( supportRealList )
2098 {
2099 nativeTypes
2100 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QVariantList, QMetaType::Type::Double ), QStringLiteral( "doublelist" ), QMetaType::Type::QVariantList, 0, 0, 0, 0, QMetaType::Type::Double );
2101 }
2102 if ( supportsStringList )
2103 {
2104 nativeTypes
2105 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QStringList ), QStringLiteral( "stringlist" ), QMetaType::Type::QVariantList, 0, 0, 0, 0, QMetaType::Type::QString );
2106 }
2107
2108 const char *pszDataSubTypes = GDALGetMetadataItem( driver, GDAL_DMD_CREATIONFIELDDATASUBTYPES, nullptr );
2109 if ( pszDataSubTypes && strstr( pszDataSubTypes, "Boolean" ) )
2110 {
2111 // boolean data type
2112 nativeTypes
2113 << QgsVectorDataProvider::NativeType( QObject::tr( "Boolean" ), QStringLiteral( "bool" ), QMetaType::Type::Bool );
2114 }
2115
2116 return nativeTypes;
2117}
2118
2119
2120#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,3,0)
2121std::unique_ptr< QgsFieldDomain > QgsOgrUtils::convertFieldDomain( OGRFieldDomainH domain )
2122{
2123 if ( !domain )
2124 return nullptr;
2125
2126 const QString name{ OGR_FldDomain_GetName( domain ) };
2127 const QString description{ OGR_FldDomain_GetDescription( domain ) };
2128
2129 QMetaType::Type fieldType = QMetaType::Type::UnknownType;
2130 QMetaType::Type fieldSubType = QMetaType::Type::UnknownType;
2131 const OGRFieldType domainFieldType = OGR_FldDomain_GetFieldType( domain );
2132 const OGRFieldSubType domainFieldSubType = OGR_FldDomain_GetFieldSubType( domain );
2133 ogrFieldTypeToQVariantType( domainFieldType, domainFieldSubType, fieldType, fieldSubType );
2134
2135 std::unique_ptr< QgsFieldDomain > res;
2136 switch ( OGR_FldDomain_GetDomainType( domain ) )
2137 {
2138 case OFDT_CODED:
2139 {
2140 QList< QgsCodedValue > values;
2141 const OGRCodedValue *codedValue = OGR_CodedFldDomain_GetEnumeration( domain );
2142 while ( codedValue && codedValue->pszCode )
2143 {
2144 const QString code( codedValue->pszCode );
2145
2146 // if pszValue is null then it indicates we are working with a set of acceptable values which aren't
2147 // coded. In this case we copy the code as the value so that QGIS exposes the domain as a choice of
2148 // the valid code values.
2149 const QString value( codedValue->pszValue ? codedValue->pszValue : codedValue->pszCode );
2150 values.append( QgsCodedValue( stringToVariant( domainFieldType, domainFieldSubType, code ), value ) );
2151
2152 codedValue++;
2153 }
2154
2155 res = std::make_unique< QgsCodedFieldDomain >( name, description, fieldType, values );
2156 break;
2157 }
2158
2159 case OFDT_RANGE:
2160 {
2161 QVariant minValue;
2162 bool minIsInclusive = false;
2163 if ( const OGRField *min = OGR_RangeFldDomain_GetMin( domain, &minIsInclusive ) )
2164 {
2165 minValue = QgsOgrUtils::OGRFieldtoVariant( min, domainFieldType );
2166 }
2167 QVariant maxValue;
2168 bool maxIsInclusive = false;
2169 if ( const OGRField *max = OGR_RangeFldDomain_GetMax( domain, &maxIsInclusive ) )
2170 {
2171 maxValue = QgsOgrUtils::OGRFieldtoVariant( max, domainFieldType );
2172 }
2173
2174 res = std::make_unique< QgsRangeFieldDomain >( name, description, fieldType,
2175 minValue, minIsInclusive,
2176 maxValue, maxIsInclusive );
2177 break;
2178 }
2179
2180 case OFDT_GLOB:
2181 res = std::make_unique< QgsGlobFieldDomain >( name, description, fieldType,
2182 QString( OGR_GlobFldDomain_GetGlob( domain ) ) );
2183 break;
2184 }
2185
2186 switch ( OGR_FldDomain_GetMergePolicy( domain ) )
2187 {
2188 case OFDMP_DEFAULT_VALUE:
2189 res->setMergePolicy( Qgis::FieldDomainMergePolicy::DefaultValue );
2190 break;
2191 case OFDMP_SUM:
2192 res->setMergePolicy( Qgis::FieldDomainMergePolicy::Sum );
2193 break;
2194 case OFDMP_GEOMETRY_WEIGHTED:
2196 break;
2197 }
2198
2199 switch ( OGR_FldDomain_GetSplitPolicy( domain ) )
2200 {
2201 case OFDSP_DEFAULT_VALUE:
2202 res->setSplitPolicy( Qgis::FieldDomainSplitPolicy::DefaultValue );
2203 break;
2204 case OFDSP_DUPLICATE:
2205 res->setSplitPolicy( Qgis::FieldDomainSplitPolicy::Duplicate );
2206 break;
2207 case OFDSP_GEOMETRY_RATIO:
2208 res->setSplitPolicy( Qgis::FieldDomainSplitPolicy::GeometryRatio );
2209 break;
2210 }
2211 return res;
2212}
2213
2214OGRFieldDomainH QgsOgrUtils::convertFieldDomain( const QgsFieldDomain *domain )
2215{
2216 if ( !domain )
2217 return nullptr;
2218
2219 OGRFieldType domainFieldType = OFTInteger;
2220 OGRFieldSubType domainFieldSubType = OFSTNone;
2221 variantTypeToOgrFieldType( domain->fieldType(), domainFieldType, domainFieldSubType );
2222
2223 OGRFieldDomainH res = nullptr;
2224 switch ( domain->type() )
2225 {
2227 {
2228 std::vector< OGRCodedValue > enumeration;
2229 const QList< QgsCodedValue> values = qgis::down_cast< const QgsCodedFieldDomain * >( domain )->values();
2230 enumeration.reserve( values.size() );
2231 for ( const QgsCodedValue &value : values )
2232 {
2233 OGRCodedValue codedValue;
2234 codedValue.pszCode = CPLStrdup( value.code().toString().toUtf8().constData() );
2235 codedValue.pszValue = CPLStrdup( value.value().toUtf8().constData() );
2236 enumeration.push_back( codedValue );
2237 }
2238 OGRCodedValue last;
2239 last.pszCode = nullptr;
2240 last.pszValue = nullptr;
2241 enumeration.push_back( last );
2242 res = OGR_CodedFldDomain_Create(
2243 domain->name().toUtf8().constData(),
2244 domain->description().toUtf8().constData(),
2245 domainFieldType,
2246 domainFieldSubType,
2247 enumeration.data()
2248 );
2249
2250 for ( const OGRCodedValue &value : std::as_const( enumeration ) )
2251 {
2252 CPLFree( value.pszCode );
2253 CPLFree( value.pszValue );
2254 }
2255 break;
2256 }
2257
2259 {
2260 std::unique_ptr< OGRField > min = variantToOGRField( qgis::down_cast< const QgsRangeFieldDomain * >( domain )->minimum(), domainFieldType );
2261 std::unique_ptr< OGRField > max = variantToOGRField( qgis::down_cast< const QgsRangeFieldDomain * >( domain )->maximum(), domainFieldType );
2262 if ( !min || !max )
2263 return nullptr;
2264 res = OGR_RangeFldDomain_Create(
2265 domain->name().toUtf8().constData(),
2266 domain->description().toUtf8().constData(),
2267 domainFieldType,
2268 domainFieldSubType,
2269 min.get(),
2270 qgis::down_cast< const QgsRangeFieldDomain * >( domain )->minimumIsInclusive(),
2271 max.get(),
2272 qgis::down_cast< const QgsRangeFieldDomain * >( domain )->maximumIsInclusive()
2273 );
2274 break;
2275 }
2276
2278 {
2279 res = OGR_GlobFldDomain_Create(
2280 domain->name().toUtf8().constData(),
2281 domain->description().toUtf8().constData(),
2282 domainFieldType,
2283 domainFieldSubType,
2284 qgis::down_cast< const QgsGlobFieldDomain * >( domain )->glob().toUtf8().constData()
2285 );
2286 break;
2287 }
2288 }
2289
2290 switch ( domain->mergePolicy() )
2291 {
2293 OGR_FldDomain_SetMergePolicy( res, OFDMP_DEFAULT_VALUE );
2294 break;
2296 OGR_FldDomain_SetMergePolicy( res, OFDMP_GEOMETRY_WEIGHTED );
2297 break;
2299 OGR_FldDomain_SetMergePolicy( res, OFDMP_SUM );
2300 break;
2301 }
2302
2303 switch ( domain->splitPolicy() )
2304 {
2306 OGR_FldDomain_SetSplitPolicy( res, OFDSP_DEFAULT_VALUE );
2307 break;
2309 OGR_FldDomain_SetSplitPolicy( res, OFDSP_GEOMETRY_RATIO );
2310 break;
2312 OGR_FldDomain_SetSplitPolicy( res, OFDSP_DUPLICATE );
2313 break;
2314
2316 // not supported
2317 break;
2318 }
2319
2320 return res;
2321}
2322
2323#endif
2324
2325#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,6,0)
2326QgsWeakRelation QgsOgrUtils::convertRelationship( GDALRelationshipH relationship, const QString &datasetUri )
2327{
2328 QgsProviderMetadata *ogrProviderMetadata = QgsProviderRegistry::instance()->providerMetadata( QStringLiteral( "ogr" ) );
2329 const QVariantMap datasetUriParts = ogrProviderMetadata->decodeUri( datasetUri );
2330
2331 const QString leftTableName( GDALRelationshipGetLeftTableName( relationship ) );
2332
2333 QVariantMap leftTableUriParts = datasetUriParts;
2334 leftTableUriParts.insert( QStringLiteral( "layerName" ), leftTableName );
2335 const QString leftTableSource = ogrProviderMetadata->encodeUri( leftTableUriParts );
2336
2337 const QString rightTableName( GDALRelationshipGetRightTableName( relationship ) );
2338 QVariantMap rightTableUriParts = datasetUriParts;
2339 rightTableUriParts.insert( QStringLiteral( "layerName" ), rightTableName );
2340 const QString rightTableSource = ogrProviderMetadata->encodeUri( rightTableUriParts );
2341
2342 const QString mappingTableName( GDALRelationshipGetMappingTableName( relationship ) );
2343 QString mappingTableSource;
2344 if ( !mappingTableName.isEmpty() )
2345 {
2346 QVariantMap mappingTableUriParts = datasetUriParts;
2347 mappingTableUriParts.insert( QStringLiteral( "layerName" ), mappingTableName );
2348 mappingTableSource = ogrProviderMetadata->encodeUri( mappingTableUriParts );
2349 }
2350
2351 const QString relationshipName( GDALRelationshipGetName( relationship ) );
2352
2353 char **cslLeftTableFieldNames = GDALRelationshipGetLeftTableFields( relationship );
2354 const QStringList leftTableFieldNames = QgsOgrUtils::cStringListToQStringList( cslLeftTableFieldNames );
2355 CSLDestroy( cslLeftTableFieldNames );
2356
2357 char **cslRightTableFieldNames = GDALRelationshipGetRightTableFields( relationship );
2358 const QStringList rightTableFieldNames = QgsOgrUtils::cStringListToQStringList( cslRightTableFieldNames );
2359 CSLDestroy( cslRightTableFieldNames );
2360
2361 char **cslLeftMappingTableFieldNames = GDALRelationshipGetLeftMappingTableFields( relationship );
2362 const QStringList leftMappingTableFieldNames = QgsOgrUtils::cStringListToQStringList( cslLeftMappingTableFieldNames );
2363 CSLDestroy( cslLeftMappingTableFieldNames );
2364
2365 char **cslRightMappingTableFieldNames = GDALRelationshipGetRightMappingTableFields( relationship );
2366 const QStringList rightMappingTableFieldNames = QgsOgrUtils::cStringListToQStringList( cslRightMappingTableFieldNames );
2367 CSLDestroy( cslRightMappingTableFieldNames );
2368
2369 const QString forwardPathLabel( GDALRelationshipGetForwardPathLabel( relationship ) );
2370 const QString backwardPathLabel( GDALRelationshipGetBackwardPathLabel( relationship ) );
2371 const QString relatedTableType( GDALRelationshipGetRelatedTableType( relationship ) );
2372
2373 const GDALRelationshipType relationshipType = GDALRelationshipGetType( relationship );
2375 switch ( relationshipType )
2376 {
2377 case GRT_COMPOSITE:
2379 break;
2380
2381 case GRT_ASSOCIATION:
2383 break;
2384
2385 case GRT_AGGREGATION:
2386 QgsLogger::warning( "Aggregation relationships are not supported, treating as association instead" );
2387 break;
2388 }
2389
2390 const GDALRelationshipCardinality eCardinality = GDALRelationshipGetCardinality( relationship );
2392 switch ( eCardinality )
2393 {
2394 case GRC_ONE_TO_ONE:
2396 break;
2397 case GRC_ONE_TO_MANY:
2399 break;
2400 case GRC_MANY_TO_ONE:
2402 break;
2403 case GRC_MANY_TO_MANY:
2405 break;
2406 }
2407
2408 switch ( cardinality )
2409 {
2413 {
2414 QgsWeakRelation rel( relationshipName,
2415 relationshipName,
2416 strength,
2417 QString(), QString(), rightTableSource, QStringLiteral( "ogr" ),
2418 QString(), QString(), leftTableSource, QStringLiteral( "ogr" ) );
2419 rel.setCardinality( cardinality );
2420 rel.setForwardPathLabel( forwardPathLabel );
2421 rel.setBackwardPathLabel( backwardPathLabel );
2422 rel.setRelatedTableType( relatedTableType );
2423 rel.setReferencedLayerFields( leftTableFieldNames );
2424 rel.setReferencingLayerFields( rightTableFieldNames );
2425 return rel;
2426 }
2427
2429 {
2430 QgsWeakRelation rel( relationshipName,
2431 relationshipName,
2432 strength,
2433 QString(), QString(), rightTableSource, QStringLiteral( "ogr" ),
2434 QString(), QString(), leftTableSource, QStringLiteral( "ogr" ) );
2435 rel.setCardinality( cardinality );
2436 rel.setForwardPathLabel( forwardPathLabel );
2437 rel.setBackwardPathLabel( backwardPathLabel );
2438 rel.setRelatedTableType( relatedTableType );
2439 rel.setMappingTable( QgsVectorLayerRef( QString(), QString(), mappingTableSource, QStringLiteral( "ogr" ) ) );
2440 rel.setReferencedLayerFields( leftTableFieldNames );
2441 rel.setMappingReferencedLayerFields( leftMappingTableFieldNames );
2442 rel.setReferencingLayerFields( rightTableFieldNames );
2443 rel.setMappingReferencingLayerFields( rightMappingTableFieldNames );
2444 return rel;
2445 }
2446 }
2447 return QgsWeakRelation();
2448}
2449
2451{
2452 GDALRelationshipCardinality gCardinality = GDALRelationshipCardinality::GRC_ONE_TO_MANY;
2453 switch ( relationship.cardinality() )
2454 {
2456 gCardinality = GDALRelationshipCardinality::GRC_ONE_TO_ONE;
2457 break;
2459 gCardinality = GDALRelationshipCardinality::GRC_ONE_TO_MANY;
2460 break;
2462 gCardinality = GDALRelationshipCardinality::GRC_MANY_TO_ONE;
2463 break;
2465 gCardinality = GDALRelationshipCardinality::GRC_MANY_TO_MANY;
2466 break;
2467 }
2468
2469 QgsProviderMetadata *ogrProviderMetadata = QgsProviderRegistry::instance()->providerMetadata( QStringLiteral( "ogr" ) );
2470
2471 const QVariantMap leftParts = ogrProviderMetadata->decodeUri( relationship.referencedLayerSource() );
2472 const QString leftTableName = leftParts.value( QStringLiteral( "layerName" ) ).toString();
2473 if ( leftTableName.isEmpty() )
2474 {
2475 error = QObject::tr( "Parent table name was not set" );
2476 return nullptr;
2477 }
2478
2479 const QVariantMap rightParts = ogrProviderMetadata->decodeUri( relationship.referencingLayerSource() );
2480 const QString rightTableName = rightParts.value( QStringLiteral( "layerName" ) ).toString();
2481 if ( rightTableName.isEmpty() )
2482 {
2483 error = QObject::tr( "Child table name was not set" );
2484 return nullptr;
2485 }
2486
2487 if ( leftParts.value( QStringLiteral( "path" ) ).toString() != rightParts.value( QStringLiteral( "path" ) ).toString() )
2488 {
2489 error = QObject::tr( "Parent and child table must be from the same dataset" );
2490 return nullptr;
2491 }
2492
2493 QString mappingTableName;
2494 if ( !relationship.mappingTableSource().isEmpty() )
2495 {
2496 const QVariantMap mappingParts = ogrProviderMetadata->decodeUri( relationship.mappingTableSource() );
2497 mappingTableName = mappingParts.value( QStringLiteral( "layerName" ) ).toString();
2498 if ( leftParts.value( QStringLiteral( "path" ) ).toString() != mappingParts.value( QStringLiteral( "path" ) ).toString() )
2499 {
2500 error = QObject::tr( "Parent and mapping table must be from the same dataset" );
2501 return nullptr;
2502 }
2503 }
2504
2505 gdal::relationship_unique_ptr relationH( GDALRelationshipCreate( relationship.name().toLocal8Bit().constData(),
2506 leftTableName.toLocal8Bit().constData(),
2507 rightTableName.toLocal8Bit().constData(),
2508 gCardinality ) );
2509
2510 // set left table fields
2511 const QStringList leftFieldNames = relationship.referencedLayerFields();
2512 int count = leftFieldNames.count();
2513 char **lst = new char *[count + 1];
2514 if ( count > 0 )
2515 {
2516 int pos = 0;
2517 for ( const QString &string : leftFieldNames )
2518 {
2519 lst[pos] = CPLStrdup( string.toLocal8Bit().constData() );
2520 pos++;
2521 }
2522 }
2523 lst[count] = nullptr;
2524 GDALRelationshipSetLeftTableFields( relationH.get(), lst );
2525 CSLDestroy( lst );
2526
2527 // set right table fields
2528 const QStringList rightFieldNames = relationship.referencingLayerFields();
2529 count = rightFieldNames.count();
2530 lst = new char *[count + 1];
2531 if ( count > 0 )
2532 {
2533 int pos = 0;
2534 for ( const QString &string : rightFieldNames )
2535 {
2536 lst[pos] = CPLStrdup( string.toLocal8Bit().constData() );
2537 pos++;
2538 }
2539 }
2540 lst[count] = nullptr;
2541 GDALRelationshipSetRightTableFields( relationH.get(), lst );
2542 CSLDestroy( lst );
2543
2544 if ( !mappingTableName.isEmpty() )
2545 {
2546 GDALRelationshipSetMappingTableName( relationH.get(), mappingTableName.toLocal8Bit().constData() );
2547
2548 // set left mapping table fields
2549 const QStringList leftFieldNames = relationship.mappingReferencedLayerFields();
2550 int count = leftFieldNames.count();
2551 char **lst = new char *[count + 1];
2552 if ( count > 0 )
2553 {
2554 int pos = 0;
2555 for ( const QString &string : leftFieldNames )
2556 {
2557 lst[pos] = CPLStrdup( string.toLocal8Bit().constData() );
2558 pos++;
2559 }
2560 }
2561 lst[count] = nullptr;
2562 GDALRelationshipSetLeftMappingTableFields( relationH.get(), lst );
2563 CSLDestroy( lst );
2564
2565 // set right table fields
2566 const QStringList rightFieldNames = relationship.mappingReferencingLayerFields();
2567 count = rightFieldNames.count();
2568 lst = new char *[count + 1];
2569 if ( count > 0 )
2570 {
2571 int pos = 0;
2572 for ( const QString &string : rightFieldNames )
2573 {
2574 lst[pos] = CPLStrdup( string.toLocal8Bit().constData() );
2575 pos++;
2576 }
2577 }
2578 lst[count] = nullptr;
2579 GDALRelationshipSetRightMappingTableFields( relationH.get(), lst );
2580 CSLDestroy( lst );
2581 }
2582
2583 // set type
2584 switch ( relationship.strength() )
2585 {
2587 GDALRelationshipSetType( relationH.get(), GDALRelationshipType::GRT_ASSOCIATION );
2588 break;
2589
2591 GDALRelationshipSetType( relationH.get(), GDALRelationshipType::GRT_COMPOSITE );
2592 break;
2593 }
2594
2595 // set labels
2596 if ( !relationship.forwardPathLabel().isEmpty() )
2597 GDALRelationshipSetForwardPathLabel( relationH.get(), relationship.forwardPathLabel().toLocal8Bit().constData() );
2598 if ( !relationship.backwardPathLabel().isEmpty() )
2599 GDALRelationshipSetBackwardPathLabel( relationH.get(), relationship.backwardPathLabel().toLocal8Bit().constData() );
2600
2601 // set table type
2602 if ( !relationship.relatedTableType().isEmpty() )
2603 GDALRelationshipSetRelatedTableType( relationH.get(), relationship.relatedTableType().toLocal8Bit().constData() );
2604
2605 return relationH;
2606}
2607#endif
2608
2609int QgsOgrUtils::listStyles( GDALDatasetH hDS, const QString &layerName, const QString &geomColumn, QStringList &ids, QStringList &names, QStringList &descriptions, QString &errCause )
2610{
2611 OGRLayerH hLayer = GDALDatasetGetLayerByName( hDS, "layer_styles" );
2612 if ( !hLayer )
2613 {
2614 QgsDebugMsgLevel( QStringLiteral( "No styles available on DB" ), 2 );
2615 errCause = QObject::tr( "No styles available on DB" );
2616 return 0;
2617 }
2618
2619 if ( OGR_L_GetFeatureCount( hLayer, TRUE ) == 0 )
2620 {
2621 QgsDebugMsgLevel( QStringLiteral( "No styles available on DB" ), 2 );
2622 errCause = QObject::tr( "No styles available on DB" );
2623 return 0;
2624 }
2625
2626 OGRFeatureDefnH hLayerDefn = OGR_L_GetLayerDefn( hLayer );
2627
2628 OGR_L_ResetReading( hLayer );
2629
2630 QList<qlonglong> listTimestamp;
2631 QMap<int, QString> mapIdToStyleName;
2632 QMap<int, QString> mapIdToDescription;
2633 QMap<qlonglong, QList<int> > mapTimestampToId;
2634 int numberOfRelatedStyles = 0;
2635
2636 while ( true )
2637 {
2638 gdal::ogr_feature_unique_ptr hFeature( OGR_L_GetNextFeature( hLayer ) );
2639 if ( !hFeature )
2640 break;
2641
2642 QString tableName( QString::fromUtf8(
2643 OGR_F_GetFieldAsString( hFeature.get(),
2644 OGR_FD_GetFieldIndex( hLayerDefn, "f_table_name" ) ) ) );
2645 QString geometryColumn( QString::fromUtf8(
2646 OGR_F_GetFieldAsString( hFeature.get(),
2647 OGR_FD_GetFieldIndex( hLayerDefn, "f_geometry_column" ) ) ) );
2648 QString styleName( QString::fromUtf8(
2649 OGR_F_GetFieldAsString( hFeature.get(),
2650 OGR_FD_GetFieldIndex( hLayerDefn, "styleName" ) ) ) );
2651 QString description( QString::fromUtf8(
2652 OGR_F_GetFieldAsString( hFeature.get(),
2653 OGR_FD_GetFieldIndex( hLayerDefn, "description" ) ) ) );
2654 int fid = static_cast<int>( OGR_F_GetFID( hFeature.get() ) );
2655 if ( tableName == layerName &&
2656 geometryColumn == geomColumn )
2657 {
2658 // Append first all related styles
2659 QString id( QString::number( fid ) );
2660 ids.append( id );
2661 names.append( styleName );
2662 descriptions.append( description );
2663 ++ numberOfRelatedStyles;
2664 }
2665 else
2666 {
2667 int year, month, day, hour, minute, second, TZ;
2668 OGR_F_GetFieldAsDateTime( hFeature.get(), OGR_FD_GetFieldIndex( hLayerDefn, "update_time" ),
2669 &year, &month, &day, &hour, &minute, &second, &TZ );
2670 const qlonglong ts = second + minute * 60 + hour * 3600 + day * 24 * 3600 +
2671 static_cast<qlonglong>( month ) * 31 * 24 * 3600 + static_cast<qlonglong>( year ) * 12 * 31 * 24 * 3600;
2672
2673 listTimestamp.append( ts );
2674 mapIdToStyleName[fid] = styleName;
2675 mapIdToDescription[fid] = description;
2676 mapTimestampToId[ts].append( fid );
2677 }
2678 }
2679
2680 std::sort( listTimestamp.begin(), listTimestamp.end() );
2681 // Sort from most recent to least recent
2682 for ( int i = listTimestamp.size() - 1; i >= 0; i-- )
2683 {
2684 const QList<int> &listId = mapTimestampToId[listTimestamp[i]];
2685 for ( int j = 0; j < listId.size(); j++ )
2686 {
2687 int fid = listId[j];
2688 QString id( QString::number( fid ) );
2689 ids.append( id );
2690 names.append( mapIdToStyleName[fid] );
2691 descriptions.append( mapIdToDescription[fid] );
2692 }
2693 }
2694
2695 return numberOfRelatedStyles;
2696}
2697
2698bool QgsOgrUtils::styleExists( GDALDatasetH hDS, const QString &layerName, const QString &geomColumn, const QString &styleId, QString &errorCause )
2699{
2700 errorCause.clear();
2701
2702 // check if layer_styles table exists
2703 OGRLayerH hLayer = GDALDatasetGetLayerByName( hDS, "layer_styles" );
2704 if ( !hLayer )
2705 return false;
2706
2707 const QString realStyleId = styleId.isEmpty() ? layerName : styleId;
2708
2709 const QString checkQuery = QStringLiteral( "f_table_schema=''"
2710 " AND f_table_name=%1"
2711 " AND f_geometry_column=%2"
2712 " AND styleName=%3" )
2713 .arg( QgsOgrProviderUtils::quotedValue( layerName ),
2714 QgsOgrProviderUtils::quotedValue( geomColumn ),
2715 QgsOgrProviderUtils::quotedValue( realStyleId ) );
2716 OGR_L_SetAttributeFilter( hLayer, checkQuery.toUtf8().constData() );
2717 OGR_L_ResetReading( hLayer );
2718 gdal::ogr_feature_unique_ptr hFeature( OGR_L_GetNextFeature( hLayer ) );
2719 OGR_L_ResetReading( hLayer );
2720
2721 if ( hFeature )
2722 return true;
2723
2724 return false;
2725}
2726
2727QString QgsOgrUtils::getStyleById( GDALDatasetH hDS, const QString &styleId, QString &errCause )
2728{
2729 OGRLayerH hLayer = GDALDatasetGetLayerByName( hDS, "layer_styles" );
2730 if ( !hLayer )
2731 {
2732 QgsDebugMsgLevel( QStringLiteral( "No styles available on DB" ), 2 );
2733 errCause = QObject::tr( "No styles available on DB" );
2734 return QString();
2735 }
2736
2737 bool ok;
2738 int id = styleId.toInt( &ok );
2739 if ( !ok )
2740 {
2741 errCause = QObject::tr( "Invalid style identifier" );
2742 return QString();
2743 }
2744
2745 gdal::ogr_feature_unique_ptr hFeature( OGR_L_GetFeature( hLayer, id ) );
2746 if ( !hFeature )
2747 {
2748 errCause = QObject::tr( "No style corresponding to style identifier" );
2749 return QString();
2750 }
2751
2752 OGRFeatureDefnH hLayerDefn = OGR_L_GetLayerDefn( hLayer );
2753 QString styleQML( QString::fromUtf8(
2754 OGR_F_GetFieldAsString( hFeature.get(),
2755 OGR_FD_GetFieldIndex( hLayerDefn, "styleQML" ) ) ) );
2756 OGR_L_ResetReading( hLayer );
2757
2758 return styleQML;
2759}
2760
2761bool QgsOgrUtils::deleteStyleById( GDALDatasetH hDS, const QString &styleId, QString &errCause )
2762{
2763 bool deleted;
2764
2765 OGRLayerH hLayer = GDALDatasetGetLayerByName( hDS, "layer_styles" );
2766
2767 // check if layer_styles table already exist
2768 if ( !hLayer )
2769 {
2770 errCause = QObject::tr( "Connection to database failed" );
2771 deleted = false;
2772 }
2773 else
2774 {
2775 if ( OGR_L_DeleteFeature( hLayer, styleId.toInt() ) != OGRERR_NONE )
2776 {
2777 errCause = QObject::tr( "Error executing the delete query." );
2778 deleted = false;
2779 }
2780 else
2781 {
2782 deleted = true;
2783 }
2784 }
2785 return deleted;
2786}
2787
2788QString QgsOgrUtils::loadStoredStyle( GDALDatasetH hDS, const QString &layerName, const QString &geomColumn, QString &styleName, QString &errCause )
2789{
2790 OGRLayerH hLayer = GDALDatasetGetLayerByName( hDS, "layer_styles" );
2791 if ( !hLayer )
2792 {
2793 QgsDebugMsgLevel( QStringLiteral( "No styles available on DB" ), 2 );
2794 errCause = QObject::tr( "No styles available on DB" );
2795 return QString();
2796 }
2797
2798 QString selectQmlQuery = QStringLiteral( "f_table_schema=''"
2799 " AND f_table_name=%1"
2800 " AND f_geometry_column=%2"
2801 " ORDER BY CASE WHEN useAsDefault THEN 1 ELSE 2 END"
2802 ",update_time DESC" )
2803 .arg( QgsOgrProviderUtils::quotedValue( layerName ),
2804 QgsOgrProviderUtils::quotedValue( geomColumn ) );
2805 OGR_L_SetAttributeFilter( hLayer, selectQmlQuery.toUtf8().constData() );
2806 OGR_L_ResetReading( hLayer );
2807 OGRFeatureDefnH hLayerDefn = OGR_L_GetLayerDefn( hLayer );
2808 QString styleQML;
2809 qlonglong moreRecentTimestamp = 0;
2810 while ( true )
2811 {
2812 gdal::ogr_feature_unique_ptr hFeat( OGR_L_GetNextFeature( hLayer ) );
2813 if ( !hFeat )
2814 break;
2815 if ( OGR_F_GetFieldAsInteger( hFeat.get(), OGR_FD_GetFieldIndex( hLayerDefn, "useAsDefault" ) ) )
2816 {
2817 styleQML = QString::fromUtf8(
2818 OGR_F_GetFieldAsString( hFeat.get(), OGR_FD_GetFieldIndex( hLayerDefn, "styleQML" ) ) );
2819 styleName = QString::fromUtf8(
2820 OGR_F_GetFieldAsString( hFeat.get(), OGR_FD_GetFieldIndex( hLayerDefn, "styleName" ) ) );
2821 break;
2822 }
2823
2824 int year, month, day, hour, minute, second, TZ;
2825 OGR_F_GetFieldAsDateTime( hFeat.get(), OGR_FD_GetFieldIndex( hLayerDefn, "update_time" ),
2826 &year, &month, &day, &hour, &minute, &second, &TZ );
2827 qlonglong ts = second + minute * 60 + hour * 3600 + day * 24 * 3600 +
2828 static_cast<qlonglong>( month ) * 31 * 24 * 3600 + static_cast<qlonglong>( year ) * 12 * 31 * 24 * 3600;
2829 if ( ts > moreRecentTimestamp )
2830 {
2831 moreRecentTimestamp = ts;
2832 styleQML = QString::fromUtf8(
2833 OGR_F_GetFieldAsString( hFeat.get(), OGR_FD_GetFieldIndex( hLayerDefn, "styleQML" ) ) );
2834 styleName = QString::fromUtf8(
2835 OGR_F_GetFieldAsString( hFeat.get(), OGR_FD_GetFieldIndex( hLayerDefn, "styleName" ) ) );
2836 }
2837 }
2838 OGR_L_ResetReading( hLayer );
2839
2840 return styleQML;
2841}
2842
2844 GDALDatasetH hDS, const QString &layerName, const QString &geomColumn, const QString &qmlStyle, const QString &sldStyle,
2845 const QString &styleName, const QString &styleDescription,
2846 const QString &uiFileContent, bool useAsDefault, QString &errCause
2847)
2848{
2849 // check if layer_styles table already exist
2850 OGRLayerH hLayer = GDALDatasetGetLayerByName( hDS, "layer_styles" );
2851 if ( !hLayer )
2852 {
2853 // if not create it
2854 // Note: we use the same schema as in the SpatiaLite and postgres providers
2855 //for cross interoperability
2856
2857 char **options = nullptr;
2858 // TODO: might need change if other drivers than GPKG / SQLite
2859 options = CSLSetNameValue( options, "FID", "id" );
2860 hLayer = GDALDatasetCreateLayer( hDS, "layer_styles", nullptr, wkbNone, options );
2861 QgsOgrProviderUtils::invalidateCachedDatasets( QString::fromUtf8( GDALGetDescription( hDS ) ) );
2862 CSLDestroy( options );
2863 if ( !hLayer )
2864 {
2865 errCause = QObject::tr( "Unable to save layer style. It's not possible to create the destination table on the database." );
2866 return false;
2867 }
2868 bool ok = true;
2869 {
2870 gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "f_table_catalog", OFTString ) );
2871 OGR_Fld_SetWidth( fld.get(), 256 );
2872 ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
2873 }
2874 {
2875 gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "f_table_schema", OFTString ) );
2876 OGR_Fld_SetWidth( fld.get(), 256 );
2877 ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
2878 }
2879 {
2880 gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "f_table_name", OFTString ) );
2881 OGR_Fld_SetWidth( fld.get(), 256 );
2882 ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
2883 }
2884 {
2885 gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "f_geometry_column", OFTString ) );
2886 OGR_Fld_SetWidth( fld.get(), 256 );
2887 ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
2888 }
2889 {
2890 gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "styleName", OFTString ) );
2891 OGR_Fld_SetWidth( fld.get(), 30 );
2892 ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
2893 }
2894 {
2895 gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "styleQML", OFTString ) );
2896 ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
2897 }
2898 {
2899 gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "styleSLD", OFTString ) );
2900 ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
2901 }
2902 {
2903 gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "useAsDefault", OFTInteger ) );
2904 OGR_Fld_SetSubType( fld.get(), OFSTBoolean );
2905 ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
2906 }
2907 {
2908 gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "description", OFTString ) );
2909 ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
2910 }
2911 {
2912 gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "owner", OFTString ) );
2913 OGR_Fld_SetWidth( fld.get(), 30 );
2914 ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
2915 }
2916 {
2917 gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "ui", OFTString ) );
2918 OGR_Fld_SetWidth( fld.get(), 30 );
2919 ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
2920 }
2921 {
2922 gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( "update_time", OFTDateTime ) );
2923 OGR_Fld_SetDefault( fld.get(), "CURRENT_TIMESTAMP" );
2924 ok &= OGR_L_CreateField( hLayer, fld.get(), true ) == OGRERR_NONE;
2925 }
2926 if ( !ok )
2927 {
2928 errCause = QObject::tr( "Unable to save layer style. It's not possible to create the destination table on the database." );
2929 return false;
2930 }
2931 }
2932
2933 QString realStyleName =
2934 styleName.isEmpty() ? layerName : styleName;
2935
2936 OGRFeatureDefnH hLayerDefn = OGR_L_GetLayerDefn( hLayer );
2937
2938 if ( useAsDefault )
2939 {
2940 QString oldDefaultQuery = QStringLiteral( "useAsDefault = 1 AND f_table_schema=''"
2941 " AND f_table_name=%1"
2942 " AND f_geometry_column=%2" )
2943 .arg( QgsOgrProviderUtils::quotedValue( layerName ) )
2944 .arg( QgsOgrProviderUtils::quotedValue( geomColumn ) );
2945 OGR_L_SetAttributeFilter( hLayer, oldDefaultQuery.toUtf8().constData() );
2946 gdal::ogr_feature_unique_ptr hFeature( OGR_L_GetNextFeature( hLayer ) );
2947 if ( hFeature )
2948 {
2949 OGR_F_SetFieldInteger( hFeature.get(),
2950 OGR_FD_GetFieldIndex( hLayerDefn, "useAsDefault" ),
2951 0 );
2952 bool ok = OGR_L_SetFeature( hLayer, hFeature.get() ) == 0;
2953 if ( !ok )
2954 {
2955 QgsDebugError( QStringLiteral( "Could not unset previous useAsDefault style" ) );
2956 }
2957 }
2958 }
2959
2960 QString checkQuery = QStringLiteral( "f_table_schema=''"
2961 " AND f_table_name=%1"
2962 " AND f_geometry_column=%2"
2963 " AND styleName=%3" )
2964 .arg( QgsOgrProviderUtils::quotedValue( layerName ) )
2965 .arg( QgsOgrProviderUtils::quotedValue( geomColumn ) )
2966 .arg( QgsOgrProviderUtils::quotedValue( realStyleName ) );
2967 OGR_L_SetAttributeFilter( hLayer, checkQuery.toUtf8().constData() );
2968 OGR_L_ResetReading( hLayer );
2969 gdal::ogr_feature_unique_ptr hFeature( OGR_L_GetNextFeature( hLayer ) );
2970 OGR_L_ResetReading( hLayer );
2971 bool bNew = true;
2972
2973 if ( hFeature )
2974 {
2975 bNew = false;
2976 }
2977 else
2978 {
2979 hFeature.reset( OGR_F_Create( hLayerDefn ) );
2980 OGR_F_SetFieldString( hFeature.get(),
2981 OGR_FD_GetFieldIndex( hLayerDefn, "f_table_catalog" ),
2982 "" );
2983 OGR_F_SetFieldString( hFeature.get(),
2984 OGR_FD_GetFieldIndex( hLayerDefn, "f_table_schema" ),
2985 "" );
2986 OGR_F_SetFieldString( hFeature.get(),
2987 OGR_FD_GetFieldIndex( hLayerDefn, "f_table_name" ),
2988 layerName.toUtf8().constData() );
2989 OGR_F_SetFieldString( hFeature.get(),
2990 OGR_FD_GetFieldIndex( hLayerDefn, "f_geometry_column" ),
2991 geomColumn.toUtf8().constData() );
2992 OGR_F_SetFieldString( hFeature.get(),
2993 OGR_FD_GetFieldIndex( hLayerDefn, "styleName" ),
2994 realStyleName.toUtf8().constData() );
2995 if ( !uiFileContent.isEmpty() )
2996 {
2997 OGR_F_SetFieldString( hFeature.get(),
2998 OGR_FD_GetFieldIndex( hLayerDefn, "ui" ),
2999 uiFileContent.toUtf8().constData() );
3000 }
3001 }
3002 OGR_F_SetFieldString( hFeature.get(),
3003 OGR_FD_GetFieldIndex( hLayerDefn, "styleQML" ),
3004 qmlStyle.toUtf8().constData() );
3005 OGR_F_SetFieldString( hFeature.get(),
3006 OGR_FD_GetFieldIndex( hLayerDefn, "styleSLD" ),
3007 sldStyle.toUtf8().constData() );
3008 OGR_F_SetFieldInteger( hFeature.get(),
3009 OGR_FD_GetFieldIndex( hLayerDefn, "useAsDefault" ),
3010 useAsDefault ? 1 : 0 );
3011 OGR_F_SetFieldString( hFeature.get(),
3012 OGR_FD_GetFieldIndex( hLayerDefn, "description" ),
3013 ( styleDescription.isEmpty() ? QDateTime::currentDateTime().toString() : styleDescription ).toUtf8().constData() );
3014 OGR_F_SetFieldString( hFeature.get(),
3015 OGR_FD_GetFieldIndex( hLayerDefn, "owner" ),
3016 "" );
3017
3018 bool bFeatureOK;
3019 if ( bNew )
3020 bFeatureOK = OGR_L_CreateFeature( hLayer, hFeature.get() ) == OGRERR_NONE;
3021 else
3022 bFeatureOK = OGR_L_SetFeature( hLayer, hFeature.get() ) == OGRERR_NONE;
3023
3024 if ( !bFeatureOK )
3025 {
3026 QgsMessageLog::logMessage( QObject::tr( "Error updating style" ) );
3027 errCause = QObject::tr( "Error looking for style. The query was logged" );
3028 return false;
3029 }
3030
3031 return true;
3032}
RelationshipStrength
Relationship strength.
Definition qgis.h:4078
@ 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:2847
@ Line
Vertical line.
@ Triangle
Triangle.
@ Cross2
Rotated cross (lines only), 'x' shape.
@ Cross
Cross (lines only)
RenderUnit
Rendering size units.
Definition qgis.h:4795
@ Millimeters
Millimeters.
@ Points
Points (e.g., for font sizes)
@ MapUnits
Map units.
@ Coded
Coded field domain.
@ Range
Numeric range field domain (min/max)
@ Glob
Glob string pattern field domain.
RelationshipCardinality
Relationship cardinality.
Definition qgis.h:4090
@ 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:574
@ 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:256
@ LineString25D
LineString25D.
@ MultiSurfaceM
MultiSurfaceM.
@ PolyhedralSurfaceM
PolyhedralSurfaceM.
@ 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.
@ PolyhedralSurfaceZM
PolyhedralSurfaceM.
@ MultiPolygonZ
MultiPolygonZ.
@ CurvePolygonZ
CurvePolygonZ.
@ MultiCurve
MultiCurve.
@ CompoundCurveZ
CompoundCurveZ.
@ MultiCurveZ
MultiCurveZ.
@ MultiCurveM
MultiCurveM.
@ PolyhedralSurfaceZ
PolyhedralSurfaceZ.
@ CircularStringM
CircularStringM.
@ CurvePolygon
CurvePolygon.
@ PointZM
PointZM.
@ TriangleM
TriangleM.
@ CircularStringZ
CircularStringZ.
@ LineStringZ
LineStringZ.
@ PolyhedralSurface
PolyhedralSurface.
@ MultiSurface
MultiSurface.
@ PolygonZ
PolygonZ.
@ PreferredGdal
Preferred format for conversion of CRS to WKT for use with the GDAL library.
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.
QString toWkt(Qgis::CrsWktVariant variant=Qgis::CrsWktVariant::Wkt1Gdal, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
void setCoordinateEpoch(double epoch)
Sets the coordinate epoch, as a decimal year.
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.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
Q_INVOKABLE bool setAttribute(int field, const QVariant &attr)
Sets an attribute's value by field index.
void initAttributes(int fieldCount)
Initialize this feature with the given number of fields.
void setFields(const QgsFields &fields, bool initAttributes=false)
Assigns a field map with the feature to allow attribute access by attribute name.
void setId(QgsFeatureId id)
Sets the feature id for this feature.
void clearGeometry()
Removes any geometry associated with the feature.
void setValid(bool validity)
Sets the validity of the feature.
bool isValid() const
Returns the validity of this feature.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Base class for field domains.
Qgis::FieldDomainMergePolicy mergePolicy() const
Returns the merge policy.
Qgis::FieldDomainSplitPolicy splitPolicy() const
Returns the split policy.
QMetaType::Type fieldType() const
Returns the associated field type.
virtual Qgis::FieldDomainType type() const =0
Returns the type of field domain.
QString name() const
Returns the name of the field domain.
QString description() const
Returns the description of the field domain.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:53
QMetaType::Type type
Definition qgsfield.h:60
QMetaType::Type subType() const
If the field is a collection, gets its element's type.
Definition qgsfield.cpp:155
Container of fields for a vector layer.
Definition qgsfields.h:46
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:69
int count
Definition qgsfields.h:50
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Abstract base class for fill symbol layers.
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.
void fromWkb(unsigned char *wkb, int length)
Set the geometry, feeding in the buffer containing OGC Well-Known Binary and the buffer's length.
Abstract base class for line symbol layers.
static void warning(const QString &msg)
Goes to qWarning.
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 QString loadStoredStyle(GDALDatasetH hDS, const QString &layerName, const QString &geomColumn, QString &styleName, QString &errCause)
Helper function for loading a stored styles in ogr/gdal database datasources.
static QString getStyleById(GDALDatasetH hDS, const QString &styleId, QString &errCause)
Helper function for getting a style by ID from ogr/gdal database datasources.
static Qgis::WkbType ogrGeometryTypeToQgsWkbType(OGRwkbGeometryType ogrGeomType)
Converts a OGRwkbGeometryType to QgsWkbTypes::Type.
static int listStyles(GDALDatasetH hDS, const QString &layerName, const QString &geomColumn, QStringList &ids, QStringList &names, QStringList &descriptions, QString &errCause)
Helper function for listing styles in ogr/gdal database datasources.
static QgsGeometry ogrGeometryToQgsGeometry(OGRGeometryH geom)
Converts an OGR geometry representation to a QgsGeometry object.
static QString OGRSpatialReferenceToWkt(OGRSpatialReferenceH srs)
Returns a WKT string corresponding to the specified OGR srs object.
static OGRSpatialReferenceH crsToOGRSpatialReference(const QgsCoordinateReferenceSystem &crs)
Returns a OGRSpatialReferenceH corresponding to the specified crs object.
static bool saveStyle(GDALDatasetH hDS, const QString &layerName, const QString &geomColumn, const QString &qmlStyle, const QString &sldStyle, const QString &styleName, const QString &styleDescription, const QString &uiFileContent, bool useAsDefault, QString &errCause)
Helper function for saving a style to ogr/gdal database datasources.
static bool styleExists(GDALDatasetH hDS, const QString &layerName, const QString &geomColumn, const QString &styleId, QString &errorCause)
Helper function for checking whether a style exists in ogr/gdal database datasources.
static QVariant OGRFieldtoVariant(const OGRField *value, OGRFieldType type)
Converts an OGRField value of the specified type into a QVariant.
static QgsFeature readOgrFeature(OGRFeatureH ogrFet, const QgsFields &fields, QTextCodec *encoding)
Reads an OGR feature and converts it to a QgsFeature.
static QStringList cStringListToQStringList(char **stringList)
Converts a c string list to a QStringList.
static void ogrFieldTypeToQVariantType(OGRFieldType ogrType, OGRFieldSubType ogrSubType, QMetaType::Type &variantType, QMetaType::Type &variantSubType)
Converts an OGR field type and sub type to the best matching QVariant::Type equivalent.
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 std::unique_ptr< OGRField > variantToOGRField(const QVariant &value, OGRFieldType type)
Converts a QVariant to an OGRField value of specified type.
static bool deleteStyleById(GDALDatasetH hDS, const QString &styleId, QString &errCause)
Helper function for deleting a style by id from ogr/gdal database datasources.
static void variantTypeToOgrFieldType(QMetaType::Type variantType, OGRFieldType &ogrType, OGRFieldSubType &ogrSubType)
Converts an QVariant type to the best matching OGR field type and sub type.
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(QMetaType::Type type, QMetaType::Type subType=QMetaType::Type::UnknownType)
Returns a user-friendly translated string representing a QVariant type.
static QVariant createNullVariant(QMetaType::Type metaType)
Helper method to properly create a null QVariant from a metaType Returns the created QVariant.
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)
Tests whether a WKB type contains the z-dimension.
static bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
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.
std::unique_ptr< std::remove_pointer< OGRFeatureH >::type, OGRFeatureDeleter > ogr_feature_unique_ptr
Scoped OGR feature.
std::unique_ptr< std::remove_pointer< GDALDatasetH >::type, GDALDatasetCloser > dataset_unique_ptr
Scoped GDAL dataset.
std::unique_ptr< std::remove_pointer< OGRDataSourceH >::type, OGRDataSourceDeleter > ogr_datasource_unique_ptr
Scoped OGR data source.
std::unique_ptr< std::remove_pointer< OGRFieldDefnH >::type, OGRFldDeleter > ogr_field_def_unique_ptr
Scoped OGR field definition.
std::unique_ptr< std::remove_pointer< GDALRelationshipH >::type, GDALRelationshipDeleter > relationship_unique_ptr
Scoped GDAL relationship.
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:6405
void * GDALDatasetH
void * OGRSpatialReferenceH
QList< QgsFeature > QgsFeatureList
#define DEFAULT_SIMPLELINE_WIDTH
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
#define QgsDebugError(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:30
_LayerRef< QgsVectorLayer > QgsVectorLayerRef
const QgsCoordinateReferenceSystem & crs
void CORE_EXPORT operator()(GDALDatasetH datasource) const
Destroys an gdal dataset, using the correct gdal calls.
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.
void CORE_EXPORT operator()(OGRFeatureH feature) const
Destroys an OGR feature, using the correct gdal calls.
void CORE_EXPORT operator()(OGRFieldDefnH definition) const
Destroys an OGR field definition, using the correct gdal calls.
void CORE_EXPORT operator()(OGRGeometryH geometry) const
Destroys an OGR geometry, using the correct gdal calls.