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