LCOV - code coverage report
Current view: top level - src/utils/geom - GeoConvHelper.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 75.3 % 304 229
Test Date: 2026-06-15 15:46:12 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-2026 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       210360 : GeoConvHelper::GeoConvHelper(const std::string& proj, const Position& offset,
      52       210360 :                              const Boundary& orig, const Boundary& conv, double scale, double rot, bool inverse, bool flatten):
      53       210360 :     myProjString(proj),
      54              : #ifdef PROJ_API_FILE
      55       210360 :     myProjection(nullptr),
      56       210360 :     myInverseProjection(nullptr),
      57       210360 :     myGeoProjection(nullptr),
      58       210360 :     myRadians(true),
      59              : #endif
      60       210360 :     myOffset(offset),
      61       210360 :     myGeoScale(scale),
      62       210360 :     mySin(sin(DEG2RAD(-rot))), // rotate clockwise
      63       210360 :     myCos(cos(DEG2RAD(-rot))),
      64       210360 :     myProjectionMethod(NONE),
      65       210360 :     myUseInverseProjection(inverse),
      66       210360 :     myFlatten(flatten),
      67              :     myOrigBoundary(orig),
      68              :     myConvBoundary(conv) {
      69              :     // older PROJ libraries fail to construct inverse projection if this
      70              :     // string is present
      71       631080 :     myProjString = StringUtils::replace(myProjString, "+type=crs", "");
      72              : 
      73       210360 :     if (proj == "!") {
      74       195264 :         myProjectionMethod = NONE;
      75        15096 :     } else if (proj == "-") {
      76            3 :         myProjectionMethod = SIMPLE;
      77        15093 :     } else if (proj == "UTM") {
      78          609 :         myProjectionMethod = UTM;
      79        14484 :     } else if (proj == "DHDN") {
      80            0 :         myProjectionMethod = DHDN;
      81        14484 :     } else if (proj == "DHDN_UTM") {
      82            0 :         myProjectionMethod = DHDN_UTM;
      83              : #ifdef PROJ_API_FILE
      84              :     } else {
      85        14484 :         myProjectionMethod = PROJ;
      86        14484 :         initProj(myProjString);
      87        14484 :         if (myProjection == nullptr) {
      88              :             // avoid error about missing datum shift file
      89            0 :             myProjString = std::regex_replace(proj, std::regex("\\+geoidgrids[^ ]*"), std::string(""));
      90            0 :             myProjString = std::regex_replace(myProjString, std::regex("\\+step \\+proj=vgridshift \\+grids[^ ]*"), std::string(""));
      91            0 :             if (myProjString != proj) {
      92            0 :                 WRITE_WARNING(TL("Ignoring geoidgrids and vgridshift in projection"));
      93            0 :                 initProj(myProjString);
      94              :             }
      95              :         }
      96        14484 :         if (myProjection == nullptr) {
      97            0 :             throw ProcessError(TL("Could not build projection!"));
      98              :         }
      99              : #endif
     100              :     }
     101       210360 : }
     102              : 
     103              : 
     104              : #ifdef PROJ_API_FILE
     105              : void
     106        43100 : GeoConvHelper::initProj(const std::string& proj) {
     107              : #ifdef PROJ_VERSION_MAJOR
     108        43100 :     myProjection = proj_create(PJ_DEFAULT_CTX, proj.c_str());
     109        43100 :     checkError(myProjection);
     110              : #if PROJ_VERSION_MAJOR > 5
     111        43100 :     if (myProjection != nullptr) {
     112        43100 :         const PJ_TYPE type = proj_get_type(myProjection);
     113        43100 :         if (type != PJ_TYPE_TRANSFORMATION
     114              :                 && type != PJ_TYPE_CONCATENATED_OPERATION
     115        43100 :                 && type != PJ_TYPE_OTHER_COORDINATE_OPERATION) {
     116              :             // handle PROJCS WKT (i.e. from Visum) which doesn't define a transformation but only CRS
     117            0 :             proj_destroy(myProjection);
     118            0 :             PJ_CONTEXT* ctx = proj_context_create();
     119            0 :             proj_context_use_proj4_init_rules(ctx, 1);
     120            0 :             myProjection = proj_create_crs_to_crs(ctx,
     121              :                                                   "+proj=longlat +datum=WGS84 +type=crs +no_defs",
     122              :                                                   proj.c_str(),
     123              :                                                   NULL);
     124            0 :             checkError(myProjection);
     125              :             // "modern" proj doesn't default to radians but traditional proj strings do
     126            0 :             myRadians = false;
     127              :             //auto tmp = proj_normalize_for_visualization(ctx, myProjection);
     128              :             //PJ_PROJ_INFO info = proj_pj_info(myProjection);
     129              :             //printf("ID init: %s\n", info.id);
     130              :             //printf("Description: %s\n", info.description);
     131              :             //printf("Definition: %s\n", info.definition);
     132              :             //printf("Has Inverse: %s\n", info.has_inverse ? "Yes" : "No");
     133              :         }
     134              :     }
     135              : #endif
     136              : #else
     137              :     myProjection = pj_init_plus(proj.c_str());
     138              : #endif
     139        43100 : }
     140              : #endif
     141              : 
     142              : 
     143       210360 : GeoConvHelper::~GeoConvHelper() {
     144              : #ifdef PROJ_API_FILE
     145       210360 :     if (myProjection != nullptr) {
     146              : #ifdef PROJ_VERSION_MAJOR
     147        43677 :         proj_destroy(myProjection);
     148              : #else
     149              :         pj_free(myProjection);
     150              : #endif
     151              :     }
     152       210360 :     if (myInverseProjection != nullptr) {
     153              : #ifdef PROJ_VERSION_MAJOR
     154            0 :         proj_destroy(myInverseProjection);
     155              : #else
     156              :         pj_free(myInverseProjection);
     157              : #endif
     158              :     }
     159       210360 :     if (myGeoProjection != nullptr) {
     160              : #ifdef PROJ_VERSION_MAJOR
     161            0 :         proj_destroy(myGeoProjection);
     162              : #else
     163              :         pj_free(myGeoProjection);
     164              : #endif
     165              :     }
     166              : #endif
     167       210360 : }
     168              : 
     169              : bool
     170         2612 : GeoConvHelper::operator==(const GeoConvHelper& o) const {
     171              :     return (
     172         2612 :                myProjString == o.myProjString &&
     173          370 :                myOffset == o.myOffset &&
     174          740 :                myProjectionMethod == o.myProjectionMethod &&
     175          370 :                myOrigBoundary == o.myOrigBoundary &&
     176            0 :                myConvBoundary == o.myConvBoundary &&
     177            0 :                myGeoScale == o.myGeoScale &&
     178            0 :                myCos == o.myCos &&
     179            0 :                mySin == o.mySin &&
     180         2612 :                myUseInverseProjection == o.myUseInverseProjection &&
     181              :                myFlatten == o.myFlatten
     182         2612 :            );
     183              : }
     184              : 
     185              : GeoConvHelper&
     186       102351 : GeoConvHelper::operator=(const GeoConvHelper& orig) {
     187       102351 :     myProjString = orig.myProjString;
     188       102351 :     myOffset = orig.myOffset;
     189       102351 :     myProjectionMethod = orig.myProjectionMethod;
     190              :     myOrigBoundary = orig.myOrigBoundary;
     191              :     myConvBoundary = orig.myConvBoundary;
     192       102351 :     myGeoScale = orig.myGeoScale;
     193       102351 :     myCos = orig.myCos;
     194       102351 :     mySin = orig.mySin;
     195       102351 :     myUseInverseProjection = orig.myUseInverseProjection;
     196       102351 :     myFlatten = orig.myFlatten;
     197              : #ifdef PROJ_API_FILE
     198       102351 :     if (myProjection != nullptr) {
     199              : #ifdef PROJ_VERSION_MAJOR
     200           18 :         proj_destroy(myProjection);
     201              : #else
     202              :         pj_free(myProjection);
     203              : #endif
     204           18 :         myProjection = nullptr;
     205              :     }
     206       102351 :     if (myInverseProjection != nullptr) {
     207              : #ifdef PROJ_VERSION_MAJOR
     208            0 :         proj_destroy(myInverseProjection);
     209              : #else
     210              :         pj_free(myInverseProjection);
     211              : #endif
     212            0 :         myInverseProjection = nullptr;
     213              :     }
     214       102351 :     if (myGeoProjection != nullptr) {
     215              : #ifdef PROJ_VERSION_MAJOR
     216            0 :         proj_destroy(myGeoProjection);
     217              : #else
     218              :         pj_free(myGeoProjection);
     219              : #endif
     220            0 :         myGeoProjection = nullptr;
     221              :     }
     222       102351 :     if (orig.myProjection != nullptr) {
     223        28616 :         initProj(myProjString);
     224              :     }
     225       102351 :     if (orig.myInverseProjection != nullptr) {
     226              : #ifdef PROJ_VERSION_MAJOR
     227            0 :         myInverseProjection = orig.myInverseProjection;
     228              : #else
     229              :         myInverseProjection = pj_init_plus(pj_get_def(orig.myInverseProjection, 0));
     230              : #endif
     231              :     }
     232       102351 :     if (orig.myGeoProjection != nullptr) {
     233              : #ifdef PROJ_VERSION_MAJOR
     234            0 :         myGeoProjection = orig.myGeoProjection;
     235              : #else
     236              :         myGeoProjection = pj_init_plus(pj_get_def(orig.myGeoProjection, 0));
     237              : #endif
     238              :     }
     239              : #endif
     240       102351 :     return *this;
     241              : }
     242              : 
     243              : 
     244              : bool
     245         2456 : GeoConvHelper::init(OptionsCont& oc) {
     246         2456 :     std::string proj = "!"; // the default
     247         2456 :     double scale = oc.getFloat("proj.scale");
     248         2456 :     double rot = oc.getFloat("proj.rotate");
     249         4912 :     Position offset = Position(oc.getFloat("offset.x"), oc.getFloat("offset.y"), oc.getFloat("offset.z"));
     250         4912 :     bool inverse = oc.exists("proj.inverse") && oc.getBool("proj.inverse");
     251         4912 :     bool flatten = oc.exists("flatten") && oc.getBool("flatten");
     252              : 
     253         4912 :     if (oc.getBool("simple-projection")) {
     254              :         proj = "-";
     255              :     }
     256              : 
     257              : #ifdef PROJ_API_FILE
     258              : #ifdef PROJ_VERSION_MAJOR
     259              : #ifdef WIN32
     260              :     if (getenv("SUMO_HOME") != nullptr) {
     261              :         const char* paths[] = { (std::string(getenv("SUMO_HOME")) + "/data/proj").c_str() };
     262              :         proj_context_set_search_paths(PJ_DEFAULT_CTX, 1, paths);
     263              :     }
     264              : #endif
     265              : #endif
     266         2456 :     if (oc.getBool("proj.inverse") && oc.getString("proj") == "!") {
     267            0 :         WRITE_ERROR(TL("Inverse projection works only with explicit proj parameters."));
     268            0 :         return false;
     269              :     }
     270         4912 :     unsigned numProjections = oc.getBool("simple-projection") + oc.getBool("proj.utm") + oc.getBool("proj.dhdn") + oc.getBool("proj.dhdnutm") + (oc.getString("proj").length() > 1);
     271         2456 :     if (numProjections > 1) {
     272            0 :         WRITE_ERROR(TL("The projection method needs to be uniquely defined."));
     273            0 :         return false;
     274              :     }
     275              : 
     276         4912 :     if (oc.getBool("proj.utm")) {
     277              :         proj = "UTM";
     278         3696 :     } else if (oc.getBool("proj.dhdn")) {
     279              :         proj = "DHDN";
     280         3696 :     } else if (oc.getBool("proj.dhdnutm")) {
     281              :         proj = "DHDN_UTM";
     282         3696 :     } else if (!oc.isDefault("proj")) {
     283            4 :         proj = oc.getString("proj");
     284              :     }
     285              : #endif
     286         2456 :     myProcessing = GeoConvHelper(proj, offset, Boundary(), Boundary(), scale, rot, inverse, flatten);
     287         2456 :     myFinal = myProcessing;
     288              :     return true;
     289              : }
     290              : 
     291              : 
     292              : void
     293        46928 : GeoConvHelper::init(const std::string& proj, const Position& offset, const Boundary& orig,
     294              :                     const Boundary& conv, double scale) {
     295        46928 :     myProcessing = GeoConvHelper(proj, offset, orig, conv, scale);
     296        46928 :     myProcessing.resolveAbstractProjection();
     297        46928 :     myFinal = myProcessing;
     298        46928 : }
     299              : 
     300              : void
     301        48283 : GeoConvHelper::resolveAbstractProjection() {
     302              : #ifdef PROJ_API_FILE
     303        48283 :     if (myProjection == nullptr &&
     304        34245 :             myProjectionMethod != NONE && myProjectionMethod != SIMPLE) {
     305              :         const std::string origProj = myProjString;
     306              :         // try to initialized projection based on origBoundary
     307            1 :         Position tmp = myOrigBoundary.getCenter();
     308            1 :         x2cartesian(tmp, false);
     309            1 :         if (myProjection == nullptr) {
     310            0 :             WRITE_WARNING("Failed to intialized projection '" + origProj + "' based on origBoundary centered on '" + toString(myOrigBoundary.getCenter()) + "'");
     311            0 :             myProjectionMethod = NONE;
     312              :         }
     313              :     }
     314              : #endif
     315        48283 : }
     316              : 
     317              : void
     318         2494 : GeoConvHelper::addProjectionOptions(OptionsCont& oc) {
     319         2494 :     oc.addOptionSubTopic("Projection");
     320              : 
     321         2494 :     oc.doRegister("simple-projection", new Option_Bool(false));
     322         4988 :     oc.addSynonyme("simple-projection", "proj.simple", true);
     323         4988 :     oc.addDescription("simple-projection", "Projection", TL("Uses a simple method for projection"));
     324              : 
     325         2494 :     oc.doRegister("proj.scale", new Option_Float(1.0));
     326         4988 :     oc.addDescription("proj.scale", "Projection", TL("Scaling factor for input coordinates"));
     327              : 
     328         2494 :     oc.doRegister("proj.rotate", new Option_Float(0.0));
     329         4988 :     oc.addDescription("proj.rotate", "Projection", TL("Rotation (clockwise degrees) for input coordinates"));
     330              : 
     331              : #ifdef PROJ_API_FILE
     332         2494 :     oc.doRegister("proj.utm", new Option_Bool(false));
     333         4988 :     oc.addDescription("proj.utm", "Projection", TL("Determine the UTM zone (for a universal transversal mercator projection based on the WGS84 ellipsoid)"));
     334              : 
     335         2494 :     oc.doRegister("proj.dhdn", new Option_Bool(false));
     336         4988 :     oc.addDescription("proj.dhdn", "Projection", "Determine the DHDN zone (for a transversal mercator projection based on the bessel ellipsoid, \"Gauss-Krueger\")");
     337              : 
     338         4988 :     oc.doRegister("proj", new Option_String("!"));
     339         4988 :     oc.addDescription("proj", "Projection", TL("Uses STR as proj.4 definition for projection"));
     340              : 
     341         2494 :     oc.doRegister("proj.inverse", new Option_Bool(false));
     342         4988 :     oc.addDescription("proj.inverse", "Projection", TL("Inverses projection"));
     343              : 
     344         2494 :     oc.doRegister("proj.dhdnutm", new Option_Bool(false));
     345         4988 :     oc.addDescription("proj.dhdnutm", "Projection", TL("Convert from Gauss-Krueger to UTM"));
     346              : #endif // PROJ_API_FILE
     347         2494 : }
     348              : 
     349              : 
     350              : bool
     351       972338 : GeoConvHelper::usingGeoProjection() const {
     352       972338 :     return myProjectionMethod != NONE;
     353              : }
     354              : 
     355              : 
     356              : bool
     357          344 : GeoConvHelper::usingInverseGeoProjection() const {
     358          344 :     return myUseInverseProjection;
     359              : }
     360              : 
     361              : 
     362              : void
     363        23173 : GeoConvHelper::cartesian2geo(Position& cartesian) const {
     364        23173 :     cartesian.sub(getOffsetBase());
     365        23173 :     if (myProjectionMethod == NONE) {
     366         2908 :         return;
     367              :     }
     368        20265 :     if (myProjectionMethod == SIMPLE) {
     369            0 :         const double y = cartesian.y() / 111136.;
     370            0 :         const double x = cartesian.x() / 111320. / cos(DEG2RAD(y));
     371              :         cartesian.set(x, y);
     372            0 :         return;
     373              :     }
     374              : #ifdef PROJ_API_FILE
     375              : #ifdef PROJ_VERSION_MAJOR
     376        20265 :     PJ_COORD c = proj_coord(cartesian.x(), cartesian.y(), cartesian.z(), 0);
     377        20265 :     c = proj_trans(myProjection, PJ_INV, c);
     378        20265 :     checkError(myProjection);
     379        20265 :     if (myRadians) {
     380        20265 :         cartesian.set(proj_todeg(c.lp.lam), proj_todeg(c.lp.phi));
     381              :     } else {
     382              :         cartesian.set(c.lp.lam, c.lp.phi);
     383              :     }
     384              : #else
     385              :     projUV p;
     386              :     p.u = cartesian.x();
     387              :     p.v = cartesian.y();
     388              :     p = pj_inv(p, myProjection);
     389              :     //!!! check pj_errno
     390              :     p.u *= RAD_TO_DEG;
     391              :     p.v *= RAD_TO_DEG;
     392              :     cartesian.set((double) p.u, (double) p.v);
     393              : #endif
     394              : #endif
     395              : }
     396              : 
     397              : 
     398              : #ifdef PROJ_API_FILE
     399              : #ifdef PROJ_VERSION_MAJOR
     400              : bool
     401       317834 : GeoConvHelper::checkError(projPJ projection) const {
     402       317834 :     const int err_no = proj_context_errno(PJ_DEFAULT_CTX);
     403              :     const char* err_string = "";
     404       317834 :     if (err_no != 0) {
     405              : #if PROJ_VERSION_MAJOR > 7
     406            0 :         err_string = proj_context_errno_string(PJ_DEFAULT_CTX, err_no);
     407              : #else
     408              :         err_string = proj_errno_string(err_no);
     409              : #endif
     410              :     }
     411       317834 :     if (projection == nullptr) {
     412            0 :         if (err_no == 0) {
     413            0 :             WRITE_WARNING(TL("Failed to create transformation, reason unknown."));
     414              :         } else {
     415            0 :             WRITE_WARNINGF(TL("Failed to create transformation, %."), err_string);
     416              :         }
     417            0 :         return false;
     418              :     }
     419       317834 :     if (err_no != 0) {
     420            0 :         WRITE_WARNINGF(TL("Failed to transform, %."), err_string);
     421            0 :         return false;
     422              :     }
     423              :     return true;
     424              : }
     425              : #endif
     426              : #endif
     427              : 
     428              : 
     429              : bool
     430      1112582 : GeoConvHelper::x2cartesian(Position& from, bool includeInBoundary) {
     431      1112582 :     if (includeInBoundary) {
     432       734806 :         myOrigBoundary.add(from);
     433              :     }
     434              :     // init projection parameter on first use
     435              : #ifdef PROJ_API_FILE
     436      1112582 :     if (myProjection == nullptr) {
     437       859958 :         double x = from.x() * myGeoScale;
     438       859958 :         switch (myProjectionMethod) {
     439            0 :             case DHDN_UTM: {
     440            0 :                 int zone = (int)((x - 500000.) / 1000000.);
     441            0 :                 if (zone < 1 || zone > 5) {
     442            0 :                     WRITE_WARNING("Attempt to initialize DHDN_UTM-projection on invalid longitude " + toString(x));
     443            0 :                     return false;
     444              :                 }
     445            0 :                 myProjString = "+proj=tmerc +lat_0=0 +lon_0=" + toString(3 * zone) +
     446            0 :                                " +k=1 +x_0=" + toString(zone * 1000000 + 500000) +
     447            0 :                                " +y_0=0 +ellps=bessel +datum=potsdam +units=m +no_defs";
     448              : #ifdef PROJ_VERSION_MAJOR
     449            0 :                 myInverseProjection = proj_create(PJ_DEFAULT_CTX, myProjString.c_str());
     450            0 :                 checkError(myInverseProjection);
     451            0 :                 myGeoProjection = proj_create(PJ_DEFAULT_CTX, "+proj=latlong +datum=WGS84");
     452            0 :                 checkError(myGeoProjection);
     453              : #else
     454              :                 myInverseProjection = pj_init_plus(myProjString.c_str());
     455              :                 myGeoProjection = pj_init_plus("+proj=latlong +datum=WGS84");
     456              : #endif
     457            0 :                 x = ((x - 500000.) / 1000000.) * 3; // continues with UTM
     458              :             }
     459              :             FALLTHROUGH;
     460          595 :             case UTM: {
     461          595 :                 int zone = (int)(x + 180) / 6 + 1;
     462         1190 :                 myProjString = "+proj=utm +zone=" + toString(zone) +
     463          595 :                                " +ellps=WGS84 +datum=WGS84 +units=m +no_defs";
     464              : #ifdef PROJ_VERSION_MAJOR
     465          595 :                 myProjection = proj_create(PJ_DEFAULT_CTX, myProjString.c_str());
     466          595 :                 checkError(myProjection);
     467              : #else
     468              :                 myProjection = pj_init_plus(myProjString.c_str());
     469              : #endif
     470              :             }
     471          595 :             break;
     472            0 :             case DHDN: {
     473            0 :                 int zone = (int)(x / 3);
     474            0 :                 if (zone < 1 || zone > 5) {
     475            0 :                     WRITE_WARNING("Attempt to initialize DHDN-projection on invalid longitude " + toString(x));
     476            0 :                     return false;
     477              :                 }
     478            0 :                 myProjString = "+proj=tmerc +lat_0=0 +lon_0=" + toString(3 * zone) +
     479            0 :                                " +k=1 +x_0=" + toString(zone * 1000000 + 500000) +
     480            0 :                                " +y_0=0 +ellps=bessel +datum=potsdam +units=m +no_defs";
     481              : #ifdef PROJ_VERSION_MAJOR
     482            0 :                 myProjection = proj_create(PJ_DEFAULT_CTX, myProjString.c_str());
     483            0 :                 checkError(myProjection);
     484              : #else
     485              :                 myProjection = pj_init_plus(myProjString.c_str());
     486              : #endif
     487              :             }
     488              :             break;
     489              :             default:
     490              :                 break;
     491              :         }
     492              :     }
     493      1112582 :     if (myInverseProjection != nullptr) {
     494              : #ifdef PROJ_VERSION_MAJOR
     495            0 :         PJ_COORD c = proj_coord(from.x(), from.y(), from.z(), 0);
     496            0 :         c = proj_trans(myInverseProjection, PJ_INV, c);
     497            0 :         checkError(myInverseProjection);
     498            0 :         from.set(proj_todeg(c.lp.lam), proj_todeg(c.lp.phi));
     499              : #else
     500              :         double x = from.x();
     501              :         double y = from.y();
     502              :         if (pj_transform(myInverseProjection, myGeoProjection, 1, 1, &x, &y, nullptr)) {
     503              :             WRITE_WARNINGF(TL("Could not transform (%,%)"), toString(x), toString(y));
     504              :         }
     505              :         from.set(double(x * RAD_TO_DEG), double(y * RAD_TO_DEG));
     506              : #endif
     507              :     }
     508              : #endif
     509              :     // perform conversion
     510      1112582 :     bool ok = x2cartesian_const(from);
     511      1112582 :     if (ok) {
     512      1112581 :         if (includeInBoundary) {
     513       734805 :             myConvBoundary.add(from);
     514              :         }
     515              :     }
     516              :     return ok;
     517              : }
     518              : 
     519              : 
     520              : bool
     521      1113238 : GeoConvHelper::x2cartesian_const(Position& from) const {
     522      1113238 :     const double x2 = from.x() * myGeoScale;
     523      1113238 :     const double y2 = from.y() * myGeoScale;
     524      1113238 :     double x = x2 * myCos - y2 * mySin;
     525      1113238 :     double y = x2 * mySin + y2 * myCos;
     526      1113238 :     if (myProjectionMethod == NONE) {
     527              :         // do nothing
     528       253889 :     } else if (myUseInverseProjection) {
     529            0 :         cartesian2geo(from);
     530              :     } else {
     531       253889 :         if (x > 180.1 || x < -180.1) {
     532            0 :             WRITE_WARNING("Invalid longitude " + toString(x));
     533            0 :             return false;
     534              :         }
     535       253889 :         if (y > 90.1 || y < -90.1) {
     536            2 :             WRITE_WARNING("Invalid latitude " + toString(y));
     537            1 :             return false;
     538              :         }
     539              : #ifdef PROJ_API_FILE
     540       253888 :         if (myProjection != nullptr) {
     541              : #ifdef PROJ_VERSION_MAJOR
     542       253874 :             PJ_COORD c = proj_coord(myRadians ? proj_torad(x) : x, myRadians ? proj_torad(y) : y, from.z(), 0);
     543       253874 :             c = proj_trans(myProjection, PJ_FWD, c);
     544       253874 :             checkError(myProjection);
     545       253874 :             x = c.xy.x;
     546       253874 :             y = c.xy.y;
     547              : #else
     548              :             projUV p;
     549              :             p.u = x * DEG_TO_RAD;
     550              :             p.v = y * DEG_TO_RAD;
     551              :             p = pj_fwd(p, myProjection);
     552              :             //!!! check pj_errno
     553              :             x = p.u;
     554              :             y = p.v;
     555              : #endif
     556              :         }
     557              : #endif
     558       253888 :         if (myProjectionMethod == SIMPLE) {
     559              :             // Sinusoidal projection (https://en.wikipedia.org/wiki/Sinusoidal_projection)
     560           14 :             x *= 111320. * cos(DEG2RAD(y));
     561           14 :             y *= 111136.;
     562              :             //!!! recheck whether the axes are mirrored
     563              :         }
     564              :     }
     565      1113237 :     if (x > std::numeric_limits<double>::max() ||
     566      1113237 :             y > std::numeric_limits<double>::max()) {
     567              :         return false;
     568              :     }
     569              :     from.set(x, y);
     570              :     from.add(myOffset);
     571      1113237 :     if (myFlatten) {
     572              :         from.setz(0);
     573              :     }
     574              :     return true;
     575              : }
     576              : 
     577              : 
     578              : void
     579         1653 : GeoConvHelper::moveConvertedBy(double x, double y) {
     580              :     myOffset.add(x, y);
     581         1653 :     myConvBoundary.moveby(x, y);
     582         1653 : }
     583              : 
     584              : 
     585              : const Boundary&
     586         5664 : GeoConvHelper::getOrigBoundary() const {
     587         5664 :     return myOrigBoundary;
     588              : }
     589              : 
     590              : 
     591              : const Boundary&
     592        21143 : GeoConvHelper::getConvBoundary() const {
     593        21143 :     return myConvBoundary;
     594              : }
     595              : 
     596              : 
     597              : const Position
     598         4436 : GeoConvHelper::getOffset() const {
     599         4436 :     return myOffset;
     600              : }
     601              : 
     602              : 
     603              : const Position
     604        27767 : GeoConvHelper::getOffsetBase() const {
     605        27767 :     return myOffset;
     606              : }
     607              : 
     608              : 
     609              : const std::string&
     610         4593 : GeoConvHelper::getProjString() const {
     611         4593 :     return myProjString;
     612              : }
     613              : 
     614              : 
     615              : void
     616         2265 : GeoConvHelper::computeFinal(bool lefthand) {
     617         2265 :     if (myNumLoaded == 0) {
     618         1163 :         myFinal = myProcessing;
     619         1163 :         if (lefthand) {
     620              :             myFinal.myOffset.mul(1, -1);
     621              :         }
     622              :     } else  {
     623         1102 :         if (lefthand) {
     624              :             myProcessing.myOffset.mul(1, -1);
     625              :         }
     626         4408 :         myFinal = GeoConvHelper(
     627              :                       // prefer options over loaded location
     628         2204 :                       myProcessing.usingGeoProjection() ? myProcessing.getProjString() : myLoaded.getProjString(),
     629              :                       // let offset and boundary lead back to the original coords of the loaded data
     630         2204 :                       myProcessing.getOffset() + myLoaded.getOffset(),
     631              :                       myLoaded.getOrigBoundary(),
     632              :                       // the new boundary (updated during loading)
     633         1102 :                       myProcessing.getConvBoundary());
     634              :     }
     635         1111 :     if (lefthand) {
     636           23 :         myFinal.myConvBoundary.flipY();
     637              :     }
     638         2265 : }
     639              : 
     640              : 
     641              : void
     642         1331 : GeoConvHelper::setLoaded(const GeoConvHelper& loaded) {
     643         1331 :     myNumLoaded++;
     644         1331 :     if (myNumLoaded > 1) {
     645           26 :         WRITE_WARNINGF(TL("Ignoring loaded location attribute nr. % for tracking of original location"), toString(myNumLoaded));
     646              :     } else {
     647         1318 :         myLoaded = loaded;
     648              :     }
     649         1331 : }
     650              : 
     651              : 
     652              : void
     653          570 : GeoConvHelper::setLoadedPlain(const std::string& nodFile, const GeoConvHelper& loaded) {
     654         1140 :     myLoadedPlain[nodFile] = {loaded.getProjString(), loaded.getOffset()};
     655          570 : }
     656              : 
     657              : 
     658              : GeoConvHelper*
     659         1416 : GeoConvHelper::getLoadedPlain(const std::string& plainFile, const std::string& suffix) {
     660         4248 :     std::string nodFile = StringUtils::replace(plainFile, suffix, ".nod.xml");
     661              :     auto it = myLoadedPlain.find(nodFile);
     662         1416 :     if (it != myLoadedPlain.end()) {
     663          699 :         return new GeoConvHelper(it->second.first, it->second.second, Boundary(), Boundary());
     664              :     } else {
     665              :         return nullptr;
     666              :     }
     667              : }
     668              : 
     669              : 
     670              : void
     671            0 : GeoConvHelper::resetLoaded() {
     672            0 :     myNumLoaded = 0;
     673              :     myLoadedPlain.clear();
     674            0 : }
     675              : 
     676              : 
     677              : void
     678         2340 : GeoConvHelper::writeLocation(OutputDevice& into) {
     679         2340 :     into.openTag(SUMO_TAG_LOCATION);
     680         2340 :     into.writeAttr(SUMO_ATTR_NET_OFFSET, myFinal.getOffsetBase());
     681         2340 :     into.writeAttr(SUMO_ATTR_CONV_BOUNDARY, myFinal.getConvBoundary());
     682         2340 :     if (myFinal.usingGeoProjection()) {
     683          894 :         into.setPrecision(gPrecisionGeo);
     684              :     }
     685         2340 :     into.writeAttr(SUMO_ATTR_ORIG_BOUNDARY, myFinal.getOrigBoundary());
     686         2340 :     if (myFinal.usingGeoProjection()) {
     687          894 :         into.setPrecision();
     688              :     }
     689         2340 :     into.writeAttr(SUMO_ATTR_ORIG_PROJ, StringUtils::escapeXML(myFinal.getProjString()));
     690         2340 :     into.closeTag();
     691         2340 :     into.lf();
     692         2340 : }
     693              : 
     694              : 
     695              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1