From 1db2ac83a553c3a29d1de1ea70ef3eea9c341111 Mon Sep 17 00:00:00 2001 From: Karsten Rink <karsten.rink@ufz.de> Date: Fri, 24 Nov 2017 10:15:42 +0100 Subject: [PATCH] Raster to mesh conversion (#1991) * reimplemented raster to mesh conversion * fixed index issues and added 3d index calculation * added necessary changes to gui dialogue * added tests * clang format * added ifdef for data explorer/vtk includes * modified dereferencing of unique pointers, formatting * changed pointer to vector in vtk prep * using const& for nodes vec * fixed remaining occurences of dereferencing raster object --- .../DataView/MeshElementRemovalDialog.cpp | 2 +- .../NetCdfDialog/NetCdfConfigureDialog.cpp | 3 +- .../DataExplorer/VtkVis/CMakeLists.txt | 3 +- .../DataExplorer/VtkVis/MeshFromRaster.ui | 10 + .../VtkVis/MeshFromRasterDialog.cpp | 18 +- .../VtkVis/MeshFromRasterDialog.h | 2 + .../VtkCompositeTextureOnSurfaceFilter.cpp | 4 - .../DataExplorer/VtkVis/VtkRaster.cpp | 8 +- Applications/FileIO/AsciiRasterInterface.cpp | 3 + GeoLib/Raster.cpp | 3 +- GeoLib/Raster.h | 4 +- MeshLib/MeshGenerators/RasterToMesh.cpp | 311 +++++++++--------- MeshLib/MeshGenerators/RasterToMesh.h | 59 +--- MeshLib/MeshSearch/ElementSearch.h | 11 +- Tests/CMakeLists.txt | 2 +- Tests/MeshLib/TestMeshValidation.cpp | 4 +- Tests/MeshLib/TestNodeSearch.cpp | 4 +- Tests/MeshLib/TestRasterToMesh.cpp | 304 +++++++++++++++++ Tests/lfs-data/MeshLib/testraster_selke.asc | 39 +++ 19 files changed, 566 insertions(+), 228 deletions(-) create mode 100644 Tests/MeshLib/TestRasterToMesh.cpp create mode 100644 Tests/lfs-data/MeshLib/testraster_selke.asc diff --git a/Applications/DataExplorer/DataView/MeshElementRemovalDialog.cpp b/Applications/DataExplorer/DataView/MeshElementRemovalDialog.cpp index dfcd451863a..7e52a1a6366 100644 --- a/Applications/DataExplorer/DataView/MeshElementRemovalDialog.cpp +++ b/Applications/DataExplorer/DataView/MeshElementRemovalDialog.cpp @@ -74,7 +74,7 @@ void MeshElementRemovalDialog::accept() { QList<QListWidgetItem*> items = this->materialListWidget->selectedItems(); for (auto& item : items) - ex.searchByPropertyValue(item->text().toInt()); + ex.searchByPropertyValue(item->text().toInt(), "MaterialIDs"); anything_checked = true; } if (this->boundingBoxCheckBox->isChecked()) diff --git a/Applications/DataExplorer/NetCdfDialog/NetCdfConfigureDialog.cpp b/Applications/DataExplorer/NetCdfDialog/NetCdfConfigureDialog.cpp index 88b9095500b..f4897339419 100644 --- a/Applications/DataExplorer/NetCdfDialog/NetCdfConfigureDialog.cpp +++ b/Applications/DataExplorer/NetCdfDialog/NetCdfConfigureDialog.cpp @@ -353,7 +353,8 @@ void NetCdfConfigureDialog::createDataObject() if (originLat > lastLat) // reverse lines in vertical direction if the original file has its origin in the northwest corner this->reverseNorthSouth(data_array, sizeLon, sizeLat); - GeoLib::RasterHeader header = {sizeLon,sizeLat,origin,resolution,-9999}; + GeoLib::RasterHeader const header = {sizeLon, sizeLat, 1, + origin, resolution, -9999}; if (this->radioMesh->isChecked()) { MeshLib::MeshElemType meshElemType = MeshLib::MeshElemType::QUAD; diff --git a/Applications/DataExplorer/VtkVis/CMakeLists.txt b/Applications/DataExplorer/VtkVis/CMakeLists.txt index 1ff4e6f778c..b746c07548b 100644 --- a/Applications/DataExplorer/VtkVis/CMakeLists.txt +++ b/Applications/DataExplorer/VtkVis/CMakeLists.txt @@ -127,9 +127,8 @@ if(GEOTIFF_FOUND) endif() # GEOTIFF_FOUND target_link_libraries(VtkVis - PUBLIC BaseLib GeoLib MeshLib DataHolderLib QtBase + PUBLIC BaseLib GeoLib MeshLib DataHolderLib QtBase ${VTK_LIBRARIES} NetCdfDialogLib PRIVATE MathLib ApplicationsFileIO Qt5::Gui logog - INTERFACE ${VTK_LIBRARIES} ) set_property(TARGET VtkVis PROPERTY FOLDER "DataExplorer") diff --git a/Applications/DataExplorer/VtkVis/MeshFromRaster.ui b/Applications/DataExplorer/VtkVis/MeshFromRaster.ui index 61a16073870..746fd2e790e 100644 --- a/Applications/DataExplorer/VtkVis/MeshFromRaster.ui +++ b/Applications/DataExplorer/VtkVis/MeshFromRaster.ui @@ -125,6 +125,16 @@ </property> </widget> </item> + <item> + <widget class="QRadioButton" name="prismButton"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>set of two prisms</string> + </property> + </widget> + </item> <item> <widget class="QRadioButton" name="hexButton"> <property name="enabled"> diff --git a/Applications/DataExplorer/VtkVis/MeshFromRasterDialog.cpp b/Applications/DataExplorer/VtkVis/MeshFromRasterDialog.cpp index d06af5490d0..9823b8cd289 100644 --- a/Applications/DataExplorer/VtkVis/MeshFromRasterDialog.cpp +++ b/Applications/DataExplorer/VtkVis/MeshFromRasterDialog.cpp @@ -17,7 +17,7 @@ #include "MeshGenerators/VtkMeshConverter.h" MeshFromRasterDialog::MeshFromRasterDialog(QDialog* parent) -: QDialog(parent), _mesh_name("mesh"), _array_name("Colour") +: QDialog(parent), _mesh_name("mesh"), _array_name("MaterialIDs") { setupUi(this); @@ -28,6 +28,20 @@ MeshFromRasterDialog::MeshFromRasterDialog(QDialog* parent) MeshFromRasterDialog::~MeshFromRasterDialog() = default; +void MeshFromRasterDialog::on_elevationButton_toggled(bool isChecked) +{ + if (isChecked) + { + if (this->prismButton->isChecked()) + this->triButton->setChecked(true); + if (this->hexButton->isChecked()) + this->quadButton->setChecked(true); + } + + this->prismButton->setEnabled(!isChecked); + this->hexButton->setEnabled(!isChecked); +} + void MeshFromRasterDialog::accept() { if (this->mshNameEdit->text().isEmpty()) @@ -54,6 +68,8 @@ void MeshFromRasterDialog::accept() } _element_selection = MeshLib::MeshElemType::TRIANGLE; if (this->quadButton->isChecked()) _element_selection = MeshLib::MeshElemType::QUAD; + else if (this->prismButton->isChecked()) + _element_selection = MeshLib::MeshElemType::PRISM; else if (this->hexButton->isChecked()) _element_selection = MeshLib::MeshElemType::HEXAHEDRON; this->done(QDialog::Accepted); diff --git a/Applications/DataExplorer/VtkVis/MeshFromRasterDialog.h b/Applications/DataExplorer/VtkVis/MeshFromRasterDialog.h index 2933ef04c09..68bff68f873 100644 --- a/Applications/DataExplorer/VtkVis/MeshFromRasterDialog.h +++ b/Applications/DataExplorer/VtkVis/MeshFromRasterDialog.h @@ -43,6 +43,8 @@ public: MeshLib::UseIntensityAs getIntensitySelection() const { return _intensity_selection; } private slots: + void on_elevationButton_toggled(bool isChecked); + /// Instructions if the OK-Button has been pressed. void accept() override; diff --git a/Applications/DataExplorer/VtkVis/VtkCompositeTextureOnSurfaceFilter.cpp b/Applications/DataExplorer/VtkVis/VtkCompositeTextureOnSurfaceFilter.cpp index c6d5aaf410e..c253f0c6cc4 100644 --- a/Applications/DataExplorer/VtkVis/VtkCompositeTextureOnSurfaceFilter.cpp +++ b/Applications/DataExplorer/VtkVis/VtkCompositeTextureOnSurfaceFilter.cpp @@ -90,10 +90,6 @@ void VtkCompositeTextureOnSurfaceFilter::init() image->GetOutput()->GetOrigin(origin); double spacing[3]; image->GetOutput()->GetSpacing(spacing); -/* - VtkCompositeColormapToImageFilter* cm = new VtkCompositeColormapToImageFilter(image); - vtkImageAlgorithm* img = dynamic_cast<vtkImageAlgorithm*>(cm->GetOutputAlgorithm()); -*/ surface->SetRaster(image, origin[0], origin[1], spacing[0]); surface->Update(); } diff --git a/Applications/DataExplorer/VtkVis/VtkRaster.cpp b/Applications/DataExplorer/VtkVis/VtkRaster.cpp index 8a65b927c4e..1ee2b08499e 100644 --- a/Applications/DataExplorer/VtkVis/VtkRaster.cpp +++ b/Applications/DataExplorer/VtkVis/VtkRaster.cpp @@ -70,7 +70,7 @@ vtkImageAlgorithm* VtkRaster::loadImage(const std::string &fileName, } vtkImageImport* VtkRaster::loadImageFromArray(double const*const data_array, GeoLib::RasterHeader header) { - const unsigned length = header.n_rows*header.n_cols; + const unsigned length = header.n_rows * header.n_cols * header.n_depth; auto* data = new float[length * 2]; float max_val = *std::max_element(data_array, data_array+length); for (unsigned j=0; j<length; ++j) @@ -88,8 +88,10 @@ vtkImageImport* VtkRaster::loadImageFromArray(double const*const data_array, Geo vtkImageImport* image = vtkImageImport::New(); image->SetDataSpacing(header.cell_size, header.cell_size, header.cell_size); image->SetDataOrigin(header.origin[0]+(header.cell_size/2.0), header.origin[1]+(header.cell_size/2.0), 0); // translate whole mesh by half a pixel in x and y - image->SetWholeExtent(0, header.n_cols-1, 0, header.n_rows-1, 0, 0); - image->SetDataExtent(0, header.n_cols-1, 0, header.n_rows-1, 0, 0); + image->SetWholeExtent(0, header.n_cols - 1, 0, header.n_rows - 1, 0, + header.n_depth - 1); + image->SetDataExtent(0, header.n_cols - 1, 0, header.n_rows - 1, 0, + header.n_depth - 1); image->SetDataExtentToWholeExtent(); image->SetDataScalarTypeToFloat(); image->SetNumberOfScalarComponents(2); diff --git a/Applications/FileIO/AsciiRasterInterface.cpp b/Applications/FileIO/AsciiRasterInterface.cpp index fd7d1468776..560192ab3ee 100644 --- a/Applications/FileIO/AsciiRasterInterface.cpp +++ b/Applications/FileIO/AsciiRasterInterface.cpp @@ -86,6 +86,8 @@ bool AsciiRasterInterface::readASCHeader(std::ifstream &in, GeoLib::RasterHeader header.n_rows = atoi(value.c_str()); } else return false; + header.n_depth = 1; + in >> tag; if (tag == "xllcorner" || tag == "xllcenter") { @@ -192,6 +194,7 @@ bool AsciiRasterInterface::readSurferHeader( ERR("Error in readSurferHeader() - Anisotropic cellsize detected."); return false; } + header.n_depth = 1; header.no_data = min - 1; in >> min >> max; diff --git a/GeoLib/Raster.cpp b/GeoLib/Raster.cpp index 625d37dd808..2a2fdff78dd 100644 --- a/GeoLib/Raster.cpp +++ b/GeoLib/Raster.cpp @@ -85,7 +85,8 @@ Raster* Raster::getRasterFromSurface(Surface const& sfc, double cell_size, doubl } } - RasterHeader header = {std::size_t(n_cols), std::size_t(n_rows), MathLib::Point3d(ll), cell_size, static_cast<double>(-9999)}; + RasterHeader header = { std::size_t(n_cols), std::size_t(n_rows), 1, + MathLib::Point3d(ll), cell_size, static_cast<double>(-9999) }; return new Raster(header, z_vals, z_vals+n_cols*n_rows); } diff --git a/GeoLib/Raster.h b/GeoLib/Raster.h index 07a5914c199..32e6229b320 100644 --- a/GeoLib/Raster.h +++ b/GeoLib/Raster.h @@ -13,17 +13,19 @@ #pragma once +#include <array> #include <utility> #include "Surface.h" namespace GeoLib { -/// Contains the relevant information when handling with geoscientific raster data +/// Contains the relevant information when storing a geoscientific raster data struct RasterHeader { std::size_t n_cols; // width std::size_t n_rows; // height + std::size_t n_depth; // depth (for 3d image) MathLib::Point3d origin; // lower left corner double cell_size; // edge length of each pixel double no_data; // no data value diff --git a/MeshLib/MeshGenerators/RasterToMesh.cpp b/MeshLib/MeshGenerators/RasterToMesh.cpp index dba1407c6c2..5b4e8089959 100644 --- a/MeshLib/MeshGenerators/RasterToMesh.cpp +++ b/MeshLib/MeshGenerators/RasterToMesh.cpp @@ -12,6 +12,9 @@ #include "MeshLib/Elements/Elements.h" #include "MeshLib/Mesh.h" #include "MeshLib/Node.h" +#include "MeshLib/MeshEditing/RemoveMeshComponents.h" +#include "MeshLib/MeshGenerators/MeshGenerator.h" +#include "MeshLib/MeshSearch/ElementSearch.h" #include <vtkImageData.h> #include <vtkDataArray.h> @@ -40,14 +43,25 @@ MeshLib::Mesh* RasterToMesh::convert( UseIntensityAs intensity_type, std::string const& array_name) { - if ((elem_type != MeshElemType::TRIANGLE) && (elem_type != MeshElemType::QUAD)) + if ((elem_type != MeshElemType::TRIANGLE) && + (elem_type != MeshElemType::QUAD) && + (elem_type != MeshElemType::HEXAHEDRON) && + (elem_type != MeshElemType::PRISM)) { - ERR("VtkMeshConverter::convertImgToMesh(): Invalid Mesh Element Type."); + ERR("Invalid Mesh Element Type."); return nullptr; } - vtkSmartPointer<vtkDataArray> pixelData = vtkSmartPointer<vtkDataArray>(img->GetPointData()->GetScalars()); int* dims = img->GetDimensions(); + if (((elem_type == MeshElemType::TRIANGLE) || (elem_type == MeshElemType::QUAD)) && + dims[2] != 1) + { + ERR("Triangle or Quad elements cannot be used to construct meshes from 3D rasters."); + return nullptr; + } + + vtkSmartPointer<vtkDataArray> pixelData = + vtkSmartPointer<vtkDataArray>(img->GetPointData()->GetScalars()); int nTuple = pixelData->GetNumberOfComponents(); if (nTuple < 1 || nTuple > 4) { @@ -55,193 +69,168 @@ MeshLib::Mesh* RasterToMesh::convert( return nullptr; } - MathLib::Point3d const orig (std::array<double,3>{{origin[0], origin[1], origin[2]}}); - GeoLib::RasterHeader const header = - {static_cast<std::size_t>(dims[0]), static_cast<std::size_t>(dims[1]), orig, scalingFactor, -9999}; - const std::size_t incHeight = header.n_rows+1; - const std::size_t incWidth = header.n_cols+1; - std::vector<double> pix_val (incHeight * incWidth, std::numeric_limits<double>::max()); - std::vector<bool> pix_vis (header.n_rows * header.n_cols, false); + MathLib::Point3d const orig(std::array<double, 3>{ {origin[0], origin[1], origin[2]}}); + GeoLib::RasterHeader const header = {static_cast<std::size_t>(dims[0]), + static_cast<std::size_t>(dims[1]), + static_cast<std::size_t>(dims[2]), + orig, + scalingFactor, + -9999}; - for (std::size_t i = 0; i < header.n_rows; i++) - for (std::size_t j = 0; j < header.n_cols; j++) + std::vector<double> pix(header.n_cols * header.n_rows * header.n_depth, 0); + for (std::size_t k = 0; k < header.n_depth; k++) + { + std::size_t const layer_idx = (k*header.n_rows*header.n_cols); + for (std::size_t i = 0; i < header.n_rows; i++) { - std::size_t const img_idx = i*header.n_cols + j; - std::size_t const fld_idx = i*incWidth + j; - - // colour of current pixel - double* colour = pixelData->GetTuple(img_idx); - // is current pixel visible? - bool const visible = (nTuple == 2 || nTuple == 4) ? (colour[nTuple-1] != 0) : true; - if (!visible) - continue; - - double const value = (nTuple < 3) ? - colour[0] : // grey (+ alpha) - (0.3 * colour[0] + 0.6 * colour[1] + 0.1 * colour[2]); // rgb(a) - pix_vis[img_idx] = true; - pix_val[fld_idx] = value; - pix_val[fld_idx+1] = value; - pix_val[fld_idx+incWidth] = value; - pix_val[fld_idx+incWidth+1] = value; + std::size_t const idx = i * header.n_cols + layer_idx; + for (std::size_t j = 0; j < header.n_cols; j++) + { + double* colour = pixelData->GetTuple(idx + j); + bool const visible = (nTuple == 2 || nTuple == 4) ? (colour[nTuple - 1] != 0) : true; + if (!visible) + pix[idx + j] = header.no_data; + else + pix[idx + j] = (nTuple < 3) ? + colour[0] : // grey (+ alpha) + (0.3 * colour[0] + 0.6 * colour[1] + 0.1 * colour[2]); // rgb(a) + } } + } - return constructMesh(pix_val, array_name, pix_vis, header, elem_type, intensity_type); + MeshLib::Mesh* mesh = convert(pix.data(), header, elem_type, intensity_type, array_name); + return mesh; } MeshLib::Mesh* RasterToMesh::convert( - double const* img, + double const*const img, GeoLib::RasterHeader const& header, MeshElemType elem_type, UseIntensityAs intensity_type, std::string const& array_name) { - if ((elem_type != MeshElemType::TRIANGLE) && (elem_type != MeshElemType::QUAD)) + if ((elem_type != MeshElemType::TRIANGLE) && + (elem_type != MeshElemType::QUAD) && + (elem_type != MeshElemType::HEXAHEDRON) && + (elem_type != MeshElemType::PRISM)) { ERR("Invalid Mesh Element Type."); return nullptr; } - std::size_t const incHeight (header.n_rows+1); - std::size_t const incWidth (header.n_cols+1); - std::vector<double> pix_val (incHeight * incWidth, std::numeric_limits<double>::max()); - std::vector<bool> pix_vis (incHeight * incWidth, false); - - for (std::size_t i = 0; i < header.n_rows; i++) - for (std::size_t j = 0; j < header.n_cols; j++) - { - std::size_t const img_idx = i*header.n_cols + j; - std::size_t const fld_idx = i*incWidth + j; - if (img[img_idx] == -9999) - continue; - - pix_vis[img_idx] = true; - pix_val[fld_idx] = img[img_idx]; - pix_val[fld_idx+1] = img[img_idx]; - pix_val[fld_idx+incWidth] = img[img_idx]; - pix_val[fld_idx+incWidth+1] = img[img_idx]; - } - - return constructMesh(pix_val, array_name, pix_vis, header, elem_type, intensity_type); -} - -MeshLib::Mesh* RasterToMesh::constructMesh( - std::vector<double> const& pix_val, - std::string const& array_name, - std::vector<bool> const& pix_vis, - GeoLib::RasterHeader const& header, - MeshLib::MeshElemType elem_type, - MeshLib::UseIntensityAs intensity_type) -{ - std::vector<int> node_idx_map ((header.n_rows+1) * (header.n_cols+1), -1); - bool const use_elevation (intensity_type == MeshLib::UseIntensityAs::ELEVATION); - std::vector<MeshLib::Node*> nodes (createNodeVector(pix_val, node_idx_map, header, use_elevation)); - if (nodes.empty()) + if (((elem_type == MeshElemType::TRIANGLE) || (elem_type == MeshElemType::QUAD)) && + header.n_depth != 1) + { + ERR("Triangle or Quad elements cannot be used to construct meshes from 3D rasters."); return nullptr; + } - std::vector<MeshLib::Element*> elements(createElementVector( - pix_vis, nodes, node_idx_map, header.n_rows, header.n_cols, elem_type)); - if (elements.empty()) + if (intensity_type == UseIntensityAs::ELEVATION && + ((elem_type == MeshElemType::PRISM) || (elem_type == MeshElemType::HEXAHEDRON))) + { + ERR("Elevation mapping can only be performed for 2D meshes."); return nullptr; + } - MeshLib::Properties properties; - if (intensity_type == MeshLib::UseIntensityAs::MATERIALS) + MathLib::Point3d mesh_origin({header.origin[0] - (header.cell_size / 2.0), + header.origin[1] - (header.cell_size / 2.0), + header.origin[2]}); + std::unique_ptr<MeshLib::Mesh> mesh (nullptr); + if (elem_type == MeshElemType::TRIANGLE) + mesh.reset( + MeshLib::MeshGenerator::generateRegularTriMesh(header.n_cols, + header.n_rows, + header.cell_size, + mesh_origin, + "RasterDataMesh")); + else if (elem_type == MeshElemType::QUAD) + mesh.reset( + MeshLib::MeshGenerator::generateRegularQuadMesh(header.n_cols, + header.n_rows, + header.cell_size, + mesh_origin, + "RasterDataMesh")); + else if (elem_type == MeshElemType::PRISM) + mesh.reset( + MeshLib::MeshGenerator::generateRegularPrismMesh(header.n_cols, + header.n_rows, + header.n_depth, + header.cell_size, + mesh_origin, + "RasterDataMesh")); + else if (elem_type == MeshElemType::HEXAHEDRON) + mesh.reset( + MeshLib::MeshGenerator::generateRegularHexMesh(header.n_cols, + header.n_rows, + header.n_depth, + header.cell_size, + mesh_origin, + "RasterDataMesh")); + + MeshLib::Mesh* new_mesh(nullptr); + std::vector<std::size_t> elements_to_remove; + if (intensity_type == UseIntensityAs::ELEVATION) { - auto* const prop_vec = properties.createNewPropertyVector<int>( - "MaterialIDs", MeshLib::MeshItemType::Cell, 1); - fillPropertyVector<int>(*prop_vec, pix_val, pix_vis, header.n_rows, header.n_cols, elem_type); + std::vector<MeshLib::Node*> const& nodes(mesh->getNodes()); + std::vector<MeshLib::Element*> const& elems(mesh->getElements()); + std::size_t const n_nodes(elems[0]->getNumberOfNodes()); + bool const double_idx = (elem_type == MeshElemType::TRIANGLE) || (elem_type == MeshElemType::PRISM); + std::size_t const m = (double_idx) ? 2 : 1; + for (std::size_t k = 0; k < header.n_depth; k++) + { + std::size_t const layer_idx = (k*header.n_rows*header.n_cols); + for (std::size_t i = 0; i < header.n_cols; i++) + { + std::size_t const idx(i * header.n_rows + layer_idx); + for (std::size_t j = 0; j < header.n_rows; j++) + { + double const val(img[idx + j]); + if (val == header.no_data) + { + elements_to_remove.push_back(m * (idx + j)); + if (double_idx) + elements_to_remove.push_back(m * (idx + j) + 1); + continue; + } + for (std::size_t n = 0; n < n_nodes; ++n) + { + (*(nodes[elems[m * (idx + j)]->getNodeIndex(n)]))[2] = val; + if (double_idx) + (*(nodes[elems[m * (idx + j) + 1]->getNodeIndex(n)]))[2] = val; + } + } + } + } } - else if (intensity_type == MeshLib::UseIntensityAs::DATAVECTOR) + else { - auto* const prop_vec = properties.createNewPropertyVector<double>( - array_name, MeshLib::MeshItemType::Cell, 1); - fillPropertyVector<double>(*prop_vec, pix_val, pix_vis, header.n_rows, header.n_cols, elem_type); - } - - return new MeshLib::Mesh("RasterDataMesh", nodes, elements, properties); -} - -std::vector<MeshLib::Node*> RasterToMesh::createNodeVector( - std::vector<double> const& elevation, - std::vector<int> & node_idx_map, - GeoLib::RasterHeader const& header, - bool use_elevation) -{ - std::size_t node_idx_count(0); - double const x_offset(header.origin[0] - header.cell_size/2.0); - double const y_offset(header.origin[1] - header.cell_size/2.0); - std::vector<MeshLib::Node*> nodes; - for (std::size_t i = 0; i < (header.n_rows+1); i++) - for (std::size_t j = 0; j < (header.n_cols+1); j++) + MeshLib::Properties &properties = mesh->getProperties(); + MeshLib::ElementSearch ex(*mesh); + if (array_name == "MaterialIDs") { - std::size_t const index = i * (header.n_cols+1) + j; - if (elevation[index] == std::numeric_limits<double>::max()) - continue; - - double const zValue = (use_elevation) ? elevation[index] : 0; - auto* node(new MeshLib::Node(x_offset + (header.cell_size * j), - y_offset + (header.cell_size * i), - zValue)); - nodes.push_back(node); - node_idx_map[index] = node_idx_count; - node_idx_count++; + auto* const prop_vec = properties.createNewPropertyVector<int>( + array_name, MeshLib::MeshItemType::Cell, 1); + fillPropertyVector<int>(*prop_vec, img, header, elem_type); + ex.searchByPropertyValue<int>(header.no_data, array_name); } - return nodes; -} - -std::vector<MeshLib::Element*> RasterToMesh::createElementVector( - std::vector<bool> const& pix_vis, - std::vector<MeshLib::Node*> const& nodes, - std::vector<int> const&node_idx_map, - std::size_t const imgHeight, - std::size_t const imgWidth, - MeshElemType elem_type) -{ - std::vector<MeshLib::Element*> elements; - std::size_t const incWidth (imgWidth+1); - for (std::size_t i = 0; i < imgHeight; i++) - for (std::size_t j = 0; j < imgWidth; j++) + else { - if (!pix_vis[i*imgWidth+j]) - continue; - - int const idx = i * incWidth + j; - if (elem_type == MeshElemType::TRIANGLE) - { - auto** tri1_nodes = new MeshLib::Node*[3]; - tri1_nodes[0] = nodes[node_idx_map[idx]]; - tri1_nodes[1] = nodes[node_idx_map[idx+1]]; - tri1_nodes[2] = nodes[node_idx_map[idx+incWidth]]; - - auto** tri2_nodes = new MeshLib::Node*[3]; - tri2_nodes[0] = nodes[node_idx_map[idx+1]]; - tri2_nodes[1] = nodes[node_idx_map[idx+incWidth+1]]; - tri2_nodes[2] = nodes[node_idx_map[idx+incWidth]]; - - elements.push_back(new MeshLib::Tri(tri1_nodes)); // upper left triangle - elements.push_back(new MeshLib::Tri(tri2_nodes)); // lower right triangle - } - else if (elem_type == MeshElemType::QUAD) - { - auto** quad_nodes = new MeshLib::Node*[4]; - quad_nodes[0] = nodes[node_idx_map[idx]]; - quad_nodes[1] = nodes[node_idx_map[idx + 1]]; - quad_nodes[2] = nodes[node_idx_map[idx + incWidth + 1]]; - quad_nodes[3] = nodes[node_idx_map[idx + incWidth]]; - elements.push_back(new MeshLib::Quad(quad_nodes)); - } + auto* const prop_vec = properties.createNewPropertyVector<double>( + array_name, MeshLib::MeshItemType::Cell, 1); + fillPropertyVector<double>(*prop_vec, img, header, elem_type); + ex.searchByPropertyValue<double>(header.no_data, array_name); } - return elements; -} - -double RasterToMesh::getExistingValue(const double* img, std::size_t length) -{ - for (std::size_t i=0; i<length; i++) - { - if (img[i] != -9999) - return img[i]; + elements_to_remove = ex.getSearchedElementIDs(); } - return -9999; + if (!elements_to_remove.empty()) + new_mesh = MeshLib::removeElements(*mesh, elements_to_remove, mesh->getName()); + else + new_mesh = mesh.release(); + + if (intensity_type == UseIntensityAs::NONE) + new_mesh->getProperties().removePropertyVector(array_name); + + return new_mesh; } } // end namespace MeshLib diff --git a/MeshLib/MeshGenerators/RasterToMesh.h b/MeshLib/MeshGenerators/RasterToMesh.h index 8ea537a7493..85487f11e00 100644 --- a/MeshLib/MeshGenerators/RasterToMesh.h +++ b/MeshLib/MeshGenerators/RasterToMesh.h @@ -75,61 +75,36 @@ public: * \param array_name mesh property name, defaults to "Colour" if not * given. */ - static MeshLib::Mesh* convert(const double* img, - GeoLib::RasterHeader const& header, - MeshElemType elem_type, - UseIntensityAs intensity_type, - std::string const& array_name = "Colour"); - -private: - static MeshLib::Mesh* constructMesh( - std::vector<double> const& pix_val, - std::string const& array_name, - std::vector<bool> const& pix_vis, - GeoLib::RasterHeader const& header, - MeshLib::MeshElemType elem_type, - MeshLib::UseIntensityAs intensity_type); - - static std::vector<MeshLib::Node*> createNodeVector( - std::vector<double> const& elevation, - std::vector<int> &node_idx_map, + static MeshLib::Mesh* convert(const double*const img, GeoLib::RasterHeader const& header, - bool use_elevation); - - static std::vector<MeshLib::Element*> createElementVector( - std::vector<bool> const& pix_vis, - std::vector<MeshLib::Node*> const& nodes, - std::vector<int> const& node_idx_map, - std::size_t const imgHeight, - std::size_t const imgWidth, - MeshElemType elem_type); + MeshElemType elem_type, + UseIntensityAs intensity_type, + std::string const& array_name = "Colour"); +private: template<typename T> static void fillPropertyVector( MeshLib::PropertyVector<T> &prop_vec, - std::vector<double> const& pix_val, - std::vector<bool> const& pix_vis, - const std::size_t &imgHeight, - const std::size_t &imgWidth, + double const*const img, + GeoLib::RasterHeader const& header, MeshElemType elem_type) { - for (std::size_t i = 0; i < imgHeight; i++) - for (std::size_t j = 0; j < imgWidth; j++) + for (std::size_t k = 0; k < header.n_depth; k++) + { + std::size_t const layer_idx = (k*header.n_rows*header.n_cols); + for (std::size_t i = 0; i < header.n_cols; i++) { - if (!pix_vis[i*imgWidth+j]) - continue; - auto val(static_cast<T>(pix_val[i * (imgWidth + 1) + j])); - if (elem_type == MeshElemType::TRIANGLE) + std::size_t idx(i * header.n_rows + layer_idx); + for (std::size_t j = 0; j < header.n_rows; j++) { + auto val(static_cast<T>(img[idx + j])); prop_vec.push_back(val); - prop_vec.push_back(val); + if (elem_type == MeshElemType::TRIANGLE || elem_type == MeshElemType::PRISM) + prop_vec.push_back(val); // because each pixel is represented by two cells } - else if (elem_type == MeshElemType::QUAD) - prop_vec.push_back(val); } + } } - - static double getExistingValue(const double* img, std::size_t length); }; } // end namespace MeshLib diff --git a/MeshLib/MeshSearch/ElementSearch.h b/MeshLib/MeshSearch/ElementSearch.h index d18d9f10d5a..d0aed4843c0 100644 --- a/MeshLib/MeshSearch/ElementSearch.h +++ b/MeshLib/MeshSearch/ElementSearch.h @@ -45,17 +45,15 @@ public: template <typename PROPERTY_TYPE> std::size_t searchByPropertyValue( PROPERTY_TYPE const property_value, - std::string const& property_name = "MaterialIDs") + std::string const& property_name) { - if (!_mesh.getProperties().existsPropertyVector<PROPERTY_TYPE>( - property_name)) + if (!_mesh.getProperties().existsPropertyVector<PROPERTY_TYPE>(property_name)) { WARN("Property \"%s\" not found in mesh.", property_name.c_str()); return 0; } auto const* const pv = - _mesh.getProperties().getPropertyVector<PROPERTY_TYPE>( - property_name); + _mesh.getProperties().getPropertyVector<PROPERTY_TYPE>(property_name); if (pv->getMeshItemType() != MeshLib::MeshItemType::Cell) { @@ -65,7 +63,8 @@ public: } std::vector<std::size_t> matchedIDs; - for (std::size_t i(0); i < pv->getNumberOfTuples(); ++i) { + for (std::size_t i(0); i < pv->getNumberOfTuples(); ++i) + { if ((*pv)[i] == property_value) matchedIDs.push_back(i); } diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index fb6b261ecdf..a623a41f84a 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -63,7 +63,7 @@ if(OGS_INSITU) endif() if(Qt5XmlPatterns_FOUND) - target_link_libraries(testrunner Qt5::Core Qt5::Gui Qt5::Xml Qt5::Network) + target_link_libraries(testrunner Qt5::Core Qt5::Gui Qt5::Xml Qt5::Network VtkVis vtkGUISupportQt QtDataView) if(CMAKE_CROSSCOMPILING) target_link_libraries(testrunner ${QT_XML_DEPS_LIBRARIES} diff --git a/Tests/MeshLib/TestMeshValidation.cpp b/Tests/MeshLib/TestMeshValidation.cpp index acbf18911eb..4be198a0ffa 100644 --- a/Tests/MeshLib/TestMeshValidation.cpp +++ b/Tests/MeshLib/TestMeshValidation.cpp @@ -46,8 +46,8 @@ detectHoles(MeshLib::Mesh const& mesh, TEST(MeshValidation, DetectHolesTri) { std::array<double, 12> pix = {{0,0.1,0.2,0.1,0,0,0.1,0,0,0,-0.1,0}}; - GeoLib::RasterHeader const header = - {4,3,MathLib::Point3d(std::array<double,3>{{0,0,0}}),1,-9999}; + GeoLib::RasterHeader const header = { + 4, 3, 1, MathLib::Point3d(std::array<double, 3>{{0, 0, 0}}), 1, -9999}; GeoLib::Raster const raster(header ,pix.begin(), pix.end()); std::unique_ptr<MeshLib::Mesh> mesh (MeshLib::RasterToMesh::convert( raster, MeshLib::MeshElemType::TRIANGLE, MeshLib::UseIntensityAs::ELEVATION)); diff --git a/Tests/MeshLib/TestNodeSearch.cpp b/Tests/MeshLib/TestNodeSearch.cpp index 923e6344a92..7cb79a3557a 100644 --- a/Tests/MeshLib/TestNodeSearch.cpp +++ b/Tests/MeshLib/TestNodeSearch.cpp @@ -23,8 +23,8 @@ TEST(NodeSearch, UnusedNodes) { std::array<double, 12> pix = {{0,0.1,0.2,0.1,0,0,0.1,0,0,0,-0.1,0}}; - GeoLib::RasterHeader const header = - {4,3,MathLib::Point3d(std::array<double,3>{{0,0,0}}),1,-9999}; + GeoLib::RasterHeader const header = { + 4, 3, 1, MathLib::Point3d(std::array<double, 3>{{0, 0, 0}}), 1, -9999}; GeoLib::Raster const raster(header,pix.begin(), pix.end()); MeshLib::Mesh* mesh (MeshLib::RasterToMesh::convert(raster, MeshLib::MeshElemType::TRIANGLE, MeshLib::UseIntensityAs::ELEVATION)); MeshLib::NodeSearch ns(*mesh); diff --git a/Tests/MeshLib/TestRasterToMesh.cpp b/Tests/MeshLib/TestRasterToMesh.cpp new file mode 100644 index 00000000000..2fac62f2b8a --- /dev/null +++ b/Tests/MeshLib/TestRasterToMesh.cpp @@ -0,0 +1,304 @@ +/** + * \copyright + * Copyright (c) 2012-2017, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + * + */ + +#include <cstdio> +#include <memory> + +#include "gtest/gtest.h" + +#include "BaseLib/BuildInfo.h" + +#include "Applications/FileIO/AsciiRasterInterface.h" +#include "GeoLib/Raster.h" +#include "MeshLib/Mesh.h" +#include "MeshLib/MeshGenerators/RasterToMesh.h" +#include "MeshLib/MeshInformation.h" +#include "MeshLib/Node.h" +#include "MeshLib/IO/VtkIO/VtuInterface.h" + +#ifdef OGS_BUILD_GUI +#include "Applications/DataExplorer/VtkVis/VtkGeoImageSource.h" +#include "Applications/DataExplorer/VtkVis/VtkRaster.h" + +#include <vtkImageData.h> +#endif + +class RasterToMeshTest : public ::testing::Test +{ +public: + RasterToMeshTest() + : _file_name(BaseLib::BuildInfo::data_path + "/MeshLib/testraster_selke.asc"), + _mesh_name(BaseLib::BuildInfo::data_path + "/MeshLib/testraster_selke.vtu") + { + _raster.reset(FileIO::AsciiRasterInterface::readRaster(_file_name)); + } + + ~RasterToMeshTest() { std::remove(_mesh_name.c_str()); } + +protected: + std::size_t const _n_pix = 542; + std::size_t const _n_nodes = 626; + double _spacing = 1000; + std::string const _file_name; + std::string const _mesh_name; + std::unique_ptr<GeoLib::Raster> _raster; +}; + +TEST_F(RasterToMeshTest, convertRasterToTriMeshElevation) +{ + std::unique_ptr<MeshLib::Mesh> mesh(MeshLib::RasterToMesh::convert( + *_raster, MeshLib::MeshElemType::TRIANGLE, + MeshLib::UseIntensityAs::ELEVATION, "test")); + ASSERT_TRUE(mesh != nullptr); + + MeshLib::IO::VtuInterface vtkio(mesh.get(), 0, false); + std::string name(BaseLib::BuildInfo::data_path + + "/MeshLib/testraster_selke.vtu"); + vtkio.writeToFile(name); + + std::cout << mesh->getNodes().size() << ", " << mesh->getNumberOfNodes() + << std::endl; + ASSERT_EQ(_n_nodes, mesh->getNodes().size()); + ASSERT_EQ(_n_nodes, mesh->getNumberOfNodes()); + + std::vector<std::string> names = + mesh->getProperties().getPropertyVectorNames(); + ASSERT_TRUE(names.empty()); + + std::array<unsigned, 7> n_types = + MeshLib::MeshInformation::getNumberOfElementTypes(*mesh); + ASSERT_EQ(2 * _n_pix, n_types[1]); + + GeoLib::AABB aabb = MeshLib::MeshInformation::getBoundingBox(*mesh); + ASSERT_NEAR(aabb.getMinPoint()[2], 0, + std::numeric_limits<double>::epsilon()); + ASSERT_NEAR(aabb.getMaxPoint()[2], 0.07, + std::numeric_limits<double>::epsilon()); +} + +TEST_F(RasterToMeshTest, convertRasterToQuadMeshElevation) +{ + std::unique_ptr<MeshLib::Mesh> mesh(MeshLib::RasterToMesh::convert( + *_raster, MeshLib::MeshElemType::QUAD, MeshLib::UseIntensityAs::ELEVATION, "test")); + ASSERT_TRUE(mesh != nullptr); + + ASSERT_EQ(_n_nodes, mesh->getNumberOfBaseNodes()); + + std::vector<std::string> names = mesh->getProperties().getPropertyVectorNames(); + ASSERT_TRUE(names.empty()); + + std::array<unsigned, 7> n_types = + MeshLib::MeshInformation::getNumberOfElementTypes(*mesh); + ASSERT_EQ(_n_pix, n_types[2]); + + GeoLib::AABB aabb = MeshLib::MeshInformation::getBoundingBox(*mesh); + ASSERT_NEAR(aabb.getMinPoint()[2], 0, + std::numeric_limits<double>::epsilon()); + ASSERT_NEAR(aabb.getMaxPoint()[2], 0.07, + std::numeric_limits<double>::epsilon()); +} + +TEST_F(RasterToMeshTest, convertRasterTo3DMeshElevation) +{ + std::unique_ptr<MeshLib::Mesh> mesh(MeshLib::RasterToMesh::convert( + *_raster, MeshLib::MeshElemType::PRISM, + MeshLib::UseIntensityAs::ELEVATION, "test")); + ASSERT_TRUE(mesh == nullptr); + + std::unique_ptr<MeshLib::Mesh> mesh2(MeshLib::RasterToMesh::convert( + *_raster, MeshLib::MeshElemType::HEXAHEDRON, + MeshLib::UseIntensityAs::ELEVATION, "test")); + ASSERT_TRUE(mesh2 == nullptr); +} + +TEST_F(RasterToMeshTest, convertRasterToTriMeshValue) +{ + std::unique_ptr<MeshLib::Mesh> mesh(MeshLib::RasterToMesh::convert( + *_raster, MeshLib::MeshElemType::TRIANGLE, + MeshLib::UseIntensityAs::DATAVECTOR, "test")); + ASSERT_TRUE(mesh != nullptr); + + ASSERT_EQ(_n_nodes, mesh->getNumberOfBaseNodes()); + + std::vector<std::string> names = + mesh->getProperties().getPropertyVectorNames(); + ASSERT_EQ(1, names.size()); + + MeshLib::PropertyVector<double>* prop = + mesh->getProperties().getPropertyVector<double>("test"); + ASSERT_EQ(2 * _n_pix, prop->size()); + + std::pair<double, double> const& bounds = + MeshLib::MeshInformation::getValueBounds<double>(*mesh, "test"); + ASSERT_NEAR(0, bounds.first, std::numeric_limits<double>::epsilon()); + ASSERT_NEAR(0.07, bounds.second, std::numeric_limits<double>::epsilon()); + + std::vector<MeshLib::Node*> const& nodes = mesh->getNodes(); + for (MeshLib::Node* n : nodes) + ASSERT_NEAR(0, (*n)[2], std::numeric_limits<double>::epsilon()); + + std::array<unsigned, 7> n_types = + MeshLib::MeshInformation::getNumberOfElementTypes(*mesh); + ASSERT_EQ(2 * _n_pix, n_types[1]); +} + +TEST_F(RasterToMeshTest, convertRasterToQuadMeshValue) +{ + std::unique_ptr<MeshLib::Mesh> mesh(MeshLib::RasterToMesh::convert( + *_raster, MeshLib::MeshElemType::QUAD, + MeshLib::UseIntensityAs::DATAVECTOR, "test")); + ASSERT_TRUE(mesh != nullptr); + + ASSERT_EQ(_n_nodes, mesh->getNumberOfBaseNodes()); + + std::vector<std::string> names = + mesh->getProperties().getPropertyVectorNames(); + ASSERT_EQ(1, names.size()); + + MeshLib::PropertyVector<double>* prop = + mesh->getProperties().getPropertyVector<double>("test"); + ASSERT_EQ(_n_pix, prop->size()); + + std::pair<double, double> const& bounds = + MeshLib::MeshInformation::getValueBounds<double>(*mesh, "test"); + ASSERT_NEAR(0, bounds.first, std::numeric_limits<double>::epsilon()); + ASSERT_NEAR(0.07, bounds.second, std::numeric_limits<double>::epsilon()); + + std::vector<MeshLib::Node*> const& nodes = mesh->getNodes(); + for (MeshLib::Node* n : nodes) + ASSERT_TRUE((*n)[2] == 0); + + std::array<unsigned, 7> n_types = + MeshLib::MeshInformation::getNumberOfElementTypes(*mesh); + ASSERT_EQ(_n_pix, n_types[2]); +} + +TEST_F(RasterToMeshTest, convertRasterToPrismMeshValue) +{ + std::unique_ptr<MeshLib::Mesh> mesh(MeshLib::RasterToMesh::convert( + *_raster, MeshLib::MeshElemType::PRISM, + MeshLib::UseIntensityAs::DATAVECTOR, "test")); + ASSERT_TRUE(mesh != nullptr); + + ASSERT_EQ(2 * _n_nodes, mesh->getNumberOfBaseNodes()); + + std::vector<std::string> names = + mesh->getProperties().getPropertyVectorNames(); + ASSERT_EQ(1, names.size()); + + MeshLib::PropertyVector<double>* prop = + mesh->getProperties().getPropertyVector<double>("test"); + ASSERT_EQ(2 * _n_pix, prop->size()); + + std::pair<double, double> const& bounds = + MeshLib::MeshInformation::getValueBounds<double>(*mesh, "test"); + ASSERT_NEAR(0, bounds.first, std::numeric_limits<double>::epsilon()); + ASSERT_NEAR(0.07, bounds.second, std::numeric_limits<double>::epsilon()); + + std::vector<MeshLib::Node*> const& nodes = mesh->getNodes(); + for (MeshLib::Node* n : nodes) + ASSERT_TRUE(((*n)[2] == 0) || ((*n)[2] == _spacing)); + + std::array<unsigned, 7> n_types = + MeshLib::MeshInformation::getNumberOfElementTypes(*mesh); + ASSERT_EQ(2 * _n_pix, n_types[6]); +} + +TEST_F(RasterToMeshTest, convertRasterToHexMeshValue) +{ + std::unique_ptr<MeshLib::Mesh> mesh(MeshLib::RasterToMesh::convert( + *_raster, MeshLib::MeshElemType::HEXAHEDRON, + MeshLib::UseIntensityAs::MATERIALS, "MaterialIDs")); + ASSERT_TRUE(mesh != nullptr); + + ASSERT_EQ(2 * _n_nodes, mesh->getNumberOfBaseNodes()); + + std::vector<std::string> names = + mesh->getProperties().getPropertyVectorNames(); + ASSERT_EQ(1, names.size()); + + MeshLib::PropertyVector<int>* prop = + mesh->getProperties().getPropertyVector<int>("MaterialIDs"); + ASSERT_EQ(_n_pix, prop->size()); + + std::pair<int, int> const& bounds = + MeshLib::MeshInformation::getValueBounds<int>(*mesh, "MaterialIDs"); + ASSERT_NEAR(0, bounds.first, std::numeric_limits<double>::epsilon()); + ASSERT_NEAR(0, bounds.second, std::numeric_limits<double>::epsilon()); + + std::vector<MeshLib::Node*> const& nodes = mesh->getNodes(); + for (MeshLib::Node* n : nodes) + ASSERT_TRUE(((*n)[2] == 0) || ((*n)[2] == _spacing)); + + std::array<unsigned, 7> n_types = + MeshLib::MeshInformation::getNumberOfElementTypes(*mesh); + ASSERT_EQ(_n_pix, n_types[4]); +} + +TEST_F(RasterToMeshTest, convertRasterToQuadMeshNone) +{ + std::unique_ptr<MeshLib::Mesh> mesh(MeshLib::RasterToMesh::convert( + *_raster, MeshLib::MeshElemType::QUAD, + MeshLib::UseIntensityAs::NONE, "test")); + ASSERT_TRUE(mesh != nullptr); + + ASSERT_EQ(_n_nodes, mesh->getNumberOfBaseNodes()); + + std::vector<std::string> names = + mesh->getProperties().getPropertyVectorNames(); + ASSERT_TRUE(names.empty()); + + std::vector<MeshLib::Node*> const& nodes = mesh->getNodes(); + for (MeshLib::Node* n : nodes) + ASSERT_TRUE((*n)[2] == 0); + + std::array<unsigned, 7> n_types = + MeshLib::MeshInformation::getNumberOfElementTypes(*mesh); + ASSERT_EQ(_n_pix, n_types[2]); +} + +#ifdef OGS_BUILD_GUI +TEST_F(RasterToMeshTest, vtkImage) +{ + double x0, y0, spacing; + vtkImageAlgorithm* raster = + VtkRaster::loadImage(_file_name, x0, y0, spacing); + double origin[3]; + raster->GetOutput()->GetOrigin(origin); + + std::unique_ptr<MeshLib::Mesh> mesh(MeshLib::RasterToMesh::convert( + raster->GetOutput(), origin, spacing, MeshLib::MeshElemType::TRIANGLE, + MeshLib::UseIntensityAs::DATAVECTOR, "test")); + ASSERT_TRUE(mesh != nullptr); + + ASSERT_EQ(_n_nodes, mesh->getNumberOfBaseNodes()); + + std::vector<std::string> names = + mesh->getProperties().getPropertyVectorNames(); + ASSERT_EQ(1, names.size()); + + MeshLib::PropertyVector<double>* prop = + mesh->getProperties().getPropertyVector<double>("test"); + ASSERT_EQ(2 * _n_pix, prop->size()); + + std::pair<double, double> const& bounds = + MeshLib::MeshInformation::getValueBounds<double>(*mesh, "test"); + ASSERT_NEAR(0, bounds.first, std::numeric_limits<double>::epsilon()); + ASSERT_NEAR(0.07, bounds.second, std::numeric_limits<float>::epsilon()); + + std::vector<MeshLib::Node*> const& nodes = mesh->getNodes(); + for (MeshLib::Node* n : nodes) + ASSERT_TRUE((*n)[2] == 0); + + std::array<unsigned, 7> n_types = + MeshLib::MeshInformation::getNumberOfElementTypes(*mesh); + ASSERT_EQ(2 * _n_pix, n_types[1]); +} +#endif diff --git a/Tests/lfs-data/MeshLib/testraster_selke.asc b/Tests/lfs-data/MeshLib/testraster_selke.asc new file mode 100644 index 00000000000..52713da098b --- /dev/null +++ b/Tests/lfs-data/MeshLib/testraster_selke.asc @@ -0,0 +1,39 @@ +ncols 43 +nrows 33 +xllcorner 4423656.0 +yllcorner 5716944.0 +cellsize 1000 +NODATA_value -9999 + -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 0.01 0.01 0.01 0.02 0.01 0.01 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 + -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 0.02 0.01 0.01 0.01 0.01 0.01 0.02 0.01 0.01 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 + -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 0.01 0.01 0.01 0.01 0.01 0.02 0.02 0.01 0.01 0.01 0.01 0.01 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 + -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 -9999.00 -9999.00 -9999.00 -9999.00 + -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 0.01 0.01 0.00 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 -9999.00 + -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 -9999.00 + -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 0.01 0.01 0.01 0.00 0.00 0.01 0.01 0.01 0.01 0.01 0.00 0.01 0.01 0.01 0.01 0.00 0.01 0.01 0.01 0.01 + -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 0.01 0.01 0.01 0.01 0.00 0.01 0.00 0.01 0.01 0.00 0.00 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 + -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.00 0.00 0.00 0.00 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 + -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 0.01 0.01 0.01 0.01 0.01 0.01 0.00 0.01 0.01 0.00 0.01 0.01 0.00 0.01 0.01 0.01 0.01 0.01 0.01 + -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 0.01 0.01 0.01 0.00 0.00 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 + -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.00 0.00 0.01 0.01 0.01 0.01 + -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 0.01 0.01 0.01 0.01 0.01 0.00 0.01 0.01 0.01 0.01 0.01 0.00 0.00 0.01 0.01 0.01 -9999.00 + -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 0.01 0.01 0.01 0.01 0.01 0.01 0.00 0.01 0.01 0.01 0.01 0.01 0.00 0.00 0.00 0.00 -9999.00 -9999.00 + -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.00 0.00 0.00 -9999.00 -9999.00 -9999.00 + -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 + -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 0.01 0.01 0.01 0.01 0.01 0.01 0.00 0.00 0.01 0.01 0.01 0.01 0.01 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 + -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 0.01 0.01 0.00 0.00 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 + -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 0.01 0.01 0.01 0.00 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 + -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.00 0.01 0.01 0.01 0.01 0.01 0.01 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 + -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 0.01 0.03 -9999.00 -9999.00 0.02 -9999.00 -9999.00 -9999.00 0.02 0.02 0.02 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 + -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 0.03 0.03 0.04 0.04 0.02 0.03 0.02 0.02 0.02 0.02 0.02 0.01 0.01 0.02 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.02 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 + -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 0.04 0.04 0.04 0.04 0.04 0.03 0.03 0.02 0.02 0.02 0.02 0.02 0.02 0.02 0.02 0.01 0.01 0.01 0.01 0.01 0.01 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 + -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 0.05 0.05 0.04 0.04 0.04 0.04 0.03 0.03 0.03 0.02 0.02 0.02 0.02 0.02 0.02 0.01 0.02 0.01 0.01 0.01 0.01 0.01 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 + -9999.00 0.06 0.05 0.05 0.05 0.05 0.06 0.05 0.04 0.05 0.04 0.03 0.03 0.03 0.03 0.03 0.02 0.02 0.02 0.02 0.01 0.01 0.01 0.01 0.01 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 + 0.06 0.06 0.05 0.05 0.05 0.06 0.05 0.05 0.04 0.05 0.04 0.04 0.03 0.03 0.02 0.01 0.02 0.02 0.01 0.02 0.02 0.02 0.02 0.02 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 + 0.06 0.06 0.05 0.05 0.05 0.03 0.04 0.04 0.04 0.04 0.02 0.04 0.03 0.03 0.02 0.02 0.01 0.00 0.02 0.02 0.02 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 + 0.06 0.06 0.06 0.06 0.06 0.05 0.05 0.04 0.04 0.04 0.04 0.03 0.03 0.02 0.02 0.03 0.03 0.03 0.03 0.02 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 + 0.06 0.06 0.06 0.05 0.06 0.05 0.05 0.04 0.04 0.05 0.05 0.04 0.03 0.02 0.03 0.03 0.03 0.03 0.03 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 + 0.07 0.06 0.07 0.06 0.06 0.05 0.05 0.04 0.04 0.04 0.02 0.04 0.03 0.03 0.03 0.04 0.04 0.02 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 + -9999.00 -9999.00 -9999.00 0.06 0.06 0.05 0.05 0.05 0.04 0.04 0.05 0.04 0.04 0.03 0.03 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 + -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 0.05 0.05 0.04 0.04 0.05 0.04 0.04 0.04 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 + -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 0.04 0.05 0.05 0.05 0.04 0.04 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -9999.00 -- GitLab