26#include <proj_experimental.h> 
   45                     const QString &error )> QgsCoordinateTransformPrivate::sCoordinateOperationCreationErrorHandler = 
nullptr;
 
   55QgsCoordinateTransformPrivate::QgsCoordinateTransformPrivate()
 
   64  : mSourceCRS( source )
 
   65  , mDestCRS( destination )
 
   67  if ( mSourceCRS != mDestCRS )
 
   68    calculateTransforms( context );
 
   74  : mSourceCRS( source )
 
   75  , mDestCRS( destination )
 
   76  , mSourceDatumTransform( sourceDatumTransform )
 
   77  , mDestinationDatumTransform( destDatumTransform )
 
   81QgsCoordinateTransformPrivate::QgsCoordinateTransformPrivate( 
const QgsCoordinateTransformPrivate &other )
 
   82  : QSharedData( other )
 
   83  , mAvailableOpCount( other.mAvailableOpCount )
 
   84  , mIsValid( other.mIsValid )
 
   85  , mShortCircuit( other.mShortCircuit )
 
   86  , mGeographicToWebMercator( other.mGeographicToWebMercator )
 
   87  , mSourceCRS( other.mSourceCRS )
 
   88  , mDestCRS( other.mDestCRS )
 
   89  , mSourceDatumTransform( other.mSourceDatumTransform )
 
   90  , mDestinationDatumTransform( other.mDestinationDatumTransform )
 
   91  , mProjCoordinateOperation( other.mProjCoordinateOperation )
 
   92  , mShouldReverseCoordinateOperation( other.mShouldReverseCoordinateOperation )
 
   93  , mAllowFallbackTransforms( other.mAllowFallbackTransforms )
 
   94  , mSourceIsDynamic( other.mSourceIsDynamic )
 
   95  , mDestIsDynamic( other.mDestIsDynamic )
 
   96  , mSourceCoordinateEpoch( other.mSourceCoordinateEpoch )
 
   97  , mDestCoordinateEpoch( other.mDestCoordinateEpoch )
 
   98  , mDefaultTime( other.mDefaultTime )
 
   99  , mIsReversed( other.mIsReversed )
 
  102  , mProjFallbackProjections()
 
  108QgsCoordinateTransformPrivate::~QgsCoordinateTransformPrivate()
 
  115bool QgsCoordinateTransformPrivate::checkValidity()
 
  117  if ( !mSourceCRS.isValid() || !mDestCRS.isValid() )
 
  125void QgsCoordinateTransformPrivate::invalidate()
 
  127  mShortCircuit = 
true;
 
  129  mAvailableOpCount = -1;
 
  132bool QgsCoordinateTransformPrivate::initialize()
 
  135  if ( !mSourceCRS.isValid() )
 
  143  if ( !mDestCRS.isValid() )
 
  147    mDestCRS = mSourceCRS;
 
  154  if ( mSourceCRS == mDestCRS )
 
  158    mShortCircuit = 
true;
 
  162  mGeographicToWebMercator =
 
  163    mSourceCRS.isGeographic() &&
 
  164    mDestCRS.authid() == QLatin1String( 
"EPSG:3857" );
 
  166  mSourceIsDynamic = mSourceCRS.isDynamic();
 
  167  mSourceCoordinateEpoch = mSourceCRS.coordinateEpoch();
 
  168  mDestIsDynamic = mDestCRS.isDynamic();
 
  169  mDestCoordinateEpoch = mDestCRS.coordinateEpoch();
 
  176  mDefaultTime = ( mSourceIsDynamic && !std::isnan( mSourceCoordinateEpoch ) && !mDestIsDynamic )
 
  177                 ? mSourceCoordinateEpoch
 
  178                 : ( mDestIsDynamic && !std::isnan( mDestCoordinateEpoch ) && !mSourceIsDynamic )
 
  179                 ? mDestCoordinateEpoch : std::numeric_limits< double >::quiet_NaN();
 
  181  if ( mSourceIsDynamic && mDestIsDynamic && !
qgsNanCompatibleEquals( mSourceCoordinateEpoch, mDestCoordinateEpoch ) )
 
  184    if ( sDynamicCrsToDynamicCrsWarningHandler )
 
  186      sDynamicCrsToDynamicCrsWarningHandler( mSourceCRS, mDestCRS );
 
  194  ProjData res = threadLocalProjData();
 
  196#ifdef COORDINATE_TRANSFORM_VERBOSE 
  204#ifdef COORDINATE_TRANSFORM_VERBOSE 
  207    QgsDebugMsgLevel( QStringLiteral( 
"------------------------------------------------------------" ), 2 );
 
  208    QgsDebugMsgLevel( QStringLiteral( 
"The OGR Coordinate transformation for this layer was set to" ), 2 );
 
  209    QgsLogger::debug<QgsCoordinateReferenceSystem>( 
"Input", mSourceCRS, __FILE__, __FUNCTION__, __LINE__ );
 
  210    QgsLogger::debug<QgsCoordinateReferenceSystem>( 
"Output", mDestCRS, __FILE__, __FUNCTION__, __LINE__ );
 
  211    QgsDebugMsgLevel( QStringLiteral( 
"------------------------------------------------------------" ), 2 );
 
  215    QgsDebugError( QStringLiteral( 
"The OGR Coordinate transformation FAILED TO INITIALIZE!" ) );
 
  220    QgsDebugError( QStringLiteral( 
"Coordinate transformation failed to initialize!" ) );
 
  225  mShortCircuit = 
false;
 
  233  if ( mSourceCRS.isValid() && mDestCRS.isValid() )
 
  241    mProjCoordinateOperation.clear();
 
  242    mShouldReverseCoordinateOperation = 
false;
 
  243    mAllowFallbackTransforms = 
false;
 
  247static void proj_collecting_logger( 
void *user_data, 
int , 
const char *message )
 
  249  QStringList *dest = 
reinterpret_cast< QStringList * 
>( user_data );
 
  250  dest->append( QString( message ) );
 
  253static void proj_logger( 
void *, 
int level, 
const char *message )
 
  258  if ( level == PJ_LOG_ERROR )
 
  260    const QString messageString( message );
 
  261    if ( messageString == QLatin1String( 
"push: Invalid latitude" ) )
 
  271  else if ( level == PJ_LOG_DEBUG )
 
  277ProjData QgsCoordinateTransformPrivate::threadLocalProjData()
 
  282  const QMap < uintptr_t, ProjData >::const_iterator it = mProjProjections.constFind( 
reinterpret_cast< uintptr_t
>( context ) );
 
  284  if ( it != mProjProjections.constEnd() )
 
  286    ProjData res = it.value();
 
  294  QStringList projErrors;
 
  295  proj_log_func( context, &projErrors, proj_collecting_logger );
 
  300  if ( !mProjCoordinateOperation.isEmpty() )
 
  302    transform.reset( proj_create( context, mProjCoordinateOperation.toUtf8().constData() ) );
 
  312           proj_context_is_network_enabled( context ) &&
 
  313           !proj_coordoperation_is_instantiable( context, transform.get() ) )
 
  316      if ( sMissingGridUsedByContextHandler )
 
  319        desired.
proj = mProjCoordinateOperation;
 
  322        sMissingGridUsedByContextHandler( mSourceCRS, mDestCRS, desired );
 
  326        const QString err = QObject::tr( 
"Could not use operation specified in project between %1 and %2. (Wanted to use: %3)." ).arg( mSourceCRS.authid(),
 
  328                            mProjCoordinateOperation );
 
  336      mIsReversed = mShouldReverseCoordinateOperation;
 
  340  QString nonAvailableError;
 
  343    if ( !mSourceCRS.projObject() || ! mDestCRS.projObject() )
 
  345      proj_log_func( context, 
nullptr, 
nullptr );
 
  349    PJ_OPERATION_FACTORY_CONTEXT *operationContext = proj_create_operation_factory_context( context, 
nullptr );
 
  352    proj_operation_factory_context_set_grid_availability_use( context, operationContext, PROJ_GRID_AVAILABILITY_IGNORED );
 
  355    proj_operation_factory_context_set_spatial_criterion( context, operationContext, PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION );
 
  357    if ( PJ_OBJ_LIST *ops = proj_create_operations( context, mSourceCRS.projObject(), mDestCRS.projObject(), operationContext ) )
 
  359      mAvailableOpCount = proj_list_get_count( ops );
 
  360      if ( mAvailableOpCount < 1 )
 
  363        const int errNo = proj_context_errno( context );
 
  364        if ( errNo && errNo != -61 )
 
  366          nonAvailableError = QString( proj_errno_string( errNo ) );
 
  370          nonAvailableError = QObject::tr( 
"No coordinate operations are available between these two reference systems" );
 
  373      else if ( mAvailableOpCount == 1 )
 
  376        transform.reset( proj_list_get( context, ops, 0 ) );
 
  379          if ( !proj_coordoperation_is_instantiable( context, transform.get() ) )
 
  382            for ( 
int j = 0; j < proj_coordoperation_get_grid_used_count( context, transform.get() ); ++j )
 
  384              const char *shortName = 
nullptr;
 
  385              const char *fullName = 
nullptr;
 
  386              const char *packageName = 
nullptr;
 
  387              const char *url = 
nullptr;
 
  388              int directDownload = 0;
 
  391              proj_coordoperation_get_grid_used( context, transform.get(), j, &shortName, &fullName, &packageName, &url, &directDownload, &openLicense, &isAvailable );
 
  395                if ( sMissingRequiredGridHandler )
 
  398                  gridDetails.
shortName = QString( shortName );
 
  399                  gridDetails.
fullName = QString( fullName );
 
  401                  gridDetails.
url = QString( url );
 
  405                  sMissingRequiredGridHandler( mSourceCRS, mDestCRS, gridDetails );
 
  409                  const QString err = QObject::tr( 
"Cannot create transform between %1 and %2, missing required grid %3" ).arg( mSourceCRS.authid(),
 
  422            transform.reset( proj_normalize_for_visualization( context, transform.get() ) );
 
  425              const QString err = QObject::tr( 
"Cannot normalize transform between %1 and %2" ).arg( mSourceCRS.authid(),
 
  436        bool missingPreferred = 
false;
 
  437        bool stillLookingForPreferred = 
true;
 
  438        for ( 
int i = 0; i < mAvailableOpCount; ++ i )
 
  440          transform.reset( proj_list_get( context, ops, i ) );
 
  441          const bool isInstantiable = transform && proj_coordoperation_is_instantiable( context, transform.get() );
 
  442          if ( stillLookingForPreferred && transform && !isInstantiable )
 
  446            if ( !candidate.
proj.isEmpty() )
 
  448              preferred = candidate;
 
  449              missingPreferred = 
true;
 
  450              stillLookingForPreferred = 
false;
 
  453          if ( transform && isInstantiable )
 
  461        if ( transform && missingPreferred )
 
  465          if ( sMissingPreferredGridHandler )
 
  467            sMissingPreferredGridHandler( mSourceCRS, mDestCRS, preferred, available );
 
  471            const QString err = QObject::tr( 
"Using non-preferred coordinate operation between %1 and %2. Using %3, preferred %4." ).arg( mSourceCRS.authid(),
 
  481          transform.reset( proj_normalize_for_visualization( context, transform.get() ) );
 
  484          const QString err = QObject::tr( 
"Cannot normalize transform between %1 and %2" ).arg( mSourceCRS.authid(),
 
  489      proj_list_destroy( ops );
 
  491    proj_operation_factory_context_destroy( operationContext );
 
  494  if ( !transform && nonAvailableError.isEmpty() )
 
  496    const int errNo = proj_context_errno( context );
 
  497    if ( errNo && errNo != -61 )
 
  499      nonAvailableError = QString( proj_errno_string( errNo ) );
 
  501    else if ( !projErrors.empty() )
 
  503      nonAvailableError = projErrors.constLast();
 
  506    if ( nonAvailableError.isEmpty() )
 
  508      nonAvailableError = QObject::tr( 
"No coordinate operations are available between these two reference systems" );
 
  513      nonAvailableError = nonAvailableError.remove( QStringLiteral( 
"internal_proj_create_operations: " ) );
 
  517  if ( !nonAvailableError.isEmpty() )
 
  519    if ( sCoordinateOperationCreationErrorHandler )
 
  521      sCoordinateOperationCreationErrorHandler( mSourceCRS, mDestCRS, nonAvailableError );
 
  525      const QString err = QObject::tr( 
"Cannot create transform between %1 and %2: %3" ).arg( mSourceCRS.authid(),
 
  533  proj_log_func( context, 
nullptr, proj_logger );
 
  541  ProjData res = transform.release();
 
  542  mProjProjections.insert( 
reinterpret_cast< uintptr_t
>( context ), res );
 
  546ProjData QgsCoordinateTransformPrivate::threadLocalFallbackProjData()
 
  551  const QMap < uintptr_t, ProjData >::const_iterator it = mProjFallbackProjections.constFind( 
reinterpret_cast< uintptr_t
>( context ) );
 
  553  if ( it != mProjFallbackProjections.constEnd() )
 
  555    ProjData res = it.value();
 
  564    transform.reset( proj_normalize_for_visualization( 
QgsProjContext::get(), transform.get() ) );
 
  566  ProjData res = transform.release();
 
  567  mProjFallbackProjections.insert( 
reinterpret_cast< uintptr_t
>( context ), res );
 
  573  sMissingRequiredGridHandler = handler;
 
  578  sMissingPreferredGridHandler = handler;
 
  583  sCoordinateOperationCreationErrorHandler = handler;
 
  588  sMissingGridUsedByContextHandler = handler;
 
  593  sDynamicCrsToDynamicCrsWarningHandler = handler;
 
  596void QgsCoordinateTransformPrivate::freeProj()
 
  599  if ( mProjProjections.isEmpty() && mProjFallbackProjections.isEmpty() )
 
  601  QMap < uintptr_t, ProjData >::const_iterator it = mProjProjections.constBegin();
 
  608  PJ_CONTEXT *tmpContext = proj_context_create();
 
  609  for ( ; it != mProjProjections.constEnd(); ++it )
 
  611    proj_assign_context( it.value(), tmpContext );
 
  612    proj_destroy( it.value() );
 
  615  it = mProjFallbackProjections.constBegin();
 
  616  for ( ; it != mProjFallbackProjections.constEnd(); ++it )
 
  618    proj_assign_context( it.value(), tmpContext );
 
  619    proj_destroy( it.value() );
 
  622  proj_context_destroy( tmpContext );
 
  623  mProjProjections.clear();
 
  624  mProjFallbackProjections.clear();
 
  627bool QgsCoordinateTransformPrivate::removeObjectsBelongingToCurrentThread( 
void *pj_context )
 
  631  QMap < uintptr_t, ProjData >::iterator it = mProjProjections.find( 
reinterpret_cast< uintptr_t
>( pj_context ) );
 
  632  if ( it != mProjProjections.end() )
 
  634    proj_destroy( it.value() );
 
  635    mProjProjections.erase( it );
 
  638  it = mProjFallbackProjections.find( 
reinterpret_cast< uintptr_t
>( pj_context ) );
 
  639  if ( it != mProjFallbackProjections.end() )
 
  641    proj_destroy( it.value() );
 
  642    mProjFallbackProjections.erase( it );
 
  645  return mProjProjections.isEmpty();
 
This class represents a coordinate reference system (CRS).
 
Contains information about the context in which a coordinate transform is executed.
 
bool allowFallbackTransform(const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination) const
Returns true if approximate "ballpark" transforms may be used when transforming between a source and ...
 
QString calculateCoordinateOperation(const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination) const
Returns the Proj coordinate operation string to use when transforming from the specified source CRS t...
 
bool mustReverseCoordinateOperation(const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination) const
Returns true if the coordinate operation returned by calculateCoordinateOperation() for the source to...
 
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
 
static PJ_CONTEXT * get()
Returns a thread local instance of a proj context, safe for use in the current thread.
 
static QList< QgsDatumTransform::GridDetails > gridsUsed(const QString &proj)
Returns a list of grids used by the given proj string.
 
std::unique_ptr< PJ, ProjPJDeleter > proj_pj_unique_ptr
Scoped Proj PJ object.
 
The QgsReadWriteLocker class is a convenience class that simplifies locking and unlocking QReadWriteL...
 
#define Q_NOWARN_DEPRECATED_POP
 
#define Q_NOWARN_DEPRECATED_PUSH
 
bool qgsNanCompatibleEquals(double a, double b)
Compare two doubles, treating nan values as equal.
 
struct projCtx_t PJ_CONTEXT
 
#define QgsDebugMsgLevel(str, level)
 
#define QgsDebugError(str)