Skip to content
Permalink
Browse files

[FEATURE] Mesh 1D Renderer (#34848)

* MDAL 0.5.90 : support for custom Logger and 1D meshes
* [FEATURE] [MESH] Support rendering of 1D meshes, see qgis/QGIS-Enhancement-Proposals#164

1D mesh consist of edges (edge is straight line segment with 2 vertices) and the data that is defined on either
vertices or edges. Such data can be loaded by MDAL and rendered as mesh layer in QGIS.
  • Loading branch information
PeterPetrik committed Mar 9, 2020
1 parent a4b9774 commit 0675c0b6975ed85572786b40ce680a3499acbb9b
Showing with 3,558 additions and 1,032 deletions.
  1. +202 −60 external/mdal/api/mdal.h
  2. +52 −33 external/mdal/frmts/mdal_2dm.cpp
  3. +7 −3 external/mdal/frmts/mdal_2dm.hpp
  4. +3 −3 external/mdal/frmts/mdal_3di.cpp
  5. +86 −53 external/mdal/frmts/mdal_ascii_dat.cpp
  6. +12 −10 external/mdal/frmts/mdal_ascii_dat.hpp
  7. +30 −32 external/mdal/frmts/mdal_binary_dat.cpp
  8. +1 −1 external/mdal/frmts/mdal_binary_dat.hpp
  9. +22 −9 external/mdal/frmts/mdal_cf.cpp
  10. +1 −1 external/mdal/frmts/mdal_cf.hpp
  11. +11 −9 external/mdal/frmts/mdal_driver.cpp
  12. +10 −9 external/mdal/frmts/mdal_driver.hpp
  13. +26 −18 external/mdal/frmts/mdal_esri_tin.cpp
  14. +1 −8 external/mdal/frmts/mdal_esri_tin.hpp
  15. +43 −33 external/mdal/frmts/mdal_flo2d.cpp
  16. +2 −2 external/mdal/frmts/mdal_flo2d.hpp
  17. +25 −13 external/mdal/frmts/mdal_gdal.cpp
  18. +1 −1 external/mdal/frmts/mdal_gdal.hpp
  19. +18 −18 external/mdal/frmts/mdal_hdf5.cpp
  20. +3 −2 external/mdal/frmts/mdal_hdf5.hpp
  21. +25 −13 external/mdal/frmts/mdal_hec2d.cpp
  22. +1 −1 external/mdal/frmts/mdal_hec2d.hpp
  23. +42 −50 external/mdal/frmts/mdal_netcdf.cpp
  24. +1 −0 external/mdal/frmts/mdal_netcdf.hpp
  25. +26 −19 external/mdal/frmts/mdal_selafin.cpp
  26. +1 −1 external/mdal/frmts/mdal_selafin.hpp
  27. +21 −11 external/mdal/frmts/mdal_sww.cpp
  28. +1 −1 external/mdal/frmts/mdal_sww.hpp
  29. +13 −8 external/mdal/frmts/mdal_ugrid.cpp
  30. +1 −1 external/mdal/frmts/mdal_ugrid.hpp
  31. +28 −34 external/mdal/frmts/mdal_xdmf.cpp
  32. +1 −1 external/mdal/frmts/mdal_xdmf.hpp
  33. +11 −11 external/mdal/frmts/mdal_xmdf.cpp
  34. +1 −1 external/mdal/frmts/mdal_xmdf.hpp
  35. +2 −2 external/mdal/frmts/mdal_xml.cpp
  36. +166 −0 external/mdal/frmts/mdal_xms_tin.cpp
  37. +39 −0 external/mdal/frmts/mdal_xms_tin.hpp
  38. +187 −110 external/mdal/mdal.cpp
  39. +19 −10 external/mdal/mdal_data_model.cpp
  40. +24 −11 external/mdal/mdal_data_model.hpp
  41. +2 −0 external/mdal/mdal_datetime.cpp
  42. +0 −1 external/mdal/mdal_datetime.hpp
  43. +14 −13 external/mdal/mdal_driver_manager.cpp
  44. +4 −3 external/mdal/mdal_driver_manager.hpp
  45. +106 −0 external/mdal/mdal_logger.cpp
  46. +41 −0 external/mdal/mdal_logger.hpp
  47. +55 −4 external/mdal/mdal_memory_data_model.cpp
  48. +24 −2 external/mdal/mdal_memory_data_model.hpp
  49. +13 −15 external/mdal/mdal_utils.cpp
  50. +8 −3 external/mdal/mdal_utils.hpp
  51. +61 −4 python/core/auto_generated/mesh/qgsmeshdataprovider.sip.in
  52. +1 −6 python/core/auto_generated/mesh/qgsmeshlayer.sip.in
  53. +61 −5 python/core/auto_generated/mesh/qgsmeshrenderersettings.sip.in
  54. +13 −3 python/core/auto_generated/mesh/qgsmeshspatialindex.sip.in
  55. +0 −1 python/core/auto_generated/mesh/qgsmeshtracerenderer.sip.in
  56. +1 −0 python/core/auto_generated/qgsprovidermetadata.sip.in
  57. +8 −1 src/3d/qgsmeshlayer3drenderer.cpp
  58. +16 −0 src/analysis/mesh/qgsmeshcalcutils.cpp
  59. +4 −0 src/analysis/mesh/qgsmeshcontours.cpp
  60. +1 −0 src/app/mesh/qgsmeshcalculatordialog.cpp
  61. +1 −0 src/app/mesh/qgsmeshlayerproperties.cpp
  62. +27 −0 src/app/mesh/qgsmeshrendereractivedatasetwidget.cpp
  63. +25 −7 src/app/mesh/qgsmeshrenderermeshsettingswidget.cpp
  64. +13 −11 src/app/mesh/qgsmeshrenderermeshsettingswidget.h
  65. +20 −1 src/app/mesh/qgsmeshrendererscalarsettingswidget.cpp
  66. +28 −8 src/app/mesh/qgsrenderermeshpropertieswidget.cpp
  67. +45 −0 src/core/mesh/qgsmeshdataprovider.cpp
  68. +67 −7 src/core/mesh/qgsmeshdataprovider.h
  69. +25 −14 src/core/mesh/qgsmeshlayer.cpp
  70. +5 −8 src/core/mesh/qgsmeshlayer.h
  71. +18 −12 src/core/mesh/qgsmeshlayerinterpolator.cpp
  72. +2 −2 src/core/mesh/qgsmeshlayerinterpolator.h
  73. +294 −40 src/core/mesh/qgsmeshlayerrenderer.cpp
  74. +12 −7 src/core/mesh/qgsmeshlayerrenderer.h
  75. +55 −11 src/core/mesh/qgsmeshlayerutils.cpp
  76. +31 −5 src/core/mesh/qgsmeshlayerutils.h
  77. +45 −2 src/core/mesh/qgsmeshrenderersettings.cpp
  78. +63 −5 src/core/mesh/qgsmeshrenderersettings.h
  79. +57 −18 src/core/mesh/qgsmeshspatialindex.cpp
  80. +15 −3 src/core/mesh/qgsmeshspatialindex.h
  81. +1 −1 src/core/mesh/qgsmeshtracerenderer.cpp
  82. +6 −3 src/core/mesh/qgsmeshtracerenderer.h
  83. +84 −66 src/core/mesh/qgsmeshvectorrenderer.cpp
  84. +20 −17 src/core/mesh/qgsmeshvectorrenderer.h
  85. +120 −13 src/core/mesh/qgstriangularmesh.cpp
  86. +72 −7 src/core/mesh/qgstriangularmesh.h
  87. +81 −30 src/core/providers/meshmemory/qgsmeshmemorydataprovider.cpp
  88. +31 −8 src/core/providers/meshmemory/qgsmeshmemorydataprovider.h
  89. +1 −0 src/core/qgsprovidermetadata.h
  90. +4 −0 src/providers/mdal/CMakeLists.txt
  91. +55 −9 src/providers/mdal/qgsmdalprovider.cpp
  92. +2 −0 src/providers/mdal/qgsmdalprovider.h
  93. +11 −3 src/ui/mesh/qgsmeshrenderermeshsettingswidgetbase.ui
  94. +42 −12 src/ui/mesh/qgsmeshrendererscalarsettingswidgetbase.ui
  95. +75 −29 src/ui/mesh/qgsrenderermeshpropswidgetbase.ui
  96. +256 −6 tests/src/core/testqgsmeshlayer.cpp
  97. +101 −3 tests/src/core/testqgsmeshlayerrenderer.cpp
  98. BIN tests/testdata/control_images/mesh/expected_lines_edge_mesh/expected_lines_edge_mesh.png
  99. BIN ...ata/control_images/mesh/expected_lines_edge_scalar_dataset/expected_lines_edge_scalar_dataset.png
  100. BIN ...ata/control_images/mesh/expected_lines_edge_vector_dataset/expected_lines_edge_vector_dataset.png
  101. BIN ...control_images/mesh/expected_lines_vertex_scalar_dataset/expected_lines_vertex_scalar_dataset.png
  102. BIN ...control_images/mesh/expected_lines_vertex_vector_dataset/expected_lines_vertex_vector_dataset.png
  103. +8 −0 tests/testdata/mesh/lines.2dm
  104. +8 −0 tests/testdata/mesh/lines.txt
  105. +9 −0 tests/testdata/mesh/lines_bed_elevation.txt
  106. +15 −0 tests/testdata/mesh/lines_els_scalar.dat
  107. +13 −0 tests/testdata/mesh/lines_els_scalar.txt
  108. +17 −0 tests/testdata/mesh/lines_els_vector.dat
  109. +13 −0 tests/testdata/mesh/lines_els_vector.txt
  110. +18 −0 tests/testdata/mesh/lines_vertex_scalar.dat
  111. +15 −0 tests/testdata/mesh/lines_vertex_scalar.txt
  112. +18 −0 tests/testdata/mesh/lines_vertex_vector.dat
  113. +15 −0 tests/testdata/mesh/lines_vertex_vector.txt

Large diffs are not rendered by default.

@@ -18,17 +18,20 @@
#include "mdal_2dm.hpp"
#include "mdal.h"
#include "mdal_utils.hpp"
#include "mdal_logger.hpp"

#define DRIVER_NAME "2DM"

MDAL::Mesh2dm::Mesh2dm( size_t verticesCount,
size_t edgesCount,
size_t facesCount,
size_t faceVerticesMaximumCount,
MDAL::BBox extent,
const std::string &uri,
const std::map<size_t, size_t> vertexIDtoIndex )
: MemoryMesh( DRIVER_NAME,
verticesCount,
edgesCount,
facesCount,
faceVerticesMaximumCount,
extent,
@@ -39,15 +42,15 @@ MDAL::Mesh2dm::Mesh2dm( size_t verticesCount,

MDAL::Mesh2dm::~Mesh2dm() = default;

bool _parse_vertex_id_gaps( std::map<size_t, size_t> &vertexIDtoIndex, size_t vertexIndex, size_t vertexID, MDAL_Status *status )
bool _parse_vertex_id_gaps( std::map<size_t, size_t> &vertexIDtoIndex, size_t vertexIndex, size_t vertexID )
{
if ( vertexIndex == vertexID )
return false;

std::map<size_t, size_t>::iterator search = vertexIDtoIndex.find( vertexID );
if ( search != vertexIDtoIndex.end() )
{
if ( status ) *status = MDAL_Status::Warn_ElementNotUnique;
MDAL::Log::warning( Warn_ElementNotUnique, DRIVER_NAME, "could not find vertex" );
return true;
}

@@ -105,22 +108,23 @@ bool MDAL::Driver2dm::canReadMesh( const std::string &uri )
return true;
}

std::unique_ptr<MDAL::Mesh> MDAL::Driver2dm::load( const std::string &meshFile, MDAL_Status *status )
std::unique_ptr<MDAL::Mesh> MDAL::Driver2dm::load( const std::string &meshFile )
{
mMeshFile = meshFile;

if ( status ) *status = MDAL_Status::None;
MDAL::Log::resetLastStatus();

std::ifstream in( mMeshFile, std::ifstream::in );
std::string line;
if ( !std::getline( in, line ) || !startsWith( line, "MESH2D" ) )
{
if ( status ) *status = MDAL_Status::Err_UnknownFormat;
MDAL::Log::error( MDAL_Status::Err_UnknownFormat, name(), meshFile + " could not be opened" );
return nullptr;
}

size_t faceCount = 0;
size_t vertexCount = 0;
size_t edgesCount = 0;

// Find out how many nodes and elements are contained in the .2dm mesh file
while ( std::getline( in, line ) )
@@ -134,20 +138,24 @@ std::unique_ptr<MDAL::Mesh> MDAL::Driver2dm::load( const std::string &meshFile,
{
vertexCount++;
}
else if ( startsWith( line, "E2L" ) ||
startsWith( line, "E3L" ) ||
else if ( startsWith( line, "E2L" ) )
{
edgesCount++;
}
else if ( startsWith( line, "E3L" ) ||
startsWith( line, "E6T" ) ||
startsWith( line, "E8Q" ) ||
startsWith( line, "E9Q" ) )
{
if ( status ) *status = MDAL_Status::Warn_UnsupportedElement;
faceCount += 1; // We still count them as elements
MDAL::Log::warning( MDAL_Status::Err_UnsupportedElement, name(), "found unsupported element" );
return nullptr;
}
}

// Allocate memory
std::vector<Vertex> vertices( vertexCount );
std::vector<Face> faces( faceCount );
Vertices vertices( vertexCount );
Edges edges( edgesCount );
Faces faces( faceCount );

// Basement 3.x supports definition of elevation for cell centers
std::vector<double> elementCenteredElevation;
@@ -159,6 +167,7 @@ std::unique_ptr<MDAL::Mesh> MDAL::Driver2dm::load( const std::string &meshFile,

size_t faceIndex = 0;
size_t vertexIndex = 0;
size_t edgeIndex = 0;
std::map<size_t, size_t> vertexIDtoIndex;
size_t lastVertexID = 0;

@@ -202,20 +211,18 @@ std::unique_ptr<MDAL::Mesh> MDAL::Driver2dm::load( const std::string &meshFile,

faceIndex++;
}
else if ( startsWith( line, "E2L" ) ||
startsWith( line, "E3L" ) ||
startsWith( line, "E6T" ) ||
startsWith( line, "E8Q" ) ||
startsWith( line, "E9Q" ) )
else if ( startsWith( line, "E2L" ) )
{
// We do not yet support these elements
// format: E2L id n1 n2 matid
chunks = split( line, ' ' );
assert( faceIndex < faceCount );

//size_t elemID = toSizeT( chunks[1] );
assert( false ); //TODO mark element as unusable

faceIndex++;
assert( edgeIndex < edgesCount );
assert( chunks.size() > 4 );
size_t startVertexIndex = MDAL::toSizeT( chunks[2] ) - 1; // 2dm is numbered from 1
size_t endVertexIndex = MDAL::toSizeT( chunks[3] ) - 1; // 2dm is numbered from 1
Edge &edge = edges[edgeIndex];
edge.startVertex = startVertexIndex;
edge.endVertex = endVertexIndex;
edgeIndex++;
}
else if ( startsWith( line, "ND" ) )
{
@@ -229,14 +236,14 @@ std::unique_ptr<MDAL::Mesh> MDAL::Driver2dm::load( const std::string &meshFile,
if ( ( lastVertexID != 0 ) && ( nodeID <= lastVertexID ) )
{
// the algorithm requires that the file has NDs orderer by index
if ( status ) *status = MDAL_Status::Err_InvalidData;
MDAL::Log::error( MDAL_Status::Err_InvalidData, name(), "nodes are not ordered by index" );
return nullptr;
}
lastVertexID = nodeID;
}
nodeID -= 1; // 2dm is numbered from 1

_parse_vertex_id_gaps( vertexIDtoIndex, vertexIndex, nodeID, status );
_parse_vertex_id_gaps( vertexIDtoIndex, vertexIndex, nodeID );
assert( vertexIndex < vertexCount );
Vertex &vertex = vertices[vertexIndex];
vertex.x = toDouble( chunks[2] );
@@ -260,7 +267,7 @@ std::unique_ptr<MDAL::Mesh> MDAL::Driver2dm::load( const std::string &meshFile,
}
else if ( vertices.size() < nodeID )
{
if ( status ) *status = MDAL_Status::Warn_ElementWithInvalidNode;
MDAL::Log::warning( MDAL_Status::Warn_ElementWithInvalidNode, name(), "found invalid node" );
}
}
//TODO check validity of the face
@@ -270,6 +277,7 @@ std::unique_ptr<MDAL::Mesh> MDAL::Driver2dm::load( const std::string &meshFile,
std::unique_ptr< Mesh2dm > mesh(
new Mesh2dm(
vertices.size(),
edges.size(),
faces.size(),
MAX_VERTICES_PER_FACE_2DM,
computeExtent( vertices ),
@@ -279,6 +287,7 @@ std::unique_ptr<MDAL::Mesh> MDAL::Driver2dm::load( const std::string &meshFile,
);
mesh->faces = faces;
mesh->vertices = vertices;
mesh->edges = edges;

// Add Bed Elevations
MDAL::addFaceScalarDatasetGroup( mesh.get(), elementCenteredElevation, "Bed Elevation (Face)" );
@@ -287,21 +296,21 @@ std::unique_ptr<MDAL::Mesh> MDAL::Driver2dm::load( const std::string &meshFile,
return std::unique_ptr<Mesh>( mesh.release() );
}

void MDAL::Driver2dm::save( const std::string &uri, MDAL::Mesh *mesh, MDAL_Status *status )
void MDAL::Driver2dm::save( const std::string &uri, MDAL::Mesh *mesh )
{
if ( status ) *status = MDAL_Status::None;
MDAL::Log::resetLastStatus();

std::ofstream file( uri, std::ofstream::out );

if ( !file.is_open() )
{
if ( status ) *status = MDAL_Status::Err_FailToWriteToDisk;
MDAL::Log::error( MDAL_Status::Err_FailToWriteToDisk, name(), "Could not open file " + uri );
}

std::string line = "MESH2D";
file << line << std::endl;

//write vertices
// write vertices
std::unique_ptr<MDAL::MeshVertexIterator> vertexIterator = mesh->readVertices();
double vertex[3];
for ( size_t i = 0; i < mesh->verticesCount(); ++i )
@@ -320,12 +329,12 @@ void MDAL::Driver2dm::save( const std::string &uri, MDAL::Mesh *mesh, MDAL_Statu
file << line << std::endl;
}

//write faces
// write faces
std::unique_ptr<MDAL::MeshFaceIterator> faceIterator = mesh->readFaces();
for ( size_t i = 0; i < mesh->facesCount(); ++i )
{
int faceOffsets[1];
int vertexIndices[4]; //max 4 vertices for a face
int vertexIndices[MAX_VERTICES_PER_FACE_2DM];
faceIterator->next( 1, faceOffsets, 4, vertexIndices );

if ( faceOffsets[0] > 2 && faceOffsets[0] < 5 )
@@ -343,9 +352,19 @@ void MDAL::Driver2dm::save( const std::string &uri, MDAL::Mesh *mesh, MDAL_Statu
line.append( std::to_string( vertexIndices[j] + 1 ) );
}
}

file << line << std::endl;
}

// write edges
std::unique_ptr<MDAL::MeshEdgeIterator> edgeIterator = mesh->readEdges();
for ( size_t i = 0; i < mesh->edgesCount(); ++i )
{
int startIndex;
int endIndex;
edgeIterator->next( 1, &startIndex, &endIndex );
line = "E2L " + std::to_string( mesh->facesCount() + i + 1 ) + " " + std::to_string( startIndex + 1 ) + " " + std::to_string( endIndex + 1 ) + " 1";
file << line << std::endl;
}

file.close();
}
@@ -23,6 +23,7 @@ namespace MDAL
{
public:
Mesh2dm( size_t verticesCount,
size_t edgesCount,
size_t facesCount,
size_t faceVerticesMaximumCount,
BBox extent,
@@ -54,7 +55,10 @@ namespace MDAL
* 2DM format specification used in TUFLOW, HYDRO_AS-2D and BASEMENET solvers
* Text file format representing mesh vertices (ND) and faces (E**)
* ND id x y z
* Supports lines, triangles and polygons up to 9 vertices (implemented triangles and quads)
* The format supports lines, triangles and quads, where the elememts could be
* stored with some intermediate points (e.g. triangle defined by 6 vertices, vertices
* and the middle of the edges) We do support only simple definition, so E6T, E8Q and E9Q
* are not supported.
* E3T id 1 2 3 mat_id -> face type, id, vertex indices ..., material index
*
* full specification here: https://www.xmswiki.com/wiki/SMS:2D_Mesh_Files_*.2dm
@@ -87,8 +91,8 @@ namespace MDAL
{return MAX_VERTICES_PER_FACE_2DM;}

bool canReadMesh( const std::string &uri ) override;
std::unique_ptr< Mesh > load( const std::string &meshFile, MDAL_Status *status ) override;
void save( const std::string &uri, Mesh *mesh, MDAL_Status *status ) override;
std::unique_ptr< Mesh > load( const std::string &meshFile ) override;
void save( const std::string &uri, Mesh *mesh ) override;

private:
std::string mMeshFile;
@@ -57,13 +57,13 @@ void MDAL::Driver3Di::populateFacesAndVertices( Vertices &vertices, Faces &faces
int ncidX = mNcFile->getVarId( "Mesh2DContour_x" );
double fillX = mNcFile->getFillValue( ncidX );
std::vector<double> faceVerticesX( arrsize );
if ( nc_get_var_double( mNcFile->handle(), ncidX, faceVerticesX.data() ) ) throw MDAL_Status::Err_UnknownFormat;
if ( nc_get_var_double( mNcFile->handle(), ncidX, faceVerticesX.data() ) ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Unknown format" );

// Y coordinate
int ncidY = mNcFile->getVarId( "Mesh2DContour_y" );
double fillY = mNcFile->getFillValue( ncidY );
std::vector<double> faceVerticesY( arrsize );
if ( nc_get_var_double( mNcFile->handle(), ncidY, faceVerticesY.data() ) ) throw MDAL_Status::Err_UnknownFormat;
if ( nc_get_var_double( mNcFile->handle(), ncidY, faceVerticesY.data() ) ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Unknown format" );

// now populate create faces and backtrack which vertices
// are used in multiple faces
@@ -135,7 +135,7 @@ void MDAL::Driver3Di::addBedElevation( MemoryMesh *mesh )
"Bed Elevation"
);

group->setDataLocation( MDAL_DataLocation::DataOnFaces2D );
group->setDataLocation( MDAL_DataLocation::DataOnFaces );
group->setIsScalar( true );

std::shared_ptr<MDAL::MemoryDataset2D> dataset = std::make_shared< MemoryDataset2D >( group.get() );

0 comments on commit 0675c0b

Please sign in to comment.
You can’t perform that action at this time.