Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
GeoConvHelper.cpp
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3// Copyright (C) 2001-2024 German Aerospace Center (DLR) and others.
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// https://www.eclipse.org/legal/epl-2.0/
7// This Source Code may also be made available under the following Secondary
8// Licenses when the conditions for such availability set forth in the Eclipse
9// Public License 2.0 are satisfied: GNU General Public License, version 2
10// or later which is available at
11// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13/****************************************************************************/
20// static methods for processing the coordinates conversion for the current net
21/****************************************************************************/
22#include <config.h>
23
24#include <map>
25#include <cmath>
26#include <cassert>
27#include <climits>
28#include <regex>
34#include "GeoConvHelper.h"
35
36
37// ===========================================================================
38// static member variables
39// ===========================================================================
40
45std::map<std::string, std::pair<std::string, Position> > GeoConvHelper::myLoadedPlain;
46
47// ===========================================================================
48// method definitions
49// ===========================================================================
50
51GeoConvHelper::GeoConvHelper(const std::string& proj, const Position& offset,
52 const Boundary& orig, const Boundary& conv, double scale, double rot, bool inverse, bool flatten):
53 myProjString(proj),
54#ifdef PROJ_API_FILE
55 myProjection(nullptr),
56 myInverseProjection(nullptr),
57 myGeoProjection(nullptr),
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 myFlatten(flatten),
66 myOrigBoundary(orig),
67 myConvBoundary(conv) {
68 if (proj == "!") {
70 } else if (proj == "-") {
72 } else if (proj == "UTM") {
74 } else if (proj == "DHDN") {
76 } else if (proj == "DHDN_UTM") {
78#ifdef PROJ_API_FILE
79 } else {
81 initProj(proj);
82 if (myProjection == nullptr) {
83 // avoid error about missing datum shift file
84 myProjString = std::regex_replace(proj, std::regex("\\+geoidgrids[^ ]*"), std::string(""));
85 myProjString = std::regex_replace(myProjString, std::regex("\\+step \\+proj=vgridshift \\+grids[^ ]*"), std::string(""));
86 if (myProjString != proj) {
87 WRITE_WARNING(TL("Ignoring geoidgrids and vgridshift in projection"));
88 initProj(myProjString);
89 }
90 }
91 if (myProjection == nullptr) {
92 throw ProcessError(TL("Could not build projection!"));
93 }
94#endif
95 }
96}
97
98
99#ifdef PROJ_API_FILE
100void
101GeoConvHelper::initProj(const std::string& proj) {
102#ifdef PROJ_VERSION_MAJOR
103 myProjection = proj_create(PJ_DEFAULT_CTX, proj.c_str());
104 checkError(myProjection);
105#else
106 myProjection = pj_init_plus(proj.c_str());
107#endif
108}
109#endif
110
111
113#ifdef PROJ_API_FILE
114 if (myProjection != nullptr) {
115#ifdef PROJ_VERSION_MAJOR
116 proj_destroy(myProjection);
117#else
118 pj_free(myProjection);
119#endif
120 }
121 if (myInverseProjection != nullptr) {
122#ifdef PROJ_VERSION_MAJOR
123 proj_destroy(myInverseProjection);
124#else
125 pj_free(myInverseProjection);
126#endif
127 }
128 if (myGeoProjection != nullptr) {
129#ifdef PROJ_VERSION_MAJOR
130 proj_destroy(myGeoProjection);
131#else
132 pj_free(myGeoProjection);
133#endif
134 }
135#endif
136}
137
138bool
153
157 myOffset = orig.myOffset;
161 myGeoScale = orig.myGeoScale;
162 myCos = orig.myCos;
163 mySin = orig.mySin;
165 myFlatten = orig.myFlatten;
166#ifdef PROJ_API_FILE
167 if (myProjection != nullptr) {
168#ifdef PROJ_VERSION_MAJOR
169 proj_destroy(myProjection);
170#else
171 pj_free(myProjection);
172#endif
173 myProjection = nullptr;
174 }
175 if (myInverseProjection != nullptr) {
176#ifdef PROJ_VERSION_MAJOR
177 proj_destroy(myInverseProjection);
178#else
179 pj_free(myInverseProjection);
180#endif
181 myInverseProjection = nullptr;
182 }
183 if (myGeoProjection != nullptr) {
184#ifdef PROJ_VERSION_MAJOR
185 proj_destroy(myGeoProjection);
186#else
187 pj_free(myGeoProjection);
188#endif
189 myGeoProjection = nullptr;
190 }
191 if (orig.myProjection != nullptr) {
192#ifdef PROJ_VERSION_MAJOR
193 myProjection = proj_create(PJ_DEFAULT_CTX, orig.myProjString.c_str());
194#else
195 myProjection = pj_init_plus(orig.myProjString.c_str());
196#endif
197 }
198 if (orig.myInverseProjection != nullptr) {
199#ifdef PROJ_VERSION_MAJOR
200 myInverseProjection = orig.myInverseProjection;
201#else
202 myInverseProjection = pj_init_plus(pj_get_def(orig.myInverseProjection, 0));
203#endif
204 }
205 if (orig.myGeoProjection != nullptr) {
206#ifdef PROJ_VERSION_MAJOR
207 myGeoProjection = orig.myGeoProjection;
208#else
209 myGeoProjection = pj_init_plus(pj_get_def(orig.myGeoProjection, 0));
210#endif
211 }
212#endif
213 return *this;
214}
215
216
217bool
219 std::string proj = "!"; // the default
220 double scale = oc.getFloat("proj.scale");
221 double rot = oc.getFloat("proj.rotate");
222 Position offset = Position(oc.getFloat("offset.x"), oc.getFloat("offset.y"), oc.getFloat("offset.z"));
223 bool inverse = oc.exists("proj.inverse") && oc.getBool("proj.inverse");
224 bool flatten = oc.exists("flatten") && oc.getBool("flatten");
225
226 if (oc.getBool("simple-projection")) {
227 proj = "-";
228 }
229
230#ifdef PROJ_API_FILE
231 if (oc.getBool("proj.inverse") && oc.getString("proj") == "!") {
232 WRITE_ERROR(TL("Inverse projection works only with explicit proj parameters."));
233 return false;
234 }
235 unsigned numProjections = oc.getBool("simple-projection") + oc.getBool("proj.utm") + oc.getBool("proj.dhdn") + oc.getBool("proj.dhdnutm") + (oc.getString("proj").length() > 1);
236 if (numProjections > 1) {
237 WRITE_ERROR(TL("The projection method needs to be uniquely defined."));
238 return false;
239 }
240
241 if (oc.getBool("proj.utm")) {
242 proj = "UTM";
243 } else if (oc.getBool("proj.dhdn")) {
244 proj = "DHDN";
245 } else if (oc.getBool("proj.dhdnutm")) {
246 proj = "DHDN_UTM";
247 } else if (!oc.isDefault("proj")) {
248 proj = oc.getString("proj");
249 }
250#endif
251 myProcessing = GeoConvHelper(proj, offset, Boundary(), Boundary(), scale, rot, inverse, flatten);
253 return true;
254}
255
256
257void
258GeoConvHelper::init(const std::string& proj, const Position& offset, const Boundary& orig,
259 const Boundary& conv, double scale) {
260 myProcessing = GeoConvHelper(proj, offset, orig, conv, scale);
263}
264
265void
267#ifdef PROJ_API_FILE
268 if (myProjection == nullptr &&
270 const std::string origProj = myProjString;
271 // try to initialized projection based on origBoundary
273 x2cartesian(tmp, false);
274 if (myProjection == nullptr) {
275 WRITE_WARNING("Failed to intialized projection '" + origProj + "' based on origBoundary centered on '" + toString(myOrigBoundary.getCenter()) + "'");
277 }
278 }
279#endif
280}
281
282void
284 oc.addOptionSubTopic("Projection");
285
286 oc.doRegister("simple-projection", new Option_Bool(false));
287 oc.addSynonyme("simple-projection", "proj.simple", true);
288 oc.addDescription("simple-projection", "Projection", TL("Uses a simple method for projection"));
289
290 oc.doRegister("proj.scale", new Option_Float(1.0));
291 oc.addDescription("proj.scale", "Projection", TL("Scaling factor for input coordinates"));
292
293 oc.doRegister("proj.rotate", new Option_Float(0.0));
294 oc.addDescription("proj.rotate", "Projection", TL("Rotation (clockwise degrees) for input coordinates"));
295
296#ifdef PROJ_API_FILE
297 oc.doRegister("proj.utm", new Option_Bool(false));
298 oc.addDescription("proj.utm", "Projection", TL("Determine the UTM zone (for a universal transversal mercator projection based on the WGS84 ellipsoid)"));
299
300 oc.doRegister("proj.dhdn", new Option_Bool(false));
301 oc.addDescription("proj.dhdn", "Projection", "Determine the DHDN zone (for a transversal mercator projection based on the bessel ellipsoid, \"Gauss-Krueger\")");
302
303 oc.doRegister("proj", new Option_String("!"));
304 oc.addDescription("proj", "Projection", TL("Uses STR as proj.4 definition for projection"));
305
306 oc.doRegister("proj.inverse", new Option_Bool(false));
307 oc.addDescription("proj.inverse", "Projection", TL("Inverses projection"));
308
309 oc.doRegister("proj.dhdnutm", new Option_Bool(false));
310 oc.addDescription("proj.dhdnutm", "Projection", TL("Convert from Gauss-Krueger to UTM"));
311#endif // PROJ_API_FILE
312}
313
314
315bool
319
320
321bool
325
326
327void
329 cartesian.sub(getOffsetBase());
330 if (myProjectionMethod == NONE) {
331 return;
332 }
333 if (myProjectionMethod == SIMPLE) {
334 const double y = cartesian.y() / 111136.;
335 const double x = cartesian.x() / 111320. / cos(DEG2RAD(y));
336 cartesian.set(x, y);
337 return;
338 }
339#ifdef PROJ_API_FILE
340#ifdef PROJ_VERSION_MAJOR
341 PJ_COORD c = proj_coord(cartesian.x(), cartesian.y(), cartesian.z(), 0);
342 c = proj_trans(myProjection, PJ_INV, c);
343 checkError(myProjection);
344 cartesian.set(proj_todeg(c.lp.lam), proj_todeg(c.lp.phi));
345#else
346 projUV p;
347 p.u = cartesian.x();
348 p.v = cartesian.y();
349 p = pj_inv(p, myProjection);
351 p.u *= RAD_TO_DEG;
352 p.v *= RAD_TO_DEG;
353 cartesian.set((double) p.u, (double) p.v);
354#endif
355#endif
356}
357
358
359#ifdef PROJ_API_FILE
360#ifdef PROJ_VERSION_MAJOR
361bool
362GeoConvHelper::checkError(projPJ projection) const {
363 const int err_no = proj_context_errno(PJ_DEFAULT_CTX);
364 const char* err_string = "";
365 if (err_no != 0) {
366#if PROJ_VERSION_MAJOR > 7
367 err_string = proj_context_errno_string(PJ_DEFAULT_CTX, err_no);
368#else
369 err_string = proj_errno_string(err_no);
370#endif
371 }
372 if (projection == nullptr) {
373 if (err_no == 0) {
374 WRITE_WARNING(TL("Failed to create transformation, reason unknown."));
375 } else {
376 WRITE_WARNINGF(TL("Failed to create transformation, %."), err_string);
377 }
378 return false;
379 }
380 if (err_no != 0) {
381 WRITE_WARNINGF(TL("Failed to transform, %."), err_string);
382 return false;
383 }
384 return true;
385}
386#endif
387#endif
388
389
390bool
391GeoConvHelper::x2cartesian(Position& from, bool includeInBoundary) {
392 if (includeInBoundary) {
393 myOrigBoundary.add(from);
394 }
395 // init projection parameter on first use
396#ifdef PROJ_API_FILE
397 if (myProjection == nullptr) {
398 double x = from.x() * myGeoScale;
399 switch (myProjectionMethod) {
400 case DHDN_UTM: {
401 int zone = (int)((x - 500000.) / 1000000.);
402 if (zone < 1 || zone > 5) {
403 WRITE_WARNING("Attempt to initialize DHDN_UTM-projection on invalid longitude " + toString(x));
404 return false;
405 }
406 myProjString = "+proj=tmerc +lat_0=0 +lon_0=" + toString(3 * zone) +
407 " +k=1 +x_0=" + toString(zone * 1000000 + 500000) +
408 " +y_0=0 +ellps=bessel +datum=potsdam +units=m +no_defs";
409#ifdef PROJ_VERSION_MAJOR
410 myInverseProjection = proj_create(PJ_DEFAULT_CTX, myProjString.c_str());
411 checkError(myInverseProjection);
412 myGeoProjection = proj_create(PJ_DEFAULT_CTX, "+proj=latlong +datum=WGS84");
413 checkError(myGeoProjection);
414#else
415 myInverseProjection = pj_init_plus(myProjString.c_str());
416 myGeoProjection = pj_init_plus("+proj=latlong +datum=WGS84");
417#endif
418 x = ((x - 500000.) / 1000000.) * 3; // continues with UTM
419 }
421 case UTM: {
422 int zone = (int)(x + 180) / 6 + 1;
423 myProjString = "+proj=utm +zone=" + toString(zone) +
424 " +ellps=WGS84 +datum=WGS84 +units=m +no_defs";
425#ifdef PROJ_VERSION_MAJOR
426 myProjection = proj_create(PJ_DEFAULT_CTX, myProjString.c_str());
427 checkError(myProjection);
428#else
429 myProjection = pj_init_plus(myProjString.c_str());
430#endif
431 }
432 break;
433 case DHDN: {
434 int zone = (int)(x / 3);
435 if (zone < 1 || zone > 5) {
436 WRITE_WARNING("Attempt to initialize DHDN-projection on invalid longitude " + toString(x));
437 return false;
438 }
439 myProjString = "+proj=tmerc +lat_0=0 +lon_0=" + toString(3 * zone) +
440 " +k=1 +x_0=" + toString(zone * 1000000 + 500000) +
441 " +y_0=0 +ellps=bessel +datum=potsdam +units=m +no_defs";
442#ifdef PROJ_VERSION_MAJOR
443 myProjection = proj_create(PJ_DEFAULT_CTX, myProjString.c_str());
444 checkError(myProjection);
445#else
446 myProjection = pj_init_plus(myProjString.c_str());
447#endif
448 }
449 break;
450 default:
451 break;
452 }
453 }
454 if (myInverseProjection != nullptr) {
455#ifdef PROJ_VERSION_MAJOR
456 PJ_COORD c = proj_coord(from.x(), from.y(), from.z(), 0);
457 c = proj_trans(myInverseProjection, PJ_INV, c);
458 checkError(myInverseProjection);
459 from.set(proj_todeg(c.lp.lam), proj_todeg(c.lp.phi));
460#else
461 double x = from.x();
462 double y = from.y();
463 if (pj_transform(myInverseProjection, myGeoProjection, 1, 1, &x, &y, nullptr)) {
464 WRITE_WARNINGF(TL("Could not transform (%,%)"), toString(x), toString(y));
465 }
466 from.set(double(x * RAD_TO_DEG), double(y * RAD_TO_DEG));
467#endif
468 }
469#endif
470 // perform conversion
471 bool ok = x2cartesian_const(from);
472 if (ok) {
473 if (includeInBoundary) {
474 myConvBoundary.add(from);
475 }
476 }
477 return ok;
478}
479
480
481bool
483 const double x2 = from.x() * myGeoScale;
484 const double y2 = from.y() * myGeoScale;
485 double x = x2 * myCos - y2 * mySin;
486 double y = x2 * mySin + y2 * myCos;
487 if (myProjectionMethod == NONE) {
488 // do nothing
489 } else if (myUseInverseProjection) {
490 cartesian2geo(from);
491 } else {
492 if (x > 180.1 || x < -180.1) {
493 WRITE_WARNING("Invalid longitude " + toString(x));
494 return false;
495 }
496 if (y > 90.1 || y < -90.1) {
497 WRITE_WARNING("Invalid latitude " + toString(y));
498 return false;
499 }
500#ifdef PROJ_API_FILE
501 if (myProjection != nullptr) {
502#ifdef PROJ_VERSION_MAJOR
503 PJ_COORD c = proj_coord(proj_torad(x), proj_torad(y), from.z(), 0);
504 c = proj_trans(myProjection, PJ_FWD, c);
505 checkError(myProjection);
506 x = c.xy.x;
507 y = c.xy.y;
508#else
509 projUV p;
510 p.u = x * DEG_TO_RAD;
511 p.v = y * DEG_TO_RAD;
512 p = pj_fwd(p, myProjection);
514 x = p.u;
515 y = p.v;
516#endif
517 }
518#endif
519 if (myProjectionMethod == SIMPLE) {
520 // Sinusoidal projection (https://en.wikipedia.org/wiki/Sinusoidal_projection)
521 x *= 111320. * cos(DEG2RAD(y));
522 y *= 111136.;
524 }
525 }
526 if (x > std::numeric_limits<double>::max() ||
527 y > std::numeric_limits<double>::max()) {
528 return false;
529 }
530 from.set(x, y);
531 from.add(myOffset);
532 if (myFlatten) {
533 from.setz(0);
534 }
535 return true;
536}
537
538
539void
541 myOffset.add(x, y);
543}
544
545
546const Boundary&
550
551
552const Boundary&
556
557
558const Position
560 return myOffset;
561}
562
563
564const Position
566 return myOffset;
567}
568
569
570const std::string&
574
575
576void
578 if (myNumLoaded == 0) {
580 if (lefthand) {
581 myFinal.myOffset.mul(1, -1);
582 }
583 } else {
584 if (lefthand) {
586 }
588 // prefer options over loaded location
590 // let offset and boundary lead back to the original coords of the loaded data
593 // the new boundary (updated during loading)
595 }
596 if (lefthand) {
598 }
599}
600
601
602void
604 myNumLoaded++;
605 if (myNumLoaded > 1) {
606 WRITE_WARNINGF(TL("Ignoring loaded location attribute nr. % for tracking of original location"), toString(myNumLoaded));
607 } else {
608 myLoaded = loaded;
609 }
610}
611
612
613void
614GeoConvHelper::setLoadedPlain(const std::string& nodFile, const GeoConvHelper& loaded) {
615 myLoadedPlain[nodFile] = {loaded.getProjString(), loaded.getOffset()};
616}
617
618
620GeoConvHelper::getLoadedPlain(const std::string& plainFile, const std::string& suffix) {
621 std::string nodFile = StringUtils::replace(plainFile, suffix, ".nod.xml");
622 auto it = myLoadedPlain.find(nodFile);
623 if (it != myLoadedPlain.end()) {
624 return new GeoConvHelper(it->second.first, it->second.second, Boundary(), Boundary());
625 } else {
626 return nullptr;
627 }
628}
629
630
631void
636
637
638void
654
655
656/****************************************************************************/
#define DEG2RAD(x)
Definition GeomHelper.h:35
#define WRITE_WARNINGF(...)
Definition MsgHandler.h:296
#define WRITE_ERROR(msg)
Definition MsgHandler.h:304
#define WRITE_WARNING(msg)
Definition MsgHandler.h:295
#define TL(string)
Definition MsgHandler.h:315
@ SUMO_TAG_LOCATION
@ SUMO_ATTR_CONV_BOUNDARY
@ SUMO_ATTR_NET_OFFSET
@ SUMO_ATTR_ORIG_BOUNDARY
@ SUMO_ATTR_ORIG_PROJ
int gPrecisionGeo
Definition StdDefs.cpp:27
#define FALLTHROUGH
Definition StdDefs.h:35
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition ToString.h:46
A class that stores a 2D geometrical boundary.
Definition Boundary.h:39
Position getCenter() const
Returns the center of the boundary.
Definition Boundary.cpp:112
void add(double x, double y, double z=0)
Makes the boundary include the given coordinate.
Definition Boundary.cpp:78
void moveby(double x, double y, double z=0)
Moves the boundary by the given amount.
Definition Boundary.cpp:436
void flipY()
flips ymin and ymax
Definition Boundary.cpp:375
static methods for processing the coordinates conversion for the current net
static void resetLoaded()
resets loaded location elements
static void setLoadedPlain(const std::string &nodFile, const GeoConvHelper &loaded)
registers the coordinate transformation as having been loaded from the given file
const Position getOffset() const
Returns the network offset.
static void writeLocation(OutputDevice &into)
writes the location element
Boundary myOrigBoundary
The boundary before conversion (x2cartesian)
static void addProjectionOptions(OptionsCont &oc)
Adds projection options to the given container.
bool x2cartesian(Position &from, bool includeInBoundary=true)
Converts the given coordinate into a cartesian and optionally update myConvBoundary.
GeoConvHelper & operator=(const GeoConvHelper &)
make assignment operator private
static GeoConvHelper * getLoadedPlain(const std::string &plainFile, const std::string &suffix=".edg.xml")
ProjectionMethod myProjectionMethod
Information whether no projection shall be done.
void cartesian2geo(Position &cartesian) const
Converts the given cartesian (shifted) position to its geo (lat/long) representation.
GeoConvHelper(OptionsCont &oc)
Constructor based on the stored options.
Position myOffset
The offset to apply.
void moveConvertedBy(double x, double y)
Shifts the converted boundary by the given amounts.
bool usingInverseGeoProjection() const
Returns the information whether an inverse transformation will happen.
bool operator==(const GeoConvHelper &o) const
void resolveAbstractProjection()
init projString such as 'UTM' in loaded projection
const std::string & getProjString() const
Returns the original projection definition.
const Boundary & getOrigBoundary() const
Returns the original boundary.
double mySin
The rotation to apply to geo-coordinates.
static GeoConvHelper myLoaded
coordinate transformation loaded from a location element
static GeoConvHelper myFinal
coordinate transformation to use for writing the location element and for tracking the original coord...
static void setLoaded(const GeoConvHelper &loaded)
sets the coordinate transformation loaded from a location element
double myGeoScale
The scaling to apply to geo-coordinates.
const Position getOffsetBase() const
Returns the network base.
static bool init(OptionsCont &oc)
Initialises the processing and the final instance using the given options.
bool myFlatten
whether to discard z-data
bool myUseInverseProjection
Information whether inverse projection shall be used.
Boundary myConvBoundary
The boundary after conversion (x2cartesian)
static void computeFinal(bool lefthand=false)
compute the location attributes which will be used for output based on the loaded location data,...
bool usingGeoProjection() const
Returns whether a transformation from geo to metric coordinates will be performed.
const Boundary & getConvBoundary() const
Returns the converted boundary.
bool x2cartesian_const(Position &from) const
Converts the given coordinate into a cartesian using the previous initialisation.
~GeoConvHelper()
Destructor.
std::string myProjString
A proj options string describing the proj.4-projection to use.
static int myNumLoaded
the numer of coordinate transformations loaded from location elements
static std::map< std::string, std::pair< std::string, Position > > myLoadedPlain
the projections loaded from .nod.xml (to be re-used when loading edg.xml)
static GeoConvHelper myProcessing
coordinate transformation to use for input conversion and processing
A storage for options typed value containers)
Definition OptionsCont.h:89
void addDescription(const std::string &name, const std::string &subtopic, const std::string &description)
Adds a description for an option.
double getFloat(const std::string &name) const
Returns the double-value of the named option (only for Option_Float)
std::string getString(const std::string &name) const
Returns the string-value of the named option (only for Option_String)
void addSynonyme(const std::string &name1, const std::string &name2, bool isDeprecated=false)
Adds a synonyme for an options name (any order)
bool isDefault(const std::string &name) const
Returns the information whether the named option has still the default value.
void doRegister(const std::string &name, Option *o)
Adds an option under the given name.
bool exists(const std::string &name) const
Returns the information whether the named option is known.
void addOptionSubTopic(const std::string &topic)
Adds an option subtopic.
bool getBool(const std::string &name) const
Returns the boolean-value of the named option (only for Option_Bool)
Static storage of an output device and its base (abstract) implementation.
void lf()
writes a line feed if applicable
OutputDevice & writeAttr(const SumoXMLAttr attr, const T &val)
writes a named attribute
OutputDevice & openTag(const std::string &xmlElement)
Opens an XML tag.
bool closeTag(const std::string &comment="")
Closes the most recently opened tag and optionally adds a comment.
void setPrecision(int precision=gPrecision)
Sets the precision or resets it to default.
A point in 2D or 3D with translation and scaling methods.
Definition Position.h:37
void set(double x, double y)
set positions x and y
Definition Position.h:85
void sub(double dx, double dy)
Subtracts the given position from this one.
Definition Position.h:152
double x() const
Returns the x-position.
Definition Position.h:55
void add(const Position &pos)
Adds the given position to this one.
Definition Position.h:132
void setz(double z)
set position z
Definition Position.h:80
void mul(double val)
Multiplies position with the given value.
Definition Position.h:105
double z() const
Returns the z-position.
Definition Position.h:65
double y() const
Returns the y-position.
Definition Position.h:60
static std::string replace(std::string str, const std::string &what, const std::string &by)
Replaces all occurrences of the second string by the third string within the first string.