LCOV - code coverage report
Current view: top level - src/utils/geom - GeoConvHelper.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 76.2 % 290 221
Test Date: 2024-11-22 15:46:21 Functions: 96.3 % 27 26

            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              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1