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 225653 : GeoConvHelper::GeoConvHelper(const std::string& proj, const Position& offset,
52 225653 : const Boundary& orig, const Boundary& conv, double scale, double rot, bool inverse, bool flatten):
53 225653 : myProjString(proj),
54 : #ifdef PROJ_API_FILE
55 225653 : myProjection(nullptr),
56 225653 : myInverseProjection(nullptr),
57 225653 : myGeoProjection(nullptr),
58 : #endif
59 225653 : myOffset(offset),
60 225653 : myGeoScale(scale),
61 225653 : mySin(sin(DEG2RAD(-rot))), // rotate clockwise
62 225653 : myCos(cos(DEG2RAD(-rot))),
63 225653 : myProjectionMethod(NONE),
64 225653 : myUseInverseProjection(inverse),
65 225653 : myFlatten(flatten),
66 : myOrigBoundary(orig),
67 : myConvBoundary(conv) {
68 225653 : if (proj == "!") {
69 : myProjectionMethod = NONE;
70 12406 : } else if (proj == "-") {
71 3 : myProjectionMethod = SIMPLE;
72 12403 : } else if (proj == "UTM") {
73 260 : myProjectionMethod = UTM;
74 12143 : } else if (proj == "DHDN") {
75 0 : myProjectionMethod = DHDN;
76 12143 : } else if (proj == "DHDN_UTM") {
77 0 : myProjectionMethod = DHDN_UTM;
78 : #ifdef PROJ_API_FILE
79 : } else {
80 12143 : myProjectionMethod = PROJ;
81 12143 : initProj(proj);
82 12143 : 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 12143 : if (myProjection == nullptr) {
92 0 : throw ProcessError(TL("Could not build projection!"));
93 : }
94 : #endif
95 : }
96 225653 : }
97 :
98 :
99 : #ifdef PROJ_API_FILE
100 : void
101 12143 : GeoConvHelper::initProj(const std::string& proj) {
102 : #ifdef PROJ_VERSION_MAJOR
103 12143 : myProjection = proj_create(PJ_DEFAULT_CTX, proj.c_str());
104 12143 : checkError(myProjection);
105 : #else
106 : myProjection = pj_init_plus(proj.c_str());
107 : #endif
108 12143 : }
109 : #endif
110 :
111 :
112 225649 : GeoConvHelper::~GeoConvHelper() {
113 : #ifdef PROJ_API_FILE
114 225649 : if (myProjection != nullptr) {
115 : #ifdef PROJ_VERSION_MAJOR
116 36177 : proj_destroy(myProjection);
117 : #else
118 : pj_free(myProjection);
119 : #endif
120 : }
121 225649 : if (myInverseProjection != nullptr) {
122 : #ifdef PROJ_VERSION_MAJOR
123 0 : proj_destroy(myInverseProjection);
124 : #else
125 : pj_free(myInverseProjection);
126 : #endif
127 : }
128 225649 : 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 225649 : }
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 112687 : GeoConvHelper::operator=(const GeoConvHelper& orig) {
156 112687 : myProjString = orig.myProjString;
157 112687 : myOffset = orig.myOffset;
158 112687 : myProjectionMethod = orig.myProjectionMethod;
159 : myOrigBoundary = orig.myOrigBoundary;
160 : myConvBoundary = orig.myConvBoundary;
161 112687 : myGeoScale = orig.myGeoScale;
162 112687 : myCos = orig.myCos;
163 112687 : mySin = orig.mySin;
164 112687 : myUseInverseProjection = orig.myUseInverseProjection;
165 112687 : myFlatten = orig.myFlatten;
166 : #ifdef PROJ_API_FILE
167 112687 : 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 112687 : 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 112687 : 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 112687 : if (orig.myProjection != nullptr) {
192 : #ifdef PROJ_VERSION_MAJOR
193 23810 : 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 112687 : 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 112687 : 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 112687 : return *this;
214 : }
215 :
216 :
217 : bool
218 1926 : GeoConvHelper::init(OptionsCont& oc) {
219 1926 : std::string proj = "!"; // the default
220 1926 : double scale = oc.getFloat("proj.scale");
221 1926 : double rot = oc.getFloat("proj.rotate");
222 3852 : Position offset = Position(oc.getFloat("offset.x"), oc.getFloat("offset.y"), oc.getFloat("offset.z"));
223 3852 : bool inverse = oc.exists("proj.inverse") && oc.getBool("proj.inverse");
224 3852 : bool flatten = oc.exists("flatten") && oc.getBool("flatten");
225 :
226 3852 : if (oc.getBool("simple-projection")) {
227 : proj = "-";
228 : }
229 :
230 : #ifdef PROJ_API_FILE
231 1926 : 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 3852 : unsigned numProjections = oc.getBool("simple-projection") + oc.getBool("proj.utm") + oc.getBool("proj.dhdn") + oc.getBool("proj.dhdnutm") + (oc.getString("proj").length() > 1);
236 1926 : if (numProjections > 1) {
237 0 : WRITE_ERROR(TL("The projection method needs to be uniquely defined."));
238 0 : return false;
239 : }
240 :
241 3852 : if (oc.getBool("proj.utm")) {
242 : proj = "UTM";
243 3334 : } else if (oc.getBool("proj.dhdn")) {
244 : proj = "DHDN";
245 3334 : } else if (oc.getBool("proj.dhdnutm")) {
246 : proj = "DHDN_UTM";
247 3334 : } else if (!oc.isDefault("proj")) {
248 4 : proj = oc.getString("proj");
249 : }
250 : #endif
251 1926 : myProcessing = GeoConvHelper(proj, offset, Boundary(), Boundary(), scale, rot, inverse, flatten);
252 1926 : myFinal = myProcessing;
253 : return true;
254 : }
255 :
256 :
257 : void
258 52974 : GeoConvHelper::init(const std::string& proj, const Position& offset, const Boundary& orig,
259 : const Boundary& conv, double scale) {
260 52974 : myProcessing = GeoConvHelper(proj, offset, orig, conv, scale);
261 52974 : myProcessing.resolveAbstractProjection();
262 52974 : myFinal = myProcessing;
263 52974 : }
264 :
265 : void
266 54152 : GeoConvHelper::resolveAbstractProjection() {
267 : #ifdef PROJ_API_FILE
268 54152 : if (myProjection == nullptr &&
269 42353 : 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 54152 : }
281 :
282 : void
283 1961 : GeoConvHelper::addProjectionOptions(OptionsCont& oc) {
284 1961 : oc.addOptionSubTopic("Projection");
285 :
286 1961 : oc.doRegister("simple-projection", new Option_Bool(false));
287 3922 : oc.addSynonyme("simple-projection", "proj.simple", true);
288 3922 : oc.addDescription("simple-projection", "Projection", TL("Uses a simple method for projection"));
289 :
290 1961 : oc.doRegister("proj.scale", new Option_Float(1.0));
291 3922 : oc.addDescription("proj.scale", "Projection", TL("Scaling factor for input coordinates"));
292 :
293 1961 : oc.doRegister("proj.rotate", new Option_Float(0.0));
294 3922 : oc.addDescription("proj.rotate", "Projection", TL("Rotation (clockwise degrees) for input coordinates"));
295 :
296 : #ifdef PROJ_API_FILE
297 1961 : oc.doRegister("proj.utm", new Option_Bool(false));
298 3922 : oc.addDescription("proj.utm", "Projection", TL("Determine the UTM zone (for a universal transversal mercator projection based on the WGS84 ellipsoid)"));
299 :
300 1961 : oc.doRegister("proj.dhdn", new Option_Bool(false));
301 3922 : oc.addDescription("proj.dhdn", "Projection", "Determine the DHDN zone (for a transversal mercator projection based on the bessel ellipsoid, \"Gauss-Krueger\")");
302 :
303 3922 : oc.doRegister("proj", new Option_String("!"));
304 3922 : oc.addDescription("proj", "Projection", TL("Uses STR as proj.4 definition for projection"));
305 :
306 1961 : oc.doRegister("proj.inverse", new Option_Bool(false));
307 3922 : oc.addDescription("proj.inverse", "Projection", TL("Inverses projection"));
308 :
309 1961 : oc.doRegister("proj.dhdnutm", new Option_Bool(false));
310 3922 : oc.addDescription("proj.dhdnutm", "Projection", TL("Convert from Gauss-Krueger to UTM"));
311 : #endif // PROJ_API_FILE
312 1961 : }
313 :
314 :
315 : bool
316 451887 : GeoConvHelper::usingGeoProjection() const {
317 451887 : return myProjectionMethod != NONE;
318 : }
319 :
320 :
321 : bool
322 318 : GeoConvHelper::usingInverseGeoProjection() const {
323 318 : return myUseInverseProjection;
324 : }
325 :
326 :
327 : void
328 21050 : GeoConvHelper::cartesian2geo(Position& cartesian) const {
329 21050 : cartesian.sub(getOffsetBase());
330 21050 : if (myProjectionMethod == NONE) {
331 2398 : return;
332 : }
333 18652 : 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 18652 : PJ_COORD c = proj_coord(cartesian.x(), cartesian.y(), cartesian.z(), 0);
342 18652 : c = proj_trans(myProjection, PJ_INV, c);
343 18652 : checkError(myProjection);
344 18652 : 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);
350 : //!!! check pj_errno
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
361 : bool
362 278199 : GeoConvHelper::checkError(projPJ projection) const {
363 278199 : const int err_no = proj_context_errno(PJ_DEFAULT_CTX);
364 : const char* err_string = "";
365 278199 : if (err_no != 0) {
366 : #if PROJ_VERSION_MAJOR > 7
367 0 : 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 278199 : if (projection == nullptr) {
373 0 : if (err_no == 0) {
374 0 : WRITE_WARNING(TL("Failed to create transformation, reason unknown."));
375 : } else {
376 0 : WRITE_WARNINGF(TL("Failed to create transformation, %."), err_string);
377 : }
378 0 : return false;
379 : }
380 278199 : if (err_no != 0) {
381 0 : WRITE_WARNINGF(TL("Failed to transform, %."), err_string);
382 0 : return false;
383 : }
384 : return true;
385 : }
386 : #endif
387 : #endif
388 :
389 :
390 : bool
391 595234 : GeoConvHelper::x2cartesian(Position& from, bool includeInBoundary) {
392 595234 : if (includeInBoundary) {
393 470832 : myOrigBoundary.add(from);
394 : }
395 : // init projection parameter on first use
396 : #ifdef PROJ_API_FILE
397 595234 : if (myProjection == nullptr) {
398 348926 : double x = from.x() * myGeoScale;
399 348926 : switch (myProjectionMethod) {
400 0 : case DHDN_UTM: {
401 0 : int zone = (int)((x - 500000.) / 1000000.);
402 0 : if (zone < 1 || zone > 5) {
403 0 : WRITE_WARNING("Attempt to initialize DHDN_UTM-projection on invalid longitude " + toString(x));
404 0 : return false;
405 : }
406 0 : myProjString = "+proj=tmerc +lat_0=0 +lon_0=" + toString(3 * zone) +
407 0 : " +k=1 +x_0=" + toString(zone * 1000000 + 500000) +
408 0 : " +y_0=0 +ellps=bessel +datum=potsdam +units=m +no_defs";
409 : #ifdef PROJ_VERSION_MAJOR
410 0 : myInverseProjection = proj_create(PJ_DEFAULT_CTX, myProjString.c_str());
411 0 : checkError(myInverseProjection);
412 0 : myGeoProjection = proj_create(PJ_DEFAULT_CTX, "+proj=latlong +datum=WGS84");
413 0 : checkError(myGeoProjection);
414 : #else
415 : myInverseProjection = pj_init_plus(myProjString.c_str());
416 : myGeoProjection = pj_init_plus("+proj=latlong +datum=WGS84");
417 : #endif
418 0 : x = ((x - 500000.) / 1000000.) * 3; // continues with UTM
419 : }
420 : FALLTHROUGH;
421 246 : case UTM: {
422 246 : int zone = (int)(x + 180) / 6 + 1;
423 492 : myProjString = "+proj=utm +zone=" + toString(zone) +
424 246 : " +ellps=WGS84 +datum=WGS84 +units=m +no_defs";
425 : #ifdef PROJ_VERSION_MAJOR
426 246 : myProjection = proj_create(PJ_DEFAULT_CTX, myProjString.c_str());
427 246 : checkError(myProjection);
428 : #else
429 : myProjection = pj_init_plus(myProjString.c_str());
430 : #endif
431 : }
432 246 : break;
433 0 : case DHDN: {
434 0 : int zone = (int)(x / 3);
435 0 : if (zone < 1 || zone > 5) {
436 0 : WRITE_WARNING("Attempt to initialize DHDN-projection on invalid longitude " + toString(x));
437 0 : return false;
438 : }
439 0 : myProjString = "+proj=tmerc +lat_0=0 +lon_0=" + toString(3 * zone) +
440 0 : " +k=1 +x_0=" + toString(zone * 1000000 + 500000) +
441 0 : " +y_0=0 +ellps=bessel +datum=potsdam +units=m +no_defs";
442 : #ifdef PROJ_VERSION_MAJOR
443 0 : myProjection = proj_create(PJ_DEFAULT_CTX, myProjString.c_str());
444 0 : checkError(myProjection);
445 : #else
446 : myProjection = pj_init_plus(myProjString.c_str());
447 : #endif
448 : }
449 : break;
450 : default:
451 : break;
452 : }
453 : }
454 595234 : if (myInverseProjection != nullptr) {
455 : #ifdef PROJ_VERSION_MAJOR
456 0 : PJ_COORD c = proj_coord(from.x(), from.y(), from.z(), 0);
457 0 : c = proj_trans(myInverseProjection, PJ_INV, c);
458 0 : checkError(myInverseProjection);
459 0 : 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 595234 : bool ok = x2cartesian_const(from);
472 595234 : if (ok) {
473 595233 : if (includeInBoundary) {
474 470831 : myConvBoundary.add(from);
475 : }
476 : }
477 : return ok;
478 : }
479 :
480 :
481 : bool
482 595839 : GeoConvHelper::x2cartesian_const(Position& from) const {
483 595839 : const double x2 = from.x() * myGeoScale;
484 595839 : const double y2 = from.y() * myGeoScale;
485 595839 : double x = x2 * myCos - y2 * mySin;
486 595839 : double y = x2 * mySin + y2 * myCos;
487 595839 : if (myProjectionMethod == NONE) {
488 : // do nothing
489 247173 : } else if (myUseInverseProjection) {
490 0 : cartesian2geo(from);
491 : } else {
492 247173 : if (x > 180.1 || x < -180.1) {
493 0 : WRITE_WARNING("Invalid longitude " + toString(x));
494 0 : return false;
495 : }
496 247173 : if (y > 90.1 || y < -90.1) {
497 2 : WRITE_WARNING("Invalid latitude " + toString(y));
498 1 : return false;
499 : }
500 : #ifdef PROJ_API_FILE
501 247172 : if (myProjection != nullptr) {
502 : #ifdef PROJ_VERSION_MAJOR
503 247158 : PJ_COORD c = proj_coord(proj_torad(x), proj_torad(y), from.z(), 0);
504 247158 : c = proj_trans(myProjection, PJ_FWD, c);
505 247158 : checkError(myProjection);
506 247158 : x = c.xy.x;
507 247158 : 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);
513 : //!!! check pj_errno
514 : x = p.u;
515 : y = p.v;
516 : #endif
517 : }
518 : #endif
519 247172 : if (myProjectionMethod == SIMPLE) {
520 : // Sinusoidal projection (https://en.wikipedia.org/wiki/Sinusoidal_projection)
521 14 : x *= 111320. * cos(DEG2RAD(y));
522 14 : y *= 111136.;
523 : //!!! recheck whether the axes are mirrored
524 : }
525 : }
526 595838 : if (x > std::numeric_limits<double>::max() ||
527 595838 : y > std::numeric_limits<double>::max()) {
528 : return false;
529 : }
530 : from.set(x, y);
531 : from.add(myOffset);
532 595838 : if (myFlatten) {
533 : from.setz(0);
534 : }
535 : return true;
536 : }
537 :
538 :
539 : void
540 1246 : GeoConvHelper::moveConvertedBy(double x, double y) {
541 : myOffset.add(x, y);
542 1246 : myConvBoundary.moveby(x, y);
543 1246 : }
544 :
545 :
546 : const Boundary&
547 4441 : GeoConvHelper::getOrigBoundary() const {
548 4441 : return myOrigBoundary;
549 : }
550 :
551 :
552 : const Boundary&
553 16857 : GeoConvHelper::getConvBoundary() const {
554 16857 : return myConvBoundary;
555 : }
556 :
557 :
558 : const Position
559 2434 : GeoConvHelper::getOffset() const {
560 2434 : return myOffset;
561 : }
562 :
563 :
564 : const Position
565 24587 : GeoConvHelper::getOffsetBase() const {
566 24587 : return myOffset;
567 : }
568 :
569 :
570 : const std::string&
571 3503 : GeoConvHelper::getProjString() const {
572 3503 : return myProjString;
573 : }
574 :
575 :
576 : void
577 1736 : GeoConvHelper::computeFinal(bool lefthand) {
578 1736 : if (myNumLoaded == 0) {
579 800 : myFinal = myProcessing;
580 800 : if (lefthand) {
581 : myFinal.myOffset.mul(1, -1);
582 : }
583 : } else {
584 936 : if (lefthand) {
585 : myProcessing.myOffset.mul(1, -1);
586 : }
587 3744 : myFinal = GeoConvHelper(
588 : // prefer options over loaded location
589 1872 : myProcessing.usingGeoProjection() ? myProcessing.getProjString() : myLoaded.getProjString(),
590 : // let offset and boundary lead back to the original coords of the loaded data
591 1872 : myProcessing.getOffset() + myLoaded.getOffset(),
592 : myLoaded.getOrigBoundary(),
593 : // the new boundary (updated during loading)
594 936 : myProcessing.getConvBoundary());
595 : }
596 945 : if (lefthand) {
597 23 : myFinal.myConvBoundary.flipY();
598 : }
599 1736 : }
600 :
601 :
602 : void
603 1162 : GeoConvHelper::setLoaded(const GeoConvHelper& loaded) {
604 1162 : myNumLoaded++;
605 1162 : if (myNumLoaded > 1) {
606 22 : WRITE_WARNINGF(TL("Ignoring loaded location attribute nr. % for tracking of original location"), toString(myNumLoaded));
607 : } else {
608 1151 : myLoaded = loaded;
609 : }
610 1162 : }
611 :
612 :
613 : void
614 522 : GeoConvHelper::setLoadedPlain(const std::string& nodFile, const GeoConvHelper& loaded) {
615 1044 : myLoadedPlain[nodFile] = {loaded.getProjString(), loaded.getOffset()};
616 522 : }
617 :
618 :
619 : GeoConvHelper*
620 1292 : GeoConvHelper::getLoadedPlain(const std::string& plainFile, const std::string& suffix) {
621 3876 : std::string nodFile = StringUtils::replace(plainFile, suffix, ".nod.xml");
622 : auto it = myLoadedPlain.find(nodFile);
623 1292 : if (it != myLoadedPlain.end()) {
624 641 : return new GeoConvHelper(it->second.first, it->second.second, Boundary(), Boundary());
625 : } else {
626 : return nullptr;
627 : }
628 : }
629 :
630 :
631 : void
632 0 : GeoConvHelper::resetLoaded() {
633 0 : myNumLoaded = 0;
634 : myLoadedPlain.clear();
635 0 : }
636 :
637 :
638 : void
639 1809 : GeoConvHelper::writeLocation(OutputDevice& into) {
640 1809 : into.openTag(SUMO_TAG_LOCATION);
641 1809 : into.writeAttr(SUMO_ATTR_NET_OFFSET, myFinal.getOffsetBase());
642 1809 : into.writeAttr(SUMO_ATTR_CONV_BOUNDARY, myFinal.getConvBoundary());
643 1809 : if (myFinal.usingGeoProjection()) {
644 459 : into.setPrecision(gPrecisionGeo);
645 : }
646 1809 : into.writeAttr(SUMO_ATTR_ORIG_BOUNDARY, myFinal.getOrigBoundary());
647 1809 : if (myFinal.usingGeoProjection()) {
648 459 : into.setPrecision();
649 : }
650 1809 : into.writeAttr(SUMO_ATTR_ORIG_PROJ, myFinal.getProjString());
651 1809 : into.closeTag();
652 1809 : into.lf();
653 1809 : }
654 :
655 :
656 : /****************************************************************************/
|