LCOV - code coverage report
Current view: top level - core - qgsogrutils.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 110 590 18.6 %
Date: 2021-03-26 12:19:53 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       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"
      28                 :            : 
      29                 :            : #include <QTextCodec>
      30                 :            : #include <QUuid>
      31                 :            : #include <cpl_error.h>
      32                 :            : #include <QJsonDocument>
      33                 :            : #include <QFileInfo>
      34                 :            : #include <QDir>
      35                 :            : #include <QTextStream>
      36                 :            : #include <QDataStream>
      37                 :            : #include <QRegularExpression>
      38                 :            : 
      39                 :            : #include "ogr_srs_api.h"
      40                 :            : 
      41                 :            : // Starting with GDAL 2.2, there are 2 concepts: unset fields and null fields
      42                 :            : // whereas previously there was only unset fields. For QGIS purposes, both
      43                 :            : // states (unset/null) are equivalent.
      44                 :            : #ifndef OGRNullMarker
      45                 :            : #define OGR_F_IsFieldSetAndNotNull OGR_F_IsFieldSet
      46                 :            : #endif
      47                 :            : 
      48                 :            : 
      49                 :            : 
      50                 :          0 : void gdal::OGRDataSourceDeleter::operator()( OGRDataSourceH source )
      51                 :            : {
      52                 :          0 :   OGR_DS_Destroy( source );
      53                 :          0 : }
      54                 :            : 
      55                 :            : 
      56                 :          0 : void gdal::OGRGeometryDeleter::operator()( OGRGeometryH geometry )
      57                 :            : {
      58                 :          0 :   OGR_G_DestroyGeometry( geometry );
      59                 :          0 : }
      60                 :            : 
      61                 :          0 : void gdal::OGRFldDeleter::operator()( OGRFieldDefnH definition )
      62                 :            : {
      63                 :          0 :   OGR_Fld_Destroy( definition );
      64                 :          0 : }
      65                 :            : 
      66                 :       1134 : void gdal::OGRFeatureDeleter::operator()( OGRFeatureH feature )
      67                 :            : {
      68                 :       1134 :   OGR_F_Destroy( feature );
      69                 :       1134 : }
      70                 :            : 
      71                 :          0 : void gdal::GDALDatasetCloser::operator()( GDALDatasetH dataset )
      72                 :            : {
      73                 :          0 :   GDALClose( dataset );
      74                 :          0 : }
      75                 :            : 
      76                 :          0 : void gdal::fast_delete_and_close( gdal::dataset_unique_ptr &dataset, GDALDriverH driver, const QString &path )
      77                 :            : {
      78                 :            :   // see https://github.com/qgis/QGIS/commit/d024910490a39e65e671f2055c5b6543e06c7042#commitcomment-25194282
      79                 :            :   // faster if we close the handle AFTER delete, but doesn't work for windows
      80                 :            : #ifdef Q_OS_WIN
      81                 :            :   // close dataset handle
      82                 :            :   dataset.reset();
      83                 :            : #endif
      84                 :            : 
      85                 :          0 :   CPLPushErrorHandler( CPLQuietErrorHandler );
      86                 :          0 :   GDALDeleteDataset( driver, path.toUtf8().constData() );
      87                 :          0 :   CPLPopErrorHandler();
      88                 :            : 
      89                 :            : #ifndef Q_OS_WIN
      90                 :            :   // close dataset handle
      91                 :          0 :   dataset.reset();
      92                 :            : #endif
      93                 :          0 : }
      94                 :            : 
      95                 :            : 
      96                 :          0 : void gdal::GDALWarpOptionsDeleter::operator()( GDALWarpOptions *options )
      97                 :            : {
      98                 :          0 :   GDALDestroyWarpOptions( options );
      99                 :          0 : }
     100                 :            : 
     101                 :          0 : QgsFeature QgsOgrUtils::readOgrFeature( OGRFeatureH ogrFet, const QgsFields &fields, QTextCodec *encoding )
     102                 :            : {
     103                 :          0 :   QgsFeature feature;
     104                 :          0 :   if ( !ogrFet )
     105                 :            :   {
     106                 :          0 :     feature.setValid( false );
     107                 :          0 :     return feature;
     108                 :            :   }
     109                 :            : 
     110                 :          0 :   feature.setId( OGR_F_GetFID( ogrFet ) );
     111                 :          0 :   feature.setValid( true );
     112                 :            : 
     113                 :          0 :   if ( !readOgrFeatureGeometry( ogrFet, feature ) )
     114                 :            :   {
     115                 :          0 :     feature.setValid( false );
     116                 :          0 :   }
     117                 :            : 
     118                 :          0 :   if ( !readOgrFeatureAttributes( ogrFet, fields, feature, encoding ) )
     119                 :            :   {
     120                 :          0 :     feature.setValid( false );
     121                 :          0 :   }
     122                 :            : 
     123                 :          0 :   return feature;
     124                 :          0 : }
     125                 :            : 
     126                 :          0 : QgsFields QgsOgrUtils::readOgrFields( OGRFeatureH ogrFet, QTextCodec *encoding )
     127                 :            : {
     128                 :          0 :   QgsFields fields;
     129                 :            : 
     130                 :          0 :   if ( !ogrFet )
     131                 :          0 :     return fields;
     132                 :            : 
     133                 :          0 :   int fieldCount = OGR_F_GetFieldCount( ogrFet );
     134                 :          0 :   for ( int i = 0; i < fieldCount; ++i )
     135                 :            :   {
     136                 :          0 :     OGRFieldDefnH fldDef = OGR_F_GetFieldDefnRef( ogrFet, i );
     137                 :          0 :     if ( !fldDef )
     138                 :            :     {
     139                 :          0 :       fields.append( QgsField() );
     140                 :          0 :       continue;
     141                 :            :     }
     142                 :            : 
     143                 :          0 :     QString name = encoding ? encoding->toUnicode( OGR_Fld_GetNameRef( fldDef ) ) : QString::fromUtf8( OGR_Fld_GetNameRef( fldDef ) );
     144                 :            :     QVariant::Type varType;
     145                 :          0 :     switch ( OGR_Fld_GetType( fldDef ) )
     146                 :            :     {
     147                 :            :       case OFTInteger:
     148                 :          0 :         if ( OGR_Fld_GetSubType( fldDef ) == OFSTBoolean )
     149                 :          0 :           varType = QVariant::Bool;
     150                 :            :         else
     151                 :          0 :           varType = QVariant::Int;
     152                 :          0 :         break;
     153                 :            :       case OFTInteger64:
     154                 :          0 :         varType = QVariant::LongLong;
     155                 :          0 :         break;
     156                 :            :       case OFTReal:
     157                 :          0 :         varType = QVariant::Double;
     158                 :          0 :         break;
     159                 :            :       case OFTDate:
     160                 :          0 :         varType = QVariant::Date;
     161                 :          0 :         break;
     162                 :            :       case OFTTime:
     163                 :          0 :         varType = QVariant::Time;
     164                 :          0 :         break;
     165                 :            :       case OFTDateTime:
     166                 :          0 :         varType = QVariant::DateTime;
     167                 :          0 :         break;
     168                 :            :       case OFTString:
     169                 :          0 :         if ( OGR_Fld_GetSubType( fldDef ) == OFSTJSON )
     170                 :          0 :           varType = QVariant::Map;
     171                 :            :         else
     172                 :          0 :           varType = QVariant::String;
     173                 :          0 :         break;
     174                 :            :       default:
     175                 :          0 :         varType = QVariant::String; // other unsupported, leave it as a string
     176                 :          0 :     }
     177                 :          0 :     fields.append( QgsField( name, varType ) );
     178                 :          0 :   }
     179                 :          0 :   return fields;
     180                 :          0 : }
     181                 :            : 
     182                 :            : 
     183                 :       1564 : QVariant QgsOgrUtils::getOgrFeatureAttribute( OGRFeatureH ogrFet, const QgsFields &fields, int attIndex, QTextCodec *encoding, bool *ok )
     184                 :            : {
     185                 :       1564 :   if ( attIndex < 0 || attIndex >= fields.count() )
     186                 :            :   {
     187                 :          0 :     if ( ok )
     188                 :          0 :       *ok = false;
     189                 :          0 :     return QVariant();
     190                 :            :   }
     191                 :            : 
     192                 :       1564 :   const QgsField field = fields.at( attIndex );
     193                 :       1564 :   return getOgrFeatureAttribute( ogrFet, field, attIndex, encoding, ok );
     194                 :       1564 : }
     195                 :            : 
     196                 :       1564 : QVariant QgsOgrUtils::getOgrFeatureAttribute( OGRFeatureH ogrFet, const QgsField &field, int attIndex, QTextCodec *encoding, bool *ok )
     197                 :            : {
     198                 :       1564 :   if ( !ogrFet || attIndex < 0 )
     199                 :            :   {
     200                 :          0 :     if ( ok )
     201                 :          0 :       *ok = false;
     202                 :          0 :     return QVariant();
     203                 :            :   }
     204                 :            : 
     205                 :       1564 :   OGRFieldDefnH fldDef = OGR_F_GetFieldDefnRef( ogrFet, attIndex );
     206                 :            : 
     207                 :       1564 :   if ( ! fldDef )
     208                 :            :   {
     209                 :          0 :     if ( ok )
     210                 :          0 :       *ok = false;
     211                 :            : 
     212                 :          0 :     QgsDebugMsg( QStringLiteral( "ogrFet->GetFieldDefnRef(attindex) returns NULL" ) );
     213                 :          0 :     return QVariant();
     214                 :            :   }
     215                 :            : 
     216                 :       1564 :   QVariant value;
     217                 :            : 
     218                 :       1564 :   if ( ok )
     219                 :       1564 :     *ok = true;
     220                 :            : 
     221                 :       1564 :   if ( OGR_F_IsFieldSetAndNotNull( ogrFet, attIndex ) )
     222                 :            :   {
     223                 :       1048 :     switch ( field.type() )
     224                 :            :     {
     225                 :            :       case QVariant::String:
     226                 :            :       {
     227                 :          1 :         if ( encoding )
     228                 :          1 :           value = QVariant( encoding->toUnicode( OGR_F_GetFieldAsString( ogrFet, attIndex ) ) );
     229                 :            :         else
     230                 :          0 :           value = QVariant( QString::fromUtf8( OGR_F_GetFieldAsString( ogrFet, attIndex ) ) );
     231                 :            : 
     232                 :            : #ifdef Q_OS_WIN
     233                 :            :         // Fixes GH #41076 (empty strings shown as NULL), because we have checked before that it was NOT NULL
     234                 :            :         // Note:  QVariant( QString( ) ).isNull( ) is still true on windows so we really need string literal :(
     235                 :            :         if ( value.isNull() )
     236                 :            :           value = QVariant( QStringLiteral( "" ) ); // skip-keyword-check
     237                 :            : #endif
     238                 :            : 
     239                 :          1 :         break;
     240                 :            :       }
     241                 :            :       case QVariant::Int:
     242                 :        231 :         value = QVariant( OGR_F_GetFieldAsInteger( ogrFet, attIndex ) );
     243                 :        231 :         break;
     244                 :            :       case QVariant::Bool:
     245                 :          0 :         value = QVariant( bool( OGR_F_GetFieldAsInteger( ogrFet, attIndex ) ) );
     246                 :          0 :         break;
     247                 :            :       case QVariant::LongLong:
     248                 :        816 :         value = QVariant( OGR_F_GetFieldAsInteger64( ogrFet, attIndex ) );
     249                 :        816 :         break;
     250                 :            :       case QVariant::Double:
     251                 :          0 :         value = QVariant( OGR_F_GetFieldAsDouble( ogrFet, attIndex ) );
     252                 :          0 :         break;
     253                 :            :       case QVariant::Date:
     254                 :            :       case QVariant::DateTime:
     255                 :            :       case QVariant::Time:
     256                 :            :       {
     257                 :            :         int year, month, day, hour, minute, second, tzf;
     258                 :            : 
     259                 :          0 :         OGR_F_GetFieldAsDateTime( ogrFet, attIndex, &year, &month, &day, &hour, &minute, &second, &tzf );
     260                 :          0 :         if ( field.type() == QVariant::Date )
     261                 :          0 :           value = QDate( year, month, day );
     262                 :          0 :         else if ( field.type() == QVariant::Time )
     263                 :          0 :           value = QTime( hour, minute, second );
     264                 :            :         else
     265                 :          0 :           value = QDateTime( QDate( year, month, day ), QTime( hour, minute, second ) );
     266                 :            :       }
     267                 :          0 :       break;
     268                 :            : 
     269                 :            :       case QVariant::ByteArray:
     270                 :            :       {
     271                 :          0 :         int size = 0;
     272                 :          0 :         const GByte *b = OGR_F_GetFieldAsBinary( ogrFet, attIndex, &size );
     273                 :            : 
     274                 :            :         // QByteArray::fromRawData is funny. It doesn't take ownership of the data, so we have to explicitly call
     275                 :            :         // detach on it to force a copy which owns the data
     276                 :          0 :         QByteArray ba = QByteArray::fromRawData( reinterpret_cast<const char *>( b ), size );
     277                 :          0 :         ba.detach();
     278                 :            : 
     279                 :          0 :         value = ba;
     280                 :            :         break;
     281                 :          0 :       }
     282                 :            : 
     283                 :            :       case QVariant::List:
     284                 :            :       {
     285                 :          0 :         if ( field.subType() == QVariant::String )
     286                 :            :         {
     287                 :          0 :           QStringList list;
     288                 :          0 :           char **lst = OGR_F_GetFieldAsStringList( ogrFet, attIndex );
     289                 :          0 :           const int count = CSLCount( lst );
     290                 :          0 :           if ( count > 0 )
     291                 :            :           {
     292                 :          0 :             for ( int i = 0; i < count; i++ )
     293                 :            :             {
     294                 :          0 :               if ( encoding )
     295                 :          0 :                 list << encoding->toUnicode( lst[i] );
     296                 :            :               else
     297                 :          0 :                 list << QString::fromUtf8( lst[i] );
     298                 :          0 :             }
     299                 :          0 :           }
     300                 :          0 :           value = list;
     301                 :          0 :         }
     302                 :            :         else
     303                 :            :         {
     304                 :            :           Q_ASSERT_X( false, "QgsOgrUtils::getOgrFeatureAttribute", "unsupported field type" );
     305                 :          0 :           if ( ok )
     306                 :          0 :             *ok = false;
     307                 :            :         }
     308                 :          0 :         break;
     309                 :            :       }
     310                 :            : 
     311                 :            :       case QVariant::Map:
     312                 :            :       {
     313                 :            :         //it has to be JSON
     314                 :            :         //it's null if no json format
     315                 :          0 :         if ( encoding )
     316                 :          0 :           value = QJsonDocument::fromJson( encoding->toUnicode( OGR_F_GetFieldAsString( ogrFet, attIndex ) ).toUtf8() ).toVariant();
     317                 :            :         else
     318                 :          0 :           value = QJsonDocument::fromJson( QString::fromUtf8( OGR_F_GetFieldAsString( ogrFet, attIndex ) ).toUtf8() ).toVariant();
     319                 :          0 :         break;
     320                 :            :       }
     321                 :            :       default:
     322                 :            :         Q_ASSERT_X( false, "QgsOgrUtils::getOgrFeatureAttribute", "unsupported field type" );
     323                 :          0 :         if ( ok )
     324                 :          0 :           *ok = false;
     325                 :          0 :     }
     326                 :       1048 :   }
     327                 :            :   else
     328                 :            :   {
     329                 :        516 :     value = QVariant( field.type() );
     330                 :            :   }
     331                 :            : 
     332                 :       1564 :   return value;
     333                 :       1564 : }
     334                 :            : 
     335                 :          0 : bool QgsOgrUtils::readOgrFeatureAttributes( OGRFeatureH ogrFet, const QgsFields &fields, QgsFeature &feature, QTextCodec *encoding )
     336                 :            : {
     337                 :            :   // read all attributes
     338                 :          0 :   feature.initAttributes( fields.count() );
     339                 :          0 :   feature.setFields( fields );
     340                 :            : 
     341                 :          0 :   if ( !ogrFet )
     342                 :          0 :     return false;
     343                 :            : 
     344                 :          0 :   bool ok = false;
     345                 :          0 :   for ( int idx = 0; idx < fields.count(); ++idx )
     346                 :            :   {
     347                 :          0 :     QVariant value = getOgrFeatureAttribute( ogrFet, fields, idx, encoding, &ok );
     348                 :          0 :     if ( ok )
     349                 :            :     {
     350                 :          0 :       feature.setAttribute( idx, value );
     351                 :          0 :     }
     352                 :          0 :   }
     353                 :          0 :   return true;
     354                 :          0 : }
     355                 :            : 
     356                 :          0 : bool QgsOgrUtils::readOgrFeatureGeometry( OGRFeatureH ogrFet, QgsFeature &feature )
     357                 :            : {
     358                 :          0 :   if ( !ogrFet )
     359                 :          0 :     return false;
     360                 :            : 
     361                 :          0 :   OGRGeometryH geom = OGR_F_GetGeometryRef( ogrFet );
     362                 :          0 :   if ( !geom )
     363                 :          0 :     feature.clearGeometry();
     364                 :            :   else
     365                 :          0 :     feature.setGeometry( ogrGeometryToQgsGeometry( geom ) );
     366                 :            : 
     367                 :          0 :   return true;
     368                 :          0 : }
     369                 :            : 
     370                 :        133 : std::unique_ptr< QgsPoint > ogrGeometryToQgsPoint( OGRGeometryH geom )
     371                 :            : {
     372                 :        133 :   QgsWkbTypes::Type wkbType = static_cast<QgsWkbTypes::Type>( OGR_G_GetGeometryType( geom ) );
     373                 :            : 
     374                 :            :   double x, y, z, m;
     375                 :        133 :   OGR_G_GetPointZM( geom, 0, &x, &y, &z, &m );
     376                 :        133 :   return std::make_unique< QgsPoint >( wkbType, x, y, z, m );
     377                 :            : }
     378                 :            : 
     379                 :          0 : std::unique_ptr< QgsMultiPoint > ogrGeometryToQgsMultiPoint( OGRGeometryH geom )
     380                 :            : {
     381                 :          0 :   std::unique_ptr< QgsMultiPoint > mp = std::make_unique< QgsMultiPoint >();
     382                 :            : 
     383                 :          0 :   const int count = OGR_G_GetGeometryCount( geom );
     384                 :          0 :   mp->reserve( count );
     385                 :          0 :   for ( int i = 0; i < count; ++i )
     386                 :            :   {
     387                 :          0 :     mp->addGeometry( ogrGeometryToQgsPoint( OGR_G_GetGeometryRef( geom, i ) ).release() );
     388                 :          0 :   }
     389                 :            : 
     390                 :          0 :   return mp;
     391                 :          0 : }
     392                 :            : 
     393                 :       1036 : std::unique_ptr< QgsLineString > ogrGeometryToQgsLineString( OGRGeometryH geom )
     394                 :            : {
     395                 :       1036 :   QgsWkbTypes::Type wkbType = static_cast<QgsWkbTypes::Type>( OGR_G_GetGeometryType( geom ) );
     396                 :            : 
     397                 :       1036 :   int count = OGR_G_GetPointCount( geom );
     398                 :       1036 :   QVector< double > x( count );
     399                 :       1036 :   QVector< double > y( count );
     400                 :       1036 :   QVector< double > z;
     401                 :       1036 :   double *pz = nullptr;
     402                 :       1036 :   if ( QgsWkbTypes::hasZ( wkbType ) )
     403                 :            :   {
     404                 :          0 :     z.resize( count );
     405                 :          0 :     pz = z.data();
     406                 :          0 :   }
     407                 :       1036 :   double *pm = nullptr;
     408                 :       1036 :   QVector< double > m;
     409                 :       1036 :   if ( QgsWkbTypes::hasM( wkbType ) )
     410                 :            :   {
     411                 :          0 :     m.resize( count );
     412                 :          0 :     pm = m.data();
     413                 :          0 :   }
     414                 :       1036 :   OGR_G_GetPointsZM( geom, x.data(), sizeof( double ), y.data(), sizeof( double ), pz, sizeof( double ), pm, sizeof( double ) );
     415                 :            : 
     416                 :       1036 :   return std::make_unique< QgsLineString>( x, y, z, m, wkbType == QgsWkbTypes::LineString25D );
     417                 :       1036 : }
     418                 :            : 
     419                 :         21 : std::unique_ptr< QgsMultiLineString > ogrGeometryToQgsMultiLineString( OGRGeometryH geom )
     420                 :            : {
     421                 :         21 :   std::unique_ptr< QgsMultiLineString > mp = std::make_unique< QgsMultiLineString >();
     422                 :            : 
     423                 :         21 :   const int count = OGR_G_GetGeometryCount( geom );
     424                 :         21 :   mp->reserve( count );
     425                 :         63 :   for ( int i = 0; i < count; ++i )
     426                 :            :   {
     427                 :         42 :     mp->addGeometry( ogrGeometryToQgsLineString( OGR_G_GetGeometryRef( geom, i ) ).release() );
     428                 :         42 :   }
     429                 :            : 
     430                 :         21 :   return mp;
     431                 :         21 : }
     432                 :            : 
     433                 :        814 : std::unique_ptr< QgsPolygon > ogrGeometryToQgsPolygon( OGRGeometryH geom )
     434                 :            : {
     435                 :        814 :   std::unique_ptr< QgsPolygon > polygon = std::make_unique< QgsPolygon >();
     436                 :            : 
     437                 :        814 :   const int count = OGR_G_GetGeometryCount( geom );
     438                 :        814 :   if ( count >= 1 )
     439                 :            :   {
     440                 :        814 :     polygon->setExteriorRing( ogrGeometryToQgsLineString( OGR_G_GetGeometryRef( geom, 0 ) ).release() );
     441                 :        814 :   }
     442                 :            : 
     443                 :        841 :   for ( int i = 1; i < count; ++i )
     444                 :            :   {
     445                 :         27 :     polygon->addInteriorRing( ogrGeometryToQgsLineString( OGR_G_GetGeometryRef( geom, i ) ).release() );
     446                 :         27 :   }
     447                 :            : 
     448                 :        814 :   return polygon;
     449                 :        814 : }
     450                 :            : 
     451                 :         26 : std::unique_ptr< QgsMultiPolygon > ogrGeometryToQgsMultiPolygon( OGRGeometryH geom )
     452                 :            : {
     453                 :         26 :   std::unique_ptr< QgsMultiPolygon > polygon = std::make_unique< QgsMultiPolygon >();
     454                 :            : 
     455                 :         26 :   const int count = OGR_G_GetGeometryCount( geom );
     456                 :         26 :   polygon->reserve( count );
     457                 :         72 :   for ( int i = 0; i < count; ++i )
     458                 :            :   {
     459                 :         46 :     polygon->addGeometry( ogrGeometryToQgsPolygon( OGR_G_GetGeometryRef( geom, i ) ).release() );
     460                 :         46 :   }
     461                 :            : 
     462                 :         26 :   return polygon;
     463                 :         26 : }
     464                 :            : 
     465                 :       1298 : QgsWkbTypes::Type QgsOgrUtils::ogrGeometryTypeToQgsWkbType( OGRwkbGeometryType ogrGeomType )
     466                 :            : {
     467                 :       1298 :   switch ( ogrGeomType )
     468                 :            :   {
     469                 :          0 :     case wkbUnknown: return QgsWkbTypes::Type::Unknown;
     470                 :        190 :     case wkbPoint: return QgsWkbTypes::Type::Point;
     471                 :        210 :     case wkbLineString: return QgsWkbTypes::Type::LineString;
     472                 :        848 :     case wkbPolygon: return QgsWkbTypes::Type::Polygon;
     473                 :          0 :     case wkbMultiPoint: return QgsWkbTypes::Type::MultiPoint;
     474                 :         21 :     case wkbMultiLineString: return QgsWkbTypes::Type::MultiLineString;
     475                 :         29 :     case wkbMultiPolygon: return QgsWkbTypes::Type::MultiPolygon;
     476                 :          0 :     case wkbGeometryCollection: return QgsWkbTypes::Type::GeometryCollection;
     477                 :          0 :     case wkbCircularString: return QgsWkbTypes::Type::CircularString;
     478                 :          0 :     case wkbCompoundCurve: return QgsWkbTypes::Type::CompoundCurve;
     479                 :          0 :     case wkbCurvePolygon: return QgsWkbTypes::Type::CurvePolygon;
     480                 :          0 :     case wkbMultiCurve: return QgsWkbTypes::Type::MultiCurve;
     481                 :          0 :     case wkbMultiSurface: return QgsWkbTypes::Type::MultiSurface;
     482                 :          0 :     case wkbCurve: return QgsWkbTypes::Type::Unknown; // not an actual concrete type
     483                 :          0 :     case wkbSurface: return QgsWkbTypes::Type::Unknown; // not an actual concrete type
     484                 :          0 :     case wkbPolyhedralSurface: return QgsWkbTypes::Type::Unknown; // no actual matching
     485                 :          0 :     case wkbTIN: return QgsWkbTypes::Type::Unknown; // no actual matching
     486                 :          0 :     case wkbTriangle: return QgsWkbTypes::Type::Triangle;
     487                 :            : 
     488                 :          0 :     case wkbNone: return QgsWkbTypes::Type::NoGeometry;
     489                 :          0 :     case wkbLinearRing: return QgsWkbTypes::Type::LineString; // approximate match
     490                 :            : 
     491                 :          0 :     case wkbCircularStringZ: return QgsWkbTypes::Type::CircularStringZ;
     492                 :          0 :     case wkbCompoundCurveZ: return QgsWkbTypes::Type::CompoundCurveZ;
     493                 :          0 :     case wkbCurvePolygonZ: return QgsWkbTypes::Type::CurvePolygonZ;
     494                 :          0 :     case wkbMultiCurveZ: return QgsWkbTypes::Type::MultiCurveZ;
     495                 :          0 :     case wkbMultiSurfaceZ: return QgsWkbTypes::Type::MultiSurfaceZ;
     496                 :          0 :     case wkbCurveZ: return QgsWkbTypes::Type::Unknown; // not an actual concrete type
     497                 :          0 :     case wkbSurfaceZ: return QgsWkbTypes::Type::Unknown; // not an actual concrete type
     498                 :          0 :     case wkbPolyhedralSurfaceZ: return QgsWkbTypes::Type::Unknown; // no actual matching
     499                 :          0 :     case wkbTINZ: return QgsWkbTypes::Type::Unknown; // no actual matching
     500                 :          0 :     case wkbTriangleZ: return QgsWkbTypes::Type::TriangleZ;
     501                 :            : 
     502                 :          0 :     case wkbPointM: return QgsWkbTypes::Type::PointM;
     503                 :          0 :     case wkbLineStringM: return QgsWkbTypes::Type::LineStringM;
     504                 :          0 :     case wkbPolygonM: return QgsWkbTypes::Type::PolygonM;
     505                 :          0 :     case wkbMultiPointM: return QgsWkbTypes::Type::MultiPointM;
     506                 :          0 :     case wkbMultiLineStringM: return QgsWkbTypes::Type::MultiLineStringM;
     507                 :          0 :     case wkbMultiPolygonM: return QgsWkbTypes::Type::MultiPolygonM;
     508                 :          0 :     case wkbGeometryCollectionM: return QgsWkbTypes::Type::GeometryCollectionM;
     509                 :          0 :     case wkbCircularStringM: return QgsWkbTypes::Type::CircularStringM;
     510                 :          0 :     case wkbCompoundCurveM: return QgsWkbTypes::Type::CompoundCurveM;
     511                 :          0 :     case wkbCurvePolygonM: return QgsWkbTypes::Type::CurvePolygonM;
     512                 :          0 :     case wkbMultiCurveM: return QgsWkbTypes::Type::MultiCurveM;
     513                 :          0 :     case wkbMultiSurfaceM: return QgsWkbTypes::Type::MultiSurfaceM;
     514                 :          0 :     case wkbCurveM: return QgsWkbTypes::Type::Unknown; // not an actual concrete type
     515                 :          0 :     case wkbSurfaceM: return QgsWkbTypes::Type::Unknown; // not an actual concrete type
     516                 :          0 :     case wkbPolyhedralSurfaceM: return QgsWkbTypes::Type::Unknown; // no actual matching
     517                 :          0 :     case wkbTINM: return QgsWkbTypes::Type::Unknown; // no actual matching
     518                 :          0 :     case wkbTriangleM: return QgsWkbTypes::Type::TriangleM;
     519                 :            : 
     520                 :          0 :     case wkbPointZM: return QgsWkbTypes::Type::PointZM;
     521                 :          0 :     case wkbLineStringZM: return QgsWkbTypes::Type::LineStringZM;
     522                 :          0 :     case wkbPolygonZM: return QgsWkbTypes::Type::PolygonZM;
     523                 :          0 :     case wkbMultiPointZM: return QgsWkbTypes::Type::MultiPointZM;
     524                 :          0 :     case wkbMultiLineStringZM: return QgsWkbTypes::Type::MultiLineStringZM;
     525                 :          0 :     case wkbMultiPolygonZM: return QgsWkbTypes::Type::MultiPolygonZM;
     526                 :          0 :     case wkbGeometryCollectionZM: return QgsWkbTypes::Type::GeometryCollectionZM;
     527                 :          0 :     case wkbCircularStringZM: return QgsWkbTypes::Type::CircularStringZM;
     528                 :          0 :     case wkbCompoundCurveZM: return QgsWkbTypes::Type::CompoundCurveZM;
     529                 :          0 :     case wkbCurvePolygonZM: return QgsWkbTypes::Type::CurvePolygonZM;
     530                 :          0 :     case wkbMultiCurveZM: return QgsWkbTypes::Type::MultiCurveZM;
     531                 :          0 :     case wkbMultiSurfaceZM: return QgsWkbTypes::Type::MultiSurfaceZM;
     532                 :          0 :     case wkbCurveZM: return QgsWkbTypes::Type::Unknown; // not an actual concrete type
     533                 :          0 :     case wkbSurfaceZM: return QgsWkbTypes::Type::Unknown; // not an actual concrete type
     534                 :          0 :     case wkbPolyhedralSurfaceZM: return QgsWkbTypes::Type::Unknown; // no actual matching
     535                 :          0 :     case wkbTINZM: return QgsWkbTypes::Type::Unknown; // no actual matching
     536                 :          0 :     case wkbTriangleZM: return QgsWkbTypes::Type::TriangleZM;
     537                 :            : 
     538                 :          0 :     case wkbPoint25D: return QgsWkbTypes::Type::PointZ;
     539                 :          0 :     case wkbLineString25D: return QgsWkbTypes::Type::LineStringZ;
     540                 :          0 :     case wkbPolygon25D: return QgsWkbTypes::Type::PolygonZ;
     541                 :          0 :     case wkbMultiPoint25D: return QgsWkbTypes::Type::MultiPointZ;
     542                 :          0 :     case wkbMultiLineString25D: return QgsWkbTypes::Type::MultiLineStringZ;
     543                 :          0 :     case wkbMultiPolygon25D: return QgsWkbTypes::Type::MultiPolygonZ;
     544                 :          0 :     case wkbGeometryCollection25D: return QgsWkbTypes::Type::GeometryCollectionZ;
     545                 :            :   }
     546                 :            : 
     547                 :            :   // should not reach that point normally
     548                 :          0 :   return QgsWkbTypes::Type::Unknown;
     549                 :       1298 : }
     550                 :            : 
     551                 :       1101 : QgsGeometry QgsOgrUtils::ogrGeometryToQgsGeometry( OGRGeometryH geom )
     552                 :            : {
     553                 :       1101 :   if ( !geom )
     554                 :          0 :     return QgsGeometry();
     555                 :            : 
     556                 :       1101 :   const auto ogrGeomType = OGR_G_GetGeometryType( geom );
     557                 :       1101 :   QgsWkbTypes::Type wkbType = ogrGeometryTypeToQgsWkbType( ogrGeomType );
     558                 :            : 
     559                 :            :   // optimised case for some geometry classes, avoiding wkb conversion on OGR/QGIS sides
     560                 :            :   // TODO - extend to other classes!
     561                 :       1101 :   switch ( QgsWkbTypes::flatType( wkbType ) )
     562                 :            :   {
     563                 :            :     case QgsWkbTypes::Point:
     564                 :            :     {
     565                 :        133 :       return QgsGeometry( ogrGeometryToQgsPoint( geom ) );
     566                 :            :     }
     567                 :            : 
     568                 :            :     case QgsWkbTypes::MultiPoint:
     569                 :            :     {
     570                 :          0 :       return QgsGeometry( ogrGeometryToQgsMultiPoint( geom ) );
     571                 :            :     }
     572                 :            : 
     573                 :            :     case QgsWkbTypes::LineString:
     574                 :            :     {
     575                 :        153 :       return QgsGeometry( ogrGeometryToQgsLineString( geom ) );
     576                 :            :     }
     577                 :            : 
     578                 :            :     case QgsWkbTypes::MultiLineString:
     579                 :            :     {
     580                 :         21 :       return QgsGeometry( ogrGeometryToQgsMultiLineString( geom ) );
     581                 :            :     }
     582                 :            : 
     583                 :            :     case QgsWkbTypes::Polygon:
     584                 :            :     {
     585                 :        768 :       return QgsGeometry( ogrGeometryToQgsPolygon( geom ) );
     586                 :            :     }
     587                 :            : 
     588                 :            :     case QgsWkbTypes::MultiPolygon:
     589                 :            :     {
     590                 :         26 :       return QgsGeometry( ogrGeometryToQgsMultiPolygon( geom ) );
     591                 :            :     }
     592                 :            : 
     593                 :            :     default:
     594                 :          0 :       break;
     595                 :            :   }
     596                 :            : 
     597                 :            :   // Fallback to inefficient WKB conversions
     598                 :            : 
     599                 :          0 :   if ( wkbFlatten( wkbType ) == wkbGeometryCollection )
     600                 :            :   {
     601                 :            :     // Shapefile MultiPatch can be reported as GeometryCollectionZ of TINZ
     602                 :          0 :     if ( OGR_G_GetGeometryCount( geom ) >= 1 &&
     603                 :          0 :          wkbFlatten( OGR_G_GetGeometryType( OGR_G_GetGeometryRef( geom, 0 ) ) ) == wkbTIN )
     604                 :            :     {
     605                 :          0 :       auto newGeom = OGR_G_ForceToMultiPolygon( OGR_G_Clone( geom ) );
     606                 :          0 :       auto ret = ogrGeometryToQgsGeometry( newGeom );
     607                 :          0 :       OGR_G_DestroyGeometry( newGeom );
     608                 :          0 :       return ret;
     609                 :          0 :     }
     610                 :          0 :   }
     611                 :            : 
     612                 :            :   // get the wkb representation
     613                 :          0 :   int memorySize = OGR_G_WkbSize( geom );
     614                 :          0 :   unsigned char *wkb = new unsigned char[memorySize];
     615                 :          0 :   OGR_G_ExportToWkb( geom, static_cast<OGRwkbByteOrder>( QgsApplication::endian() ), wkb );
     616                 :            : 
     617                 :            :   // Read original geometry type
     618                 :            :   uint32_t origGeomType;
     619                 :          0 :   memcpy( &origGeomType, wkb + 1, sizeof( uint32_t ) );
     620                 :          0 :   bool hasZ = ( origGeomType >= 1000 && origGeomType < 2000 ) || ( origGeomType >= 3000 && origGeomType < 4000 );
     621                 :          0 :   bool hasM = ( origGeomType >= 2000 && origGeomType < 3000 ) || ( origGeomType >= 3000 && origGeomType < 4000 );
     622                 :            : 
     623                 :            :   // PolyhedralSurface and TINs are not supported, map them to multipolygons...
     624                 :          0 :   if ( origGeomType % 1000 == 16 ) // is TIN, TINZ, TINM or TINZM
     625                 :            :   {
     626                 :            :     // TIN has the same wkb layout as a multipolygon, just need to overwrite the geom types...
     627                 :          0 :     int nDims = 2 + hasZ + hasM;
     628                 :          0 :     uint32_t newMultiType = static_cast<uint32_t>( QgsWkbTypes::zmType( QgsWkbTypes::MultiPolygon, hasZ, hasM ) );
     629                 :          0 :     uint32_t newSingleType = static_cast<uint32_t>( QgsWkbTypes::zmType( QgsWkbTypes::Polygon, hasZ, hasM ) );
     630                 :          0 :     unsigned char *wkbptr = wkb;
     631                 :            : 
     632                 :            :     // Endianness
     633                 :          0 :     wkbptr += 1;
     634                 :            : 
     635                 :            :     // Overwrite geom type
     636                 :          0 :     memcpy( wkbptr, &newMultiType, sizeof( uint32_t ) );
     637                 :          0 :     wkbptr += 4;
     638                 :            : 
     639                 :            :     // Geom count
     640                 :            :     uint32_t numGeoms;
     641                 :          0 :     memcpy( &numGeoms, wkb + 5, sizeof( uint32_t ) );
     642                 :          0 :     wkbptr += 4;
     643                 :            : 
     644                 :            :     // For each part, overwrite the geometry type to polygon (Z|M)
     645                 :          0 :     for ( uint32_t i = 0; i < numGeoms; ++i )
     646                 :            :     {
     647                 :            :       // Endianness
     648                 :          0 :       wkbptr += 1;
     649                 :            : 
     650                 :            :       // Overwrite geom type
     651                 :          0 :       memcpy( wkbptr, &newSingleType, sizeof( uint32_t ) );
     652                 :          0 :       wkbptr += sizeof( uint32_t );
     653                 :            : 
     654                 :            :       // skip coordinates
     655                 :            :       uint32_t nRings;
     656                 :          0 :       memcpy( &nRings, wkbptr, sizeof( uint32_t ) );
     657                 :          0 :       wkbptr += sizeof( uint32_t );
     658                 :            : 
     659                 :          0 :       for ( uint32_t j = 0; j < nRings; ++j )
     660                 :            :       {
     661                 :            :         uint32_t nPoints;
     662                 :          0 :         memcpy( &nPoints, wkbptr, sizeof( uint32_t ) );
     663                 :          0 :         wkbptr += sizeof( uint32_t ) + sizeof( double ) * nDims * nPoints;
     664                 :          0 :       }
     665                 :          0 :     }
     666                 :          0 :   }
     667                 :          0 :   else if ( origGeomType % 1000 == 15 ) // PolyhedralSurface, PolyhedralSurfaceZ, PolyhedralSurfaceM or PolyhedralSurfaceZM
     668                 :            :   {
     669                 :            :     // PolyhedralSurface has the same wkb layout as a MultiPolygon, just need to overwrite the geom type...
     670                 :          0 :     uint32_t newType = static_cast<uint32_t>( QgsWkbTypes::zmType( QgsWkbTypes::MultiPolygon, hasZ, hasM ) );
     671                 :            :     // Overwrite geom type
     672                 :          0 :     memcpy( wkb + 1, &newType, sizeof( uint32_t ) );
     673                 :          0 :   }
     674                 :            : 
     675                 :          0 :   QgsGeometry g;
     676                 :          0 :   g.fromWkb( wkb, memorySize );
     677                 :          0 :   return g;
     678                 :       1101 : }
     679                 :            : 
     680                 :          0 : QgsFeatureList QgsOgrUtils::stringToFeatureList( const QString &string, const QgsFields &fields, QTextCodec *encoding )
     681                 :            : {
     682                 :          0 :   QgsFeatureList features;
     683                 :          0 :   if ( string.isEmpty() )
     684                 :          0 :     return features;
     685                 :            : 
     686                 :          0 :   QString randomFileName = QStringLiteral( "/vsimem/%1" ).arg( QUuid::createUuid().toString() );
     687                 :            : 
     688                 :            :   // create memory file system object from string buffer
     689                 :          0 :   QByteArray ba = string.toUtf8();
     690                 :          0 :   VSIFCloseL( VSIFileFromMemBuffer( randomFileName.toUtf8().constData(), reinterpret_cast< GByte * >( ba.data() ),
     691                 :          0 :                                     static_cast< vsi_l_offset >( ba.size() ), FALSE ) );
     692                 :            : 
     693                 :          0 :   gdal::ogr_datasource_unique_ptr hDS( OGROpen( randomFileName.toUtf8().constData(), false, nullptr ) );
     694                 :          0 :   if ( !hDS )
     695                 :            :   {
     696                 :          0 :     VSIUnlink( randomFileName.toUtf8().constData() );
     697                 :          0 :     return features;
     698                 :            :   }
     699                 :            : 
     700                 :          0 :   OGRLayerH ogrLayer = OGR_DS_GetLayer( hDS.get(), 0 );
     701                 :          0 :   if ( !ogrLayer )
     702                 :            :   {
     703                 :          0 :     hDS.reset();
     704                 :          0 :     VSIUnlink( randomFileName.toUtf8().constData() );
     705                 :          0 :     return features;
     706                 :            :   }
     707                 :            : 
     708                 :          0 :   gdal::ogr_feature_unique_ptr oFeat;
     709                 :          0 :   while ( oFeat.reset( OGR_L_GetNextFeature( ogrLayer ) ), oFeat )
     710                 :            :   {
     711                 :          0 :     QgsFeature feat = readOgrFeature( oFeat.get(), fields, encoding );
     712                 :          0 :     if ( feat.isValid() )
     713                 :          0 :       features << feat;
     714                 :          0 :   }
     715                 :            : 
     716                 :          0 :   hDS.reset();
     717                 :          0 :   VSIUnlink( randomFileName.toUtf8().constData() );
     718                 :            : 
     719                 :          0 :   return features;
     720                 :          0 : }
     721                 :            : 
     722                 :          0 : QgsFields QgsOgrUtils::stringToFields( const QString &string, QTextCodec *encoding )
     723                 :            : {
     724                 :          0 :   QgsFields fields;
     725                 :          0 :   if ( string.isEmpty() )
     726                 :          0 :     return fields;
     727                 :            : 
     728                 :          0 :   QString randomFileName = QStringLiteral( "/vsimem/%1" ).arg( QUuid::createUuid().toString() );
     729                 :            : 
     730                 :            :   // create memory file system object from buffer
     731                 :          0 :   QByteArray ba = string.toUtf8();
     732                 :          0 :   VSIFCloseL( VSIFileFromMemBuffer( randomFileName.toUtf8().constData(), reinterpret_cast< GByte * >( ba.data() ),
     733                 :          0 :                                     static_cast< vsi_l_offset >( ba.size() ), FALSE ) );
     734                 :            : 
     735                 :          0 :   gdal::ogr_datasource_unique_ptr hDS( OGROpen( randomFileName.toUtf8().constData(), false, nullptr ) );
     736                 :          0 :   if ( !hDS )
     737                 :            :   {
     738                 :          0 :     VSIUnlink( randomFileName.toUtf8().constData() );
     739                 :          0 :     return fields;
     740                 :            :   }
     741                 :            : 
     742                 :          0 :   OGRLayerH ogrLayer = OGR_DS_GetLayer( hDS.get(), 0 );
     743                 :          0 :   if ( !ogrLayer )
     744                 :            :   {
     745                 :          0 :     hDS.reset();
     746                 :          0 :     VSIUnlink( randomFileName.toUtf8().constData() );
     747                 :          0 :     return fields;
     748                 :            :   }
     749                 :            : 
     750                 :          0 :   gdal::ogr_feature_unique_ptr oFeat;
     751                 :            :   //read in the first feature only
     752                 :          0 :   if ( oFeat.reset( OGR_L_GetNextFeature( ogrLayer ) ), oFeat )
     753                 :            :   {
     754                 :          0 :     fields = readOgrFields( oFeat.get(), encoding );
     755                 :          0 :   }
     756                 :            : 
     757                 :          0 :   hDS.reset();
     758                 :          0 :   VSIUnlink( randomFileName.toUtf8().constData() );
     759                 :          0 :   return fields;
     760                 :          0 : }
     761                 :            : 
     762                 :          0 : QStringList QgsOgrUtils::cStringListToQStringList( char **stringList )
     763                 :            : {
     764                 :          0 :   QStringList strings;
     765                 :            : 
     766                 :            :   // presume null terminated string list
     767                 :          0 :   for ( qgssize i = 0; stringList[i]; ++i )
     768                 :            :   {
     769                 :          0 :     strings.append( QString::fromUtf8( stringList[i] ) );
     770                 :          0 :   }
     771                 :            : 
     772                 :          0 :   return strings;
     773                 :          0 : }
     774                 :            : 
     775                 :          3 : QString QgsOgrUtils::OGRSpatialReferenceToWkt( OGRSpatialReferenceH srs )
     776                 :            : {
     777                 :          3 :   if ( !srs )
     778                 :          0 :     return QString();
     779                 :            : 
     780                 :          3 :   char *pszWkt = nullptr;
     781                 :          6 :   const QByteArray multiLineOption = QStringLiteral( "MULTILINE=NO" ).toLocal8Bit();
     782                 :          6 :   const QByteArray formatOption = QStringLiteral( "FORMAT=WKT2" ).toLocal8Bit();
     783                 :          3 :   const char *const options[] = {multiLineOption.constData(), formatOption.constData(), nullptr};
     784                 :          3 :   OSRExportToWktEx( srs, &pszWkt, options );
     785                 :            : 
     786                 :          3 :   const QString res( pszWkt );
     787                 :          3 :   CPLFree( pszWkt );
     788                 :          3 :   return res;
     789                 :          3 : }
     790                 :            : 
     791                 :          3 : QgsCoordinateReferenceSystem QgsOgrUtils::OGRSpatialReferenceToCrs( OGRSpatialReferenceH srs )
     792                 :            : {
     793                 :          3 :   const QString wkt = OGRSpatialReferenceToWkt( srs );
     794                 :          3 :   if ( wkt.isEmpty() )
     795                 :          0 :     return QgsCoordinateReferenceSystem();
     796                 :            : 
     797                 :          3 :   return QgsCoordinateReferenceSystem::fromWkt( wkt );
     798                 :          3 : }
     799                 :            : 
     800                 :          0 : QString QgsOgrUtils::readShapefileEncoding( const QString &path )
     801                 :            : {
     802                 :          0 :   const QString cpgEncoding = readShapefileEncodingFromCpg( path );
     803                 :          0 :   if ( !cpgEncoding.isEmpty() )
     804                 :          0 :     return cpgEncoding;
     805                 :            : 
     806                 :          0 :   return readShapefileEncodingFromLdid( path );
     807                 :          0 : }
     808                 :            : 
     809                 :          0 : QString QgsOgrUtils::readShapefileEncodingFromCpg( const QString &path )
     810                 :            : {
     811                 :            : #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,1,0)
     812                 :          0 :   QString errCause;
     813                 :          0 :   QgsOgrLayerUniquePtr layer = QgsOgrProviderUtils::getLayer( path, false, QStringList(), 0, errCause, false );
     814                 :          0 :   return layer ? layer->GetMetadataItem( QStringLiteral( "ENCODING_FROM_CPG" ), QStringLiteral( "SHAPEFILE" ) ) : QString();
     815                 :            : #else
     816                 :            :   if ( !QFileInfo::exists( path ) )
     817                 :            :     return QString();
     818                 :            : 
     819                 :            :   // first try to read cpg file, if present
     820                 :            :   const QFileInfo fi( path );
     821                 :            :   const QString baseName = fi.completeBaseName();
     822                 :            :   const QString cpgPath = fi.dir().filePath( QStringLiteral( "%1.%2" ).arg( baseName, fi.suffix() == QLatin1String( "SHP" ) ? QStringLiteral( "CPG" ) : QStringLiteral( "cpg" ) ) );
     823                 :            :   if ( QFile::exists( cpgPath ) )
     824                 :            :   {
     825                 :            :     QFile cpgFile( cpgPath );
     826                 :            :     if ( cpgFile.open( QIODevice::ReadOnly ) )
     827                 :            :     {
     828                 :            :       QTextStream cpgStream( &cpgFile );
     829                 :            :       const QString cpgString = cpgStream.readLine();
     830                 :            :       cpgFile.close();
     831                 :            : 
     832                 :            :       if ( !cpgString.isEmpty() )
     833                 :            :       {
     834                 :            :         // from OGRShapeLayer::ConvertCodePage
     835                 :            :         // https://github.com/OSGeo/gdal/blob/master/gdal/ogr/ogrsf_frmts/shape/ogrshapelayer.cpp#L342
     836                 :            :         bool ok = false;
     837                 :            :         int cpgCodePage = cpgString.toInt( &ok );
     838                 :            :         if ( ok && ( ( cpgCodePage >= 437 && cpgCodePage <= 950 )
     839                 :            :                      || ( cpgCodePage >= 1250 && cpgCodePage <= 1258 ) ) )
     840                 :            :         {
     841                 :            :           return QStringLiteral( "CP%1" ).arg( cpgCodePage );
     842                 :            :         }
     843                 :            :         else if ( cpgString.startsWith( QLatin1String( "8859" ) ) )
     844                 :            :         {
     845                 :            :           if ( cpgString.length() > 4 && cpgString.at( 4 ) == '-' )
     846                 :            :             return QStringLiteral( "ISO-8859-%1" ).arg( cpgString.mid( 5 ) );
     847                 :            :           else
     848                 :            :             return QStringLiteral( "ISO-8859-%1" ).arg( cpgString.mid( 4 ) );
     849                 :            :         }
     850                 :            :         else if ( cpgString.startsWith( QLatin1String( "UTF-8" ), Qt::CaseInsensitive ) ||
     851                 :            :                   cpgString.startsWith( QLatin1String( "UTF8" ), Qt::CaseInsensitive ) )
     852                 :            :           return QStringLiteral( "UTF-8" );
     853                 :            :         else if ( cpgString.startsWith( QLatin1String( "ANSI 1251" ), Qt::CaseInsensitive ) )
     854                 :            :           return QStringLiteral( "CP1251" );
     855                 :            : 
     856                 :            :         return cpgString;
     857                 :            :       }
     858                 :            :     }
     859                 :            :   }
     860                 :            : 
     861                 :            :   return QString();
     862                 :            : #endif
     863                 :          0 : }
     864                 :            : 
     865                 :          0 : QString QgsOgrUtils::readShapefileEncodingFromLdid( const QString &path )
     866                 :            : {
     867                 :            : #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,1,0)
     868                 :          0 :   QString errCause;
     869                 :          0 :   QgsOgrLayerUniquePtr layer = QgsOgrProviderUtils::getLayer( path, false, QStringList(), 0, errCause, false );
     870                 :          0 :   return layer ? layer->GetMetadataItem( QStringLiteral( "ENCODING_FROM_LDID" ), QStringLiteral( "SHAPEFILE" ) ) : QString();
     871                 :            : #else
     872                 :            :   // from OGRShapeLayer::ConvertCodePage
     873                 :            :   // https://github.com/OSGeo/gdal/blob/master/gdal/ogr/ogrsf_frmts/shape/ogrshapelayer.cpp#L342
     874                 :            : 
     875                 :            :   if ( !QFileInfo::exists( path ) )
     876                 :            :     return QString();
     877                 :            : 
     878                 :            :   // first try to read cpg file, if present
     879                 :            :   const QFileInfo fi( path );
     880                 :            :   const QString baseName = fi.completeBaseName();
     881                 :            : 
     882                 :            :   // fallback to LDID value, read from DBF file
     883                 :            :   const QString dbfPath = fi.dir().filePath( QStringLiteral( "%1.%2" ).arg( baseName, fi.suffix() == QLatin1String( "SHP" ) ? QStringLiteral( "DBF" ) : QStringLiteral( "dbf" ) ) );
     884                 :            :   if ( QFile::exists( dbfPath ) )
     885                 :            :   {
     886                 :            :     QFile dbfFile( dbfPath );
     887                 :            :     if ( dbfFile.open( QIODevice::ReadOnly ) )
     888                 :            :     {
     889                 :            :       dbfFile.read( 29 );
     890                 :            :       QDataStream dbfIn( &dbfFile );
     891                 :            :       dbfIn.setByteOrder( QDataStream::LittleEndian );
     892                 :            :       quint8 ldid;
     893                 :            :       dbfIn >> ldid;
     894                 :            :       dbfFile.close();
     895                 :            : 
     896                 :            :       int nCP = -1;  // Windows code page.
     897                 :            : 
     898                 :            :       // http://www.autopark.ru/ASBProgrammerGuide/DBFSTRUC.HTM
     899                 :            :       switch ( ldid )
     900                 :            :       {
     901                 :            :         case 1: nCP = 437;      break;
     902                 :            :         case 2: nCP = 850;      break;
     903                 :            :         case 3: nCP = 1252;     break;
     904                 :            :         case 4: nCP = 10000;    break;
     905                 :            :         case 8: nCP = 865;      break;
     906                 :            :         case 10: nCP = 850;     break;
     907                 :            :         case 11: nCP = 437;     break;
     908                 :            :         case 13: nCP = 437;     break;
     909                 :            :         case 14: nCP = 850;     break;
     910                 :            :         case 15: nCP = 437;     break;
     911                 :            :         case 16: nCP = 850;     break;
     912                 :            :         case 17: nCP = 437;     break;
     913                 :            :         case 18: nCP = 850;     break;
     914                 :            :         case 19: nCP = 932;     break;
     915                 :            :         case 20: nCP = 850;     break;
     916                 :            :         case 21: nCP = 437;     break;
     917                 :            :         case 22: nCP = 850;     break;
     918                 :            :         case 23: nCP = 865;     break;
     919                 :            :         case 24: nCP = 437;     break;
     920                 :            :         case 25: nCP = 437;     break;
     921                 :            :         case 26: nCP = 850;     break;
     922                 :            :         case 27: nCP = 437;     break;
     923                 :            :         case 28: nCP = 863;     break;
     924                 :            :         case 29: nCP = 850;     break;
     925                 :            :         case 31: nCP = 852;     break;
     926                 :            :         case 34: nCP = 852;     break;
     927                 :            :         case 35: nCP = 852;     break;
     928                 :            :         case 36: nCP = 860;     break;
     929                 :            :         case 37: nCP = 850;     break;
     930                 :            :         case 38: nCP = 866;     break;
     931                 :            :         case 55: nCP = 850;     break;
     932                 :            :         case 64: nCP = 852;     break;
     933                 :            :         case 77: nCP = 936;     break;
     934                 :            :         case 78: nCP = 949;     break;
     935                 :            :         case 79: nCP = 950;     break;
     936                 :            :         case 80: nCP = 874;     break;
     937                 :            :         case 87: return QStringLiteral( "ISO-8859-1" );
     938                 :            :         case 88: nCP = 1252;     break;
     939                 :            :         case 89: nCP = 1252;     break;
     940                 :            :         case 100: nCP = 852;     break;
     941                 :            :         case 101: nCP = 866;     break;
     942                 :            :         case 102: nCP = 865;     break;
     943                 :            :         case 103: nCP = 861;     break;
     944                 :            :         case 104: nCP = 895;     break;
     945                 :            :         case 105: nCP = 620;     break;
     946                 :            :         case 106: nCP = 737;     break;
     947                 :            :         case 107: nCP = 857;     break;
     948                 :            :         case 108: nCP = 863;     break;
     949                 :            :         case 120: nCP = 950;     break;
     950                 :            :         case 121: nCP = 949;     break;
     951                 :            :         case 122: nCP = 936;     break;
     952                 :            :         case 123: nCP = 932;     break;
     953                 :            :         case 124: nCP = 874;     break;
     954                 :            :         case 134: nCP = 737;     break;
     955                 :            :         case 135: nCP = 852;     break;
     956                 :            :         case 136: nCP = 857;     break;
     957                 :            :         case 150: nCP = 10007;   break;
     958                 :            :         case 151: nCP = 10029;   break;
     959                 :            :         case 200: nCP = 1250;    break;
     960                 :            :         case 201: nCP = 1251;    break;
     961                 :            :         case 202: nCP = 1254;    break;
     962                 :            :         case 203: nCP = 1253;    break;
     963                 :            :         case 204: nCP = 1257;    break;
     964                 :            :         default: break;
     965                 :            :       }
     966                 :            : 
     967                 :            :       if ( nCP != -1 )
     968                 :            :       {
     969                 :            :         return QStringLiteral( "CP%1" ).arg( nCP );
     970                 :            :       }
     971                 :            :     }
     972                 :            :   }
     973                 :            :   return QString();
     974                 :            : #endif
     975                 :          0 : }
     976                 :            : 
     977                 :          0 : QVariantMap QgsOgrUtils::parseStyleString( const QString &string )
     978                 :            : {
     979                 :          0 :   QVariantMap styles;
     980                 :            : 
     981                 :          0 :   char **papszStyleString = CSLTokenizeString2( string.toUtf8().constData(), ";",
     982                 :            :                             CSLT_HONOURSTRINGS
     983                 :            :                             | CSLT_PRESERVEQUOTES
     984                 :            :                             | CSLT_PRESERVEESCAPES );
     985                 :          0 :   for ( int i = 0; papszStyleString[i] != nullptr; ++i )
     986                 :            :   {
     987                 :            :     // style string format is:
     988                 :            :     // <tool_name>([<tool_param>[,<tool_param>[,...]]])
     989                 :            : 
     990                 :            :     // first extract tool name
     991                 :          0 :     const thread_local QRegularExpression sToolPartRx( QStringLiteral( "^(.*?)\\((.*)\\)$" ) );
     992                 :          0 :     const QString stylePart( papszStyleString[i] );
     993                 :          0 :     const QRegularExpressionMatch match = sToolPartRx.match( stylePart );
     994                 :          0 :     if ( !match.hasMatch() )
     995                 :          0 :       continue;
     996                 :            : 
     997                 :          0 :     const QString tool = match.captured( 1 );
     998                 :          0 :     const QString params = match.captured( 2 );
     999                 :            : 
    1000                 :          0 :     char **papszTokens = CSLTokenizeString2( params.toUtf8().constData(), ",", CSLT_HONOURSTRINGS
    1001                 :            :                          | CSLT_PRESERVEESCAPES );
    1002                 :            : 
    1003                 :          0 :     QVariantMap toolParts;
    1004                 :          0 :     const thread_local QRegularExpression sToolParamRx( QStringLiteral( "^(.*?):(.*)$" ) );
    1005                 :          0 :     for ( int j = 0; papszTokens[j] != nullptr; ++j )
    1006                 :            :     {
    1007                 :          0 :       const QString toolPart( papszTokens[j] );
    1008                 :          0 :       const QRegularExpressionMatch toolMatch = sToolParamRx.match( toolPart );
    1009                 :          0 :       if ( !match.hasMatch() )
    1010                 :          0 :         continue;
    1011                 :            : 
    1012                 :            :       // note we always convert the keys to lowercase, just to be safe...
    1013                 :          0 :       toolParts.insert( toolMatch.captured( 1 ).toLower(), toolMatch.captured( 2 ) );
    1014                 :          0 :     }
    1015                 :          0 :     CSLDestroy( papszTokens );
    1016                 :            : 
    1017                 :            :     // note we always convert the keys to lowercase, just to be safe...
    1018                 :          0 :     styles.insert( tool.toLower(), toolParts );
    1019                 :          0 :   }
    1020                 :          0 :   CSLDestroy( papszStyleString );
    1021                 :          0 :   return styles;
    1022                 :          0 : }
    1023                 :            : 
    1024                 :          0 : std::unique_ptr<QgsSymbol> QgsOgrUtils::symbolFromStyleString( const QString &string, QgsSymbol::SymbolType type )
    1025                 :            : {
    1026                 :          0 :   const QVariantMap styles = parseStyleString( string );
    1027                 :            : 
    1028                 :          0 :   auto convertSize = []( const QString & size, double & value, QgsUnitTypes::RenderUnit & unit )->bool
    1029                 :            :   {
    1030                 :          0 :     const thread_local QRegularExpression sUnitRx = QRegularExpression( QStringLiteral( "^([\\d\\.]+)(g|px|pt|mm|cm|in)$" ) );
    1031                 :          0 :     const QRegularExpressionMatch match = sUnitRx.match( size );
    1032                 :          0 :     if ( match.hasMatch() )
    1033                 :            :     {
    1034                 :          0 :       value = match.captured( 1 ).toDouble();
    1035                 :          0 :       const QString unitString = match.captured( 2 );
    1036                 :          0 :       if ( unitString.compare( QLatin1String( "px" ), Qt::CaseInsensitive ) == 0 )
    1037                 :            :       {
    1038                 :            :         // pixels are a poor unit choice for QGIS -- they render badly in hidpi layouts. Convert to points instead, using
    1039                 :            :         // a 96 dpi conversion
    1040                 :            :         static constexpr double PT_TO_INCHES_FACTOR = 1 / 72.0;
    1041                 :            :         static constexpr double PX_TO_PT_FACTOR = 1 / ( 96.0 * PT_TO_INCHES_FACTOR );
    1042                 :          0 :         unit = QgsUnitTypes::RenderPoints;
    1043                 :          0 :         value *= PX_TO_PT_FACTOR;
    1044                 :          0 :         return true;
    1045                 :            :       }
    1046                 :          0 :       else if ( unitString.compare( QLatin1String( "pt" ), Qt::CaseInsensitive ) == 0 )
    1047                 :            :       {
    1048                 :          0 :         unit = QgsUnitTypes::RenderPoints;
    1049                 :          0 :         return true;
    1050                 :            :       }
    1051                 :          0 :       else if ( unitString.compare( QLatin1String( "mm" ), Qt::CaseInsensitive ) == 0 )
    1052                 :            :       {
    1053                 :          0 :         unit = QgsUnitTypes::RenderMillimeters;
    1054                 :          0 :         return true;
    1055                 :            :       }
    1056                 :          0 :       else if ( unitString.compare( QLatin1String( "cm" ), Qt::CaseInsensitive ) == 0 )
    1057                 :            :       {
    1058                 :          0 :         value *= 10;
    1059                 :          0 :         unit = QgsUnitTypes::RenderMillimeters;
    1060                 :          0 :         return true;
    1061                 :            :       }
    1062                 :          0 :       else if ( unitString.compare( QLatin1String( "in" ), Qt::CaseInsensitive ) == 0 )
    1063                 :            :       {
    1064                 :          0 :         unit = QgsUnitTypes::RenderInches;
    1065                 :          0 :         return true;
    1066                 :            :       }
    1067                 :          0 :       else if ( unitString.compare( QLatin1String( "g" ), Qt::CaseInsensitive ) == 0 )
    1068                 :            :       {
    1069                 :          0 :         unit = QgsUnitTypes::RenderMapUnits;
    1070                 :          0 :         return true;
    1071                 :            :       }
    1072                 :          0 :       QgsDebugMsg( QStringLiteral( "Unknown unit %1" ).arg( unitString ) );
    1073                 :          0 :     }
    1074                 :            :     else
    1075                 :            :     {
    1076                 :          0 :       QgsDebugMsg( QStringLiteral( "Could not parse style size %1" ).arg( size ) );
    1077                 :            :     }
    1078                 :          0 :     return false;
    1079                 :          0 :   };
    1080                 :            : 
    1081                 :          0 :   auto convertColor = []( const QString & string ) -> QColor
    1082                 :            :   {
    1083                 :          0 :     const thread_local QRegularExpression sColorWithAlphaRx = QRegularExpression( QStringLiteral( "^#([0-9a-fA-F]{6})([0-9a-fA-F]{2})$" ) );
    1084                 :          0 :     const QRegularExpressionMatch match = sColorWithAlphaRx.match( string );
    1085                 :          0 :     if ( match.hasMatch() )
    1086                 :            :     {
    1087                 :            :       // need to convert #RRGGBBAA to #AARRGGBB for QColor
    1088                 :          0 :       return QColor( QStringLiteral( "#%1%2" ).arg( match.captured( 2 ), match.captured( 1 ) ) );
    1089                 :            :     }
    1090                 :            :     else
    1091                 :            :     {
    1092                 :          0 :       return QColor( string );
    1093                 :            :     }
    1094                 :          0 :   };
    1095                 :            : 
    1096                 :          0 :   if ( type == QgsSymbol::Line && styles.contains( QStringLiteral( "pen" ) ) )
    1097                 :            :   {
    1098                 :            :     // line symbol type
    1099                 :          0 :     const QVariantMap lineStyle = styles.value( QStringLiteral( "pen" ) ).toMap();
    1100                 :          0 :     QColor color = convertColor( lineStyle.value( QStringLiteral( "c" ), QStringLiteral( "#000000" ) ).toString() );
    1101                 :            : 
    1102                 :          0 :     double lineWidth = DEFAULT_SIMPLELINE_WIDTH;
    1103                 :          0 :     QgsUnitTypes::RenderUnit lineWidthUnit = QgsUnitTypes::RenderMillimeters;
    1104                 :          0 :     convertSize( lineStyle.value( QStringLiteral( "w" ) ).toString(), lineWidth, lineWidthUnit );
    1105                 :            : 
    1106                 :          0 :     std::unique_ptr< QgsSimpleLineSymbolLayer > simpleLine = std::make_unique< QgsSimpleLineSymbolLayer >( color, lineWidth );
    1107                 :          0 :     simpleLine->setWidthUnit( lineWidthUnit );
    1108                 :            : 
    1109                 :            :     // pattern
    1110                 :          0 :     const QString pattern = lineStyle.value( QStringLiteral( "p" ) ).toString();
    1111                 :          0 :     if ( !pattern.isEmpty() )
    1112                 :            :     {
    1113                 :          0 :       const thread_local QRegularExpression sPatternUnitRx = QRegularExpression( QStringLiteral( "^([\\d\\.\\s]+)(g|px|pt|mm|cm|in)$" ) );
    1114                 :          0 :       const QRegularExpressionMatch match = sPatternUnitRx.match( pattern );
    1115                 :          0 :       if ( match.hasMatch() )
    1116                 :            :       {
    1117                 :          0 :         const QStringList patternValues = match.captured( 1 ).split( ' ' );
    1118                 :          0 :         QVector< qreal > dashPattern;
    1119                 :          0 :         QgsUnitTypes::RenderUnit patternUnits = QgsUnitTypes::RenderMillimeters;
    1120                 :          0 :         for ( const QString &val : patternValues )
    1121                 :            :         {
    1122                 :            :           double length;
    1123                 :          0 :           convertSize( val + match.captured( 2 ), length, patternUnits );
    1124                 :          0 :           dashPattern.push_back( length * lineWidth * 2 );
    1125                 :            :         }
    1126                 :            : 
    1127                 :          0 :         simpleLine->setCustomDashVector( dashPattern );
    1128                 :          0 :         simpleLine->setCustomDashPatternUnit( patternUnits );
    1129                 :          0 :         simpleLine->setUseCustomDashPattern( true );
    1130                 :          0 :       }
    1131                 :          0 :     }
    1132                 :            : 
    1133                 :          0 :     Qt::PenCapStyle capStyle = Qt::FlatCap;
    1134                 :          0 :     Qt::PenJoinStyle joinStyle = Qt::MiterJoin;
    1135                 :            :     // workaround https://github.com/OSGeo/gdal/pull/3509 in older GDAL versions
    1136                 :          0 :     const QString id = lineStyle.value( QStringLiteral( "id" ) ).toString();
    1137                 :          0 :     if ( id.contains( QLatin1String( "mapinfo-pen" ), Qt::CaseInsensitive ) )
    1138                 :            :     {
    1139                 :            :       // MapInfo renders all lines using a round pen cap and round pen join
    1140                 :            :       // which are not the default values for OGR pen cap/join styles. So we need to explicitly
    1141                 :            :       // override the OGR default values here on older GDAL versions
    1142                 :          0 :       capStyle = Qt::RoundCap;
    1143                 :          0 :       joinStyle = Qt::RoundJoin;
    1144                 :          0 :     }
    1145                 :            : 
    1146                 :            :     // pen cap
    1147                 :          0 :     const QString penCap = lineStyle.value( QStringLiteral( "cap" ) ).toString();
    1148                 :          0 :     if ( penCap.compare( QLatin1String( "b" ), Qt::CaseInsensitive ) == 0 )
    1149                 :            :     {
    1150                 :          0 :       capStyle = Qt::FlatCap;
    1151                 :          0 :     }
    1152                 :          0 :     else if ( penCap.compare( QLatin1String( "r" ), Qt::CaseInsensitive ) == 0 )
    1153                 :            :     {
    1154                 :          0 :       capStyle = Qt::RoundCap;
    1155                 :          0 :     }
    1156                 :          0 :     else if ( penCap.compare( QLatin1String( "p" ), Qt::CaseInsensitive ) == 0 )
    1157                 :            :     {
    1158                 :          0 :       capStyle = Qt::SquareCap;
    1159                 :          0 :     }
    1160                 :          0 :     simpleLine->setPenCapStyle( capStyle );
    1161                 :            : 
    1162                 :            :     // pen join
    1163                 :          0 :     const QString penJoin = lineStyle.value( QStringLiteral( "j" ) ).toString();
    1164                 :          0 :     if ( penJoin.compare( QLatin1String( "m" ), Qt::CaseInsensitive ) == 0 )
    1165                 :            :     {
    1166                 :          0 :       joinStyle = Qt::MiterJoin;
    1167                 :          0 :     }
    1168                 :          0 :     else if ( penJoin.compare( QLatin1String( "r" ), Qt::CaseInsensitive ) == 0 )
    1169                 :            :     {
    1170                 :          0 :       joinStyle = Qt::RoundJoin;
    1171                 :          0 :     }
    1172                 :          0 :     else if ( penJoin.compare( QLatin1String( "b" ), Qt::CaseInsensitive ) == 0 )
    1173                 :            :     {
    1174                 :          0 :       joinStyle = Qt::BevelJoin;
    1175                 :          0 :     }
    1176                 :          0 :     simpleLine->setPenJoinStyle( joinStyle );
    1177                 :            : 
    1178                 :          0 :     const QString priority = lineStyle.value( QStringLiteral( "l" ) ).toString();
    1179                 :          0 :     if ( !priority.isEmpty() )
    1180                 :            :     {
    1181                 :          0 :       simpleLine->setRenderingPass( priority.toInt() );
    1182                 :          0 :     }
    1183                 :          0 :     return std::make_unique< QgsLineSymbol >( QgsSymbolLayerList() << simpleLine.release() );
    1184                 :          0 :   }
    1185                 :          0 :   return nullptr;
    1186                 :          0 : }

Generated by: LCOV version 1.14