Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #41927 from nyalldawson/pdal_warn
Show user-friendly explanation if las/laz files cannot be used on their QGIS install
  • Loading branch information
nyalldawson committed Mar 2, 2021
1 parent 2436862 commit da9a255
Show file tree
Hide file tree
Showing 5 changed files with 340 additions and 1 deletion.
120 changes: 120 additions & 0 deletions python/core/auto_generated/qgsproviderregistry.sip.in
Expand Up @@ -12,6 +12,7 @@




class QgsProviderRegistry
{
%Docstring
Expand Down Expand Up @@ -337,6 +338,125 @@ all of these providers will be returned.
.. seealso:: :py:func:`shouldDeferUriForOtherProviders`

.. versionadded:: 3.18
%End

class UnusableUriDetails
{
%Docstring

Contains information about unusable URIs which aren't handled by any registered providers.

For example, if a QGIS install is built without the PDAL library then las/laz files are unusable.
This class can then be used to construct friendly warnings to users advising them why the las/laz
files cannot be used on their QGIS build.

.. versionadded:: 3.18.1
%End

%TypeHeaderCode
#include "qgsproviderregistry.h"
%End
public:

UnusableUriDetails( const QString &uri = QString(), const QString &warning = QString(), const QList< QgsMapLayerType > &layerTypes = QList< QgsMapLayerType >() );
%Docstring
Constructor for UnusableUriDetails for the given ``uri``, with the specified user-friendly, translated ``warning``.

The optional ``layerTypes`` argument can be used to specify layer types which are usually valid
options for opening the URI.
%End

QString uri;

QString warning;

QString detailedWarning;

QList<QgsMapLayerType> layerTypes;

SIP_PYOBJECT __repr__();
%MethodCode
QString str = QStringLiteral( "<QgsProviderRegistry.UnusableUriDetails: %1>" ).arg( sipCpp->warning );
sipRes = PyUnicode_FromString( str.toUtf8().constData() );
%End

};

class UnusableUriHandlerInterface
{
%Docstring

An interface used to handle unusable URIs which aren't handled by any registered providers, and construct
user-friendly warnings as to why the URI is unusable.

For example, if a QGIS install is built without the PDAL library then las/laz files are unusable.
This class can then be used to construct friendly warnings to users advising them why the las/laz
files cannot be used on their QGIS build.

.. versionadded:: 3.18.1
%End

%TypeHeaderCode
#include "qgsproviderregistry.h"
%End
public:

virtual ~UnusableUriHandlerInterface();

virtual bool matchesUri( const QString &uri ) const = 0;
%Docstring
Returns ``True`` if the handle is an unusable URI handler for the specified ``uri``.
%End

virtual UnusableUriDetails details( const QString &uri ) const = 0;
%Docstring
Returns the details for advising the user why the ``uri`` is not usable.
%End

};

bool registerUnusableUriHandler( UnusableUriHandlerInterface *handler /Transfer/ );
%Docstring
Registers an unusable URI ``handler``, used to handle unusable URIs which aren't
handled by any registered providers, and construct user-friendly warnings as to why the URI is unusable.

:return: ``True`` on success

.. note::

ownership of the UnusableUriHandlerInterface instance is transferred to the registry


.. versionadded:: 3.18.1
%End

bool handleUnusableUri( const QString &uri, UnusableUriDetails &details /Out/ ) const;
%Docstring
Returns ``True`` if the specified ``uri`` can potentially be handled by QGIS, if additional
dependencies or build-time requirements are present.

This can be used to show user-friendly warning messages advising them why a particular
``uri`` cannot be opened on their QGIS install. For example, if a QGIS install is built
without the PDAL library then las/laz files are unusable, and this method can be used
to retrieve a user-friendly warning as to why the las/laz files cannot be used on their
QGIS build.

.. warning::

This method does not perform the test to actually determine if the given ``uri``
can be handled by any registered provider. It is assumed that prior to calling this method
the caller has already determined in advance that the ``uri`` could not be handled.

:param uri: URI to test


:return: - ``True`` if the ``uri`` was matched to a registered QgsProviderRegistry.UnusableUriHandlerInterface.
- details: will be populated with details allowing construction of a user-friendly warning message


.. seealso:: :py:func:`registerUnusableUriHandler`

.. versionadded:: 3.18.1
%End

bool shouldDeferUriForOtherProviders( const QString &uri, const QString &providerKey ) const;
Expand Down
14 changes: 14 additions & 0 deletions src/app/qgisapp.cpp
Expand Up @@ -7643,6 +7643,20 @@ bool QgisApp::openLayer( const QString &fileName, bool allowInteractive )
ok = static_cast< bool >( addMeshLayerPrivate( fileName, fileInfo.completeBaseName(), QStringLiteral( "mdal" ), false ) );
}

// maybe a known file type, which couldn't be opened due to a missing dependency... (eg. las for a non-pdal-enabled build)
{
QgsProviderRegistry::UnusableUriDetails details;
if ( QgsProviderRegistry::instance()->handleUnusableUri( fileName, details ) )
{
ok = true;

if ( details.detailedWarning.isEmpty() )
visibleMessageBar()->pushMessage( QString(), details.warning, Qgis::Critical );
else
visibleMessageBar()->pushMessage( QString(), details.warning, details.detailedWarning, Qgis::Critical );
}
}

if ( !ok )
{
// we have no idea what this file is...
Expand Down
56 changes: 56 additions & 0 deletions src/core/qgsproviderregistry.cpp
Expand Up @@ -117,6 +117,38 @@ QgsProviderRegistry::QgsProviderRegistry( const QString &pluginPath )
init();
}

///@cond PRIVATE
class PdalUnusableUriHandlerInterface : public QgsProviderRegistry::UnusableUriHandlerInterface
{
public:
bool matchesUri( const QString &uri ) const override
{
const QFileInfo fi( uri );
if ( fi.suffix().compare( QLatin1String( "las" ), Qt::CaseInsensitive ) == 0 || fi.suffix().compare( QLatin1String( "laz" ), Qt::CaseInsensitive ) == 0 )
return true;

return false;
}

QgsProviderRegistry::UnusableUriDetails details( const QString &uri ) const override
{
QgsProviderRegistry::UnusableUriDetails res = QgsProviderRegistry::UnusableUriDetails( uri,
QObject::tr( "LAS and LAZ files cannot be opened by this QGIS install." ),
QList<QgsMapLayerType>() << QgsMapLayerType::PointCloudLayer );

#ifdef Q_OS_WIN
res.detailedWarning = QObject::tr( "The installer used to install this version of QGIS does "
"not include the PDAL library required for opening LAS and LAZ point clouds. Please "
"obtain one of the alternative installers from https://qgis.org which has point "
"cloud support enabled." );
#else
res.detailedWarning = QObject::tr( "This QGIS build does not include the PDAL library dependency required for opening LAS or LAZ point clouds." );
#endif
return res;
}
};
///@endcond

void QgsProviderRegistry::init()
{
// add static providers
Expand Down Expand Up @@ -150,6 +182,9 @@ void QgsProviderRegistry::init()
mProviders[ pc->key() ] = pc;
}
#endif

registerUnusableUriHandler( new PdalUnusableUriHandlerInterface() );

#ifdef HAVE_STATIC_PROVIDERS
mProviders[ QgsWmsProvider::providerKey() ] = new QgsWmsProviderMetadata();
mProviders[ QgsPostgresProvider::providerKey() ] = new QgsPostgresProviderMetadata();
Expand Down Expand Up @@ -379,6 +414,8 @@ bool QgsProviderRegistry::exists()

QgsProviderRegistry::~QgsProviderRegistry()
{
qDeleteAll( mUnusableUriHandlers );

clean();
if ( sInstance == this )
sInstance = nullptr;
Expand Down Expand Up @@ -807,6 +844,25 @@ QList<QgsProviderRegistry::ProviderCandidateDetails> QgsProviderRegistry::prefer
return res;
}

bool QgsProviderRegistry::registerUnusableUriHandler( QgsProviderRegistry::UnusableUriHandlerInterface *handler )
{
mUnusableUriHandlers << handler;
return true;
}

bool QgsProviderRegistry::handleUnusableUri( const QString &uri, UnusableUriDetails &details ) const
{
for ( const QgsProviderRegistry::UnusableUriHandlerInterface *handler : mUnusableUriHandlers )
{
if ( handler->matchesUri( uri ) )
{
details = handler->details( uri );
return true;
}
}
return false;
}

bool QgsProviderRegistry::shouldDeferUriForOtherProviders( const QString &uri, const QString &providerKey ) const
{
const QList< ProviderCandidateDetails > providers = preferredProvidersForUri( uri );
Expand Down
131 changes: 131 additions & 0 deletions src/core/qgsproviderregistry.h
Expand Up @@ -30,6 +30,9 @@
#include "qgis_core.h"
#include "qgis_sip.h"

#include <vector>
#include <memory>

class QgsProviderMetadata;
class QgsVectorLayer;
class QgsCoordinateReferenceSystem;
Expand Down Expand Up @@ -363,6 +366,132 @@ class CORE_EXPORT QgsProviderRegistry
*/
QList< QgsProviderRegistry::ProviderCandidateDetails > preferredProvidersForUri( const QString &uri ) const;

/**
* \ingroup core
*
* \brief Contains information about unusable URIs which aren't handled by any registered providers.
*
* For example, if a QGIS install is built without the PDAL library then las/laz files are unusable.
* This class can then be used to construct friendly warnings to users advising them why the las/laz
* files cannot be used on their QGIS build.
*
* \since QGIS 3.18.1
*/
class CORE_EXPORT UnusableUriDetails
{
public:

/**
* Constructor for UnusableUriDetails for the given \a uri, with the specified user-friendly, translated \a warning.
*
* The optional \a layerTypes argument can be used to specify layer types which are usually valid
* options for opening the URI.
*/
UnusableUriDetails( const QString &uri = QString(), const QString &warning = QString(), const QList< QgsMapLayerType > &layerTypes = QList< QgsMapLayerType >() )
: uri( uri )
, warning( warning )
, layerTypes( layerTypes )
{}

/**
* URI which could not be handled.
*/
QString uri;

/**
* Contains a short, user-friendly, translated message advising why the URI is not usable.
*/
QString warning;

/**
* Contains a longer, user-friendly, translated message advising why the URI is not usable.
*/
QString detailedWarning;

/**
* Contains a list of map layer types which are usually valid options for opening the
* target URI.
*/
QList<QgsMapLayerType> layerTypes;

#ifdef SIP_RUN
SIP_PYOBJECT __repr__();
% MethodCode
QString str = QStringLiteral( "<QgsProviderRegistry.UnusableUriDetails: %1>" ).arg( sipCpp->warning );
sipRes = PyUnicode_FromString( str.toUtf8().constData() );
% End
#endif

};

/**
* \ingroup core
*
* \brief An interface used to handle unusable URIs which aren't handled by any registered providers, and construct
* user-friendly warnings as to why the URI is unusable.
*
* For example, if a QGIS install is built without the PDAL library then las/laz files are unusable.
* This class can then be used to construct friendly warnings to users advising them why the las/laz
* files cannot be used on their QGIS build.
*
* \since QGIS 3.18.1
*/
class CORE_EXPORT UnusableUriHandlerInterface
{

public:

virtual ~UnusableUriHandlerInterface() = default;

/**
* Returns TRUE if the handle is an unusable URI handler for the specified \a uri.
*/
virtual bool matchesUri( const QString &uri ) const = 0;

/**
* Returns the details for advising the user why the \a uri is not usable.
*/
virtual UnusableUriDetails details( const QString &uri ) const = 0;

};

/**
* \brief Registers an unusable URI \a handler, used to handle unusable URIs which aren't
* handled by any registered providers, and construct user-friendly warnings as to why the URI is unusable.
*
* \return TRUE on success
*
* \note ownership of the UnusableUriHandlerInterface instance is transferred to the registry
*
* \since QGIS 3.18.1
*/
bool registerUnusableUriHandler( UnusableUriHandlerInterface *handler SIP_TRANSFER );

/**
* Returns TRUE if the specified \a uri can potentially be handled by QGIS, if additional
* dependencies or build-time requirements are present.
*
* This can be used to show user-friendly warning messages advising them why a particular
* \a uri cannot be opened on their QGIS install. For example, if a QGIS install is built
* without the PDAL library then las/laz files are unusable, and this method can be used
* to retrieve a user-friendly warning as to why the las/laz files cannot be used on their
* QGIS build.
*
* \warning This method does not perform the test to actually determine if the given \a uri
* can be handled by any registered provider. It is assumed that prior to calling this method
* the caller has already determined in advance that the \a uri could not be handled.
*
* \param uri URI to test
* \param details will be populated with details allowing construction of a user-friendly
* warning message
*
* \returns TRUE if the \a uri was matched to a registered QgsProviderRegistry::UnusableUriHandlerInterface.
*
* \see registerUnusableUriHandler()
* \since QGIS 3.18.1
*/
bool handleUnusableUri( const QString &uri, UnusableUriDetails &details SIP_OUT ) const;

/**
* Returns TRUE if the provider with matching \a providerKey should defer handling of
* the specified \a uri to another provider.
Expand Down Expand Up @@ -557,6 +686,8 @@ class CORE_EXPORT QgsProviderRegistry
*/
QString mProtocolDrivers;

QList< UnusableUriHandlerInterface * > mUnusableUriHandlers;

/**
* Returns TRUE if registry instance exists.
*/
Expand Down

0 comments on commit da9a255

Please sign in to comment.