Line data Source code
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 : /****************************************************************************/
14 : /// @file GeoConvHelper.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Michael Behrisch
18 : /// @date 2006-08-01
19 : ///
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>
29 : #include <utils/common/MsgHandler.h>
30 : #include <utils/common/ToString.h>
31 : #include <utils/geom/GeomHelper.h>
32 : #include <utils/options/OptionsCont.h>
33 : #include <utils/iodevices/OutputDevice.h>
34 : #include "GeoConvHelper.h"
35 :
36 :
37 : // ===========================================================================
38 : // static member variables
39 : // ===========================================================================
40 :
41 : GeoConvHelper GeoConvHelper::myProcessing("!", Position(), Boundary(), Boundary());
42 : GeoConvHelper GeoConvHelper::myLoaded("!", Position(), Boundary(), Boundary());
43 : GeoConvHelper GeoConvHelper::myFinal("!", Position(), Boundary(), Boundary());
44 : int GeoConvHelper::myNumLoaded = 0;
45 : std::map<std::string, std::pair<std::string, Position> > GeoConvHelper::myLoadedPlain;
46 :
47 : // ===========================================================================
48 : // method definitions
49 : // ===========================================================================
50 :
51 178880 : GeoConvHelper::GeoConvHelper(const std::string& proj, const Position& offset,
52 178880 : const Boundary& orig, const Boundary& conv, double scale, double rot, bool inverse, bool flatten):
53 178880 : myProjString(proj),
54 : #ifdef PROJ_API_FILE
55 178880 : myProjection(nullptr),
56 178880 : myInverseProjection(nullptr),
57 178880 : myGeoProjection(nullptr),
58 : #endif
59 178880 : myOffset(offset),
60 178880 : myGeoScale(scale),
61 178880 : mySin(sin(DEG2RAD(-rot))), // rotate clockwise
62 178880 : myCos(cos(DEG2RAD(-rot))),
63 178880 : myProjectionMethod(NONE),
64 178880 : myUseInverseProjection(inverse),
65 178880 : myFlatten(flatten),
66 178880 : myOrigBoundary(orig),
67 178880 : myConvBoundary(conv) {
68 178880 : if (proj == "!") {
69 173813 : myProjectionMethod = NONE;
70 5067 : } else if (proj == "-") {
71 3 : myProjectionMethod = SIMPLE;
72 5064 : } else if (proj == "UTM") {
73 265 : myProjectionMethod = UTM;
74 4799 : } else if (proj == "DHDN") {
75 0 : myProjectionMethod = DHDN;
76 4799 : } else if (proj == "DHDN_UTM") {
77 0 : myProjectionMethod = DHDN_UTM;
78 : #ifdef PROJ_API_FILE
79 : } else {
80 4799 : myProjectionMethod = PROJ;
81 4799 : initProj(proj);
82 4799 : if (myProjection == nullptr) {
83 : // avoid error about missing datum shift file
84 0 : myProjString = std::regex_replace(proj, std::regex("\\+geoidgrids[^ ]*"), std::string(""));
85 0 : myProjString = std::regex_replace(myProjString, std::regex("\\+step \\+proj=vgridshift \\+grids[^ ]*"), std::string(""));
86 0 : if (myProjString != proj) {
87 0 : WRITE_WARNING(TL("Ignoring geoidgrids and vgridshift in projection"));
88 0 : initProj(myProjString);
89 : }
90 : }
91 4799 : if (myProjection == nullptr) {
92 : // !!! check pj_errno
93 0 : throw ProcessError(TL("Could not build projection!"));
94 : }
95 : #endif
96 : }
97 178880 : }
98 :
99 :
100 : #ifdef PROJ_API_FILE
101 : void
102 4799 : GeoConvHelper::initProj(const std::string& proj) {
103 : #ifdef PROJ_VERSION_MAJOR
104 4799 : myProjection = proj_create(PJ_DEFAULT_CTX, proj.c_str());
105 : #else
106 : myProjection = pj_init_plus(proj.c_str());
107 : #endif
108 4799 : }
109 : #endif
110 :
111 :
112 178876 : GeoConvHelper::~GeoConvHelper() {
113 : #ifdef PROJ_API_FILE
114 178876 : if (myProjection != nullptr) {
115 : #ifdef PROJ_VERSION_MAJOR
116 14191 : proj_destroy(myProjection);
117 : #else
118 : pj_free(myProjection);
119 : #endif
120 : }
121 178876 : if (myInverseProjection != nullptr) {
122 : #ifdef PROJ_VERSION_MAJOR
123 0 : proj_destroy(myInverseProjection);
124 : #else
125 : pj_free(myInverseProjection);
126 : #endif
127 : }
128 178876 : if (myGeoProjection != nullptr) {
129 : #ifdef PROJ_VERSION_MAJOR
130 0 : proj_destroy(myGeoProjection);
131 : #else
132 : pj_free(myGeoProjection);
133 : #endif
134 : }
135 : #endif
136 178876 : }
137 :
138 : bool
139 2326 : GeoConvHelper::operator==(const GeoConvHelper& o) const {
140 : return (
141 2326 : myProjString == o.myProjString &&
142 370 : myOffset == o.myOffset &&
143 740 : myProjectionMethod == o.myProjectionMethod &&
144 370 : myOrigBoundary == o.myOrigBoundary &&
145 0 : myConvBoundary == o.myConvBoundary &&
146 0 : myGeoScale == o.myGeoScale &&
147 0 : myCos == o.myCos &&
148 0 : mySin == o.mySin &&
149 2326 : myUseInverseProjection == o.myUseInverseProjection &&
150 : myFlatten == o.myFlatten
151 2326 : );
152 : }
153 :
154 : GeoConvHelper&
155 88913 : GeoConvHelper::operator=(const GeoConvHelper& orig) {
156 88913 : myProjString = orig.myProjString;
157 88913 : myOffset = orig.myOffset;
158 88913 : myProjectionMethod = orig.myProjectionMethod;
159 : myOrigBoundary = orig.myOrigBoundary;
160 : myConvBoundary = orig.myConvBoundary;
161 88913 : myGeoScale = orig.myGeoScale;
162 88913 : myCos = orig.myCos;
163 88913 : mySin = orig.mySin;
164 88913 : myUseInverseProjection = orig.myUseInverseProjection;
165 88913 : myFlatten = orig.myFlatten;
166 : #ifdef PROJ_API_FILE
167 88913 : if (myProjection != nullptr) {
168 : #ifdef PROJ_VERSION_MAJOR
169 18 : proj_destroy(myProjection);
170 : #else
171 : pj_free(myProjection);
172 : #endif
173 18 : myProjection = nullptr;
174 : }
175 88913 : if (myInverseProjection != nullptr) {
176 : #ifdef PROJ_VERSION_MAJOR
177 0 : proj_destroy(myInverseProjection);
178 : #else
179 : pj_free(myInverseProjection);
180 : #endif
181 0 : myInverseProjection = nullptr;
182 : }
183 88913 : if (myGeoProjection != nullptr) {
184 : #ifdef PROJ_VERSION_MAJOR
185 0 : proj_destroy(myGeoProjection);
186 : #else
187 : pj_free(myGeoProjection);
188 : #endif
189 0 : myGeoProjection = nullptr;
190 : }
191 88913 : if (orig.myProjection != nullptr) {
192 : #ifdef PROJ_VERSION_MAJOR
193 9163 : 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 88913 : if (orig.myInverseProjection != nullptr) {
199 : #ifdef PROJ_VERSION_MAJOR
200 0 : myInverseProjection = orig.myInverseProjection;
201 : #else
202 : myInverseProjection = pj_init_plus(pj_get_def(orig.myInverseProjection, 0));
203 : #endif
204 : }
205 88913 : if (orig.myGeoProjection != nullptr) {
206 : #ifdef PROJ_VERSION_MAJOR
207 0 : myGeoProjection = orig.myGeoProjection;
208 : #else
209 : myGeoProjection = pj_init_plus(pj_get_def(orig.myGeoProjection, 0));
210 : #endif
211 : }
212 : #endif
213 88913 : return *this;
214 : }
215 :
216 :
217 : bool
218 2266 : GeoConvHelper::init(OptionsCont& oc) {
219 2266 : std::string proj = "!"; // the default
220 2266 : double scale = oc.getFloat("proj.scale");
221 2266 : double rot = oc.getFloat("proj.rotate");
222 4532 : Position offset = Position(oc.getFloat("offset.x"), oc.getFloat("offset.y"), oc.getFloat("offset.z"));
223 6798 : bool inverse = oc.exists("proj.inverse") && oc.getBool("proj.inverse");
224 6733 : bool flatten = oc.exists("flatten") && oc.getBool("flatten");
225 :
226 4532 : if (oc.getBool("simple-projection")) {
227 : proj = "-";
228 : }
229 :
230 : #ifdef PROJ_API_FILE
231 2266 : if (oc.getBool("proj.inverse") && oc.getString("proj") == "!") {
232 0 : WRITE_ERROR(TL("Inverse projection works only with explicit proj parameters."));
233 0 : return false;
234 : }
235 2266 : unsigned numProjections = oc.getBool("simple-projection") + oc.getBool("proj.utm") + oc.getBool("proj.dhdn") + oc.getBool("proj.dhdnutm") + (oc.getString("proj").length() > 1);
236 2266 : if (numProjections > 1) {
237 0 : WRITE_ERROR(TL("The projection method needs to be uniquely defined."));
238 0 : return false;
239 : }
240 :
241 4532 : if (oc.getBool("proj.utm")) {
242 : proj = "UTM";
243 4004 : } else if (oc.getBool("proj.dhdn")) {
244 : proj = "DHDN";
245 4004 : } else if (oc.getBool("proj.dhdnutm")) {
246 : proj = "DHDN_UTM";
247 4004 : } else if (!oc.isDefault("proj")) {
248 0 : proj = oc.getString("proj");
249 : }
250 : #endif
251 2266 : myProcessing = GeoConvHelper(proj, offset, Boundary(), Boundary(), scale, rot, inverse, flatten);
252 2266 : myFinal = myProcessing;
253 : return true;
254 : }
255 :
256 :
257 : void
258 40530 : GeoConvHelper::init(const std::string& proj, const Position& offset, const Boundary& orig,
259 : const Boundary& conv, double scale) {
260 40530 : myProcessing = GeoConvHelper(proj, offset, orig, conv, scale);
261 40530 : myProcessing.resolveAbstractProjection();
262 40530 : myFinal = myProcessing;
263 40530 : }
264 :
265 : void
266 41995 : GeoConvHelper::resolveAbstractProjection() {
267 : #ifdef PROJ_API_FILE
268 41995 : if (myProjection == nullptr &&
269 37525 : myProjectionMethod != NONE && myProjectionMethod != SIMPLE) {
270 : const std::string origProj = myProjString;
271 : // try to initialized projection based on origBoundary
272 1 : Position tmp = myOrigBoundary.getCenter();
273 1 : x2cartesian(tmp, false);
274 1 : if (myProjection == nullptr) {
275 0 : WRITE_WARNING("Failed to intialized projection '" + origProj + "' based on origBoundary centered on '" + toString(myOrigBoundary.getCenter()) + "'");
276 0 : myProjectionMethod = NONE;
277 : }
278 : }
279 : #endif
280 41995 : }
281 :
282 : void
283 2311 : GeoConvHelper::addProjectionOptions(OptionsCont& oc) {
284 2311 : oc.addOptionSubTopic("Projection");
285 :
286 2311 : oc.doRegister("simple-projection", new Option_Bool(false));
287 4622 : oc.addSynonyme("simple-projection", "proj.simple", true);
288 4622 : oc.addDescription("simple-projection", "Projection", TL("Uses a simple method for projection"));
289 :
290 2311 : oc.doRegister("proj.scale", new Option_Float(1.0));
291 4622 : oc.addDescription("proj.scale", "Projection", TL("Scaling factor for input coordinates"));
292 :
293 2311 : oc.doRegister("proj.rotate", new Option_Float(0.0));
294 4622 : oc.addDescription("proj.rotate", "Projection", TL("Rotation (clockwise degrees) for input coordinates"));
295 :
296 : #ifdef PROJ_API_FILE
297 2311 : oc.doRegister("proj.utm", new Option_Bool(false));
298 4622 : oc.addDescription("proj.utm", "Projection", TL("Determine the UTM zone (for a universal transversal mercator projection based on the WGS84 ellipsoid)"));
299 :
300 2311 : oc.doRegister("proj.dhdn", new Option_Bool(false));
301 4622 : oc.addDescription("proj.dhdn", "Projection", "Determine the DHDN zone (for a transversal mercator projection based on the bessel ellipsoid, \"Gauss-Krueger\")");
302 :
303 4622 : oc.doRegister("proj", new Option_String("!"));
304 4622 : oc.addDescription("proj", "Projection", TL("Uses STR as proj.4 definition for projection"));
305 :
306 2311 : oc.doRegister("proj.inverse", new Option_Bool(false));
307 4622 : oc.addDescription("proj.inverse", "Projection", TL("Inverses projection"));
308 :
309 2311 : oc.doRegister("proj.dhdnutm", new Option_Bool(false));
310 4622 : oc.addDescription("proj.dhdnutm", "Projection", TL("Convert from Gauss-Krueger to UTM"));
311 : #endif // PROJ_API_FILE
312 2311 : }
313 :
314 :
315 : bool
316 683587 : GeoConvHelper::usingGeoProjection() const {
317 683587 : return myProjectionMethod != NONE;
318 : }
319 :
320 :
321 : bool
322 370 : GeoConvHelper::usingInverseGeoProjection() const {
323 370 : return myUseInverseProjection;
324 : }
325 :
326 :
327 : void
328 21896 : GeoConvHelper::cartesian2geo(Position& cartesian) const {
329 21896 : cartesian.sub(getOffsetBase());
330 21896 : if (myProjectionMethod == NONE) {
331 3010 : return;
332 : }
333 18886 : if (myProjectionMethod == SIMPLE) {
334 0 : const double y = cartesian.y() / 111136.;
335 0 : const double x = cartesian.x() / 111320. / cos(DEG2RAD(y));
336 : cartesian.set(x, y);
337 0 : return;
338 : }
339 : #ifdef PROJ_API_FILE
340 : #ifdef PROJ_VERSION_MAJOR
341 : PJ_COORD c;
342 : c.xy.x = cartesian.x();
343 : c.xy.y = cartesian.y();
344 18886 : c = proj_trans(myProjection, PJ_INV, c);
345 18886 : cartesian.set(proj_todeg(c.lp.lam), proj_todeg(c.lp.phi));
346 : #else
347 : projUV p;
348 : p.u = cartesian.x();
349 : p.v = cartesian.y();
350 : p = pj_inv(p, myProjection);
351 : //!!! check pj_errno
352 : p.u *= RAD_TO_DEG;
353 : p.v *= RAD_TO_DEG;
354 : cartesian.set((double) p.u, (double) p.v);
355 : #endif
356 : #endif
357 : }
358 :
359 :
360 : bool
361 878930 : GeoConvHelper::x2cartesian(Position& from, bool includeInBoundary) {
362 878930 : if (includeInBoundary) {
363 673906 : myOrigBoundary.add(from);
364 : }
365 : // init projection parameter on first use
366 : #ifdef PROJ_API_FILE
367 878930 : if (myProjection == nullptr) {
368 511341 : double x = from.x() * myGeoScale;
369 511341 : switch (myProjectionMethod) {
370 0 : case DHDN_UTM: {
371 0 : int zone = (int)((x - 500000.) / 1000000.);
372 0 : if (zone < 1 || zone > 5) {
373 0 : WRITE_WARNING("Attempt to initialize DHDN_UTM-projection on invalid longitude " + toString(x));
374 0 : return false;
375 : }
376 0 : myProjString = "+proj=tmerc +lat_0=0 +lon_0=" + toString(3 * zone) +
377 0 : " +k=1 +x_0=" + toString(zone * 1000000 + 500000) +
378 0 : " +y_0=0 +ellps=bessel +datum=potsdam +units=m +no_defs";
379 : #ifdef PROJ_VERSION_MAJOR
380 0 : myInverseProjection = proj_create(PJ_DEFAULT_CTX, myProjString.c_str());
381 0 : myGeoProjection = proj_create(PJ_DEFAULT_CTX, "+proj=latlong +datum=WGS84");
382 : #else
383 : myInverseProjection = pj_init_plus(myProjString.c_str());
384 : myGeoProjection = pj_init_plus("+proj=latlong +datum=WGS84");
385 : #endif
386 : //!!! check pj_errno
387 0 : x = ((x - 500000.) / 1000000.) * 3; // continues with UTM
388 : }
389 : FALLTHROUGH;
390 251 : case UTM: {
391 251 : int zone = (int)(x + 180) / 6 + 1;
392 502 : myProjString = "+proj=utm +zone=" + toString(zone) +
393 251 : " +ellps=WGS84 +datum=WGS84 +units=m +no_defs";
394 : #ifdef PROJ_VERSION_MAJOR
395 251 : myProjection = proj_create(PJ_DEFAULT_CTX, myProjString.c_str());
396 : #else
397 : myProjection = pj_init_plus(myProjString.c_str());
398 : #endif
399 : //!!! check pj_errno
400 : }
401 251 : break;
402 0 : case DHDN: {
403 0 : int zone = (int)(x / 3);
404 0 : if (zone < 1 || zone > 5) {
405 0 : WRITE_WARNING("Attempt to initialize DHDN-projection on invalid longitude " + toString(x));
406 0 : return false;
407 : }
408 0 : myProjString = "+proj=tmerc +lat_0=0 +lon_0=" + toString(3 * zone) +
409 0 : " +k=1 +x_0=" + toString(zone * 1000000 + 500000) +
410 0 : " +y_0=0 +ellps=bessel +datum=potsdam +units=m +no_defs";
411 : #ifdef PROJ_VERSION_MAJOR
412 0 : myProjection = proj_create(PJ_DEFAULT_CTX, myProjString.c_str());
413 : #else
414 : myProjection = pj_init_plus(myProjString.c_str());
415 : #endif
416 : //!!! check pj_errno
417 : }
418 0 : break;
419 : default:
420 : break;
421 : }
422 : }
423 878930 : if (myInverseProjection != nullptr) {
424 : #ifdef PROJ_VERSION_MAJOR
425 : PJ_COORD c;
426 : c.xy.x = from.x();
427 : c.xy.y = from.y();
428 0 : c = proj_trans(myInverseProjection, PJ_INV, c);
429 0 : from.set(proj_todeg(c.lp.lam), proj_todeg(c.lp.phi));
430 : #else
431 : double x = from.x();
432 : double y = from.y();
433 : if (pj_transform(myInverseProjection, myGeoProjection, 1, 1, &x, &y, nullptr)) {
434 : WRITE_WARNINGF(TL("Could not transform (%,%)"), toString(x), toString(y));
435 : }
436 : from.set(double(x * RAD_TO_DEG), double(y * RAD_TO_DEG));
437 : #endif
438 : }
439 : #endif
440 : // perform conversion
441 878930 : bool ok = x2cartesian_const(from);
442 878930 : if (ok) {
443 878929 : if (includeInBoundary) {
444 673905 : myConvBoundary.add(from);
445 : }
446 : }
447 : return ok;
448 : }
449 :
450 :
451 : bool
452 879220 : GeoConvHelper::x2cartesian_const(Position& from) const {
453 879220 : double x2 = from.x() * myGeoScale;
454 879220 : double y2 = from.y() * myGeoScale;
455 879220 : double x = x2 * myCos - y2 * mySin;
456 879220 : double y = x2 * mySin + y2 * myCos;
457 879220 : if (myProjectionMethod == NONE) {
458 : // do nothing
459 368144 : } else if (myUseInverseProjection) {
460 0 : cartesian2geo(from);
461 : } else {
462 368144 : if (x > 180.1 || x < -180.1) {
463 0 : WRITE_WARNING("Invalid longitude " + toString(x));
464 0 : return false;
465 : }
466 368144 : if (y > 90.1 || y < -90.1) {
467 2 : WRITE_WARNING("Invalid latitude " + toString(y));
468 1 : return false;
469 : }
470 : #ifdef PROJ_API_FILE
471 368143 : if (myProjection != nullptr) {
472 : #ifdef PROJ_VERSION_MAJOR
473 : PJ_COORD c;
474 368129 : c.lp.lam = proj_torad(x);
475 368129 : c.lp.phi = proj_torad(y);
476 368129 : c = proj_trans(myProjection, PJ_FWD, c);
477 : //!!! check pj_errno
478 368129 : x = c.xy.x;
479 368129 : y = c.xy.y;
480 : #else
481 : projUV p;
482 : p.u = x * DEG_TO_RAD;
483 : p.v = y * DEG_TO_RAD;
484 : p = pj_fwd(p, myProjection);
485 : //!!! check pj_errno
486 : x = p.u;
487 : y = p.v;
488 : #endif
489 : }
490 : #endif
491 368143 : if (myProjectionMethod == SIMPLE) {
492 : // Sinusoidal projection (https://en.wikipedia.org/wiki/Sinusoidal_projection)
493 14 : x *= 111320. * cos(DEG2RAD(y));
494 14 : y *= 111136.;
495 : //!!! recheck whether the axes are mirrored
496 : }
497 : }
498 879219 : if (x > std::numeric_limits<double>::max() ||
499 879219 : y > std::numeric_limits<double>::max()) {
500 : return false;
501 : }
502 : from.set(x, y);
503 : from.add(myOffset);
504 879219 : if (myFlatten) {
505 : from.setz(0);
506 : }
507 : return true;
508 : }
509 :
510 :
511 : void
512 1272 : GeoConvHelper::moveConvertedBy(double x, double y) {
513 : myOffset.add(x, y);
514 1272 : myConvBoundary.moveby(x, y);
515 1272 : }
516 :
517 :
518 : const Boundary&
519 4810 : GeoConvHelper::getOrigBoundary() const {
520 4810 : return myOrigBoundary;
521 : }
522 :
523 :
524 : const Boundary&
525 17414 : GeoConvHelper::getConvBoundary() const {
526 17414 : return myConvBoundary;
527 : }
528 :
529 :
530 : const Position
531 2583 : GeoConvHelper::getOffset() const {
532 2583 : return myOffset;
533 : }
534 :
535 :
536 : const Position
537 25719 : GeoConvHelper::getOffsetBase() const {
538 25719 : return myOffset;
539 : }
540 :
541 :
542 : const std::string&
543 3710 : GeoConvHelper::getProjString() const {
544 3710 : return myProjString;
545 : }
546 :
547 :
548 : void
549 1880 : GeoConvHelper::computeFinal(bool lefthand) {
550 1880 : if (myNumLoaded == 0) {
551 857 : myFinal = myProcessing;
552 857 : if (lefthand) {
553 : myFinal.myOffset.mul(1, -1);
554 : }
555 : } else {
556 1023 : if (lefthand) {
557 : myProcessing.myOffset.mul(1, -1);
558 : }
559 4092 : myFinal = GeoConvHelper(
560 : // prefer options over loaded location
561 2046 : myProcessing.usingGeoProjection() ? myProcessing.getProjString() : myLoaded.getProjString(),
562 : // let offset and boundary lead back to the original coords of the loaded data
563 2046 : myProcessing.getOffset() + myLoaded.getOffset(),
564 : myLoaded.getOrigBoundary(),
565 : // the new boundary (updated during loading)
566 1023 : myProcessing.getConvBoundary());
567 : }
568 1880 : if (lefthand) {
569 23 : myFinal.myConvBoundary.flipY();
570 : }
571 1880 : }
572 :
573 :
574 : void
575 1453 : GeoConvHelper::setLoaded(const GeoConvHelper& loaded) {
576 1453 : myNumLoaded++;
577 1453 : if (myNumLoaded > 1) {
578 24 : WRITE_WARNINGF(TL("Ignoring loaded location attribute nr. % for tracking of original location"), toString(myNumLoaded));
579 : } else {
580 1441 : myLoaded = loaded;
581 : }
582 1453 : }
583 :
584 :
585 : void
586 497 : GeoConvHelper::setLoadedPlain(const std::string& nodFile, const GeoConvHelper& loaded) {
587 1491 : myLoadedPlain[nodFile] = {loaded.getProjString(), loaded.getOffset()};
588 497 : }
589 :
590 :
591 : GeoConvHelper*
592 1092 : GeoConvHelper::getLoadedPlain(const std::string& edgFile) {
593 3276 : std::string nodFile = StringUtils::replace(edgFile, ".edg.xml", ".nod.xml");
594 : auto it = myLoadedPlain.find(nodFile);
595 1092 : if (it != myLoadedPlain.end()) {
596 465 : return new GeoConvHelper(it->second.first, it->second.second, Boundary(), Boundary());
597 : } else {
598 : return nullptr;
599 : }
600 : }
601 :
602 :
603 : void
604 0 : GeoConvHelper::resetLoaded() {
605 0 : myNumLoaded = 0;
606 : myLoadedPlain.clear();
607 0 : }
608 :
609 :
610 : void
611 1950 : GeoConvHelper::writeLocation(OutputDevice& into) {
612 1950 : into.openTag(SUMO_TAG_LOCATION);
613 1950 : into.writeAttr(SUMO_ATTR_NET_OFFSET, myFinal.getOffsetBase());
614 1950 : into.writeAttr(SUMO_ATTR_CONV_BOUNDARY, myFinal.getConvBoundary());
615 1950 : if (myFinal.usingGeoProjection()) {
616 478 : into.setPrecision(gPrecisionGeo);
617 : }
618 1950 : into.writeAttr(SUMO_ATTR_ORIG_BOUNDARY, myFinal.getOrigBoundary());
619 1950 : if (myFinal.usingGeoProjection()) {
620 478 : into.setPrecision();
621 : }
622 1950 : into.writeAttr(SUMO_ATTR_ORIG_PROJ, myFinal.getProjString());
623 1950 : into.closeTag();
624 1950 : into.lf();
625 1950 : }
626 :
627 :
628 : /****************************************************************************/
|