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