OGS
BoostXmlGmlInterface.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 <boost/property_tree/xml_parser.hpp>
7
8#include "BaseLib/Algorithm.h"
10#include "BaseLib/Logging.h"
11#include "GeoLib/GEOObjects.h"
12#include "GeoLib/Point.h"
13#include "GeoLib/PointVec.h"
14#include "GeoLib/Polyline.h"
15#include "GeoLib/Surface.h"
16#include "GeoLib/Triangle.h"
17
18namespace GeoLib
19{
20namespace IO
21{
26
27bool BoostXmlGmlInterface::readFile(const std::string& fname)
28{
30 auto doc = BaseLib::makeConfigTreeFromFile(fname, true, "OpenGeoSysGLI");
31
32 // ignore attributes related to XML schema
33 doc.ignoreConfigAttribute("xmlns:xsi");
34 doc.ignoreConfigAttribute("xsi:noNamespaceSchemaLocation");
35 doc.ignoreConfigAttribute("xmlns:ogs");
36
38 auto geo_name = doc.getConfigParameter<std::string>("name");
39 if (geo_name.empty())
40 {
41 OGS_FATAL("BoostXmlGmlInterface::readFile(): <name> tag is empty.");
42 }
43
45 for (auto st : doc.getConfigSubtreeList("points"))
46 {
47 std::vector<GeoLib::Point*> points;
49 readPoints(st, points, pnt_names);
50 _geo_objects.addPointVec(std::move(points), geo_name,
51 std::move(pnt_names));
52 }
53
54 std::vector<GeoLib::Polyline*> polylines;
57 for (auto st : doc.getConfigSubtreeList("polylines"))
58 {
60 polylines,
61 *_geo_objects.getPointVec(geo_name),
62 _geo_objects.getPointVecObj(geo_name)->getIDMap(),
63 ply_names);
64 }
65
66 std::vector<GeoLib::Surface*> surfaces;
67 SurfaceVec::NameIdMap sfc_names;
68
70 for (auto st : doc.getConfigSubtreeList("surfaces"))
71 {
72 readSurfaces(st, surfaces, *_geo_objects.getPointVec(geo_name),
73 _geo_objects.getPointVecObj(geo_name)->getIDMap(),
74 sfc_names);
75 }
76
77 if (!polylines.empty())
78 {
79 _geo_objects.addPolylineVec(std::move(polylines), geo_name,
80 std::move(ply_names));
81 }
82
83 if (!surfaces.empty())
84 {
85 _geo_objects.addSurfaceVec(std::move(surfaces), geo_name,
86 std::move(sfc_names));
87 }
88
89 return true;
90}
91
93 BaseLib::ConfigTree const& pointsRoot,
94 std::vector<GeoLib::Point*>& points,
95 std::map<std::string, std::size_t>& pnt_names)
96{
98 for (auto const pt : pointsRoot.getConfigParameterList("point"))
99 {
101 auto const p_id = pt.getConfigAttribute<std::size_t>("id");
103 auto const p_x = pt.getConfigAttribute<double>("x");
105 auto const p_y = pt.getConfigAttribute<double>("y");
107 auto const p_z = pt.getConfigAttribute<double>("z");
108
109 auto const p_size = points.size();
111 "The point id is not unique.");
112 points.push_back(new GeoLib::Point(p_x, p_y, p_z, p_id));
113
114 if (auto const p_name =
116 pt.getConfigAttributeOptional<std::string>("name"))
117 {
118 if (p_name->empty())
119 {
120 OGS_FATAL("Empty point name found in geometry file.");
121 }
122
124 pnt_names, *p_name, p_size, "The point name is not unique.");
125 }
126 }
127}
128
130 BaseLib::ConfigTree const& polylinesRoot,
131 std::vector<GeoLib::Polyline*>& polylines,
132 std::vector<GeoLib::Point*> const& points,
133 std::vector<std::size_t> const& pnt_id_map,
134 std::map<std::string, std::size_t>& ply_names)
135{
137 for (auto const pl : polylinesRoot.getConfigSubtreeList("polyline"))
138 {
140 auto const id = pl.getConfigAttribute<std::size_t>("id");
141 // The id is not used but must be present in the GML file.
142 // That's why pl.ignore...() cannot be used.
143 (void)id;
144
145 polylines.push_back(new GeoLib::Polyline(points));
146
147 if (auto const p_name =
149 pl.getConfigAttributeOptional<std::string>("name"))
150 {
151 if (p_name->empty())
152 {
153 OGS_FATAL("Empty polyline name found in geometry file.");
154 }
155
157 ply_names, *p_name, polylines.size() - 1,
158 "The polyline name is not unique.");
159
160 auto accessOrError = [this, &p_name](auto pt_idx)
161 {
162 auto search = _idx_map.find(pt_idx);
163 if (search == _idx_map.end())
164 {
165 OGS_FATAL(
166 "Polyline `{:s}' contains the point id `{:d}', but the "
167 "id is not in the point list.",
168 p_name->c_str(), pt_idx);
169 }
170 return search->second;
171 };
172
174 for (auto const pt : pl.getConfigParameterList<std::size_t>("pnt"))
175 {
176 polylines.back()->addPoint(pnt_id_map[accessOrError(pt)]);
177 }
178 }
179 else
180 {
181 // polyline has no name, ignore it.
182 pl.ignoreConfigParameterAll("pnt");
183 WARN(
184 "Polyline name is required! Polylines without a name are "
185 "ignored.");
186 }
187 }
188}
189
191 BaseLib::ConfigTree const& surfacesRoot,
192 std::vector<GeoLib::Surface*>& surfaces,
193 std::vector<GeoLib::Point*> const& points,
194 const std::vector<std::size_t>& pnt_id_map,
195 std::map<std::string, std::size_t>& sfc_names)
196{
198 for (auto const& sfc : surfacesRoot.getConfigSubtreeList("surface"))
199 {
201 auto const id = sfc.getConfigAttribute<std::size_t>("id");
202 // The id is not used but must be present in the GML file.
203 // That's why sfc.ignore...() cannot be used.
204 (void)id;
205 surfaces.push_back(new GeoLib::Surface(points));
206
207 if (auto const s_name =
209 sfc.getConfigAttributeOptional<std::string>("name"))
210 {
211 if (s_name->empty())
212 {
213 OGS_FATAL("Empty surface name found in geometry file.");
214 }
215
217 sfc_names, *s_name, surfaces.size() - 1,
218 "The surface name is not unique.");
219
221 for (auto const& element : sfc.getConfigParameterList("element"))
222 {
223 auto const p1_attr =
225 element.getConfigAttribute<std::size_t>("p1");
226 auto const p2_attr =
228 element.getConfigAttribute<std::size_t>("p2");
229 auto const p3_attr =
231 element.getConfigAttribute<std::size_t>("p3");
232
233 auto accessOrError = [this, &s_name](std::size_t pt_idx)
234 {
235 auto search = _idx_map.find(pt_idx);
236 if (search == _idx_map.end())
237 {
238 OGS_FATAL(
239 "The element list of the surface `{:s}' contains "
240 "the invalid point id `{:d}'.",
241 s_name->c_str(), pt_idx);
242 }
243 return search->second;
244 };
245
246 auto const p1 = pnt_id_map[accessOrError(p1_attr)];
247 auto const p2 = pnt_id_map[accessOrError(p2_attr)];
248 auto const p3 = pnt_id_map[accessOrError(p3_attr)];
249 surfaces.back()->addTriangle(p1, p2, p3);
250 }
251 }
252 else
253 {
254 // surface has no name, ignore it.
255 sfc.ignoreConfigParameterAll("element");
256 }
257 }
258}
259
261{
262 if (export_name.empty())
263 {
264 ERR("BoostXmlGmlInterface::write(): No geometry specified.");
265 return false;
266 }
267
268 GeoLib::PointVec const* const pnt_vec(
269 _geo_objects.getPointVecObj(export_name));
270 if (!pnt_vec)
271 {
272 ERR("BoostXmlGmlInterface::write(): No PointVec within the geometry "
273 "'{:s}'.",
275 return false;
276 }
277
278 auto const& pnts(pnt_vec->getVector());
279 if (pnts.empty())
280 {
281 ERR("BoostXmlGmlInterface::write(): No points within the geometry "
282 "'{:s}'.",
284 return false;
285 }
286
287 // create a property tree for writing it to file
288 boost::property_tree::ptree pt;
289
290 // put header in property tree
291 pt.put("<xmlattr>.xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
292 pt.put("<xmlattr>.xmlns:ogs", "https://www.opengeosys.org");
293 auto& geometry_set = pt.add("OpenGeoSysGLI", "");
294
295 geometry_set.add("name", export_name);
296 auto& pnts_tag = geometry_set.add("points", "");
297 for (std::size_t k(0); k < pnts.size(); k++)
298 {
299 auto& pnt_tag = pnts_tag.add("point", "");
300 pnt_tag.put("<xmlattr>.id", k);
301 pnt_tag.put("<xmlattr>.x", (*pnts[k])[0]);
302 pnt_tag.put("<xmlattr>.y", (*pnts[k])[1]);
303 pnt_tag.put("<xmlattr>.z", (*pnts[k])[2]);
304 std::string const& point_name(pnt_vec->getItemNameByID(k));
305 if (!point_name.empty())
306 {
307 pnt_tag.put("<xmlattr>.name", point_name);
308 }
309 }
310
311 addPolylinesToPropertyTree(geometry_set);
312 addSurfacesToPropertyTree(geometry_set);
313
314 boost::property_tree::xml_writer_settings<std::string> settings('\t', 1);
315 write_xml(out, pt, settings);
316 return true;
317}
318
320 boost::property_tree::ptree& geometry_set)
321{
322 GeoLib::SurfaceVec const* const sfc_vec(
323 _geo_objects.getSurfaceVecObj(export_name));
324 if (!sfc_vec)
325 {
326 INFO(
327 "BoostXmlGmlInterface::addSurfacesToPropertyTree(): No surfaces "
328 "within the geometry '{:s}'.",
330 return;
331 }
332
333 auto const& surfaces(sfc_vec->getVector());
334 if (surfaces.empty())
335 {
336 INFO(
337 "BoostXmlGmlInterface::addSurfacesToPropertyTree(): No surfaces "
338 "within the geometry '{:s}'.",
340 return;
341 }
342
343 auto& surfaces_tag = geometry_set.add("surfaces", "");
344 for (std::size_t i = 0; i < surfaces.size(); ++i)
345 {
346 GeoLib::Surface const* const surface(surfaces[i]);
347 std::string sfc_name;
348 sfc_vec->getNameOfElement(surface, sfc_name);
349 auto& surface_tag = surfaces_tag.add("surface", "");
350 surface_tag.put("<xmlattr>.id", i);
351 if (!sfc_name.empty())
352 {
353 surface_tag.put("<xmlattr>.name", sfc_name);
354 }
355 for (std::size_t j = 0; j < surface->getNumberOfTriangles(); ++j)
356 {
357 auto& element_tag = surface_tag.add("element", "");
358 element_tag.put("<xmlattr>.p1", (*(*surface)[j])[0]);
359 element_tag.put("<xmlattr>.p2", (*(*surface)[j])[1]);
360 element_tag.put("<xmlattr>.p3", (*(*surface)[j])[2]);
361 }
362 }
363}
364
366 boost::property_tree::ptree& geometry_set)
367{
368 GeoLib::PolylineVec const* const vec(
369 _geo_objects.getPolylineVecObj(export_name));
370 if (!vec)
371 {
372 INFO(
373 "BoostXmlGmlInterface::addPolylinesToPropertyTree(): No polylines "
374 "within the geometry '{:s}'.",
376 return;
377 }
378
379 auto const& polylines(vec->getVector());
380 if (polylines.empty())
381 {
382 INFO(
383 "BoostXmlGmlInterface::addPolylinesToPropertyTree(): No polylines "
384 "within the geometry '{:s}'.",
386 return;
387 }
388
389 auto& polylines_tag = geometry_set.add("polylines", "");
390 for (std::size_t i = 0; i < polylines.size(); ++i)
391 {
392 GeoLib::Polyline const* const polyline(polylines[i]);
393 std::string ply_name;
394 vec->getNameOfElement(polyline, ply_name);
395 auto& polyline_tag = polylines_tag.add("polyline", "");
396 polyline_tag.put("<xmlattr>.id", i);
397 if (!ply_name.empty())
398 {
399 polyline_tag.put("<xmlattr>.name", ply_name);
400 }
401 for (std::size_t j = 0; j < polyline->getNumberOfPoints(); ++j)
402 {
403 polyline_tag.add("pnt", polyline->getPointID(j));
404 }
405 }
406}
407
408} // end namespace IO
409} // end namespace GeoLib
#define OGS_FATAL(...)
Definition Error.h:19
void INFO(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:28
void ERR(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:40
void WARN(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:34
Range< SubtreeIterator > getConfigSubtreeList(std::string const &root) const
Range< ValueIterator< T > > getConfigParameterList(std::string const &param) const
std::ostringstream out
The stream to write to.
Definition Writer.h:36
Container class for geometric objects.
Definition GEOObjects.h:46
bool write() override
Writes the object to the internal stream. This method must be implemented by a subclass....
void readPolylines(BaseLib::ConfigTree const &polylinesRoot, std::vector< GeoLib::Polyline * > &polylines, std::vector< GeoLib::Point * > const &points, const std::vector< std::size_t > &pnt_id_map, std::map< std::string, std::size_t > &ply_names)
Reads GeoLib::Polyline-objects from an xml-file.
void addPolylinesToPropertyTree(boost::property_tree::ptree &geometry_set)
void readPoints(BaseLib::ConfigTree const &pointsRoot, std::vector< GeoLib::Point * > &points, std::map< std::string, std::size_t > &pnt_names)
Reads GeoLib::Point-objects from an xml-file.
void readSurfaces(BaseLib::ConfigTree const &surfacesRoot, std::vector< GeoLib::Surface * > &surfaces, std::vector< GeoLib::Point * > const &points, const std::vector< std::size_t > &pnt_id_map, std::map< std::string, std::size_t > &sfc_names)
Reads GeoLib::Surface-objects from an xml-file.
bool readFile(const std::string &fname) override
Reads an xml-file containing OGS geometry.
void addSurfacesToPropertyTree(boost::property_tree::ptree &geometry_set)
std::map< std::size_t, std::size_t > _idx_map
BoostXmlGmlInterface(GeoLib::GEOObjects &geo_objs)
This class manages pointers to Points in a std::vector along with a name. It also handles the deletio...
Definition PointVec.h:25
std::string const & getItemNameByID(std::size_t id) const
Definition PointVec.cpp:239
Class Polyline consists mainly of a reference to a point vector and a vector that stores the indices ...
Definition Polyline.h:29
std::size_t getPointID(std::size_t const i) const
Definition Polyline.cpp:149
std::size_t getNumberOfPoints() const
Definition Polyline.cpp:98
A Surface is represented by Triangles. It consists of a reference to a vector of (pointers to) points...
std::size_t getNumberOfTriangles() const
Definition Surface.cpp:76
std::map< std::string, std::size_t > NameIdMap
Definition TemplateVec.h:30
bool getNameOfElement(const T *data, std::string &name) const
std::vector< T * > const & getVector() const
Definition TemplateVec.h:94
ConfigTree makeConfigTreeFromFile(const std::string &filepath, const bool be_ruthless, const std::string &toplevel_tag)
void insertIfKeyUniqueElseError(Map &map, Key const &key, Value &&value, std::string const &error_message)
Definition Algorithm.h:97
TemplateVec< GeoLib::Surface > SurfaceVec
Definition SurfaceVec.h:17
TemplateVec< GeoLib::Polyline > PolylineVec
class PolylineVec encapsulate a std::vector of Polylines additional one can give the vector of polyli...
Definition PolylineVec.h:16