diff --git a/external/mdal/api/mdal.h b/external/mdal/api/mdal.h index 305e4d5630b8..df91756e1474 100644 --- a/external/mdal/api/mdal.h +++ b/external/mdal/api/mdal.h @@ -442,6 +442,14 @@ MDAL_EXPORT MDAL_DatasetGroupH MDAL_M_addDatasetGroup( MDAL_DriverH driver, const char *datasetGroupFile ); +/** + * Removes DatasetGroup from Mesh based on it's index. On error see MDAL_LastStatus + * for error type. + * + * \since MDAL 1.3.0 + */ +MDAL_EXPORT void MDAL_M_RemoveDatasetGroup( MDAL_MeshH mesh, int index ); + /** * Returns name of MDAL driver * not thread-safe and valid only till next call @@ -587,6 +595,13 @@ MDAL_EXPORT void MDAL_G_setMetadata( MDAL_DatasetGroupH group, const char *key, */ MDAL_EXPORT const char *MDAL_G_name( MDAL_DatasetGroupH group ); +/** + * Sets dataset group name + * + * \since MDAL 1.3.0 + */ +MDAL_EXPORT void MDAL_G_setName( MDAL_DatasetGroupH group, const char *name ); + /** * Returns name of MDAL driver * not thread-safe and valid only till next call diff --git a/external/mdal/frmts/mdal_2dm.cpp b/external/mdal/frmts/mdal_2dm.cpp index 35b73bfb0034..243cb12c403e 100644 --- a/external/mdal/frmts/mdal_2dm.cpp +++ b/external/mdal/frmts/mdal_2dm.cpp @@ -402,7 +402,7 @@ void MDAL::Driver2dm::save( const std::string &fileName, const std::string &, MD for ( size_t j = 0; j < 2; ++j ) { line.append( " " ); - line.append( MDAL::coordinateToString( vertex[j] ) ); + line.append( MDAL::doubleToString( vertex[j], 8, true ) ); } line.append( " " ); line.append( MDAL::doubleToString( vertex[2] ) ); diff --git a/external/mdal/frmts/mdal_xmdf.cpp b/external/mdal/frmts/mdal_xmdf.cpp index c09d5fa0e557..b2b5583fc835 100644 --- a/external/mdal/frmts/mdal_xmdf.cpp +++ b/external/mdal/frmts/mdal_xmdf.cpp @@ -92,8 +92,8 @@ size_t MDAL::XmdfDataset::activeData( size_t indexStart, size_t count, int *buff MDAL::DriverXmdf::DriverXmdf() : Driver( "XMDF", "TUFLOW XMDF", - "*.xmdf", - Capability::ReadDatasets ) + "*.xmdf;;*.h5", + Capability::ReadDatasets | Capability::ReadMesh ) { } @@ -206,6 +206,17 @@ void MDAL::DriverXmdf::addDatasetGroupsFromXmdfGroup( DatasetGroups &groups, size_t vertexCount, size_t faceCount ) const { + // check if this root group can be loaded as a dataset group and if so, then load it + std::vector gDataNames = rootGroup.datasets(); + if ( MDAL::contains( gDataNames, "Times" ) && + MDAL::contains( gDataNames, "Values" ) && + MDAL::contains( gDataNames, "Mins" ) && + MDAL::contains( gDataNames, "Maxs" ) ) + { + std::shared_ptr ds = readXmdfGroupAsDatasetGroup( rootGroup, rootGroup.name() + nameSuffix, vertexCount, faceCount ); + groups.push_back( ds ); + } + for ( const std::string &groupName : rootGroup.groups() ) { HdfGroup g = rootGroup.group( groupName ); @@ -331,3 +342,241 @@ std::shared_ptr MDAL::DriverXmdf::readXmdfGroupAsDatasetGrou return group; } + +bool MDAL::DriverXmdf::canReadMesh( const std::string &uri ) +{ + HdfFile file( uri, HdfFile::ReadOnly ); + if ( !file.isValid() ) + { + return false; + } + + HdfDataset dsFileType = file.dataset( "/File Type" ); + if ( dsFileType.readString() != "Xmdf" ) + { + return false; + } + + std::vector meshPaths = meshGroupPaths( file ); + + return !meshPaths.empty(); +} + +std::string MDAL::DriverXmdf::buildUri( const std::string &meshFile ) +{ + mDatFile = meshFile; + + std::vector meshNames = findMeshesNames(); + + return MDAL::buildAndMergeMeshUris( meshFile, meshNames, name() ); +} + +std::vector MDAL::DriverXmdf::findMeshesNames() const +{ + std::vector meshesInFile; + + HdfFile file( mDatFile, HdfFile::ReadOnly ); + if ( !file.isValid() ) + { + return meshesInFile; + } + + meshesInFile = meshGroupPaths( file ); + + return meshesInFile; +} + +std::vector MDAL::DriverXmdf::meshGroupPaths( const HdfFile &file ) const +{ + std::vector meshPaths; + + std::vector rootGroups = file.groups(); + + for ( const std::string &groupName : rootGroups ) + { + HdfGroup g = file.group( groupName ); + std::vector paths = meshGroupPaths( g ); + meshPaths.insert( meshPaths.end(), paths.begin(), paths.end() ); + } + + return meshPaths; +} + +std::vector MDAL::DriverXmdf::meshGroupPaths( const HdfGroup &group ) const +{ + std::vector meshPaths; + + std::vector gDataNames = group.groups(); + + if ( MDAL::contains( gDataNames, "Nodes" ) || + MDAL::contains( gDataNames, "Elements" ) ) + { + meshPaths.push_back( group.name() ); + } + + for ( const std::string &groupName : gDataNames ) + { + HdfGroup g = group.group( groupName ); + std::vector paths = meshGroupPaths( g ); + meshPaths.insert( meshPaths.end(), paths.begin(), paths.end() ); + } + + return meshPaths; +} + +std::unique_ptr< MDAL::Mesh > MDAL::DriverXmdf::load( const std::string &meshFile, const std::string &meshName ) +{ + mDatFile = meshFile; + + MDAL::Log::resetLastStatus(); + + HdfFile file( mDatFile, HdfFile::ReadOnly ); + if ( !file.isValid() ) + { + MDAL::Log::error( MDAL_Status::Err_UnknownFormat, name(), "File " + mDatFile + " is not valid" ); + return nullptr; + } + + HdfDataset dsFileType = file.dataset( "/File Type" ); + if ( dsFileType.readString() != "Xmdf" ) + { + MDAL::Log::error( MDAL_Status::Err_UnknownFormat, name(), "Unknown dataset file type" ); + return nullptr; + } + + std::vector meshNames = findMeshesNames(); + + if ( meshNames.empty() ) + { + MDAL::Log::error( MDAL_Status::Err_IncompatibleMesh, name(), "No meshes found in file " + mDatFile ); + return nullptr; + } + + std::string meshNameToLoad = meshName; + + if ( meshNameToLoad.empty() ) + { + meshNameToLoad = meshNames[0]; + } + + if ( !MDAL::contains( meshNames, meshNameToLoad ) ) + { + MDAL::Log::error( MDAL_Status::Err_IncompatibleMesh, name(), "No meshes with name " + meshNameToLoad + " found in file " + mDatFile ); + return nullptr; + } + + HdfGroup groupMeshModule = file.group( meshNameToLoad ); + + std::vector gDataNames = groupMeshModule.groups(); + + HdfGroup gNodes = groupMeshModule.group( "Nodes" ); + + std::vector namesNodes = gNodes.datasets(); + HdfDataset nodes = gNodes.dataset( namesNodes[0] ); + + std::vector nodesDims = nodes.dims(); + hsize_t nodesRows = nodesDims[0]; + size_t vertexDims = nodesDims[1]; + + if ( vertexDims < 2 || vertexDims > 3 ) + { + MDAL::Log::error( MDAL_Status::Err_IncompatibleMesh, name(), "Vertices have unsupported number of dimensions " + std::to_string( vertexDims ) + " only 2 (X,Y) or 3 (X, Y, Z) dimensions are allowed." ); + return nullptr; + } + + std::vector nodesData = nodes.readArrayDouble(); + + Vertices vertices( nodesRows ); + + size_t currentVertexIndex = 0; + size_t i = 0; + while ( i < nodesData.size() ) + { + Vertex &vertex = vertices[currentVertexIndex]; + + vertex.x = nodesData[i]; + i++; + vertex.y = nodesData[i]; + i++; + if ( vertexDims == 3 ) + { + vertex.z = nodesData[i]; + i++; + } + currentVertexIndex++; + } + + nodesData.clear(); + + HdfGroup gElements = groupMeshModule.group( "Elements" ); + + std::vector namesElements = gElements.datasets(); + HdfDataset elements = gElements.dataset( namesElements[0] ); + + std::vector elementsDims = elements.dims(); + hsize_t elementsRows = elementsDims[0]; + hsize_t elementsRowsDims = elementsDims[1]; + + std::vector facesData = elements.readArrayInt(); + + Faces faces( elementsRows ); + size_t maxVerticesPerFace = 0; + + size_t currentFaceIndex = 0; + i = 0; + while ( i < facesData.size() ) + { + std::vector tempFace; + for ( hsize_t j = 0; j < elementsRowsDims; j++ ) + { + int vertexIndex = facesData[i]; + if ( vertexIndex > 0 ) + { + // XMDF is 1-based, MDAL is 0-based + tempFace.push_back( facesData[i] - 1 ); + } + i++; + } + + // only store faces with more than 2 vertices + if ( tempFace.size() > static_cast( 2 ) ) + { + Face &face = faces[currentFaceIndex]; + std::copy( tempFace.begin(), tempFace.end(), std::back_inserter( face ) ); + + if ( tempFace.size() > maxVerticesPerFace ) + { + maxVerticesPerFace = tempFace.size(); + } + + currentFaceIndex++; + } + } + + facesData.clear(); + + // copy only the faces that have been properly filled + faces = Faces( faces.begin(), faces.begin() + static_cast( currentFaceIndex ) ); + + // create the mesh and set the required data + std::unique_ptr< MemoryMesh > mesh( + new MemoryMesh( + name(), + maxVerticesPerFace, + mDatFile + ) + ); + + std::vector values( vertices.size() ); + for ( size_t i = 0; i < vertices.size(); ++i ) + { + values[i] = vertices[i].z; + } + + mesh->setFaces( std::move( faces ) ); + mesh->setVertices( std::move( vertices ) ); + + addVertexScalarDatasetGroup( mesh.get(), values, "Z-Values" ); + + return mesh; +} diff --git a/external/mdal/frmts/mdal_xmdf.hpp b/external/mdal/frmts/mdal_xmdf.hpp index cd565034d712..3163031b4a33 100644 --- a/external/mdal/frmts/mdal_xmdf.hpp +++ b/external/mdal/frmts/mdal_xmdf.hpp @@ -89,6 +89,9 @@ namespace MDAL bool canReadDatasets( const std::string &uri ) override; void load( const std::string &datFile, Mesh *mesh ) override; + bool canReadMesh( const std::string &uri ) override; + std::unique_ptr< Mesh > load( const std::string &meshFile, const std::string &meshName = "" ) override; + private: MDAL::Mesh *mMesh = nullptr; std::string mDatFile; @@ -111,6 +114,11 @@ namespace MDAL size_t vertexCount, size_t faceCount ) const; + std::string buildUri( const std::string &meshFile ) override; + std::vector findMeshesNames() const; + + std::vector meshGroupPaths( const HdfGroup &group ) const; + std::vector meshGroupPaths( const HdfFile &file ) const; }; } // namespace MDAL diff --git a/external/mdal/mdal.cpp b/external/mdal/mdal.cpp index c1c2ed1e06c7..6faa0d42894e 100644 --- a/external/mdal/mdal.cpp +++ b/external/mdal/mdal.cpp @@ -21,7 +21,7 @@ static const char *EMPTY_STR = ""; const char *MDAL_Version() { - return "1.2.0"; + return "1.3.0"; } MDAL_Status MDAL_LastStatus() @@ -580,6 +580,34 @@ MDAL_DatasetGroupH MDAL_M_addDatasetGroup( return nullptr; } +void MDAL_M_RemoveDatasetGroup( MDAL_MeshH mesh, int index ) +{ + MDAL::Log::resetLastStatus(); + + if ( !mesh ) + { + MDAL::Log::error( MDAL_Status::Err_IncompatibleMesh, "Mesh is not valid (null)" ); + return; + } + + if ( index < 0 ) + { + MDAL::Log::error( MDAL_Status::Err_IncompatibleMesh, "Requested index is not valid: " + std::to_string( index ) ); + return; + } + + MDAL::Mesh *m = static_cast< MDAL::Mesh * >( mesh ); + int len = static_cast( m->datasetGroups.size() ); + if ( len <= index ) + { + MDAL::Log::error( MDAL_Status::Err_IncompatibleMesh, "Requested index " + std::to_string( index ) + " is bigger than datasets count" ); + return; + } + size_t i = static_cast( index ); + + m->datasetGroups.erase( m->datasetGroups.begin() + static_cast( i ) ); +} + const char *MDAL_M_driverName( MDAL_MeshH mesh ) { if ( !mesh ) @@ -847,6 +875,18 @@ const char *MDAL_G_name( MDAL_DatasetGroupH group ) return _return_str( g->name() ); } +void MDAL_G_setName( MDAL_DatasetGroupH group, const char *name ) +{ + if ( !group ) + { + MDAL::Log::error( MDAL_Status::Err_IncompatibleDataset, "Dataset Group is not valid (null)" ); + return; + } + + MDAL::DatasetGroup *g = static_cast< MDAL::DatasetGroup * >( group ); + g->setName( name ); +} + bool MDAL_G_hasScalarData( MDAL_DatasetGroupH group ) { if ( !group ) diff --git a/external/mdal/mdal_utils.cpp b/external/mdal/mdal_utils.cpp index 6babb1bd4c2d..b9ee0482d416 100644 --- a/external/mdal/mdal_utils.cpp +++ b/external/mdal/mdal_utils.cpp @@ -839,10 +839,14 @@ std::string MDAL::coordinateToString( double coordinate, int precision ) return returnString; } -std::string MDAL::doubleToString( double value, int precision ) +std::string MDAL::doubleToString( double value, int precision, bool forceScientific ) { std::ostringstream oss; oss.precision( precision ); + if ( forceScientific ) + { + oss.setf( std::ios::scientific ); + } oss << value; return oss.str(); } diff --git a/external/mdal/mdal_utils.hpp b/external/mdal/mdal_utils.hpp index 9997c9474ec2..db9e56d9acf3 100644 --- a/external/mdal/mdal_utils.hpp +++ b/external/mdal/mdal_utils.hpp @@ -117,7 +117,8 @@ namespace MDAL //! Returns a string with scientific format //! precision is the number of signifiant digits - std::string doubleToString( double value, int precision = 6 ); + //! forceScientific forces the scientific notation of the number even if not necessary + std::string doubleToString( double value, int precision = 6, bool forceScientific = false ); /** * Splits by deliminer and skips empty parts. diff --git a/src/providers/mdal/qgsmdalprovider.cpp b/src/providers/mdal/qgsmdalprovider.cpp index 8a17d2ced2f4..3192d888a040 100644 --- a/src/providers/mdal/qgsmdalprovider.cpp +++ b/src/providers/mdal/qgsmdalprovider.cpp @@ -1084,7 +1084,7 @@ QVariantMap QgsMdalProviderMetadata::decodeUri( const QString &uri ) const { QVariantMap uriComponents; - const thread_local QRegularExpression layerRegex( QStringLiteral( "^([a-zA-Z0-9_]+?):\"(.*)\"(?::([a-zA-Z0-9_ ]+?$)|($))" ) ); + const thread_local QRegularExpression layerRegex( QStringLiteral( "^([a-zA-Z0-9_]+?):\"(.*)\"(?::([a-zA-Z0-9_ /]+?$)|($))" ) ); const QRegularExpressionMatch layerNameMatch = layerRegex.match( uri ); if ( layerNameMatch.hasMatch() ) { diff --git a/tests/testdata/mesh/quad_flower_to_edit_expected.2dm b/tests/testdata/mesh/quad_flower_to_edit_expected.2dm index 86a1eaf44781..d7e75cdaed8e 100644 --- a/tests/testdata/mesh/quad_flower_to_edit_expected.2dm +++ b/tests/testdata/mesh/quad_flower_to_edit_expected.2dm @@ -1,14 +1,14 @@ MESH2D -ND 1 2000 2000 200 -ND 2 2500 2500 800 -ND 3 2000 3000 200 -ND 4 1000 3000 200 -ND 5 1500 1500 -200 -ND 6 700 1750 -200 -ND 7 4000 4000 0 -ND 8 4000 4100 0 -ND 9 4100 4000 0 -ND 10 4100 4100 0 +ND 1 2.00000000e+03 2.00000000e+03 200 +ND 2 2.50000000e+03 2.50000000e+03 800 +ND 3 2.00000000e+03 3.00000000e+03 200 +ND 4 1.00000000e+03 3.00000000e+03 200 +ND 5 1.50000000e+03 1.50000000e+03 -200 +ND 6 7.00000000e+02 1.75000000e+03 -200 +ND 7 4.00000000e+03 4.00000000e+03 0 +ND 8 4.00000000e+03 4.10000000e+03 0 +ND 9 4.10000000e+03 4.00000000e+03 0 +ND 10 4.10000000e+03 4.10000000e+03 0 E3T 1 1 2 3 E3T 2 4 1 3 E3T 3 1 4 6