SUMO - Simulation of Urban MObility
GeoConvHelper.cpp
Go to the documentation of this file.
1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-2018 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
17 // static methods for processing the coordinates conversion for the current net
18 /****************************************************************************/
19 
20 
21 // ===========================================================================
22 // included modules
23 // ===========================================================================
24 #include <config.h>
25 
26 #include <map>
27 #include <cmath>
28 #include <cassert>
29 #include <climits>
31 #include <utils/common/ToString.h>
32 #include <utils/geom/GeomHelper.h>
35 #include "GeoConvHelper.h"
36 
37 
38 // ===========================================================================
39 // static member variables
40 // ===========================================================================
41 
46 
47 // ===========================================================================
48 // method definitions
49 // ===========================================================================
50 
51 GeoConvHelper::GeoConvHelper(const std::string& proj, const Position& offset,
52  const Boundary& orig, const Boundary& conv, double scale, double rot, bool inverse):
53  myProjString(proj),
54 #ifdef HAVE_PROJ
55  myProjection(0),
56  myInverseProjection(0),
57  myGeoProjection(0),
58 #endif
59  myOffset(offset),
60  myGeoScale(scale),
61  mySin(sin(DEG2RAD(-rot))), // rotate clockwise
62  myCos(cos(DEG2RAD(-rot))),
63  myProjectionMethod(NONE),
64  myUseInverseProjection(inverse),
65  myOrigBoundary(orig),
66  myConvBoundary(conv) {
67  if (proj == "!") {
69  } else if (proj == "-") {
71  } else if (proj == "UTM") {
73  } else if (proj == "DHDN") {
75  } else if (proj == "DHDN_UTM") {
77 #ifdef HAVE_PROJ
78  } else {
80  myProjection = pj_init_plus(proj.c_str());
81  if (myProjection == 0) {
82  // !!! check pj_errno
83  throw ProcessError("Could not build projection!");
84  }
85 #endif
86  }
87 }
88 
89 
91 #ifdef HAVE_PROJ
92  if (myProjection != 0) {
93  pj_free(myProjection);
94  }
95  if (myInverseProjection != 0) {
96  pj_free(myInverseProjection);
97  }
98  if (myGeoProjection != 0) {
99  pj_free(myInverseProjection);
100  }
101 #endif
102 }
103 
104 bool
106  return (
107  myProjString == o.myProjString &&
108  myOffset == o.myOffset &&
112  myGeoScale == o.myGeoScale &&
113  myCos == o.myCos &&
114  mySin == o.mySin &&
116  );
117 }
118 
121  myProjString = orig.myProjString;
122  myOffset = orig.myOffset;
126  myGeoScale = orig.myGeoScale;
127  myCos = orig.myCos;
128  mySin = orig.mySin;
130 #ifdef HAVE_PROJ
131  if (myProjection != 0) {
132  pj_free(myProjection);
133  myProjection = 0;
134  }
135  if (myInverseProjection != 0) {
136  pj_free(myInverseProjection);
137  myInverseProjection = 0;
138  }
139  if (myGeoProjection != 0) {
140  pj_free(myGeoProjection);
141  myGeoProjection = 0;
142  }
143  if (orig.myProjection != 0) {
144  myProjection = pj_init_plus(orig.myProjString.c_str());
145  }
146  if (orig.myInverseProjection != 0) {
147  myInverseProjection = pj_init_plus(pj_get_def(orig.myInverseProjection, 0));
148  }
149  if (orig.myGeoProjection != 0) {
150  myGeoProjection = pj_init_plus(pj_get_def(orig.myGeoProjection, 0));
151  }
152 #endif
153  return *this;
154 }
155 
156 
157 bool
159  std::string proj = "!"; // the default
160  double scale = oc.getFloat("proj.scale");
161  double rot = oc.getFloat("proj.rotate");
162  Position offset = Position(oc.getFloat("offset.x"), oc.getFloat("offset.y"));
163  bool inverse = oc.exists("proj.inverse") && oc.getBool("proj.inverse");
164 
165  if (oc.getBool("simple-projection")) {
166  proj = "-";
167  }
168 
169 #ifdef HAVE_PROJ
170  if (oc.getBool("proj.inverse") && oc.getString("proj") == "!") {
171  WRITE_ERROR("Inverse projection works only with explicit proj parameters.");
172  return false;
173  }
174  unsigned numProjections = oc.getBool("simple-projection") + oc.getBool("proj.utm") + oc.getBool("proj.dhdn") + oc.getBool("proj.dhdnutm") + (oc.getString("proj").length() > 1);
175  if (numProjections > 1) {
176  WRITE_ERROR("The projection method needs to be uniquely defined.");
177  return false;
178  }
179 
180  if (oc.getBool("proj.utm")) {
181  proj = "UTM";
182  } else if (oc.getBool("proj.dhdn")) {
183  proj = "DHDN";
184  } else if (oc.getBool("proj.dhdnutm")) {
185  proj = "DHDN_UTM";
186  } else if (!oc.isDefault("proj")) {
187  proj = oc.getString("proj");
188  }
189 #endif
190  myProcessing = GeoConvHelper(proj, offset, Boundary(), Boundary(), scale, rot, inverse);
192  return true;
193 }
194 
195 
196 void
197 GeoConvHelper::init(const std::string& proj, const Position& offset, const Boundary& orig,
198  const Boundary& conv, double scale) {
199  myProcessing = GeoConvHelper(proj, offset, orig, conv, scale);
201 }
202 
203 
204 void
206  oc.addOptionSubTopic("Projection");
207 
208  oc.doRegister("simple-projection", new Option_Bool(false));
209  oc.addSynonyme("simple-projection", "proj.simple", true);
210  oc.addDescription("simple-projection", "Projection", "Uses a simple method for projection");
211 
212  oc.doRegister("proj.scale", new Option_Float(1.0));
213  oc.addDescription("proj.scale", "Projection", "Scaling factor for input coordinates");
214 
215  oc.doRegister("proj.rotate", new Option_Float(0.0));
216  oc.addDescription("proj.rotate", "Projection", "Rotation (clockwise degrees) for input coordinates");
217 
218 #ifdef HAVE_PROJ
219  oc.doRegister("proj.utm", new Option_Bool(false));
220  oc.addDescription("proj.utm", "Projection", "Determine the UTM zone (for a universal transversal mercator projection based on the WGS84 ellipsoid)");
221 
222  oc.doRegister("proj.dhdn", new Option_Bool(false));
223  oc.addDescription("proj.dhdn", "Projection", "Determine the DHDN zone (for a transversal mercator projection based on the bessel ellipsoid, \"Gauss-Krueger\")");
224 
225  oc.doRegister("proj", new Option_String("!"));
226  oc.addDescription("proj", "Projection", "Uses STR as proj.4 definition for projection");
227 
228  oc.doRegister("proj.inverse", new Option_Bool(false));
229  oc.addDescription("proj.inverse", "Projection", "Inverses projection");
230 
231  oc.doRegister("proj.dhdnutm", new Option_Bool(false));
232  oc.addDescription("proj.dhdnutm", "Projection", "Convert from Gauss-Krueger to UTM");
233 #endif // HAVE_PROJ
234 }
235 
236 
237 bool
239  return myProjectionMethod != NONE;
240 }
241 
242 
243 bool
245  return myUseInverseProjection;
246 }
247 
248 
249 void
251  cartesian.sub(getOffsetBase());
252  if (myProjectionMethod == NONE) {
253  return;
254  }
255  if (myProjectionMethod == SIMPLE) {
256  const double y = cartesian.y() / 111136.;
257  const double x = cartesian.x() / 111320. / cos(DEG2RAD(y));
258  cartesian.set(x, y);
259  return;
260  }
261 #ifdef HAVE_PROJ
262  projUV p;
263  p.u = cartesian.x();
264  p.v = cartesian.y();
265  p = pj_inv(p, myProjection);
267  p.u *= RAD_TO_DEG;
268  p.v *= RAD_TO_DEG;
269  cartesian.set((double) p.u, (double) p.v);
270 #endif
271 }
272 
273 
274 bool
275 GeoConvHelper::x2cartesian(Position& from, bool includeInBoundary) {
276  if (includeInBoundary) {
277  myOrigBoundary.add(from);
278  }
279  // init projection parameter on first use
280 #ifdef HAVE_PROJ
281  if (myProjection == 0) {
282  double x = from.x() * myGeoScale;
283  switch (myProjectionMethod) {
284  case DHDN_UTM: {
285  int zone = (int)((x - 500000.) / 1000000.);
286  if (zone < 1 || zone > 5) {
287  WRITE_WARNING("Attempt to initialize DHDN_UTM-projection on invalid longitude " + toString(x));
288  return false;
289  }
290  myProjString = "+proj=tmerc +lat_0=0 +lon_0=" + toString(3 * zone) +
291  " +k=1 +x_0=" + toString(zone * 1000000 + 500000) +
292  " +y_0=0 +ellps=bessel +datum=potsdam +units=m +no_defs";
293  myInverseProjection = pj_init_plus(myProjString.c_str());
294  myGeoProjection = pj_init_plus("+proj=latlong +datum=WGS84");
296  x = ((x - 500000.) / 1000000.) * 3; // continues with UTM
297  }
298  FALLTHROUGH;
299  case UTM: {
300  int zone = (int)(x + 180) / 6 + 1;
301  myProjString = "+proj=utm +zone=" + toString(zone) +
302  " +ellps=WGS84 +datum=WGS84 +units=m +no_defs";
303  myProjection = pj_init_plus(myProjString.c_str());
305  }
306  break;
307  case DHDN: {
308  int zone = (int)(x / 3);
309  if (zone < 1 || zone > 5) {
310  WRITE_WARNING("Attempt to initialize DHDN-projection on invalid longitude " + toString(x));
311  return false;
312  }
313  myProjString = "+proj=tmerc +lat_0=0 +lon_0=" + toString(3 * zone) +
314  " +k=1 +x_0=" + toString(zone * 1000000 + 500000) +
315  " +y_0=0 +ellps=bessel +datum=potsdam +units=m +no_defs";
316  myProjection = pj_init_plus(myProjString.c_str());
318  }
319  break;
320  default:
321  break;
322  }
323  }
324  if (myInverseProjection != 0) {
325  double x = from.x();
326  double y = from.y();
327  if (pj_transform(myInverseProjection, myGeoProjection, 1, 1, &x, &y, NULL)) {
328  WRITE_WARNING("Could not transform (" + toString(x) + "," + toString(y) + ")");
329  }
330  from.set(double(x * RAD_TO_DEG), double(y * RAD_TO_DEG));
331  }
332 #endif
333  // perform conversion
334  bool ok = x2cartesian_const(from);
335  if (ok) {
336  if (includeInBoundary) {
337  myConvBoundary.add(from);
338  }
339  }
340  return ok;
341 }
342 
343 
344 bool
346  double x2 = from.x() * myGeoScale;
347  double y2 = from.y() * myGeoScale;
348  double x = x2 * myCos - y2 * mySin;
349  double y = x2 * mySin + y2 * myCos;
350  if (myProjectionMethod == NONE) {
351  from.add(myOffset);
352  } else if (myUseInverseProjection) {
353  cartesian2geo(from);
354  } else {
355  if (x > 180.1 || x < -180.1) {
356  WRITE_WARNING("Invalid longitude " + toString(x));
357  return false;
358  }
359  if (y > 90.1 || y < -90.1) {
360  WRITE_WARNING("Invalid latitude " + toString(y));
361  return false;
362  }
363 #ifdef HAVE_PROJ
364  if (myProjection != 0) {
365  projUV p;
366  p.u = x * DEG_TO_RAD;
367  p.v = y * DEG_TO_RAD;
368  p = pj_fwd(p, myProjection);
370  x = p.u;
371  y = p.v;
372  }
373 #endif
374  if (myProjectionMethod == SIMPLE) {
375  x *= 111320. * cos(DEG2RAD(y));
376  y *= 111136.;
378  }
379  }
380  if (x > std::numeric_limits<double>::max() ||
381  y > std::numeric_limits<double>::max()) {
382  return false;
383  }
384  from.set(x, y);
385  from.add(myOffset);
386  return true;
387 }
388 
389 
390 void
391 GeoConvHelper::moveConvertedBy(double x, double y) {
392  myOffset.add(x, y);
393  myConvBoundary.moveby(x, y);
394 }
395 
396 
397 const Boundary&
399  return myOrigBoundary;
400 }
401 
402 
403 const Boundary&
405  return myConvBoundary;
406 }
407 
408 
409 const Position
411  return myOffset;
412 }
413 
414 
415 const Position
417  return myOffset;
418 }
419 
420 
421 const std::string&
423  return myProjString;
424 }
425 
426 const std::string
428  if (myProjection == 0) {
429  return "";
430  } else {
431  return pj_get_def(myProjection, 0);
432  }
433 }
434 
435 void
437  if (myNumLoaded == 0) {
439  if (lefthand) {
440  myFinal.myOffset.mul(1, -1);
441  }
442  } else {
443  if (lefthand) {
444  myProcessing.myOffset.mul(1, -1);
445  }
447  // prefer options over loaded location
449  // let offset and boundary lead back to the original coords of the loaded data
452  // the new boundary (updated during loading)
454  }
455  if (lefthand) {
457  }
458 }
459 
460 
461 void
463  myNumLoaded++;
464  if (myNumLoaded > 1) {
465  WRITE_WARNING("Ignoring loaded location attribute nr. " + toString(myNumLoaded) + " for tracking of original location");
466  } else {
467  myLoaded = loaded;
468  }
469 }
470 
471 
472 void
474  myNumLoaded = 0;
475 }
476 
477 
478 void
483  if (myFinal.usingGeoProjection()) {
485  }
487  if (myFinal.usingGeoProjection()) {
488  into.setPrecision();
489  }
491  into.closeTag();
492  into.lf();
493 }
494 
495 
496 
497 /****************************************************************************/
498 
void doRegister(const std::string &name, Option *v)
Adds an option under the given name.
Definition: OptionsCont.cpp:75
OutputDevice & writeAttr(const SumoXMLAttr attr, const T &val)
writes a named attribute
Definition: OutputDevice.h:256
static void writeLocation(OutputDevice &into)
writes the location element
static GeoConvHelper myProcessing
coordinate transformation to use for input conversion and processing
~GeoConvHelper()
Destructor.
const Boundary & getConvBoundary() const
Returns the converted boundary.
void add(const Position &pos)
Adds the given position to this one.
Definition: Position.h:126
Position myOffset
The offset to apply.
static void computeFinal(bool lefthand=false)
compute the location attributes which will be used for output based on the loaded location data...
bool x2cartesian(Position &from, bool includeInBoundary=true)
Converts the given coordinate into a cartesian and optionally update myConvBoundary.
double y() const
Returns the y-position.
Definition: Position.h:61
bool usingGeoProjection() const
Returns whether a transformation from geo to metric coordinates will be performed.
void moveby(double x, double y, double z=0)
Moves the boundary by the given amount.
Definition: Boundary.cpp:369
double mySin
The rotation to apply to geo-coordinates.
Boundary myOrigBoundary
The boundary before conversion (x2cartesian)
static void setLoaded(const GeoConvHelper &loaded)
sets the coordinate transformation loaded from a location element
double x() const
Returns the x-position.
Definition: Position.h:56
void setPrecision(int precision=gPrecision)
Sets the precison or resets it to default.
const Boundary & getOrigBoundary() const
Returns the original boundary.
static GeoConvHelper myLoaded
coordinate transformation loaded from a location element
bool getBool(const std::string &name) const
Returns the boolean-value of the named option (only for Option_Bool)
const std::string & getProjString() const
Returns the original projection definition.
static void resetLoaded()
resets loaded location elements
bool isDefault(const std::string &name) const
Returns the information whether the named option has still the default value.
void set(double x, double y)
set positions x and y
Definition: Position.h:86
bool myUseInverseProjection
Information whether inverse projection shall be used.
void moveConvertedBy(double x, double y)
Shifts the converted boundary by the given amounts.
A class that stores a 2D geometrical boundary.
Definition: Boundary.h:42
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:242
void addSynonyme(const std::string &name1, const std::string &name2, bool isDeprecated=false)
Adds a synonyme for an options name (any order)
Definition: OptionsCont.cpp:96
void cartesian2geo(Position &cartesian) const
Converts the given cartesian (shifted) position to its geo (lat/long) representation.
double myGeoScale
The scaling to apply to geo-coordinates.
static methods for processing the coordinates conversion for the current net
Definition: GeoConvHelper.h:53
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:49
static GeoConvHelper myFinal
coordinate transformation to use for writing the location element and for tracking the original coord...
A point in 2D or 3D with translation and scaling methods.
Definition: Position.h:39
int gPrecisionGeo
Definition: StdDefs.cpp:28
ProjectionMethod myProjectionMethod
Information whether no projection shall be done.
std::string myProjString
A proj options string describing the proj.4-projection to use.
std::string getString(const std::string &name) const
Returns the string-value of the named option (only for Option_String)
bool exists(const std::string &name) const
Returns the information whether the named option is known.
static bool init(OptionsCont &oc)
Initialises the processing and the final instance using the given options.
void addOptionSubTopic(const std::string &topic)
Adds an option subtopic.
#define DEG2RAD(x)
Definition: GeomHelper.h:38
static void addProjectionOptions(OptionsCont &oc)
Adds projection options to the given container.
GeoConvHelper(OptionsCont &oc)
Constructor based on the stored options.
double getFloat(const std::string &name) const
Returns the double-value of the named option (only for Option_Float)
Boundary myConvBoundary
The boundary after conversion (x2cartesian)
#define WRITE_ERROR(msg)
Definition: MsgHandler.h:248
static int myNumLoaded
the numer of coordinate transformations loaded from location elements
bool operator==(const GeoConvHelper &o) const
void flipY()
flips ymin and ymax
Definition: Boundary.cpp:323
A storage for options typed value containers)
Definition: OptionsCont.h:92
bool usingInverseGeoProjection() const
Returns the information whether an inverse transformation will happen.
const Position getOffsetBase() const
Returns the network base.
const std::string getProjStringExpaneded() const
Returns the expanded projection definition.
Static storage of an output device and its base (abstract) implementation.
Definition: OutputDevice.h:64
bool x2cartesian_const(Position &from) const
Converts the given coordinate into a cartesian using the previous initialisation. ...
bool closeTag(const std::string &comment="")
Closes the most recently opened tag and optionally adds a comment.
void mul(double val)
Multiplies both positions with the given value.
Definition: Position.h:106
const Position getOffset() const
Returns the network offset.
void addDescription(const std::string &name, const std::string &subtopic, const std::string &description)
Adds a description for an option.
void add(double x, double y, double z=0)
Makes the boundary include the given coordinate.
Definition: Boundary.cpp:79
#define HAVE_PROJ
Definition: config.h:74
OutputDevice & openTag(const std::string &xmlElement)
Opens an XML tag.
void lf()
writes a line feed if applicable
Definition: OutputDevice.h:234
GeoConvHelper & operator=(const GeoConvHelper &)
make assignment operator private
void sub(double dx, double dy)
Substracts the given position from this one.
Definition: Position.h:146
#define FALLTHROUGH
Definition: StdDefs.h:38