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