24 #if PROJ_VERSION_MAJOR>=6 33 #include <QStringList> 48 const QString &error )> QgsCoordinateTransformPrivate::sCoordinateOperationCreationErrorHandler =
nullptr;
54 #if PROJ_VERSION_MAJOR<6 55 #ifdef USE_THREAD_LOCAL 56 thread_local QgsProjContextStore QgsCoordinateTransformPrivate::mProjContext;
58 QThreadStorage< QgsProjContextStore * > QgsCoordinateTransformPrivate::mProjContext;
61 QgsProjContextStore::QgsProjContextStore()
63 context = pj_ctx_alloc();
66 QgsProjContextStore::~QgsProjContextStore()
68 pj_ctx_free( context );
74 QgsCoordinateTransformPrivate::QgsCoordinateTransformPrivate()
83 : mSourceCRS( source )
84 , mDestCRS( destination )
86 calculateTransforms( context );
92 : mSourceCRS( source )
93 , mDestCRS( destination )
94 , mSourceDatumTransform( sourceDatumTransform )
95 , mDestinationDatumTransform( destDatumTransform )
99 QgsCoordinateTransformPrivate::QgsCoordinateTransformPrivate(
const QgsCoordinateTransformPrivate &other )
100 : QSharedData( other )
101 , mIsValid( other.mIsValid )
102 , mShortCircuit( other.mShortCircuit )
103 , mSourceCRS( other.mSourceCRS )
104 , mDestCRS( other.mDestCRS )
105 , mSourceDatumTransform( other.mSourceDatumTransform )
106 , mDestinationDatumTransform( other.mDestinationDatumTransform )
107 , mProjCoordinateOperation( other.mProjCoordinateOperation )
109 #if PROJ_VERSION_MAJOR < 6 117 QgsCoordinateTransformPrivate::~QgsCoordinateTransformPrivate()
124 bool QgsCoordinateTransformPrivate::checkValidity()
126 if ( !mSourceCRS.isValid() || !mDestCRS.isValid() )
134 void QgsCoordinateTransformPrivate::invalidate()
136 mShortCircuit =
true;
140 bool QgsCoordinateTransformPrivate::initialize()
143 if ( !mSourceCRS.isValid() )
151 if ( !mDestCRS.isValid() )
155 mDestCRS = mSourceCRS;
165 #if PROJ_VERSION_MAJOR < 6 167 int sourceDatumTransform = mSourceDatumTransform;
168 int destDatumTransform = mDestinationDatumTransform;
169 bool useDefaultDatumTransform = ( sourceDatumTransform == - 1 && destDatumTransform == -1 );
171 mSourceProjString = mSourceCRS.toProj4();
172 if ( !useDefaultDatumTransform )
174 mSourceProjString = stripDatumTransform( mSourceProjString );
176 if ( sourceDatumTransform != -1 )
181 mDestProjString = mDestCRS.toProj4();
182 if ( !useDefaultDatumTransform )
184 mDestProjString = stripDatumTransform( mDestProjString );
186 if ( destDatumTransform != -1 )
191 if ( !useDefaultDatumTransform )
193 addNullGridShifts( mSourceProjString, mDestProjString, sourceDatumTransform, destDatumTransform );
199 ProjData res = threadLocalProjData();
201 #ifdef COORDINATE_TRANSFORM_VERBOSE 202 QgsDebugMsg(
"From proj : " + mSourceCRS.toProj4() );
206 #if PROJ_VERSION_MAJOR>=6 210 if ( !res.first || !res.second )
216 #ifdef COORDINATE_TRANSFORM_VERBOSE 219 QgsDebugMsg( QStringLiteral(
"------------------------------------------------------------" ) );
220 QgsDebugMsg( QStringLiteral(
"The OGR Coordinate transformation for this layer was set to" ) );
221 QgsLogger::debug<QgsCoordinateReferenceSystem>(
"Input", mSourceCRS, __FILE__, __FUNCTION__, __LINE__ );
222 QgsLogger::debug<QgsCoordinateReferenceSystem>(
"Output", mDestCRS, __FILE__, __FUNCTION__, __LINE__ );
223 QgsDebugMsg( QStringLiteral(
"------------------------------------------------------------" ) );
227 QgsDebugMsg( QStringLiteral(
"------------------------------------------------------------" ) );
228 QgsDebugMsg( QStringLiteral(
"The OGR Coordinate transformation FAILED TO INITIALIZE!" ) );
229 QgsDebugMsg( QStringLiteral(
"------------------------------------------------------------" ) );
234 QgsDebugMsg( QStringLiteral(
"Coordinate transformation failed to initialize!" ) );
241 if ( mSourceCRS == mDestCRS )
245 mShortCircuit =
true;
246 QgsDebugMsgLevel( QStringLiteral(
"Source/Dest CRS equal, shortcircuit is set." ), 3 );
251 mShortCircuit =
false;
252 QgsDebugMsgLevel( QStringLiteral(
"Source/Dest CRS not equal, shortcircuit is not set." ), 3 );
260 #if PROJ_VERSION_MAJOR >= 6 266 mDestinationDatumTransform = transforms.destinationTransformId;
271 #if PROJ_VERSION_MAJOR>=6 272 static void proj_collecting_logger(
void *user_data,
int ,
const char *message )
274 QStringList *dest =
reinterpret_cast< QStringList *
>( user_data );
275 dest->append( QString( message ) );
278 static void proj_logger(
void *,
int level,
const char *message )
280 if ( level == PJ_LOG_ERROR )
284 else if ( level == PJ_LOG_DEBUG )
291 ProjData QgsCoordinateTransformPrivate::threadLocalProjData()
295 #if PROJ_VERSION_MAJOR>=6 297 QMap < uintptr_t, ProjData >::const_iterator it = mProjProjections.constFind( reinterpret_cast< uintptr_t>( context ) );
299 #ifdef USE_THREAD_LOCAL 300 QMap < uintptr_t, QPair< projPJ, projPJ > >::const_iterator it = mProjProjections.constFind( reinterpret_cast< uintptr_t>( mProjContext.get() ) );
302 projCtx pContext =
nullptr;
303 if ( mProjContext.hasLocalData() )
305 pContext = mProjContext.localData()->get();
309 mProjContext.setLocalData(
new QgsProjContextStore() );
310 pContext = mProjContext.localData()->get();
312 QMap < uintptr_t, QPair< projPJ, projPJ > >::const_iterator it = mProjProjections.constFind( reinterpret_cast< uintptr_t>( pContext ) );
316 if ( it != mProjProjections.constEnd() )
318 ProjData res = it.value();
325 #if PROJ_VERSION_MAJOR>=6 327 QStringList projErrors;
328 proj_log_func( context, &projErrors, proj_collecting_logger );
330 QgsProjUtils::proj_pj_unique_ptr transform;
331 if ( !mProjCoordinateOperation.isEmpty() )
333 transform.reset( proj_create( context, mProjCoordinateOperation.toUtf8().constData() ) );
334 if ( !transform || !proj_coordoperation_is_instantiable( context, transform.get() ) )
336 if ( sMissingGridUsedByContextHandler )
339 desired.
proj = mProjCoordinateOperation;
341 desired.
grids = QgsProjUtils::gridsUsed( mProjCoordinateOperation );
342 sMissingGridUsedByContextHandler( mSourceCRS, mDestCRS, desired );
346 const QString err = QObject::tr(
"Could not use operation specified in project between %1 and %2. (Wanted to use: %3)." ).arg( mSourceCRS.authid(),
348 mProjCoordinateOperation );
356 QString nonAvailableError;
359 if ( !mSourceCRS.projObject() || ! mDestCRS.projObject() )
361 proj_log_func( context,
nullptr,
nullptr );
365 PJ_OPERATION_FACTORY_CONTEXT *operationContext = proj_create_operation_factory_context( context,
nullptr );
368 proj_operation_factory_context_set_grid_availability_use( context, operationContext, PROJ_GRID_AVAILABILITY_IGNORED );
371 proj_operation_factory_context_set_spatial_criterion( context, operationContext, PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION );
373 if ( PJ_OBJ_LIST *ops = proj_create_operations( context, mSourceCRS.projObject(), mDestCRS.projObject(), operationContext ) )
375 int count = proj_list_get_count( ops );
379 int errNo = proj_context_errno( context );
380 if ( errNo && errNo != -61 )
382 nonAvailableError = QString( proj_errno_string( errNo ) );
386 nonAvailableError = QObject::tr(
"No coordinate operations are available between these two reference systems" );
389 else if ( count == 1 )
392 transform.reset( proj_list_get( context, ops, 0 ) );
395 if ( !proj_coordoperation_is_instantiable( context, transform.get() ) )
398 for (
int j = 0; j < proj_coordoperation_get_grid_used_count( context, transform.get() ); ++j )
400 const char *shortName =
nullptr;
401 const char *fullName =
nullptr;
402 const char *packageName =
nullptr;
403 const char *url =
nullptr;
404 int directDownload = 0;
407 proj_coordoperation_get_grid_used( context, transform.get(), j, &shortName, &fullName, &packageName, &url, &directDownload, &openLicense, &isAvailable );
411 if ( sMissingRequiredGridHandler )
414 gridDetails.
shortName = QString( shortName );
415 gridDetails.
fullName = QString( fullName );
417 gridDetails.
url = QString( url );
421 sMissingRequiredGridHandler( mSourceCRS, mDestCRS, gridDetails );
425 const QString err = QObject::tr(
"Cannot create transform between %1 and %2, missing required grid %3" ).arg( mSourceCRS.authid(),
438 transform.reset( proj_normalize_for_visualization( context, transform.get() ) );
441 const QString err = QObject::tr(
"Cannot normalize transform between %1 and %2" ).arg( mSourceCRS.authid(),
452 bool missingPreferred =
false;
453 for (
int i = 0; i < count; ++ i )
455 transform.reset( proj_list_get( context, ops, i ) );
456 const bool isInstantiable = transform && proj_coordoperation_is_instantiable( context, transform.get() );
457 if ( i == 0 && transform && !isInstantiable )
460 missingPreferred =
true;
461 preferred = QgsDatumTransform::transformDetailsFromPj( transform.get() );
463 if ( transform && isInstantiable )
471 if ( transform && missingPreferred )
475 if ( sMissingPreferredGridHandler )
477 sMissingPreferredGridHandler( mSourceCRS, mDestCRS, preferred, available );
481 const QString err = QObject::tr(
"Using non-preferred coordinate operation between %1 and %2. Using %3, preferred %4." ).arg( mSourceCRS.authid(),
491 transform.reset( proj_normalize_for_visualization( context, transform.get() ) );
494 const QString err = QObject::tr(
"Cannot normalize transform between %1 and %2" ).arg( mSourceCRS.authid(),
499 proj_list_destroy( ops );
501 proj_operation_factory_context_destroy( operationContext );
504 if ( !transform && nonAvailableError.isEmpty() )
506 int errNo = proj_context_errno( context );
507 if ( errNo && errNo != -61 )
509 nonAvailableError = QString( proj_errno_string( errNo ) );
511 else if ( !projErrors.empty() )
513 nonAvailableError = projErrors.constLast();
516 if ( nonAvailableError.isEmpty() )
518 nonAvailableError = QObject::tr(
"No coordinate operations are available between these two reference systems" );
523 nonAvailableError = nonAvailableError.remove( QStringLiteral(
"internal_proj_create_operations: " ) );
527 if ( !nonAvailableError.isEmpty() )
529 if ( sCoordinateOperationCreationErrorHandler )
531 sCoordinateOperationCreationErrorHandler( mSourceCRS, mDestCRS, nonAvailableError );
535 const QString err = QObject::tr(
"Cannot create transform between %1 and %2: %3" ).arg( mSourceCRS.authid(),
543 proj_log_func( context,
nullptr, proj_logger );
551 ProjData res = transform.release();
552 mProjProjections.insert( reinterpret_cast< uintptr_t>( context ), res );
554 #ifdef USE_THREAD_LOCAL 556 QPair<projPJ, projPJ> res = qMakePair( pj_init_plus_ctx( mProjContext.get(), mSourceProjString.toUtf8() ),
557 pj_init_plus_ctx( mProjContext.get(), mDestProjString.toUtf8() ) );
559 mProjProjections.insert( reinterpret_cast< uintptr_t>( mProjContext.get() ), res );
561 QPair<projPJ, projPJ> res = qMakePair( pj_init_plus_ctx( pContext, mSourceProjString.toUtf8() ),
562 pj_init_plus_ctx( pContext, mDestProjString.toUtf8() ) );
563 mProjProjections.insert( reinterpret_cast< uintptr_t>( pContext ), res );
571 sMissingRequiredGridHandler = handler;
576 sMissingPreferredGridHandler = handler;
581 sCoordinateOperationCreationErrorHandler = handler;
586 sMissingGridUsedByContextHandler = handler;
589 #if PROJ_VERSION_MAJOR<6 590 QString QgsCoordinateTransformPrivate::stripDatumTransform(
const QString &proj4 )
const 592 QStringList parameterSplit = proj4.split(
'+', QString::SkipEmptyParts );
593 QString currentParameter;
594 QString newProjString;
596 for (
int i = 0; i < parameterSplit.size(); ++i )
598 currentParameter = parameterSplit.at( i );
599 if ( !currentParameter.startsWith( QLatin1String(
"towgs84" ), Qt::CaseInsensitive )
600 && !currentParameter.startsWith( QLatin1String(
"nadgrids" ), Qt::CaseInsensitive ) )
602 newProjString.append(
'+' );
603 newProjString.append( currentParameter );
604 newProjString.append(
' ' );
607 return newProjString;
610 void QgsCoordinateTransformPrivate::addNullGridShifts( QString &srcProjString, QString &destProjString,
611 int sourceDatumTransform,
int destinationDatumTransform )
const 614 if ( destinationDatumTransform == -1 && srcProjString.contains( QLatin1String(
"+nadgrids" ) ) )
616 destProjString += QLatin1String(
" +nadgrids=@null" );
619 if ( sourceDatumTransform == -1 && destProjString.contains( QLatin1String(
"+nadgrids" ) ) )
621 srcProjString += QLatin1String(
" +nadgrids=@null" );
627 if ( mSourceCRS.authid().compare( QLatin1String(
"EPSG:3857" ), Qt::CaseInsensitive ) == 0 && sourceDatumTransform == -1 )
629 srcProjString += QLatin1String(
" +nadgrids=@null" );
631 if ( mDestCRS.authid().compare( QLatin1String(
"EPSG:3857" ), Qt::CaseInsensitive ) == 0 && destinationDatumTransform == -1 )
633 destProjString += QLatin1String(
" +nadgrids=@null" );
638 void QgsCoordinateTransformPrivate::freeProj()
641 QMap < uintptr_t, ProjData >::const_iterator it = mProjProjections.constBegin();
642 for ( ; it != mProjProjections.constEnd(); ++it )
644 #if PROJ_VERSION_MAJOR>=6 645 proj_destroy( it.value() );
647 pj_free( it.value().first );
648 pj_free( it.value().second );
651 mProjProjections.clear();
#define Q_NOWARN_DEPRECATED_PUSH
The QgsReadWriteLocker class is a convenience class that simplifies locking and unlocking QReadWriteL...
#define QgsDebugMsgLevel(str, level)
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.