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

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :   qgsellipsoidutils.cpp
       3                 :            :  ----------------------
       4                 :            :   Date                 : April 2017
       5                 :            :   Copyright            : (C) 2017 by 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 "qgsellipsoidutils.h"
      17                 :            : #include "qgsapplication.h"
      18                 :            : #include "qgslogger.h"
      19                 :            : #include "qgsmessagelog.h"
      20                 :            : #include <sqlite3.h>
      21                 :            : #include <QCollator>
      22                 :            : #include "qgsprojutils.h"
      23                 :            : #include "qgsreadwritelocker.h"
      24                 :            : #include "qgsruntimeprofiler.h"
      25                 :            : 
      26                 :            : #include <proj.h>
      27                 :            : #include <mutex>
      28                 :            : 
      29                 :         23 : Q_GLOBAL_STATIC( QReadWriteLock, sEllipsoidCacheLock )
      30                 :            : typedef QHash< QString, QgsEllipsoidUtils::EllipsoidParameters > EllipsoidParamCache;
      31                 :         20 : Q_GLOBAL_STATIC( EllipsoidParamCache, sEllipsoidCache )
      32                 :            : 
      33                 :         23 : Q_GLOBAL_STATIC( QReadWriteLock, sDefinitionCacheLock );
      34                 :            : typedef QList< QgsEllipsoidUtils::EllipsoidDefinition > EllipsoidDefinitionCache;
      35                 :         20 : Q_GLOBAL_STATIC( EllipsoidDefinitionCache, sDefinitionCache )
      36                 :            : 
      37                 :            : static bool sDisableCache = false;
      38                 :            : 
      39                 :          0 : QgsEllipsoidUtils::EllipsoidParameters QgsEllipsoidUtils::ellipsoidParameters( const QString &e )
      40                 :            : {
      41                 :            : // maps older QGIS ellipsoid acronyms to proj acronyms/names
      42                 :          0 :   static const QMap< QString, QString > sProj6EllipsoidAcronymMap
      43                 :          0 :   {
      44                 :          0 :     { "clrk80", "clrk80ign"  },
      45                 :          0 :     {"Adrastea2000", "ESRI:107909"},
      46                 :          0 :     {"Amalthea2000", "ESRI:107910"},
      47                 :          0 :     {"Ananke2000", "ESRI:107911"},
      48                 :          0 :     {"Ariel2000", "ESRI:107945"},
      49                 :          0 :     {"Atlas2000", "ESRI:107926"},
      50                 :          0 :     {"Belinda2000", "ESRI:107946"},
      51                 :          0 :     {"Bianca2000", "ESRI:107947"},
      52                 :          0 :     {"Callisto2000", "ESRI:107912"},
      53                 :          0 :     {"Calypso2000", "ESRI:107927"},
      54                 :          0 :     {"Carme2000", "ESRI:107913"},
      55                 :          0 :     {"Charon2000", "ESRI:107970"},
      56                 :          0 :     {"Cordelia2000", "ESRI:107948"},
      57                 :          0 :     {"Cressida2000", "ESRI:107949"},
      58                 :          0 :     {"Deimos2000", "ESRI:107906"},
      59                 :          0 :     {"Desdemona2000", "ESRI:107950"},
      60                 :          0 :     {"Despina2000", "ESRI:107961"},
      61                 :          0 :     {"Dione2000", "ESRI:107928"},
      62                 :          0 :     {"Elara2000", "ESRI:107914"},
      63                 :          0 :     {"Enceladus2000", "ESRI:107929"},
      64                 :          0 :     {"Epimetheus2000", "ESRI:107930"},
      65                 :          0 :     {"Europa2000", "ESRI:107915"},
      66                 :          0 :     {"Galatea2000", "ESRI:107962"},
      67                 :          0 :     {"Ganymede2000", "ESRI:107916"},
      68                 :          0 :     {"Helene2000", "ESRI:107931"},
      69                 :          0 :     {"Himalia2000", "ESRI:107917"},
      70                 :          0 :     {"Hyperion2000", "ESRI:107932"},
      71                 :          0 :     {"Iapetus2000", "ESRI:107933"},
      72                 :          0 :     {"Io2000", "ESRI:107918"},
      73                 :          0 :     {"Janus2000", "ESRI:107934"},
      74                 :          0 :     {"Juliet2000", "ESRI:107951"},
      75                 :          0 :     {"Jupiter2000", "ESRI:107908"},
      76                 :          0 :     {"Larissa2000", "ESRI:107963"},
      77                 :          0 :     {"Leda2000", "ESRI:107919"},
      78                 :          0 :     {"Lysithea2000", "ESRI:107920"},
      79                 :          0 :     {"Mars2000", "ESRI:107905"},
      80                 :          0 :     {"Mercury2000", "ESRI:107900"},
      81                 :          0 :     {"Metis2000", "ESRI:107921"},
      82                 :          0 :     {"Mimas2000", "ESRI:107935"},
      83                 :          0 :     {"Miranda2000", "ESRI:107952"},
      84                 :          0 :     {"Moon2000", "ESRI:107903"},
      85                 :          0 :     {"Naiad2000", "ESRI:107964"},
      86                 :          0 :     {"Neptune2000", "ESRI:107960"},
      87                 :          0 :     {"Nereid2000", "ESRI:107965"},
      88                 :          0 :     {"Oberon2000", "ESRI:107953"},
      89                 :          0 :     {"Ophelia2000", "ESRI:107954"},
      90                 :          0 :     {"Pan2000", "ESRI:107936"},
      91                 :          0 :     {"Pandora2000", "ESRI:107937"},
      92                 :          0 :     {"Pasiphae2000", "ESRI:107922"},
      93                 :          0 :     {"Phobos2000", "ESRI:107907"},
      94                 :          0 :     {"Phoebe2000", "ESRI:107938"},
      95                 :          0 :     {"Pluto2000", "ESRI:107969"},
      96                 :          0 :     {"Portia2000", "ESRI:107955"},
      97                 :          0 :     {"Prometheus2000", "ESRI:107939"},
      98                 :          0 :     {"Proteus2000", "ESRI:107966"},
      99                 :          0 :     {"Puck2000", "ESRI:107956"},
     100                 :          0 :     {"Rhea2000", "ESRI:107940"},
     101                 :          0 :     {"Rosalind2000", "ESRI:107957"},
     102                 :          0 :     {"Saturn2000", "ESRI:107925"},
     103                 :          0 :     {"Sinope2000", "ESRI:107923"},
     104                 :          0 :     {"Telesto2000", "ESRI:107941"},
     105                 :          0 :     {"Tethys2000", "ESRI:107942"},
     106                 :          0 :     {"Thalassa2000", "ESRI:107967"},
     107                 :          0 :     {"Thebe2000", "ESRI:107924"},
     108                 :          0 :     {"Titan2000", "ESRI:107943"},
     109                 :          0 :     {"Titania2000", "ESRI:107958"},
     110                 :          0 :     {"Triton2000", "ESRI:107968"},
     111                 :          0 :     {"Umbriel2000", "ESRI:107959"},
     112                 :          0 :     {"Uranus2000", "ESRI:107944"},
     113                 :          0 :     {"Venus2000", "ESRI:107902"},
     114                 :          0 :     {"IGNF:ELG053", "EPSG:7030"},
     115                 :          0 :     {"IGNF:ELG052", "EPSG:7043"},
     116                 :          0 :     {"IGNF:ELG102", "EPSG:7043"},
     117                 :          0 :     {"WGS66", "ESRI:107001"},
     118                 :          0 :     {"plessis", "EPSG:7027"},
     119                 :          0 :     {"IGNF:ELG017", "EPSG:7027"},
     120                 :          0 :     {"mod_airy", "EPSG:7002"},
     121                 :          0 :     {"IGNF:ELG037", "EPSG:7019"},
     122                 :          0 :     {"IGNF:ELG108", "EPSG:7036"},
     123                 :          0 :     {"cape", "EPSG:7034"},
     124                 :          0 :     {"IGNF:ELG010", "EPSG:7011"},
     125                 :          0 :     {"IGNF:ELG003", "EPSG:7012"},
     126                 :          0 :     {"IGNF:ELG004", "EPSG:7008"},
     127                 :          0 :     {"GSK2011", "EPSG:1025"},
     128                 :          0 :     {"airy", "EPSG:7001"},
     129                 :          0 :     {"aust_SA", "EPSG:7003"},
     130                 :          0 :     {"bessel", "EPSG:7004"},
     131                 :          0 :     {"clrk66", "EPSG:7008"},
     132                 :          0 :     {"clrk80ign", "EPSG:7011"},
     133                 :          0 :     {"evrst30", "EPSG:7015"},
     134                 :          0 :     {"evrstSS", "EPSG:7016"},
     135                 :          0 :     {"evrst48", "EPSG:7018"},
     136                 :          0 :     {"GRS80", "EPSG:7019"},
     137                 :          0 :     {"helmert", "EPSG:7020"},
     138                 :          0 :     {"intl", "EPSG:7022"},
     139                 :          0 :     {"krass", "EPSG:7024"},
     140                 :          0 :     {"NWL9D", "EPSG:7025"},
     141                 :          0 :     {"WGS84", "EPSG:7030"},
     142                 :          0 :     {"GRS67", "EPSG:7036"},
     143                 :          0 :     {"WGS72", "EPSG:7043"},
     144                 :          0 :     {"bess_nam", "EPSG:7046"},
     145                 :          0 :     {"IAU76", "EPSG:7049"},
     146                 :          0 :     {"sphere", "EPSG:7052"},
     147                 :          0 :     {"hough", "EPSG:7053"},
     148                 :          0 :     {"evrst69", "EPSG:7056"},
     149                 :          0 :     {"fschr60", "ESRI:107002"},
     150                 :          0 :     {"fschr68", "ESRI:107003"},
     151                 :          0 :     {"fschr60m", "ESRI:107004"},
     152                 :          0 :     {"walbeck", "ESRI:107007"},
     153                 :          0 :     {"IGNF:ELG001", "EPSG:7022"},
     154                 :          0 :     {"engelis", "EPSG:7054"},
     155                 :          0 :     {"evrst56", "EPSG:7044"},
     156                 :          0 :     {"SEasia", "ESRI:107004"},
     157                 :          0 :     {"SGS85", "EPSG:7054"},
     158                 :          0 :     {"andrae", "PROJ:ANDRAE"},
     159                 :          0 :     {"clrk80", "EPSG:7034"},
     160                 :          0 :     {"CPM", "PROJ:CPM"},
     161                 :          0 :     {"delmbr", "PROJ:DELMBR"},
     162                 :          0 :     {"Earth2000", "PROJ:EARTH2000"},
     163                 :          0 :     {"kaula", "PROJ:KAULA"},
     164                 :          0 :     {"lerch", "PROJ:LERCH"},
     165                 :          0 :     {"MERIT", "PROJ:MERIT"},
     166                 :          0 :     {"mprts", "PROJ:MPRTS"},
     167                 :          0 :     {"new_intl", "PROJ:NEW_INTL"},
     168                 :          0 :     {"WGS60", "PROJ:WGS60"}
     169                 :            :   };
     170                 :            : 
     171                 :          0 :   QString ellipsoid = e;
     172                 :            :   // ensure ellipsoid database is populated when first called
     173                 :            :   static std::once_flag initialized;
     174                 :          0 :   std::call_once( initialized, [ = ]
     175                 :            :   {
     176                 :          0 :     QgsScopedRuntimeProfile profile( QObject::tr( "Initialize ellipsoids" ) );
     177                 :          0 :     ( void )definitions();
     178                 :          0 :   } );
     179                 :            : 
     180                 :          0 :   ellipsoid = sProj6EllipsoidAcronymMap.value( ellipsoid, ellipsoid ); // silently upgrade older QGIS acronyms to proj acronyms
     181                 :            : 
     182                 :            :   // check cache
     183                 :            :   {
     184                 :          0 :     QgsReadWriteLocker locker( *sEllipsoidCacheLock(), QgsReadWriteLocker::Read );
     185                 :          0 :     if ( !sDisableCache )
     186                 :            :     {
     187                 :          0 :       QHash< QString, EllipsoidParameters >::const_iterator cacheIt = sEllipsoidCache()->constFind( ellipsoid );
     188                 :          0 :       if ( cacheIt != sEllipsoidCache()->constEnd() )
     189                 :            :       {
     190                 :            :         // found a match in the cache
     191                 :          0 :         QgsEllipsoidUtils::EllipsoidParameters params = cacheIt.value();
     192                 :          0 :         return params;
     193                 :          0 :       }
     194                 :          0 :     }
     195                 :          0 :   }
     196                 :            : 
     197                 :          0 :   EllipsoidParameters params;
     198                 :            : 
     199                 :            :   // Check if we have a custom projection, and set from text string.
     200                 :            :   // Format is "PARAMETER:<semi-major axis>:<semi minor axis>
     201                 :            :   // Numbers must be with (optional) decimal point and no other separators (C locale)
     202                 :            :   // Distances in meters.  Flattening is calculated.
     203                 :          0 :   if ( ellipsoid.startsWith( QLatin1String( "PARAMETER" ) ) )
     204                 :            :   {
     205                 :          0 :     QStringList paramList = ellipsoid.split( ':' );
     206                 :            :     bool semiMajorOk, semiMinorOk;
     207                 :          0 :     double semiMajor = paramList[1].toDouble( & semiMajorOk );
     208                 :          0 :     double semiMinor = paramList[2].toDouble( & semiMinorOk );
     209                 :          0 :     if ( semiMajorOk && semiMinorOk )
     210                 :            :     {
     211                 :          0 :       params.semiMajor = semiMajor;
     212                 :          0 :       params.semiMinor = semiMinor;
     213                 :          0 :       params.inverseFlattening = semiMajor / ( semiMajor - semiMinor );
     214                 :          0 :       params.useCustomParameters = true;
     215                 :          0 :     }
     216                 :            :     else
     217                 :            :     {
     218                 :          0 :       params.valid = false;
     219                 :            :     }
     220                 :            : 
     221                 :          0 :     QgsReadWriteLocker locker( *sEllipsoidCacheLock(), QgsReadWriteLocker::Write );
     222                 :          0 :     if ( !sDisableCache )
     223                 :            :     {
     224                 :          0 :       sEllipsoidCache()->insert( ellipsoid, params );
     225                 :          0 :     }
     226                 :          0 :     return params;
     227                 :          0 :   }
     228                 :          0 :   params.valid = false;
     229                 :            : 
     230                 :          0 :   QgsReadWriteLocker l( *sEllipsoidCacheLock(), QgsReadWriteLocker::Write );
     231                 :          0 :   if ( !sDisableCache )
     232                 :            :   {
     233                 :          0 :     sEllipsoidCache()->insert( ellipsoid, params );
     234                 :          0 :   }
     235                 :            : 
     236                 :          0 :   return params;
     237                 :          0 : }
     238                 :            : 
     239                 :          0 : QList<QgsEllipsoidUtils::EllipsoidDefinition> QgsEllipsoidUtils::definitions()
     240                 :            : {
     241                 :          0 :   QgsReadWriteLocker defLocker( *sDefinitionCacheLock(), QgsReadWriteLocker::Read );
     242                 :          0 :   if ( !sDefinitionCache()->isEmpty() )
     243                 :            :   {
     244                 :          0 :     return *sDefinitionCache();
     245                 :            :   }
     246                 :          0 :   defLocker.changeMode( QgsReadWriteLocker::Write );
     247                 :            : 
     248                 :          0 :   QList<QgsEllipsoidUtils::EllipsoidDefinition> defs;
     249                 :            : 
     250                 :          0 :   QgsReadWriteLocker locker( *sEllipsoidCacheLock(), QgsReadWriteLocker::Write );
     251                 :            : 
     252                 :          0 :   PJ_CONTEXT *context = QgsProjContext::get();
     253                 :          0 :   if ( PROJ_STRING_LIST authorities = proj_get_authorities_from_database( context ) )
     254                 :            :   {
     255                 :          0 :     PROJ_STRING_LIST authoritiesIt = authorities;
     256                 :          0 :     while ( char *authority = *authoritiesIt )
     257                 :            :     {
     258                 :          0 :       if ( PROJ_STRING_LIST codes = proj_get_codes_from_database( context, authority, PJ_TYPE_ELLIPSOID, 0 ) )
     259                 :            :       {
     260                 :          0 :         PROJ_STRING_LIST codesIt = codes;
     261                 :          0 :         while ( char *code = *codesIt )
     262                 :            :         {
     263                 :          0 :           QgsProjUtils::proj_pj_unique_ptr ellipsoid( proj_create_from_database( context, authority, code, PJ_CATEGORY_ELLIPSOID, 0, nullptr ) );
     264                 :          0 :           if ( ellipsoid.get() )
     265                 :            :           {
     266                 :          0 :             EllipsoidDefinition def;
     267                 :          0 :             QString name = QString( proj_get_name( ellipsoid.get() ) );
     268                 :          0 :             def.acronym = QStringLiteral( "%1:%2" ).arg( authority, code );
     269                 :          0 :             name.replace( '_', ' ' );
     270                 :          0 :             def.description = QStringLiteral( "%1 (%2:%3)" ).arg( name, authority, code );
     271                 :            : 
     272                 :            :             double semiMajor, semiMinor, invFlattening;
     273                 :          0 :             int semiMinorComputed = 0;
     274                 :          0 :             if ( proj_ellipsoid_get_parameters( context, ellipsoid.get(), &semiMajor, &semiMinor, &semiMinorComputed, &invFlattening ) )
     275                 :            :             {
     276                 :          0 :               def.parameters.semiMajor = semiMajor;
     277                 :          0 :               def.parameters.semiMinor = semiMinor;
     278                 :          0 :               def.parameters.inverseFlattening = invFlattening;
     279                 :          0 :               if ( !semiMinorComputed )
     280                 :          0 :                 def.parameters.crs.createFromProj( QStringLiteral( "+proj=longlat +a=%1 +b=%2 +no_defs +type=crs" ).arg( def.parameters.semiMajor, 0, 'g', 17 ).arg( def.parameters.semiMinor, 0, 'g', 17 ), false );
     281                 :          0 :               else if ( !qgsDoubleNear( def.parameters.inverseFlattening, 0.0 ) )
     282                 :          0 :                 def.parameters.crs.createFromProj( QStringLiteral( "+proj=longlat +a=%1 +rf=%2 +no_defs +type=crs" ).arg( def.parameters.semiMajor, 0, 'g', 17 ).arg( def.parameters.inverseFlattening, 0, 'g', 17 ), false );
     283                 :            :               else
     284                 :          0 :                 def.parameters.crs.createFromProj( QStringLiteral( "+proj=longlat +a=%1 +no_defs +type=crs" ).arg( def.parameters.semiMajor, 0, 'g', 17 ), false );
     285                 :          0 :             }
     286                 :            :             else
     287                 :            :             {
     288                 :          0 :               def.parameters.valid = false;
     289                 :            :             }
     290                 :            : 
     291                 :          0 :             defs << def;
     292                 :          0 :             if ( !sDisableCache )
     293                 :            :             {
     294                 :          0 :               sEllipsoidCache()->insert( def.acronym, def.parameters );
     295                 :          0 :             }
     296                 :          0 :           }
     297                 :            : 
     298                 :          0 :           codesIt++;
     299                 :          0 :         }
     300                 :          0 :         proj_string_list_destroy( codes );
     301                 :          0 :       }
     302                 :            : 
     303                 :          0 :       authoritiesIt++;
     304                 :            :     }
     305                 :          0 :     proj_string_list_destroy( authorities );
     306                 :          0 :   }
     307                 :          0 :   locker.unlock();
     308                 :            : 
     309                 :          0 :   QCollator collator;
     310                 :          0 :   collator.setCaseSensitivity( Qt::CaseInsensitive );
     311                 :          0 :   std::sort( defs.begin(), defs.end(), [&collator]( const EllipsoidDefinition & a, const EllipsoidDefinition & b )
     312                 :            :   {
     313                 :          0 :     return collator.compare( a.description, b.description ) < 0;
     314                 :            :   } );
     315                 :          0 :   if ( !sDisableCache )
     316                 :            :   {
     317                 :          0 :     *sDefinitionCache() = defs;
     318                 :          0 :   }
     319                 :            : 
     320                 :          0 :   return defs;
     321                 :          0 : }
     322                 :            : 
     323                 :          0 : QStringList QgsEllipsoidUtils::acronyms()
     324                 :            : {
     325                 :          0 :   QStringList result;
     326                 :          0 :   const QList<QgsEllipsoidUtils::EllipsoidDefinition> defs = definitions();
     327                 :          0 :   result.reserve( defs.size() );
     328                 :          0 :   for ( const QgsEllipsoidUtils::EllipsoidDefinition &def : defs )
     329                 :            :   {
     330                 :          0 :     result << def.acronym;
     331                 :            :   }
     332                 :          0 :   return result;
     333                 :          0 : }
     334                 :            : 
     335                 :          8 : void QgsEllipsoidUtils::invalidateCache( bool disableCache )
     336                 :            : {
     337                 :          8 :   QgsReadWriteLocker locker1( *sEllipsoidCacheLock(), QgsReadWriteLocker::Write );
     338                 :          8 :   QgsReadWriteLocker locker2( *sDefinitionCacheLock(), QgsReadWriteLocker::Write );
     339                 :            : 
     340                 :          8 :   if ( !sDisableCache )
     341                 :            :   {
     342                 :          5 :     if ( disableCache )
     343                 :          5 :       sDisableCache = true;
     344                 :          5 :     sEllipsoidCache()->clear();
     345                 :          5 :     sDefinitionCache()->clear();
     346                 :          5 :   }
     347                 :          8 : }

Generated by: LCOV version 1.14