From 8b2690477f6471efa0eb217adfb6a6f1640938b5 Mon Sep 17 00:00:00 2001
From: rinkk <karsten.rink@ufz.de>
Date: Thu, 14 Feb 2019 18:54:25 +0100
Subject: [PATCH] implemented mesh rasterisation

---
 .../Utils/FileConverter/CMakeLists.txt        |   5 +
 .../Utils/FileConverter/MeshToRaster.cpp      | 209 ++++++++++++++++++
 2 files changed, 214 insertions(+)
 create mode 100644 Applications/Utils/FileConverter/MeshToRaster.cpp

diff --git a/Applications/Utils/FileConverter/CMakeLists.txt b/Applications/Utils/FileConverter/CMakeLists.txt
index 8db49249a1b..89ea4052bf8 100644
--- a/Applications/Utils/FileConverter/CMakeLists.txt
+++ b/Applications/Utils/FileConverter/CMakeLists.txt
@@ -52,6 +52,11 @@ target_link_libraries(GocadSGridReader
     ${Boost_LIBRARIES}
 )
 
+add_executable(Mesh2Raster MeshToRaster.cpp)
+set_target_properties(Mesh2Raster PROPERTIES FOLDER Utilities)
+target_link_libraries(Mesh2Raster MeshLib)
+
+
 ####################
 ### Installation ###
 ####################
diff --git a/Applications/Utils/FileConverter/MeshToRaster.cpp b/Applications/Utils/FileConverter/MeshToRaster.cpp
new file mode 100644
index 00000000000..9e0e7734309
--- /dev/null
+++ b/Applications/Utils/FileConverter/MeshToRaster.cpp
@@ -0,0 +1,209 @@
+/**
+ * \file
+ *
+ * \copyright
+ * Copyright (c) 2012-2019, OpenGeoSys Community (http://www.opengeosys.org)
+ *            Distributed under a Modified BSD License.
+ *              See accompanying file LICENSE.txt or
+ *              http://www.opengeosys.org/LICENSE.txt
+ */
+
+#include <memory>
+#include <string>
+
+#include <tclap/CmdLine.h>
+
+#include <Applications/ApplicationsLib/LogogSetup.h>
+
+#include "BaseLib/BuildInfo.h"
+#include "GeoLib/AABB.h"
+#include "GeoLib/AnalyticalGeometry.h"
+#include "MeshLib/Elements/Quad.h"
+#include "MeshLib/Elements/Tri.h"
+#include "MeshLib/IO/readMeshFromFile.h"
+#include "MeshLib/Mesh.h"
+#include "MeshLib/MeshSearch/MeshElementGrid.h"
+#include "MeshLib/Node.h"
+
+/// Returns the index of the element the given node is located in when projected
+/// onto a mesh.
+std::size_t getProjectedElementIndex(
+    std::vector<const MeshLib::Element*> const& elems,
+    MeshLib::Node const& node)
+{
+    std::size_t const n_elems = elems.size();
+    for (std::size_t i = 0; i < n_elems; ++i)
+    {
+        if (elems[i]->getGeomType() == MeshLib::MeshElemType::LINE)
+            continue;
+        MeshLib::Node const* const a = elems[i]->getNode(0);
+        MeshLib::Node const* const b = elems[i]->getNode(1);
+        if (GeoLib::getOrientationFast(node, *a, *b) == GeoLib::Orientation::CW)
+            continue;
+        MeshLib::Node const* const c = elems[i]->getNode(2);
+        if (GeoLib::getOrientationFast(node, *b, *c) == GeoLib::Orientation::CW)
+            continue;
+        if (elems[i]->getGeomType() == MeshLib::MeshElemType::TRIANGLE)
+        {
+            if (GeoLib::getOrientationFast(node, *c, *a) ==
+                GeoLib::Orientation::CW)
+                continue;
+        }
+        if (elems[i]->getGeomType() == MeshLib::MeshElemType::QUAD)
+        {
+            MeshLib::Node const* const d = elems[i]->getNode(3);
+            if (GeoLib::getOrientationFast(node, *c, *d) ==
+                GeoLib::Orientation::CW)
+                continue;
+            if (GeoLib::getOrientationFast(node, *d, *a) ==
+                GeoLib::Orientation::CW)
+                continue;
+        }
+        return elems[i]->getID();
+    }
+    return std::numeric_limits<size_t>::max();
+}
+
+/// Returns the z-coordinate of a point projected onto the plane defined by a
+/// mesh element.
+double getElevation(MeshLib::Element const& elem, MeshLib::Node const& node)
+{
+    MathLib::Vector3 const n =
+        MeshLib::FaceRule::getSurfaceNormal(&elem).getNormalizedVector();
+    MeshLib::Node const& orig = *elem.getNode(0);
+    MathLib::Point3d const v{
+        {node[0] - orig[0], node[1] - orig[1], node[2] - orig[2]}};
+    double const dist = n[0] * v[0] + n[1] * v[1] + n[2] * v[2];
+    return node[2] - dist * n[2];
+}
+
+int main(int argc, char* argv[])
+{
+    ApplicationsLib::LogogSetup logog_setup;
+
+    TCLAP::CmdLine cmd(
+        "Mesh to raster converter.\n"
+        "Rasterises a 2D mesh, pixel values are set to the elevation of a "
+        "regular grid superimposed on the mesh. If no mesh element is located "
+        "beneath  a pixel it is set to NODATA.\n\n"
+        "OpenGeoSys-6 software, version " +
+            BaseLib::BuildInfo::git_describe +
+            ".\n"
+            "Copyright (c) 2012-2019, OpenGeoSys Community "
+            "(http://www.opengeosys.org)",
+        ' ', BaseLib::BuildInfo::git_describe);
+    TCLAP::ValueArg<std::string> input_arg("i", "input-file",
+                                           "Mesh input file (*.vtu, *.msh)",
+                                           true, "", "string");
+    cmd.add(input_arg);
+    TCLAP::ValueArg<std::string> output_arg(
+        "o", "output-file", "Raster output file (*.asc)", true, "", "string");
+    cmd.add(output_arg);
+    TCLAP::ValueArg<double> cell_arg("c", "cellsize",
+                                     "edge length of raster cells in result",
+                                     false, 1, "real");
+    cmd.add(cell_arg);
+
+    cmd.parse(argc, argv);
+
+    INFO("Rasterising mesh...");
+    std::unique_ptr<MeshLib::Mesh> const mesh(
+        MeshLib::IO::readMeshFromFile(input_arg.getValue()));
+    if (mesh == nullptr)
+    {
+        ERR("Error reading mesh file.");
+        return 1;
+    }
+    if (mesh->getDimension() != 2)
+    {
+        ERR("The programme requires a mesh containing two-dimensional elements "
+            "(i.e. triangles or quadrilaterals.");
+        return 2;
+    }
+
+    double const cellsize =
+        (cell_arg.isSet()) ? cell_arg.getValue() : mesh->getMinEdgeLength();
+    INFO("Cellsize set to %f", cellsize);
+
+    std::vector<MeshLib::Node*> const& nodes_vec(mesh->getNodes());
+    GeoLib::AABB const bounding_box(nodes_vec.begin(), nodes_vec.end());
+    MathLib::Point3d const& min(bounding_box.getMinPoint());
+    MathLib::Point3d const& max(bounding_box.getMaxPoint());
+    std::size_t const n_cols =
+        static_cast<std::size_t>(std::ceil((max[0] - min[0]) / cellsize));
+    std::size_t const n_rows =
+        static_cast<std::size_t>(std::ceil((max[1] - min[1]) / cellsize));
+
+    // raster header
+    std::ofstream out(output_arg.getValue());
+    out << "ncols         " << n_cols + 1 << "\n";
+    out << "nrows         " << n_rows + 1 << "\n";
+    out << std::fixed << "xllcorner     " << (min[0] - cellsize / 2.0) << "\n";
+    out << std::fixed << "yllcorner     " << (min[1] - cellsize / 2.0) << "\n";
+    out << std::fixed << "cellsize      " << cellsize << "\n";
+    out << "NODATA_value  "
+        << "-9999\n";
+    INFO("Writing raster with %d x %d pixels.", n_cols, n_rows);
+
+    MeshLib::MeshElementGrid const grid(*mesh);
+    double const max_edge(mesh->getMaxEdgeLength() + cellsize);
+
+    // pixel values
+    double x = min[0];
+    double y = max[1];
+    double const half_cell = cellsize / 2.0;
+    for (std::size_t j = 0; j <= n_rows; ++j)
+    {
+        for (std::size_t i = 0; i <= n_cols; ++i)
+        {
+            MeshLib::Node const node(x, y, 0);
+            MathLib::Point3d min_vol{{x - max_edge, y - max_edge,
+                                      -std::numeric_limits<double>::max()}};
+            MathLib::Point3d max_vol{{x + max_edge, y + max_edge,
+                                      std::numeric_limits<double>::max()}};
+            std::vector<const MeshLib::Element*> const elems =
+                grid.getElementsInVolume(min_vol, max_vol);
+            std::size_t const elem_idx = getProjectedElementIndex(elems, node);
+            // centre of the pixel is located within a mesh element
+            if (elem_idx != std::numeric_limits<std::size_t>::max())
+                out << getElevation(*(mesh->getElement(elem_idx)), node) << " ";
+            // centre of the pixel isn't located within a mesh element
+            else
+            {
+                std::array<double, 4> const x_off{
+                    {-half_cell, half_cell, -half_cell, half_cell}};
+                std::array<double, 4> const y_off{
+                    {-half_cell, -half_cell, half_cell, half_cell}};
+                double sum(0);
+                std::size_t nonzero_count(0);
+                // test for all of the pixel's corner if any are within an
+                // element
+                for (std::size_t i = 0; i < 4; ++i)
+                {
+                    MeshLib::Node const node(x + x_off[i], y + y_off[i], 0);
+                    std::size_t const corner_idx =
+                        getProjectedElementIndex(elems, node);
+                    if (corner_idx != std::numeric_limits<std::size_t>::max())
+                    {
+                        sum +=
+                            getElevation(*(mesh->getElement(corner_idx)), node);
+                        nonzero_count++;
+                    }
+                }
+                // calculate pixel value as average of corner values
+                if (nonzero_count > 0)
+                    out << sum / nonzero_count << " ";
+                // if none of the corners give a value, set pixel to NODATA
+                else
+                    out << "-9999 ";
+            }
+            x += cellsize;
+        }
+        x = min[0];
+        y -= cellsize;
+        out << "\n";
+    }
+    out.close();
+    INFO("Result written to %s", output_arg.getValue().c_str());
+    return 0;
+}
-- 
GitLab