26 #include <proj_experimental.h> 
   30 #include <QStringList> 
   45                      const QString &error )> QgsCoordinateTransformPrivate::sCoordinateOperationCreationErrorHandler = 
nullptr;
 
   55 QgsCoordinateTransformPrivate::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 )
 
   81 QgsCoordinateTransformPrivate::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()
 
  108 QgsCoordinateTransformPrivate::~QgsCoordinateTransformPrivate()
 
  115 bool QgsCoordinateTransformPrivate::checkValidity()
 
  117   if ( !mSourceCRS.isValid() || !mDestCRS.isValid() )
 
  125 void QgsCoordinateTransformPrivate::invalidate()
 
  127   mShortCircuit = 
true;
 
  129   mAvailableOpCount = -1;
 
  132 bool 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 
  197   QgsDebugMsg( 
"From proj : " + mSourceCRS.toProj() );
 
  204 #ifdef COORDINATE_TRANSFORM_VERBOSE 
  207     QgsDebugMsg( QStringLiteral( 
"------------------------------------------------------------" ) );
 
  208     QgsDebugMsg( QStringLiteral( 
"The OGR Coordinate transformation for this layer was set to" ) );
 
  209     QgsLogger::debug<QgsCoordinateReferenceSystem>( 
"Input", mSourceCRS, __FILE__, __FUNCTION__, __LINE__ );
 
  210     QgsLogger::debug<QgsCoordinateReferenceSystem>( 
"Output", mDestCRS, __FILE__, __FUNCTION__, __LINE__ );
 
  211     QgsDebugMsg( QStringLiteral( 
"------------------------------------------------------------" ) );
 
  215     QgsDebugMsg( QStringLiteral( 
"------------------------------------------------------------" ) );
 
  216     QgsDebugMsg( QStringLiteral( 
"The OGR Coordinate transformation FAILED TO INITIALIZE!" ) );
 
  217     QgsDebugMsg( QStringLiteral( 
"------------------------------------------------------------" ) );
 
  222     QgsDebugMsg( QStringLiteral( 
"Coordinate transformation failed to initialize!" ) );
 
  227   mShortCircuit = 
false;
 
  235   if ( mSourceCRS.isValid() && mDestCRS.isValid() )
 
  243     mProjCoordinateOperation.clear();
 
  244     mShouldReverseCoordinateOperation = 
false;
 
  245     mAllowFallbackTransforms = 
false;
 
  249 static void proj_collecting_logger( 
void *user_data, 
int , 
const char *message )
 
  251   QStringList *dest = 
reinterpret_cast< QStringList * 
>( user_data );
 
  252   dest->append( QString( message ) );
 
  255 static void proj_logger( 
void *, 
int level, 
const char *message )
 
  260   if ( level == PJ_LOG_ERROR )
 
  264   else if ( level == PJ_LOG_DEBUG )
 
  270 ProjData QgsCoordinateTransformPrivate::threadLocalProjData()
 
  275   const QMap < uintptr_t, ProjData >::const_iterator it = mProjProjections.constFind( 
reinterpret_cast< uintptr_t
>( context ) );
 
  277   if ( it != mProjProjections.constEnd() )
 
  279     ProjData res = it.value();
 
  287   QStringList projErrors;
 
  288   proj_log_func( context, &projErrors, proj_collecting_logger );
 
  293   if ( !mProjCoordinateOperation.isEmpty() )
 
  295     transform.reset( proj_create( context, mProjCoordinateOperation.toUtf8().constData() ) );
 
  304 #
if PROJ_VERSION_MAJOR >= 7
 
  306            proj_context_is_network_enabled( context ) &&
 
  307            !proj_coordoperation_is_instantiable( context, transform.get() ) )
 
  311       if ( sMissingGridUsedByContextHandler )
 
  314         desired.
proj = mProjCoordinateOperation;
 
  317         sMissingGridUsedByContextHandler( mSourceCRS, mDestCRS, desired );
 
  321         const QString err = QObject::tr( 
"Could not use operation specified in project between %1 and %2. (Wanted to use: %3)." ).arg( mSourceCRS.authid(),
 
  323                             mProjCoordinateOperation );
 
  331       mIsReversed = mShouldReverseCoordinateOperation;
 
  335   QString nonAvailableError;
 
  338     if ( !mSourceCRS.projObject() || ! mDestCRS.projObject() )
 
  340       proj_log_func( context, 
nullptr, 
nullptr );
 
  344     PJ_OPERATION_FACTORY_CONTEXT *operationContext = proj_create_operation_factory_context( context, 
nullptr );
 
  347     proj_operation_factory_context_set_grid_availability_use( context, operationContext, PROJ_GRID_AVAILABILITY_IGNORED );
 
  350     proj_operation_factory_context_set_spatial_criterion( context, operationContext, PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION );
 
  352     if ( PJ_OBJ_LIST *ops = proj_create_operations( context, mSourceCRS.projObject(), mDestCRS.projObject(), operationContext ) )
 
  354       mAvailableOpCount = proj_list_get_count( ops );
 
  355       if ( mAvailableOpCount < 1 )
 
  358         const int errNo = proj_context_errno( context );
 
  359         if ( errNo && errNo != -61 )
 
  361           nonAvailableError = QString( proj_errno_string( errNo ) );
 
  365           nonAvailableError = QObject::tr( 
"No coordinate operations are available between these two reference systems" );
 
  368       else if ( mAvailableOpCount == 1 )
 
  371         transform.reset( proj_list_get( context, ops, 0 ) );
 
  374           if ( !proj_coordoperation_is_instantiable( context, transform.get() ) )
 
  377             for ( 
int j = 0; j < proj_coordoperation_get_grid_used_count( context, transform.get() ); ++j )
 
  379               const char *shortName = 
nullptr;
 
  380               const char *fullName = 
nullptr;
 
  381               const char *packageName = 
nullptr;
 
  382               const char *url = 
nullptr;
 
  383               int directDownload = 0;
 
  386               proj_coordoperation_get_grid_used( context, transform.get(), j, &shortName, &fullName, &packageName, &url, &directDownload, &openLicense, &isAvailable );
 
  390                 if ( sMissingRequiredGridHandler )
 
  393                   gridDetails.
shortName = QString( shortName );
 
  394                   gridDetails.
fullName = QString( fullName );
 
  396                   gridDetails.
url = QString( url );
 
  400                   sMissingRequiredGridHandler( mSourceCRS, mDestCRS, gridDetails );
 
  404                   const QString err = QObject::tr( 
"Cannot create transform between %1 and %2, missing required grid %3" ).arg( mSourceCRS.authid(),
 
  417             transform.reset( proj_normalize_for_visualization( context, transform.get() ) );
 
  420               const QString err = QObject::tr( 
"Cannot normalize transform between %1 and %2" ).arg( mSourceCRS.authid(),
 
  431         bool missingPreferred = 
false;
 
  432         bool stillLookingForPreferred = 
true;
 
  433         for ( 
int i = 0; i < mAvailableOpCount; ++ i )
 
  435           transform.reset( proj_list_get( context, ops, i ) );
 
  436           const bool isInstantiable = transform && proj_coordoperation_is_instantiable( context, transform.get() );
 
  437           if ( stillLookingForPreferred && transform && !isInstantiable )
 
  441             if ( !candidate.
proj.isEmpty() )
 
  443               preferred = candidate;
 
  444               missingPreferred = 
true;
 
  445               stillLookingForPreferred = 
false;
 
  448           if ( transform && isInstantiable )
 
  456         if ( transform && missingPreferred )
 
  460           if ( sMissingPreferredGridHandler )
 
  462             sMissingPreferredGridHandler( mSourceCRS, mDestCRS, preferred, available );
 
  466             const QString err = QObject::tr( 
"Using non-preferred coordinate operation between %1 and %2. Using %3, preferred %4." ).arg( mSourceCRS.authid(),
 
  476           transform.reset( proj_normalize_for_visualization( context, transform.get() ) );
 
  479           const QString err = QObject::tr( 
"Cannot normalize transform between %1 and %2" ).arg( mSourceCRS.authid(),
 
  484       proj_list_destroy( ops );
 
  486     proj_operation_factory_context_destroy( operationContext );
 
  489   if ( !transform && nonAvailableError.isEmpty() )
 
  491     const int errNo = proj_context_errno( context );
 
  492     if ( errNo && errNo != -61 )
 
  494       nonAvailableError = QString( proj_errno_string( errNo ) );
 
  496     else if ( !projErrors.empty() )
 
  498       nonAvailableError = projErrors.constLast();
 
  501     if ( nonAvailableError.isEmpty() )
 
  503       nonAvailableError = QObject::tr( 
"No coordinate operations are available between these two reference systems" );
 
  508       nonAvailableError = nonAvailableError.remove( QStringLiteral( 
"internal_proj_create_operations: " ) );
 
  512   if ( !nonAvailableError.isEmpty() )
 
  514     if ( sCoordinateOperationCreationErrorHandler )
 
  516       sCoordinateOperationCreationErrorHandler( mSourceCRS, mDestCRS, nonAvailableError );
 
  520       const QString err = QObject::tr( 
"Cannot create transform between %1 and %2: %3" ).arg( mSourceCRS.authid(),
 
  528   proj_log_func( context, 
nullptr, proj_logger );
 
  536   ProjData res = transform.release();
 
  537   mProjProjections.insert( 
reinterpret_cast< uintptr_t
>( context ), res );
 
  541 ProjData QgsCoordinateTransformPrivate::threadLocalFallbackProjData()
 
  546   const QMap < uintptr_t, ProjData >::const_iterator it = mProjFallbackProjections.constFind( 
reinterpret_cast< uintptr_t
>( context ) );
 
  548   if ( it != mProjFallbackProjections.constEnd() )
 
  550     ProjData res = it.value();
 
  559     transform.reset( proj_normalize_for_visualization( 
QgsProjContext::get(), transform.get() ) );
 
  561   ProjData res = transform.release();
 
  562   mProjFallbackProjections.insert( 
reinterpret_cast< uintptr_t
>( context ), res );
 
  568   sMissingRequiredGridHandler = handler;
 
  573   sMissingPreferredGridHandler = handler;
 
  578   sCoordinateOperationCreationErrorHandler = handler;
 
  583   sMissingGridUsedByContextHandler = handler;
 
  588   sDynamicCrsToDynamicCrsWarningHandler = handler;
 
  591 void QgsCoordinateTransformPrivate::freeProj()
 
  594   if ( mProjProjections.isEmpty() && mProjFallbackProjections.isEmpty() )
 
  596   QMap < uintptr_t, ProjData >::const_iterator it = mProjProjections.constBegin();
 
  603   PJ_CONTEXT *tmpContext = proj_context_create();
 
  604   for ( ; it != mProjProjections.constEnd(); ++it )
 
  606     proj_assign_context( it.value(), tmpContext );
 
  607     proj_destroy( it.value() );
 
  610   it = mProjFallbackProjections.constBegin();
 
  611   for ( ; it != mProjFallbackProjections.constEnd(); ++it )
 
  613     proj_assign_context( it.value(), tmpContext );
 
  614     proj_destroy( it.value() );
 
  617   proj_context_destroy( tmpContext );
 
  618   mProjProjections.clear();
 
  619   mProjFallbackProjections.clear();
 
  622 bool QgsCoordinateTransformPrivate::removeObjectsBelongingToCurrentThread( 
void *pj_context )
 
  626   QMap < uintptr_t, ProjData >::iterator it = mProjProjections.find( 
reinterpret_cast< uintptr_t
>( pj_context ) );
 
  627   if ( it != mProjProjections.end() )
 
  629     proj_destroy( it.value() );
 
  630     mProjProjections.erase( it );
 
  633   it = mProjFallbackProjections.find( 
reinterpret_cast< uintptr_t
>( pj_context ) );
 
  634   if ( it != mProjFallbackProjections.end() )
 
  636     proj_destroy( it.value() );
 
  637     mProjFallbackProjections.erase( it );
 
  640   return mProjProjections.isEmpty();