LCOV - code coverage report
Current view: top level - src/utils/shapes - ShapeHandler.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 83.4 % 145 121
Test Date: 2025-12-06 15:35:27 Functions: 81.8 % 11 9

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-2025 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    ShapeHandler.cpp
      15              : /// @author  Jakob Erdmann
      16              : /// @date    Feb 2015
      17              : ///
      18              : // The XML-Handler for network loading
      19              : /****************************************************************************/
      20              : #include <config.h>
      21              : 
      22              : #include <string>
      23              : #include <utils/xml/SUMOXMLDefinitions.h>
      24              : #include <utils/xml/SUMOSAXHandler.h>
      25              : #include <utils/xml/XMLSubSys.h>
      26              : #include <utils/common/MsgHandler.h>
      27              : #include <utils/common/StringUtils.h>
      28              : #include <utils/common/StringTokenizer.h>
      29              : #include <utils/common/RGBColor.h>
      30              : #include <utils/geom/GeomConvHelper.h>
      31              : #include <utils/iodevices/OutputDevice.h>
      32              : #include <utils/common/UtilExceptions.h>
      33              : #include <utils/geom/GeoConvHelper.h>
      34              : #include <utils/gui/globjects/GUIGlObjectTypes.h>
      35              : 
      36              : #include "Shape.h"
      37              : #include "ShapeContainer.h"
      38              : #include "ShapeHandler.h"
      39              : 
      40              : 
      41              : // ===========================================================================
      42              : // method definitions
      43              : // ===========================================================================
      44              : 
      45        25486 : ShapeHandler::ShapeHandler(const std::string& file, ShapeContainer& sc, const GeoConvHelper* geoConvHelper) :
      46              :     SUMOSAXHandler(file),
      47        25486 :     myShapeContainer(sc),
      48        50972 :     myPrefix(""),
      49        25486 :     myDefaultColor(RGBColor::RED),
      50        25486 :     myDefaultIcon(SUMOXMLDefinitions::POIIcons.getString(POIIcon::NONE)),
      51        25486 :     myDefaultLayer(0),
      52        25486 :     myDefaultFill(false),
      53        25486 :     myLastParameterised(nullptr),
      54        50972 :     myGeoConvHelper(geoConvHelper) {
      55        25486 : }
      56              : 
      57              : 
      58        25486 : ShapeHandler::~ShapeHandler() {}
      59              : 
      60              : 
      61              : void
      62       458126 : ShapeHandler::myStartElement(int element, const SUMOSAXAttributes& attrs) {
      63              :     try {
      64       458126 :         switch (element) {
      65         4061 :             case SUMO_TAG_POLY:
      66              :                 // default layer is different depending if we're parsing a Poly or a POI, therefore it has to be here defined
      67         4061 :                 myDefaultLayer = Shape::DEFAULT_LAYER;
      68         4061 :                 addPoly(attrs, false, false);
      69              :                 break;
      70         9420 :             case SUMO_TAG_POI:
      71              :                 // default layer is different depending if we're parsing a Poly or a POI, therefore it has to be here defined
      72         9420 :                 myDefaultLayer = Shape::DEFAULT_LAYER_POI;
      73         9420 :                 addPOI(attrs, false, false);
      74              :                 break;
      75        11469 :             case SUMO_TAG_PARAM:
      76        11469 :                 if (myLastParameterised != nullptr) {
      77         1459 :                     bool ok = true;
      78         1459 :                     const std::string key = attrs.get<std::string>(SUMO_ATTR_KEY, nullptr, ok);
      79              :                     // continue if key was successfully loaded
      80         1459 :                     if (ok) {
      81              :                         // circumventing empty string value
      82         1459 :                         const std::string val = attrs.hasAttribute(SUMO_ATTR_VALUE) ? attrs.getString(SUMO_ATTR_VALUE) : "";
      83              :                         // show warnings if values are invalid
      84         1459 :                         if (key.empty()) {
      85            0 :                             WRITE_WARNING(TL("Error parsing key from shape generic parameter. Key cannot be empty"));
      86         1459 :                         } else if (!SUMOXMLDefinitions::isValidParameterKey(key)) {
      87            0 :                             WRITE_WARNING(TL("Error parsing key from shape generic parameter. Key contains invalid characters"));
      88              :                         } else {
      89         1459 :                             myLastParameterised->setParameter(key, val);
      90              :                         }
      91              :                     }
      92              :                 }
      93              :                 break;
      94              :             default:
      95              :                 break;
      96              :         }
      97            0 :     } catch (InvalidArgument& e) {
      98            0 :         WRITE_ERROR(e.what());
      99            0 :     }
     100       458126 : }
     101              : 
     102              : 
     103              : void
     104       458155 : ShapeHandler::myEndElement(int element) {
     105       458155 :     if (element != SUMO_TAG_PARAM) {
     106       446686 :         myLastParameterised = nullptr;
     107              :     }
     108       458155 : }
     109              : 
     110              : 
     111              : void
     112         9429 : ShapeHandler::addPOI(const SUMOSAXAttributes& attrs, const bool ignorePruning, const bool useProcessing) {
     113         9429 :     bool ok = true;
     114              :     const double INVALID_POSITION(-1000000);
     115        18858 :     const std::string id = myPrefix + attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     116         9429 :     double x = attrs.getOpt<double>(SUMO_ATTR_X, id.c_str(), ok, INVALID_POSITION);
     117         9429 :     const double y = attrs.getOpt<double>(SUMO_ATTR_Y, id.c_str(), ok, INVALID_POSITION);
     118         9429 :     const double z = attrs.getOpt<double>(SUMO_ATTR_Z, id.c_str(), ok, INVALID_POSITION);
     119         9429 :     double lon = attrs.getOpt<double>(SUMO_ATTR_LON, id.c_str(), ok, INVALID_POSITION);
     120         9429 :     double lat = attrs.getOpt<double>(SUMO_ATTR_LAT, id.c_str(), ok, INVALID_POSITION);
     121         9429 :     const double lanePos = attrs.getOpt<double>(SUMO_ATTR_POSITION, id.c_str(), ok, 0);
     122         9429 :     const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, id.c_str(), ok, false);
     123         9429 :     const double lanePosLat = attrs.getOpt<double>(SUMO_ATTR_POSITION_LAT, id.c_str(), ok, 0);
     124        18858 :     std::string icon = attrs.getOpt<std::string>(SUMO_ATTR_ICON, id.c_str(), ok, myDefaultIcon);
     125              :     // check icon
     126         9429 :     if (!SUMOXMLDefinitions::POIIcons.hasString(icon)) {
     127            0 :         WRITE_WARNING(TLF("Invalid icon % for POI '%', using default", icon, id));
     128              :         icon = "none";
     129              :     }
     130         9429 :     const double layer = attrs.getOpt<double>(SUMO_ATTR_LAYER, id.c_str(), ok, myDefaultLayer);
     131         9429 :     const std::string type = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, "");
     132         9429 :     const std::string laneID = attrs.getOpt<std::string>(SUMO_ATTR_LANE, id.c_str(), ok, "");
     133         9429 :     const double angle = attrs.getOpt<double>(SUMO_ATTR_ANGLE, id.c_str(), ok, Shape::DEFAULT_ANGLE);
     134         9429 :     std::string imgFile = attrs.getOpt<std::string>(SUMO_ATTR_IMGFILE, id.c_str(), ok, Shape::DEFAULT_IMG_FILE);
     135         9429 :     const RGBColor color = attrs.hasAttribute(SUMO_ATTR_COLOR) ? attrs.get<RGBColor>(SUMO_ATTR_COLOR, id.c_str(), ok) : (imgFile != "" ? RGBColor::WHITE : myDefaultColor);
     136              :     // If the image file is set, change the default POI color to white.
     137         9429 :     if (imgFile != "" && !FileHelpers::isAbsolute(imgFile)) {
     138         8612 :         imgFile = FileHelpers::getConfigurationRelative(getFileName(), imgFile);
     139              :     }
     140         9429 :     const double width = attrs.getOpt<double>(SUMO_ATTR_WIDTH, id.c_str(), ok, Shape::DEFAULT_IMG_WIDTH);
     141         9429 :     const double height = attrs.getOpt<double>(SUMO_ATTR_HEIGHT, id.c_str(), ok, Shape::DEFAULT_IMG_HEIGHT);
     142              :     // check if ID is valid
     143         9429 :     if (!SUMOXMLDefinitions::isValidTypeID(id)) {
     144            0 :         WRITE_WARNINGF(TL("Invalid characters for PoI ID '%'"), id);
     145            0 :         ok = false;
     146              :     }
     147              :     // continue
     148         9429 :     if (ok) {
     149              :         const GeoConvHelper* gch;
     150              :         // set GEOConverter
     151         9429 :         if (myGeoConvHelper != nullptr) {
     152              :             gch = myGeoConvHelper;
     153         9371 :         } else if (useProcessing) {
     154              :             gch = &GeoConvHelper::getProcessing();
     155              :         } else {
     156              :             gch = &GeoConvHelper::getFinal();
     157              :         }
     158              :         // check if GEOProjection has to be used
     159           67 :         if (useProcessing && gch->usingGeoProjection()) {
     160            6 :             if ((lat == INVALID_POSITION) || (lon == INVALID_POSITION)) {
     161              :                 lon = x;
     162              :                 lat = y;
     163              :                 x = INVALID_POSITION;
     164              :             }
     165              :         }
     166              :         Position pos(x, y);
     167              :         bool useGeo = false;
     168         9429 :         if ((x == INVALID_POSITION) || (y == INVALID_POSITION)) {
     169              :             // try computing x,y from lane,pos
     170         1399 :             if (laneID != "") {
     171          869 :                 pos = getLanePos(id, laneID, lanePos, friendlyPos, lanePosLat);
     172              :             } else {
     173              :                 // try computing x,y from lon,lat
     174          530 :                 if ((lat == INVALID_POSITION) || (lon == INVALID_POSITION)) {
     175            0 :                     WRITE_ERRORF(TL("Either (x, y), (lon, lat) or (lane, pos) must be specified for PoI '%'."), id);
     176            0 :                     return;
     177          530 :                 } else if (!gch->usingGeoProjection()) {
     178            0 :                     WRITE_ERRORF(TL("(lon, lat) is specified for PoI '%' but no geo-conversion is specified for the network."), id);
     179            0 :                     return;
     180              :                 }
     181              :                 pos.set(lon, lat);
     182              :                 useGeo = true;
     183              :                 bool success = true;
     184          530 :                 if (useProcessing) {
     185            6 :                     success = GeoConvHelper::getProcessing().x2cartesian(pos);
     186              :                 } else {
     187          524 :                     success = gch->x2cartesian_const(pos);
     188              :                 }
     189          530 :                 if (!success) {
     190            0 :                     WRITE_ERRORF(TL("Unable to project coordinates for POI '%'."), id);
     191            0 :                     return;
     192              :                 }
     193              :             }
     194              :         }
     195         9429 :         if (z != INVALID_POSITION) {
     196              :             pos.setz(z);
     197              :         }
     198         9429 :         if (!myShapeContainer.addPOI(id, type, color, pos, useGeo, laneID, lanePos, friendlyPos, lanePosLat, icon,
     199              :                                      layer, angle, imgFile, width, height, ignorePruning)) {
     200            3 :             WRITE_ERRORF(TL("PoI '%' already exists."), id);
     201              :         }
     202         9429 :         myLastParameterised = myShapeContainer.getPOIs().get(id);
     203         9429 :         if ((laneID != "") && addLanePosParams()) {
     204          868 :             myLastParameterised->setParameter(toString(SUMO_ATTR_LANE), laneID);
     205         1736 :             myLastParameterised->setParameter(toString(SUMO_ATTR_POSITION), toString(lanePos));
     206         1736 :             myLastParameterised->setParameter(toString(SUMO_ATTR_POSITION_LAT), toString(lanePosLat));
     207              :         }
     208              :     }
     209              : }
     210              : 
     211              : 
     212              : void
     213         4070 : ShapeHandler::addPoly(const SUMOSAXAttributes& attrs, const bool ignorePruning, const bool useProcessing) {
     214         4070 :     bool ok = true;
     215         4070 :     const std::string id = myPrefix + attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     216              :     // check if ID is valid
     217         4070 :     if (!SUMOXMLDefinitions::isValidTypeID(id)) {
     218            0 :         WRITE_WARNINGF(TL("Invalid characters for Poly ID '%'"), id);
     219            0 :         ok = false;
     220              :     }
     221              :     // get the id, report an error if not given or empty...
     222         4070 :     if (ok) {
     223              :         // continue loading parameters
     224         4070 :         const double layer = attrs.getOpt<double>(SUMO_ATTR_LAYER, id.c_str(), ok, myDefaultLayer);
     225         4070 :         const bool fill = attrs.getOpt<bool>(SUMO_ATTR_FILL, id.c_str(), ok, myDefaultFill);
     226         4070 :         const double lineWidth = attrs.getOpt<double>(SUMO_ATTR_LINEWIDTH, id.c_str(), ok, Shape::DEFAULT_LINEWIDTH);
     227         4070 :         const std::string type = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, Shape::DEFAULT_TYPE);
     228         4070 :         const RGBColor color = attrs.hasAttribute(SUMO_ATTR_COLOR) ? attrs.get<RGBColor>(SUMO_ATTR_COLOR, id.c_str(), ok) : myDefaultColor;
     229         4070 :         PositionVector shape = attrs.get<PositionVector>(SUMO_ATTR_SHAPE, id.c_str(), ok);
     230         4070 :         const bool geo = attrs.getOpt<bool>(SUMO_ATTR_GEO, id.c_str(), ok, false);
     231              :         // set geo converter
     232              :         const GeoConvHelper* gch;
     233         4070 :         if (myGeoConvHelper != nullptr) {
     234              :             gch = myGeoConvHelper;
     235              :         } else {
     236              :             gch = &GeoConvHelper::getFinal();
     237              :         }
     238              :         // check if poly use geo coordinates
     239         4070 :         if (geo || useProcessing) {
     240              :             bool success = true;
     241          124 :             for (int i = 0; i < (int)shape.size(); i++) {
     242          103 :                 if (useProcessing) {
     243           39 :                     success &= GeoConvHelper::getProcessing().x2cartesian(shape[i]);
     244              :                 } else {
     245           64 :                     success &= gch->x2cartesian_const(shape[i]);
     246              :                 }
     247              :             }
     248           21 :             if (!success) {
     249            3 :                 WRITE_WARNINGF(TL("Unable to project coordinates for polygon '%'."), id);
     250            1 :                 return;
     251              :             }
     252              :         }
     253         4069 :         const double angle = attrs.getOpt<double>(SUMO_ATTR_ANGLE, id.c_str(), ok, Shape::DEFAULT_ANGLE);
     254         8138 :         std::string imgFile = attrs.getOpt<std::string>(SUMO_ATTR_IMGFILE, id.c_str(), ok, Shape::DEFAULT_IMG_FILE);
     255         4069 :         if (imgFile != "" && !FileHelpers::isAbsolute(imgFile)) {
     256           20 :             imgFile = FileHelpers::getConfigurationRelative(getFileName(), imgFile);
     257              :         }
     258              :         // check that shape's size is valid
     259         4069 :         if (shape.size() == 0) {
     260            0 :             WRITE_ERROR(TL("Polygon's shape cannot be empty."));
     261            0 :             return;
     262              :         }
     263              :         // check that lineWidth is positive
     264         4069 :         if (lineWidth <= 0) {
     265            0 :             WRITE_ERROR(TL("Polygon's lineWidth must be greater than 0."));
     266            0 :             return;
     267              :         }
     268              :         // create polygon, or show an error if polygon already exists
     269         4069 :         if (!myShapeContainer.addPolygon(id, type, color, layer, angle, imgFile, shape, geo, fill, lineWidth, ignorePruning)) {
     270            0 :             WRITE_ERRORF(TL("Polygon '%' already exists."), id);
     271              :         }
     272         4069 :         myLastParameterised = myShapeContainer.getPolygons().get(id);
     273         4070 :     }
     274              : }
     275              : 
     276              : 
     277              : Parameterised*
     278            0 : ShapeHandler::getLastParameterised() const {
     279            0 :     return myLastParameterised;
     280              : }
     281              : 
     282              : 
     283              : bool
     284        23532 : ShapeHandler::loadFiles(const std::vector<std::string>& files, ShapeHandler& sh) {
     285        62322 :     for (const auto& fileIt : files) {
     286        38790 :         if (!XMLSubSys::runParser(sh, fileIt, false)) {
     287            0 :             WRITE_MESSAGEF(TL("Loading of shapes from % failed."), fileIt);
     288              :             return false;
     289              :         }
     290              :     }
     291              :     return true;
     292              : }
     293              : 
     294              : 
     295              : void
     296           18 : ShapeHandler::setDefaults(const std::string& prefix, const RGBColor& color, const std::string& icon, const double layer, const bool fill) {
     297           18 :     myPrefix = prefix;
     298           18 :     myDefaultColor = color;
     299           18 :     myDefaultIcon = icon;
     300           18 :     myDefaultLayer = layer;
     301           18 :     myDefaultFill = fill;
     302           18 : }
     303              : 
     304              : 
     305              : bool
     306            1 : ShapeHandler::addLanePosParams() {
     307            1 :     return false;
     308              : }
     309              : 
     310              : 
     311              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1