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

Generated by: LCOV version 2.0-1