QGIS API Documentation 3.28.14-Firenze (exported)
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 "qgsogrprovider.h"
25#include "qgslinesymbollayer.h"
26#include "qgspolygon.h"
27#include "qgsmultipolygon.h"
29#include "qgsfillsymbollayer.h"
31#include "qgssymbollayerutils.h"
32#include "qgsfontutils.h"
33#include "qgsmessagelog.h"
34#include "qgssymbol.h"
35#include "qgsfillsymbol.h"
36#include "qgslinesymbol.h"
37#include "qgsmarkersymbol.h"
38#include "qgsfielddomain.h"
39#include "qgsfontmanager.h"
40#include "qgsvariantutils.h"
41
42#include <cmath>
43#include <limits>
44#include <QTextCodec>
45#include <QUuid>
46#include <cpl_error.h>
47#include <QJsonDocument>
48#include <QFileInfo>
49#include <QDir>
50#include <QTextStream>
51#include <QDataStream>
52#include <QRegularExpression>
53
54#include "ogr_srs_api.h"
55
56
57void gdal::OGRDataSourceDeleter::operator()( OGRDataSourceH source ) const
58{
59 OGR_DS_Destroy( source );
60}
61
62
63void gdal::OGRGeometryDeleter::operator()( OGRGeometryH geometry ) const
64{
65 OGR_G_DestroyGeometry( geometry );
66}
67
68void gdal::OGRFldDeleter::operator()( OGRFieldDefnH definition ) const
69{
70 OGR_Fld_Destroy( definition );
71}
72
73void gdal::OGRFeatureDeleter::operator()( OGRFeatureH feature ) const
74{
75 OGR_F_Destroy( feature );
76}
77
79{
80 GDALClose( dataset );
81}
82
83void gdal::fast_delete_and_close( gdal::dataset_unique_ptr &dataset, GDALDriverH driver, const QString &path )
84{
85 // see https://github.com/qgis/QGIS/commit/d024910490a39e65e671f2055c5b6543e06c7042#commitcomment-25194282
86 // faster if we close the handle AFTER delete, but doesn't work for windows
87#ifdef Q_OS_WIN
88 // close dataset handle
89 dataset.reset();
90#endif
91
92 CPLPushErrorHandler( CPLQuietErrorHandler );
93 GDALDeleteDataset( driver, path.toUtf8().constData() );
94 CPLPopErrorHandler();
95
96#ifndef Q_OS_WIN
97 // close dataset handle
98 dataset.reset();
99#endif
100}
101
102
103void gdal::GDALWarpOptionsDeleter::operator()( GDALWarpOptions *options ) const
104{
105 GDALDestroyWarpOptions( options );
106}
107
108static void setQTTimeZoneFromOGRTZFlag( QDateTime &dt, int nTZFlag )
109{
110 // Take into account time zone
111 if ( nTZFlag == 0 )
112 {
113 // unknown time zone
114 }
115 else if ( nTZFlag == 1 )
116 {
117 dt.setTimeSpec( Qt::LocalTime );
118 }
119 else if ( nTZFlag == 100 )
120 {
121 dt.setTimeSpec( Qt::UTC );
122 }
123 else
124 {
125 // TZFlag = 101 ==> UTC+00:15
126 // TZFlag = 99 ==> UTC-00:15
127 dt.setOffsetFromUtc( ( nTZFlag - 100 ) * 15 * 60 );
128 }
129}
130
131QVariant QgsOgrUtils::OGRFieldtoVariant( const OGRField *value, OGRFieldType type )
132{
133 if ( !value || OGR_RawField_IsUnset( value ) || OGR_RawField_IsNull( value ) )
134 return QVariant();
135
136 switch ( type )
137 {
138 case OFTInteger:
139 return value->Integer;
140
141 case OFTInteger64:
142 return value->Integer64;
143
144 case OFTReal:
145 return value->Real;
146
147 case OFTString:
148 case OFTWideString:
149 return QString::fromUtf8( value->String );
150
151 case OFTDate:
152 return QDate( value->Date.Year, value->Date.Month, value->Date.Day );
153
154 case OFTTime:
155 {
156 float secondsPart = 0;
157 float millisecondPart = std::modf( value->Date.Second, &secondsPart );
158 return QTime( value->Date.Hour, value->Date.Minute, static_cast< int >( secondsPart ), static_cast< int >( std::round( 1000 * millisecondPart ) ) );
159 }
160
161 case OFTDateTime:
162 {
163 float secondsPart = 0;
164 float millisecondPart = std::modf( value->Date.Second, &secondsPart );
165 QDateTime dt = QDateTime( QDate( value->Date.Year, value->Date.Month, value->Date.Day ),
166 QTime( value->Date.Hour, value->Date.Minute, static_cast< int >( secondsPart ), static_cast< int >( std::round( 1000 * millisecondPart ) ) ) );
167 setQTTimeZoneFromOGRTZFlag( dt, value->Date.TZFlag );
168 return dt;
169 }
170
171 case OFTBinary:
172 // not supported!
173 Q_ASSERT_X( false, "QgsOgrUtils::OGRFieldtoVariant", "OFTBinary type not supported" );
174 return QVariant();
175
176 case OFTIntegerList:
177 {
178 QVariantList res;
179 res.reserve( value->IntegerList.nCount );
180 for ( int i = 0; i < value->IntegerList.nCount; ++i )
181 res << value->IntegerList.paList[ i ];
182 return res;
183 }
184
185 case OFTInteger64List:
186 {
187 QVariantList res;
188 res.reserve( value->Integer64List.nCount );
189 for ( int i = 0; i < value->Integer64List.nCount; ++i )
190 res << value->Integer64List.paList[ i ];
191 return res;
192 }
193
194 case OFTRealList:
195 {
196 QVariantList res;
197 res.reserve( value->RealList.nCount );
198 for ( int i = 0; i < value->RealList.nCount; ++i )
199 res << value->RealList.paList[ i ];
200 return res;
201 }
202
203 case OFTStringList:
204 case OFTWideStringList:
205 {
206 QVariantList res;
207 res.reserve( value->StringList.nCount );
208 for ( int i = 0; i < value->StringList.nCount; ++i )
209 res << QString::fromUtf8( value->StringList.paList[ i ] );
210 return res;
211 }
212 }
213 return QVariant();
214}
215
216int QgsOgrUtils::OGRTZFlagFromQt( const QDateTime &datetime )
217{
218 if ( datetime.timeSpec() == Qt::LocalTime )
219 return 1;
220 return 100 + datetime.offsetFromUtc() / ( 60 * 15 );
221}
222
223std::unique_ptr< OGRField > QgsOgrUtils::variantToOGRField( const QVariant &value, OGRFieldType type )
224{
225 std::unique_ptr< OGRField > res = std::make_unique< OGRField >();
226
227 switch ( value.type() )
228 {
229 case QVariant::Invalid:
230 OGR_RawField_SetUnset( res.get() );
231 break;
232 case QVariant::Bool:
233 {
234 const int val = value.toBool() ? 1 : 0;
235 if ( type == OFTInteger )
236 res->Integer = val;
237 else if ( type == OFTInteger64 )
238 res->Integer64 = val;
239 else if ( type == OFTReal )
240 res->Real = val;
241 else
242 {
243 QgsDebugMsg( "Unsupported output data type for Bool" );
244 return nullptr;
245 }
246 break;
247 }
248 case QVariant::Int:
249 {
250 const int val = value.toInt();
251 if ( type == OFTInteger )
252 res->Integer = val;
253 else if ( type == OFTInteger64 )
254 res->Integer64 = val;
255 else if ( type == OFTReal )
256 res->Real = val;
257 else
258 {
259 QgsDebugMsg( "Unsupported output data type for Int" );
260 return nullptr;
261 }
262 break;
263 }
264 case QVariant::LongLong:
265 {
266 const qint64 val = value.toLongLong();
267 if ( type == OFTInteger )
268 {
269 if ( val <= std::numeric_limits<int>::max() &&
270 val >= std::numeric_limits<int>::min() )
271 {
272 res->Integer = static_cast<int>( val );
273 }
274 else
275 {
276 QgsDebugMsg( "Value does not fit on Integer" );
277 return nullptr;
278 }
279 }
280 else if ( type == OFTInteger64 )
281 res->Integer64 = val;
282 else if ( type == OFTReal )
283 {
284 res->Real = static_cast<double>( val );
285 }
286 else
287 {
288 QgsDebugMsg( "Unsupported output data type for LongLong" );
289 return nullptr;
290 }
291 break;
292 }
293 case QVariant::Double:
294 {
295 double val = value.toDouble();
296 if ( type == OFTInteger )
297 {
298 if ( val <= std::numeric_limits<int>::max() &&
299 val >= std::numeric_limits<int>::min() )
300 {
301 res->Integer = static_cast<int>( val );
302 }
303 else
304 {
305 QgsDebugMsg( "Value does not fit on Integer" );
306 return nullptr;
307 }
308 }
309 else if ( type == OFTInteger64 )
310 {
311 if ( val <= static_cast<double>( std::numeric_limits<qint64>::max() ) &&
312 val >= static_cast<double>( std::numeric_limits<qint64>::min() ) )
313 {
314 res->Integer64 = static_cast<qint64>( val );
315 }
316 else
317 {
318 QgsDebugMsg( "Value does not fit on Integer64" );
319 return nullptr;
320 }
321 }
322 else if ( type == OFTReal )
323 {
324 res->Real = val;
325 }
326 else
327 {
328 QgsDebugMsg( "Unsupported output data type for LongLong" );
329 return nullptr;
330 }
331 break;
332 }
333 case QVariant::Char:
334 case QVariant::String:
335 {
336 if ( type == OFTString )
337 res->String = CPLStrdup( value.toString().toUtf8().constData() );
338 else
339 {
340 QgsDebugMsg( "Unsupported output data type for String" );
341 return nullptr;
342 }
343 break;
344 }
345 case QVariant::Date:
346 {
347 if ( type == OFTDate )
348 {
349 const QDate date = value.toDate();
350 res->Date.Day = date.day();
351 res->Date.Month = date.month();
352 res->Date.Year = static_cast<GInt16>( date.year() );
353 res->Date.TZFlag = 0;
354 }
355 else
356 {
357 QgsDebugMsg( "Unsupported output data type for Date" );
358 return nullptr;
359 }
360 break;
361 }
362 case QVariant::Time:
363 {
364 if ( type == OFTTime )
365 {
366 const QTime time = value.toTime();
367 res->Date.Hour = time.hour();
368 res->Date.Minute = time.minute();
369 res->Date.Second = static_cast<float>( time.second() + static_cast< double >( time.msec() ) / 1000 );
370 res->Date.TZFlag = 0;
371 }
372 else
373 {
374 QgsDebugMsg( "Unsupported output data type for Time" );
375 return nullptr;
376 }
377 break;
378 }
379 case QVariant::DateTime:
380 {
381 if ( type == OFTDateTime )
382 {
383 const QDateTime dt = value.toDateTime();
384 const QDate date = dt.date();
385 res->Date.Day = date.day();
386 res->Date.Month = date.month();
387 res->Date.Year = static_cast<GInt16>( date.year() );
388 const QTime time = dt.time();
389 res->Date.Hour = time.hour();
390 res->Date.Minute = time.minute();
391 res->Date.Second = static_cast<float>( time.second() + static_cast< double >( time.msec() ) / 1000 );
392 res->Date.TZFlag = OGRTZFlagFromQt( dt );
393 }
394 else
395 {
396 QgsDebugMsg( "Unsupported output data type for DateTime" );
397 return nullptr;
398 }
399 break;
400 }
401
402 default:
403 QgsDebugMsg( "Unhandled variant type in variantToOGRField" );
404 OGR_RawField_SetUnset( res.get() );
405 break;
406 }
407
408 return res;
409}
410
411QgsFeature QgsOgrUtils::readOgrFeature( OGRFeatureH ogrFet, const QgsFields &fields, QTextCodec *encoding )
412{
413 QgsFeature feature;
414 if ( !ogrFet )
415 {
416 feature.setValid( false );
417 return feature;
418 }
419
420 feature.setId( OGR_F_GetFID( ogrFet ) );
421 feature.setValid( true );
422
423 if ( !readOgrFeatureGeometry( ogrFet, feature ) )
424 {
425 feature.setValid( false );
426 }
427
428 if ( !readOgrFeatureAttributes( ogrFet, fields, feature, encoding ) )
429 {
430 feature.setValid( false );
431 }
432
433 return feature;
434}
435
436QgsFields QgsOgrUtils::readOgrFields( OGRFeatureH ogrFet, QTextCodec *encoding )
437{
438 QgsFields fields;
439
440 if ( !ogrFet )
441 return fields;
442
443 int fieldCount = OGR_F_GetFieldCount( ogrFet );
444 for ( int i = 0; i < fieldCount; ++i )
445 {
446 OGRFieldDefnH fldDef = OGR_F_GetFieldDefnRef( ogrFet, i );
447 if ( !fldDef )
448 {
449 fields.append( QgsField() );
450 continue;
451 }
452
453 QString name = encoding ? encoding->toUnicode( OGR_Fld_GetNameRef( fldDef ) ) : QString::fromUtf8( OGR_Fld_GetNameRef( fldDef ) );
454 QVariant::Type varType;
455 switch ( OGR_Fld_GetType( fldDef ) )
456 {
457 case OFTInteger:
458 if ( OGR_Fld_GetSubType( fldDef ) == OFSTBoolean )
459 varType = QVariant::Bool;
460 else
461 varType = QVariant::Int;
462 break;
463 case OFTInteger64:
464 varType = QVariant::LongLong;
465 break;
466 case OFTReal:
467 varType = QVariant::Double;
468 break;
469 case OFTDate:
470 varType = QVariant::Date;
471 break;
472 case OFTTime:
473 varType = QVariant::Time;
474 break;
475 case OFTDateTime:
476 varType = QVariant::DateTime;
477 break;
478 case OFTString:
479 if ( OGR_Fld_GetSubType( fldDef ) == OFSTJSON )
480 varType = QVariant::Map;
481 else
482 varType = QVariant::String;
483 break;
484 default:
485 varType = QVariant::String; // other unsupported, leave it as a string
486 }
487 fields.append( QgsField( name, varType ) );
488 }
489 return fields;
490}
491
492
493QVariant QgsOgrUtils::getOgrFeatureAttribute( OGRFeatureH ogrFet, const QgsFields &fields, int attIndex, QTextCodec *encoding, bool *ok )
494{
495 if ( attIndex < 0 || attIndex >= fields.count() )
496 {
497 if ( ok )
498 *ok = false;
499 return QVariant();
500 }
501
502 const QgsField field = fields.at( attIndex );
503 return getOgrFeatureAttribute( ogrFet, field, attIndex, encoding, ok );
504}
505
506QVariant QgsOgrUtils::getOgrFeatureAttribute( OGRFeatureH ogrFet, const QgsField &field, int attIndex, QTextCodec *encoding, bool *ok )
507{
508 if ( !ogrFet || attIndex < 0 )
509 {
510 if ( ok )
511 *ok = false;
512 return QVariant();
513 }
514
515 OGRFieldDefnH fldDef = OGR_F_GetFieldDefnRef( ogrFet, attIndex );
516
517 if ( ! fldDef )
518 {
519 if ( ok )
520 *ok = false;
521
522 QgsDebugMsg( QStringLiteral( "ogrFet->GetFieldDefnRef(attindex) returns NULL" ) );
523 return QVariant();
524 }
525
526 QVariant value;
527
528 if ( ok )
529 *ok = true;
530
531 if ( OGR_F_IsFieldSetAndNotNull( ogrFet, attIndex ) )
532 {
533 switch ( field.type() )
534 {
535 case QVariant::String:
536 {
537 if ( encoding )
538 value = QVariant( encoding->toUnicode( OGR_F_GetFieldAsString( ogrFet, attIndex ) ) );
539 else
540 value = QVariant( QString::fromUtf8( OGR_F_GetFieldAsString( ogrFet, attIndex ) ) );
541
542#ifdef Q_OS_WIN
543 // Fixes GH #41076 (empty strings shown as NULL), because we have checked before that it was NOT NULL
544 // Note: QVariant( QString( ) ).isNull( ) is still true on windows so we really need string literal :(
545 if ( value.isNull() )
546 value = QVariant( QStringLiteral( "" ) ); // skip-keyword-check
547#endif
548
549 break;
550 }
551 case QVariant::Int:
552 value = QVariant( OGR_F_GetFieldAsInteger( ogrFet, attIndex ) );
553 break;
554 case QVariant::Bool:
555 value = QVariant( bool( OGR_F_GetFieldAsInteger( ogrFet, attIndex ) ) );
556 break;
557 case QVariant::LongLong:
558 value = QVariant( OGR_F_GetFieldAsInteger64( ogrFet, attIndex ) );
559 break;
560 case QVariant::Double:
561 value = QVariant( OGR_F_GetFieldAsDouble( ogrFet, attIndex ) );
562 break;
563 case QVariant::Date:
564 case QVariant::DateTime:
565 case QVariant::Time:
566 {
567 int year, month, day, hour, minute, tzf;
568 float second;
569 float secondsPart = 0;
570
571 OGR_F_GetFieldAsDateTimeEx( ogrFet, attIndex, &year, &month, &day, &hour, &minute, &second, &tzf );
572 float millisecondPart = std::modf( second, &secondsPart );
573
574 if ( field.type() == QVariant::Date )
575 value = QDate( year, month, day );
576 else if ( field.type() == QVariant::Time )
577 value = QTime( hour, minute, static_cast< int >( secondsPart ), static_cast< int >( std::round( 1000 * millisecondPart ) ) );
578 else
579 {
580 QDateTime dt = QDateTime( QDate( year, month, day ),
581 QTime( hour, minute, static_cast< int >( secondsPart ), static_cast< int >( std::round( 1000 * millisecondPart ) ) ) );
582 setQTTimeZoneFromOGRTZFlag( dt, tzf );
583 value = dt;
584 }
585 }
586 break;
587
588 case QVariant::ByteArray:
589 {
590 int size = 0;
591 const GByte *b = OGR_F_GetFieldAsBinary( ogrFet, attIndex, &size );
592
593 // QByteArray::fromRawData is funny. It doesn't take ownership of the data, so we have to explicitly call
594 // detach on it to force a copy which owns the data
595 QByteArray ba = QByteArray::fromRawData( reinterpret_cast<const char *>( b ), size );
596 ba.detach();
597
598 value = ba;
599 break;
600 }
601
602 case QVariant::StringList:
603 {
604 QStringList list;
605 char **lst = OGR_F_GetFieldAsStringList( ogrFet, attIndex );
606 const int count = CSLCount( lst );
607 if ( count > 0 )
608 {
609 list.reserve( count );
610 for ( int i = 0; i < count; i++ )
611 {
612 if ( encoding )
613 list << encoding->toUnicode( lst[i] );
614 else
615 list << QString::fromUtf8( lst[i] );
616 }
617 }
618 value = list;
619 break;
620 }
621
622 case QVariant::List:
623 {
624 switch ( field.subType() )
625 {
626 case QVariant::String:
627 {
628 QStringList list;
629 char **lst = OGR_F_GetFieldAsStringList( ogrFet, attIndex );
630 const int count = CSLCount( lst );
631 if ( count > 0 )
632 {
633 list.reserve( count );
634 for ( int i = 0; i < count; i++ )
635 {
636 if ( encoding )
637 list << encoding->toUnicode( lst[i] );
638 else
639 list << QString::fromUtf8( lst[i] );
640 }
641 }
642 value = list;
643 break;
644 }
645
646 case QVariant::Int:
647 {
648 QVariantList list;
649 int count = 0;
650 const int *lst = OGR_F_GetFieldAsIntegerList( ogrFet, attIndex, &count );
651 if ( count > 0 )
652 {
653 list.reserve( count );
654 for ( int i = 0; i < count; i++ )
655 {
656 list << lst[i];
657 }
658 }
659 value = list;
660 break;
661 }
662
663 case QVariant::Double:
664 {
665 QVariantList list;
666 int count = 0;
667 const double *lst = OGR_F_GetFieldAsDoubleList( ogrFet, attIndex, &count );
668 if ( count > 0 )
669 {
670 list.reserve( count );
671 for ( int i = 0; i < count; i++ )
672 {
673 list << lst[i];
674 }
675 }
676 value = list;
677 break;
678 }
679
680 case QVariant::LongLong:
681 {
682 QVariantList list;
683 int count = 0;
684 const long long *lst = OGR_F_GetFieldAsInteger64List( ogrFet, attIndex, &count );
685 if ( count > 0 )
686 {
687 list.reserve( count );
688 for ( int i = 0; i < count; i++ )
689 {
690 list << lst[i];
691 }
692 }
693 value = list;
694 break;
695 }
696
697 default:
698 {
699 Q_ASSERT_X( false, "QgsOgrUtils::getOgrFeatureAttribute", "unsupported field type" );
700 if ( ok )
701 *ok = false;
702 break;
703 }
704 }
705 break;
706 }
707
708 case QVariant::Map:
709 {
710 //it has to be JSON
711 //it's null if no json format
712 if ( encoding )
713 value = QJsonDocument::fromJson( encoding->toUnicode( OGR_F_GetFieldAsString( ogrFet, attIndex ) ).toUtf8() ).toVariant();
714 else
715 value = QJsonDocument::fromJson( QString::fromUtf8( OGR_F_GetFieldAsString( ogrFet, attIndex ) ).toUtf8() ).toVariant();
716 break;
717 }
718 default:
719 Q_ASSERT_X( false, "QgsOgrUtils::getOgrFeatureAttribute", "unsupported field type" );
720 if ( ok )
721 *ok = false;
722 }
723 }
724 else
725 {
726 value = QVariant( field.type() );
727 }
728
729 return value;
730}
731
732bool QgsOgrUtils::readOgrFeatureAttributes( OGRFeatureH ogrFet, const QgsFields &fields, QgsFeature &feature, QTextCodec *encoding )
733{
734 // read all attributes
735 feature.initAttributes( fields.count() );
736 feature.setFields( fields );
737
738 if ( !ogrFet )
739 return false;
740
741 bool ok = false;
742 for ( int idx = 0; idx < fields.count(); ++idx )
743 {
744 QVariant value = getOgrFeatureAttribute( ogrFet, fields, idx, encoding, &ok );
745 if ( ok )
746 {
747 feature.setAttribute( idx, value );
748 }
749 }
750 return true;
751}
752
753bool QgsOgrUtils::readOgrFeatureGeometry( OGRFeatureH ogrFet, QgsFeature &feature )
754{
755 if ( !ogrFet )
756 return false;
757
758 OGRGeometryH geom = OGR_F_GetGeometryRef( ogrFet );
759 if ( !geom )
760 feature.clearGeometry();
761 else
762 feature.setGeometry( ogrGeometryToQgsGeometry( geom ) );
763
764 return true;
765}
766
767std::unique_ptr< QgsPoint > ogrGeometryToQgsPoint( OGRGeometryH geom )
768{
769 QgsWkbTypes::Type wkbType = QgsOgrUtils::ogrGeometryTypeToQgsWkbType( OGR_G_GetGeometryType( geom ) );
770
771 double x, y, z, m;
772 OGR_G_GetPointZM( geom, 0, &x, &y, &z, &m );
773 return std::make_unique< QgsPoint >( wkbType, x, y, z, m );
774}
775
776std::unique_ptr< QgsMultiPoint > ogrGeometryToQgsMultiPoint( OGRGeometryH geom )
777{
778 std::unique_ptr< QgsMultiPoint > mp = std::make_unique< QgsMultiPoint >();
779
780 const int count = OGR_G_GetGeometryCount( geom );
781 mp->reserve( count );
782 for ( int i = 0; i < count; ++i )
783 {
784 mp->addGeometry( ogrGeometryToQgsPoint( OGR_G_GetGeometryRef( geom, i ) ).release() );
785 }
786
787 return mp;
788}
789
790std::unique_ptr< QgsLineString > ogrGeometryToQgsLineString( OGRGeometryH geom )
791{
792 QgsWkbTypes::Type wkbType = QgsOgrUtils::ogrGeometryTypeToQgsWkbType( OGR_G_GetGeometryType( geom ) );
793
794 int count = OGR_G_GetPointCount( geom );
795 QVector< double > x( count );
796 QVector< double > y( count );
797 QVector< double > z;
798 double *pz = nullptr;
799 if ( QgsWkbTypes::hasZ( wkbType ) )
800 {
801 z.resize( count );
802 pz = z.data();
803 }
804 double *pm = nullptr;
805 QVector< double > m;
806 if ( QgsWkbTypes::hasM( wkbType ) )
807 {
808 m.resize( count );
809 pm = m.data();
810 }
811 OGR_G_GetPointsZM( geom, x.data(), sizeof( double ), y.data(), sizeof( double ), pz, sizeof( double ), pm, sizeof( double ) );
812
813 return std::make_unique< QgsLineString>( x, y, z, m, wkbType == QgsWkbTypes::LineString25D );
814}
815
816std::unique_ptr< QgsMultiLineString > ogrGeometryToQgsMultiLineString( OGRGeometryH geom )
817{
818 std::unique_ptr< QgsMultiLineString > mp = std::make_unique< QgsMultiLineString >();
819
820 const int count = OGR_G_GetGeometryCount( geom );
821 mp->reserve( count );
822 for ( int i = 0; i < count; ++i )
823 {
824 mp->addGeometry( ogrGeometryToQgsLineString( OGR_G_GetGeometryRef( geom, i ) ).release() );
825 }
826
827 return mp;
828}
829
830std::unique_ptr< QgsPolygon > ogrGeometryToQgsPolygon( OGRGeometryH geom )
831{
832 std::unique_ptr< QgsPolygon > polygon = std::make_unique< QgsPolygon >();
833
834 const int count = OGR_G_GetGeometryCount( geom );
835 if ( count >= 1 )
836 {
837 polygon->setExteriorRing( ogrGeometryToQgsLineString( OGR_G_GetGeometryRef( geom, 0 ) ).release() );
838 }
839
840 for ( int i = 1; i < count; ++i )
841 {
842 polygon->addInteriorRing( ogrGeometryToQgsLineString( OGR_G_GetGeometryRef( geom, i ) ).release() );
843 }
844
845 return polygon;
846}
847
848std::unique_ptr< QgsMultiPolygon > ogrGeometryToQgsMultiPolygon( OGRGeometryH geom )
849{
850 std::unique_ptr< QgsMultiPolygon > polygon = std::make_unique< QgsMultiPolygon >();
851
852 const int count = OGR_G_GetGeometryCount( geom );
853 polygon->reserve( count );
854 for ( int i = 0; i < count; ++i )
855 {
856 polygon->addGeometry( ogrGeometryToQgsPolygon( OGR_G_GetGeometryRef( geom, i ) ).release() );
857 }
858
859 return polygon;
860}
861
863{
864 switch ( ogrGeomType )
865 {
866 case wkbUnknown: return QgsWkbTypes::Type::Unknown;
867 case wkbPoint: return QgsWkbTypes::Type::Point;
868 case wkbLineString: return QgsWkbTypes::Type::LineString;
869 case wkbPolygon: return QgsWkbTypes::Type::Polygon;
870 case wkbMultiPoint: return QgsWkbTypes::Type::MultiPoint;
871 case wkbMultiLineString: return QgsWkbTypes::Type::MultiLineString;
872 case wkbMultiPolygon: return QgsWkbTypes::Type::MultiPolygon;
873 case wkbGeometryCollection: return QgsWkbTypes::Type::GeometryCollection;
874 case wkbCircularString: return QgsWkbTypes::Type::CircularString;
875 case wkbCompoundCurve: return QgsWkbTypes::Type::CompoundCurve;
876 case wkbCurvePolygon: return QgsWkbTypes::Type::CurvePolygon;
877 case wkbMultiCurve: return QgsWkbTypes::Type::MultiCurve;
878 case wkbMultiSurface: return QgsWkbTypes::Type::MultiSurface;
879 case wkbCurve: return QgsWkbTypes::Type::Unknown; // not an actual concrete type
880 case wkbSurface: return QgsWkbTypes::Type::Unknown; // not an actual concrete type
881 case wkbPolyhedralSurface: return QgsWkbTypes::Type::Unknown; // no actual matching
882 case wkbTIN: return QgsWkbTypes::Type::Unknown; // no actual matching
883 case wkbTriangle: return QgsWkbTypes::Type::Triangle;
884
885 case wkbNone: return QgsWkbTypes::Type::NoGeometry;
886 case wkbLinearRing: return QgsWkbTypes::Type::LineString; // approximate match
887
888 case wkbCircularStringZ: return QgsWkbTypes::Type::CircularStringZ;
889 case wkbCompoundCurveZ: return QgsWkbTypes::Type::CompoundCurveZ;
890 case wkbCurvePolygonZ: return QgsWkbTypes::Type::CurvePolygonZ;
891 case wkbMultiCurveZ: return QgsWkbTypes::Type::MultiCurveZ;
892 case wkbMultiSurfaceZ: return QgsWkbTypes::Type::MultiSurfaceZ;
893 case wkbCurveZ: return QgsWkbTypes::Type::Unknown; // not an actual concrete type
894 case wkbSurfaceZ: return QgsWkbTypes::Type::Unknown; // not an actual concrete type
895 case wkbPolyhedralSurfaceZ: return QgsWkbTypes::Type::Unknown; // no actual matching
896 case wkbTINZ: return QgsWkbTypes::Type::Unknown; // no actual matching
897 case wkbTriangleZ: return QgsWkbTypes::Type::TriangleZ;
898
899 case wkbPointM: return QgsWkbTypes::Type::PointM;
900 case wkbLineStringM: return QgsWkbTypes::Type::LineStringM;
901 case wkbPolygonM: return QgsWkbTypes::Type::PolygonM;
902 case wkbMultiPointM: return QgsWkbTypes::Type::MultiPointM;
903 case wkbMultiLineStringM: return QgsWkbTypes::Type::MultiLineStringM;
904 case wkbMultiPolygonM: return QgsWkbTypes::Type::MultiPolygonM;
905 case wkbGeometryCollectionM: return QgsWkbTypes::Type::GeometryCollectionM;
906 case wkbCircularStringM: return QgsWkbTypes::Type::CircularStringM;
907 case wkbCompoundCurveM: return QgsWkbTypes::Type::CompoundCurveM;
908 case wkbCurvePolygonM: return QgsWkbTypes::Type::CurvePolygonM;
909 case wkbMultiCurveM: return QgsWkbTypes::Type::MultiCurveM;
910 case wkbMultiSurfaceM: return QgsWkbTypes::Type::MultiSurfaceM;
911 case wkbCurveM: return QgsWkbTypes::Type::Unknown; // not an actual concrete type
912 case wkbSurfaceM: return QgsWkbTypes::Type::Unknown; // not an actual concrete type
913 case wkbPolyhedralSurfaceM: return QgsWkbTypes::Type::Unknown; // no actual matching
914 case wkbTINM: return QgsWkbTypes::Type::Unknown; // no actual matching
915 case wkbTriangleM: return QgsWkbTypes::Type::TriangleM;
916
917 case wkbPointZM: return QgsWkbTypes::Type::PointZM;
918 case wkbLineStringZM: return QgsWkbTypes::Type::LineStringZM;
919 case wkbPolygonZM: return QgsWkbTypes::Type::PolygonZM;
920 case wkbMultiPointZM: return QgsWkbTypes::Type::MultiPointZM;
921 case wkbMultiLineStringZM: return QgsWkbTypes::Type::MultiLineStringZM;
922 case wkbMultiPolygonZM: return QgsWkbTypes::Type::MultiPolygonZM;
923 case wkbGeometryCollectionZM: return QgsWkbTypes::Type::GeometryCollectionZM;
924 case wkbCircularStringZM: return QgsWkbTypes::Type::CircularStringZM;
925 case wkbCompoundCurveZM: return QgsWkbTypes::Type::CompoundCurveZM;
926 case wkbCurvePolygonZM: return QgsWkbTypes::Type::CurvePolygonZM;
927 case wkbMultiCurveZM: return QgsWkbTypes::Type::MultiCurveZM;
928 case wkbMultiSurfaceZM: return QgsWkbTypes::Type::MultiSurfaceZM;
929 case wkbCurveZM: return QgsWkbTypes::Type::Unknown; // not an actual concrete type
930 case wkbSurfaceZM: return QgsWkbTypes::Type::Unknown; // not an actual concrete type
931 case wkbPolyhedralSurfaceZM: return QgsWkbTypes::Type::Unknown; // no actual matching
932 case wkbTINZM: return QgsWkbTypes::Type::Unknown; // no actual matching
933 case wkbTriangleZM: return QgsWkbTypes::Type::TriangleZM;
934
935 case wkbPoint25D: return QgsWkbTypes::Type::PointZ;
936 case wkbLineString25D: return QgsWkbTypes::Type::LineStringZ;
937 case wkbPolygon25D: return QgsWkbTypes::Type::PolygonZ;
938 case wkbMultiPoint25D: return QgsWkbTypes::Type::MultiPointZ;
939 case wkbMultiLineString25D: return QgsWkbTypes::Type::MultiLineStringZ;
940 case wkbMultiPolygon25D: return QgsWkbTypes::Type::MultiPolygonZ;
941 case wkbGeometryCollection25D: return QgsWkbTypes::Type::GeometryCollectionZ;
942 }
943
944 // should not reach that point normally
946}
947
949{
950 if ( !geom )
951 return QgsGeometry();
952
953 const auto ogrGeomType = OGR_G_GetGeometryType( geom );
954 QgsWkbTypes::Type wkbType = ogrGeometryTypeToQgsWkbType( ogrGeomType );
955
956 // optimised case for some geometry classes, avoiding wkb conversion on OGR/QGIS sides
957 // TODO - extend to other classes!
958 switch ( QgsWkbTypes::flatType( wkbType ) )
959 {
961 {
962 return QgsGeometry( ogrGeometryToQgsPoint( geom ) );
963 }
964
966 {
967 return QgsGeometry( ogrGeometryToQgsMultiPoint( geom ) );
968 }
969
971 {
972 return QgsGeometry( ogrGeometryToQgsLineString( geom ) );
973 }
974
976 {
978 }
979
981 {
982 return QgsGeometry( ogrGeometryToQgsPolygon( geom ) );
983 }
984
986 {
988 }
989
990 default:
991 break;
992 }
993
994 // Fallback to inefficient WKB conversions
995
996 if ( wkbFlatten( wkbType ) == wkbGeometryCollection )
997 {
998 // Shapefile MultiPatch can be reported as GeometryCollectionZ of TINZ
999 if ( OGR_G_GetGeometryCount( geom ) >= 1 &&
1000 wkbFlatten( OGR_G_GetGeometryType( OGR_G_GetGeometryRef( geom, 0 ) ) ) == wkbTIN )
1001 {
1002 auto newGeom = OGR_G_ForceToMultiPolygon( OGR_G_Clone( geom ) );
1003 auto ret = ogrGeometryToQgsGeometry( newGeom );
1004 OGR_G_DestroyGeometry( newGeom );
1005 return ret;
1006 }
1007 }
1008
1009 // get the wkb representation
1010 int memorySize = OGR_G_WkbSize( geom );
1011 unsigned char *wkb = new unsigned char[memorySize];
1012 OGR_G_ExportToWkb( geom, static_cast<OGRwkbByteOrder>( QgsApplication::endian() ), wkb );
1013
1014 // Read original geometry type
1015 uint32_t origGeomType;
1016 memcpy( &origGeomType, wkb + 1, sizeof( uint32_t ) );
1017 bool hasZ = ( origGeomType >= 1000 && origGeomType < 2000 ) || ( origGeomType >= 3000 && origGeomType < 4000 );
1018 bool hasM = ( origGeomType >= 2000 && origGeomType < 3000 ) || ( origGeomType >= 3000 && origGeomType < 4000 );
1019
1020 // PolyhedralSurface and TINs are not supported, map them to multipolygons...
1021 if ( origGeomType % 1000 == 16 ) // is TIN, TINZ, TINM or TINZM
1022 {
1023 // TIN has the same wkb layout as a multipolygon, just need to overwrite the geom types...
1024 int nDims = 2 + hasZ + hasM;
1025 uint32_t newMultiType = static_cast<uint32_t>( QgsWkbTypes::zmType( QgsWkbTypes::MultiPolygon, hasZ, hasM ) );
1026 uint32_t newSingleType = static_cast<uint32_t>( QgsWkbTypes::zmType( QgsWkbTypes::Polygon, hasZ, hasM ) );
1027 unsigned char *wkbptr = wkb;
1028
1029 // Endianness
1030 wkbptr += 1;
1031
1032 // Overwrite geom type
1033 memcpy( wkbptr, &newMultiType, sizeof( uint32_t ) );
1034 wkbptr += 4;
1035
1036 // Geom count
1037 uint32_t numGeoms;
1038 memcpy( &numGeoms, wkb + 5, sizeof( uint32_t ) );
1039 wkbptr += 4;
1040
1041 // For each part, overwrite the geometry type to polygon (Z|M)
1042 for ( uint32_t i = 0; i < numGeoms; ++i )
1043 {
1044 // Endianness
1045 wkbptr += 1;
1046
1047 // Overwrite geom type
1048 memcpy( wkbptr, &newSingleType, sizeof( uint32_t ) );
1049 wkbptr += sizeof( uint32_t );
1050
1051 // skip coordinates
1052 uint32_t nRings;
1053 memcpy( &nRings, wkbptr, sizeof( uint32_t ) );
1054 wkbptr += sizeof( uint32_t );
1055
1056 for ( uint32_t j = 0; j < nRings; ++j )
1057 {
1058 uint32_t nPoints;
1059 memcpy( &nPoints, wkbptr, sizeof( uint32_t ) );
1060 wkbptr += sizeof( uint32_t ) + sizeof( double ) * nDims * nPoints;
1061 }
1062 }
1063 }
1064 else if ( origGeomType % 1000 == 15 ) // PolyhedralSurface, PolyhedralSurfaceZ, PolyhedralSurfaceM or PolyhedralSurfaceZM
1065 {
1066 // PolyhedralSurface has the same wkb layout as a MultiPolygon, just need to overwrite the geom type...
1067 uint32_t newType = static_cast<uint32_t>( QgsWkbTypes::zmType( QgsWkbTypes::MultiPolygon, hasZ, hasM ) );
1068 // Overwrite geom type
1069 memcpy( wkb + 1, &newType, sizeof( uint32_t ) );
1070 }
1071
1072 QgsGeometry g;
1073 g.fromWkb( wkb, memorySize );
1074 return g;
1075}
1076
1077QgsFeatureList QgsOgrUtils::stringToFeatureList( const QString &string, const QgsFields &fields, QTextCodec *encoding )
1078{
1079 QgsFeatureList features;
1080 if ( string.isEmpty() )
1081 return features;
1082
1083 QString randomFileName = QStringLiteral( "/vsimem/%1" ).arg( QUuid::createUuid().toString() );
1084
1085 // create memory file system object from string buffer
1086 QByteArray ba = string.toUtf8();
1087 VSIFCloseL( VSIFileFromMemBuffer( randomFileName.toUtf8().constData(), reinterpret_cast< GByte * >( ba.data() ),
1088 static_cast< vsi_l_offset >( ba.size() ), FALSE ) );
1089
1090 gdal::ogr_datasource_unique_ptr hDS( OGROpen( randomFileName.toUtf8().constData(), false, nullptr ) );
1091 if ( !hDS )
1092 {
1093 VSIUnlink( randomFileName.toUtf8().constData() );
1094 return features;
1095 }
1096
1097 OGRLayerH ogrLayer = OGR_DS_GetLayer( hDS.get(), 0 );
1098 if ( !ogrLayer )
1099 {
1100 hDS.reset();
1101 VSIUnlink( randomFileName.toUtf8().constData() );
1102 return features;
1103 }
1104
1106 while ( oFeat.reset( OGR_L_GetNextFeature( ogrLayer ) ), oFeat )
1107 {
1108 QgsFeature feat = readOgrFeature( oFeat.get(), fields, encoding );
1109 if ( feat.isValid() )
1110 features << feat;
1111 }
1112
1113 hDS.reset();
1114 VSIUnlink( randomFileName.toUtf8().constData() );
1115
1116 return features;
1117}
1118
1119QgsFields QgsOgrUtils::stringToFields( const QString &string, QTextCodec *encoding )
1120{
1121 QgsFields fields;
1122 if ( string.isEmpty() )
1123 return fields;
1124
1125 QString randomFileName = QStringLiteral( "/vsimem/%1" ).arg( QUuid::createUuid().toString() );
1126
1127 // create memory file system object from buffer
1128 QByteArray ba = string.toUtf8();
1129 VSIFCloseL( VSIFileFromMemBuffer( randomFileName.toUtf8().constData(), reinterpret_cast< GByte * >( ba.data() ),
1130 static_cast< vsi_l_offset >( ba.size() ), FALSE ) );
1131
1132 gdal::ogr_datasource_unique_ptr hDS( OGROpen( randomFileName.toUtf8().constData(), false, nullptr ) );
1133 if ( !hDS )
1134 {
1135 VSIUnlink( randomFileName.toUtf8().constData() );
1136 return fields;
1137 }
1138
1139 OGRLayerH ogrLayer = OGR_DS_GetLayer( hDS.get(), 0 );
1140 if ( !ogrLayer )
1141 {
1142 hDS.reset();
1143 VSIUnlink( randomFileName.toUtf8().constData() );
1144 return fields;
1145 }
1146
1148 //read in the first feature only
1149 if ( oFeat.reset( OGR_L_GetNextFeature( ogrLayer ) ), oFeat )
1150 {
1151 fields = readOgrFields( oFeat.get(), encoding );
1152 }
1153
1154 hDS.reset();
1155 VSIUnlink( randomFileName.toUtf8().constData() );
1156 return fields;
1157}
1158
1159QStringList QgsOgrUtils::cStringListToQStringList( char **stringList )
1160{
1161 if ( !stringList )
1162 return {};
1163
1164 QStringList strings;
1165 // presume null terminated string list
1166 for ( qgssize i = 0; stringList[i]; ++i )
1167 {
1168 strings.append( QString::fromUtf8( stringList[i] ) );
1169 }
1170
1171 return strings;
1172}
1173
1175{
1176 if ( !srs )
1177 return QString();
1178
1179 char *pszWkt = nullptr;
1180 const QByteArray multiLineOption = QStringLiteral( "MULTILINE=NO" ).toLocal8Bit();
1181 const QByteArray formatOption = QStringLiteral( "FORMAT=WKT2" ).toLocal8Bit();
1182 const char *const options[] = {multiLineOption.constData(), formatOption.constData(), nullptr};
1183 OSRExportToWktEx( srs, &pszWkt, options );
1184
1185 const QString res( pszWkt );
1186 CPLFree( pszWkt );
1187 return res;
1188}
1189
1191{
1192 const QString wkt = OGRSpatialReferenceToWkt( srs );
1193 if ( wkt.isEmpty() )
1195
1196 const char *authorityName = OSRGetAuthorityName( srs, nullptr );
1197 const char *authorityCode = OSRGetAuthorityCode( srs, nullptr );
1199 if ( authorityName && authorityCode )
1200 {
1201 QString authId = QString( authorityName ) + ':' + QString( authorityCode );
1202 OGRSpatialReferenceH ogrSrsTmp = OSRNewSpatialReference( nullptr );
1203 // Check that the CRS build from authId and the input one are the "same".
1204 if ( OSRSetFromUserInput( ogrSrsTmp, authId.toUtf8().constData() ) != OGRERR_NONE &&
1205 OSRIsSame( srs, ogrSrsTmp ) )
1206 {
1208 res.createFromUserInput( authId );
1209 }
1210 OSRDestroySpatialReference( ogrSrsTmp );
1211 }
1212 if ( !res.isValid() )
1214
1215#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,4,0)
1216 const double coordinateEpoch = OSRGetCoordinateEpoch( srs );
1217 if ( coordinateEpoch > 0 )
1218 res.setCoordinateEpoch( coordinateEpoch );
1219#endif
1220 return res;
1221}
1222
1224{
1225 if ( crs.isValid() )
1226 {
1227 OGRSpatialReferenceH ogrSrs = nullptr;
1228
1229 // First try instantiating the CRS from its authId. This will give a
1230 // more complete representation of the CRS for GDAL. In particular it might
1231 // help a few drivers to get the datum code, that would be missing in WKT-2.
1232 // See https://github.com/OSGeo/gdal/pull/5218
1233 const QString authId = crs.authid();
1235 if ( !authId.isEmpty() )
1236 {
1237 ogrSrs = OSRNewSpatialReference( nullptr );
1238 if ( OSRSetFromUserInput( ogrSrs, authId.toUtf8().constData() ) == OGRERR_NONE )
1239 {
1240 // Check that the CRS build from WKT and authId are the "same".
1241 OGRSpatialReferenceH ogrSrsFromWkt = OSRNewSpatialReference( srsWkt.toUtf8().constData() );
1242 if ( ogrSrsFromWkt )
1243 {
1244 if ( !OSRIsSame( ogrSrs, ogrSrsFromWkt ) )
1245 {
1246 OSRDestroySpatialReference( ogrSrs );
1247 ogrSrs = ogrSrsFromWkt;
1248 }
1249 else
1250 {
1251 OSRDestroySpatialReference( ogrSrsFromWkt );
1252 }
1253 }
1254 }
1255 else
1256 {
1257 OSRDestroySpatialReference( ogrSrs );
1258 ogrSrs = nullptr;
1259 }
1260 }
1261 if ( !ogrSrs )
1262 {
1263 ogrSrs = OSRNewSpatialReference( srsWkt.toUtf8().constData() );
1264 }
1265 if ( ogrSrs )
1266 {
1267 OSRSetAxisMappingStrategy( ogrSrs, OAMS_TRADITIONAL_GIS_ORDER );
1268#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,4,0)
1269 if ( !std::isnan( crs.coordinateEpoch() ) )
1270 {
1271 OSRSetCoordinateEpoch( ogrSrs, crs.coordinateEpoch() );
1272 }
1273#endif
1274 return ogrSrs;
1275 }
1276 }
1277
1278 return nullptr;
1279}
1280
1281QString QgsOgrUtils::readShapefileEncoding( const QString &path )
1282{
1283 const QString cpgEncoding = readShapefileEncodingFromCpg( path );
1284 if ( !cpgEncoding.isEmpty() )
1285 return cpgEncoding;
1286
1287 return readShapefileEncodingFromLdid( path );
1288}
1289
1290QString QgsOgrUtils::readShapefileEncodingFromCpg( const QString &path )
1291{
1292 QString errCause;
1293 QgsOgrLayerUniquePtr layer = QgsOgrProviderUtils::getLayer( path, false, QStringList(), 0, errCause, false );
1294 return layer ? layer->GetMetadataItem( QStringLiteral( "ENCODING_FROM_CPG" ), QStringLiteral( "SHAPEFILE" ) ) : QString();
1295}
1296
1298{
1299 QString errCause;
1300 QgsOgrLayerUniquePtr layer = QgsOgrProviderUtils::getLayer( path, false, QStringList(), 0, errCause, false );
1301 return layer ? layer->GetMetadataItem( QStringLiteral( "ENCODING_FROM_LDID" ), QStringLiteral( "SHAPEFILE" ) ) : QString();
1302}
1303
1304QVariantMap QgsOgrUtils::parseStyleString( const QString &string )
1305{
1306 QVariantMap styles;
1307
1308 char **papszStyleString = CSLTokenizeString2( string.toUtf8().constData(), ";",
1309 CSLT_HONOURSTRINGS
1310 | CSLT_PRESERVEQUOTES
1311 | CSLT_PRESERVEESCAPES );
1312 for ( int i = 0; papszStyleString[i] != nullptr; ++i )
1313 {
1314 // style string format is:
1315 // <tool_name>([<tool_param>[,<tool_param>[,...]]])
1316
1317 // first extract tool name
1318 const thread_local QRegularExpression sToolPartRx( QStringLiteral( "^(.*?)\\((.*)\\)$" ) );
1319 const QString stylePart( papszStyleString[i] );
1320 const QRegularExpressionMatch match = sToolPartRx.match( stylePart );
1321 if ( !match.hasMatch() )
1322 continue;
1323
1324 const QString tool = match.captured( 1 );
1325 const QString params = match.captured( 2 );
1326
1327 char **papszTokens = CSLTokenizeString2( params.toUtf8().constData(), ",", CSLT_HONOURSTRINGS
1328 | CSLT_PRESERVEESCAPES );
1329
1330 QVariantMap toolParts;
1331 const thread_local QRegularExpression sToolParamRx( QStringLiteral( "^(.*?):(.*)$" ) );
1332 for ( int j = 0; papszTokens[j] != nullptr; ++j )
1333 {
1334 const QString toolPart( papszTokens[j] );
1335 const QRegularExpressionMatch toolMatch = sToolParamRx.match( toolPart );
1336 if ( !match.hasMatch() )
1337 continue;
1338
1339 // note we always convert the keys to lowercase, just to be safe...
1340 toolParts.insert( toolMatch.captured( 1 ).toLower(), toolMatch.captured( 2 ) );
1341 }
1342 CSLDestroy( papszTokens );
1343
1344 // note we always convert the keys to lowercase, just to be safe...
1345 styles.insert( tool.toLower(), toolParts );
1346 }
1347 CSLDestroy( papszStyleString );
1348 return styles;
1349}
1350
1351std::unique_ptr<QgsSymbol> QgsOgrUtils::symbolFromStyleString( const QString &string, Qgis::SymbolType type )
1352{
1353 const QVariantMap styles = parseStyleString( string );
1354
1355 auto convertSize = []( const QString & size, double & value, QgsUnitTypes::RenderUnit & unit )->bool
1356 {
1357 const thread_local QRegularExpression sUnitRx = QRegularExpression( QStringLiteral( "^([\\d\\.]+)(g|px|pt|mm|cm|in)$" ) );
1358 const QRegularExpressionMatch match = sUnitRx.match( size );
1359 if ( match.hasMatch() )
1360 {
1361 value = match.captured( 1 ).toDouble();
1362 const QString unitString = match.captured( 2 );
1363 if ( unitString.compare( QLatin1String( "px" ), Qt::CaseInsensitive ) == 0 )
1364 {
1365 // pixels are a poor unit choice for QGIS -- they render badly in hidpi layouts. Convert to points instead, using
1366 // a 96 dpi conversion
1367 static constexpr double PT_TO_INCHES_FACTOR = 1 / 72.0;
1368 static constexpr double PX_TO_PT_FACTOR = 1 / ( 96.0 * PT_TO_INCHES_FACTOR );
1370 value *= PX_TO_PT_FACTOR;
1371 return true;
1372 }
1373 else if ( unitString.compare( QLatin1String( "pt" ), Qt::CaseInsensitive ) == 0 )
1374 {
1376 return true;
1377 }
1378 else if ( unitString.compare( QLatin1String( "mm" ), Qt::CaseInsensitive ) == 0 )
1379 {
1381 return true;
1382 }
1383 else if ( unitString.compare( QLatin1String( "cm" ), Qt::CaseInsensitive ) == 0 )
1384 {
1385 value *= 10;
1387 return true;
1388 }
1389 else if ( unitString.compare( QLatin1String( "in" ), Qt::CaseInsensitive ) == 0 )
1390 {
1392 return true;
1393 }
1394 else if ( unitString.compare( QLatin1String( "g" ), Qt::CaseInsensitive ) == 0 )
1395 {
1397 return true;
1398 }
1399 QgsDebugMsg( QStringLiteral( "Unknown unit %1" ).arg( unitString ) );
1400 }
1401 else
1402 {
1403 QgsDebugMsg( QStringLiteral( "Could not parse style size %1" ).arg( size ) );
1404 }
1405 return false;
1406 };
1407
1408 auto convertColor = []( const QString & string ) -> QColor
1409 {
1410 if ( string.isEmpty() )
1411 return QColor();
1412
1413 const thread_local QRegularExpression sColorWithAlphaRx = QRegularExpression( QStringLiteral( "^#([0-9a-fA-F]{6})([0-9a-fA-F]{2})$" ) );
1414 const QRegularExpressionMatch match = sColorWithAlphaRx.match( string );
1415 if ( match.hasMatch() )
1416 {
1417 // need to convert #RRGGBBAA to #AARRGGBB for QColor
1418 return QColor( QStringLiteral( "#%1%2" ).arg( match.captured( 2 ), match.captured( 1 ) ) );
1419 }
1420 else
1421 {
1422 return QColor( string );
1423 }
1424 };
1425
1426 auto convertPen = [&convertColor, &convertSize, string]( const QVariantMap & lineStyle ) -> std::unique_ptr< QgsSymbol >
1427 {
1428 QColor color = convertColor( lineStyle.value( QStringLiteral( "c" ), QStringLiteral( "#000000" ) ).toString() );
1429
1430 double lineWidth = DEFAULT_SIMPLELINE_WIDTH;
1432 convertSize( lineStyle.value( QStringLiteral( "w" ) ).toString(), lineWidth, lineWidthUnit );
1433
1434 // if the pen is a mapinfo pen, use dedicated converter for more accurate results
1435 const thread_local QRegularExpression sMapInfoId = QRegularExpression( QStringLiteral( "mapinfo-pen-(\\d+)" ) );
1436 const QRegularExpressionMatch match = sMapInfoId.match( string );
1437 if ( match.hasMatch() )
1438 {
1439 const int penId = match.captured( 1 ).toInt();
1441 std::unique_ptr<QgsSymbol> res( QgsMapInfoSymbolConverter::convertLineSymbol( penId, context, color, lineWidth, lineWidthUnit ) );
1442 if ( res )
1443 return res;
1444 }
1445
1446 std::unique_ptr< QgsSimpleLineSymbolLayer > simpleLine = std::make_unique< QgsSimpleLineSymbolLayer >( color, lineWidth );
1447 simpleLine->setWidthUnit( lineWidthUnit );
1448
1449 // pattern
1450 const QString pattern = lineStyle.value( QStringLiteral( "p" ) ).toString();
1451 if ( !pattern.isEmpty() )
1452 {
1453 const thread_local QRegularExpression sPatternUnitRx = QRegularExpression( QStringLiteral( "^([\\d\\.\\s]+)(g|px|pt|mm|cm|in)$" ) );
1454 const QRegularExpressionMatch match = sPatternUnitRx.match( pattern );
1455 if ( match.hasMatch() )
1456 {
1457 const QStringList patternValues = match.captured( 1 ).split( ' ' );
1458 QVector< qreal > dashPattern;
1460 for ( const QString &val : patternValues )
1461 {
1462 double length;
1463 convertSize( val + match.captured( 2 ), length, patternUnits );
1464 dashPattern.push_back( length * lineWidth * 2 );
1465 }
1466
1467 simpleLine->setCustomDashVector( dashPattern );
1468 simpleLine->setCustomDashPatternUnit( patternUnits );
1469 simpleLine->setUseCustomDashPattern( true );
1470 }
1471 }
1472
1473 Qt::PenCapStyle capStyle = Qt::FlatCap;
1474 Qt::PenJoinStyle joinStyle = Qt::MiterJoin;
1475 // workaround https://github.com/OSGeo/gdal/pull/3509 in older GDAL versions
1476 const QString id = lineStyle.value( QStringLiteral( "id" ) ).toString();
1477 if ( id.contains( QLatin1String( "mapinfo-pen" ), Qt::CaseInsensitive ) )
1478 {
1479 // MapInfo renders all lines using a round pen cap and round pen join
1480 // which are not the default values for OGR pen cap/join styles. So we need to explicitly
1481 // override the OGR default values here on older GDAL versions
1482 capStyle = Qt::RoundCap;
1483 joinStyle = Qt::RoundJoin;
1484 }
1485
1486 // pen cap
1487 const QString penCap = lineStyle.value( QStringLiteral( "cap" ) ).toString();
1488 if ( penCap.compare( QLatin1String( "b" ), Qt::CaseInsensitive ) == 0 )
1489 {
1490 capStyle = Qt::FlatCap;
1491 }
1492 else if ( penCap.compare( QLatin1String( "r" ), Qt::CaseInsensitive ) == 0 )
1493 {
1494 capStyle = Qt::RoundCap;
1495 }
1496 else if ( penCap.compare( QLatin1String( "p" ), Qt::CaseInsensitive ) == 0 )
1497 {
1498 capStyle = Qt::SquareCap;
1499 }
1500 simpleLine->setPenCapStyle( capStyle );
1501
1502 // pen join
1503 const QString penJoin = lineStyle.value( QStringLiteral( "j" ) ).toString();
1504 if ( penJoin.compare( QLatin1String( "m" ), Qt::CaseInsensitive ) == 0 )
1505 {
1506 joinStyle = Qt::MiterJoin;
1507 }
1508 else if ( penJoin.compare( QLatin1String( "r" ), Qt::CaseInsensitive ) == 0 )
1509 {
1510 joinStyle = Qt::RoundJoin;
1511 }
1512 else if ( penJoin.compare( QLatin1String( "b" ), Qt::CaseInsensitive ) == 0 )
1513 {
1514 joinStyle = Qt::BevelJoin;
1515 }
1516 simpleLine->setPenJoinStyle( joinStyle );
1517
1518 const QString priority = lineStyle.value( QStringLiteral( "l" ) ).toString();
1519 if ( !priority.isEmpty() )
1520 {
1521 simpleLine->setRenderingPass( priority.toInt() );
1522 }
1523 return std::make_unique< QgsLineSymbol >( QgsSymbolLayerList() << simpleLine.release() );
1524 };
1525
1526 auto convertBrush = [&convertColor]( const QVariantMap & brushStyle ) -> std::unique_ptr< QgsSymbol >
1527 {
1528 const QColor foreColor = convertColor( brushStyle.value( QStringLiteral( "fc" ), QStringLiteral( "#000000" ) ).toString() );
1529 const QColor backColor = convertColor( brushStyle.value( QStringLiteral( "bc" ), QString() ).toString() );
1530
1531 const QString id = brushStyle.value( QStringLiteral( "id" ) ).toString();
1532
1533 // if the pen is a mapinfo brush, use dedicated converter for more accurate results
1534 const thread_local QRegularExpression sMapInfoId = QRegularExpression( QStringLiteral( "mapinfo-brush-(\\d+)" ) );
1535 const QRegularExpressionMatch match = sMapInfoId.match( id );
1536 if ( match.hasMatch() )
1537 {
1538 const int brushId = match.captured( 1 ).toInt();
1540 std::unique_ptr<QgsSymbol> res( QgsMapInfoSymbolConverter::convertFillSymbol( brushId, context, foreColor, backColor ) );
1541 if ( res )
1542 return res;
1543 }
1544
1545 const thread_local QRegularExpression sOgrId = QRegularExpression( QStringLiteral( "ogr-brush-(\\d+)" ) );
1546 const QRegularExpressionMatch ogrMatch = sOgrId.match( id );
1547
1548 Qt::BrushStyle style = Qt::SolidPattern;
1549 if ( ogrMatch.hasMatch() )
1550 {
1551 const int brushId = ogrMatch.captured( 1 ).toInt();
1552 switch ( brushId )
1553 {
1554 case 0:
1555 style = Qt::SolidPattern;
1556 break;
1557
1558 case 1:
1559 style = Qt::NoBrush;
1560 break;
1561
1562 case 2:
1563 style = Qt::HorPattern;
1564 break;
1565
1566 case 3:
1567 style = Qt::VerPattern;
1568 break;
1569
1570 case 4:
1571 style = Qt::FDiagPattern;
1572 break;
1573
1574 case 5:
1575 style = Qt::BDiagPattern;
1576 break;
1577
1578 case 6:
1579 style = Qt::CrossPattern;
1580 break;
1581
1582 case 7:
1583 style = Qt::DiagCrossPattern;
1584 break;
1585 }
1586 }
1587
1588 QgsSymbolLayerList layers;
1589 if ( backColor.isValid() && style != Qt::SolidPattern && style != Qt::NoBrush )
1590 {
1591 std::unique_ptr< QgsSimpleFillSymbolLayer > backgroundFill = std::make_unique< QgsSimpleFillSymbolLayer >( backColor );
1592 backgroundFill->setLocked( true );
1593 backgroundFill->setStrokeStyle( Qt::NoPen );
1594 layers << backgroundFill.release();
1595 }
1596
1597 std::unique_ptr< QgsSimpleFillSymbolLayer > foregroundFill = std::make_unique< QgsSimpleFillSymbolLayer >( foreColor );
1598 foregroundFill->setBrushStyle( style );
1599 foregroundFill->setStrokeStyle( Qt::NoPen );
1600
1601 const QString priority = brushStyle.value( QStringLiteral( "l" ) ).toString();
1602 if ( !priority.isEmpty() )
1603 {
1604 foregroundFill->setRenderingPass( priority.toInt() );
1605 }
1606 layers << foregroundFill.release();
1607 return std::make_unique< QgsFillSymbol >( layers );
1608 };
1609
1610 auto convertSymbol = [&convertColor, &convertSize, string]( const QVariantMap & symbolStyle ) -> std::unique_ptr< QgsSymbol >
1611 {
1612 const QColor color = convertColor( symbolStyle.value( QStringLiteral( "c" ), QStringLiteral( "#000000" ) ).toString() );
1613
1614 double symbolSize = DEFAULT_SIMPLEMARKER_SIZE;
1616 convertSize( symbolStyle.value( QStringLiteral( "s" ) ).toString(), symbolSize, symbolSizeUnit );
1617
1618 const double angle = symbolStyle.value( QStringLiteral( "a" ), QStringLiteral( "0" ) ).toDouble();
1619
1620 const QString id = symbolStyle.value( QStringLiteral( "id" ) ).toString();
1621
1622 // if the symbol is a mapinfo symbol, use dedicated converter for more accurate results
1623 const thread_local QRegularExpression sMapInfoId = QRegularExpression( QStringLiteral( "mapinfo-sym-(\\d+)" ) );
1624 const QRegularExpressionMatch match = sMapInfoId.match( id );
1625 if ( match.hasMatch() )
1626 {
1627 const int symbolId = match.captured( 1 ).toInt();
1629
1630 // ogr interpretations of mapinfo symbol sizes are too large -- scale these down
1631 symbolSize *= 0.61;
1632
1633 std::unique_ptr<QgsSymbol> res( QgsMapInfoSymbolConverter::convertMarkerSymbol( symbolId, context, color, symbolSize, symbolSizeUnit ) );
1634 if ( res )
1635 return res;
1636 }
1637
1638 std::unique_ptr< QgsMarkerSymbolLayer > markerLayer;
1639
1640 const thread_local QRegularExpression sFontId = QRegularExpression( QStringLiteral( "font-sym-(\\d+)" ) );
1641 const QRegularExpressionMatch fontMatch = sFontId.match( id );
1642 if ( fontMatch.hasMatch() )
1643 {
1644 const int symId = fontMatch.captured( 1 ).toInt();
1645 const QStringList families = symbolStyle.value( QStringLiteral( "f" ), QString() ).toString().split( ',' );
1646
1647 bool familyFound = false;
1648 QString fontFamily;
1649 QString matched;
1650 for ( const QString &family : std::as_const( families ) )
1651 {
1652 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( family );
1653
1654 if ( QgsFontUtils::fontFamilyMatchOnSystem( processedFamily ) ||
1655 QgsApplication::fontManager()->tryToDownloadFontFamily( processedFamily, matched ) )
1656 {
1657 familyFound = true;
1658 fontFamily = processedFamily;
1659 break;
1660 }
1661 }
1662
1663 if ( familyFound )
1664 {
1665 std::unique_ptr< QgsFontMarkerSymbolLayer > fontMarker = std::make_unique< QgsFontMarkerSymbolLayer >( fontFamily, QChar( symId ), symbolSize );
1666 fontMarker->setSizeUnit( symbolSizeUnit );
1667 fontMarker->setAngle( -angle );
1668
1669 fontMarker->setColor( color );
1670
1671 const QColor strokeColor = convertColor( symbolStyle.value( QStringLiteral( "o" ), QString() ).toString() );
1672 if ( strokeColor.isValid() )
1673 {
1674 fontMarker->setStrokeColor( strokeColor );
1675 fontMarker->setStrokeWidth( 1 );
1676 fontMarker->setStrokeWidthUnit( QgsUnitTypes::RenderPoints );
1677 }
1678 else
1679 {
1680 fontMarker->setStrokeWidth( 0 );
1681 }
1682
1683 markerLayer = std::move( fontMarker );
1684 }
1685 else if ( !families.empty() )
1686 {
1687 // couldn't even find a matching font in the backup list
1688 QgsMessageLog::logMessage( QObject::tr( "Font %1 not found on system" ).arg( families.at( 0 ) ) );
1689 }
1690 }
1691
1692 if ( !markerLayer )
1693 {
1694 const thread_local QRegularExpression sOgrId = QRegularExpression( QStringLiteral( "ogr-sym-(\\d+)" ) );
1695 const QRegularExpressionMatch ogrMatch = sOgrId.match( id );
1696
1697 Qgis::MarkerShape shape;
1698 bool isFilled = true;
1699 if ( ogrMatch.hasMatch() )
1700 {
1701 const int symId = ogrMatch.captured( 1 ).toInt();
1702 switch ( symId )
1703 {
1704 case 0:
1706 break;
1707
1708 case 1:
1710 break;
1711
1712 case 2:
1713 isFilled = false;
1715 break;
1716
1717 case 3:
1719 break;
1720
1721 case 4:
1722 isFilled = false;
1724 break;
1725
1726 case 5:
1728 break;
1729
1730 case 6:
1731 isFilled = false;
1733 break;
1734
1735 case 7:
1737 break;
1738
1739 case 8:
1740 isFilled = false;
1742 break;
1743
1744 case 9:
1746 break;
1747
1748 case 10:
1750 break;
1751
1752 default:
1753 isFilled = false;
1754 shape = Qgis::MarkerShape::Square; // to initialize the variable
1755 break;
1756 }
1757 }
1758 else
1759 {
1760 isFilled = false;
1761 shape = Qgis::MarkerShape::Square; // to initialize the variable
1762 }
1763
1764 std::unique_ptr< QgsSimpleMarkerSymbolLayer > simpleMarker = std::make_unique< QgsSimpleMarkerSymbolLayer >( shape, symbolSize, -angle );
1765 simpleMarker->setSizeUnit( symbolSizeUnit );
1766
1767 if ( isFilled && QgsSimpleMarkerSymbolLayer::shapeIsFilled( shape ) )
1768 {
1769 simpleMarker->setColor( color );
1770 simpleMarker->setStrokeStyle( Qt::NoPen );
1771 }
1772 else
1773 {
1774 simpleMarker->setFillColor( QColor( 0, 0, 0, 0 ) );
1775 simpleMarker->setStrokeColor( color );
1776 }
1777
1778 const QColor strokeColor = convertColor( symbolStyle.value( QStringLiteral( "o" ), QString() ).toString() );
1779 if ( strokeColor.isValid() )
1780 {
1781 simpleMarker->setStrokeColor( strokeColor );
1782 simpleMarker->setStrokeStyle( Qt::SolidLine );
1783 }
1784
1785 markerLayer = std::move( simpleMarker );
1786 }
1787
1788 return std::make_unique< QgsMarkerSymbol >( QgsSymbolLayerList() << markerLayer.release() );
1789 };
1790
1791 switch ( type )
1792 {
1794 if ( styles.contains( QStringLiteral( "symbol" ) ) )
1795 {
1796 const QVariantMap symbolStyle = styles.value( QStringLiteral( "symbol" ) ).toMap();
1797 return convertSymbol( symbolStyle );
1798 }
1799 else
1800 {
1801 return nullptr;
1802 }
1803
1805 if ( styles.contains( QStringLiteral( "pen" ) ) )
1806 {
1807 // line symbol type
1808 const QVariantMap lineStyle = styles.value( QStringLiteral( "pen" ) ).toMap();
1809 return convertPen( lineStyle );
1810 }
1811 else
1812 {
1813 return nullptr;
1814 }
1815
1817 {
1818 std::unique_ptr< QgsSymbol > fillSymbol = std::make_unique< QgsFillSymbol >();
1819 if ( styles.contains( QStringLiteral( "brush" ) ) )
1820 {
1821 const QVariantMap brushStyle = styles.value( QStringLiteral( "brush" ) ).toMap();
1822 fillSymbol = convertBrush( brushStyle );
1823 }
1824 else
1825 {
1826 std::unique_ptr< QgsSimpleFillSymbolLayer > emptyFill = std::make_unique< QgsSimpleFillSymbolLayer >();
1827 emptyFill->setBrushStyle( Qt::NoBrush );
1828 fillSymbol = std::make_unique< QgsFillSymbol >( QgsSymbolLayerList() << emptyFill.release() );
1829 }
1830
1831 std::unique_ptr< QgsSymbol > penSymbol;
1832 if ( styles.contains( QStringLiteral( "pen" ) ) )
1833 {
1834 const QVariantMap lineStyle = styles.value( QStringLiteral( "pen" ) ).toMap();
1835 penSymbol = convertPen( lineStyle );
1836 }
1837
1838 if ( penSymbol )
1839 {
1840 const int count = penSymbol->symbolLayerCount();
1841
1842 if ( count == 1 )
1843 {
1844 // if only one pen symbol layer, let's try and combine it with the topmost brush layer, so that the resultant QGIS symbol is simpler
1845 if ( QgsSymbolLayerUtils::condenseFillAndOutline( dynamic_cast< QgsFillSymbolLayer * >( fillSymbol->symbolLayer( fillSymbol->symbolLayerCount() - 1 ) ),
1846 dynamic_cast< QgsLineSymbolLayer * >( penSymbol->symbolLayer( 0 ) ) ) )
1847 return fillSymbol;
1848 }
1849
1850 for ( int i = 0; i < count; ++i )
1851 {
1852 std::unique_ptr< QgsSymbolLayer > layer( penSymbol->takeSymbolLayer( 0 ) );
1853 layer->setLocked( true );
1854 fillSymbol->appendSymbolLayer( layer.release() );
1855 }
1856 }
1857
1858 return fillSymbol;
1859 }
1860
1862 break;
1863 }
1864
1865 return nullptr;
1866}
1867
1868void QgsOgrUtils::ogrFieldTypeToQVariantType( OGRFieldType ogrType, OGRFieldSubType ogrSubType, QVariant::Type &variantType, QVariant::Type &variantSubType )
1869{
1870 variantType = QVariant::Type::Invalid;
1871 variantSubType = QVariant::Type::Invalid;
1872
1873 switch ( ogrType )
1874 {
1875 case OFTInteger:
1876 if ( ogrSubType == OFSTBoolean )
1877 {
1878 variantType = QVariant::Bool;
1879 ogrSubType = OFSTBoolean;
1880 }
1881 else
1882 variantType = QVariant::Int;
1883 break;
1884 case OFTInteger64:
1885 variantType = QVariant::LongLong;
1886 break;
1887 case OFTReal:
1888 variantType = QVariant::Double;
1889 break;
1890 case OFTDate:
1891 variantType = QVariant::Date;
1892 break;
1893 case OFTTime:
1894 variantType = QVariant::Time;
1895 break;
1896 case OFTDateTime:
1897 variantType = QVariant::DateTime;
1898 break;
1899
1900 case OFTBinary:
1901 variantType = QVariant::ByteArray;
1902 break;
1903
1904 case OFTString:
1905 case OFTWideString:
1906 if ( ogrSubType == OFSTJSON )
1907 {
1908 ogrSubType = OFSTJSON;
1909 variantType = QVariant::Map;
1910 variantSubType = QVariant::String;
1911 }
1912 else
1913 {
1914 variantType = QVariant::String;
1915 }
1916 break;
1917
1918 case OFTStringList:
1919 case OFTWideStringList:
1920 variantType = QVariant::StringList;
1921 variantSubType = QVariant::String;
1922 break;
1923
1924 case OFTIntegerList:
1925 variantType = QVariant::List;
1926 variantSubType = QVariant::Int;
1927 break;
1928
1929 case OFTRealList:
1930 variantType = QVariant::List;
1931 variantSubType = QVariant::Double;
1932 break;
1933
1934 case OFTInteger64List:
1935 variantType = QVariant::List;
1936 variantSubType = QVariant::LongLong;
1937 break;
1938 }
1939}
1940
1941void QgsOgrUtils::variantTypeToOgrFieldType( QVariant::Type variantType, OGRFieldType &ogrType, OGRFieldSubType &ogrSubType )
1942{
1943 ogrSubType = OFSTNone;
1944 switch ( variantType )
1945 {
1946 case QVariant::Bool:
1947 ogrType = OFTInteger;
1948 ogrSubType = OFSTBoolean;
1949 break;
1950
1951 case QVariant::Int:
1952 ogrType = OFTInteger;
1953 break;
1954
1955 case QVariant::LongLong:
1956 ogrType = OFTInteger64;
1957 break;
1958
1959 case QVariant::Double:
1960 ogrType = OFTReal;
1961 break;
1962
1963 case QVariant::Char:
1964 ogrType = OFTString;
1965 break;
1966
1967 case QVariant::String:
1968 ogrType = OFTString;
1969 break;
1970
1971 case QVariant::StringList:
1972 ogrType = OFTStringList;
1973 break;
1974
1975 case QVariant::ByteArray:
1976 ogrType = OFTBinary;
1977 break;
1978
1979 case QVariant::Date:
1980 ogrType = OFTDate;
1981 break;
1982
1983 case QVariant::Time:
1984 ogrType = OFTTime;
1985 break;
1986 case QVariant::DateTime:
1987 ogrType = OFTDateTime;
1988 break;
1989
1990 default:
1991 ogrType = OFTString;
1992 break;
1993 }
1994}
1995
1996QVariant QgsOgrUtils::stringToVariant( OGRFieldType type, OGRFieldSubType, const QString &string )
1997{
1998 if ( string.isEmpty() )
1999 return QVariant();
2000
2001 bool ok = false;
2002 QVariant res;
2003 switch ( type )
2004 {
2005 case OFTInteger:
2006 res = string.toInt( &ok );
2007 break;
2008
2009 case OFTInteger64:
2010 res = string.toLongLong( &ok );
2011 break;
2012
2013 case OFTReal:
2014 res = string.toDouble( &ok );
2015 break;
2016
2017 case OFTString:
2018 case OFTWideString:
2019 res = string;
2020 ok = true;
2021 break;
2022
2023 case OFTDate:
2024 res = QDate::fromString( string, Qt::ISODate );
2025 ok = res.isValid();
2026 break;
2027
2028 case OFTTime:
2029 res = QTime::fromString( string, Qt::ISODate );
2030 ok = res.isValid();
2031 break;
2032
2033 case OFTDateTime:
2034 res = QDateTime::fromString( string, Qt::ISODate );
2035 ok = res.isValid();
2036 break;
2037
2038 default:
2039 res = string;
2040 ok = true;
2041 break;
2042 }
2043
2044 return ok ? res : QVariant();
2045}
2046
2047QList<QgsVectorDataProvider::NativeType> QgsOgrUtils::nativeFieldTypesForDriver( GDALDriverH driver )
2048{
2049 if ( !driver )
2050 return {};
2051
2052 const QString driverName = QString::fromUtf8( GDALGetDriverShortName( driver ) );
2053
2054 int nMaxIntLen = 11;
2055 int nMaxInt64Len = 21;
2056 int nMaxDoubleLen = 20;
2057 int nMaxDoublePrec = 15;
2058 int nDateLen = 8;
2059 if ( driverName == QLatin1String( "GPKG" ) )
2060 {
2061 // GPKG only supports field length for text (and binary)
2062 nMaxIntLen = 0;
2063 nMaxInt64Len = 0;
2064 nMaxDoubleLen = 0;
2065 nMaxDoublePrec = 0;
2066 nDateLen = 0;
2067 }
2068
2069 QList<QgsVectorDataProvider::NativeType> nativeTypes;
2070 nativeTypes
2071 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QVariant::Int ), QStringLiteral( "integer" ), QVariant::Int, 0, nMaxIntLen )
2072 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QVariant::LongLong ), QStringLiteral( "integer64" ), QVariant::LongLong, 0, nMaxInt64Len )
2073 << QgsVectorDataProvider::NativeType( QObject::tr( "Decimal number (real)" ), QStringLiteral( "double" ), QVariant::Double, 0, nMaxDoubleLen, 0, nMaxDoublePrec )
2074 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QVariant::String ), QStringLiteral( "string" ), QVariant::String, 0, 65535 );
2075
2076 if ( driverName == QLatin1String( "GPKG" ) )
2077 nativeTypes << QgsVectorDataProvider::NativeType( QObject::tr( "JSON (string)" ), QStringLiteral( "JSON" ), QVariant::Map, 0, 0, 0, 0, QVariant::String );
2078
2079 bool supportsDate = true;
2080 bool supportsTime = true;
2081 bool supportsDateTime = true;
2082 bool supportsBinary = false;
2083 bool supportIntegerList = false;
2084 bool supportInteger64List = false;
2085 bool supportRealList = false;
2086 bool supportsStringList = false;
2087
2088 // For drivers that advertise their data type, use that instead of the
2089 // above hardcoded defaults.
2090 if ( const char *pszDataTypes = GDALGetMetadataItem( driver, GDAL_DMD_CREATIONFIELDDATATYPES, nullptr ) )
2091 {
2092 char **papszTokens = CSLTokenizeString2( pszDataTypes, " ", 0 );
2093 supportsDate = CSLFindString( papszTokens, "Date" ) >= 0;
2094 supportsTime = CSLFindString( papszTokens, "Time" ) >= 0;
2095 supportsDateTime = CSLFindString( papszTokens, "DateTime" ) >= 0;
2096 supportsBinary = CSLFindString( papszTokens, "Binary" ) >= 0;
2097 supportIntegerList = CSLFindString( papszTokens, "IntegerList" ) >= 0;
2098 supportInteger64List = CSLFindString( papszTokens, "Integer64List" ) >= 0;
2099 supportRealList = CSLFindString( papszTokens, "RealList" ) >= 0;
2100 supportsStringList = CSLFindString( papszTokens, "StringList" ) >= 0;
2101 CSLDestroy( papszTokens );
2102 }
2103
2104 // Older versions of GDAL incorrectly report that shapefiles support
2105 // DateTime.
2106#if GDAL_VERSION_NUM < GDAL_COMPUTE_VERSION(3,2,0)
2107 if ( driverName == QLatin1String( "ESRI Shapefile" ) )
2108 {
2109 supportsDateTime = false;
2110 }
2111#endif
2112
2113 if ( supportsDate )
2114 {
2115 nativeTypes
2116 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QVariant::Date ), QStringLiteral( "date" ), QVariant::Date, nDateLen, nDateLen );
2117 }
2118 if ( supportsTime )
2119 {
2120 nativeTypes
2121 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QVariant::Time ), QStringLiteral( "time" ), QVariant::Time );
2122 }
2123 if ( supportsDateTime )
2124 {
2125 nativeTypes
2126 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QVariant::DateTime ), QStringLiteral( "datetime" ), QVariant::DateTime );
2127 }
2128 if ( supportsBinary )
2129 {
2130 nativeTypes
2131 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QVariant::ByteArray ), QStringLiteral( "binary" ), QVariant::ByteArray );
2132 }
2133 if ( supportIntegerList )
2134 {
2135 nativeTypes
2136 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QVariant::List, QVariant::Int ), QStringLiteral( "integerlist" ), QVariant::List, 0, 0, 0, 0, QVariant::Int );
2137 }
2138 if ( supportInteger64List )
2139 {
2140 nativeTypes
2141 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QVariant::List, QVariant::LongLong ), QStringLiteral( "integer64list" ), QVariant::List, 0, 0, 0, 0, QVariant::LongLong );
2142 }
2143 if ( supportRealList )
2144 {
2145 nativeTypes
2146 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QVariant::List, QVariant::Double ), QStringLiteral( "doublelist" ), QVariant::List, 0, 0, 0, 0, QVariant::Double );
2147 }
2148 if ( supportsStringList )
2149 {
2150 nativeTypes
2151 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QVariant::StringList ), QStringLiteral( "stringlist" ), QVariant::List, 0, 0, 0, 0, QVariant::String );
2152 }
2153
2154 const char *pszDataSubTypes = GDALGetMetadataItem( driver, GDAL_DMD_CREATIONFIELDDATASUBTYPES, nullptr );
2155 if ( pszDataSubTypes && strstr( pszDataSubTypes, "Boolean" ) )
2156 {
2157 // boolean data type
2158 nativeTypes
2159 << QgsVectorDataProvider::NativeType( QObject::tr( "Boolean" ), QStringLiteral( "bool" ), QVariant::Bool );
2160 }
2161
2162 return nativeTypes;
2163}
2164
2165
2166#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,3,0)
2167std::unique_ptr< QgsFieldDomain > QgsOgrUtils::convertFieldDomain( OGRFieldDomainH domain )
2168{
2169 if ( !domain )
2170 return nullptr;
2171
2172 const QString name{ OGR_FldDomain_GetName( domain ) };
2173 const QString description{ OGR_FldDomain_GetDescription( domain ) };
2174
2175 QVariant::Type fieldType = QVariant::Type::Invalid;
2176 QVariant::Type fieldSubType = QVariant::Type::Invalid;
2177 const OGRFieldType domainFieldType = OGR_FldDomain_GetFieldType( domain );
2178 const OGRFieldSubType domainFieldSubType = OGR_FldDomain_GetFieldSubType( domain );
2179 ogrFieldTypeToQVariantType( domainFieldType, domainFieldSubType, fieldType, fieldSubType );
2180
2181 std::unique_ptr< QgsFieldDomain > res;
2182 switch ( OGR_FldDomain_GetDomainType( domain ) )
2183 {
2184 case OFDT_CODED:
2185 {
2186 QList< QgsCodedValue > values;
2187 const OGRCodedValue *codedValue = OGR_CodedFldDomain_GetEnumeration( domain );
2188 while ( codedValue && codedValue->pszCode )
2189 {
2190 const QString code( codedValue->pszCode );
2191
2192 // if pszValue is null then it indicates we are working with a set of acceptable values which aren't
2193 // coded. In this case we copy the code as the value so that QGIS exposes the domain as a choice of
2194 // the valid code values.
2195 const QString value( codedValue->pszValue ? codedValue->pszValue : codedValue->pszCode );
2196 values.append( QgsCodedValue( stringToVariant( domainFieldType, domainFieldSubType, code ), value ) );
2197
2198 codedValue++;
2199 }
2200
2201 res = std::make_unique< QgsCodedFieldDomain >( name, description, fieldType, values );
2202 break;
2203 }
2204
2205 case OFDT_RANGE:
2206 {
2207 QVariant minValue;
2208 bool minIsInclusive = false;
2209 if ( const OGRField *min = OGR_RangeFldDomain_GetMin( domain, &minIsInclusive ) )
2210 {
2211 minValue = QgsOgrUtils::OGRFieldtoVariant( min, domainFieldType );
2212 }
2213 QVariant maxValue;
2214 bool maxIsInclusive = false;
2215 if ( const OGRField *max = OGR_RangeFldDomain_GetMax( domain, &maxIsInclusive ) )
2216 {
2217 maxValue = QgsOgrUtils::OGRFieldtoVariant( max, domainFieldType );
2218 }
2219
2220 res = std::make_unique< QgsRangeFieldDomain >( name, description, fieldType,
2221 minValue, minIsInclusive,
2222 maxValue, maxIsInclusive );
2223 break;
2224 }
2225
2226 case OFDT_GLOB:
2227 res = std::make_unique< QgsGlobFieldDomain >( name, description, fieldType,
2228 QString( OGR_GlobFldDomain_GetGlob( domain ) ) );
2229 break;
2230 }
2231
2232 switch ( OGR_FldDomain_GetMergePolicy( domain ) )
2233 {
2234 case OFDMP_DEFAULT_VALUE:
2235 res->setMergePolicy( Qgis::FieldDomainMergePolicy::DefaultValue );
2236 break;
2237 case OFDMP_SUM:
2238 res->setMergePolicy( Qgis::FieldDomainMergePolicy::Sum );
2239 break;
2240 case OFDMP_GEOMETRY_WEIGHTED:
2242 break;
2243 }
2244
2245 switch ( OGR_FldDomain_GetSplitPolicy( domain ) )
2246 {
2247 case OFDSP_DEFAULT_VALUE:
2248 res->setSplitPolicy( Qgis::FieldDomainSplitPolicy::DefaultValue );
2249 break;
2250 case OFDSP_DUPLICATE:
2251 res->setSplitPolicy( Qgis::FieldDomainSplitPolicy::Duplicate );
2252 break;
2253 case OFDSP_GEOMETRY_RATIO:
2254 res->setSplitPolicy( Qgis::FieldDomainSplitPolicy::GeometryRatio );
2255 break;
2256 }
2257 return res;
2258}
2259
2260OGRFieldDomainH QgsOgrUtils::convertFieldDomain( const QgsFieldDomain *domain )
2261{
2262 if ( !domain )
2263 return nullptr;
2264
2265 OGRFieldType domainFieldType = OFTInteger;
2266 OGRFieldSubType domainFieldSubType = OFSTNone;
2267 variantTypeToOgrFieldType( domain->fieldType(), domainFieldType, domainFieldSubType );
2268
2269 OGRFieldDomainH res = nullptr;
2270 switch ( domain->type() )
2271 {
2273 {
2274 std::vector< OGRCodedValue > enumeration;
2275 const QList< QgsCodedValue> values = qgis::down_cast< const QgsCodedFieldDomain * >( domain )->values();
2276 enumeration.reserve( values.size() );
2277 for ( const QgsCodedValue &value : values )
2278 {
2279 OGRCodedValue codedValue;
2280 codedValue.pszCode = CPLStrdup( value.code().toString().toUtf8().constData() );
2281 codedValue.pszValue = CPLStrdup( value.value().toUtf8().constData() );
2282 enumeration.push_back( codedValue );
2283 }
2284 OGRCodedValue last;
2285 last.pszCode = nullptr;
2286 last.pszValue = nullptr;
2287 enumeration.push_back( last );
2288 res = OGR_CodedFldDomain_Create(
2289 domain->name().toUtf8().constData(),
2290 domain->description().toUtf8().constData(),
2291 domainFieldType,
2292 domainFieldSubType,
2293 enumeration.data()
2294 );
2295
2296 for ( const OGRCodedValue &value : std::as_const( enumeration ) )
2297 {
2298 CPLFree( value.pszCode );
2299 CPLFree( value.pszValue );
2300 }
2301 break;
2302 }
2303
2305 {
2306 std::unique_ptr< OGRField > min = variantToOGRField( qgis::down_cast< const QgsRangeFieldDomain * >( domain )->minimum(), domainFieldType );
2307 std::unique_ptr< OGRField > max = variantToOGRField( qgis::down_cast< const QgsRangeFieldDomain * >( domain )->maximum(), domainFieldType );
2308 if ( !min || !max )
2309 return nullptr;
2310 res = OGR_RangeFldDomain_Create(
2311 domain->name().toUtf8().constData(),
2312 domain->description().toUtf8().constData(),
2313 domainFieldType,
2314 domainFieldSubType,
2315 min.get(),
2316 qgis::down_cast< const QgsRangeFieldDomain * >( domain )->minimumIsInclusive(),
2317 max.get(),
2318 qgis::down_cast< const QgsRangeFieldDomain * >( domain )->maximumIsInclusive()
2319 );
2320 break;
2321 }
2322
2324 {
2325 res = OGR_GlobFldDomain_Create(
2326 domain->name().toUtf8().constData(),
2327 domain->description().toUtf8().constData(),
2328 domainFieldType,
2329 domainFieldSubType,
2330 qgis::down_cast< const QgsGlobFieldDomain * >( domain )->glob().toUtf8().constData()
2331 );
2332 break;
2333 }
2334 }
2335
2336 switch ( domain->mergePolicy() )
2337 {
2339 OGR_FldDomain_SetMergePolicy( res, OFDMP_DEFAULT_VALUE );
2340 break;
2342 OGR_FldDomain_SetMergePolicy( res, OFDMP_GEOMETRY_WEIGHTED );
2343 break;
2345 OGR_FldDomain_SetMergePolicy( res, OFDMP_SUM );
2346 break;
2347 }
2348
2349 switch ( domain->splitPolicy() )
2350 {
2352 OGR_FldDomain_SetSplitPolicy( res, OFDSP_DEFAULT_VALUE );
2353 break;
2355 OGR_FldDomain_SetSplitPolicy( res, OFDSP_GEOMETRY_RATIO );
2356 break;
2358 OGR_FldDomain_SetSplitPolicy( res, OFDSP_DUPLICATE );
2359 break;
2360 }
2361
2362 return res;
2363}
2364
2365#endif
@ 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...
@ DefaultValue
Use default field value.
@ Duplicate
Duplicate original value.
MarkerShape
Marker shapes.
Definition qgis.h:1532
@ Line
Vertical line.
@ Triangle
Triangle.
@ Cross2
Rotated cross (lines only), 'x' shape.
@ Cross
Cross (lines only)
@ Coded
Coded field domain.
@ Range
Numeric range field domain (min/max)
@ Glob
Glob string pattern field domain.
SymbolType
Symbol types.
Definition qgis.h:206
@ Marker
Marker symbol.
@ Line
Line symbol.
@ Fill
Fill symbol.
@ Hybrid
Hybrid symbol.
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:51
QVariant::Type type
Definition qgsfield.h:58
QVariant::Type subType() const
If the field is a collection, gets its element's type.
Definition qgsfield.cpp:147
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.
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 QgsLineSymbol * convertLineSymbol(int identifier, QgsMapInfoSymbolConversionContext &context, const QColor &foreColor, double size, QgsUnitTypes::RenderUnit sizeUnit, bool interleaved=false)
Converts the MapInfo line symbol with the specified identifier to a QgsLineSymbol.
static QgsMarkerSymbol * convertMarkerSymbol(int identifier, QgsMapInfoSymbolConversionContext &context, const QColor &color, double size, QgsUnitTypes::RenderUnit sizeUnit)
Converts the MapInfo marker symbol with the specified identifier to a QgsMarkerSymbol.
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 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 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 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 QgsWkbTypes::Type ogrGeometryTypeToQgsWkbType(OGRwkbGeometryType ogrGeomType)
Converts a OGRwkbGeometryType to QgsWkbTypes::Type.
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 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.
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...
RenderUnit
Rendering size units.
@ RenderPoints
Points (e.g., for font sizes)
@ RenderInches
Inches.
@ RenderMillimeters
Millimeters.
@ RenderMapUnits
Map units.
static QString typeToDisplayString(QVariant::Type type, QVariant::Type subType=QVariant::Type::Invalid)
Returns a user-friendly translated string representing a QVariant type.
Type
The WKB type describes the number of dimensions a geometry has.
Definition qgswkbtypes.h:70
@ GeometryCollectionZ
Definition qgswkbtypes.h:93
static bool hasZ(Type type)
Tests whether a WKB type contains the z-dimension.
static bool hasM(Type type)
Tests whether a WKB type contains m values.
static Type zmType(Type type, bool hasZ, bool hasM)
Returns the modified input geometry type according to hasZ / hasM.
static Type flatType(Type 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.
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:3032
void * GDALDatasetH
void * OGRSpatialReferenceH
QList< QgsFeature > QgsFeatureList
Definition qgsfeature.h:922
const QgsField & field
Definition qgsfield.h:476
#define DEFAULT_SIMPLELINE_WIDTH
#define QgsDebugMsg(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:29
const QgsCoordinateReferenceSystem & crs
void CORE_EXPORT operator()(GDALDatasetH datasource) const
Destroys an gdal dataset, 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.