OGS
MeshLayerEditDialog.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
5
6#include <QCheckBox>
7#include <QDoubleValidator>
8#include <QElapsedTimer>
9#include <QFileDialog>
10#include <QFileInfo>
11#include <QGridLayout>
12#include <QGroupBox>
13#include <QIntValidator>
14#include <QLineEdit>
15#include <QPushButton>
16#include <QRadioButton>
17#include <QSettings>
18#include <QVBoxLayout>
19
21#include "Base/OGSError.h"
22#include "BaseLib/Logging.h"
23#include "BaseLib/StringTools.h"
25#include "MeshLib/Mesh.h"
27
29 QDialog* parent)
30 : QDialog(parent),
31 _msh(mesh),
32 _n_layers(0),
33 _layerEdit(new QLineEdit("1", this)),
35 _minThicknessEdit(nullptr),
36 _nextButton(new QPushButton("Next", this)),
37 _layerBox(nullptr),
38 _radioButtonBox(nullptr),
39 _ogsMeshButton(nullptr),
40 _layerSelectionLayout(new QGridLayout(_layerBox)),
41 _use_rasters(true)
42{
43 setupUi(this);
44
45 this->gridLayoutLayerMapping->addWidget(
46 new QLabel("Please specify the number of layers to add:", this), 0, 0);
47 this->gridLayoutLayerMapping->addWidget(_layerEdit, 0, 1);
48 this->gridLayoutLayerMapping->addWidget(_nextButton, 0, 2);
49 _layerEdit->setValidator(new QIntValidator(1, 999, _layerEdit));
50 connect(_nextButton, SIGNAL(pressed()), this, SLOT(nextButtonPressed()));
51
52 // configure group box + layout
53 this->_layerSelectionLayout->setMargin(10);
54 this->_layerSelectionLayout->setColumnMinimumWidth(2, 10);
55 this->_layerSelectionLayout->setColumnStretch(0, 80);
56 this->_layerSelectionLayout->setColumnStretch(1, 200);
57 this->_layerSelectionLayout->setColumnStretch(2, 10);
58}
59
61
63{
64 _n_layers = static_cast<unsigned>(_layerEdit->text().toInt());
65
66 if (_n_layers < 1)
67 {
68 OGSError::box("Add the number of layers to add (at least 1)");
69 return;
70 }
71
72 _layerEdit->setEnabled(false);
73 _nextButton->setEnabled(false);
74
75 auto* _radiobuttonLayout(new QVBoxLayout(_radioButtonBox));
76 QRadioButton* selectButton1(
77 new QRadioButton("Add layers based on raster files", _radioButtonBox));
78 QRadioButton* selectButton2(
79 new QRadioButton("Add layers with static thickness", _radioButtonBox));
80 _radioButtonBox = new QGroupBox(this);
81 _radiobuttonLayout->addWidget(selectButton1);
82 _radiobuttonLayout->addWidget(selectButton2);
83 _radioButtonBox->setLayout(_radiobuttonLayout);
84 gridLayoutLayerMapping->addWidget(_radioButtonBox, 2, 0, 1, 3);
85 connect(selectButton1, SIGNAL(pressed()), this, SLOT(createWithRasters()));
86 connect(selectButton2, SIGNAL(pressed()), this, SLOT(createStatic()));
87}
88
90{
91 this->_use_rasters = true;
92 this->_radioButtonBox->setEnabled(false);
93 const QString selectText =
94 (_n_layers > 0) ? "Please specify a raster file for mapping each layer:"
95 : "Please specify raster file for surface mapping:";
96 this->_layerBox = new QGroupBox(this);
97 this->_layerBox->setTitle(selectText);
98
99 for (unsigned i = 0; i <= _n_layers; ++i)
100 {
101 QString text("");
102 if (i == 0)
103 {
104 text = "Surface";
105 }
106 else if (i == _n_layers)
107 {
108 text = "Layer" + QString::number(_n_layers) + "-Bottom";
109 }
110 else
111 {
112 text = "Layer" + QString::number(i + 1) + "-Top";
113 }
114 auto* edit(new QLineEdit(this));
115 QPushButton* button(new QPushButton("...", _layerBox));
116
117 this->_edits.push_back(edit);
118 this->_fileButtonMap.insert(button, edit);
119 connect(button, SIGNAL(clicked()), this, SLOT(getFileName()));
120
121 this->_layerSelectionLayout->addWidget(new QLabel(text, _layerBox), i,
122 0);
123 this->_layerSelectionLayout->addWidget(_edits[i], i, 1);
124 this->_layerSelectionLayout->addWidget(button, i, 2);
125 }
126 this->_layerBox->setLayout(this->_layerSelectionLayout);
127 this->gridLayoutLayerMapping->addWidget(_layerBox, 4, 0, 1, 3);
128 if (this->_n_layers > 0)
129 {
131 }
132}
133
135{
136 this->_use_rasters = false;
137 this->_radioButtonBox->setEnabled(false);
138 this->_layerBox = new QGroupBox(this);
139 this->_layerBox->setTitle("Please specify a thickness or each layer");
140
141 for (unsigned i = 0; i < this->_n_layers; ++i)
142 {
143 QString text("Layer" + QString::number(i) + "-Thickness");
144 QLineEdit* staticLayerEdit = new QLineEdit("10", this);
145 staticLayerEdit->setValidator(new QDoubleValidator(staticLayerEdit));
146 _edits.push_back(staticLayerEdit);
147 this->_layerSelectionLayout->addWidget(new QLabel(text, _layerBox), i,
148 0);
149 this->_layerSelectionLayout->addWidget(_edits[i], i, 1);
150 }
151 this->_layerBox->setLayout(this->_layerSelectionLayout);
152 this->gridLayoutLayerMapping->addWidget(_layerBox, 4, 0, 1, 3);
154}
155
157{
158 auto* meshToolSelectionBox(new QGroupBox(this));
159 meshToolSelectionBox->setTitle("Output element type");
160 auto* meshToolSelectionLayout(new QGridLayout(meshToolSelectionBox));
161 _ogsMeshButton = new QRadioButton("Prisms", meshToolSelectionBox);
162 QRadioButton* tetgenMeshButton =
163 new QRadioButton("Tetrahedra", meshToolSelectionBox);
164 tetgenMeshButton->setFixedWidth(150);
165 auto* minThicknessLabel = new QLabel(meshToolSelectionBox);
166 minThicknessLabel->setText("Minimum thickness of layers:");
167 _minThicknessEdit = new QLineEdit(meshToolSelectionBox);
168 _minThicknessEdit->setText("1.0");
169 auto* min_thickness_validator =
170 new QDoubleValidator(0, 1000000, 15, _minThicknessEdit);
171 _minThicknessEdit->setValidator(min_thickness_validator);
172 _minThicknessEdit->setMaximumWidth(100);
173 _minThicknessEdit->setFixedWidth(100);
174 meshToolSelectionLayout->addWidget(_ogsMeshButton, 0, 0);
175 meshToolSelectionLayout->addWidget(tetgenMeshButton, 0, 1);
176 meshToolSelectionLayout->addWidget(minThicknessLabel, 1, 0);
177 meshToolSelectionLayout->addWidget(_minThicknessEdit, 1, 1);
178 meshToolSelectionBox->setLayout(meshToolSelectionLayout);
179 _ogsMeshButton->setChecked(true);
180
181 gridLayoutLayerMapping->addWidget(meshToolSelectionBox, 5, 0, 1, 3);
182}
183
185{
186 const unsigned nLayers = _layerEdit->text().toInt();
187
189
190 QElapsedTimer myTimer0;
191 myTimer0.start();
192 if (_use_rasters)
193 {
194 float minimum_thickness(_minThicknessEdit->text().toFloat());
195 if (minimum_thickness <= 0)
196 {
197 minimum_thickness = std::numeric_limits<float>::epsilon();
198 }
199 std::vector<std::string> raster_paths;
200 for (int i = nLayers; i >= 0; --i)
201 {
202 raster_paths.push_back(this->_edits[i]->text().toStdString());
203 }
204
205 auto const rasters = FileIO::readRasters(raster_paths);
206 if (rasters && mapper.createLayers(*_msh, *rasters, minimum_thickness))
207 {
208 INFO("Mesh construction time: {:d} ms.", myTimer0.elapsed());
209 return mapper.getMesh("SubsurfaceMesh").release();
210 }
211 return nullptr;
212 }
213
214 std::vector<float> layer_thickness;
215 for (unsigned i = 0; i < nLayers; ++i)
216 {
217 layer_thickness.push_back(this->_edits[i]->text().toFloat());
218 }
219 INFO("Mesh construction time: {:d} ms.", myTimer0.elapsed());
221 layer_thickness);
222}
223
225{
226 QSettings settings;
227 QString filename = QFileDialog::getSaveFileName(
228 this, "Write TetGen input file to",
229 settings.value("lastOpenedTetgenFileDirectory").toString(),
230 "TetGen Geometry (*.smesh)");
231 if (filename.isEmpty())
232 {
233 return nullptr;
234 }
235
236 const unsigned nLayers = _layerEdit->text().toInt();
237 MeshLib::Mesh* tg_mesh(nullptr);
238 QElapsedTimer myTimer0;
239 myTimer0.start();
240
241 if (_use_rasters)
242 {
243 float minimum_thickness(_minThicknessEdit->text().toFloat());
244 if (minimum_thickness <= 0)
245 {
246 minimum_thickness = std::numeric_limits<float>::epsilon();
247 }
248 std::vector<std::string> raster_paths;
249 for (int i = nLayers; i >= 0; --i)
250 {
251 raster_paths.push_back(this->_edits[i]->text().toStdString());
252 }
253 LayeredVolume lv;
254
255 auto const rasters = FileIO::readRasters(raster_paths);
256 if (rasters && lv.createLayers(*_msh, *rasters, minimum_thickness))
257 {
258 tg_mesh = lv.getMesh("SubsurfaceMesh").release();
259 }
260
261 if (tg_mesh)
262 {
263 std::vector<MeshLib::Node> tg_attr(lv.getAttributePoints());
264 FileIO::TetGenInterface tetgen_interface;
265 tetgen_interface.writeTetGenSmesh(filename.toStdString(), *tg_mesh,
266 tg_attr);
267 }
268 }
269 else
270 {
271 std::vector<float> layer_thickness;
272 for (unsigned i = 0; i < nLayers; ++i)
273 {
274 layer_thickness.push_back(this->_edits[i]->text().toFloat());
275 }
277 *_msh, layer_thickness);
278 std::vector<MeshLib::Node> tg_attr;
279 FileIO::TetGenInterface tetgen_interface;
280 tetgen_interface.writeTetGenSmesh(filename.toStdString(), *tg_mesh,
281 tg_attr);
282 }
283 INFO("Mesh construction time: {:d} ms.", myTimer0.elapsed());
284
285 return tg_mesh;
286}
287
289{
290 if (this->_edits.isEmpty())
291 {
293 "Please specify the number and\n type of layers and press 'Next'");
294 return;
295 }
296
297 bool all_paths_set(true);
298 if (_n_layers == 0)
299 {
300 if (_edits[0]->text().isEmpty())
301 {
302 all_paths_set = false;
303 }
304 }
305 else
306 {
307 int start_idx = (_use_rasters) ? 1 : 0;
308 for (int i = start_idx; i < _edits.size(); ++i)
309 {
310 if (_edits[i]->text().isEmpty())
311 {
312 all_paths_set = false;
313 }
314 }
315 }
316
317 if (!all_paths_set)
318 {
319 OGSError::box("Please specify raster files for all layers.");
320 return;
321 }
322
323 MeshLib::Mesh* new_mesh(nullptr);
324 if (_ogsMeshButton->isChecked())
325 {
326 new_mesh = createPrismMesh();
327 }
328 else
329 {
330 new_mesh = createTetMesh();
331 }
332
333 if (new_mesh)
334 {
335 emit mshEditFinished(new_mesh);
336 }
337 else
338 {
339 OGSError::box("Error creating mesh");
340 }
341
342 this->done(QDialog::Accepted);
343}
344
346{
347 this->done(QDialog::Rejected);
348}
349
351{
352 auto* button = dynamic_cast<QPushButton*>(this->sender());
353 QSettings settings;
354 QString filename = QFileDialog::getOpenFileName(
355 this, "Select raster file to open",
356 settings.value("lastOpenedRasterFileDirectory").toString(),
357 "ASCII raster files (*.asc *.grd *.xyz);;All files (* *.*)");
358 _fileButtonMap[button]->setText(filename);
359 QFileInfo fi(filename);
360 settings.setValue("lastOpenedRasterFileDirectory", fi.absolutePath());
361}
void INFO(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:28
static bool writeTetGenSmesh(const std::string &file_name, const GeoLib::GEOObjects &geo_objects, const std::string &geo_name, const std::vector< GeoLib::Point > &attribute_points)
virtual bool createLayers(MeshLib::Mesh const &mesh, std::vector< GeoLib::Raster const * > const &rasters, double minimum_thickness, double noDataReplacementValue=0.0) final
std::unique_ptr< MeshLib::Mesh > getMesh(std::string const &mesh_name) const
Returns a mesh of the subsurface representation.
Creates a volume geometry from 2D mesh layers based on raster data.
std::vector< MeshLib::Node > getAttributePoints() const
MeshLayerEditDialog(const MeshLib::Mesh *mesh, QDialog *parent=nullptr)
~MeshLayerEditDialog() override
void mshEditFinished(MeshLib::Mesh *)
QRadioButton * _ogsMeshButton
MeshLib::Mesh * createPrismMesh()
QMap< QPushButton *, QLineEdit * > _fileButtonMap
void reject() override
Instructions if the Cancel-Button has been pressed.
MeshLib::Mesh * createTetMesh()
QGridLayout * _layerSelectionLayout
QVector< QLineEdit * > _edits
void accept() override
Instructions if the OK-Button has been pressed.
const MeshLib::Mesh * _msh
QLineEdit * _noDataReplacementEdit
Manipulating and adding prism element layers to an existing 2D mesh.
static MeshLib::Mesh * createStaticLayers(MeshLib::Mesh const &mesh, std::vector< float > const &layer_thickness_vector, std::string const &mesh_name="SubsurfaceMesh")
static void box(const QString &e)
Definition OGSError.cpp:13
std::optional< std::vector< GeoLib::Raster const * > > readRasters(std::vector< std::string > const &raster_paths)