LCOV - code coverage report
Current view: top level - src/netimport - NIXMLPTHandler.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 90.5 % 221 200
Test Date: 2026-07-26 16:30:20 Functions: 92.9 % 14 13

            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    NIXMLPTHandler.cpp
      15              : /// @author  Jakob Erdmann
      16              : /// @date    Sat, 28 Jul 2018
      17              : ///
      18              : // Importer for static public transport information
      19              : /****************************************************************************/
      20              : #include <config.h>
      21              : 
      22              : #include <string>
      23              : #include <iostream>
      24              : #include <map>
      25              : #include <cmath>
      26              : #include <utils/xml/SUMOSAXHandler.h>
      27              : #include <utils/xml/SUMOXMLDefinitions.h>
      28              : #include <utils/common/MsgHandler.h>
      29              : #include <utils/common/StringUtils.h>
      30              : #include <utils/common/StringTokenizer.h>
      31              : #include <utils/geom/GeomConvHelper.h>
      32              : #include <utils/common/ToString.h>
      33              : #include <utils/options/OptionsCont.h>
      34              : #include <utils/geom/GeoConvHelper.h>
      35              : #include <netbuild/NBNodeCont.h>
      36              : #include <netbuild/NBTypeCont.h>
      37              : #include <netbuild/NBNetBuilder.h>
      38              : #include <netbuild/NBPTStop.h>
      39              : #include "NIImporter_OpenStreetMap.h"
      40              : #include "NIXMLNodesHandler.h"
      41              : #include "NIXMLPTHandler.h"
      42              : 
      43              : 
      44              : // ===========================================================================
      45              : // method definitions
      46              : // ===========================================================================
      47         4742 : NIXMLPTHandler::NIXMLPTHandler(NBEdgeCont& ec, NBPTStopCont& sc, NBPTLineCont& lc) :
      48              :     SUMOSAXHandler("public transport - file"),
      49         4742 :     myEdgeCont(ec),
      50         4742 :     myStopCont(sc),
      51         4742 :     myLineCont(lc),
      52              :     myCurrentStop(nullptr),
      53         4742 :     myCurrentLine(nullptr),
      54         4742 :     myCurrentCompletion(0),
      55         4742 :     myCurrentStopWasIgnored(false),
      56         9484 :     myWarnOnly(OptionsCont::getOptions().getBool("ignore-errors"))
      57         4742 : { }
      58              : 
      59              : 
      60        14226 : NIXMLPTHandler::~NIXMLPTHandler() {}
      61              : 
      62              : 
      63              : void
      64          245 : NIXMLPTHandler::myStartElement(int element,
      65              :                                const SUMOSAXAttributes& attrs) {
      66          245 :     switch (element) {
      67          139 :         case SUMO_TAG_BUS_STOP:
      68              :         case SUMO_TAG_TRAIN_STOP:
      69              :         case SUMO_TAG_STOP:
      70          139 :             if (myCurrentRouteID != "") {
      71            6 :                 addRouteStop(attrs);
      72          133 :             } else if (myCurrentLine == nullptr) {
      73           99 :                 addPTStop(element, attrs);
      74              :             } else {
      75           34 :                 addPTLineStop(attrs);
      76              :             }
      77              :             break;
      78           12 :         case SUMO_TAG_ACCESS:
      79           12 :             addAccess(attrs);
      80           12 :             break;
      81           18 :         case SUMO_TAG_PT_LINE:
      82           18 :             addPTLine(attrs);
      83           18 :             break;
      84            7 :         case SUMO_TAG_ROUTE:
      85            7 :             if (myCurrentLine == nullptr) {
      86            3 :                 addRoute(attrs);
      87              :             } else {
      88            4 :                 addPTLineRoute(attrs);
      89              :             }
      90              :             break;
      91            3 :         case SUMO_TAG_FLOW:
      92              :         case SUMO_TAG_TRIP:
      93            3 :             addPTLineFromFlow(attrs);
      94            3 :             break;
      95            7 :         case SUMO_TAG_PARAM: {
      96            7 :             bool ok = true;
      97            7 :             const std::string key = attrs.get<std::string>(SUMO_ATTR_KEY, nullptr, ok);
      98            7 :             if (myCurrentLine != nullptr) {
      99            6 :                 if (key == "completeness") {
     100            3 :                     myCurrentCompletion = attrs.get<double>(SUMO_ATTR_VALUE, nullptr, ok);
     101            3 :                 } else if (key == "name") {
     102            6 :                     myCurrentLine->setName(attrs.get<std::string>(SUMO_ATTR_VALUE, nullptr, ok));
     103            0 :                 } else if (key == "missingBefore") {
     104            0 :                     myMissingBefore = attrs.get<int>(SUMO_ATTR_VALUE, nullptr, ok);
     105            0 :                 } else if (key == "missingAfter") {
     106            0 :                     myMissingAfter = attrs.get<int>(SUMO_ATTR_VALUE, nullptr, ok);
     107              :                 }
     108            1 :             } else if (myCurrentStop != nullptr) {
     109            1 :                 const std::string val = attrs.hasAttribute(SUMO_ATTR_VALUE) ? attrs.getString(SUMO_ATTR_VALUE) : "";
     110            1 :                 myCurrentStop->setParameter(key, val);
     111              :             }
     112              :         }
     113            7 :         break;
     114              :         default:
     115              :             break;
     116              :     }
     117          245 : }
     118              : 
     119              : void
     120          245 : NIXMLPTHandler::myEndElement(int element) {
     121          245 :     switch (element) {
     122              :         case SUMO_TAG_BUS_STOP:
     123              :         case SUMO_TAG_TRAIN_STOP:
     124              :             myCurrentStop = nullptr;
     125          133 :             myCurrentStopWasIgnored = false;
     126          133 :             break;
     127           21 :         case SUMO_TAG_PT_LINE:
     128              :         case SUMO_TAG_FLOW:
     129              :         case SUMO_TAG_TRIP:
     130           21 :             if (myCurrentLine != nullptr) {
     131           21 :                 myCurrentLine->setNumOfStops((int)((double)myCurrentLine->getStops().size() / myCurrentCompletion), myMissingBefore, myMissingAfter);
     132              :             }
     133           21 :             myCurrentLine = nullptr;
     134           21 :             break;
     135            7 :         case SUMO_TAG_ROUTE:
     136            7 :             myCurrentRouteID = "";
     137              :             break;
     138              :         default:
     139              :             break;
     140              :     }
     141          245 : }
     142              : 
     143              : 
     144              : bool
     145            9 : NIXMLPTHandler::reportError(const std::string& msg, const std::string& edgeID, const std::string& id) {
     146            9 :     const bool ignored = myEdgeCont.wasIgnored(edgeID);
     147            9 :     if (!ignored && !myWarnOnly) {
     148            0 :         WRITE_ERRORF(msg, edgeID, id);
     149            0 :         return true;
     150              :     } else if (!ignored) {
     151            6 :         WRITE_WARNINGF(msg, edgeID, id);
     152              :     }
     153              :     return false;
     154              : }
     155              : 
     156              : 
     157              : void
     158           99 : NIXMLPTHandler::addPTStop(int element, const SUMOSAXAttributes& attrs) {
     159           99 :     bool ok = true;
     160           99 :     const std::string id = attrs.get<std::string>(SUMO_ATTR_ID, "busStop", ok);
     161          198 :     const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
     162           99 :     const std::string laneID = attrs.get<std::string>(SUMO_ATTR_LANE, id.c_str(), ok);
     163           99 :     double startPos = attrs.get<double>(SUMO_ATTR_STARTPOS, id.c_str(), ok);
     164           99 :     double endPos = attrs.get<double>(SUMO_ATTR_ENDPOS, id.c_str(), ok);
     165           99 :     const double parkingLength = attrs.getOpt<double>(SUMO_ATTR_PARKING_LENGTH, id.c_str(), ok, 0);
     166           99 :     const RGBColor color = attrs.getOpt<RGBColor>(SUMO_ATTR_COLOR, id.c_str(), ok, RGBColor(false));
     167          198 :     const std::string lines = attrs.getOpt<std::string>(SUMO_ATTR_LINES, id.c_str(), ok, "");
     168          198 :     int laneIndex = NBEdge::getLaneIndexFromLaneID(laneID);
     169           99 :     std::string edgeID = SUMOXMLDefinitions::getEdgeIDFromLane(laneID);
     170           99 :     NBEdge* edge = myEdgeCont.retrieve(edgeID);
     171           99 :     if (edge == nullptr) {
     172            7 :         edge = myEdgeCont.retrieve(edgeID, true);
     173            7 :         if (edge != nullptr && myEdgeCont.getSplit(edge) == nullptr) {
     174              :             // splits are treated later
     175              :             edge = nullptr;
     176              :         }
     177              :     }
     178            7 :     if (edge == nullptr) {
     179           12 :         if (!reportError(TL("Edge '%' for stop '%' not found"), edgeID, id)) {
     180            6 :             myCurrentStopWasIgnored = true;
     181              :             NBPTStopCont::addIgnored(id);
     182              :         }
     183            6 :         return;
     184              :     }
     185           93 :     if (edge->getNumLanes() <= laneIndex) {
     186            0 :         if (myWarnOnly) {
     187            0 :             WRITE_WARNINGF(TL("Lane '%' for stop '%' not found. Using leftmost lane"), laneID, id);
     188            0 :             laneIndex = edge->getNumLanes() - 1;
     189              :         } else {
     190            0 :             WRITE_ERRORF(TL("Lane '%' for stop '%' not found"), laneID, id);
     191            0 :             return;
     192              :         }
     193              :     }
     194           93 :     SVCPermissions permissions = edge->getPermissions(laneIndex);
     195              :     // possibly the stops were written for a different network. If the lane is not a typical public transport stop lane, assume bus as the default
     196           93 :     if (!isRailway(permissions) && permissions != SVC_SHIP && permissions != SVC_TAXI) {
     197            9 :         permissions = SVC_BUS;
     198              :     }
     199           93 :     if (ok) {
     200           93 :         if (startPos < 0) {
     201            1 :             startPos += edge->getLoadedLength();
     202              :         }
     203           93 :         if (endPos < 0) {
     204            8 :             endPos += edge->getLoadedLength();
     205              :         }
     206          186 :         if (myEdgeCont.wasRemoved(edgeID) && (
     207            4 :                     startPos >= endPos || startPos < 0 || endPos < 0
     208            4 :                     || startPos >= edge->getLoadedLength()
     209            1 :                     || endPos >= edge->getLoadedLength())) {
     210            3 :             NBEdge* longest = myEdgeCont.getSplitBase(edgeID);
     211            3 :             if (longest != nullptr) {
     212              :                 edge = longest;
     213              :             }
     214              :         }
     215           93 :         Position pos = edge->geometryPositionAtOffset((startPos + endPos) / 2);
     216           93 :         myCurrentStop = std::make_shared<NBPTStop>((SumoXMLTag)element, id, pos, edgeID, edgeID, endPos - startPos, name, permissions, parkingLength, color, startPos);
     217           98 :         while (myEdgeCont.getSplit(edge) != nullptr) {
     218              :             myCurrentStop->resetLoaded();
     219            5 :             const std::pair<NBEdge*, NBEdge*> split = *myEdgeCont.getSplit(edge);
     220            5 :             if (myCurrentStop->replaceEdge(edgeID, {split.first, split.second})) {
     221            5 :                 edge = split.first->getID() == myCurrentStop->getEdgeId() ? split.first : split.second;
     222            5 :                 edgeID = edge->getID();
     223              :             }
     224              :         }
     225          242 :         for (const std::string& line : StringTokenizer(lines).getVector()) {
     226           56 :             myCurrentStop->addLine(line);
     227           93 :         }
     228          186 :         if (!myStopCont.insert(myCurrentStop)) {
     229            0 :             WRITE_ERRORF(TL("Could not add public transport stop '%' (already exists)"), id);
     230              :         }
     231              :     }
     232              : }
     233              : 
     234              : void
     235           12 : NIXMLPTHandler::addAccess(const SUMOSAXAttributes& attrs) {
     236           12 :     if (myCurrentStop == nullptr) {
     237            5 :         if (myCurrentStopWasIgnored) {
     238            6 :             return;
     239              :         } else {
     240            0 :             throw InvalidArgument("Could not add access outside a stopping place.");
     241              :         }
     242              :     }
     243            7 :     bool ok = true;
     244            7 :     const std::string laneID = attrs.get<std::string>(SUMO_ATTR_LANE, "access", ok);
     245            7 :     const std::string edgeID = SUMOXMLDefinitions::getEdgeIDFromLane(laneID);
     246            7 :     if (myEdgeCont.retrieve(edgeID) == nullptr) {
     247            2 :         reportError(TL("Edge '%' for access to stop '%' not found"), edgeID, myCurrentStop->getID());
     248              :         return;
     249              :     }
     250            6 :     const double pos = attrs.get<double>(SUMO_ATTR_POSITION, "access", ok);
     251            6 :     const double length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, "access", ok, -1);
     252            6 :     if (ok) {
     253           12 :         myCurrentStop->addAccess(laneID, pos, length);
     254              :     }
     255              : }
     256              : 
     257              : 
     258              : void
     259           18 : NIXMLPTHandler::addPTLine(const SUMOSAXAttributes& attrs) {
     260           18 :     bool ok = true;
     261           18 :     const std::string id = attrs.get<std::string>(SUMO_ATTR_ID, "ptLine", ok);
     262           18 :     const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
     263           18 :     const std::string line = attrs.getOpt<std::string>(SUMO_ATTR_LINE, id.c_str(), ok, "");
     264           18 :     const std::string type = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, "");
     265           18 :     SUMOVehicleClass vClass = NIImporter_OpenStreetMap::interpretTransportType(type);
     266           18 :     if (attrs.hasAttribute(SUMO_ATTR_VCLASS)) {
     267            0 :         vClass = getVehicleClassID(attrs.get<std::string>(SUMO_ATTR_VCLASS, id.c_str(), ok));
     268              :     }
     269           18 :     RGBColor color = attrs.getOpt<RGBColor>(SUMO_ATTR_COLOR, id.c_str(), ok, RGBColor(false));
     270           18 :     const int intervalS = attrs.getOpt<int>(SUMO_ATTR_PERIOD, id.c_str(), ok, -1);
     271           36 :     const std::string nightService = attrs.getStringSecure("nightService", "");
     272           36 :     myCurrentCompletion = StringUtils::toDouble(attrs.getStringSecure("completeness", "1"));
     273           36 :     myMissingBefore = StringUtils::toInt(attrs.getStringSecure("missingBefore", "0"));
     274           36 :     myMissingAfter = StringUtils::toInt(attrs.getStringSecure("missingAfter", "0"));
     275           18 :     if (ok) {
     276              :         // patching existing line?
     277           18 :         myCurrentLine = myLineCont.retrieve(id);
     278           18 :         if (myCurrentLine == nullptr) {
     279           17 :             myCurrentLine = new NBPTLine(id, name, type, line, intervalS / 60, nightService, vClass, color);
     280           17 :             myLineCont.insert(myCurrentLine);
     281              :         } else {
     282            3 :             WRITE_MESSAGEF(TL("Duplicate ptLine id occurred ('%'); assuming overwriting is wished."), id);
     283            1 :             if (name != "") {
     284            0 :                 myCurrentLine->setName(name);
     285              :             }
     286            1 :             if (line != "") {
     287            0 :                 myCurrentLine->setRef(line);
     288              :             }
     289            1 :             if (intervalS != -1) {
     290            1 :                 myCurrentLine->setPeriod(intervalS);
     291              :             }
     292              :         }
     293              :     }
     294           18 : }
     295              : 
     296              : 
     297              : void
     298            3 : NIXMLPTHandler::addPTLineFromFlow(const SUMOSAXAttributes& attrs) {
     299            3 :     bool ok = true;
     300            3 :     myMissingBefore = 0;
     301            3 :     myMissingAfter = 0;
     302            3 :     const std::string id = attrs.get<std::string>(SUMO_ATTR_ID, "flow", ok);
     303            3 :     const std::string line = attrs.get<std::string>(SUMO_ATTR_LINE, id.c_str(), ok);
     304            3 :     const std::string type = attrs.get<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok);
     305            3 :     const std::string route = attrs.get<std::string>(SUMO_ATTR_ROUTE, id.c_str(), ok);
     306            3 :     SUMOVehicleClass vClass = NIImporter_OpenStreetMap::interpretTransportType(type);
     307            3 :     const int intervalS = attrs.getOpt<int>(SUMO_ATTR_PERIOD, id.c_str(), ok, -1);
     308            3 :     RGBColor color = attrs.getOpt<RGBColor>(SUMO_ATTR_COLOR, id.c_str(), ok, RGBColor(false));
     309            3 :     if (ok) {
     310            3 :         myCurrentLine = new NBPTLine(id, "", type, line, intervalS / 60, "", vClass, color);
     311            3 :         myCurrentLine->setEdges(myRouteEdges[route]);
     312            9 :         for (std::shared_ptr<NBPTStop> stop : myRouteStops[route]) {
     313           12 :             myCurrentLine->addPTStop(stop);
     314              :         }
     315            3 :         myLineCont.insert(myCurrentLine);
     316              :     }
     317            3 : }
     318              : 
     319              : 
     320              : void
     321            4 : NIXMLPTHandler::addPTLineRoute(const SUMOSAXAttributes& attrs) {
     322            4 :     if (myCurrentLine == nullptr) {
     323            0 :         WRITE_ERROR(TL("Found route outside line definition"));
     324            0 :         return;
     325              :     }
     326            4 :     bool ok = true;
     327            4 :     const std::vector<std::string>& edgeIDs = attrs.get<std::vector<std::string> >(SUMO_ATTR_EDGES, nullptr, ok);
     328              :     EdgeVector edges;
     329           13 :     for (const std::string& edgeID : edgeIDs) {
     330            9 :         NBEdge* edge = myEdgeCont.retrieve(edgeID);
     331            9 :         if (edge == nullptr) {
     332            4 :             reportError(TL("Edge '%' in route of line '%' not found"), edgeID, myCurrentLine->getLineID());
     333              :         } else {
     334            7 :             edges.push_back(edge);
     335              :         }
     336              :     }
     337            4 :     myCurrentLine->setEdges(edges);
     338            4 : }
     339              : 
     340              : 
     341              : void
     342            3 : NIXMLPTHandler::addRoute(const SUMOSAXAttributes& attrs) {
     343            3 :     bool ok = true;
     344            6 :     myCurrentRouteID = attrs.get<std::string>(SUMO_ATTR_ID, "route", ok);
     345            3 :     const std::vector<std::string>& edgeIDs = attrs.get<std::vector<std::string> >(SUMO_ATTR_EDGES, myCurrentRouteID.c_str(), ok);
     346              :     EdgeVector edges;
     347            9 :     for (const std::string& edgeID : edgeIDs) {
     348            6 :         NBEdge* edge = myEdgeCont.retrieve(edgeID);
     349            6 :         if (edge == nullptr) {
     350            0 :             reportError(TL("Edge '%' in route of line '%' not found"), edgeID, myCurrentLine->getLineID());
     351              :         } else {
     352            6 :             edges.push_back(edge);
     353              :         }
     354              :     }
     355            3 :     myRouteEdges[myCurrentRouteID] = edges;
     356            3 : }
     357              : 
     358              : 
     359              : void
     360           34 : NIXMLPTHandler::addPTLineStop(const SUMOSAXAttributes& attrs) {
     361           34 :     bool ok = true;
     362           34 :     const std::string id = attrs.hasAttribute(SUMO_ATTR_ID)
     363           34 :                            ? attrs.get<std::string>(SUMO_ATTR_ID, "ptLine", ok)
     364           34 :                            : attrs.get<std::string>(SUMO_ATTR_BUS_STOP, "ptline", ok);
     365          102 :     std::shared_ptr<NBPTStop> stop = myStopCont.get(id);
     366           34 :     if (stop == nullptr) {
     367            2 :         if (!NBPTStopCont::wasIgnored(id)) {
     368            0 :             WRITE_ERRORF(TL("Stop '%' within line '%' not found"), id, toString(myCurrentLine->getLineID()));
     369              :         }
     370              :         return;
     371              :     }
     372           64 :     myCurrentLine->addPTStop(stop);
     373              : }
     374              : 
     375              : void
     376            6 : NIXMLPTHandler::addRouteStop(const SUMOSAXAttributes& attrs) {
     377              :     assert(myCurrentRouteID != "");
     378            6 :     bool ok = true;
     379            6 :     const std::string id = attrs.hasAttribute(SUMO_ATTR_ID)
     380            6 :                            ? attrs.get<std::string>(SUMO_ATTR_ID, "ptLine", ok)
     381            6 :                            : attrs.get<std::string>(SUMO_ATTR_BUS_STOP, "ptline", ok);
     382           18 :     std::shared_ptr<NBPTStop> stop = myStopCont.get(id);
     383            6 :     if (stop == nullptr) {
     384            0 :         WRITE_ERRORF(TL("Stop '%' within route '%' not found"), id, toString(myCurrentRouteID));
     385              :         return;
     386              :     }
     387            6 :     myRouteStops[myCurrentRouteID].push_back(stop);
     388              : }
     389              : 
     390              : 
     391              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1