SUMO - Simulation of Urban MObility
NBHeightMapper.cpp
Go to the documentation of this file.
1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2011-2017 German Aerospace Center (DLR) and others.
4 /****************************************************************************/
5 //
6 // This program and the accompanying materials
7 // are made available under the terms of the Eclipse Public License v2.0
8 // which accompanies this distribution, and is available at
9 // http://www.eclipse.org/legal/epl-v20.html
10 //
11 /****************************************************************************/
19 // Set z-values for all network positions based on data from a height map
20 /****************************************************************************/
21 
22 
23 // ===========================================================================
24 // included modules
25 // ===========================================================================
26 #ifdef _MSC_VER
27 #include <windows_config.h>
28 #else
29 #include <config.h>
30 #endif
31 
32 #include <string>
34 #include <utils/common/ToString.h>
37 #include <utils/geom/GeomHelper.h>
38 #include "NBHeightMapper.h"
40 #include <utils/common/RGBColor.h>
41 
42 #ifdef HAVE_GDAL
43 #if __GNUC__ > 3
44 #pragma GCC diagnostic push
45 #pragma GCC diagnostic ignored "-Wpedantic"
46 #endif
47 #include <ogrsf_frmts.h>
48 #include <ogr_api.h>
49 #include <gdal_priv.h>
50 #if __GNUC__ > 3
51 #pragma GCC diagnostic pop
52 #endif
53 #endif
54 
55 // ===========================================================================
56 // static members
57 // ===========================================================================
59 
60 // ===========================================================================
61 // method definitions
62 // ===========================================================================
63 
64 
66  myRTree(&Triangle::addSelf), myRaster(0) {
67 }
68 
69 
71  clearData();
72 }
73 
74 
75 const NBHeightMapper&
77  return Singleton;
78 }
79 
80 
81 bool
83  return myRaster != 0 || myTriangles.size() > 0;
84 }
85 
86 
87 double
88 NBHeightMapper::getZ(const Position& geo) const {
89  if (!ready()) {
90  WRITE_WARNING("Cannot supply height since no height data was loaded");
91  return 0;
92  }
93  if (myRaster != 0) {
94  double result = -1e6;
95  if (myBoundary.around(geo)) {
96  const int xSize = int((myBoundary.xmax() - myBoundary.xmin()) / mySizeOfPixel.x() + .5);
97  const double normX = (geo.x() - myBoundary.xmin()) / mySizeOfPixel.x();
98  const double normY = (geo.y() - myBoundary.ymax()) / mySizeOfPixel.y();
99  PositionVector corners;
100  corners.push_back(Position(floor(normX) + 0.5, floor(normY) + 0.5, myRaster[(int)normY * xSize + (int)normX]));
101  if (normX - floor(normX) > 0.5) {
102  corners.push_back(Position(floor(normX) + 1.5, floor(normY) + 0.5, myRaster[(int)normY * xSize + (int)normX + 1]));
103  } else {
104  corners.push_back(Position(floor(normX) - 0.5, floor(normY) + 0.5, myRaster[(int)normY * xSize + (int)normX - 1]));
105  }
106  if (normY - floor(normY) > 0.5) {
107  corners.push_back(Position(floor(normX) + 0.5, floor(normY) + 1.5, myRaster[((int)normY + 1) * xSize + (int)normX]));
108  } else {
109  corners.push_back(Position(floor(normX) + 0.5, floor(normY) - 0.5, myRaster[((int)normY - 1) * xSize + (int)normX]));
110  }
111  result = Triangle(corners).getZ(Position(normX, normY));
112  }
113  if (result > -1e5 && result < 1e5) {
114  return result;
115  }
116  }
117  // coordinates in degrees hence a small search window
118  float minB[2];
119  float maxB[2];
120  minB[0] = (float)geo.x() - 0.00001f;
121  minB[1] = (float)geo.y() - 0.00001f;
122  maxB[0] = (float)geo.x() + 0.00001f;
123  maxB[1] = (float)geo.y() + 0.00001f;
124  QueryResult queryResult;
125  int hits = myRTree.Search(minB, maxB, queryResult);
126  Triangles result = queryResult.triangles;
127  assert(hits == (int)result.size());
128  UNUSED_PARAMETER(hits); // only used for assertion
129 
130  for (Triangles::iterator it = result.begin(); it != result.end(); it++) {
131  const Triangle* triangle = *it;
132  if (triangle->contains(geo)) {
133  return triangle->getZ(geo);
134  }
135  }
136  WRITE_WARNING("Could not get height data for coordinate " + toString(geo));
137  return 0;
138 }
139 
140 
141 void
143  Triangle* triangle = new Triangle(corners);
144  myTriangles.push_back(triangle);
145  Boundary b = corners.getBoxBoundary();
146  const float cmin[2] = {(float) b.xmin(), (float) b.ymin()};
147  const float cmax[2] = {(float) b.xmax(), (float) b.ymax()};
148  myRTree.Insert(cmin, cmax, triangle);
149 }
150 
151 
152 void
154  if (oc.isSet("heightmap.geotiff")) {
155  // parse file(s)
156  std::vector<std::string> files = oc.getStringVector("heightmap.geotiff");
157  for (std::vector<std::string>::const_iterator file = files.begin(); file != files.end(); ++file) {
158  PROGRESS_BEGIN_MESSAGE("Parsing from GeoTIFF '" + *file + "'");
159  int numFeatures = Singleton.loadTiff(*file);
161  " done (parsed " + toString(numFeatures) +
162  " features, Boundary: " + toString(Singleton.getBoundary()) + ").");
163  }
164  }
165  if (oc.isSet("heightmap.shapefiles")) {
166  // parse file(s)
167  std::vector<std::string> files = oc.getStringVector("heightmap.shapefiles");
168  for (std::vector<std::string>::const_iterator file = files.begin(); file != files.end(); ++file) {
169  PROGRESS_BEGIN_MESSAGE("Parsing from shape-file '" + *file + "'");
170  int numFeatures = Singleton.loadShapeFile(*file);
172  " done (parsed " + toString(numFeatures) +
173  " features, Boundary: " + toString(Singleton.getBoundary()) + ").");
174  }
175  }
176 }
177 
178 
179 int
180 NBHeightMapper::loadShapeFile(const std::string& file) {
181 #ifdef HAVE_GDAL
182 #if GDAL_VERSION_MAJOR < 2
183  OGRRegisterAll();
184  OGRDataSource* ds = OGRSFDriverRegistrar::Open(file.c_str(), FALSE);
185 #else
186  GDALAllRegister();
187  GDALDataset* ds = (GDALDataset*)GDALOpenEx(file.c_str(), GDAL_OF_VECTOR | GA_ReadOnly, NULL, NULL, NULL);
188 #endif
189  if (ds == NULL) {
190  throw ProcessError("Could not open shape file '" + file + "'.");
191  }
192 
193  // begin file parsing
194  OGRLayer* layer = ds->GetLayer(0);
195  layer->ResetReading();
196 
197  // triangle coordinates are stored in WGS84 and later matched with network coordinates in WGS84
198  // build coordinate transformation
199  OGRSpatialReference* sr_src = layer->GetSpatialRef();
200  OGRSpatialReference sr_dest;
201  sr_dest.SetWellKnownGeogCS("WGS84");
202  OGRCoordinateTransformation* toWGS84 = OGRCreateCoordinateTransformation(sr_src, &sr_dest);
203  if (toWGS84 == 0) {
204  WRITE_WARNING("Could not create geocoordinates converter; check whether proj.4 is installed.");
205  }
206 
207  int numFeatures = 0;
208  OGRFeature* feature;
209  layer->ResetReading();
210  while ((feature = layer->GetNextFeature()) != NULL) {
211  OGRGeometry* geom = feature->GetGeometryRef();
212  assert(geom != 0);
213 
214  // @todo gracefull handling of shapefiles with unexpected contents or any error handling for that matter
215  assert(std::string(geom->getGeometryName()) == std::string("POLYGON"));
216  // try transform to wgs84
217  geom->transform(toWGS84);
218  OGRLinearRing* cgeom = ((OGRPolygon*) geom)->getExteriorRing();
219  // assume TIN with with 4 points and point0 == point3
220  assert(cgeom->getNumPoints() == 4);
221  PositionVector corners;
222  for (int j = 0; j < 3; j++) {
223  Position pos((double) cgeom->getX(j), (double) cgeom->getY(j), (double) cgeom->getZ(j));
224  corners.push_back(pos);
225  myBoundary.add(pos);
226  }
227  addTriangle(corners);
228  numFeatures++;
229 
230  /*
231  OGRwkbGeometryType gtype = geom->getGeometryType();
232  switch (gtype) {
233  case wkbPolygon: {
234  break;
235  }
236  case wkbPoint: {
237  WRITE_WARNING("got wkbPoint");
238  break;
239  }
240  case wkbLineString: {
241  WRITE_WARNING("got wkbLineString");
242  break;
243  }
244  case wkbMultiPoint: {
245  WRITE_WARNING("got wkbMultiPoint");
246  break;
247  }
248  case wkbMultiLineString: {
249  WRITE_WARNING("got wkbMultiLineString");
250  break;
251  }
252  case wkbMultiPolygon: {
253  WRITE_WARNING("got wkbMultiPolygon");
254  break;
255  }
256  default:
257  WRITE_WARNING("Unsupported shape type occured");
258  break;
259  }
260  */
261  OGRFeature::DestroyFeature(feature);
262  }
263 #if GDAL_VERSION_MAJOR < 2
264  OGRDataSource::DestroyDataSource(ds);
265 #else
266  GDALClose(ds);
267 #endif
268  OCTDestroyCoordinateTransformation(toWGS84);
269  OGRCleanupAll();
270  return numFeatures;
271 #else
272  WRITE_ERROR("Cannot load shape file since SUMO was compiled without GDAL support.");
273  return 0;
274 #endif
275 }
276 
277 
278 int
279 NBHeightMapper::loadTiff(const std::string& file) {
280 #ifdef HAVE_GDAL
281  GDALAllRegister();
282  GDALDataset* poDataset = (GDALDataset*)GDALOpen(file.c_str(), GA_ReadOnly);
283  if (poDataset == 0) {
284  WRITE_ERROR("Cannot load GeoTIFF file.");
285  return 0;
286  }
287  const int xSize = poDataset->GetRasterXSize();
288  const int ySize = poDataset->GetRasterYSize();
289  double adfGeoTransform[6];
290  if (poDataset->GetGeoTransform(adfGeoTransform) == CE_None) {
291  Position topLeft(adfGeoTransform[0], adfGeoTransform[3]);
292  mySizeOfPixel.set(adfGeoTransform[1], adfGeoTransform[5]);
293  const double horizontalSize = xSize * mySizeOfPixel.x();
294  const double verticalSize = ySize * mySizeOfPixel.y();
295  myBoundary.add(topLeft);
296  myBoundary.add(topLeft.x() + horizontalSize, topLeft.y() + verticalSize);
297  } else {
298  WRITE_ERROR("Could not parse geo information from " + file + ".");
299  return 0;
300  }
301  const int picSize = xSize * ySize;
302  myRaster = (int16_t*)CPLMalloc(sizeof(int16_t) * picSize);
303  for (int i = 1; i <= poDataset->GetRasterCount(); i++) {
304  GDALRasterBand* poBand = poDataset->GetRasterBand(i);
305  if (poBand->GetColorInterpretation() != GCI_GrayIndex) {
306  WRITE_ERROR("Unknown color band in " + file + ".");
307  clearData();
308  break;
309  }
310  if (poBand->GetRasterDataType() != GDT_Int16) {
311  WRITE_ERROR("Unknown data type in " + file + ".");
312  clearData();
313  break;
314  }
315  assert(xSize == poBand->GetXSize() && ySize == poBand->GetYSize());
316  if (poBand->RasterIO(GF_Read, 0, 0, xSize, ySize, myRaster, xSize, ySize, GDT_Int16, 0, 0) == CE_Failure) {
317  WRITE_ERROR("Failure in reading " + file + ".");
318  clearData();
319  break;
320  }
321  }
322  GDALClose(poDataset);
323  return picSize;
324 #else
325  WRITE_ERROR("Cannot load GeoTIFF file since SUMO was compiled without GDAL support.");
326  return 0;
327 #endif
328 }
329 
330 
331 void
333  for (Triangles::iterator it = myTriangles.begin(); it != myTriangles.end(); it++) {
334  delete *it;
335  }
336  myTriangles.clear();
337 #ifdef HAVE_GDAL
338  if (myRaster != 0) {
339  CPLFree(myRaster);
340  myRaster = 0;
341  }
342 #endif
343  myBoundary.reset();
344 }
345 
346 
347 // ===========================================================================
348 // Triangle member methods
349 // ===========================================================================
351  myCorners(corners) {
352  assert(myCorners.size() == 3);
353  // @todo assert non-colinearity
354 }
355 
356 
357 void
359  queryResult.triangles.push_back(this);
360 }
361 
362 
363 bool
365  return myCorners.around(pos);
366 }
367 
368 
369 double
371  // en.wikipedia.org/wiki/Line-plane_intersection
372  Position p0 = myCorners.front();
373  Position line(0, 0, 1);
374  p0.sub(geo); // p0 - l0
375  Position normal = normalVector();
376  return p0.dotProduct(normal) / line.dotProduct(normal);
377 }
378 
379 
380 Position
382  // @todo maybe cache result to avoid multiple computations?
383  Position side1 = myCorners[1] - myCorners[0];
384  Position side2 = myCorners[2] - myCorners[0];
385  return side1.crossProduct(side2);
386 }
387 
388 
389 /****************************************************************************/
390 
double xmax() const
Returns maximum x-coordinate.
Definition: Boundary.cpp:131
std::vector< std::string > getStringVector(const std::string &name) const
Returns the list of string-vector-value of the named option (only for Option_String) ...
Triangles myTriangles
bool ready() const
returns whether the NBHeightMapper has data
Position normalVector() const
returns the normal vector for this triangles plane
int loadShapeFile(const std::string &file)
load height data from Arcgis-shape file and returns the number of parsed features ...
bool around(const Position &p, double offset=0) const
Returns the information whether the position vector describes a polygon lying around the given point...
double ymax() const
Returns maximum y-coordinate.
Definition: Boundary.cpp:143
double x() const
Returns the x-position.
Definition: Position.h:62
Triangle(const PositionVector &corners)
double getZ(const Position &geo) const
returns the projection of the give geoCoordinate (WGS84) onto triangle plane
void addTriangle(PositionVector corners)
adds one triangles worth of height data
void clearData()
clears loaded data
static NBHeightMapper Singleton
the singleton instance
void set(double x, double y)
set positions x and y
Definition: Position.h:92
NBHeightMapper()
private constructor and destructor (Singleton)
#define UNUSED_PARAMETER(x)
Definition: StdDefs.h:39
A class that stores a 2D geometrical boundary.
Definition: Boundary.h:47
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:199
int loadTiff(const std::string &file)
load height data from GeoTIFF file and returns the number of non void pixels
static const NBHeightMapper & get()
return the singleton instance (maybe 0)
class for cirumventing the const-restriction of RTree::Search-context
static void loadIfSet(OptionsCont &oc)
loads heigh map data if any loading options are set
TRIANGLE_RTREE_QUAL myRTree
The RTree for spatial queries.
Position crossProduct(const Position &pos)
returns the cross product between this point and the second one
Definition: Position.h:264
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:55
A point in 2D or 3D with translation and scaling methods.
Definition: Position.h:45
A list of positions.
double y() const
Returns the y-position.
Definition: Position.h:67
std::vector< const Triangle * > Triangles
#define PROGRESS_BEGIN_MESSAGE(msg)
Definition: MsgHandler.h:201
static MsgHandler * getMessageInstance()
Returns the instance to add normal messages to.
Definition: MsgHandler.cpp:57
int16_t * myRaster
raster height information in m
bool contains(const Position &pos) const
checks whether pos lies within triangle (only checks x,y)
Position mySizeOfPixel
dimensions of one pixel in raster data
Boundary myBoundary
convex boundary of all known triangles;
#define WRITE_ERROR(msg)
Definition: MsgHandler.h:205
bool around(const Position &p, double offset=0) const
Returns whether the boundary contains the given coordinate.
Definition: Boundary.cpp:179
void reset()
Resets the boundary.
Definition: Boundary.cpp:73
double xmin() const
Returns minimum x-coordinate.
Definition: Boundary.cpp:125
double dotProduct(const Position &pos)
returns the dot product (scalar product) between this point and the second one
Definition: Position.h:272
PositionVector myCorners
the corners of the triangle
A storage for options typed value containers)
Definition: OptionsCont.h:98
void addSelf(const QueryResult &queryResult) const
callback for RTree search
double ymin() const
Returns minimum y-coordinate.
Definition: Boundary.cpp:137
double getZ(const Position &geo) const
returns height for the given geo coordinate (WGS84)
void add(double x, double y, double z=0)
Makes the boundary include the given coordinate.
Definition: Boundary.cpp:85
const Boundary & getBoundary()
returns the convex boundary of all known triangles
Boundary getBoxBoundary() const
Returns a boundary enclosing this list of lines.
Set z-values for all network positions based on data from a height map.
bool isSet(const std::string &name, bool failOnNonExistant=true) const
Returns the information whether the named option is set.
void sub(double dx, double dy)
Substracts the given position from this one.
Definition: Position.h:152
void endProcessMsg(std::string msg)
Ends a process information.
Definition: MsgHandler.cpp:126