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