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