LCOV - code coverage report
Current view: top level - src/netimport - NIXMLPTHandler.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 90.7 % 215 195
Test Date: 2026-03-02 16:00:03 Functions: 92.3 % 13 12

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

Generated by: LCOV version 2.0-1