1 /*************************************************************************** 2 qgscoordinatereferencesystem_p.h 3 4 -------------------------------- 5 begin : 2016 6 copyright : (C) 2016 by Nyall Dawson 7 email : nyall dot dawson at gmail dot com 8 ***************************************************************************/ 9 10 /*************************************************************************** 11 * * 12 * This program is free software; you can redistribute it and/or modify * 13 * it under the terms of the GNU General Public License as published by * 14 * the Free Software Foundation; either version 2 of the License, or * 15 * (at your option) any later version. * 16 * * 17 ***************************************************************************/ 18 #ifndef QGSCOORDINATEREFERENCESYSTEM_PRIVATE_H 19 #define QGSCOORDINATEREFERENCESYSTEM_PRIVATE_H 20 21 /// @cond PRIVATE 22 23 // 24 // W A R N I N G 25 // ------------- 26 // 27 // This file is not part of the QGIS API. It exists purely as an 28 // implementation detail. This header file may change from version to 29 // version without notice, or even be removed. 30 // 31 32 #include "qgscoordinatereferencesystem.h" 33 34 #include <proj.h> 35 #include "qgsprojutils.h" 36 #include "qgsreadwritelocker.h" 37 38 #ifdef DEBUG 39 typedef struct OGRSpatialReferenceHS *OGRSpatialReferenceH; 40 #else 41 typedef void *OGRSpatialReferenceH; 42 #endif 43 44 class QgsCoordinateReferenceSystemPrivate : public QSharedData 45 { 46 public: 47 QgsCoordinateReferenceSystemPrivate()48 explicit QgsCoordinateReferenceSystemPrivate() 49 { 50 } 51 QgsCoordinateReferenceSystemPrivate(const QgsCoordinateReferenceSystemPrivate & other)52 QgsCoordinateReferenceSystemPrivate( const QgsCoordinateReferenceSystemPrivate &other ) 53 : QSharedData( other ) 54 , mSrsId( other.mSrsId ) 55 , mDescription( other.mDescription ) 56 , mProjectionAcronym( other.mProjectionAcronym ) 57 , mEllipsoidAcronym( other.mEllipsoidAcronym ) 58 , mIsGeographic( other.mIsGeographic ) 59 , mMapUnits( other.mMapUnits ) 60 , mSRID( other.mSRID ) 61 , mAuthId( other.mAuthId ) 62 , mIsValid( other.mIsValid ) 63 , mCoordinateEpoch( other.mCoordinateEpoch ) 64 , mPj() 65 , mProj4( other.mProj4 ) 66 , mWktPreferred( other.mWktPreferred ) 67 , mAxisInvertedDirty( other.mAxisInvertedDirty ) 68 , mAxisInverted( other.mAxisInverted ) 69 , mProjObjects() 70 { 71 } 72 ~QgsCoordinateReferenceSystemPrivate()73 ~QgsCoordinateReferenceSystemPrivate() 74 { 75 QgsReadWriteLocker locker( mProjLock, QgsReadWriteLocker::Read ); 76 if ( !mProjObjects.empty() || mPj ) 77 { 78 locker.changeMode( QgsReadWriteLocker::Write ); 79 cleanPjObjects(); 80 } 81 } 82 83 //! The internal sqlite3 srs.db primary key for this CRS 84 long mSrsId = 0; 85 86 //! A textual description of the CRS 87 QString mDescription; 88 89 //! The official proj4 acronym for the projection family 90 QString mProjectionAcronym; 91 92 //! The official proj4 acronym for the ellipsoid 93 QString mEllipsoidAcronym; 94 95 //! Whether this is a geographic or projected coordinate system 96 bool mIsGeographic = false; 97 98 //! The map units for the CRS 99 QgsUnitTypes::DistanceUnit mMapUnits = QgsUnitTypes::DistanceUnknownUnit; 100 101 //! If available, the PostGIS spatial_ref_sys identifier for this CRS (defaults to 0) 102 long mSRID = 0; 103 104 //! If available the authority identifier for this CRS 105 QString mAuthId; 106 107 //! Whether this CRS is properly defined and valid 108 bool mIsValid = false; 109 110 //! Coordinate epoch 111 double mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN(); 112 113 // this is the "master" proj object, to be used as a template for new proj objects created on different threads ONLY. 114 // Always use threadLocalProjObject() instead of this. 115 116 private: 117 QgsProjUtils::proj_pj_unique_ptr mPj; 118 PJ_CONTEXT *mPjParentContext = nullptr; 119 cleanPjObjects()120 void cleanPjObjects() 121 { 122 123 // During destruction of PJ* objects, the errno is set in the underlying 124 // context. Consequently the context attached to the PJ* must still exist ! 125 // Which is not necessarily the case currently unfortunately. So 126 // create a temporary dummy context, and attach it to the PJ* before destroying 127 // it 128 PJ_CONTEXT *tmpContext = proj_context_create(); 129 for ( auto it = mProjObjects.begin(); it != mProjObjects.end(); ++it ) 130 { 131 proj_assign_context( it.value(), tmpContext ); 132 proj_destroy( it.value() ); 133 } 134 mProjObjects.clear(); 135 if ( mPj ) 136 { 137 proj_assign_context( mPj.get(), tmpContext ); 138 mPj.reset(); 139 } 140 proj_context_destroy( tmpContext ); 141 } 142 143 public: 144 setPj(QgsProjUtils::proj_pj_unique_ptr obj)145 void setPj( QgsProjUtils::proj_pj_unique_ptr obj ) 146 { 147 const QgsReadWriteLocker locker( mProjLock, QgsReadWriteLocker::Write ); 148 cleanPjObjects(); 149 150 mPj = std::move( obj ); 151 mPjParentContext = QgsProjContext::get(); 152 } 153 hasPj()154 bool hasPj() const 155 { 156 const QgsReadWriteLocker locker( mProjLock, QgsReadWriteLocker::Read ); 157 return static_cast< bool >( mPj ); 158 } 159 160 mutable QString mProj4; 161 162 mutable QString mWktPreferred; 163 164 //! True if presence of an inverted axis needs to be recalculated 165 mutable bool mAxisInvertedDirty = false; 166 167 //! Whether this is a coordinate system has inverted axis 168 mutable bool mAxisInverted = false; 169 170 private: 171 mutable QReadWriteLock mProjLock{}; 172 mutable QMap < PJ_CONTEXT *, PJ * > mProjObjects{}; 173 174 public: 175 threadLocalProjObject()176 PJ *threadLocalProjObject() const 177 { 178 QgsReadWriteLocker locker( mProjLock, QgsReadWriteLocker::Read ); 179 if ( !mPj ) 180 return nullptr; 181 182 PJ_CONTEXT *context = QgsProjContext::get(); 183 const QMap < PJ_CONTEXT *, PJ * >::const_iterator it = mProjObjects.constFind( context ); 184 185 if ( it != mProjObjects.constEnd() ) 186 { 187 return it.value(); 188 } 189 190 // proj object doesn't exist yet, so we need to create 191 locker.changeMode( QgsReadWriteLocker::Write ); 192 193 PJ *res = proj_clone( context, mPj.get() ); 194 mProjObjects.insert( context, res ); 195 return res; 196 } 197 198 // Only meant to be called by QgsCoordinateReferenceSystem::removeFromCacheObjectsBelongingToCurrentThread() removeObjectsBelongingToCurrentThread(PJ_CONTEXT * pj_context)199 bool removeObjectsBelongingToCurrentThread( PJ_CONTEXT *pj_context ) 200 { 201 const QgsReadWriteLocker locker( mProjLock, QgsReadWriteLocker::Write ); 202 203 const QMap < PJ_CONTEXT *, PJ * >::iterator it = mProjObjects.find( pj_context ); 204 if ( it != mProjObjects.end() ) 205 { 206 proj_destroy( it.value() ); 207 mProjObjects.erase( it ); 208 } 209 210 if ( mPjParentContext == pj_context ) 211 { 212 mPj.reset(); 213 mPjParentContext = nullptr; 214 } 215 216 return mProjObjects.isEmpty(); 217 } 218 219 private: 220 QgsCoordinateReferenceSystemPrivate &operator= ( const QgsCoordinateReferenceSystemPrivate & ) = delete; 221 222 }; 223 224 /// @endcond 225 226 #endif //QGSCOORDINATEREFERENCESYSTEM_PRIVATE_H 227