-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Add support for a vpc-in-zip (.vpz) virtual point cloud format #65334
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 4 commits
c556430
ae11e4a
f93529c
f0b0f0f
7233d9c
80d6694
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -38,9 +38,11 @@ | |
| #include "qgsproviderutils.h" | ||
| #include "qgsruntimeprofiler.h" | ||
| #include "qgsthreadingutils.h" | ||
| #include "qgsziputils.h" | ||
|
|
||
| #include <QIcon> | ||
| #include <QString> | ||
| #include <QTemporaryDir> | ||
|
|
||
| #include "moc_qgsvirtualpointcloudprovider.cpp" | ||
|
|
||
|
|
@@ -51,6 +53,7 @@ using namespace Qt::StringLiterals; | |
| #define PROVIDER_KEY u"vpc"_s | ||
| #define PROVIDER_DESCRIPTION u"Virtual point cloud data provider"_s | ||
|
|
||
|
|
||
| QgsVirtualPointCloudProvider::QgsVirtualPointCloudProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options, Qgis::DataProviderReadFlags flags ) | ||
| : QgsPointCloudDataProvider( uri, options, flags ) | ||
| { | ||
|
|
@@ -174,25 +177,31 @@ void QgsVirtualPointCloudProvider::parseFile() | |
| url.setUrl( path ); | ||
| QNetworkRequest request( url ); | ||
| const QgsNetworkReplyContent reply = QgsNetworkAccessManager::instance()->blockingGet( request, authcfg ); | ||
| if ( reply.error() == QNetworkReply::NoError ) | ||
| if ( reply.error() != QNetworkReply::NoError ) | ||
| { | ||
| jsonData = reply.content(); | ||
| appendError( QgsErrorMessage( u"Could not download file: %1"_s.arg( reply.errorString() ) ) ); | ||
| return; | ||
| } | ||
| else | ||
|
|
||
| QTemporaryDir tmpDir; | ||
| QFile file( tmpDir.filePath( url.fileName() ) ); | ||
| if ( !file.open( QFile::WriteOnly ) ) | ||
| { | ||
| appendError( QgsErrorMessage( u"Could not download file: %1"_s.arg( reply.errorString() ) ) ); | ||
| appendError( QgsErrorMessage( u"Could not create temporary file: %1"_s.arg( file.fileName() ) ) ); | ||
| return; | ||
| } | ||
|
|
||
| file.write( reply.content() ); | ||
| file.close(); | ||
|
|
||
| mAllLocalFiles = false; | ||
|
|
||
| jsonData = readFileContents( file.fileName() ); | ||
| } | ||
| else | ||
| { | ||
| url = QUrl::fromLocalFile( path ); | ||
| QFile file( url.toLocalFile() ); | ||
| if ( file.open( QFile::ReadOnly ) ) | ||
| { | ||
| jsonData = file.readAll(); | ||
| } | ||
| jsonData = readFileContents( url.toLocalFile() ); | ||
| } | ||
|
|
||
| if ( jsonData.isEmpty() ) | ||
|
|
@@ -467,6 +476,58 @@ void QgsVirtualPointCloudProvider::parseFile() | |
| populateAttributeCollection( attributeNames ); | ||
| } | ||
|
|
||
| QByteArray QgsVirtualPointCloudProvider::readFileContents( const QString &path ) | ||
| { | ||
| std::unique_ptr<QTemporaryDir> tmpDir; | ||
| QString readFromFilename; | ||
| if ( path.endsWith( ".vpz"_L1, Qt::CaseInsensitive ) ) | ||
| { | ||
| tmpDir = std::make_unique<QTemporaryDir>(); | ||
| if ( !tmpDir->isValid() ) | ||
| { | ||
| appendError( QgsErrorMessage( u"Could not create temporary folder"_s ) ); | ||
| return {}; | ||
| } | ||
| QStringList fileList; | ||
| if ( !QgsZipUtils::unzip( path, tmpDir->path(), fileList ) ) | ||
| { | ||
| appendError( QgsErrorMessage( u"Could not open VPZ file"_s ) ); | ||
| return {}; | ||
| } | ||
|
|
||
| const QDir dir( tmpDir->path() ); | ||
| for ( const QString &f : std::as_const( fileList ) ) | ||
| { | ||
| if ( f.endsWith( ".vpc"_L1, Qt::CaseInsensitive ) ) | ||
| { | ||
| if ( !readFromFilename.isEmpty() ) | ||
| { | ||
| appendError( QgsErrorMessage( u"VPZ file contains multiple VPCs"_s ) ); | ||
| return {}; | ||
| } | ||
| readFromFilename = dir.filePath( f ); | ||
| } | ||
| } | ||
|
Comment on lines
+498
to
+504
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we care if there are multiple VPCs in the file? Can we not just grab first and return early? We are only reading the ZIP and are not error detecting/correcting. I believe other software also just use the first file that has the correct extension in similar situations. No strong opinion. However, if you'd still prefer to have the check instead of an early return, consider something like this instead of the loop:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I prefer not to use the first vpc in case there are multiple, as that may not be obvious to the user that only a single vpc is being used. |
||
| if ( readFromFilename.isEmpty() ) | ||
| { | ||
| appendError( QgsErrorMessage( u"VPZ file does not contain any VPCs"_s ) ); | ||
| return {}; | ||
| } | ||
| } | ||
| else | ||
| { | ||
| readFromFilename = path; | ||
| } | ||
|
|
||
| QFile file( readFromFilename ); | ||
| if ( !file.open( QFile::ReadOnly ) ) | ||
| { | ||
| appendError( QgsErrorMessage( u"Could not open VPC file"_s ) ); | ||
| return {}; | ||
| } | ||
| return file.readAll(); | ||
| } | ||
|
|
||
| QgsGeometry QgsVirtualPointCloudProvider::polygonBounds() const | ||
| { | ||
| return *mPolygonBounds; | ||
|
|
@@ -659,7 +720,7 @@ QgsVirtualPointCloudProvider *QgsVirtualPointCloudProviderMetadata::createProvid | |
| QList<QgsProviderSublayerDetails> QgsVirtualPointCloudProviderMetadata::querySublayers( const QString &uri, Qgis::SublayerQueryFlags, QgsFeedback * ) const | ||
| { | ||
| const QVariantMap parts = decodeUri( uri ); | ||
| if ( parts.value( u"file-name"_s ).toString().endsWith( ".vpc", Qt::CaseSensitivity::CaseInsensitive ) ) | ||
| if ( isVpcFileName( parts.value( u"file-name"_s ).toString() ) ) | ||
| { | ||
| QgsProviderSublayerDetails details; | ||
| details.setUri( uri ); | ||
|
|
@@ -677,7 +738,7 @@ QList<QgsProviderSublayerDetails> QgsVirtualPointCloudProviderMetadata::querySub | |
| int QgsVirtualPointCloudProviderMetadata::priorityForUri( const QString &uri ) const | ||
| { | ||
| const QVariantMap parts = decodeUri( uri ); | ||
| if ( parts.value( u"file-name"_s ).toString().endsWith( ".vpc", Qt::CaseSensitivity::CaseInsensitive ) ) | ||
| if ( isVpcFileName( parts.value( u"file-name"_s ).toString() ) ) | ||
| return 100; | ||
|
|
||
| return 0; | ||
|
|
@@ -686,7 +747,7 @@ int QgsVirtualPointCloudProviderMetadata::priorityForUri( const QString &uri ) c | |
| QList<Qgis::LayerType> QgsVirtualPointCloudProviderMetadata::validLayerTypesForUri( const QString &uri ) const | ||
| { | ||
| const QVariantMap parts = decodeUri( uri ); | ||
| if ( parts.value( u"file-name"_s ).toString().endsWith( ".vpc", Qt::CaseSensitivity::CaseInsensitive ) ) | ||
| if ( isVpcFileName( parts.value( u"file-name"_s ).toString() ) ) | ||
| return QList< Qgis::LayerType>() << Qgis::LayerType::PointCloud; | ||
|
|
||
| return QList< Qgis::LayerType>(); | ||
|
|
@@ -725,7 +786,7 @@ QString QgsVirtualPointCloudProviderMetadata::filters( Qgis::FileFilterType type | |
| return QString(); | ||
|
|
||
| case Qgis::FileFilterType::PointCloud: | ||
| return QObject::tr( "Virtual Point Clouds" ) + u" (*.vpc *.VPC)"_s; | ||
| return QObject::tr( "Virtual Point Clouds" ) + u" (*.vpc *.VPC *.vpz *.VPZ)"_s; | ||
| } | ||
| return QString(); | ||
| } | ||
|
|
@@ -740,6 +801,11 @@ QList<Qgis::LayerType> QgsVirtualPointCloudProviderMetadata::supportedLayerTypes | |
| return { Qgis::LayerType::PointCloud }; | ||
| } | ||
|
|
||
| bool QgsVirtualPointCloudProviderMetadata::isVpcFileName( const QString &name ) | ||
| { | ||
| return name.endsWith( ".vpc"_L1, Qt::CaseInsensitive ) || name.endsWith( ".vpz"_L1, Qt::CaseInsensitive ); | ||
| } | ||
|
|
||
| QString QgsVirtualPointCloudProviderMetadata::encodeUri( const QVariantMap &parts ) const | ||
| { | ||
| QString uri = parts.value( u"path"_s ).toString(); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤦