mirror of
https://github.com/qgis/QGIS.git
synced 2025-12-04 00:06:46 -05:00
1331 lines
42 KiB
C++
1331 lines
42 KiB
C++
/***************************************************************************
|
|
qgsmdalprovider.cpp
|
|
-------------------
|
|
begin : April 2018
|
|
copyright : (C) 2018 by Peter Petrik
|
|
email : zilolv at gmail dot com
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include <string>
|
|
|
|
#include "qgsmdalprovider.h"
|
|
#include "qgslogger.h"
|
|
#include "qgsapplication.h"
|
|
#include "qgsmeshdataprovidertemporalcapabilities.h"
|
|
#include "qgsprovidersublayerdetails.h"
|
|
#include "qgsproviderutils.h"
|
|
#include "qgsreadwritecontext.h"
|
|
|
|
#include <QFileInfo>
|
|
#include <QRegularExpression>
|
|
#include <QIcon>
|
|
#include <mutex>
|
|
|
|
const QString QgsMdalProvider::MDAL_PROVIDER_KEY = QStringLiteral( "mdal" );
|
|
const QString QgsMdalProvider::MDAL_PROVIDER_DESCRIPTION = QStringLiteral( "MDAL provider" );
|
|
|
|
bool QgsMdalProvider::isValid() const
|
|
{
|
|
return mMeshH != nullptr;
|
|
}
|
|
|
|
QString QgsMdalProvider::name() const
|
|
{
|
|
return MDAL_PROVIDER_KEY;
|
|
}
|
|
|
|
QString QgsMdalProvider::description() const
|
|
{
|
|
return MDAL_PROVIDER_DESCRIPTION;
|
|
}
|
|
|
|
QgsCoordinateReferenceSystem QgsMdalProvider::crs() const
|
|
{
|
|
return mCrs;
|
|
}
|
|
|
|
QStringList QgsMdalProvider::subLayers() const
|
|
{
|
|
return mSubLayersUris;
|
|
}
|
|
|
|
QgsMdalProvider::QgsMdalProvider( const QString &uri, const ProviderOptions &options, QgsDataProvider::ReadFlags flags )
|
|
: QgsMeshDataProvider( uri, options, flags )
|
|
{
|
|
temporalCapabilities()->setTemporalUnit( Qgis::TemporalUnit::Hours );
|
|
QByteArray curi = dataSourceUri().toUtf8();
|
|
|
|
if ( uri.contains( "\":" ) ) //contains a mesh name, so can be directly loaded;
|
|
loadData();
|
|
else
|
|
{
|
|
QStringList meshNames = QString( MDAL_MeshNames( curi ) ).split( QStringLiteral( ";;" ) );
|
|
|
|
if ( meshNames.count() == 1 ) //only one uri is present, can be directly loaded
|
|
loadData();
|
|
else
|
|
mSubLayersUris = meshNames; //several meshes are present, the layer is invalid and has sublayers
|
|
}
|
|
}
|
|
|
|
QgsMdalProvider::~QgsMdalProvider()
|
|
{
|
|
if ( mMeshH )
|
|
MDAL_CloseMesh( mMeshH );
|
|
}
|
|
|
|
int QgsMdalProvider::vertexCount() const
|
|
{
|
|
if ( mMeshH )
|
|
return MDAL_M_vertexCount( mMeshH );
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
int QgsMdalProvider::edgeCount() const
|
|
{
|
|
if ( mMeshH )
|
|
return MDAL_M_edgeCount( mMeshH );
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
int QgsMdalProvider::faceCount() const
|
|
{
|
|
if ( mMeshH )
|
|
return MDAL_M_faceCount( mMeshH );
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
void QgsMdalProvider::populateMesh( QgsMesh *mesh ) const
|
|
{
|
|
if ( mesh )
|
|
{
|
|
mesh->faces = faces();
|
|
mesh->vertices = vertices();
|
|
mesh->edges = edges();
|
|
}
|
|
}
|
|
|
|
QVector<QgsMeshVertex> QgsMdalProvider::vertices( ) const
|
|
{
|
|
const int bufferSize = std::min( vertexCount(), 1000 );
|
|
QVector<QgsMeshVertex> ret( vertexCount() );
|
|
QVector<double> buffer( bufferSize * 3 );
|
|
MDAL_MeshVertexIteratorH it = MDAL_M_vertexIterator( mMeshH );
|
|
int vertexIndex = 0;
|
|
while ( vertexIndex < vertexCount() )
|
|
{
|
|
int verticesRead = MDAL_VI_next( it, bufferSize, buffer.data() );
|
|
if ( verticesRead == 0 )
|
|
break;
|
|
for ( int i = 0; i < verticesRead; i++ )
|
|
{
|
|
QgsMeshVertex vertex(
|
|
buffer[3 * i],
|
|
buffer[3 * i + 1],
|
|
buffer[3 * i + 2]
|
|
);
|
|
ret[vertexIndex + i] = vertex;
|
|
}
|
|
vertexIndex += verticesRead;
|
|
}
|
|
MDAL_VI_close( it );
|
|
return ret;
|
|
}
|
|
|
|
QVector<QgsMeshEdge> QgsMdalProvider::edges( ) const
|
|
{
|
|
const int edgesCount = edgeCount();
|
|
const int bufferSize = std::min( edgesCount, 1000 );
|
|
QVector<QgsMeshEdge> ret( edgesCount );
|
|
QVector<int> startBuffer( bufferSize );
|
|
QVector<int> endBuffer( bufferSize );
|
|
MDAL_MeshEdgeIteratorH it = MDAL_M_edgeIterator( mMeshH );
|
|
int edgeIndex = 0;
|
|
while ( edgeIndex < edgesCount )
|
|
{
|
|
int edgesRead = MDAL_EI_next( it, bufferSize, startBuffer.data(), endBuffer.data() );
|
|
if ( edgesRead == 0 )
|
|
break;
|
|
for ( int i = 0; i < edgesRead; i++ )
|
|
{
|
|
QgsMeshEdge edge(
|
|
startBuffer[i],
|
|
endBuffer[i]
|
|
);
|
|
ret[edgeIndex + i] = edge;
|
|
}
|
|
edgeIndex += edgesRead;
|
|
}
|
|
MDAL_EI_close( it );
|
|
return ret;
|
|
}
|
|
|
|
QVector<QgsMeshFace> QgsMdalProvider::faces( ) const
|
|
{
|
|
const int faceOffsetsBufferLen = std::min( faceCount(), 1000 );
|
|
const int vertexIndicesBufferLen = faceOffsetsBufferLen * 4; // most usually we have quads
|
|
int facesCount = faceCount();
|
|
|
|
QVector<QgsMeshFace> ret( facesCount );
|
|
QVector<int> faceOffsetsBuffer( faceOffsetsBufferLen );
|
|
QVector<int> vertexIndicesBuffer( vertexIndicesBufferLen );
|
|
|
|
MDAL_MeshFaceIteratorH it = MDAL_M_faceIterator( mMeshH );
|
|
int faceIndex = 0;
|
|
while ( faceIndex < facesCount )
|
|
{
|
|
int facesRead = MDAL_FI_next( it,
|
|
faceOffsetsBufferLen,
|
|
faceOffsetsBuffer.data(),
|
|
vertexIndicesBufferLen,
|
|
vertexIndicesBuffer.data() );
|
|
if ( facesRead == 0 )
|
|
break;
|
|
|
|
for ( int i = 0; i < facesRead; i++ )
|
|
{
|
|
QgsMeshFace face;
|
|
int startIndex = 0;
|
|
if ( i > 0 )
|
|
startIndex = faceOffsetsBuffer[ i - 1 ];
|
|
int endIndex = faceOffsetsBuffer[ i ];
|
|
|
|
for ( int j = startIndex; j < endIndex; ++j )
|
|
{
|
|
int vertexIndex = vertexIndicesBuffer[j];
|
|
face.push_back( vertexIndex );
|
|
}
|
|
ret[faceIndex + i] = face;
|
|
}
|
|
faceIndex += facesRead;
|
|
}
|
|
MDAL_FI_close( it );
|
|
return ret;
|
|
}
|
|
|
|
QgsRectangle QgsMdalProvider::extent() const
|
|
{
|
|
double xMin, yMin, xMax, yMax;
|
|
MDAL_M_extent( mMeshH, &xMin, &xMax, &yMin, &yMax );
|
|
QgsRectangle ret( xMin, yMin, xMax, yMax );
|
|
return ret;
|
|
}
|
|
|
|
int QgsMdalProvider::maximumVerticesCountPerFace() const
|
|
{
|
|
return driverMetadata().maximumVerticesCountPerFace();
|
|
}
|
|
|
|
QgsMeshDriverMetadata QgsMdalProvider::driverMetadata() const
|
|
{
|
|
if ( !mMeshH )
|
|
return QgsMeshDriverMetadata();
|
|
|
|
QString name = MDAL_M_driverName( mMeshH );
|
|
MDAL_DriverH mdalDriver = MDAL_driverFromName( name.toStdString().c_str() );
|
|
QString longName = MDAL_DR_longName( mdalDriver );
|
|
QString writeDatasetSuffix = MDAL_DR_writeDatasetsSuffix( mdalDriver );
|
|
QString writeMeshFrameSuffix = MDAL_DR_saveMeshSuffix( mdalDriver );
|
|
int maxVerticesPerFace = MDAL_DR_faceVerticesMaximumCount( mdalDriver );
|
|
|
|
QgsMeshDriverMetadata::MeshDriverCapabilities capabilities;
|
|
bool hasSaveFaceDatasetsCapability = MDAL_DR_writeDatasetsCapability( mdalDriver, MDAL_DataLocation::DataOnFaces );
|
|
if ( hasSaveFaceDatasetsCapability )
|
|
capabilities |= QgsMeshDriverMetadata::CanWriteFaceDatasets;
|
|
bool hasSaveVertexDatasetsCapability = MDAL_DR_writeDatasetsCapability( mdalDriver, MDAL_DataLocation::DataOnVertices );
|
|
if ( hasSaveVertexDatasetsCapability )
|
|
capabilities |= QgsMeshDriverMetadata::CanWriteVertexDatasets;
|
|
bool hasSaveEdgeDatasetsCapability = MDAL_DR_writeDatasetsCapability( mdalDriver, MDAL_DataLocation::DataOnEdges );
|
|
if ( hasSaveEdgeDatasetsCapability )
|
|
capabilities |= QgsMeshDriverMetadata::CanWriteEdgeDatasets;
|
|
bool hasMeshSaveCapability = MDAL_DR_saveMeshCapability( mdalDriver );
|
|
if ( hasMeshSaveCapability )
|
|
capabilities |= QgsMeshDriverMetadata::CanWriteMeshData;
|
|
const QgsMeshDriverMetadata meta( name, longName, capabilities, writeDatasetSuffix, writeMeshFrameSuffix, maxVerticesPerFace );
|
|
|
|
return meta;
|
|
}
|
|
|
|
bool QgsMdalProvider::persistDatasetGroup(
|
|
const QString &outputFilePath,
|
|
const QString &outputDriver,
|
|
const QgsMeshDatasetGroupMetadata &meta,
|
|
const QVector<QgsMeshDataBlock> &datasetValues,
|
|
const QVector<QgsMeshDataBlock> &datasetActive,
|
|
const QVector<double> ×
|
|
)
|
|
{
|
|
if ( !mMeshH )
|
|
return true;
|
|
|
|
// Check that the input vectors have consistent size
|
|
if ( times.size() != datasetValues.size() )
|
|
return true;
|
|
|
|
if ( !datasetActive.isEmpty() && ( times.size() != datasetActive.size() ) )
|
|
return true;
|
|
|
|
// Check that input data are for all values
|
|
int valuesCount = meta.dataType() == QgsMeshDatasetGroupMetadata::DataOnVertices ? vertexCount() : faceCount();
|
|
for ( int i = 0; i < datasetValues.size(); ++i )
|
|
{
|
|
if ( datasetValues.at( i ).count() != valuesCount )
|
|
return true;
|
|
|
|
if ( !datasetActive.isEmpty() && ( datasetActive.at( i ).count() != faceCount() ) )
|
|
return true;
|
|
}
|
|
|
|
if ( outputFilePath.isEmpty() )
|
|
return true;
|
|
|
|
MDAL_DriverH driver = MDAL_driverFromName( outputDriver.toStdString().c_str() );
|
|
if ( !driver )
|
|
return true;
|
|
|
|
MDAL_DataLocation location = MDAL_DataLocation::DataInvalidLocation;
|
|
switch ( meta.dataType() )
|
|
{
|
|
case QgsMeshDatasetGroupMetadata::DataOnFaces:
|
|
location = MDAL_DataLocation::DataOnFaces;
|
|
break;
|
|
case QgsMeshDatasetGroupMetadata::DataOnVertices:
|
|
location = MDAL_DataLocation::DataOnVertices;
|
|
break;
|
|
case QgsMeshDatasetGroupMetadata::DataOnEdges:
|
|
location = MDAL_DataLocation::DataOnEdges;
|
|
break;
|
|
case QgsMeshDatasetGroupMetadata::DataOnVolumes:
|
|
location = MDAL_DataLocation::DataOnVolumes;
|
|
break;
|
|
}
|
|
|
|
MDAL_DatasetGroupH g = MDAL_M_addDatasetGroup(
|
|
mMeshH,
|
|
meta.name().toStdString().c_str(),
|
|
location,
|
|
meta.isScalar(),
|
|
driver,
|
|
outputFilePath.toStdString().c_str()
|
|
);
|
|
if ( !g )
|
|
return true;
|
|
|
|
const auto extraOptions = meta.extraOptions();
|
|
for ( auto it = extraOptions.cbegin(); it != extraOptions.cend(); ++it )
|
|
{
|
|
MDAL_G_setMetadata( g, it.key().toStdString().c_str(), it.value().toStdString().c_str() );
|
|
}
|
|
|
|
if ( meta.referenceTime().isValid() )
|
|
{
|
|
MDAL_G_setReferenceTime( g, meta.referenceTime().toString( Qt::ISODateWithMs ).toStdString().c_str() );
|
|
}
|
|
|
|
for ( int i = 0; i < datasetValues.size(); ++i )
|
|
{
|
|
const QVector<double> values = datasetValues.at( i ).values();
|
|
QVector<int> active;
|
|
if ( !datasetActive.isEmpty() )
|
|
active = datasetActive.at( i ).active();
|
|
|
|
MDAL_G_addDataset( g,
|
|
times.at( i ),
|
|
values.constData(),
|
|
active.isEmpty() ? nullptr : active.constData()
|
|
);
|
|
}
|
|
|
|
MDAL_G_closeEditMode( g );
|
|
if ( MDAL_LastStatus() == 0 )
|
|
{
|
|
QgsMeshDatasetGroupMetadata meta = datasetGroupMetadata( datasetGroupCount() - 1 );
|
|
QString newUri = meta.uri();
|
|
if ( !mExtraDatasetUris.contains( newUri ) )
|
|
mExtraDatasetUris << newUri;
|
|
emit datasetGroupsAdded( 1 );
|
|
emit dataChanged();
|
|
return false;
|
|
}
|
|
else
|
|
return true;
|
|
}
|
|
|
|
bool QgsMdalProvider::persistDatasetGroup( const QString &outputFilePath, const QString &outputDriver, QgsMeshDatasetSourceInterface *source, int datasetGroupIndex )
|
|
{
|
|
if ( !mMeshH )
|
|
return true;
|
|
|
|
QgsMeshDatasetGroupMetadata meta = source->datasetGroupMetadata( datasetGroupIndex );
|
|
int faceValueCount = faceCount();
|
|
int valuesCount = meta.dataType() == QgsMeshDatasetGroupMetadata::DataOnVertices ? vertexCount() : faceValueCount;
|
|
int datasetCount = source->datasetCount( datasetGroupIndex );
|
|
|
|
if ( outputFilePath.isEmpty() )
|
|
return true;
|
|
|
|
MDAL_DriverH driver = MDAL_driverFromName( outputDriver.toStdString().c_str() );
|
|
if ( !driver )
|
|
return true;
|
|
|
|
MDAL_DataLocation location = MDAL_DataLocation::DataInvalidLocation;
|
|
switch ( meta.dataType() )
|
|
{
|
|
case QgsMeshDatasetGroupMetadata::DataOnFaces:
|
|
location = MDAL_DataLocation::DataOnFaces;
|
|
break;
|
|
case QgsMeshDatasetGroupMetadata::DataOnVertices:
|
|
location = MDAL_DataLocation::DataOnVertices;
|
|
break;
|
|
case QgsMeshDatasetGroupMetadata::DataOnEdges:
|
|
location = MDAL_DataLocation::DataOnEdges;
|
|
break;
|
|
case QgsMeshDatasetGroupMetadata::DataOnVolumes:
|
|
location = MDAL_DataLocation::DataOnVolumes;
|
|
break;
|
|
}
|
|
|
|
MDAL_DatasetGroupH g = MDAL_M_addDatasetGroup(
|
|
mMeshH,
|
|
meta.name().toStdString().c_str(),
|
|
location,
|
|
meta.isScalar(),
|
|
driver,
|
|
outputFilePath.toStdString().c_str()
|
|
);
|
|
if ( !g )
|
|
return true;
|
|
|
|
const auto extraOptions = meta.extraOptions();
|
|
for ( auto it = extraOptions.cbegin(); it != extraOptions.cend(); ++it )
|
|
{
|
|
MDAL_G_setMetadata( g, it.key().toStdString().c_str(), it.value().toStdString().c_str() );
|
|
}
|
|
|
|
if ( meta.referenceTime().isValid() )
|
|
{
|
|
MDAL_G_setReferenceTime( g, meta.referenceTime().toString( Qt::ISODateWithMs ).toStdString().c_str() );
|
|
}
|
|
|
|
bool fail = false;
|
|
for ( int i = 0; i < datasetCount; ++i )
|
|
{
|
|
QgsMeshDatasetIndex index( datasetGroupIndex, i );
|
|
QgsMeshDataBlock values = source->datasetValues( index, 0, valuesCount );
|
|
QgsMeshDataBlock active = source->areFacesActive( index, 0, faceValueCount );
|
|
QgsMeshDatasetMetadata dsm = source->datasetMetadata( index );
|
|
if ( !values.isValid() || !dsm.isValid() )
|
|
{
|
|
fail = true;
|
|
break;
|
|
}
|
|
|
|
MDAL_G_addDataset( g,
|
|
dsm.time(),
|
|
values.values().constData(),
|
|
active.active().isEmpty() ? nullptr : active.active().constData()
|
|
);
|
|
|
|
}
|
|
|
|
if ( fail )
|
|
return true;
|
|
|
|
MDAL_G_closeEditMode( g );
|
|
if ( MDAL_LastStatus() == 0 )
|
|
{
|
|
QgsMeshDatasetGroupMetadata meta = datasetGroupMetadata( datasetGroupCount() - 1 );
|
|
QString newUri = meta.uri();
|
|
if ( !mExtraDatasetUris.contains( newUri ) )
|
|
mExtraDatasetUris << newUri;
|
|
addGroupToTemporalCapabilities( datasetGroupCount() - 1 );
|
|
return false;
|
|
}
|
|
else
|
|
return true;
|
|
}
|
|
|
|
bool QgsMdalProvider::saveMeshFrame( const QgsMesh &mesh )
|
|
{
|
|
QgsMdalProviderMetadata mdalProviderMetaData;
|
|
|
|
QVariantMap uriComponent = mdalProviderMetaData.decodeUri( dataSourceUri() );
|
|
|
|
if ( uriComponent.contains( QStringLiteral( "driver" ) ) )
|
|
return mdalProviderMetaData.createMeshData( mesh, dataSourceUri(), crs() );
|
|
else if ( uriComponent.contains( QStringLiteral( "path" ) ) )
|
|
return mdalProviderMetaData.createMeshData( mesh, uriComponent.value( QStringLiteral( "path" ) ).toString(), mDriverName, crs() );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
void QgsMdalProvider::close()
|
|
{
|
|
if ( mMeshH )
|
|
MDAL_CloseMesh( mMeshH );
|
|
mMeshH = nullptr;
|
|
|
|
mExtraDatasetUris.clear();
|
|
}
|
|
|
|
void QgsMdalProvider::loadData()
|
|
{
|
|
QByteArray curi = dataSourceUri().toUtf8();
|
|
mMeshH = MDAL_LoadMesh( curi.constData() );
|
|
temporalCapabilities()->clear();
|
|
|
|
if ( mMeshH )
|
|
{
|
|
mDriverName = MDAL_M_driverName( mMeshH );
|
|
const QString proj = MDAL_M_projection( mMeshH );
|
|
if ( !proj.isEmpty() )
|
|
mCrs.createFromString( proj );
|
|
|
|
int dsGroupCount = MDAL_M_datasetGroupCount( mMeshH );
|
|
for ( int i = 0; i < dsGroupCount; ++i )
|
|
addGroupToTemporalCapabilities( i );
|
|
}
|
|
}
|
|
|
|
|
|
void QgsMdalProvider::addGroupToTemporalCapabilities( int indexGroup )
|
|
{
|
|
if ( !mMeshH )
|
|
return;
|
|
QgsMeshDataProviderTemporalCapabilities *tempCap = temporalCapabilities();
|
|
QgsMeshDatasetGroupMetadata dsgMetadata = datasetGroupMetadata( indexGroup );
|
|
QDateTime refTime = dsgMetadata.referenceTime();
|
|
tempCap->addGroupReferenceDateTime( indexGroup, refTime );
|
|
int dsCount = datasetCount( indexGroup );
|
|
|
|
if ( dsgMetadata.isTemporal() )
|
|
{
|
|
tempCap->setHasTemporalCapabilities( true );
|
|
for ( int dsi = 0; dsi < dsCount; ++dsi )
|
|
{
|
|
QgsMeshDatasetMetadata dsMeta = datasetMetadata( QgsMeshDatasetIndex( indexGroup, dsi ) );
|
|
if ( dsMeta.isValid() )
|
|
tempCap->addDatasetTime( indexGroup, dsMeta.time() );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void QgsMdalProvider::reloadProviderData()
|
|
{
|
|
if ( mMeshH )
|
|
MDAL_CloseMesh( mMeshH );
|
|
|
|
loadData();
|
|
|
|
int datasetCountBeforeAdding = datasetGroupCount();
|
|
|
|
if ( mMeshH )
|
|
for ( const QString &uri : std::as_const( mExtraDatasetUris ) )
|
|
{
|
|
std::string str = uri.toStdString();
|
|
MDAL_M_LoadDatasets( mMeshH, str.c_str() );
|
|
int datasetCount = datasetGroupCount();
|
|
for ( ; datasetCountBeforeAdding < datasetCount; datasetCountBeforeAdding++ )
|
|
addGroupToTemporalCapabilities( datasetCountBeforeAdding );
|
|
}
|
|
}
|
|
|
|
void QgsMdalProvider::fileMeshFilters( QString &fileMeshFiltersString, QString &fileMeshDatasetFiltersString )
|
|
{
|
|
MDAL_DriverH mdalDriver;
|
|
|
|
// Grind through all the drivers and their respective metadata.
|
|
// We'll add a file filter for those drivers that have a file
|
|
// extension defined for them; the others, well, even though
|
|
// theoreticaly we can open those files because there exists a
|
|
// driver for them, the user will have to use the "All Files" to
|
|
// open datasets with no explicitly defined file name extension.
|
|
|
|
fileMeshFiltersString.clear();
|
|
fileMeshDatasetFiltersString.clear();
|
|
|
|
int driverCount = MDAL_driverCount();
|
|
|
|
QgsDebugMsgLevel( QStringLiteral( "MDAL driver count: %1" ).arg( driverCount ), 2 );
|
|
|
|
for ( int i = 0; i < driverCount; ++i )
|
|
{
|
|
mdalDriver = MDAL_driverFromIndex( i );
|
|
if ( !mdalDriver )
|
|
{
|
|
QgsLogger::warning( "unable to get driver " + QString::number( i ) );
|
|
continue;
|
|
}
|
|
|
|
QString longName = MDAL_DR_longName( mdalDriver );
|
|
QString driverFilters = MDAL_DR_filters( mdalDriver );
|
|
driverFilters = driverFilters.replace( QLatin1String( ";;" ), QLatin1String( " " ) );
|
|
|
|
bool isMeshDriver = MDAL_DR_meshLoadCapability( mdalDriver );
|
|
|
|
if ( longName.isEmpty() )
|
|
{
|
|
QgsLogger::warning( "invalid driver long name " + QString::number( i ) );
|
|
continue;
|
|
}
|
|
|
|
if ( !driverFilters.isEmpty() )
|
|
{
|
|
QString driverFilter = longName + " (" + driverFilters + ");;";
|
|
if ( isMeshDriver )
|
|
fileMeshFiltersString += driverFilter;
|
|
else
|
|
fileMeshDatasetFiltersString += driverFilter;
|
|
}
|
|
}
|
|
|
|
// sort file filters alphabetically
|
|
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
|
|
QStringList filters = fileMeshFiltersString.split( QStringLiteral( ";;" ), QString::SkipEmptyParts );
|
|
#else
|
|
QStringList filters = fileMeshFiltersString.split( QStringLiteral( ";;" ), Qt::SkipEmptyParts );
|
|
#endif
|
|
filters.sort();
|
|
fileMeshFiltersString = filters.join( QLatin1String( ";;" ) ) + ";;";
|
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
|
|
filters = fileMeshDatasetFiltersString.split( QStringLiteral( ";;" ), QString::SkipEmptyParts );
|
|
#else
|
|
filters = fileMeshDatasetFiltersString.split( QStringLiteral( ";;" ), Qt::SkipEmptyParts );
|
|
#endif
|
|
filters.sort();
|
|
fileMeshDatasetFiltersString = filters.join( QLatin1String( ";;" ) ) + ";;";
|
|
|
|
// can't forget the default case - first
|
|
fileMeshFiltersString.prepend( QObject::tr( "All files" ) + " (*);;" );
|
|
fileMeshDatasetFiltersString.prepend( QObject::tr( "All files" ) + " (*);;" );
|
|
|
|
// cleanup
|
|
if ( fileMeshFiltersString.endsWith( QLatin1String( ";;" ) ) ) fileMeshFiltersString.chop( 2 );
|
|
if ( fileMeshDatasetFiltersString.endsWith( QLatin1String( ";;" ) ) ) fileMeshDatasetFiltersString.chop( 2 );
|
|
|
|
QgsDebugMsgLevel( "Mesh filter list built: " + fileMeshFiltersString, 2 );
|
|
QgsDebugMsgLevel( "Mesh dataset filter list built: " + fileMeshDatasetFiltersString, 2 );
|
|
}
|
|
|
|
void QgsMdalProvider::fileMeshExtensions( QStringList &fileMeshExtensions,
|
|
QStringList &fileMeshDatasetExtensions )
|
|
{
|
|
MDAL_DriverH mdalDriver;
|
|
|
|
// Grind through all the drivers and their respective metadata.
|
|
// We'll add a file extension for those drivers that have a file
|
|
// extension defined for them
|
|
|
|
fileMeshExtensions.clear();
|
|
fileMeshDatasetExtensions.clear();
|
|
|
|
int driverCount = MDAL_driverCount();
|
|
|
|
for ( int i = 0; i < driverCount; ++i )
|
|
{
|
|
mdalDriver = MDAL_driverFromIndex( i );
|
|
if ( !mdalDriver )
|
|
{
|
|
QgsLogger::warning( "unable to get driver " + QString::number( i ) );
|
|
continue;
|
|
}
|
|
|
|
const QString driverFilters = MDAL_DR_filters( mdalDriver );
|
|
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
|
|
QStringList extensions = driverFilters.split( QStringLiteral( ";;" ), QString::SkipEmptyParts );
|
|
#else
|
|
QStringList extensions = driverFilters.split( QStringLiteral( ";;" ), Qt::SkipEmptyParts );
|
|
#endif
|
|
bool isMeshDriver = MDAL_DR_meshLoadCapability( mdalDriver );
|
|
|
|
if ( !extensions.isEmpty() )
|
|
{
|
|
for ( auto ext : extensions )
|
|
{
|
|
ext.remove( QStringLiteral( "*." ) );
|
|
ext = ext.toLower();
|
|
if ( isMeshDriver )
|
|
fileMeshExtensions += ext;
|
|
else
|
|
fileMeshDatasetExtensions += ext;
|
|
}
|
|
}
|
|
}
|
|
|
|
// sort file extensions alphabetically
|
|
fileMeshExtensions.sort();
|
|
fileMeshDatasetExtensions.sort();
|
|
|
|
// remove duplicates
|
|
fileMeshExtensions.erase( std::unique( fileMeshExtensions.begin(), fileMeshExtensions.end() ), fileMeshExtensions.end() );
|
|
fileMeshDatasetExtensions.erase( std::unique( fileMeshDatasetExtensions.begin(), fileMeshDatasetExtensions.end() ), fileMeshDatasetExtensions.end() );
|
|
|
|
QgsDebugMsgLevel( "Mesh extensions list built: " + fileMeshExtensions.join( QLatin1String( ";;" ) ), 2 );
|
|
QgsDebugMsgLevel( "Mesh dataset extensions list built: " + fileMeshDatasetExtensions.join( QLatin1String( ";;" ) ), 2 );
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------------------------*/
|
|
|
|
bool QgsMdalProvider::addDataset( const QString &uri )
|
|
{
|
|
int datasetCount = datasetGroupCount();
|
|
|
|
std::string str = uri.toStdString();
|
|
MDAL_M_LoadDatasets( mMeshH, str.c_str() );
|
|
|
|
if ( datasetCount == datasetGroupCount() )
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if ( !mExtraDatasetUris.contains( uri ) )
|
|
mExtraDatasetUris << uri;
|
|
int datasetCountAfterAdding = datasetGroupCount();
|
|
int datasetCountAdded = datasetCountAfterAdding - datasetCount;
|
|
for ( ; datasetCount < datasetCountAfterAdding; datasetCount++ )
|
|
addGroupToTemporalCapabilities( datasetCount );
|
|
|
|
emit datasetGroupsAdded( datasetCountAdded );
|
|
emit dataChanged();
|
|
return true; // Ok
|
|
}
|
|
}
|
|
|
|
QStringList QgsMdalProvider::extraDatasets() const
|
|
{
|
|
return mExtraDatasetUris;
|
|
}
|
|
|
|
int QgsMdalProvider::datasetGroupCount() const
|
|
{
|
|
return MDAL_M_datasetGroupCount( mMeshH );
|
|
}
|
|
|
|
|
|
int QgsMdalProvider::datasetCount( int groupIndex ) const
|
|
{
|
|
MDAL_DatasetGroupH group = MDAL_M_datasetGroup( mMeshH, groupIndex );
|
|
if ( !group )
|
|
return 0;
|
|
return MDAL_G_datasetCount( group );
|
|
}
|
|
|
|
QgsMeshDatasetGroupMetadata QgsMdalProvider::datasetGroupMetadata( int groupIndex ) const
|
|
{
|
|
MDAL_DatasetGroupH group = MDAL_M_datasetGroup( mMeshH, groupIndex );
|
|
if ( !group )
|
|
return QgsMeshDatasetGroupMetadata();
|
|
|
|
|
|
bool isScalar = MDAL_G_hasScalarData( group );
|
|
MDAL_DataLocation location = MDAL_G_dataLocation( group );
|
|
QgsMeshDatasetGroupMetadata::DataType type = QgsMeshDatasetGroupMetadata::DataOnFaces;
|
|
switch ( location )
|
|
{
|
|
case MDAL_DataLocation::DataOnFaces:
|
|
type = QgsMeshDatasetGroupMetadata::DataOnFaces;
|
|
break;
|
|
case MDAL_DataLocation::DataOnVertices:
|
|
type = QgsMeshDatasetGroupMetadata::DataOnVertices;
|
|
break;
|
|
case MDAL_DataLocation::DataOnEdges:
|
|
type = QgsMeshDatasetGroupMetadata::DataOnEdges;
|
|
break;
|
|
case MDAL_DataLocation::DataOnVolumes:
|
|
type = QgsMeshDatasetGroupMetadata::DataOnVolumes;
|
|
break;
|
|
case MDAL_DataLocation::DataInvalidLocation:
|
|
return QgsMeshDatasetGroupMetadata();
|
|
}
|
|
|
|
QString name = MDAL_G_name( group );
|
|
QString uri = MDAL_G_uri( group );
|
|
double min, max;
|
|
MDAL_G_minimumMaximum( group, &min, &max );
|
|
|
|
int maximumVerticalLevels = MDAL_G_maximumVerticalLevelCount( group );
|
|
|
|
QMap<QString, QString> metadata;
|
|
int n = MDAL_G_metadataCount( group );
|
|
for ( int i = 0; i < n; ++i )
|
|
{
|
|
QString key = MDAL_G_metadataKey( group, i );
|
|
QString value = MDAL_G_metadataValue( group, i );
|
|
metadata[key] = value;
|
|
}
|
|
|
|
QString referenceTimeString( MDAL_G_referenceTime( group ) );
|
|
if ( !referenceTimeString.isEmpty() )
|
|
referenceTimeString.append( 'Z' );//For now provider doesn't support time zone and return always in local time, force UTC
|
|
QDateTime referenceTime = QDateTime::fromString( referenceTimeString, Qt::ISODate );
|
|
|
|
bool isTemporal = MDAL_G_isTemporal( group );
|
|
|
|
QgsMeshDatasetGroupMetadata meta(
|
|
name,
|
|
uri,
|
|
isScalar,
|
|
type,
|
|
min,
|
|
max,
|
|
maximumVerticalLevels,
|
|
referenceTime,
|
|
isTemporal,
|
|
metadata
|
|
);
|
|
|
|
return meta;
|
|
}
|
|
|
|
QgsMeshDatasetMetadata QgsMdalProvider::datasetMetadata( QgsMeshDatasetIndex index ) const
|
|
{
|
|
MDAL_DatasetGroupH group = MDAL_M_datasetGroup( mMeshH, index.group() );
|
|
if ( !group )
|
|
return QgsMeshDatasetMetadata();
|
|
|
|
MDAL_DatasetH dataset = MDAL_G_dataset( group, index.dataset() );
|
|
if ( !dataset )
|
|
return QgsMeshDatasetMetadata();
|
|
|
|
bool isValid = MDAL_D_isValid( dataset );
|
|
double time = MDAL_D_time( dataset );
|
|
double min, max;
|
|
MDAL_D_minimumMaximum( dataset, &min, &max );
|
|
const int maxLevels = MDAL_D_maximumVerticalLevelCount( dataset );
|
|
|
|
QgsMeshDatasetMetadata meta(
|
|
time,
|
|
isValid,
|
|
min,
|
|
max,
|
|
maxLevels
|
|
);
|
|
|
|
return meta;
|
|
|
|
}
|
|
|
|
QgsMeshDatasetValue QgsMdalProvider::datasetValue( QgsMeshDatasetIndex index, int valueIndex ) const
|
|
{
|
|
QgsMeshDataBlock vals = datasetValues( index, valueIndex, 1 );
|
|
return vals.value( 0 );
|
|
}
|
|
|
|
QgsMeshDataBlock QgsMdalProvider::datasetValues( QgsMeshDatasetIndex index, int valueIndex, int count ) const
|
|
{
|
|
MDAL_DatasetGroupH group = MDAL_M_datasetGroup( mMeshH, index.group() );
|
|
if ( !group )
|
|
return QgsMeshDataBlock();
|
|
|
|
MDAL_DatasetH dataset = MDAL_G_dataset( group, index.dataset() );
|
|
if ( !dataset )
|
|
return QgsMeshDataBlock();
|
|
|
|
bool isScalar = MDAL_G_hasScalarData( group );
|
|
|
|
QgsMeshDataBlock ret( isScalar ? QgsMeshDataBlock::ScalarDouble : QgsMeshDataBlock::Vector2DDouble, count );
|
|
QVector<double> buffer( isScalar ? count : 2 * count );
|
|
int valRead = MDAL_D_data( dataset,
|
|
valueIndex,
|
|
count,
|
|
isScalar ? MDAL_DataType::SCALAR_DOUBLE : MDAL_DataType::VECTOR_2D_DOUBLE,
|
|
buffer.data() );
|
|
if ( valRead != count )
|
|
return QgsMeshDataBlock();
|
|
|
|
ret.setValues( buffer );
|
|
return ret;
|
|
}
|
|
|
|
QgsMesh3dDataBlock QgsMdalProvider::dataset3dValues( QgsMeshDatasetIndex index, int faceIndex, int count ) const
|
|
{
|
|
MDAL_DatasetGroupH group = MDAL_M_datasetGroup( mMeshH, index.group() );
|
|
if ( !group )
|
|
return QgsMesh3dDataBlock();
|
|
|
|
MDAL_DatasetH dataset = MDAL_G_dataset( group, index.dataset() );
|
|
if ( !dataset )
|
|
return QgsMesh3dDataBlock();
|
|
|
|
if ( count < 1 )
|
|
return QgsMesh3dDataBlock();
|
|
|
|
bool isScalar = MDAL_G_hasScalarData( group );
|
|
|
|
QgsMesh3dDataBlock ret( count, !isScalar );
|
|
{
|
|
QVector<int> faceToVolumeIndexBuffer( count );
|
|
int valRead = MDAL_D_data( dataset,
|
|
faceIndex,
|
|
count,
|
|
MDAL_DataType::FACE_INDEX_TO_VOLUME_INDEX_INTEGER,
|
|
faceToVolumeIndexBuffer.data() );
|
|
if ( valRead != count )
|
|
return QgsMesh3dDataBlock();
|
|
ret.setFaceToVolumeIndex( faceToVolumeIndexBuffer );
|
|
}
|
|
|
|
{
|
|
QVector<int> verticalLevelCountBuffer( count );
|
|
int valRead = MDAL_D_data( dataset,
|
|
faceIndex,
|
|
count,
|
|
MDAL_DataType::VERTICAL_LEVEL_COUNT_INTEGER,
|
|
verticalLevelCountBuffer.data() );
|
|
if ( valRead != count )
|
|
return QgsMesh3dDataBlock();
|
|
|
|
ret.setVerticalLevelsCount( verticalLevelCountBuffer );
|
|
}
|
|
|
|
const int firstVolumeIndex = ret.firstVolumeIndex();
|
|
const int lastVolumeIndex = ret.lastVolumeIndex();
|
|
const int nVolumes = lastVolumeIndex - firstVolumeIndex;
|
|
if ( firstVolumeIndex < 0 || lastVolumeIndex < 0 || nVolumes < 1 )
|
|
return QgsMesh3dDataBlock();
|
|
|
|
const int nVerticalLevelFaces = nVolumes + count; // all volumes top face + bottom face
|
|
const int startIndexVerticalFaces = firstVolumeIndex + faceIndex;
|
|
|
|
{
|
|
QVector<double> verticalLevels( nVerticalLevelFaces );
|
|
int valRead = MDAL_D_data( dataset,
|
|
startIndexVerticalFaces,
|
|
nVerticalLevelFaces,
|
|
MDAL_DataType::VERTICAL_LEVEL_DOUBLE,
|
|
verticalLevels.data() );
|
|
if ( valRead != nVerticalLevelFaces )
|
|
return QgsMesh3dDataBlock();
|
|
ret.setVerticalLevels( verticalLevels );
|
|
}
|
|
|
|
{
|
|
QVector<double> values( isScalar ? nVolumes : 2 * nVolumes );
|
|
int valRead = MDAL_D_data( dataset,
|
|
firstVolumeIndex,
|
|
nVolumes,
|
|
isScalar ? MDAL_DataType::SCALAR_VOLUMES_DOUBLE : MDAL_DataType::VECTOR_2D_VOLUMES_DOUBLE,
|
|
values.data() );
|
|
if ( valRead != nVolumes )
|
|
return QgsMesh3dDataBlock();
|
|
ret.setValues( values );
|
|
}
|
|
|
|
ret.setValid( true );
|
|
return ret;
|
|
}
|
|
|
|
bool QgsMdalProvider::isFaceActive( QgsMeshDatasetIndex index, int faceIndex ) const
|
|
{
|
|
QgsMeshDataBlock vals = areFacesActive( index, faceIndex, 1 );
|
|
return vals.active( 0 );
|
|
}
|
|
|
|
QgsMeshDataBlock QgsMdalProvider::areFacesActive( QgsMeshDatasetIndex index, int faceIndex, int count ) const
|
|
{
|
|
MDAL_DatasetGroupH group = MDAL_M_datasetGroup( mMeshH, index.group() );
|
|
if ( !group )
|
|
return QgsMeshDataBlock();
|
|
|
|
MDAL_DatasetH dataset = MDAL_G_dataset( group, index.dataset() );
|
|
if ( !dataset )
|
|
return QgsMeshDataBlock();
|
|
|
|
QgsMeshDataBlock ret( QgsMeshDataBlock::ActiveFlagInteger, count );
|
|
|
|
if ( MDAL_D_hasActiveFlagCapability( dataset ) )
|
|
{
|
|
QVector<int> buf( count );
|
|
int valRead = MDAL_D_data( dataset, faceIndex, count, MDAL_DataType::ACTIVE_INTEGER, buf.data() );
|
|
if ( valRead != count )
|
|
return QgsMeshDataBlock();
|
|
ret.setActive( buf );
|
|
}
|
|
else
|
|
{
|
|
ret.setValid( true );
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------------------------*/
|
|
|
|
QgsMdalProvider *QgsMdalProviderMetadata::createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags )
|
|
{
|
|
return new QgsMdalProvider( uri, options, flags );
|
|
}
|
|
|
|
|
|
static MDAL_MeshH createMDALMesh( const QgsMesh &mesh, const QString &driverName, const QgsCoordinateReferenceSystem &crs )
|
|
{
|
|
MDAL_DriverH driver = MDAL_driverFromName( driverName.toStdString().c_str() );
|
|
if ( !driver )
|
|
return nullptr;
|
|
|
|
MDAL_MeshH mdalMesh = MDAL_CreateMesh( driver );
|
|
|
|
if ( !mdalMesh )
|
|
return nullptr;
|
|
|
|
int bufferSize = 2000;
|
|
int vertexIndex = 0;
|
|
int faceIndex = 0;
|
|
|
|
while ( vertexIndex < mesh.vertexCount() )
|
|
{
|
|
int vertexCount = std::min( bufferSize, mesh.vertexCount() - vertexIndex );
|
|
QVector<double> verticesCoordinates( vertexCount * 3 );
|
|
for ( int i = 0; i < vertexCount ; ++i )
|
|
{
|
|
int globalIndex = vertexIndex + i;
|
|
const QgsMeshVertex &vert = mesh.vertex( globalIndex );
|
|
verticesCoordinates[i * 3 ] = vert.x();
|
|
verticesCoordinates[i * 3 + 1] = vert.y();
|
|
verticesCoordinates[i * 3 + 2] = vert.z();
|
|
}
|
|
vertexIndex += vertexCount;
|
|
|
|
MDAL_M_addVertices( mdalMesh, vertexCount, verticesCoordinates.data() );
|
|
}
|
|
|
|
while ( faceIndex < mesh.faceCount() )
|
|
{
|
|
int faceCount = std::min( bufferSize, mesh.faceCount() - faceIndex );
|
|
QVector<int> faceSizes( faceCount );
|
|
QVector<int> indices;
|
|
for ( int i = 0; i < faceCount; ++i )
|
|
{
|
|
const QgsMeshFace &face = mesh.face( faceIndex + i );
|
|
faceSizes[i] = face.count();
|
|
for ( int j = 0; j < faceSizes[i]; ++j )
|
|
indices.push_back( face.at( j ) );
|
|
}
|
|
MDAL_M_addFaces( mdalMesh, faceCount, faceSizes.data(), indices.data() );
|
|
if ( MDAL_LastStatus() != MDAL_Status::None )
|
|
{
|
|
MDAL_CloseMesh( mdalMesh );
|
|
return nullptr;
|
|
}
|
|
faceIndex += faceCount;
|
|
}
|
|
|
|
MDAL_M_setProjection( mdalMesh, crs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ).toStdString().c_str() );
|
|
|
|
return mdalMesh;
|
|
}
|
|
|
|
|
|
bool QgsMdalProviderMetadata::createMeshData( const QgsMesh &mesh, const QString &fileName, const QString &driverName, const QgsCoordinateReferenceSystem &crs ) const
|
|
{
|
|
MDAL_MeshH mdalMesh = createMDALMesh( mesh, driverName, crs );
|
|
|
|
if ( !mdalMesh )
|
|
return false;
|
|
|
|
MDAL_SaveMesh( mdalMesh, fileName.toStdString().c_str(), driverName.toStdString().c_str() );
|
|
|
|
if ( MDAL_LastStatus() != MDAL_Status::None )
|
|
{
|
|
MDAL_CloseMesh( mdalMesh );
|
|
return false;
|
|
}
|
|
|
|
MDAL_CloseMesh( mdalMesh );
|
|
return true;
|
|
}
|
|
|
|
bool QgsMdalProviderMetadata::createMeshData( const QgsMesh &mesh, const QString &uri, const QgsCoordinateReferenceSystem &crs ) const
|
|
{
|
|
QVariantMap uriComponents = decodeUri( uri );
|
|
|
|
if ( !uriComponents.contains( QStringLiteral( "driver" ) ) || !uriComponents.contains( QStringLiteral( "path" ) ) )
|
|
return false;
|
|
|
|
MDAL_MeshH mdalMesh = createMDALMesh( mesh,
|
|
uriComponents.value( QStringLiteral( "driver" ) ).toString()
|
|
, crs );
|
|
|
|
if ( !mdalMesh )
|
|
return false;
|
|
|
|
MDAL_SaveMeshWithUri( mdalMesh, uri.toStdString().c_str() );
|
|
|
|
if ( MDAL_LastStatus() != MDAL_Status::None )
|
|
{
|
|
MDAL_CloseMesh( mdalMesh );
|
|
return false;
|
|
}
|
|
|
|
MDAL_CloseMesh( mdalMesh );
|
|
return true;
|
|
}
|
|
|
|
QVariantMap QgsMdalProviderMetadata::decodeUri( const QString &uri ) const
|
|
{
|
|
QVariantMap uriComponents;
|
|
|
|
const thread_local QRegularExpression layerRegex( QStringLiteral( "^([a-zA-Z0-9_]+?):\"(.*)\"(?::([a-zA-Z0-9_ ]+?$)|($))" ) );
|
|
const QRegularExpressionMatch layerNameMatch = layerRegex.match( uri );
|
|
if ( layerNameMatch.hasMatch() )
|
|
{
|
|
uriComponents.insert( QStringLiteral( "driver" ), layerNameMatch.captured( 1 ) );
|
|
uriComponents.insert( QStringLiteral( "path" ), layerNameMatch.captured( 2 ) );
|
|
uriComponents.insert( QStringLiteral( "layerName" ), layerNameMatch.captured( 3 ) );
|
|
}
|
|
else
|
|
{
|
|
uriComponents.insert( QStringLiteral( "path" ), uri );
|
|
}
|
|
|
|
return uriComponents;
|
|
}
|
|
|
|
QString QgsMdalProviderMetadata::encodeUri( const QVariantMap &parts ) const
|
|
{
|
|
if ( !parts.value( QStringLiteral( "layerName" ) ).toString().isEmpty() && !parts.value( QStringLiteral( "driver" ) ).toString().isEmpty() )
|
|
{
|
|
return QStringLiteral( "%1:\"%2\":%3" ).arg( parts.value( QStringLiteral( "driver" ) ).toString(),
|
|
parts.value( QStringLiteral( "path" ) ).toString(),
|
|
parts.value( QStringLiteral( "layerName" ) ).toString() );
|
|
}
|
|
else if ( !parts.value( QStringLiteral( "driver" ) ).toString().isEmpty() )
|
|
{
|
|
return QStringLiteral( "%1:\"%2\"" ).arg( parts.value( QStringLiteral( "driver" ) ).toString(),
|
|
parts.value( QStringLiteral( "path" ) ).toString() );
|
|
}
|
|
else
|
|
{
|
|
return parts.value( QStringLiteral( "path" ) ).toString();
|
|
}
|
|
}
|
|
|
|
QString QgsMdalProviderMetadata::absoluteToRelativeUri( const QString &uri, const QgsReadWriteContext &context ) const
|
|
{
|
|
QVariantMap uriParts = decodeUri( uri );
|
|
if ( uriParts.contains( QStringLiteral( "path" ) ) )
|
|
{
|
|
QString filePath = uriParts.value( QStringLiteral( "path" ) ).toString();
|
|
filePath = context.pathResolver().writePath( filePath );
|
|
uriParts.insert( QStringLiteral( "path" ), filePath );
|
|
return encodeUri( uriParts );
|
|
}
|
|
return uri;
|
|
}
|
|
|
|
QString QgsMdalProviderMetadata::relativeToAbsoluteUri( const QString &uri, const QgsReadWriteContext &context ) const
|
|
{
|
|
QVariantMap uriParts = decodeUri( uri );
|
|
if ( uriParts.contains( QStringLiteral( "path" ) ) )
|
|
{
|
|
QString filePath = uriParts.value( QStringLiteral( "path" ) ).toString();
|
|
filePath = context.pathResolver().readPath( filePath );
|
|
uriParts.insert( QStringLiteral( "path" ), filePath );
|
|
return encodeUri( uriParts );
|
|
}
|
|
return uri;
|
|
}
|
|
|
|
QgsProviderMetadata::ProviderCapabilities QgsMdalProviderMetadata::providerCapabilities() const
|
|
{
|
|
return FileBasedUris;
|
|
}
|
|
|
|
QgsProviderMetadata::ProviderMetadataCapabilities QgsMdalProviderMetadata::capabilities() const
|
|
{
|
|
return QuerySublayers;
|
|
}
|
|
|
|
QList<QgsProviderSublayerDetails> QgsMdalProviderMetadata::querySublayers( const QString &uri, Qgis::SublayerQueryFlags flags, QgsFeedback * ) const
|
|
{
|
|
if ( uri.isEmpty() )
|
|
return {};
|
|
|
|
const QVariantMap uriParts = decodeUri( uri );
|
|
const QString path = uriParts.value( QStringLiteral( "path" ), uri ).toString();
|
|
const QString layerName = uriParts.value( QStringLiteral( "layerName" ) ).toString();
|
|
|
|
const QFileInfo info( path );
|
|
|
|
if ( info.isDir() )
|
|
return {};
|
|
|
|
if ( info.isFile() )
|
|
{
|
|
const QString suffix = info.suffix().toLower();
|
|
|
|
static QStringList sExtensions;
|
|
static std::once_flag initialized;
|
|
std::call_once( initialized, [ = ]( )
|
|
{
|
|
QStringList datasetsExtensions;
|
|
QgsMdalProvider::fileMeshExtensions( sExtensions, datasetsExtensions );
|
|
Q_UNUSED( datasetsExtensions )
|
|
} );
|
|
|
|
// Filter files by extension
|
|
if ( !sExtensions.contains( suffix ) )
|
|
return {};
|
|
|
|
// special handling for .adf files -- although mdal reports support for the .adf file format, we only
|
|
// want to report sublayers for tdenv.adf or tdenv9.adf files (otherwise we are reporting that any arcinfo grids or coverages are meshes)
|
|
if ( suffix == QLatin1String( "adf" )
|
|
&& !info.completeBaseName().startsWith( QLatin1String( "tdenv" ), Qt::CaseInsensitive ) )
|
|
return {};
|
|
}
|
|
|
|
if ( flags & Qgis::SublayerQueryFlag::FastScan )
|
|
{
|
|
if ( !info.isFile() )
|
|
return {};
|
|
|
|
QgsProviderSublayerDetails details;
|
|
details.setType( Qgis::LayerType::Mesh );
|
|
details.setProviderKey( QStringLiteral( "mdal" ) );
|
|
details.setUri( uri );
|
|
details.setName( QgsProviderUtils::suggestLayerNameFromFilePath( path ) );
|
|
// treat all mesh files as potentially being containers (is this correct?)
|
|
details.setSkippedContainerScan( true );
|
|
return {details};
|
|
}
|
|
|
|
const QStringList meshNames = QString( MDAL_MeshNames( path.toUtf8() ) ).split( QStringLiteral( ";;" ) );
|
|
|
|
QList<QgsProviderSublayerDetails> res;
|
|
res.reserve( meshNames.size() );
|
|
int layerIndex = 0;
|
|
for ( const QString &layerUri : meshNames )
|
|
{
|
|
if ( layerUri.isEmpty() )
|
|
continue;
|
|
|
|
const QVariantMap layerUriParts = decodeUri( layerUri );
|
|
//if an explicit layer name was included in the original uri, we only keep that layer in the results
|
|
if ( !layerName.isEmpty() && layerUriParts.value( QStringLiteral( "layerName" ) ).toString() != layerName )
|
|
continue;
|
|
|
|
QgsProviderSublayerDetails details;
|
|
details.setUri( layerUri );
|
|
details.setProviderKey( QStringLiteral( "mdal" ) );
|
|
details.setType( Qgis::LayerType::Mesh );
|
|
details.setLayerNumber( layerIndex );
|
|
details.setDriverName( layerUriParts.value( QStringLiteral( "driver" ) ).toString() );
|
|
|
|
// strip the driver name and path from the MDAL uri to get the layer name
|
|
details.setName( layerUriParts.value( QStringLiteral( "layerName" ) ).toString() );
|
|
if ( details.name().isEmpty() )
|
|
{
|
|
// use file name as layer name if no layer name available from mdal
|
|
details.setName( QgsProviderUtils::suggestLayerNameFromFilePath( path ) );
|
|
}
|
|
|
|
res << details;
|
|
|
|
layerIndex++;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
QList<Qgis::LayerType> QgsMdalProviderMetadata::supportedLayerTypes() const
|
|
{
|
|
return { Qgis::LayerType::Mesh };
|
|
}
|
|
|
|
QString QgsMdalProviderMetadata::filters( Qgis::FileFilterType type )
|
|
{
|
|
switch ( type )
|
|
{
|
|
case Qgis::FileFilterType::Mesh:
|
|
{
|
|
QString fileMeshFiltersString;
|
|
QString fileMeshDatasetFiltersString;
|
|
QgsMdalProvider::fileMeshFilters( fileMeshFiltersString, fileMeshDatasetFiltersString );
|
|
return fileMeshFiltersString;
|
|
}
|
|
case Qgis::FileFilterType::MeshDataset:
|
|
{
|
|
QString fileMeshFiltersString;
|
|
QString fileMeshDatasetFiltersString;
|
|
QgsMdalProvider::fileMeshFilters( fileMeshFiltersString, fileMeshDatasetFiltersString );
|
|
return fileMeshDatasetFiltersString;
|
|
}
|
|
|
|
case Qgis::FileFilterType::Raster:
|
|
case Qgis::FileFilterType::Vector:
|
|
case Qgis::FileFilterType::PointCloud:
|
|
case Qgis::FileFilterType::VectorTile:
|
|
case Qgis::FileFilterType::TiledMesh:
|
|
return QString();
|
|
}
|
|
return QString();
|
|
}
|
|
|
|
QList<QgsMeshDriverMetadata> QgsMdalProviderMetadata::meshDriversMetadata()
|
|
{
|
|
MDAL_DriverH mdalDriver;
|
|
QList<QgsMeshDriverMetadata> ret;
|
|
|
|
int driverCount = MDAL_driverCount();
|
|
for ( int i = 0; i < driverCount; ++i )
|
|
{
|
|
mdalDriver = MDAL_driverFromIndex( i );
|
|
if ( !mdalDriver )
|
|
{
|
|
QgsLogger::warning( "unable to get driver " + QString::number( i ) );
|
|
continue;
|
|
}
|
|
|
|
QString name = MDAL_DR_name( mdalDriver );
|
|
QString longName = MDAL_DR_longName( mdalDriver );
|
|
QString writeDatasetSuffix = MDAL_DR_writeDatasetsSuffix( mdalDriver );
|
|
QString writeMeshFrameSuffix = MDAL_DR_saveMeshSuffix( mdalDriver );
|
|
int maxVerticesPerFace = MDAL_DR_faceVerticesMaximumCount( mdalDriver );
|
|
|
|
QgsMeshDriverMetadata::MeshDriverCapabilities capabilities;
|
|
bool hasSaveFaceDatasetsCapability = MDAL_DR_writeDatasetsCapability( mdalDriver, MDAL_DataLocation::DataOnFaces );
|
|
if ( hasSaveFaceDatasetsCapability )
|
|
capabilities |= QgsMeshDriverMetadata::CanWriteFaceDatasets;
|
|
bool hasSaveVertexDatasetsCapability = MDAL_DR_writeDatasetsCapability( mdalDriver, MDAL_DataLocation::DataOnVertices );
|
|
if ( hasSaveVertexDatasetsCapability )
|
|
capabilities |= QgsMeshDriverMetadata::CanWriteVertexDatasets;
|
|
bool hasSaveEdgeDatasetsCapability = MDAL_DR_writeDatasetsCapability( mdalDriver, MDAL_DataLocation::DataOnEdges );
|
|
if ( hasSaveEdgeDatasetsCapability )
|
|
capabilities |= QgsMeshDriverMetadata::CanWriteEdgeDatasets;
|
|
bool hasMeshSaveCapability = MDAL_DR_saveMeshCapability( mdalDriver );
|
|
if ( hasMeshSaveCapability )
|
|
capabilities |= QgsMeshDriverMetadata::CanWriteMeshData;
|
|
const QgsMeshDriverMetadata meta( name, longName, capabilities, writeDatasetSuffix, writeMeshFrameSuffix, maxVerticesPerFace );
|
|
ret.push_back( meta );
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
QgsMdalProviderMetadata::QgsMdalProviderMetadata()
|
|
: QgsProviderMetadata( QgsMdalProvider::MDAL_PROVIDER_KEY, QgsMdalProvider::MDAL_PROVIDER_DESCRIPTION )
|
|
{}
|
|
|
|
QIcon QgsMdalProviderMetadata::icon() const
|
|
{
|
|
return QgsApplication::getThemeIcon( QStringLiteral( "mIconMeshLayer.svg" ) );
|
|
}
|
|
|
|
QGISEXTERN QgsProviderMetadata *providerMetadataFactory()
|
|
{
|
|
return new QgsMdalProviderMetadata();
|
|
}
|