diff --git a/BaseLib/DateTools.cpp b/BaseLib/DateTools.cpp index 6f2b6e19c4bb7f1cf95e3f6a4dbf88a419bed906..157f572672df301afe55d8d6d9cced7e9c1f6550 100644 --- a/BaseLib/DateTools.cpp +++ b/BaseLib/DateTools.cpp @@ -44,8 +44,8 @@ std::string int2date(int date) { if (date > 10000000 && date < 22000000) { - int y = static_cast<int>(floor(date / 10000.0)); - int m = static_cast<int>(floor((date - (y * 10000)) / 100.0)); + int y = static_cast<int>(std::floor(date / 10000.0)); + int m = static_cast<int>(std::floor((date - (y * 10000)) / 100.0)); int d = date - (y * 10000) - (m * 100); std::stringstream ss; if (d < 10) @@ -68,9 +68,9 @@ std::string date2string(double ddate) } int rest (static_cast<int>(ddate)); - int y = static_cast<int>(floor(rest / 10000.0)); + int y = static_cast<int>(std::floor(rest / 10000.0)); rest = rest % (y * 10000); - int m = static_cast<int>(floor(rest / 100.0)); + int m = static_cast<int>(std::floor(rest / 100.0)); if (m < 1 || m > 12) WARN("date2String(): month not in [1:12]."); rest = rest % (m * 100); diff --git a/FileIO/AsciiRasterInterface.cpp b/FileIO/AsciiRasterInterface.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a7dca0c835b6209578d1c555ef6b3dc8bdc96feb --- /dev/null +++ b/FileIO/AsciiRasterInterface.cpp @@ -0,0 +1,221 @@ +/** + * @file AsciiRasterInterface.cpp + * @author Karsten Rink + * @date 2014-09-10 + * @brief Implementation of the AsciiRasterInterface class. + * + * @copyright + * Copyright (c) 2012-2014, 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 "AsciiRasterInterface.h" + +// ThirdParty/logog +#include "logog/include/logog.hpp" + +#include "Raster.h" + +// BaseLib +#include "FileTools.h" +#include "StringTools.h" + +namespace FileIO { + +GeoLib::Raster* AsciiRasterInterface::readRaster(std::string const& fname) +{ + std::string ext (BaseLib::getFileExtension(fname)); + std::transform(ext.begin(), ext.end(), ext.begin(), tolower); + if (ext.compare("asc") == 0) + return getRasterFromASCFile(fname); + if (ext.compare("grd") == 0) + return getRasterFromSurferFile(fname); + return nullptr; +} + +GeoLib::Raster* AsciiRasterInterface::getRasterFromASCFile(std::string const& fname) +{ + std::ifstream in(fname.c_str()); + + if (!in.is_open()) { + WARN("Raster::getRasterFromASCFile(): Could not open file %s.", fname.c_str()); + return nullptr; + } + + // header information + std::size_t n_cols(0), n_rows(0); + double xllcorner(0.0), yllcorner(0.0), cell_size(0.0), no_data_val(-9999); + + if (readASCHeader(in, n_cols, n_rows, xllcorner, yllcorner, cell_size, no_data_val)) { + double* values = new double[n_cols*n_rows]; + std::string s; + // read the data into the double-array + for (std::size_t j(0); j < n_rows; ++j) { + const size_t idx ((n_rows - j - 1) * n_cols); + for (size_t i(0); i < n_cols; ++i) { + in >> s; + values[idx+i] = strtod(BaseLib::replaceString(",", ".", s).c_str(),0); + + } + } + in.close(); + GeoLib::Raster *raster(new GeoLib::Raster(n_cols, n_rows, xllcorner, yllcorner, + cell_size, values, values+n_cols*n_rows, no_data_val)); + delete [] values; + return raster; + } else { + WARN("Raster::getRasterFromASCFile(): Could not read header of file %s", fname.c_str()); + return nullptr; + } +} + +bool AsciiRasterInterface::readASCHeader(std::ifstream &in, std::size_t &n_cols, std::size_t &n_rows, + double &xllcorner, double &yllcorner, double &cell_size, double &no_data_val) +{ + std::string tag, value; + + in >> tag; + if (tag.compare("ncols") == 0) { + in >> value; + n_cols = atoi(value.c_str()); + } else return false; + + in >> tag; + if (tag.compare("nrows") == 0) { + in >> value; + n_rows = atoi(value.c_str()); + } else return false; + + in >> tag; + if (tag.compare("xllcorner") == 0) { + in >> value; + xllcorner = strtod(BaseLib::replaceString(",", ".", value).c_str(), 0); + } else return false; + + in >> tag; + if (tag.compare("yllcorner") == 0) { + in >> value; + yllcorner = strtod(BaseLib::replaceString(",", ".", value).c_str(), 0); + } else return false; + + in >> tag; + if (tag.compare("cellsize") == 0) { + in >> value; + cell_size = strtod(BaseLib::replaceString(",", ".", value).c_str(), 0); + } else return false; + + in >> tag; + if (tag.compare("NODATA_value") == 0) { + in >> value; + no_data_val = strtod(BaseLib::replaceString(",", ".", value).c_str(), 0); + } else return false; + + return true; +} + +GeoLib::Raster* AsciiRasterInterface::getRasterFromSurferFile(std::string const& fname) +{ + std::ifstream in(fname.c_str()); + + if (!in.is_open()) { + ERR("Raster::getRasterFromSurferFile() - Could not open file %s", fname.c_str()); + return nullptr; + } + + // header information + std::size_t n_cols(0), n_rows(0); + double xllcorner(0.0), yllcorner(0.0), cell_size(0.0), min(0.0), max(0.0); + + if (readSurferHeader(in, n_cols, n_rows, xllcorner, yllcorner, cell_size, min, max)) + { + const double no_data_val (min-1); + double* values = new double[n_cols*n_rows]; + std::string s; + // read the data into the double-array + for (size_t j(0); j < n_rows; ++j) + { + const size_t idx (j * n_cols); + for (size_t i(0); i < n_cols; ++i) + { + in >> s; + const double val (strtod(BaseLib::replaceString(",", ".", s).c_str(),0)); + values[idx+i] = (val > max || val < min) ? no_data_val : val; + } + } + in.close(); + GeoLib::Raster *raster(new GeoLib::Raster(n_cols, n_rows, xllcorner, yllcorner, cell_size, + values, values+n_cols*n_rows, no_data_val)); + delete [] values; + return raster; + } else { + ERR("Raster::getRasterFromASCFile() - could not read header of file %s", fname.c_str()); + return nullptr; + } +} + +bool AsciiRasterInterface::readSurferHeader(std::ifstream &in, size_t &n_cols, std::size_t &n_rows, + double &xllcorner, double &yllcorner, double &cell_size, double &min, double &max) +{ + std::string tag; + + in >> tag; + + if (tag.compare("DSAA") != 0) + { + ERR("Error in readSurferHeader() - No Surfer file."); + return false; + } + else + { + in >> n_cols >> n_rows; + in >> min >> max; + xllcorner = min; + cell_size = (max-min)/static_cast<double>(n_cols); + + in >> min >> max; + yllcorner = min; + + if (ceil((max-min)/static_cast<double>(n_rows)) == ceil(cell_size)) + cell_size = ceil(cell_size); + else + { + ERR("Error in readSurferHeader() - Anisotropic cellsize detected."); + return 0; + } + in >> min >> max; + } + + return true; +} + +void AsciiRasterInterface::writeRasterAsASC(GeoLib::Raster const& raster, std::string const& file_name) +{ + GeoLib::Point const& origin (raster.getOrigin()); + unsigned const nCols (raster.getNCols()); + unsigned const nRows (raster.getNRows()); + + // write header + std::ofstream out(file_name); + out << "ncols " << nCols << "\n"; + out << "nrows " << nRows << "\n"; + out << "xllcorner " << origin[0] << "\n"; + out << "yllcorner " << origin[1] << "\n"; + out << "cellsize " << raster.getRasterPixelSize() << "\n"; + out << "NODATA_value " << raster.getNoDataValue() << "\n"; + + // write data + double const*const elevation(raster.begin()); + for (unsigned row(0); row < nRows; ++row) + { + for (unsigned col(0); col < nCols; ++col) + { + out << elevation[(nRows-row-1) * nCols + col] << " "; + } + out << "\n"; + } + out.close(); +} + +} // end namespace GeoLib diff --git a/FileIO/AsciiRasterInterface.h b/FileIO/AsciiRasterInterface.h new file mode 100644 index 0000000000000000000000000000000000000000..e7e654915700f5b695c8ac31762c3b75e5c1ef0c --- /dev/null +++ b/FileIO/AsciiRasterInterface.h @@ -0,0 +1,57 @@ +/** + * @file AsciiRasterInterface.h + * @author Karsten Rink + * @date 2014-09-10 + * @brief Definition of the AsciiRasterInterface class. + * + * @copyright + * Copyright (c) 2012-2014, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + */ + +#ifndef ASCIIRASTERINTERFACE_H_ +#define ASCIIRASTERINTERFACE_H_ + +#include <fstream> + +namespace GeoLib { + class Raster; +} + +namespace FileIO { + +/** + * Interface for reading and writing a number of ASCII raster formats. + * Currently supported are reading and writing of Esri asc-files and + * reading of Surfer grd-files. + */ +class AsciiRasterInterface { +public: + /// Reads raster file by detecting type based on extension and then calling the apropriate method + static GeoLib::Raster* readRaster(std::string const& fname); + + /// Reads an ArcGis ASC raster file + static GeoLib::Raster* getRasterFromASCFile(std::string const& fname); + + /// Reads a Surfer GRD raster file + static GeoLib::Raster* getRasterFromSurferFile(std::string const& fname); + + /// Writes an Esri asc-file + static void writeRasterAsASC(GeoLib::Raster const& raster, std::string const& file_name); + + +private: + /// Reads the header of a Esri asc-file. + static bool readASCHeader(std::ifstream &in, std::size_t &n_cols, std::size_t &n_rows, + double &xllcorner, double &yllcorner, double &cell_size, double &no_data_val); + + /// Reads the header of a Surfer grd-file. + static bool readSurferHeader(std::ifstream &in, size_t &n_cols, std::size_t &n_rows, + double &xllcorner, double &yllcorner, double &cell_size, double &min, double &max); +}; + +} + +#endif /* ASCIIRASTERINTERFACE_H_ */ diff --git a/FileIO/CMakeLists.txt b/FileIO/CMakeLists.txt index 1c09aec5d5e711fa03bf26654af1cf10510827ec..13fafee37191aa57ffeb9366dace79095f62e8ff 100644 --- a/FileIO/CMakeLists.txt +++ b/FileIO/CMakeLists.txt @@ -1,6 +1,8 @@ # Source files # GET_SOURCE_FILES(SOURCES_FILEIO) SET( SOURCES + AsciiRasterInterface.h + AsciiRasterInterface.cpp GMSInterface.h GMSInterface.cpp GMSHInterface.h diff --git a/FileIO/TetGenInterface.cpp b/FileIO/TetGenInterface.cpp index 1b4228f452c91727c9cc880569669a85e8d3fb88..1297eaff26ab1ce803e4f32a0ff53c2cc9bdb8f4 100644 --- a/FileIO/TetGenInterface.cpp +++ b/FileIO/TetGenInterface.cpp @@ -621,7 +621,7 @@ void TetGenInterface::write3dElements(std::ofstream &out, // get position where number of facets need to be written and figure out worst case of chars that are needed const std::streamoff before_elems_pos (out.tellp()); - const unsigned n_spaces (static_cast<unsigned>(floor(log(nElements*8))) + 1); + const unsigned n_spaces (static_cast<unsigned>(std::floor(log(nElements*8))) + 1); out << std::string(n_spaces, ' ') << "\n"; unsigned element_count(0); diff --git a/GeoLib/Raster.cpp b/GeoLib/Raster.cpp index 3879f675a8b1fd585df78360e871abd7ccb371f2..603f5578bdb19b823cba07be6fc04fc1c950dbc6 100644 --- a/GeoLib/Raster.cpp +++ b/GeoLib/Raster.cpp @@ -73,8 +73,8 @@ Raster* Raster::getRasterFromSurface(Surface const& sfc, double cell_size, doubl Point const& ll (sfc.getAABB().getMinPoint()); Point const& ur (sfc.getAABB().getMaxPoint()); - const std::size_t n_cols = static_cast<size_t>(fabs(ur[0]-ll[0]) / cell_size)+1; - const std::size_t n_rows = static_cast<size_t>(fabs(ur[1]-ll[1]) / cell_size)+1; + const std::size_t n_cols = static_cast<size_t>(std::fabs(ur[0]-ll[0]) / cell_size)+1; + const std::size_t n_rows = static_cast<size_t>(std::fabs(ur[1]-ll[1]) / cell_size)+1; const size_t n_triangles (sfc.getNTriangles()); double *z_vals (new double[n_cols*n_rows]); size_t k(0); @@ -101,13 +101,13 @@ Raster* Raster::getRasterFromSurface(Surface const& sfc, double cell_size, doubl return new Raster(n_cols, n_rows, ll[0], ll[1], cell_size, z_vals, z_vals+n_cols*n_rows ,-9999); } -double Raster::getValueAtPoint(const GeoLib::Point &pnt) +double Raster::getValueAtPoint(const GeoLib::Point &pnt) const { if (pnt[0]>=_ll_pnt[0] && pnt[0]<(_ll_pnt[0]+(_cell_size*_n_cols)) && pnt[1]>=_ll_pnt[1] && pnt[1]<(_ll_pnt[1]+(_cell_size*_n_rows))) { - int cell_x = static_cast<int>(floor((pnt[0] - _ll_pnt[0])/_cell_size)); - int cell_y = static_cast<int>(floor((pnt[1] - _ll_pnt[1])/_cell_size)); + int cell_x = static_cast<int>(std::floor((pnt[0] - _ll_pnt[0])/_cell_size)); + int cell_y = static_cast<int>(std::floor((pnt[1] - _ll_pnt[1])/_cell_size)); // use raster boundary values if node is outside raster due to rounding errors or floating point arithmetic cell_x = (cell_x < 0) ? 0 : ((cell_x > static_cast<int>(_n_cols)) ? (_n_cols-1) : cell_x); @@ -119,189 +119,60 @@ double Raster::getValueAtPoint(const GeoLib::Point &pnt) return _no_data_val; } -void Raster::writeRasterAsASC(std::ostream &os) const +double Raster::interpolateValueAtPoint(GeoLib::Point const& pnt) const { - // write header - os << "ncols " << _n_cols << "\n"; - os << "nrows " << _n_rows << "\n"; - os << "xllcorner " << _ll_pnt[0] << "\n"; - os << "yllcorner " << _ll_pnt[1] << "\n"; - os << "cellsize " << _cell_size << "\n"; - os << "NODATA_value " << _no_data_val << "\n"; - - // write data - for (unsigned row(0); row<_n_rows; row++) { - for (unsigned col(0); col<_n_cols; col++) { - os << _raster_data[(_n_rows-row-1)*_n_cols+col] << " "; - } - os << "\n"; - } -} - -Raster* Raster::readRaster(std::string const& fname) -{ - std::string ext (BaseLib::getFileExtension(fname)); - std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); - if (ext.compare("asc") == 0) - return getRasterFromASCFile(fname); - if (ext.compare("grd") == 0) - return getRasterFromSurferFile(fname); - return nullptr; -} - -Raster* Raster::getRasterFromASCFile(std::string const& fname) -{ - std::ifstream in(fname.c_str()); - - if (!in.is_open()) { - WARN("Raster::getRasterFromASCFile(): Could not open file %s.", fname.c_str()); - return NULL; - } - - // header information - std::size_t n_cols(0), n_rows(0); - double xllcorner(0.0), yllcorner(0.0), cell_size(0.0), no_data_val(-9999); - - if (readASCHeader(in, n_cols, n_rows, xllcorner, yllcorner, cell_size, no_data_val)) { - double* values = new double[n_cols*n_rows]; - std::string s; - // read the data into the double-array - for (size_t j(0); j < n_rows; ++j) { - const size_t idx ((n_rows - j - 1) * n_cols); - for (size_t i(0); i < n_cols; ++i) { - in >> s; - values[idx+i] = strtod(BaseLib::replaceString(",", ".", s).c_str(),0); - - } - } - in.close(); - Raster *raster(new Raster(n_cols, n_rows, xllcorner, yllcorner, - cell_size, values, values+n_cols*n_rows, no_data_val)); - delete [] values; - return raster; - } else { - WARN("Raster::getRasterFromASCFile(): Could not read header of file %s", fname.c_str()); - return NULL; - } + // position in raster + double const xPos ((pnt[0] - _ll_pnt[0]) / _cell_size); + double const yPos ((pnt[1] - _ll_pnt[1]) / _cell_size); + // raster cell index + std::size_t const xIdx (static_cast<size_t>(std::floor(xPos))); + std::size_t const yIdx (static_cast<size_t>(std::floor(yPos))); + + // weights for bilinear interpolation + double const half_delta = 0.5*_cell_size; + double const xShift = std::fabs(xPos-(xIdx+half_delta)) / _cell_size; + double const yShift = std::fabs(yPos-(yIdx+half_delta)) / _cell_size; + std::array<double,4> weight = {{ (1-xShift)*(1-xShift), xShift*(1-yShift), xShift*yShift, (1-xShift)*yShift }}; + + // neightbors to include in interpolation + int const xShiftIdx = (xPos-xIdx-half_delta>=0) ? 1 : -1; + int const yShiftIdx = (yPos-yIdx-half_delta>=0) ? 1 : -1; + std::array<int,4> const x_nb = {{ 0, xShiftIdx, xShiftIdx, 0 }}; + std::array<int,4> const y_nb = {{ 0, 0, yShiftIdx, yShiftIdx }}; + + // get pixel values + std::array<double,4> pix_val; + unsigned no_data_count (0); + for (unsigned j=0; j<4; ++j) + { + pix_val[j] = _raster_data[(yIdx + y_nb[j])*_n_cols + (xIdx + x_nb[j])]; + if (std::fabs(pix_val[j] - _no_data_val) < std::numeric_limits<double>::epsilon()) + { + weight[j] = 0; + no_data_count++; + } + } + + // adjust weights if necessary + if (no_data_count > 0) + { + if (no_data_count == 4) // if there is absolutely no data just use the default value + return _no_data_val; + + const double norm = (double)(4)/(4-no_data_count); + std::for_each(weight.begin(), weight.end(), [&norm](double &val){val*=norm;}); + } + + // new value + return MathLib::scalarProduct<double,4>(weight.data(), pix_val.data()); } -bool Raster::readASCHeader(std::ifstream &in, std::size_t &n_cols, std::size_t &n_rows, - double &xllcorner, double &yllcorner, double &cell_size, double &no_data_val) +bool Raster::isPntOnRaster(GeoLib::Point const& pnt) const { - std::string tag, value; - - in >> tag; - if (tag.compare("ncols") == 0) { - in >> value; - n_cols = atoi(value.c_str()); - } else return false; - - in >> tag; - if (tag.compare("nrows") == 0) { - in >> value; - n_rows = atoi(value.c_str()); - } else return false; - - in >> tag; - if (tag.compare("xllcorner") == 0) { - in >> value; - xllcorner = strtod(BaseLib::replaceString(",", ".", value).c_str(), 0); - } else return false; - - in >> tag; - if (tag.compare("yllcorner") == 0) { - in >> value; - yllcorner = strtod(BaseLib::replaceString(",", ".", value).c_str(), 0); - } else return false; - - in >> tag; - if (tag.compare("cellsize") == 0) { - in >> value; - cell_size = strtod(BaseLib::replaceString(",", ".", value).c_str(), 0); - } else return false; - - in >> tag; - if (tag.compare("NODATA_value") == 0) { - in >> value; - no_data_val = strtod(BaseLib::replaceString(",", ".", value).c_str(), 0); - } else return false; - - return true; -} - -Raster* Raster::getRasterFromSurferFile(std::string const& fname) -{ - std::ifstream in(fname.c_str()); - - if (!in.is_open()) { - ERR("Raster::getRasterFromSurferFile() - Could not open file %s", fname.c_str()); - return NULL; - } - - // header information - std::size_t n_cols(0), n_rows(0); - double xllcorner(0.0), yllcorner(0.0), cell_size(0.0), min(0.0), max(0.0); - - if (readSurferHeader(in, n_cols, n_rows, xllcorner, yllcorner, cell_size, min, max)) - { - const double no_data_val (min-1); - double* values = new double[n_cols*n_rows]; - std::string s; - // read the data into the double-array - for (size_t j(0); j < n_rows; ++j) - { - const size_t idx (j * n_cols); - for (size_t i(0); i < n_cols; ++i) - { - in >> s; - const double val (strtod(BaseLib::replaceString(",", ".", s).c_str(),0)); - values[idx+i] = (val > max || val < min) ? no_data_val : val; - } - } - in.close(); - Raster *raster(new Raster(n_cols, n_rows, xllcorner, yllcorner, - cell_size, values, values+n_cols*n_rows, no_data_val)); - delete [] values; - return raster; - } else { - ERR("Raster::getRasterFromASCFile() - could not read header of file %s", fname.c_str()); - return NULL; - } -} - -bool Raster::readSurferHeader(std::ifstream &in, size_t &n_cols, std::size_t &n_rows, - double &xllcorner, double &yllcorner, double &cell_size, double &min, double &max) -{ - std::string tag; - - in >> tag; - - if (tag.compare("DSAA") != 0) - { - ERR("Error in readSurferHeader() - No Surfer file."); + if ((pnt[0]<_ll_pnt[0]) || (pnt[0]>_ll_pnt[0]+(_n_cols*_cell_size)) || + (pnt[1]<_ll_pnt[1]) || (pnt[1]>_ll_pnt[1]+(_n_rows*_cell_size))) return false; - } - else - { - in >> n_cols >> n_rows; - in >> min >> max; - xllcorner = min; - cell_size = (max-min)/static_cast<double>(n_cols); - - in >> min >> max; - yllcorner = min; - - if (ceil((max-min)/static_cast<double>(n_rows)) == ceil(cell_size)) - cell_size = ceil(cell_size); - else - { - ERR("Error in readSurferHeader() - Anisotropic cellsize detected."); - return 0; - } - in >> min >> max; - } - - return true; + return true; } } // end namespace GeoLib diff --git a/GeoLib/Raster.h b/GeoLib/Raster.h index 52a0c74286d6c2eb14541e9b820d20e65ce62ffe..1f4473ce0ef8754058b2d548c42314eccd73e5c1 100644 --- a/GeoLib/Raster.h +++ b/GeoLib/Raster.h @@ -70,7 +70,7 @@ public: /** * get the distance between raster pixels */ - double getRasterPixelDistance() const { return _cell_size; } + double getRasterPixelSize() const { return _cell_size; } /** * get the origin of lower left raster cell @@ -99,39 +99,20 @@ public: /** * Returns the raster value at the position of the given point. */ - double getValueAtPoint(const GeoLib::Point &pnt); + double getValueAtPoint(const GeoLib::Point &pnt) const; - ~Raster(); + /// Interpolates the elevation of the given point based on the 8-neighbourhood of the raster cell it is located on + double interpolateValueAtPoint(const GeoLib::Point &pnt) const; + + /// Checks if the given point is located within the (x,y)-extension of the raster. + bool isPntOnRaster(GeoLib::Point const& node) const; - /** - * Write meta data and raw raster data as asci file into the output stream. - * @param os the output stream - */ - void writeRasterAsASC(std::ostream &os) const; - - /// Reads raster file by detecting type based on extension and then calling the apropriate method - static Raster* readRaster(std::string const& fname); + ~Raster(); /// Creates a Raster based on a GeoLib::Surface static Raster* getRasterFromSurface(Surface const& sfc, double cell_size, double no_data_val = -9999); - /// Reads an ArcGis ASC raster file - static Raster* getRasterFromASCFile(std::string const& fname); - - /// Reads a Surfer GRD raster file - static Raster* getRasterFromSurferFile(std::string const& fname); private: - static bool readASCHeader(std::ifstream &in, std::size_t &n_cols, std::size_t &n_rows, - double &xllcorner, double &yllcorner, double &cell_size, double &no_data_val); - /** - * Reads the header of a Surfer grd-file. - * \param header The ascHeader-object into which all the information will be written. - * \param in FileInputStream used for reading the data. - * \return True if the header could be read correctly, false otherwise. - */ - static bool readSurferHeader(std::ifstream &in, size_t &n_cols, std::size_t &n_rows, - double &xllcorner, double &yllcorner, double &cell_size, double &min, double &max); - void setCellSize(double cell_size); void setNoDataVal (double no_data_val); diff --git a/Gui/DataView/DiagramView/DiagramScene.cpp b/Gui/DataView/DiagramView/DiagramScene.cpp index f6f60a49dfd188fd0f831d986f1803aa610bc978..c4a49553444ff270546b3edb8f84265e64952072 100644 --- a/Gui/DataView/DiagramView/DiagramScene.cpp +++ b/Gui/DataView/DiagramView/DiagramScene.cpp @@ -134,15 +134,15 @@ void DiagramScene::adjustAxis(float &min, float &max, int &numberOfTicks) { const int MinTicks = 4; double grossStep = (max - min) / MinTicks; - double step = pow(10.0, floor(log10(grossStep))); + double step = pow(10.0, std::floor(log10(grossStep))); if (5 * step < grossStep) step *= 5; else if (2 * step < grossStep) step *= 2; - numberOfTicks = int(ceil(max / step) - floor(min / step)); + numberOfTicks = int(ceil(max / step) - std::floor(min / step)); if (numberOfTicks < MinTicks) numberOfTicks = MinTicks; - min = floor(min / step) * step; + min = std::floor(min / step) * step; max = ceil(max / step) * step; } diff --git a/Gui/DataView/DirectConditionGenerator.cpp b/Gui/DataView/DirectConditionGenerator.cpp index abb16bb120dda8dfa93d32d5650fbfd13cbce596..89d63df52262a81c0c7cae3fdc7735c86e1baa1e 100644 --- a/Gui/DataView/DirectConditionGenerator.cpp +++ b/Gui/DataView/DirectConditionGenerator.cpp @@ -19,6 +19,8 @@ #include "DirectConditionGenerator.h" +#include "AsciiRasterInterface.h" + #include "Raster.h" #include "MeshSurfaceExtraction.h" #include "PointWithID.h" @@ -32,7 +34,7 @@ const std::vector< std::pair<size_t,double> >& DirectConditionGenerator::directT { if (_direct_values.empty()) { - GeoLib::Raster* raster (GeoLib::Raster::readRaster(filename)); + GeoLib::Raster* raster (FileIO::AsciiRasterInterface::readRaster(filename)); if (! raster) { ERR("Error in DirectConditionGenerator::directToSurfaceNodes() - could not load raster file."); return _direct_values; @@ -62,7 +64,7 @@ const std::vector< std::pair<size_t,double> >& DirectConditionGenerator::directW { if (_direct_values.empty()) { - GeoLib::Raster* raster (GeoLib::Raster::readRaster(filename)); + GeoLib::Raster* raster (FileIO::AsciiRasterInterface::readRaster(filename)); if (!raster) { ERR("Error in DirectConditionGenerator::directWithSurfaceIntegration() - could not load raster file."); return _direct_values; diff --git a/Gui/DataView/GeoMapper.cpp b/Gui/DataView/GeoMapper.cpp index 0e58afedbe991ef557ca00c2f20590e4854ed709..9541cb730bdcb41fe430bcc96d0507135253e0f8 100644 --- a/Gui/DataView/GeoMapper.cpp +++ b/Gui/DataView/GeoMapper.cpp @@ -19,6 +19,8 @@ #include <numeric> +#include "FileIO/AsciiRasterInterface.h" + #include "AABB.h" #include "Mesh.h" #include "Elements/Element.h" @@ -44,7 +46,7 @@ GeoMapper::~GeoMapper() void GeoMapper::mapOnDEM(const std::string &file_name) { - this->_raster = GeoLib::Raster::getRasterFromASCFile(file_name); + this->_raster = FileIO::AsciiRasterInterface::getRasterFromASCFile(file_name); if (! _raster) { ERR("GeoMapper::mapOnDEM(): failed to load %s", file_name.c_str()); return; @@ -104,7 +106,7 @@ void GeoMapper::mapData() { GeoLib::Point* pnt ((*points)[j]); (*pnt)[2] = (_grid) ? this->getMeshElevation((*pnt)[0],(*pnt)[1], min_val, max_val) - : this->getDemElevation((*pnt)[0],(*pnt)[1]); + : this->getDemElevation(*pnt); } } else @@ -113,7 +115,7 @@ void GeoMapper::mapData() { GeoLib::Point* pnt ((*points)[j]); double offset = (_grid) ? (this->getMeshElevation((*pnt)[0],(*pnt)[1], min_val, max_val) - (*pnt)[2]) - : (this->getDemElevation((*pnt)[0],(*pnt)[1]) - (*pnt)[2]); + : this->getDemElevation(*pnt); GeoLib::StationBorehole* borehole = static_cast<GeoLib::StationBorehole*>(pnt); const std::vector<GeoLib::Point*> layers = borehole->getProfile(); @@ -127,21 +129,12 @@ void GeoMapper::mapData() } } -float GeoMapper::getDemElevation(double x, double y) const +float GeoMapper::getDemElevation(GeoLib::Point const& pnt) const { - const double origin_x(_raster->getOrigin()[0]); - const double origin_y(_raster->getOrigin()[1]); - const double cellsize(_raster->getRasterPixelDistance()); - const std::size_t width(_raster->getNCols()); - const std::size_t height(_raster->getNRows()); - - if ((x<origin_x) || (x>origin_x+(width*cellsize)) || (y<origin_y) || (y>origin_y+(height*cellsize))) - return 0; - - const unsigned x_index = static_cast<unsigned>((x-origin_x)/cellsize); - const unsigned y_index = static_cast<unsigned>((y-origin_y)/cellsize); - - return static_cast<float>(*(_raster->begin() + (y_index*width+x_index))); + double const elevation (_raster->getValueAtPoint(pnt)); + if (std::abs(elevation-_raster->getNoDataValue()) < std::numeric_limits<double>::epsilon()) + return 0.0; + return static_cast<float>(elevation); } double GeoMapper::getMeshElevation(double x, double y, double min_val, double max_val) const diff --git a/Gui/DataView/GeoMapper.h b/Gui/DataView/GeoMapper.h index 6ae5b11e22def013105409d602458a3920e35319..db3160d1b6dc3739a2f5804c06dae904756d2b50 100644 --- a/Gui/DataView/GeoMapper.h +++ b/Gui/DataView/GeoMapper.h @@ -57,7 +57,7 @@ private: double getMeshElevation(double x, double y, double min_val, double max_val) const; // Returns the elevation at Point (x,y) based on a raster - float getDemElevation(double x, double y) const; + float getDemElevation(GeoLib::Point const& pnt) const; // Calculates the intersection of two lines embedded in the xy-plane GeoLib::Point* calcIntersection(GeoLib::Point const*const p1, GeoLib::Point const*const p2, GeoLib::Point const*const q1, GeoLib::Point const*const q2) const; diff --git a/Gui/DataView/MeshLayerEditDialog.cpp b/Gui/DataView/MeshLayerEditDialog.cpp index 91650be0a9b5ed3a51506437c8efbf8211a75488..231275565f02e5cfb72321cca63a1a8057b9c506 100644 --- a/Gui/DataView/MeshLayerEditDialog.cpp +++ b/Gui/DataView/MeshLayerEditDialog.cpp @@ -106,12 +106,12 @@ void MeshLayerEditDialog::createWithRasters() this->_layerBox = new QGroupBox(this); this->_layerBox->setTitle(selectText); - for (unsigned i = 0; i <= _n_layers+1; ++i) + for (unsigned i = 0; i <= _n_layers; ++i) { QString text(""); if (i==0) text = "Surface"; - else if (i>_n_layers) text = "Layer" + QString::number(_n_layers) + "-Bottom"; - else text="Layer" + QString::number(i) + "-Top"; + else if (i == _n_layers) text = "Layer" + QString::number(_n_layers) + "-Bottom"; + else text="Layer" + QString::number(i+1) + "-Top"; QLineEdit* edit (new QLineEdit(this)); QPushButton* button (new QPushButton("...", _layerBox)); @@ -178,38 +178,30 @@ void MeshLayerEditDialog::createMeshToolSelection() MeshLib::Mesh* MeshLayerEditDialog::createPrismMesh() { - const unsigned nLayers = _layerEdit->text().toInt(); - std::vector<float> layer_thickness; - for (unsigned i=0; i<nLayers; ++i) - { - // "100" is just a default size to have any value for extruding 2D elements. - // The actual mapping based on a raster file will be performed later. - const float thickness = (_use_rasters) ? 100 : (this->_edits[i]->text().toFloat()); - layer_thickness.push_back(thickness); - } - - MeshLib::Mesh* new_mesh = MeshLayerMapper::CreateLayers(*_msh, layer_thickness); - - if (_use_rasters) - { - for (unsigned i=0; i<=nLayers; ++i) - { - const std::string imgPath ( this->_edits[i+1]->text().toStdString() ); - const double noDataReplacement = (i==0) ? 0.0 : -9999.0; - if (!MeshLayerMapper::LayerMapping(*new_mesh, imgPath, nLayers, i, noDataReplacement)) - { - delete new_mesh; - return nullptr; - } - } - if (this->_edits[0]->text().length()>0) - { - MeshLib::Mesh* final_mesh = MeshLayerMapper::blendLayersWithSurface(*new_mesh, nLayers, this->_edits[0]->text().toStdString()); - delete new_mesh; - new_mesh = final_mesh; - } - } - return new_mesh; + const unsigned nLayers = _layerEdit->text().toInt(); + std::vector<float> layer_thickness; + for (unsigned i=0; i<nLayers; ++i) + { + // "100" is just a default size to have any value for extruding 2D elements. + // The actual mapping based on a raster file will be performed later. + const float thickness = (_use_rasters) ? 100 : (this->_edits[i]->text().toFloat()); + layer_thickness.push_back(thickness); + } + + MeshLayerMapper mapper; + MeshLib::Mesh* new_mesh (nullptr); + + if (_use_rasters) + { + std::vector<std::string> raster_paths; + for (int i=nLayers; i>=0; --i) + raster_paths.push_back(this->_edits[i]->text().toStdString()); + if (mapper.createLayers(*_msh, raster_paths)) + new_mesh= mapper.getMesh("SubsurfaceMesh"); + } + else + new_mesh = mapper.createStaticLayers(*_msh, layer_thickness); + return new_mesh; } MeshLib::Mesh* MeshLayerEditDialog::createTetMesh() @@ -226,12 +218,12 @@ MeshLib::Mesh* MeshLayerEditDialog::createTetMesh() if (_use_rasters) { std::vector<std::string> raster_paths(nLayers+1); - for (unsigned i=0; i<=nLayers; ++i) - raster_paths[i] = this->_edits[i+1]->text().toStdString(); + for (int i=nLayers; i>=0; --i) + raster_paths[i] = this->_edits[i]->text().toStdString(); LayeredVolume lv; - lv.createGeoVolumes(*_msh, raster_paths); + lv.createLayers(*_msh, raster_paths); - tg_mesh = lv.getMesh(); + tg_mesh = lv.getMesh("SubsurfaceMesh"); QString file_path(""); if (tg_mesh) @@ -246,7 +238,8 @@ MeshLib::Mesh* MeshLayerEditDialog::createTetMesh() std::vector<float> layer_thickness; for (unsigned i=0; i<nLayers; ++i) layer_thickness.push_back(this->_edits[i]->text().toFloat()); - tg_mesh = MeshLayerMapper::CreateLayers(*_msh, layer_thickness); + MeshLayerMapper const mapper; + tg_mesh = mapper.createStaticLayers(*_msh, layer_thickness); std::vector<MeshLib::Node> tg_attr; FileIO::TetGenInterface tetgen_interface; tetgen_interface.writeTetGenSmesh(filename.toStdString(), *tg_mesh, tg_attr); @@ -291,7 +284,8 @@ void MeshLayerEditDialog::accept() new_mesh = new MeshLib::Mesh(*_msh); const std::string imgPath ( this->_edits[0]->text().toStdString() ); const double noDataReplacementValue = this->_noDataReplacementEdit->text().toDouble(); - if (!MeshLayerMapper::LayerMapping(*new_mesh, imgPath, nLayers, 0, noDataReplacementValue)) + MeshLayerMapper const mapper; + if (!mapper.layerMapping(*new_mesh, imgPath, noDataReplacementValue)) { delete new_mesh; return; diff --git a/Gui/VtkVis/VtkColorLookupTable.cpp b/Gui/VtkVis/VtkColorLookupTable.cpp index 93b25955192e4ac3b1a96e4b85eea34319302ace..425705500703a03c2e4720eb6b570756bd8ae773 100644 --- a/Gui/VtkVis/VtkColorLookupTable.cpp +++ b/Gui/VtkVis/VtkColorLookupTable.cpp @@ -70,7 +70,7 @@ void VtkColorLookupTable::Build() for (std::map<double, unsigned char*>::const_iterator it = _dict.begin(); it != _dict.end(); ++it) { double val = (it->first < range[0]) ? range[0] : ((it->first > range[1]) ? range[1] : it->first); - nextIndex = static_cast<size_t>( floor(val-range[0]) ); + nextIndex = static_cast<size_t>( std::floor(val-range[0]) ); this->SetTableValue(nextIndex, it->second); @@ -181,7 +181,7 @@ void VtkColorLookupTable::getColor(vtkIdType indx, unsigned char rgba[4]) const ? static_cast<vtkIdType>(this->TableRange[0]) : (indx >=this->TableRange[1] ? static_cast<vtkIdType>(this->TableRange[1]) - 1 : indx)); indx = - static_cast<size_t>( floor( (indx - this->TableRange[0]) * + static_cast<size_t>( std::floor( (indx - this->TableRange[0]) * (this->NumberOfColors / (this->TableRange[1] - this->TableRange[0])) ) ); unsigned char* _rgba; diff --git a/Gui/VtkVis/VtkRaster.cpp b/Gui/VtkVis/VtkRaster.cpp index 7cfa4db9ce9d204ffd55c5177ae2a9be60c85910..60a7fcbe2f178852c31c0109f89604534f51b9ce 100644 --- a/Gui/VtkVis/VtkRaster.cpp +++ b/Gui/VtkVis/VtkRaster.cpp @@ -27,6 +27,8 @@ #include "StringTools.h" +#include "AsciiRasterInterface.h" + // GeoLib #include "Raster.h" @@ -54,16 +56,16 @@ vtkImageAlgorithm* VtkRaster::loadImage(const std::string &fileName, GeoLib::Raster *raster(nullptr); if (fileInfo.suffix().toLower() == "asc") { - raster = GeoLib::Raster::getRasterFromASCFile(fileName); + raster = FileIO::AsciiRasterInterface::getRasterFromASCFile(fileName); } else if (fileInfo.suffix().toLower() == "grd") { - raster = GeoLib::Raster::getRasterFromSurferFile(fileName); + raster = FileIO::AsciiRasterInterface::getRasterFromSurferFile(fileName); } if (raster) { x0 = raster->getOrigin()[0]; y0 = raster->getOrigin()[1]; - delta = raster->getRasterPixelDistance(); + delta = raster->getRasterPixelSize(); double const*const data (raster->begin()); return VtkRaster::loadImageFromArray(data, x0, y0, raster->getNCols(), raster->getNRows(), delta, diff --git a/MeshLib/MeshGenerators/ConvertRasterToMesh.cpp b/MeshLib/MeshGenerators/ConvertRasterToMesh.cpp index c4d98231c9df6b3e81380976ccc2378c954a0fed..5fcfbfab7e1665a3942933ca2a0e736cfde7551c 100644 --- a/MeshLib/MeshGenerators/ConvertRasterToMesh.cpp +++ b/MeshLib/MeshGenerators/ConvertRasterToMesh.cpp @@ -76,7 +76,7 @@ MeshLib::Mesh* ConvertRasterToMesh::constructMesh(const double* pix_vals, const const size_t height = _raster.getNRows()+1; const size_t width = _raster.getNCols()+1; size_t node_idx_count(0); - const double distance(_raster.getRasterPixelDistance()); + const double distance(_raster.getRasterPixelSize()); const double x_offset(_raster.getOrigin()[0]); // - distance / 2.0); const double y_offset(_raster.getOrigin()[1]); // - distance / 2.0); diff --git a/MeshLib/MeshGenerators/LayeredMeshGenerator.cpp b/MeshLib/MeshGenerators/LayeredMeshGenerator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d16106a875b0711133a68f293881369d6c8df60b --- /dev/null +++ b/MeshLib/MeshGenerators/LayeredMeshGenerator.cpp @@ -0,0 +1,82 @@ +/** + * \file LayeredMeshGenerator.cpp + * \author Karsten Rink + * \date 2014-09-18 + * \brief Implementation of the SubsurfaceMapper class. + * + * \copyright + * Copyright (c) 2012-2014, 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 "LayeredMeshGenerator.h" + +#include <vector> +#include <fstream> + +#include "FileIO/AsciiRasterInterface.h" + +#include "Raster.h" + +#include "Mesh.h" +#include "Node.h" +#include "Elements/Element.h" +#include "MeshQuality/MeshValidation.h" + +LayeredMeshGenerator::LayeredMeshGenerator() +: _elevation_epsilon(0.0001) +{ +} + +bool LayeredMeshGenerator::createLayers(MeshLib::Mesh const& mesh, std::vector<std::string> const& raster_paths, double noDataReplacementValue) +{ + if (mesh.getDimension() != 2 || !allRastersExist(raster_paths)) + return false; + + std::vector<GeoLib::Raster const*> rasters; + rasters.reserve(raster_paths.size()); + for (auto path = raster_paths.begin(); path != raster_paths.end(); ++path) + rasters.push_back(FileIO::AsciiRasterInterface::getRasterFromASCFile(*path)); + + bool result = createRasterLayers(mesh, rasters, noDataReplacementValue); + std::for_each(rasters.begin(), rasters.end(), [](GeoLib::Raster const*const raster){ delete raster; }); + return result; +} + +MeshLib::Mesh* LayeredMeshGenerator::getMesh(std::string const& mesh_name) const +{ + if (_nodes.empty() || _elements.empty()) + return nullptr; + + MeshLib::Mesh* result (new MeshLib::Mesh(mesh_name, _nodes, _elements)); + MeshLib::MeshValidation::removeUnusedMeshNodes(*result); + return result; +} + +double LayeredMeshGenerator::calcEpsilon(GeoLib::Raster const& high, GeoLib::Raster const& low) +{ + const double max (*std::max_element(high.begin(), high.end())); + const double min (*std::min_element( low.begin(), low.end())); + return ((max-min)*1e-07); +} + +bool LayeredMeshGenerator::allRastersExist(std::vector<std::string> const& raster_paths) const +{ + for (auto raster = raster_paths.begin(); raster != raster_paths.end(); ++raster) + { + std::ifstream file_stream (*raster, std::ifstream::in); + if (!file_stream.good()) + return false; + file_stream.close(); + } + return true; +} + +void LayeredMeshGenerator::cleanUpOnError() +{ + std::for_each(_nodes.begin(), _nodes.end(), [](MeshLib::Node *node) { delete node; }); + std::for_each(_elements.begin(), _elements.end(), [](MeshLib::Element *elem) { delete elem; }); +} diff --git a/MeshLib/MeshGenerators/LayeredMeshGenerator.h b/MeshLib/MeshGenerators/LayeredMeshGenerator.h new file mode 100644 index 0000000000000000000000000000000000000000..9cd3f81599642af2a2099426aac00241ae637fb8 --- /dev/null +++ b/MeshLib/MeshGenerators/LayeredMeshGenerator.h @@ -0,0 +1,79 @@ +/** + * \file LayeredMeshGenerator.h + * \author Karsten Rink + * \date 2014-09-18 + * \brief Definition of the SubsurfaceMapper class + * + * \copyright + * Copyright (c) 2012-2014, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + */ + +#ifndef LAYEREDMESHGENERATOR_H +#define LAYEREDMESHGENERATOR_H + +#include <string> +#include <vector> + +namespace GeoLib { + class Raster; +} + +namespace MeshLib { + class Mesh; + class Node; + class Element; +} + +/** + * \brief Base class for creation of 3D subsurface meshes based on raster data + */ +class LayeredMeshGenerator +{ +public: + /** + * Returns a subsurface representation of a region represented by a 2D mesh by reading raster files and calling the appropriate construction method. + * @param mesh The 2D surface mesh that is used as a basis for the subsurface mesh + * @param raster_paths Containing all the raster-file-names for the subsurface layers from bottom to top (starting with the bottom of the oldest layer and ending with the DEM) + * @param noDataReplacementValue Default z-coordinate if there are mesh nodes not located on the DEM raster (i.e. raster_paths[0]) + * @result true if the subsurface representation has been created, false if there was an error + */ + virtual bool createLayers(MeshLib::Mesh const& mesh, std::vector<std::string> const& raster_paths, double noDataReplacementValue = 0.0) final; + + /** + * Constructs a subsurface representation based on a 2D mesh and a number of rasters representing subsurface layer boundaries. + * @param mesh The 2D surface mesh that is used as a basis for the subsurface mesh + * @param rasters Containing all the raster-data for the subsurface layers from bottom to top (starting with the bottom of the oldest layer and ending with the DEM) + * @param noDataReplacementValue Default z-coordinate if there are mesh nodes not located on the DEM raster (i.e. raster_paths[0]) + * @result true if the subsurface representation has been created, false if there was an error + */ + virtual bool createRasterLayers(MeshLib::Mesh const& mesh, std::vector<GeoLib::Raster const*> const& rasters, double noDataReplacementValue) = 0; + + /// Returns a mesh of the subsurface representation + MeshLib::Mesh* getMesh(std::string const& mesh_name) const; + +protected: + LayeredMeshGenerator(); + ~LayeredMeshGenerator() {} + + /// Adds another layer to the subsurface mesh + virtual void addLayerToMesh(MeshLib::Mesh const& mesh_layer, unsigned layer_id, GeoLib::Raster const& raster) = 0; + + /// Calculates a data-dependent epsilon value + double calcEpsilon(GeoLib::Raster const& high, GeoLib::Raster const& low); + + /// Checks if all raster files actually exist + bool allRastersExist(std::vector<std::string> const& raster_paths) const; + + /// Cleans up the already created objects in case of an error + void cleanUpOnError(); + + double _elevation_epsilon; + std::vector<MeshLib::Node*> _nodes; + std::vector<MeshLib::Element*> _elements; +}; + +#endif //LAYEREDMESHGENERATOR_H diff --git a/MeshLib/MeshGenerators/LayeredVolume.cpp b/MeshLib/MeshGenerators/LayeredVolume.cpp index 185df44928111dcbc79a98e9f9544448795a2b7b..3263dcb0343c30bc69accbfb9605e2c0e53c5409 100644 --- a/MeshLib/MeshGenerators/LayeredVolume.cpp +++ b/MeshLib/MeshGenerators/LayeredVolume.cpp @@ -14,47 +14,17 @@ #include "LayeredVolume.h" -#include <fstream> -#include <numeric> - #include "Vector3.h" -#include "GEOObjects.h" -#include "PointVec.h" -#include "Mesh.h" -#include "convertMeshToGeo.h" -#include "Elements/Element.h" +#include "Raster.h" + #include "Elements/Tri.h" #include "Elements/Quad.h" #include "MeshGenerators/MeshLayerMapper.h" -#include "MeshQuality/MeshValidation.h" #include "MeshEditing/ElementExtraction.h" -const double LayeredVolume::_invalid_value = -9999; - -LayeredVolume::LayeredVolume() -: _elevation_epsilon(0.0001), _mesh(nullptr) -{ -} - -bool LayeredVolume::createGeoVolumes(const MeshLib::Mesh &mesh, const std::vector<std::string> &raster_paths, double noDataReplacementValue) -{ - if (mesh.getDimension() != 2 || !allRastersExist(raster_paths)) - return false; - - std::vector<GeoLib::Raster const*> rasters; - rasters.reserve(raster_paths.size()); - for (auto path = raster_paths.begin(); path != raster_paths.end(); ++path) - rasters.push_back(GeoLib::Raster::getRasterFromASCFile(*path)); - - const bool result = this->createGeoVolumes(mesh, rasters, noDataReplacementValue); - std::for_each(rasters.begin(), rasters.end(), [](GeoLib::Raster const*const raster){ delete raster; }); - return result; -} - - -bool LayeredVolume::createGeoVolumes(const MeshLib::Mesh &mesh, const std::vector<GeoLib::Raster const*> &rasters, double noDataReplacementValue) +bool LayeredVolume::createRasterLayers(const MeshLib::Mesh &mesh, const std::vector<GeoLib::Raster const*> &rasters, double noDataReplacementValue) { if (mesh.getDimension() != 2) return false; @@ -74,34 +44,33 @@ bool LayeredVolume::createGeoVolumes(const MeshLib::Mesh &mesh, const std::vecto const std::size_t nRasters (rasters.size()); for (size_t i=0; i<nRasters; ++i) { - const double replacement_value = (i==0) ? noDataReplacementValue : _invalid_value; - if (!MeshLayerMapper::LayerMapping(*mesh_layer, *rasters[i], 0, 0, replacement_value)) + const double replacement_value = (i==(nRasters-1)) ? noDataReplacementValue : rasters[i]->getNoDataValue(); + if (!MeshLayerMapper::layerMapping(*mesh_layer, *rasters[i], replacement_value)) { this->cleanUpOnError(); return false; } - this->addLayerToMesh(*mesh_layer, i); + this->addLayerToMesh(*mesh_layer, i, *rasters[i]); } // close boundaries between layers this->addLayerBoundaries(*mesh_layer, nRasters); this->removeCongruentElements(nRasters, mesh_layer->getNElements()); delete mesh_layer; - _mesh = new MeshLib::Mesh("BoundaryMesh", _nodes, _elements); - MeshLib::MeshValidation::removeUnusedMeshNodes(*_mesh); return true; } -void LayeredVolume::addLayerToMesh(const MeshLib::Mesh &mesh_layer, unsigned layer_id) +void LayeredVolume::addLayerToMesh(const MeshLib::Mesh &mesh_layer, unsigned layer_id, GeoLib::Raster const& raster) { const std::vector<MeshLib::Node*> &layer_nodes (mesh_layer.getNodes()); const std::size_t nNodes (layer_nodes.size()); const std::size_t node_id_offset (_nodes.size()); const std::size_t last_layer_offset (node_id_offset-nNodes); + const double no_data_value (raster.getNoDataValue()); for (std::size_t i=0; i<nNodes; ++i) { if (layer_id > 0 && - ((*layer_nodes[i])[2] == _invalid_value || + ((*layer_nodes[i])[2] == no_data_value || (*_nodes[last_layer_offset+i])[2]-(*layer_nodes[i])[2] < _elevation_epsilon)) _nodes.push_back(new MeshLib::Node(*_nodes[last_layer_offset+i])); else @@ -196,36 +165,3 @@ void LayeredVolume::removeCongruentElements(std::size_t nLayers, std::size_t nEl auto elem_vec_end = std::remove(_elements.begin(), _elements.end(), nullptr); _elements.erase(elem_vec_end, _elements.end()); } - -bool LayeredVolume::exportToGeometry(GeoLib::GEOObjects &geo_objects) const -{ - if (_mesh == nullptr) - return false; - MeshLib::convertMeshToGeo(*_mesh, geo_objects, std::numeric_limits<double>::min()); - return true; -} - -double LayeredVolume::calcEpsilon(const GeoLib::Raster &high, const GeoLib::Raster &low) -{ - const double max (*std::max_element(high.begin(), high.end())); - const double min (*std::min_element( low.begin(), low.end())); - return ((max-min)*1e-07); -} - -bool LayeredVolume::allRastersExist(const std::vector<std::string> &raster_paths) const -{ - for (auto raster = raster_paths.begin(); raster != raster_paths.end(); ++raster) - { - std::ifstream file_stream (*raster, std::ifstream::in); - if (!file_stream.good()) - return false; - file_stream.close(); - } - return true; -} - -void LayeredVolume::cleanUpOnError() -{ - std::for_each(_nodes.begin(), _nodes.end(), [](MeshLib::Node *node) { delete node; }); - std::for_each(_elements.begin(), _elements.end(), [](MeshLib::Element *elem) { delete elem; }); -} diff --git a/MeshLib/MeshGenerators/LayeredVolume.h b/MeshLib/MeshGenerators/LayeredVolume.h index ee5ecb011aca5e65426fbf5870784e44cdd69ff1..9d6a2560df47d3ecc6ef2892f126fb9e10b6d0bd 100644 --- a/MeshLib/MeshGenerators/LayeredVolume.h +++ b/MeshLib/MeshGenerators/LayeredVolume.h @@ -18,58 +18,38 @@ #include <string> #include <vector> -#include "Raster.h" #include "Node.h" +#include "LayeredMeshGenerator.h" namespace GeoLib { class GEOObjects; class Surface; } -namespace MeshLib { - class Mesh; - class Element; -} - /** * \brief Creates a volume geometry from 2D mesh layers based on raster data. */ -class LayeredVolume +class LayeredVolume : public LayeredMeshGenerator { public: - LayeredVolume(); + LayeredVolume() {} ~LayeredVolume() {} /** * Constructs a subsurface representation of a mesh using only 2D elements (i.e. layer boundaries are represented by surfaces) * @param mesh The 2D surface mesh that is used as a basis for the subsurface mesh - * @param rasters Containing all the raster-data for the subsurface layers from top to bottom (starting with the DEM) + * @param rasters Containing all the raster-data for the subsurface layers from bottom to top (starting with the bottom of the oldest layer and ending with the DEM) * @param noDataReplacementValue Default z-coordinate if there are mesh nodes not located on the DEM raster (i.e. raster_paths[0]) * @result true if the subsurface representation has been created, false if there was an error */ - bool createGeoVolumes(const MeshLib::Mesh &mesh, const std::vector<GeoLib::Raster const*> &rasters, double noDataReplacementValue = 0.0); - - /** - * Constructs a subsurface representation of a mesh using only 2D elements (i.e. layer boundaries are represented by surfaces) - * @param mesh The 2D surface mesh that is used as a basis for the subsurface mesh - * @param raster_paths Containing all the raster-file-names for the subsurface layers from top to bottom (starting with the DEM) - * @param noDataReplacementValue Default z-coordinate if there are mesh nodes not located on the DEM raster (i.e. raster_paths[0]) - * @result true if the subsurface representation has been created, false if there was an error - */ - bool createGeoVolumes(const MeshLib::Mesh &mesh, const std::vector<std::string> &raster_paths, double noDataReplacementValue = 0.0); - - /// Returns the subsurface representation that can be used as input for the TetGenInterface - MeshLib::Mesh* getMesh() const { return _mesh; } - + bool createRasterLayers(const MeshLib::Mesh &mesh, const std::vector<GeoLib::Raster const*> &rasters, double noDataReplacementValue = 0.0); + /// Returns the region attribute vector necessary for assigning region attributes via TetGen std::vector<MeshLib::Node> getAttributePoints() { return _attribute_points; } - /// Converts the subsurface representation to geometry objects and adds them to the geo storage object - bool exportToGeometry(GeoLib::GEOObjects &geo_objects) const; - private: /// Adds another layer to the subsurface mesh - void addLayerToMesh(const MeshLib::Mesh &mesh_layer, unsigned layer_id); + void addLayerToMesh(const MeshLib::Mesh &mesh_layer, unsigned layer_id, GeoLib::Raster const& raster); /// Creates boundary surfaces between the mapped layers to make the volumes watertight void addLayerBoundaries(const MeshLib::Mesh &layer, std::size_t nLayers); @@ -77,21 +57,7 @@ private: /// Removes duplicate 2D elements (possible due to outcroppings) void removeCongruentElements(std::size_t nLayers, std::size_t nElementsPerLayer); - /// Calculates a data-dependent epsilon value - double calcEpsilon(const GeoLib::Raster &high, const GeoLib::Raster &low); - - /// Checks if all raster files actually exist - bool allRastersExist(const std::vector<std::string> &raster_paths) const; - - /// Cleans up the already created object in case of an error - void cleanUpOnError(); - - static const double _invalid_value; - double _elevation_epsilon; - std::vector<MeshLib::Node*> _nodes; - std::vector<MeshLib::Element*> _elements; std::vector<MeshLib::Node> _attribute_points; - MeshLib::Mesh* _mesh; }; #endif //LAYEREDVOLUME_H diff --git a/MeshLib/MeshGenerators/MeshLayerMapper.cpp b/MeshLib/MeshGenerators/MeshLayerMapper.cpp index b302c5e113b04401ba68cda68dafc10061551db2..c56b846d7089d364e21ead757fad6eb349914d5a 100644 --- a/MeshLib/MeshGenerators/MeshLayerMapper.cpp +++ b/MeshLib/MeshGenerators/MeshLayerMapper.cpp @@ -12,28 +12,32 @@ * */ -// stl +#include "MeshLayerMapper.h" + #include <algorithm> -// ThirdParty/logog #include "logog/include/logog.hpp" -#include "MeshLayerMapper.h" -// GeoLib +#include "FileIO/AsciiRasterInterface.h" + #include "Raster.h" -#include "Mesh.h" -#include "Node.h" -#include "Elements/Element.h" #include "Elements/Tet.h" #include "Elements/Hex.h" #include "Elements/Pyramid.h" #include "Elements/Prism.h" #include "MeshSurfaceExtraction.h" + #include "MathTools.h" +const unsigned MeshLayerMapper::_pyramid_base[3][4] = +{ + {1, 3, 4, 2}, // Point 4 missing + {2, 4, 3, 0}, // Point 5 missing + {0, 3, 4, 1}, // Point 6 missing +}; -MeshLib::Mesh* MeshLayerMapper::CreateLayers(const MeshLib::Mesh &mesh, const std::vector<float> &layer_thickness_vector) +MeshLib::Mesh* MeshLayerMapper::createStaticLayers(MeshLib::Mesh const& mesh, std::vector<float> const& layer_thickness_vector, std::string const& mesh_name) const { std::vector<float> thickness; for (std::size_t i=0; i<layer_thickness_vector.size(); ++i) @@ -45,11 +49,11 @@ MeshLib::Mesh* MeshLayerMapper::CreateLayers(const MeshLib::Mesh &mesh, const st const std::size_t nLayers(thickness.size()); if (nLayers < 1 || mesh.getDimension() != 2) { - ERR("MshLayerMapper::CreateLayers(): A 2D mesh with nLayers > 0 is required as input."); + ERR("MeshLayerMapper::createStaticLayers(): A 2D mesh with nLayers > 0 is required as input."); return nullptr; } - const size_t nNodes = mesh.getNNodes(); + const std::size_t nNodes = mesh.getNNodes(); // count number of 2d elements in the original mesh const std::size_t nElems (std::count_if(mesh.getElements().begin(), mesh.getElements().end(), [](MeshLib::Element const* elem) { return (elem->getDimension() == 2);})); @@ -57,8 +61,7 @@ MeshLib::Mesh* MeshLayerMapper::CreateLayers(const MeshLib::Mesh &mesh, const st const std::size_t nOrgElems (mesh.getNElements()); const std::vector<MeshLib::Node*> &nodes = mesh.getNodes(); const std::vector<MeshLib::Element*> &elems = mesh.getElements(); - std::vector<MeshLib::Node*> new_nodes; - new_nodes.reserve(nNodes + (nLayers * nNodes)); + std::vector<MeshLib::Node*> new_nodes(nNodes + (nLayers * nNodes)); std::vector<MeshLib::Element*> new_elems; new_elems.reserve(nElems * nLayers); double z_offset (0.0); @@ -68,304 +71,182 @@ MeshLib::Mesh* MeshLayerMapper::CreateLayers(const MeshLib::Mesh &mesh, const st // add nodes for new layer unsigned node_offset (nNodes * layer_id); if (layer_id > 0) z_offset += thickness[layer_id-1]; - for (unsigned i = 0; i < nNodes; ++i) - { - const double* coords = nodes[i]->getCoords(); - new_nodes.push_back (new MeshLib::Node(coords[0], coords[1], coords[2]-z_offset, node_offset+i)); - } + + std::transform(nodes.cbegin(), nodes.cend(), new_nodes.begin() + node_offset, + [&z_offset](MeshLib::Node* node){ return new MeshLib::Node((*node)[0], (*node)[1], (*node)[2]-z_offset); }); // starting with 2nd layer create prism or hex elements connecting the last layer with the current one - if (layer_id > 0) + if (layer_id == 0) + continue; + + node_offset -= nNodes; + const unsigned mat_id (nLayers - layer_id); + + for (unsigned i = 0; i < nOrgElems; ++i) { - node_offset -= nNodes; - const unsigned mat_id (nLayers - layer_id); + const MeshLib::Element* sfc_elem( elems[i] ); + if (sfc_elem->getDimension() < 2) // ignore line-elements + continue; + + const unsigned nElemNodes(sfc_elem->getNNodes()); + MeshLib::Node** e_nodes = new MeshLib::Node*[2*nElemNodes]; - for (unsigned i = 0; i < nOrgElems; ++i) + for (unsigned j=0; j<nElemNodes; ++j) { - const MeshLib::Element* sfc_elem( elems[i] ); - if (sfc_elem->getDimension() != 2) - { - WARN("MshLayerMapper::CreateLayers() - Method can only handle 2D mesh elements."); - WARN("Skipping Element %d of type \"%s\".", i, MeshElemType2String(sfc_elem->getGeomType()).c_str()); - continue; - } - - const unsigned nElemNodes(sfc_elem->getNNodes()); - MeshLib::Node** e_nodes = new MeshLib::Node*[2*nElemNodes]; - - for (unsigned j=0; j<nElemNodes; ++j) - { - const unsigned node_id = sfc_elem->getNode(j)->getID() + node_offset; - e_nodes[j] = new_nodes[node_id+nNodes]; - e_nodes[j+nElemNodes] = new_nodes[node_id]; - } - if (sfc_elem->getGeomType() == MeshElemType::TRIANGLE) // extrude triangles to prism - new_elems.push_back (new MeshLib::Prism(e_nodes, mat_id)); - else if (sfc_elem->getGeomType() == MeshElemType::QUAD) // extrude quads to hexes - new_elems.push_back (new MeshLib::Hex(e_nodes, mat_id)); + const unsigned node_id = sfc_elem->getNode(j)->getID() + node_offset; + e_nodes[j] = new_nodes[node_id+nNodes]; + e_nodes[j+nElemNodes] = new_nodes[node_id]; } + if (sfc_elem->getGeomType() == MeshElemType::TRIANGLE) // extrude triangles to prism + new_elems.push_back (new MeshLib::Prism(e_nodes, mat_id)); + else if (sfc_elem->getGeomType() == MeshElemType::QUAD) // extrude quads to hexes + new_elems.push_back (new MeshLib::Hex(e_nodes, mat_id)); } } - return new MeshLib::Mesh("SubsurfaceMesh", new_nodes, new_elems); + return new MeshLib::Mesh(mesh_name, new_nodes, new_elems); } -bool MeshLayerMapper::LayerMapping(MeshLib::Mesh &new_mesh, const std::string &rasterfile, - const unsigned nLayers, const unsigned layer_id, double noDataReplacementValue = 0.0) +bool MeshLayerMapper::createRasterLayers(MeshLib::Mesh const& mesh, std::vector<GeoLib::Raster const*> const& rasters, double noDataReplacementValue) { - const GeoLib::Raster *raster(GeoLib::Raster::getRasterFromASCFile(rasterfile)); + const std::size_t nLayers(rasters.size()); + if (nLayers < 1 || mesh.getDimension() != 2) + { + ERR("MeshLayerMapper::createRasterLayers(): A 2D mesh and at least two rasters required as input."); + return nullptr; + } + + MeshLib::Mesh* dem_mesh (new MeshLib::Mesh(mesh)); + if (layerMapping(*dem_mesh, *rasters.back(), 0)) + { + std::size_t const nNodes = mesh.getNNodes(); + _nodes.reserve(nLayers * nNodes); + + // number of triangles in the original mesh + std::size_t const nElems (std::count_if(mesh.getElements().begin(), mesh.getElements().end(), + [](MeshLib::Element const* elem) { return (elem->getGeomType() == MeshElemType::TRIANGLE);})); + _elements.reserve(nElems * (nLayers-1)); + + for (std::size_t i=0; i<nLayers; ++i) + addLayerToMesh(*dem_mesh, i, *rasters[i]); + + return true; + } + return false; +} + +void MeshLayerMapper::addLayerToMesh(const MeshLib::Mesh &dem_mesh, unsigned layer_id, GeoLib::Raster const& raster) +{ + std::size_t const nNodes = dem_mesh.getNNodes(); + std::vector<MeshLib::Node*> const& nodes = dem_mesh.getNodes(); + int const last_layer_node_offset = (layer_id-1) * nNodes; + double const no_data_value (raster.getNoDataValue()); + + // add nodes for new layer + for (std::size_t i=0; i<nNodes; ++i) + { + // min of dem elevation and layer elevation + double const elevation = std::min(raster.interpolateValueAtPoint(*nodes[i]), (*nodes[i])[2]); + + if ((layer_id > 0) && + ((std::abs(elevation - no_data_value) < std::numeric_limits<double>::epsilon()) || + (elevation - (*_nodes[last_layer_node_offset + i])[2] < std::numeric_limits<double>::epsilon()))) + _nodes.push_back(new MeshLib::Node((*nodes[i])[0], (*nodes[i])[1], elevation, _nodes[last_layer_node_offset +i]->getID())); + else + _nodes.push_back(new MeshLib::Node((*nodes[i])[0], (*nodes[i])[1], elevation, (layer_id * nNodes) + i)); + } + + if (layer_id == 0) + return; + + std::vector<MeshLib::Element*> const& elems = dem_mesh.getElements(); + std::size_t const nElems (dem_mesh.getNElements()); + + for (std::size_t i=0; i<nElems; ++i) + { + MeshLib::Element* elem (elems[i]); + if (elem->getGeomType() != MeshElemType::TRIANGLE) + continue; + unsigned node_counter(3), missing_idx(0); + std::array<MeshLib::Node*, 6> new_elem_nodes; + for (unsigned j=0; j<3; ++j) + { + new_elem_nodes[j] = _nodes[last_layer_node_offset + elem->getNodeIndex(j)]; + new_elem_nodes[node_counter] = (_nodes[last_layer_node_offset + elem->getNodeIndex(j) + nNodes]); + if (new_elem_nodes[j]->getID() != new_elem_nodes[node_counter]->getID()) + node_counter++; + else + missing_idx = j; + } + + switch (node_counter) + { + case 6: + _elements.push_back(new MeshLib::Prism(new_elem_nodes, layer_id)); + break; + case 5: + std::array<MeshLib::Node*, 5> pyramid_nodes; + pyramid_nodes[0] = new_elem_nodes[_pyramid_base[missing_idx][0]]; + pyramid_nodes[1] = new_elem_nodes[_pyramid_base[missing_idx][1]]; + pyramid_nodes[2] = new_elem_nodes[_pyramid_base[missing_idx][2]]; + pyramid_nodes[3] = new_elem_nodes[_pyramid_base[missing_idx][3]]; + pyramid_nodes[4] = new_elem_nodes[missing_idx]; + _elements.push_back(new MeshLib::Pyramid(pyramid_nodes, layer_id)); + break; + case 4: + std::array<MeshLib::Node*, 4> tet_nodes; + std::copy(new_elem_nodes.begin(), new_elem_nodes.begin() + node_counter, tet_nodes.begin()); + _elements.push_back(new MeshLib::Tet(tet_nodes, layer_id)); + break; + default: + continue; + } + } +} + +bool MeshLayerMapper::layerMapping(MeshLib::Mesh &new_mesh, std::string const& rasterfile, double noDataReplacementValue = 0.0) +{ + const GeoLib::Raster *raster(FileIO::AsciiRasterInterface::getRasterFromASCFile(rasterfile)); if (! raster) { - ERR("MshLayerMapper::LayerMapping - could not read raster file %s", rasterfile.c_str()); + ERR("MshLayerMapper::layerMapping - could not read raster file %s", rasterfile.c_str()); return false; } - const bool result = LayerMapping(new_mesh, *raster, nLayers, layer_id, noDataReplacementValue); + const bool result = layerMapping(new_mesh, *raster, noDataReplacementValue); delete raster; return result; } -bool MeshLayerMapper::LayerMapping(MeshLib::Mesh &new_mesh, const GeoLib::Raster &raster, - const unsigned nLayers, const unsigned layer_id, double noDataReplacementValue = 0.0) +bool MeshLayerMapper::layerMapping(MeshLib::Mesh &new_mesh, GeoLib::Raster const& raster, double noDataReplacementValue = 0.0) { - if (nLayers < layer_id) + if (new_mesh.getDimension() != 2) { - ERR("MshLayerMapper::LayerMapping() - Mesh has only %d Layers, cannot assign layer %d.", nLayers, layer_id); + ERR("MshLayerMapper::layerMapping - requires 2D mesh"); return false; } const double x0(raster.getOrigin()[0]); const double y0(raster.getOrigin()[1]); - const double delta(raster.getRasterPixelDistance()); + const double delta(raster.getRasterPixelSize()); const double no_data(raster.getNoDataValue()); - const std::size_t width(raster.getNCols()); - const std::size_t height(raster.getNRows()); - double const*const elevation(raster.begin()); - const std::pair<double, double> xDim(x0, x0 + width * delta); // extension in x-dimension - const std::pair<double, double> yDim(y0, y0 + height * delta); // extension in y-dimension + const std::pair<double, double> xDim(x0, x0 + raster.getNCols() * delta); // extension in x-dimension + const std::pair<double, double> yDim(y0, y0 + raster.getNRows() * delta); // extension in y-dimension const size_t nNodes (new_mesh.getNNodes()); - const size_t nNodesPerLayer (nNodes / (nLayers+1)); - - const size_t firstNode (layer_id * nNodesPerLayer); - const size_t lastNode (firstNode + nNodesPerLayer); - - const double half_delta = 0.5*delta; const std::vector<MeshLib::Node*> &nodes = new_mesh.getNodes(); - for (unsigned i = firstNode; i < lastNode; ++i) + for (unsigned i = 0; i < nNodes; ++i) { - const double* coords (nodes[i]->getCoords()); - - if (!isNodeOnRaster(*nodes[i], xDim, yDim)) + if (!raster.isPntOnRaster(*nodes[i])) { // use either default value or elevation from layer above - nodes[i]->updateCoordinates(coords[0], coords[1], noDataReplacementValue); + nodes[i]->updateCoordinates((*nodes[i])[0], (*nodes[i])[1], noDataReplacementValue); continue; } - // position in raster - const double xPos ((coords[0] - xDim.first) / delta); - const double yPos ((coords[1] - yDim.first) / delta); - // raster cell index - const size_t xIdx (static_cast<size_t>(floor(xPos))); - const size_t yIdx (static_cast<size_t>(floor(yPos))); - - // weights for bilinear interpolation - const double xShift = fabs(xPos-(xIdx+half_delta))/delta; - const double yShift = fabs(yPos-(yIdx+half_delta))/delta; - std::array<double,4> weight = {{ (1-xShift)*(1-xShift), xShift*(1-yShift), xShift*yShift, (1-xShift)*yShift }}; - - // neightbors to include in interpolation - const int xShiftIdx = (xPos-xIdx-half_delta>=0) ? 1 : -1; - const int yShiftIdx = (yPos-yIdx-half_delta>=0) ? 1 : -1; - const std::array<int,4> x_nb = {{ 0, xShiftIdx, xShiftIdx, 0 }}; - const std::array<int,4> y_nb = {{ 0, 0, yShiftIdx, yShiftIdx }}; - - // get pixel values - std::array<double,4> pix_val; - unsigned no_data_count (0); - for (unsigned j=0; j<4; ++j) - { - pix_val[j] = elevation[(yIdx + y_nb[j])*width + (xIdx + x_nb[j])]; - if (fabs(pix_val[j] - no_data) < std::numeric_limits<double>::epsilon()) - { - weight[j] = 0; - no_data_count++; - } - } - - // adjust weights if necessary - if (no_data_count > 0) - { - if (no_data_count == 4) // if there is absolutely no data just use the default value - { - nodes[i]->updateCoordinates(coords[0], coords[1], noDataReplacementValue); - continue; - } - const double norm = (double)(4)/(4-no_data_count); - std::for_each(weight.begin(), weight.end(), [&norm](double &val){val*=norm;}); - } - - // new value - double z = MathLib::scalarProduct<double,4>(weight.data(), pix_val.data()); - nodes[i]->updateCoordinates(coords[0], coords[1], z); + double elevation (raster.interpolateValueAtPoint(*nodes[i])); + if (std::abs(elevation - no_data) < std::numeric_limits<double>::epsilon()) + elevation = noDataReplacementValue; + nodes[i]->updateCoordinates((*nodes[i])[0], (*nodes[i])[1], elevation); } return true; } - -bool MeshLayerMapper::isNodeOnRaster(const MeshLib::Node &node, - const std::pair<double, double> &xDim, - const std::pair<double, double> &yDim) -{ - if (node[0] < xDim.first || node[0] > xDim.second || node[1] < yDim.first || node[1] > yDim.second) - return false; - - return true; -} - -MeshLib::Mesh* MeshLayerMapper::blendLayersWithSurface(MeshLib::Mesh &mesh, const unsigned nLayers, const std::string &dem_raster) -{ - // construct surface mesh from DEM - const MathLib::Vector3 dir(0,0,1); - MeshLib::Mesh* dem = MeshLib::MeshSurfaceExtraction::getMeshSurface(mesh, dir); - MeshLayerMapper::LayerMapping(*dem, dem_raster, 0, 0); - const std::vector<MeshLib::Node*> &dem_nodes (dem->getNodes()); - - const std::vector<MeshLib::Node*> &mdl_nodes (mesh.getNodes()); - const size_t nNodes (mesh.getNNodes()); - const size_t nNodesPerLayer = nNodes / (nLayers+1); - std::vector<bool> is_surface_node(nNodes, false); - std::vector<bool> nodes_below_surface(nNodes, false); - - // check if bottom layer nodes are below DEM - const unsigned bottom_firstNode = nLayers * nNodesPerLayer; - const unsigned bottom_lastNode = bottom_firstNode + nNodesPerLayer; - for (unsigned i = bottom_firstNode; i < bottom_lastNode; ++i) - { - nodes_below_surface[i]=true; - const double* coords = mdl_nodes[i]->getCoords(); - const double* dem_coords = dem_nodes[i-bottom_firstNode]->getCoords(); - if (coords[2] >= dem_coords[2]) - { - WARN("Node %d (in bottom-layer) is above surface node %d. (%f, %f)", i, (i-bottom_firstNode), coords[2], dem_coords[2]); - is_surface_node[i] = true; - } - } - - // for all other layers: - // if node < dem-node: do nothing - // if node > dem-node: - // if first node above surface: map to dem and mark as surface node - // else remove node (i.e. don't copy it) - for (int layer_id=nLayers-1; layer_id>=0; --layer_id) - { - const size_t firstNode = layer_id * nNodesPerLayer; - const size_t lastNode = firstNode + nNodesPerLayer; - - for(unsigned i = firstNode; i < lastNode; ++i) - { - if (is_surface_node[i+nNodesPerLayer]) - is_surface_node[i]=true; - else - { - nodes_below_surface[i]=true; - MeshLib::Node* node (mdl_nodes[i]); - const double* coords = node->getCoords(); - const double* dem_coords = dem_nodes[i-firstNode]->getCoords(); - if (coords[2] > dem_coords[2]) - { - node->updateCoordinates(dem_coords[0], dem_coords[1], dem_coords[2]); - is_surface_node[i] = true; - } - } - } - } - delete dem; // no longer needed - - // copy valid nodes to new node vector - std::vector<MeshLib::Node*> new_nodes; - std::vector<int> node_index_map(nNodes, -1); - size_t node_count(0); - for (unsigned j=0; j<nNodes; ++j) - if (nodes_below_surface[j]) - { - new_nodes.push_back(new MeshLib::Node(mdl_nodes[j]->getCoords(), mdl_nodes[j]->getID())); - node_index_map[j]=node_count++; - } - - // copy elements (old elements need to have at least 4 nodes remaining and form a 3d element - const std::vector<MeshLib::Element*> &mdl_elements (mesh.getElements()); - const size_t nElems (mesh.getNElements()); - std::vector<MeshLib::Element*> new_elements; - for (unsigned j=0; j<nElems; ++j) - { - const MeshLib::Element* elem = mdl_elements[j]; - - size_t count(0); - for (unsigned i=0; i<6; ++i) // check top surface of prism - if (nodes_below_surface[elem->getNode(i)->getID()]) ++count; - - if (count==6) // copy prism elements if all six nodes are valid - { - MeshLib::Node** e_nodes = new MeshLib::Node*[count]; - for (unsigned i=0; i<6; ++i) - e_nodes[i] = new_nodes[node_index_map[elem->getNode(i)->getID()]]; - - MeshLib::Element* prism (new MeshLib::Prism(e_nodes, elem->getValue())); - new_elements.push_back(prism); - } - else if (count==5) // change the current element to two tetrahedra if only five nodes are valid - { - MeshLib::Node** e_nodes = new MeshLib::Node*[count]; - unsigned top_idx(6); - for (unsigned i=3; i<6; ++i) // find node that has been cut - if (!nodes_below_surface[elem->getNode(i)->getID()]) - top_idx = i-3; - - // construct pyramid element based on missing node - unsigned idx1 ((top_idx+1)%3); - unsigned idx2 ((top_idx+2)%3); - e_nodes[0] = new_nodes[node_index_map[elem->getNode(idx1)->getID()]]; - e_nodes[1] = new_nodes[node_index_map[elem->getNode(idx1+3)->getID()]]; - e_nodes[2] = new_nodes[node_index_map[elem->getNode(idx2+3)->getID()]]; - e_nodes[3] = new_nodes[node_index_map[elem->getNode(idx2)->getID()]]; - e_nodes[4] = new_nodes[node_index_map[elem->getNode(top_idx)->getID()]]; - - MeshLib::Element* pyr (new MeshLib::Pyramid(e_nodes, elem->getValue())); - new_elements.push_back(pyr); - } - else if (count==4) // change the current element to a tetrahedron if only four nodes are valid - { - MeshLib::Node** e_nodes = new MeshLib::Node*[count]; - for (unsigned i=0; i<3; ++i) // first three nodes are the bottom-face - { - unsigned idx (elem->getNode(i)->getID()); - if (nodes_below_surface[idx]) - e_nodes[i] = new_nodes[node_index_map[idx]]; - else - e_nodes[i] = NULL; - } - - if (e_nodes[0] && e_nodes[1] && e_nodes[2]) //make sure that the 4 remaining nodes don't form a quad - { - for (unsigned i=3; i<6; ++i) // last node - { - unsigned idx (elem->getNode(i)->getID()); - if (nodes_below_surface[idx]) - { - e_nodes[3] = new_nodes[node_index_map[idx]]; - break; - } - } - - MeshLib::Element* tet (new MeshLib::Tet(e_nodes, elem->getValue())); - new_elements.push_back(tet); - } - else delete e_nodes; - } - // else remove element, if less than four nodes are valid - } - return new MeshLib::Mesh("SubsurfaceMesh", new_nodes, new_elements); -} - - - diff --git a/MeshLib/MeshGenerators/MeshLayerMapper.h b/MeshLib/MeshGenerators/MeshLayerMapper.h index 33853f4045f3535b4dfa57fd02b7a0c4d6c970f7..cb17e764adb665f6439d3bc6a7a409b9c0775865 100644 --- a/MeshLib/MeshGenerators/MeshLayerMapper.h +++ b/MeshLib/MeshGenerators/MeshLayerMapper.h @@ -15,61 +15,55 @@ #ifndef MESHLAYERMAPPER_H #define MESHLAYERMAPPER_H -#include <string> -#include "GeoLib/Raster.h" - -class QImage; - -namespace MeshLib { - class Mesh; - class Node; -} +#include "LayeredMeshGenerator.h" /** - * \brief Manipulating and adding layers to an existing mesh + * \brief Manipulating and adding prism element layers to an existing 2D mesh */ -class MeshLayerMapper +class MeshLayerMapper : public LayeredMeshGenerator { public: - MeshLayerMapper() {} - ~MeshLayerMapper() {} + MeshLayerMapper() {} + ~MeshLayerMapper() {} - /** - * Based on a triangle-or quad mesh this method creates a 3D mesh with with a given number of prism- or hex-layers - * \param mesh The triangle/quad mesh that is the basis for the new prism/hex mesh - * \param nLayers The number of layers of prism/hex elements that will be extruded from the triangle/quad elements of the original mesh - * \param thickness The thickness of each of these newly added layers - * \return A mesh with the requested number of layers of prism/hex elements - */ - static MeshLib::Mesh* CreateLayers(const MeshLib::Mesh &mesh, const std::vector<float> &layer_thickness_vector); + /** + * Based on a 2D triangle-or quad mesh this method creates a 3D mesh with a given number of prism- or hex-layers + * \param mesh The triangle/quad mesh that is the basis for the new prism/hex mesh + * \param nLayers The number of layers of prism/hex elements that will be extruded from the triangle/quad elements of the original mesh + * \param thickness The thickness of each of these newly added layers + * \param mesh_name The name of the newly created mesh + * \return A mesh with the requested number of layers of prism/hex elements + */ + MeshLib::Mesh* createStaticLayers(MeshLib::Mesh const& mesh, std::vector<float> const& layer_thickness_vector, std::string const& mesh_name = "SubsurfaceMesh") const; - /** - * Maps the z-values of nodes in the designated layer of the given mesh according to the given raster. - * Note: This only results in a valid mesh if the layers don't intersect each other. - */ - static bool LayerMapping(MeshLib::Mesh &mesh, const std::string &rasterfile, - const unsigned nLayers, const unsigned layer_id, double noDataReplacementValue); + /** + * Based on a 2D triangle mesh this method creates a 3D mesh with a given number of prism-layers. + * Note: While this method would technically also work with quad meshes, this is discouraged as quad elements will most likely not + * be coplanar after the mapping process which result in invaled mesh elements. + * \param mesh The 2D triangle mesh that is the basis for the new 3D prism mesh + * \param rasters Containing all the raster-data for the subsurface layers from bottom to top (starting with the bottom of the oldest layer and ending with the DEM) + * \param noDataReplacementValue Default z-coordinate if there are mesh nodes not located on the DEM raster (i.e. raster_paths[0]) + * \return A mesh with the requested number of layers of prism elements (also including Tet- & Pyramid-elements in case of degenerated prisms) + */ + bool createRasterLayers(MeshLib::Mesh const& mesh, std::vector<GeoLib::Raster const*> const& rasters, double noDataReplacementValue = 0.0); - /** - * Maps the z-values of nodes in the designated layer of the given mesh according to the given raster. - * Note: This only results in a valid mesh if the layers don't intersect each other. - */ - static bool LayerMapping(MeshLib::Mesh &mesh, const GeoLib::Raster &raster, - const unsigned nLayers, const unsigned layer_id, double noDataReplacementValue); + /** + * Maps the elevation of nodes of a given 2D mesh according to the raster specified by the file path. + * At locations wher no information is given, node elevation is set to noDataReplacementValue. + */ + static bool layerMapping(MeshLib::Mesh &mesh, const std::string &rasterfile, double noDataReplacementValue); - /** - * Blends a mesh with the surface given by dem_raster. Nodes and elements above the surface are either removed or adapted to fit the surface. - * Note: It is unlikely but possible that the new nodes vector contains (very few) nodes that are not part of any element. This problem is - * remedied at the end of method upon creating the actual mesh from the new node- and element-vector as the mesh-constructor checks for such - * nodes and removes them. This note is just to call this issue to attention in case this methods is changed. - */ - static MeshLib::Mesh* blendLayersWithSurface(MeshLib::Mesh &mesh, const unsigned nLayers, const std::string &dem_raster); + /** + * Maps the elevation of nodes of a given 2D mesh according to the raster. At locations wher no + * information is given, node elevation is set to noDataReplacementValue. + */ + static bool layerMapping(MeshLib::Mesh &mesh, const GeoLib::Raster &raster, double noDataReplacementValue); private: - /// Checks if the given mesh is within the dimensions given by xDim and yDim. - static bool isNodeOnRaster(const MeshLib::Node &node, - const std::pair<double, double> &xDim, - const std::pair<double, double> &yDim); + /// Adds another layer to a subsurface mesh + void addLayerToMesh(const MeshLib::Mesh &mesh_layer, unsigned layer_id, GeoLib::Raster const& raster); + + static const unsigned _pyramid_base[3][4]; }; #endif //MESHLAYERMAPPER_H diff --git a/MeshLib/Node.h b/MeshLib/Node.h index ae0a21165a679a906c5e2e8f06f8757d5f13bc19..557d26bbdccc7614eebfc80fd28b0e4243c5829f 100644 --- a/MeshLib/Node.h +++ b/MeshLib/Node.h @@ -36,9 +36,7 @@ class Element; class Node : public GeoLib::PointWithID { /* friend functions: */ - friend bool MeshLayerMapper::LayerMapping(MeshLib::Mesh &mesh, const GeoLib::Raster &raster, const unsigned nLayers, - const unsigned layer_id, double noDataReplacementValue); - friend MeshLib::Mesh* MeshLayerMapper::blendLayersWithSurface(MeshLib::Mesh &mesh, const unsigned nLayers, const std::string &dem_raster); + friend bool MeshLayerMapper::layerMapping(MeshLib::Mesh &mesh, const GeoLib::Raster &raster, double noDataReplacementValue); friend MeshLib::Mesh* MeshSurfaceExtraction::getMeshSurface(const MeshLib::Mesh &mesh, const MathLib::Vector3 &dir, bool keep3dMeshIds); /* friend classes: */ diff --git a/Utils/SimpleMeshCreation/createMeshElemPropertiesFromASCRaster.cpp b/Utils/SimpleMeshCreation/createMeshElemPropertiesFromASCRaster.cpp index 557d506b6cb6ee5b86ac08d1e07174c036db6412..22b24ad0e1cf50a74eed133f236fe9d5937a9ea6 100644 --- a/Utils/SimpleMeshCreation/createMeshElemPropertiesFromASCRaster.cpp +++ b/Utils/SimpleMeshCreation/createMeshElemPropertiesFromASCRaster.cpp @@ -29,6 +29,7 @@ #include "FileTools.h" #include "MeshIO.h" #include "readMeshFromFile.h" +#include "AsciiRasterInterface.h" // GeoLib #include "Raster.h" @@ -122,9 +123,7 @@ int main (int argc, char* argv[]) raster_arg.getValue())); new_raster_fname += "-" + std::to_string(raster->getNRows()) + "x" + std::to_string(raster->getNCols()) + ".asc"; - std::ofstream out(new_raster_fname); - raster->writeRasterAsASC(out); - out.close(); + FileIO::AsciiRasterInterface::writeRasterAsASC(raster, new_raster_fname); } }