24 #if PROJ_VERSION_MAJOR>=6 27 #include <proj_experimental.h> 34 #include <QStringList> 49 const QString &error )> QgsCoordinateTransformPrivate::sCoordinateOperationCreationErrorHandler =
nullptr;
55 #if PROJ_VERSION_MAJOR<6 56 #ifdef USE_THREAD_LOCAL 57 thread_local QgsProjContextStore QgsCoordinateTransformPrivate::mProjContext;
59 QThreadStorage< QgsProjContextStore * > QgsCoordinateTransformPrivate::mProjContext;
62 QgsProjContextStore::QgsProjContextStore()
64 context = pj_ctx_alloc();
67 QgsProjContextStore::~QgsProjContextStore()
69 pj_ctx_free( context );
75 QgsCoordinateTransformPrivate::QgsCoordinateTransformPrivate()
84 : mSourceCRS( source )
85 , mDestCRS( destination )
87 if ( mSourceCRS != mDestCRS )
88 calculateTransforms( context );
94 : mSourceCRS( source )
95 , mDestCRS( destination )
96 , mSourceDatumTransform( sourceDatumTransform )
97 , mDestinationDatumTransform( destDatumTransform )
101 QgsCoordinateTransformPrivate::QgsCoordinateTransformPrivate(
const QgsCoordinateTransformPrivate &other )
102 : QSharedData( other )
103 #if PROJ_VERSION_MAJOR >= 6
104 , mAvailableOpCount( other.mAvailableOpCount )
106 , mIsValid( other.mIsValid )
107 , mShortCircuit( other.mShortCircuit )
108 , mSourceCRS( other.mSourceCRS )
109 , mDestCRS( other.mDestCRS )
110 , mSourceDatumTransform( other.mSourceDatumTransform )
111 , mDestinationDatumTransform( other.mDestinationDatumTransform )
112 , mProjCoordinateOperation( other.mProjCoordinateOperation )
113 , mShouldReverseCoordinateOperation( other.mShouldReverseCoordinateOperation )
114 , mAllowFallbackTransforms( other.mAllowFallbackTransforms )
115 , mIsReversed( other.mIsReversed )
117 #if PROJ_VERSION_MAJOR < 6 125 QgsCoordinateTransformPrivate::~QgsCoordinateTransformPrivate()
132 bool QgsCoordinateTransformPrivate::checkValidity()
134 if ( !mSourceCRS.isValid() || !mDestCRS.isValid() )
142 void QgsCoordinateTransformPrivate::invalidate()
144 mShortCircuit =
true;
146 #if PROJ_VERSION_MAJOR >= 6 147 mAvailableOpCount = -1;
151 bool QgsCoordinateTransformPrivate::initialize()
154 if ( !mSourceCRS.isValid() )
162 if ( !mDestCRS.isValid() )
166 mDestCRS = mSourceCRS;
173 if ( mSourceCRS == mDestCRS )
177 mShortCircuit =
true;
184 #if PROJ_VERSION_MAJOR < 6 186 int sourceDatumTransform = mSourceDatumTransform;
187 int destDatumTransform = mDestinationDatumTransform;
188 bool useDefaultDatumTransform = ( sourceDatumTransform == - 1 && destDatumTransform == -1 );
190 mSourceProjString = mSourceCRS.toProj();
191 if ( !useDefaultDatumTransform )
193 mSourceProjString = stripDatumTransform( mSourceProjString );
195 if ( sourceDatumTransform != -1 )
200 mDestProjString = mDestCRS.toProj();
201 if ( !useDefaultDatumTransform )
203 mDestProjString = stripDatumTransform( mDestProjString );
205 if ( destDatumTransform != -1 )
210 if ( !useDefaultDatumTransform )
212 addNullGridShifts( mSourceProjString, mDestProjString, sourceDatumTransform, destDatumTransform );
218 ProjData res = threadLocalProjData();
220 #ifdef COORDINATE_TRANSFORM_VERBOSE 221 QgsDebugMsg(
"From proj : " + mSourceCRS.toProj() );
225 #if PROJ_VERSION_MAJOR>=6 229 if ( !res.first || !res.second )
235 #ifdef COORDINATE_TRANSFORM_VERBOSE 238 QgsDebugMsg( QStringLiteral(
"------------------------------------------------------------" ) );
239 QgsDebugMsg( QStringLiteral(
"The OGR Coordinate transformation for this layer was set to" ) );
240 QgsLogger::debug<QgsCoordinateReferenceSystem>(
"Input", mSourceCRS, __FILE__, __FUNCTION__, __LINE__ );
241 QgsLogger::debug<QgsCoordinateReferenceSystem>(
"Output", mDestCRS, __FILE__, __FUNCTION__, __LINE__ );
242 QgsDebugMsg( QStringLiteral(
"------------------------------------------------------------" ) );
246 QgsDebugMsg( QStringLiteral(
"------------------------------------------------------------" ) );
247 QgsDebugMsg( QStringLiteral(
"The OGR Coordinate transformation FAILED TO INITIALIZE!" ) );
248 QgsDebugMsg( QStringLiteral(
"------------------------------------------------------------" ) );
253 QgsDebugMsg( QStringLiteral(
"Coordinate transformation failed to initialize!" ) );
258 mShortCircuit =
false;
266 #if PROJ_VERSION_MAJOR >= 6 267 if ( mSourceCRS.isValid() && mDestCRS.isValid() )
275 mProjCoordinateOperation.clear();
276 mShouldReverseCoordinateOperation =
false;
277 mAllowFallbackTransforms =
false;
283 mDestinationDatumTransform = transforms.destinationTransformId;
288 #if PROJ_VERSION_MAJOR>=6 289 static void proj_collecting_logger(
void *user_data,
int ,
const char *message )
291 QStringList *dest =
reinterpret_cast< QStringList *
>( user_data );
292 dest->append( QString( message ) );
295 static void proj_logger(
void *,
int level,
const char *message )
300 if ( level == PJ_LOG_ERROR )
304 else if ( level == PJ_LOG_DEBUG )
311 ProjData QgsCoordinateTransformPrivate::threadLocalProjData()
315 #if PROJ_VERSION_MAJOR>=6 317 QMap < uintptr_t, ProjData >::const_iterator it = mProjProjections.constFind( reinterpret_cast< uintptr_t>( context ) );
319 #ifdef USE_THREAD_LOCAL 320 QMap < uintptr_t, QPair< projPJ, projPJ > >::const_iterator it = mProjProjections.constFind( reinterpret_cast< uintptr_t>( mProjContext.get() ) );
322 projCtx pContext =
nullptr;
323 if ( mProjContext.hasLocalData() )
325 pContext = mProjContext.localData()->get();
329 mProjContext.setLocalData(
new QgsProjContextStore() );
330 pContext = mProjContext.localData()->get();
332 QMap < uintptr_t, QPair< projPJ, projPJ > >::const_iterator it = mProjProjections.constFind( reinterpret_cast< uintptr_t>( pContext ) );
336 if ( it != mProjProjections.constEnd() )
338 ProjData res = it.value();
345 #if PROJ_VERSION_MAJOR>=6 347 QStringList projErrors;
348 proj_log_func( context, &projErrors, proj_collecting_logger );
352 QgsProjUtils::proj_pj_unique_ptr transform;
353 if ( !mProjCoordinateOperation.isEmpty() )
355 transform.reset( proj_create( context, mProjCoordinateOperation.toUtf8().constData() ) );
356 if ( !transform || !proj_coordoperation_is_instantiable( context, transform.get() ) )
358 if ( sMissingGridUsedByContextHandler )
361 desired.
proj = mProjCoordinateOperation;
363 desired.
grids = QgsProjUtils::gridsUsed( mProjCoordinateOperation );
364 sMissingGridUsedByContextHandler( mSourceCRS, mDestCRS, desired );
368 const QString err = QObject::tr(
"Could not use operation specified in project between %1 and %2. (Wanted to use: %3)." ).arg( mSourceCRS.authid(),
370 mProjCoordinateOperation );
378 mIsReversed = mShouldReverseCoordinateOperation;
382 QString nonAvailableError;
385 if ( !mSourceCRS.projObject() || ! mDestCRS.projObject() )
387 proj_log_func( context,
nullptr,
nullptr );
391 PJ_OPERATION_FACTORY_CONTEXT *operationContext = proj_create_operation_factory_context( context,
nullptr );
394 proj_operation_factory_context_set_grid_availability_use( context, operationContext, PROJ_GRID_AVAILABILITY_IGNORED );
397 proj_operation_factory_context_set_spatial_criterion( context, operationContext, PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION );
399 if ( PJ_OBJ_LIST *ops = proj_create_operations( context, mSourceCRS.projObject(), mDestCRS.projObject(), operationContext ) )
401 mAvailableOpCount = proj_list_get_count( ops );
402 if ( mAvailableOpCount < 1 )
405 int errNo = proj_context_errno( context );
406 if ( errNo && errNo != -61 )
408 nonAvailableError = QString( proj_errno_string( errNo ) );
412 nonAvailableError = QObject::tr(
"No coordinate operations are available between these two reference systems" );
415 else if ( mAvailableOpCount == 1 )
418 transform.reset( proj_list_get( context, ops, 0 ) );
421 if ( !proj_coordoperation_is_instantiable( context, transform.get() ) )
424 for (
int j = 0; j < proj_coordoperation_get_grid_used_count( context, transform.get() ); ++j )
426 const char *shortName =
nullptr;
427 const char *fullName =
nullptr;
428 const char *packageName =
nullptr;
429 const char *url =
nullptr;
430 int directDownload = 0;
433 proj_coordoperation_get_grid_used( context, transform.get(), j, &shortName, &fullName, &packageName, &url, &directDownload, &openLicense, &isAvailable );
437 if ( sMissingRequiredGridHandler )
440 gridDetails.
shortName = QString( shortName );
441 gridDetails.
fullName = QString( fullName );
443 gridDetails.
url = QString( url );
447 sMissingRequiredGridHandler( mSourceCRS, mDestCRS, gridDetails );
451 const QString err = QObject::tr(
"Cannot create transform between %1 and %2, missing required grid %3" ).arg( mSourceCRS.authid(),
464 transform.reset( proj_normalize_for_visualization( context, transform.get() ) );
467 const QString err = QObject::tr(
"Cannot normalize transform between %1 and %2" ).arg( mSourceCRS.authid(),
478 bool missingPreferred =
false;
479 bool stillLookingForPreferred =
true;
480 for (
int i = 0; i < mAvailableOpCount; ++ i )
482 transform.reset( proj_list_get( context, ops, i ) );
483 const bool isInstantiable = transform && proj_coordoperation_is_instantiable( context, transform.get() );
484 if ( stillLookingForPreferred && transform && !isInstantiable )
488 if ( !candidate.
proj.isEmpty() )
490 preferred = candidate;
491 missingPreferred =
true;
492 stillLookingForPreferred =
false;
495 if ( transform && isInstantiable )
503 if ( transform && missingPreferred )
507 if ( sMissingPreferredGridHandler )
509 sMissingPreferredGridHandler( mSourceCRS, mDestCRS, preferred, available );
513 const QString err = QObject::tr(
"Using non-preferred coordinate operation between %1 and %2. Using %3, preferred %4." ).arg( mSourceCRS.authid(),
523 transform.reset( proj_normalize_for_visualization( context, transform.get() ) );
526 const QString err = QObject::tr(
"Cannot normalize transform between %1 and %2" ).arg( mSourceCRS.authid(),
531 proj_list_destroy( ops );
533 proj_operation_factory_context_destroy( operationContext );
536 if ( !transform && nonAvailableError.isEmpty() )
538 int errNo = proj_context_errno( context );
539 if ( errNo && errNo != -61 )
541 nonAvailableError = QString( proj_errno_string( errNo ) );
543 else if ( !projErrors.empty() )
545 nonAvailableError = projErrors.constLast();
548 if ( nonAvailableError.isEmpty() )
550 nonAvailableError = QObject::tr(
"No coordinate operations are available between these two reference systems" );
555 nonAvailableError = nonAvailableError.remove( QStringLiteral(
"internal_proj_create_operations: " ) );
559 if ( !nonAvailableError.isEmpty() )
561 if ( sCoordinateOperationCreationErrorHandler )
563 sCoordinateOperationCreationErrorHandler( mSourceCRS, mDestCRS, nonAvailableError );
567 const QString err = QObject::tr(
"Cannot create transform between %1 and %2: %3" ).arg( mSourceCRS.authid(),
575 proj_log_func( context,
nullptr, proj_logger );
583 ProjData res = transform.release();
584 mProjProjections.insert( reinterpret_cast< uintptr_t>( context ), res );
586 #ifdef USE_THREAD_LOCAL 588 QPair<projPJ, projPJ> res = qMakePair( pj_init_plus_ctx( mProjContext.get(), mSourceProjString.toUtf8() ),
589 pj_init_plus_ctx( mProjContext.get(), mDestProjString.toUtf8() ) );
591 mProjProjections.insert( reinterpret_cast< uintptr_t>( mProjContext.get() ), res );
593 QPair<projPJ, projPJ> res = qMakePair( pj_init_plus_ctx( pContext, mSourceProjString.toUtf8() ),
594 pj_init_plus_ctx( pContext, mDestProjString.toUtf8() ) );
595 mProjProjections.insert( reinterpret_cast< uintptr_t>( pContext ), res );
601 #if PROJ_VERSION_MAJOR>=6 602 ProjData QgsCoordinateTransformPrivate::threadLocalFallbackProjData()
607 QMap < uintptr_t, ProjData >::const_iterator it = mProjFallbackProjections.constFind( reinterpret_cast< uintptr_t>( context ) );
609 if ( it != mProjFallbackProjections.constEnd() )
611 ProjData res = it.value();
618 QgsProjUtils::proj_pj_unique_ptr transform( proj_create_crs_to_crs_from_pj( context, mSourceCRS.projObject(), mDestCRS.projObject(),
nullptr, nullptr ) );
620 transform.reset( proj_normalize_for_visualization(
QgsProjContext::get(), transform.get() ) );
622 ProjData res = transform.release();
623 mProjFallbackProjections.insert( reinterpret_cast< uintptr_t>( context ), res );
630 sMissingRequiredGridHandler = handler;
635 sMissingPreferredGridHandler = handler;
640 sCoordinateOperationCreationErrorHandler = handler;
645 sMissingGridUsedByContextHandler = handler;
648 #if PROJ_VERSION_MAJOR<6 649 QString QgsCoordinateTransformPrivate::stripDatumTransform(
const QString &proj4 )
const 651 QStringList parameterSplit = proj4.split(
'+', QString::SkipEmptyParts );
652 QString currentParameter;
653 QString newProjString;
655 for (
int i = 0; i < parameterSplit.size(); ++i )
657 currentParameter = parameterSplit.at( i );
658 if ( !currentParameter.startsWith( QLatin1String(
"towgs84" ), Qt::CaseInsensitive )
659 && !currentParameter.startsWith( QLatin1String(
"nadgrids" ), Qt::CaseInsensitive ) )
661 newProjString.append(
'+' );
662 newProjString.append( currentParameter );
663 newProjString.append(
' ' );
666 return newProjString;
669 void QgsCoordinateTransformPrivate::addNullGridShifts( QString &srcProjString, QString &destProjString,
670 int sourceDatumTransform,
int destinationDatumTransform )
const 673 if ( destinationDatumTransform == -1 && srcProjString.contains( QLatin1String(
"+nadgrids" ) ) )
675 destProjString += QLatin1String(
" +nadgrids=@null" );
678 if ( sourceDatumTransform == -1 && destProjString.contains( QLatin1String(
"+nadgrids" ) ) )
680 srcProjString += QLatin1String(
" +nadgrids=@null" );
686 if ( mSourceCRS.authid().compare( QLatin1String(
"EPSG:3857" ), Qt::CaseInsensitive ) == 0 && sourceDatumTransform == -1 )
688 srcProjString += QLatin1String(
" +nadgrids=@null" );
690 if ( mDestCRS.authid().compare( QLatin1String(
"EPSG:3857" ), Qt::CaseInsensitive ) == 0 && destinationDatumTransform == -1 )
692 destProjString += QLatin1String(
" +nadgrids=@null" );
697 void QgsCoordinateTransformPrivate::freeProj()
700 if ( mProjProjections.isEmpty() && mProjFallbackProjections.isEmpty() )
702 QMap < uintptr_t, ProjData >::const_iterator it = mProjProjections.constBegin();
703 #if PROJ_VERSION_MAJOR>=6 709 PJ_CONTEXT *tmpContext = proj_context_create();
710 for ( ; it != mProjProjections.constEnd(); ++it )
712 proj_assign_context( it.value(), tmpContext );
713 proj_destroy( it.value() );
716 it = mProjFallbackProjections.constBegin();
717 for ( ; it != mProjFallbackProjections.constEnd(); ++it )
719 proj_assign_context( it.value(), tmpContext );
720 proj_destroy( it.value() );
723 proj_context_destroy( tmpContext );
725 projCtx tmpContext = pj_ctx_alloc();
726 for ( ; it != mProjProjections.constEnd(); ++it )
728 pj_set_ctx( it.value().first, tmpContext );
729 pj_free( it.value().first );
730 pj_set_ctx( it.value().second, tmpContext );
731 pj_free( it.value().second );
733 pj_ctx_free( tmpContext );
735 mProjProjections.clear();
736 mProjFallbackProjections.clear();
739 #if PROJ_VERSION_MAJOR>=6 740 bool QgsCoordinateTransformPrivate::removeObjectsBelongingToCurrentThread(
void *pj_context )
744 QMap < uintptr_t, ProjData >::iterator it = mProjProjections.find( reinterpret_cast< uintptr_t>( pj_context ) );
745 if ( it != mProjProjections.end() )
747 proj_destroy( it.value() );
748 mProjProjections.erase( it );
751 it = mProjFallbackProjections.find( reinterpret_cast< uintptr_t>( pj_context ) );
752 if ( it != mProjFallbackProjections.end() )
754 proj_destroy( it.value() );
755 mProjFallbackProjections.erase( it );
758 return mProjProjections.isEmpty();
#define Q_NOWARN_DEPRECATED_PUSH
The QgsReadWriteLocker class is a convenience class that simplifies locking and unlocking QReadWriteL...
#define QgsDebugMsgLevel(str, level)
bool allowFallbackTransform(const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination) const
Returns true if approximate "ballpark" transforms may be used when transforming between a source and ...
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Contains information about the context in which a coordinate transform is executed.
#define Q_NOWARN_DEPRECATED_POP
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...
Q_DECL_DEPRECATED QgsDatumTransform::TransformPair calculateDatumTransforms(const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination) const
Returns the pair of source and destination datum transforms to use for a transform from the specified...
This class represents a coordinate reference system (CRS).
static PJ_CONTEXT * get()
Returns a thread local instance of a proj context, safe for use in the current thread.
bool mustReverseCoordinateOperation(const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination) const
Returns true if the coordinate operation returned by calculateCoordinateOperation() for the source to...