LCOV - code coverage report
Current view: top level - src/netbuild - NBPTLine.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 88.3 % 180 159
Test Date: 2026-03-02 16:00:03 Functions: 94.4 % 18 17

            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    NBPTLine.cpp
      15              : /// @author  Gregor Laemmel
      16              : /// @author  Nikita Cherednychek
      17              : /// @date    Tue, 20 Mar 2017
      18              : ///
      19              : // The representation of one direction of a single pt line
      20              : /****************************************************************************/
      21              : #include <utils/iodevices/OutputDevice.h>
      22              : 
      23              : #include <utility>
      24              : #include <utils/common/ToString.h>
      25              : #include <utils/common/StringUtils.h>
      26              : #include <utils/common/MsgHandler.h>
      27              : #include "NBEdge.h"
      28              : #include "NBEdgeCont.h"
      29              : #include "NBPTStop.h"
      30              : #include "NBPTStopCont.h"
      31              : #include "NBPTLine.h"
      32              : 
      33              : 
      34              : // ===========================================================================
      35              : // method definitions
      36              : // ===========================================================================
      37         1373 : NBPTLine::NBPTLine(const std::string& id, const std::string& name, const std::string& type, const std::string& ref, int interval, const std::string& nightService,
      38         1373 :                    SUMOVehicleClass vClass, RGBColor color) :
      39         1373 :     myName(name),
      40         1373 :     myType(type),
      41         1373 :     myPTLineId(id),
      42         1373 :     myRef(ref != "" ? ref : name),
      43         1373 :     myColor(color),
      44         1373 :     myInterval(interval),
      45         1373 :     myNightService(nightService),
      46         1373 :     myVClass(vClass),
      47         1373 :     myNumOfStops(0),
      48         1373 :     myMissingStopsBefore(0),
      49         1373 :     myMissingStopsAfter(0)
      50         1373 : { }
      51              : 
      52              : 
      53              : void
      54         3243 : NBPTLine::addPTStop(std::shared_ptr<NBPTStop> pStop) {
      55         9502 :     if (!myPTStops.empty() && pStop->getName() != "" && myPTStops.back()->getName() == pStop->getName()) {
      56              :         // avoid duplicate stop when both platform and stop_position are given as nodes
      57          139 :         if (myPTStops.back()->isPlatform() && !pStop->isPlatform()) {
      58              :             myPTStops.pop_back();
      59          125 :         } else if (pStop->isPlatform()) {
      60              :             return;
      61              :         }
      62              :     }
      63         3131 :     myPTStops.push_back(pStop);
      64              : }
      65              : 
      66              : 
      67              : const std::vector<std::shared_ptr<NBPTStop> >&
      68        39363 : NBPTLine::getStops() {
      69        39363 :     return myPTStops;
      70              : }
      71              : 
      72              : 
      73              : void
      74          395 : NBPTLine::write(OutputDevice& device) {
      75          395 :     device.openTag(SUMO_TAG_PT_LINE);
      76          395 :     device.writeAttr(SUMO_ATTR_ID, myPTLineId);
      77          395 :     if (!myName.empty()) {
      78          786 :         device.writeAttr(SUMO_ATTR_NAME, StringUtils::escapeXML(myName));
      79              :     }
      80              : 
      81          395 :     device.writeAttr(SUMO_ATTR_LINE, StringUtils::escapeXML(myRef));
      82          395 :     device.writeAttr(SUMO_ATTR_TYPE, myType);
      83          395 :     device.writeAttr(SUMO_ATTR_VCLASS, toString(myVClass));
      84          395 :     if (myInterval > 0) {
      85              :         // write seconds
      86          118 :         device.writeAttr(SUMO_ATTR_PERIOD, 60 * myInterval);
      87              :     }
      88          395 :     if (myNightService != "") {
      89           28 :         device.writeAttr("nightService", myNightService);
      90              :     }
      91              : 
      92          395 :     if (myColor.isValid()) {
      93          228 :         device.writeAttr(SUMO_ATTR_COLOR, myColor);
      94              :     }
      95          395 :     device.writeAttr("completeness", (double)myPTStops.size() / myNumOfStops);
      96          395 :     if (myMissingStopsBefore != 0) {
      97          580 :         device.writeAttr("missingBefore", myMissingStopsBefore);
      98              :     }
      99          395 :     if (myMissingStopsAfter != 0) {
     100          622 :         device.writeAttr("missingAfter", myMissingStopsAfter);
     101              :     }
     102              : 
     103          395 :     if (!myRoute.empty()) {
     104          380 :         device.openTag(SUMO_TAG_ROUTE);
     105          380 :         device.writeAttr(SUMO_ATTR_EDGES, myRoute);
     106          760 :         device.closeTag();
     107              :     }
     108              : 
     109         1570 :     for (auto& myPTStop : myPTStops) {
     110         1175 :         device.openTag(SUMO_TAG_BUS_STOP);
     111         2350 :         device.writeAttr(SUMO_ATTR_ID, myPTStop->getID());
     112         1175 :         device.writeAttr(SUMO_ATTR_NAME, StringUtils::escapeXML(myPTStop->getName()));
     113         2350 :         device.closeTag();
     114              :     }
     115          395 :     device.closeTag();
     116              : 
     117          395 : }
     118              : 
     119              : 
     120              : void
     121        90388 : NBPTLine::addWayNode(long long int way, long long int node) {
     122        90388 :     std::string wayStr = toString(way);
     123        90388 :     if (wayStr != myCurrentWay) {
     124              :         myCurrentWay = wayStr;
     125        13927 :         myWays.push_back(wayStr);
     126              :     }
     127        90388 :     myWayNodes[wayStr].push_back(node);
     128        90388 : }
     129              : 
     130              : 
     131              : const std::vector<long long int>*
     132         7019 : NBPTLine::getWayNodes(std::string wayId) {
     133         7019 :     if (myWayNodes.find(wayId) != myWayNodes.end()) {
     134         7019 :         return &myWayNodes[wayId];
     135              :     }
     136              :     return nullptr;
     137              : }
     138              : 
     139              : 
     140              : void
     141         1247 : NBPTLine::setEdges(const std::vector<NBEdge*>& edges) {
     142         1247 :     myRoute = edges;
     143              :     // ensure permissions
     144        31955 :     for (NBEdge* e : edges) {
     145        30708 :         SVCPermissions permissions = e->getPermissions();
     146        30708 :         if ((permissions & myVClass) != myVClass) {
     147              :             SVCPermissions nVuln = ~(SVC_PEDESTRIAN | SVC_BICYCLE);
     148           61 :             if (permissions != 0 && (permissions & nVuln) == 0) {
     149              :                 // this is a footpath or sidewalk. Add another lane
     150            0 :                 e->addRestrictedLane(SUMO_const_laneWidth, myVClass);
     151              :             } else {
     152              :                 // add permissions to the rightmost lane that is not exclusively used for pedestrians / bicycles
     153           61 :                 for (int i = 0; i < (int)e->getNumLanes(); i++) {
     154           61 :                     if ((e->getPermissions(i) & nVuln) != 0) {
     155           61 :                         e->allowVehicleClass(i, myVClass);
     156              :                         break;
     157              :                     }
     158              :                 }
     159              :             }
     160              :         }
     161              :     }
     162         1247 : }
     163              : 
     164              : 
     165              : void
     166         1374 : NBPTLine::setNumOfStops(int numStops, int missingBefore, int missingAfter) {
     167         1374 :     myNumOfStops = numStops;
     168         1374 :     myMissingStopsBefore = missingBefore;
     169         1374 :     myMissingStopsAfter = missingAfter;
     170         1374 : }
     171              : 
     172              : 
     173              : const std::vector<NBEdge*>&
     174         3323 : NBPTLine::getRoute() const {
     175         3323 :     return myRoute;
     176              : }
     177              : 
     178              : 
     179              : std::vector<NBPTLine::PTStopInfo>
     180          671 : NBPTLine::getStopEdges(const NBEdgeCont& ec) const {
     181              :     std::vector<PTStopInfo> result;
     182              :     int i = 0;
     183         3376 :     for (std::shared_ptr<NBPTStop> stop : myPTStops) {
     184         2705 :         NBEdge* e = ec.retrieve(stop->getEdgeId());
     185         2705 :         if (e != nullptr) {
     186         2705 :             bool revised = (int)myStopsRevised.size() > i ? myStopsRevised[i] : false;
     187         5410 :             result.push_back(PTStopInfo(e, stop->getID(), stop->getEndPos(), revised));
     188              :         }
     189         2705 :         i++;
     190              :     }
     191          671 :     return result;
     192            0 : }
     193              : 
     194              : 
     195              : NBEdge*
     196          164 : NBPTLine::getRouteStart(const NBEdgeCont& ec) const {
     197              :     std::vector<NBEdge*> validEdges;
     198              :     // filter out edges that have been removed due to joining junctions
     199         5534 :     for (NBEdge* e : myRoute) {
     200         5370 :         if (ec.retrieve(e->getID())) {
     201         5370 :             validEdges.push_back(e);
     202              :         }
     203              :     }
     204          164 :     if (validEdges.size() == 0) {
     205              :         return nullptr;
     206              :     }
     207              :     // filter out edges after the first stop
     208          152 :     if (myPTStops.size() > 0) {
     209          151 :         NBEdge* firstStopEdge = ec.retrieve(myPTStops.front()->getEdgeId());
     210          151 :         if (firstStopEdge == nullptr) {
     211            0 :             WRITE_WARNINGF(TL("Could not retrieve edge '%' for first stop of line '%'."), myPTStops.front()->getEdgeId(), myPTLineId);
     212           11 :             return nullptr;
     213              : 
     214              :         }
     215          151 :         auto it = std::find(validEdges.begin(), validEdges.end(), firstStopEdge);
     216          151 :         if (it == validEdges.end()) {
     217           44 :             WRITE_WARNINGF(TL("First stop edge '%' is not part of the route of line '%'."), firstStopEdge->getID(), myPTLineId);
     218           11 :             return nullptr;
     219              :         }
     220              :     }
     221          141 :     return validEdges.front();
     222          164 : }
     223              : 
     224              : 
     225              : NBEdge*
     226          164 : NBPTLine::getRouteEnd(const NBEdgeCont& ec) const {
     227              :     std::vector<NBEdge*> validEdges;
     228              :     // filter out edges that have been removed due to joining junctions
     229         5534 :     for (NBEdge* e : myRoute) {
     230         5370 :         if (ec.retrieve(e->getID())) {
     231         5370 :             validEdges.push_back(e);
     232              :         }
     233              :     }
     234          164 :     if (validEdges.size() == 0) {
     235              :         return nullptr;
     236              :     }
     237              :     // filter out edges after the last stop
     238          152 :     if (myPTStops.size() > 0) {
     239          151 :         NBEdge* lastStopEdge = ec.retrieve(myPTStops.back()->getEdgeId());
     240          151 :         if (lastStopEdge == nullptr) {
     241            0 :             WRITE_WARNINGF(TL("Could not retrieve edge '%' for last stop of line '%'."), myPTStops.back()->getEdgeId(), myPTLineId);
     242           11 :             return nullptr;
     243              : 
     244              :         }
     245          151 :         auto it = std::find(validEdges.begin(), validEdges.end(), lastStopEdge);
     246          151 :         if (it == validEdges.end()) {
     247           44 :             WRITE_WARNINGF(TL("Last stop edge '%' is not part of the route of line '%'."), lastStopEdge->getID(), myPTLineId);
     248           11 :             return nullptr;
     249              :         }
     250              :     }
     251          141 :     return validEdges.back();
     252          164 : }
     253              : 
     254              : 
     255              : bool
     256          157 : NBPTLine::isConsistent(std::vector<NBEdge*> stops) const {
     257          157 :     if (myRoute.empty() || stops.empty()) {
     258              :         return true;
     259              :     }
     260          144 :     if (stops.size() > 1 && stops.front() == stops.back()) {
     261              :         // circular route where we don't expect the route edges to occur twice
     262            9 :         if (myRoute.front() == stops.front()) {
     263              :             stops.pop_back();
     264            2 :         } else if (myRoute.back() == stops.back()) {
     265              :             stops.erase(stops.begin());
     266              :         }
     267              :     }
     268              :     std::vector<NBEdge*>::const_iterator stopIt = stops.begin();
     269         4832 :     for (const NBEdge* const e : myRoute) {
     270         5447 :         while (stopIt != stops.end() && e == *stopIt) {
     271              :             ++stopIt;
     272              :         }
     273         4822 :         if (stopIt == stops.end()) {
     274              :             return true;
     275              :         }
     276              :     }
     277              :     return false;
     278              : }
     279              : 
     280              : 
     281              : void
     282           54 : NBPTLine::replaceStop(std::shared_ptr<NBPTStop> oldStop, std::shared_ptr<NBPTStop> newStop) {
     283          404 :     for (int i = 0; i < (int)myPTStops.size(); i++) {
     284          350 :         if (myPTStops[i] == oldStop) {
     285              :             myPTStops[i] = newStop;
     286              :         }
     287              :     }
     288           54 : }
     289              : 
     290              : 
     291              : void
     292         5161 : NBPTLine::replaceEdge(const std::string& edgeID, const EdgeVector& replacement) {
     293         5161 :     EdgeVector oldRoute = myRoute;
     294              :     myRoute.clear();
     295       204525 :     for (NBEdge* e : oldRoute) {
     296       199364 :         if (e->getID() == edgeID) {
     297        10854 :             for (NBEdge* e2 : replacement) {
     298         5678 :                 if (myRoute.empty() || myRoute.back() != e2) {
     299          766 :                     myRoute.push_back(e2);
     300              :                 }
     301              :             }
     302              :         } else {
     303       194188 :             myRoute.push_back(e);
     304              :         }
     305              :     }
     306         5161 : }
     307              : 
     308              : 
     309              : void
     310         1296 : NBPTLine::deleteInvalidStops(const NBEdgeCont& ec, const NBPTStopCont& sc) {
     311              :     // delete stops that are missing or have no edge
     312         5056 :     for (auto it = myPTStops.begin(); it != myPTStops.end();) {
     313              :         std::shared_ptr<NBPTStop> stop = *it;
     314        11280 :         if (sc.get(stop->getID()) == nullptr ||
     315         3760 :                 ec.getByID(stop->getEdgeId()) == nullptr) {
     316          396 :             WRITE_WARNINGF(TL("Removed invalid stop '%' from line '%'."), stop->getID(), getLineID());
     317          132 :             it = myPTStops.erase(it);
     318              :         } else {
     319              :             it++;
     320              :         }
     321              : 
     322              :     }
     323         1296 : }
     324              : 
     325              : 
     326              : void
     327            0 : NBPTLine::deleteDuplicateStops() {
     328              :     // delete subsequent stops that belong to the same stopArea
     329            0 :     long long int lastAreaID = -1;
     330            0 :     std::string lastName = "";
     331            0 :     for (auto it = myPTStops.begin(); it != myPTStops.end();) {
     332              :         std::shared_ptr<NBPTStop> stop = *it;
     333            0 :         if (lastAreaID != -1 && stop->getAreaID() == lastAreaID) {
     334            0 :             WRITE_WARNINGF(TL("Removed duplicate stop '%' at area '%' from line '%'."), stop->getID(), toString(lastAreaID), getLineID());
     335            0 :             it = myPTStops.erase(it);
     336            0 :         } else if (lastName != "" && stop->getName() == lastName) {
     337            0 :             WRITE_WARNINGF(TL("Removed duplicate stop '%' named '%' from line '%'."), stop->getID(), lastName, getLineID());
     338            0 :             it = myPTStops.erase(it);
     339              :         } else {
     340              :             it++;
     341              :         }
     342            0 :         lastAreaID = stop->getAreaID();
     343            0 :         lastName = stop->getName();
     344              :     }
     345            0 : }
     346              : 
     347              : 
     348              : void
     349         1147 : NBPTLine::removeInvalidEdges(const NBEdgeCont& ec) {
     350        20463 :     for (int i = 0; i < (int)myRoute.size();) {
     351        19316 :         const std::pair<NBEdge*, NBEdge*>* split = ec.getSplit(myRoute[i]);
     352              :         if (split != nullptr) {
     353            0 :             myRoute[i] = split->first;
     354            0 :             myRoute.insert(myRoute.begin() + i + 1, split->second);
     355        19316 :         } else if (ec.retrieve(myRoute[i]->getID()) == nullptr) {
     356         2373 :             myRoute.erase(myRoute.begin() + i);
     357              :         } else {
     358        16943 :             i++;
     359              :         }
     360              :     }
     361              :     // validate that all stops are actually on the route
     362              :     auto ri = myRoute.begin();
     363         4117 :     for (auto it = myPTStops.begin(); it != myPTStops.end();) {
     364              :         std::shared_ptr<NBPTStop> stop = *it;
     365         2970 :         NBEdge* e = ec.retrieve(stop->getEdgeId());
     366         2970 :         if (e == nullptr) {
     367            0 :             WRITE_WARNINGF(TL("Removed stop '%' named '%' from line '%' because edge '%' is missing."),
     368              :                     stop->getID(), stop->getName(), getLineID(), stop->getEdgeId());
     369            0 :             it = myPTStops.erase(it);
     370              :         } else {
     371              :             auto riPrev = ri;
     372         2970 :             ri = std::find(ri, myRoute.end(), e);
     373         2970 :             if (ri == myRoute.end()) {
     374          153 :                 std::string reason = std::find(myRoute.begin(), myRoute.end(), e) == myRoute.end()
     375          139 :                     ? TL("not part of the route")
     376          292 :                     : TL("not part of the route downstream of the previous stop");
     377              : 
     378          612 :                 WRITE_WARNINGF(TL("Stop '%' named '%' from line '%' on edge '%' is %."),
     379              :                         stop->getID(), stop->getName(), getLineID(), stop->getEdgeId(), reason);
     380              :                 ri = riPrev;
     381              :             }
     382              :             it++;
     383              :         }
     384              :     }
     385         1147 : }
     386              : 
     387              : 
     388              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1