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