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