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