OGS
SHPInterface.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
4#include "SHPInterface.h"
5
7#include "BaseLib/Logging.h"
9#include "GeoLib/GEOObjects.h"
10#include "GeoLib/Point.h"
11#include "GeoLib/Polyline.h"
12#include "GeoLib/Station.h"
16#include "MeshLib/Mesh.h"
17#include "MeshLib/Node.h"
18
19namespace FileIO
20{
21bool SHPInterface::readSHPInfo(const std::string& filename, int& shapeType,
22 int& numberOfEntities)
23{
24 SHPHandle hSHP = SHPOpen(filename.c_str(), "rb");
25 if (!hSHP)
26 {
27 return false;
28 }
29
30 double padfMinBound[4];
31 double padfMaxBound[4];
32
33 // The SHPGetInfo() function retrieves various information about shapefile
34 // as a whole. The bounds are read from the file header, and may be
35 // inaccurate if the file was improperly generated.
36 SHPGetInfo(hSHP, &numberOfEntities, &shapeType, padfMinBound, padfMaxBound);
37
38 SHPClose(hSHP);
39 return true;
40}
41
42void SHPInterface::readSHPFile(const std::string& filename, OGSType choice,
43 const std::string& listName,
44 std::string const& gmsh_path)
45{
46 int shapeType;
47 int numberOfElements;
48 double padfMinBound[4];
49 double padfMaxBound[4];
50
51 SHPHandle hSHP = SHPOpen(filename.c_str(), "rb");
52 SHPGetInfo(hSHP, &numberOfElements, &shapeType, padfMinBound, padfMaxBound);
53
54 if (((shapeType - 1) % 10 == 0) && (choice == SHPInterface::OGSType::POINT))
55 {
56 readPoints(hSHP, numberOfElements, listName);
57 }
58 if (((shapeType - 1) % 10 == 0) &&
60 {
61 readStations(hSHP, numberOfElements, listName);
62 }
63 if (((shapeType - 3) % 10 == 0 || (shapeType - 5) % 10 == 0) &&
65 {
66 readPolylines(hSHP, numberOfElements, listName);
67 }
68 if (((shapeType - 3) % 10 == 0 || (shapeType - 5) % 10 == 0) &&
70 {
71 readPolygons(hSHP, numberOfElements, listName, gmsh_path);
72 }
73}
74
75void SHPInterface::readPoints(const SHPHandle& hSHP, int numberOfElements,
76 std::string listName)
77{
78 if (numberOfElements > 0)
79 {
80 std::vector<GeoLib::Point*> points;
81 SHPObject* hSHPObject;
82
83 for (int i = 0; i < numberOfElements; i++)
84 {
85 hSHPObject = SHPReadObject(hSHP, i);
86
87 auto* pnt =
88 new GeoLib::Point(*(hSHPObject->padfX), *(hSHPObject->padfY),
89 *(hSHPObject->padfZ));
90 points.push_back(pnt);
91 }
92
93 _geoObjects.addPointVec(std::move(points), listName,
95 SHPDestroyObject(hSHPObject); // de-allocate SHPObject
96 }
97}
98
99void SHPInterface::readStations(const SHPHandle& hSHP, int numberOfElements,
100 std::string listName)
101{
102 if (numberOfElements > 0)
103 {
104 std::vector<GeoLib::Point*> stations;
105 stations.reserve(numberOfElements);
106 SHPObject* hSHPObject;
107
108 for (int i = 0; i < numberOfElements; i++)
109 {
110 hSHPObject = SHPReadObject(hSHP, i);
111 GeoLib::Station* stn =
112 GeoLib::Station::createStation(std::to_string(i),
113 *(hSHPObject->padfX),
114 *(hSHPObject->padfY),
115 *(hSHPObject->padfZ));
116 stations.push_back(stn);
117 }
118
119 _geoObjects.addStationVec(std::move(stations), listName);
120 SHPDestroyObject(hSHPObject); // de-allocate SHPObject
121 }
122}
123
124void SHPInterface::readPolylines(const SHPHandle& hSHP, int numberOfElements,
125 std::string listName)
126{
127 if (numberOfElements <= 0)
128 {
129 return;
130 }
131 std::vector<GeoLib::Point*> pnts;
132 std::vector<GeoLib::Polyline*> lines;
133
134 std::size_t pnt_id(0);
135 // for each polyline
136 for (int i = 0; i < numberOfElements; ++i)
137 {
138 SHPObject* hSHPObject = SHPReadObject(hSHP, i);
139 int const noOfPoints = hSHPObject->nVertices;
140 int const noOfParts = hSHPObject->nParts;
141
142 for (int p = 0; p < noOfParts; ++p)
143 {
144 int const firstPnt = *(hSHPObject->panPartStart + p);
145 int const lastPnt = (p < (noOfParts - 1))
146 ? *(hSHPObject->panPartStart + p + 1)
147 : noOfPoints;
148
149 // for each point in that polyline
150 for (int j = firstPnt; j < lastPnt; ++j)
151 {
152 pnts.push_back(new GeoLib::Point(
153 *(hSHPObject->padfX + j), *(hSHPObject->padfY + j),
154 *(hSHPObject->padfZ + j), pnt_id));
155 pnt_id++;
156 }
157 }
158 SHPDestroyObject(hSHPObject); // de-allocate SHPObject
159 }
160
161 _geoObjects.addPointVec(std::move(pnts), listName,
163 GeoLib::PointVec const& points(*(_geoObjects.getPointVecObj(listName)));
164 std::vector<std::size_t> const& pnt_id_map(points.getIDMap());
165
166 pnt_id = 0;
167 for (int i = 0; i < numberOfElements; ++i)
168 {
169 SHPObject* hSHPObject = SHPReadObject(hSHP, i);
170 int const noOfPoints = hSHPObject->nVertices;
171 int const noOfParts = hSHPObject->nParts;
172
173 for (int p = 0; p < noOfParts; ++p)
174 {
175 // output for the first part of multipart polyline
176 if (noOfParts > 1 && p == 0)
177 {
178 INFO(
179 "Polygon {:d} consists of {:d} parts (PolylineIDs "
180 "{:d}-{:d}).",
181 i, noOfParts, lines.size(), lines.size() + noOfParts - 1);
182 }
183
184 int const firstPnt = *(hSHPObject->panPartStart + p);
185 int const lastPnt = (p < (noOfParts - 1))
186 ? *(hSHPObject->panPartStart + p + 1)
187 : noOfPoints;
188
189 auto* line = new GeoLib::Polyline(points.getVector());
190
191 // create polyline
192 for (int j = firstPnt; j < lastPnt; ++j)
193 {
194 line->addPoint(pnt_id_map[pnt_id]);
195 pnt_id++;
196 }
197 // add polyline to polyline vector
198 lines.push_back(line);
199 }
200 SHPDestroyObject(hSHPObject); // de-allocate SHPObject
201 }
202 _geoObjects.addPolylineVec(std::move(lines), listName,
204}
205
206void SHPInterface::readPolygons(const SHPHandle& hSHP, int numberOfElements,
207 const std::string& listName,
208 std::string const& gmsh_path)
209{
210 readPolylines(hSHP, numberOfElements, listName);
211
212 auto const polylines = _geoObjects.getPolylineVec(listName);
213
214 for (auto const* polyline : *polylines)
215 {
216 INFO("Creating a surface by triangulation of the polyline ...");
217 if (FileIO::createSurface(*polyline, _geoObjects, listName, gmsh_path))
218 {
219 INFO("\t done");
220 }
221 else
222 {
223 WARN(
224 "\t Creating a surface by triangulation of the polyline "
225 "failed.");
226 }
227 }
228}
229
230bool SHPInterface::write2dMeshToSHP(const std::string& file_name,
231 const MeshLib::Mesh& mesh)
232{
233 if (mesh.getDimension() != 2)
234 {
235 ERR("SHPInterface::write2dMeshToSHP(): Mesh to Shape conversion is "
236 "only working for 2D Meshes.");
237 return false;
238 }
239
240 std::size_t const n_elements = mesh.getNumberOfElements();
241 if (n_elements < 1)
242 {
243 ERR("SHPInterface::write2dMeshToSHP(): Mesh contains no elements.");
244 return false;
245 }
246
247 // DBF-export requires a limit of records because of the limits to the
248 // integer values. The exponent controls maximum number of elements and
249 // the maximum number of digits written in the DBF file.
250 std::size_t const max_exp = 8;
251 if (n_elements >= std::pow(10, max_exp))
252 {
253 ERR("SHPInterface::write2dMeshToSHP(): Mesh contains too many elements "
254 "for currently implemented DBF-boundaries.");
255 return false;
256 }
257
258 SHPHandle hSHP = SHPCreate(file_name.c_str(), SHPT_POLYGON);
259 DBFHandle hDBF = DBFCreate(file_name.c_str());
260 int const elem_id_field =
261 DBFAddField(hDBF, "Elem_ID", FTInteger, max_exp, 0);
262
263 // Writing mesh elements to shape file
264 std::size_t shp_record(0);
265 std::vector<MeshLib::Element*> const& elems = mesh.getElements();
266 for (MeshLib::Element const* const e : elems)
267 {
268 // ignore all elements except triangles and quads
269 if ((e->getGeomType() == MeshLib::MeshElemType::TRIANGLE) ||
270 (e->getGeomType() == MeshLib::MeshElemType::QUAD))
271 {
272 SHPObject* object = createShapeObject(*e, shp_record);
273 SHPWriteObject(hSHP, -1, object);
274 SHPDestroyObject(object);
275
276 // write element ID to DBF-file
277 DBFWriteIntegerAttribute(hDBF, shp_record, elem_id_field,
278 e->getID());
279 shp_record++;
280 }
281 }
282 SHPClose(hSHP);
283
284 // Write scalar arrays to database file
285 int const n_recs = DBFGetRecordCount(hDBF);
286 for (auto [name, property] : mesh.getProperties())
287 {
288 if (auto p = dynamic_cast<MeshLib::PropertyVector<int>*>(property))
289 {
290 int const field =
291 DBFAddField(hDBF, std::string(name).c_str(), FTInteger, 16, 0);
292 for (int i = 0; i < n_recs; ++i)
293 {
294 std::size_t const elem_idx =
295 DBFReadIntegerAttribute(hDBF, i, elem_id_field);
296 DBFWriteIntegerAttribute(hDBF, i, field, (*p)[elem_idx]);
297 }
298 }
299 else if (auto p =
300 dynamic_cast<MeshLib::PropertyVector<double>*>(property))
301 {
302 int const field =
303 DBFAddField(hDBF, std::string(name).c_str(), FTDouble, 33, 16);
304 for (int i = 0; i < n_recs; ++i)
305 {
306 std::size_t const elem_idx =
307 DBFReadIntegerAttribute(hDBF, i, elem_id_field);
308 DBFWriteDoubleAttribute(hDBF, i, field, (*p)[elem_idx]);
309 }
310 }
311 }
312 DBFClose(hDBF);
313 INFO("Shape export of 2D mesh '{:s}' finished.", mesh.getName());
314 return true;
315}
316
318 std::size_t const shp_record)
319{
320 unsigned const nNodes(e.getNumberOfBaseNodes());
321 double* padfX = new double[nNodes + 1];
322 double* padfY = new double[nNodes + 1];
323 double* padfZ = new double[nNodes + 1];
324 for (unsigned j = 0; j < nNodes; ++j)
325 {
326 padfX[j] = (*e.getNode(j))[0];
327 padfY[j] = (*e.getNode(j))[1];
328 padfZ[j] = (*e.getNode(j))[2];
329 }
330 // Last node == first node to close the polygon
331 padfX[nNodes] = (*e.getNode(0))[0];
332 padfY[nNodes] = (*e.getNode(0))[1];
333 padfZ[nNodes] = (*e.getNode(0))[2];
334
335 // the generated shape object now handles the memory for padfX/Y/Z
336 return SHPCreateObject(SHPT_POLYGON, shp_record, 0, nullptr, nullptr,
337 nNodes + 1, padfX, padfY, padfZ, nullptr);
338}
339
340} // namespace FileIO
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
void readPolylines(const SHPHandle &hSHP, int numberOfElements, std::string listName)
Reads lines into a vector of Polyline objects.
static bool readSHPInfo(const std::string &filename, int &shapeType, int &numberOfEntities)
Reads the header of the shape file.
void readPoints(const SHPHandle &hSHP, int numberOfElements, std::string listName)
Reads points into a vector of Point objects.
OGSType
Connection between ESRI type system for shape files and OGS GeoLib.
void readStations(const SHPHandle &hSHP, int numberOfElements, std::string listName)
Reads points into a vector of Point objects and marks them as Station.
void readSHPFile(const std::string &filename, OGSType choice, const std::string &listName, std::string const &gmsh_path)
Reads data from the shape file.
GeoLib::GEOObjects & _geoObjects
static bool write2dMeshToSHP(const std::string &file_name, const MeshLib::Mesh &mesh)
static SHPObject * createShapeObject(MeshLib::Element const &e, std::size_t const shp_record)
Creates a shape object polygon out of a 2D mesh element.
void readPolygons(const SHPHandle &hSHP, int numberOfElements, const std::string &listName, std::string const &gmsh_path)
Reads lines into a vector of Polyline and Surface objects.
This class manages pointers to Points in a std::vector along with a name. It also handles the deletio...
Definition PointVec.h:25
const std::vector< std::size_t > & getIDMap() const
Definition PointVec.h:86
Class Polyline consists mainly of a reference to a point vector and a vector that stores the indices ...
Definition Polyline.h:29
A Station (observation site) is basically a Point with some additional information.
Definition Station.h:26
static Station * createStation(const std::string &line)
Definition Station.cpp:35
std::map< std::string, std::size_t > NameIdMap
Definition TemplateVec.h:30
std::vector< T * > const & getVector() const
Definition TemplateVec.h:94
virtual unsigned getNumberOfBaseNodes() const =0
virtual const Node * getNode(unsigned idx) const =0
std::vector< Element * > const & getElements() const
Get the element-vector for the mesh.
Definition Mesh.h:100
unsigned getDimension() const
Returns the dimension of the mesh (determined by the maximum dimension over all elements).
Definition Mesh.h:79
Properties & getProperties()
Definition Mesh.h:125
const std::string getName() const
Get name of the mesh.
Definition Mesh.h:94
std::size_t getNumberOfElements() const
Get the number of elements.
Definition Mesh.h:88
bool createSurface(GeoLib::Polyline const &ply, GeoLib::GEOObjects &geometries, std::string const &geometry_name, std::string const &gmsh_binary)