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 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 1359 : 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 1359 : SUMOVehicleClass vClass, RGBColor color) :
39 1359 : myName(name),
40 1359 : myType(type),
41 1359 : myPTLineId(id),
42 1359 : myRef(ref != "" ? ref : name),
43 1359 : myColor(color),
44 1359 : myInterval(interval),
45 1359 : myNightService(nightService),
46 1359 : myVClass(vClass),
47 1359 : myNumOfStops(0),
48 1359 : myMissingStopsBefore(0),
49 1359 : myMissingStopsAfter(0)
50 1359 : { }
51 :
52 :
53 : void
54 3019 : NBPTLine::addPTStop(std::shared_ptr<NBPTStop> pStop) {
55 8648 : 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 107 : if (myPTStops.back()->isPlatform() && !pStop->isPlatform()) {
58 : myPTStops.pop_back();
59 98 : } else if (pStop->isPlatform()) {
60 : return;
61 : }
62 : }
63 2933 : myPTStops.push_back(pStop);
64 : }
65 :
66 :
67 : const std::vector<std::shared_ptr<NBPTStop> >&
68 37992 : NBPTLine::getStops() {
69 37992 : return myPTStops;
70 : }
71 :
72 :
73 : void
74 381 : NBPTLine::write(OutputDevice& device) {
75 381 : device.openTag(SUMO_TAG_PT_LINE);
76 381 : device.writeAttr(SUMO_ATTR_ID, myPTLineId);
77 381 : if (!myName.empty()) {
78 758 : device.writeAttr(SUMO_ATTR_NAME, StringUtils::escapeXML(myName));
79 : }
80 :
81 381 : device.writeAttr(SUMO_ATTR_LINE, StringUtils::escapeXML(myRef));
82 381 : device.writeAttr(SUMO_ATTR_TYPE, myType);
83 381 : device.writeAttr(SUMO_ATTR_VCLASS, toString(myVClass));
84 381 : if (myInterval > 0) {
85 : // write seconds
86 104 : device.writeAttr(SUMO_ATTR_PERIOD, 60 * myInterval);
87 : }
88 381 : if (myNightService != "") {
89 28 : device.writeAttr("nightService", myNightService);
90 : }
91 :
92 381 : if (myColor.isValid()) {
93 214 : device.writeAttr(SUMO_ATTR_COLOR, myColor);
94 : }
95 381 : device.writeAttr("completeness", (double)myPTStops.size() / myNumOfStops);
96 381 : if (myMissingStopsBefore != 0) {
97 566 : device.writeAttr("missingBefore", myMissingStopsBefore);
98 : }
99 381 : if (myMissingStopsAfter != 0) {
100 608 : device.writeAttr("missingAfter", myMissingStopsAfter);
101 : }
102 :
103 381 : if (!myRoute.empty()) {
104 366 : device.openTag(SUMO_TAG_ROUTE);
105 366 : device.writeAttr(SUMO_ATTR_EDGES, myRoute);
106 732 : device.closeTag();
107 : }
108 :
109 1502 : for (auto& myPTStop : myPTStops) {
110 1121 : device.openTag(SUMO_TAG_BUS_STOP);
111 2242 : device.writeAttr(SUMO_ATTR_ID, myPTStop->getID());
112 1121 : device.writeAttr(SUMO_ATTR_NAME, StringUtils::escapeXML(myPTStop->getName()));
113 2242 : device.closeTag();
114 : }
115 381 : device.closeTag();
116 :
117 381 : }
118 :
119 :
120 : void
121 89519 : NBPTLine::addWayNode(long long int way, long long int node) {
122 89519 : std::string wayStr = toString(way);
123 89519 : if (wayStr != myCurrentWay) {
124 : myCurrentWay = wayStr;
125 13837 : myWays.push_back(wayStr);
126 : }
127 89519 : myWayNodes[wayStr].push_back(node);
128 89519 : }
129 :
130 :
131 : const std::vector<long long int>*
132 6703 : NBPTLine::getWayNodes(std::string wayId) {
133 6703 : if (myWayNodes.find(wayId) != myWayNodes.end()) {
134 6703 : return &myWayNodes[wayId];
135 : }
136 : return nullptr;
137 : }
138 :
139 :
140 : void
141 1223 : NBPTLine::setEdges(const std::vector<NBEdge*>& edges) {
142 1223 : myRoute = edges;
143 : // ensure permissions
144 31714 : for (NBEdge* e : edges) {
145 30491 : SVCPermissions permissions = e->getPermissions();
146 30491 : 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 1223 : }
163 :
164 :
165 : void
166 1360 : NBPTLine::setNumOfStops(int numStops, int missingBefore, int missingAfter) {
167 1360 : myNumOfStops = numStops;
168 1360 : myMissingStopsBefore = missingBefore;
169 1360 : myMissingStopsAfter = missingAfter;
170 1360 : }
171 :
172 :
173 : const std::vector<NBEdge*>&
174 3283 : NBPTLine::getRoute() const {
175 3283 : return myRoute;
176 : }
177 :
178 :
179 : std::vector<std::pair<NBEdge*, std::string> >
180 154 : NBPTLine::getStopEdges(const NBEdgeCont& ec) const {
181 : std::vector<std::pair<NBEdge*, std::string> > result;
182 789 : for (std::shared_ptr<NBPTStop> stop : myPTStops) {
183 635 : NBEdge* e = ec.retrieve(stop->getEdgeId());
184 635 : if (e != nullptr) {
185 1270 : result.push_back({e, stop->getID()});
186 : }
187 : }
188 154 : return result;
189 0 : }
190 :
191 :
192 : NBEdge*
193 154 : NBPTLine::getRouteStart(const NBEdgeCont& ec) const {
194 : std::vector<NBEdge*> validEdges;
195 : // filter out edges that have been removed due to joining junctions
196 5457 : for (NBEdge* e : myRoute) {
197 5303 : if (ec.retrieve(e->getID())) {
198 5303 : validEdges.push_back(e);
199 : }
200 : }
201 154 : if (validEdges.size() == 0) {
202 : return nullptr;
203 : }
204 : // filter out edges after the first stop
205 142 : if (myPTStops.size() > 0) {
206 141 : NBEdge* firstStopEdge = ec.retrieve(myPTStops.front()->getEdgeId());
207 141 : if (firstStopEdge == nullptr) {
208 0 : WRITE_WARNINGF(TL("Could not retrieve edge '%' for first stop of line '%'."), myPTStops.front()->getEdgeId(), myPTLineId);
209 9 : return nullptr;
210 :
211 : }
212 141 : auto it = std::find(validEdges.begin(), validEdges.end(), firstStopEdge);
213 141 : if (it == validEdges.end()) {
214 36 : WRITE_WARNINGF(TL("First stop edge '%' is not part of the route of line '%'."), firstStopEdge->getID(), myPTLineId);
215 9 : return nullptr;
216 : }
217 : }
218 133 : return validEdges.front();
219 154 : }
220 :
221 :
222 : NBEdge*
223 154 : NBPTLine::getRouteEnd(const NBEdgeCont& ec) const {
224 : std::vector<NBEdge*> validEdges;
225 : // filter out edges that have been removed due to joining junctions
226 5457 : for (NBEdge* e : myRoute) {
227 5303 : if (ec.retrieve(e->getID())) {
228 5303 : validEdges.push_back(e);
229 : }
230 : }
231 154 : if (validEdges.size() == 0) {
232 : return nullptr;
233 : }
234 : // filter out edges after the last stop
235 142 : if (myPTStops.size() > 0) {
236 141 : NBEdge* lastStopEdge = ec.retrieve(myPTStops.back()->getEdgeId());
237 141 : if (lastStopEdge == nullptr) {
238 0 : WRITE_WARNINGF(TL("Could not retrieve edge '%' for last stop of line '%'."), myPTStops.back()->getEdgeId(), myPTLineId);
239 11 : return nullptr;
240 :
241 : }
242 141 : auto it = std::find(validEdges.begin(), validEdges.end(), lastStopEdge);
243 141 : if (it == validEdges.end()) {
244 44 : WRITE_WARNINGF(TL("Last stop edge '%' is not part of the route of line '%'."), lastStopEdge->getID(), myPTLineId);
245 11 : return nullptr;
246 : }
247 : }
248 131 : return validEdges.back();
249 154 : }
250 :
251 :
252 : bool
253 147 : NBPTLine::isConsistent(std::vector<NBEdge*> stops) const {
254 147 : if (myRoute.empty() || stops.empty()) {
255 : return true;
256 : }
257 134 : if (stops.size() > 1 && stops.front() == stops.back()) {
258 : // circular route where we don't expect the route edges to occur twice
259 9 : if (myRoute.front() == stops.front()) {
260 : stops.pop_back();
261 2 : } else if (myRoute.back() == stops.back()) {
262 : stops.erase(stops.begin());
263 : }
264 : }
265 : std::vector<NBEdge*>::const_iterator stopIt = stops.begin();
266 4751 : for (const NBEdge* const e : myRoute) {
267 5327 : while (stopIt != stops.end() && e == *stopIt) {
268 : ++stopIt;
269 : }
270 4743 : if (stopIt == stops.end()) {
271 : return true;
272 : }
273 : }
274 : return false;
275 : }
276 :
277 :
278 : void
279 54 : NBPTLine::replaceStop(std::shared_ptr<NBPTStop> oldStop, std::shared_ptr<NBPTStop> newStop) {
280 404 : for (int i = 0; i < (int)myPTStops.size(); i++) {
281 350 : if (myPTStops[i] == oldStop) {
282 : myPTStops[i] = newStop;
283 : }
284 : }
285 54 : }
286 :
287 :
288 : void
289 5143 : NBPTLine::replaceEdge(const std::string& edgeID, const EdgeVector& replacement) {
290 5143 : EdgeVector oldRoute = myRoute;
291 : myRoute.clear();
292 203307 : for (NBEdge* e : oldRoute) {
293 198164 : if (e->getID() == edgeID) {
294 10818 : for (NBEdge* e2 : replacement) {
295 5660 : if (myRoute.empty() || myRoute.back() != e2) {
296 772 : myRoute.push_back(e2);
297 : }
298 : }
299 : } else {
300 193006 : myRoute.push_back(e);
301 : }
302 : }
303 5143 : }
304 :
305 :
306 : void
307 1272 : NBPTLine::deleteInvalidStops(const NBEdgeCont& ec, const NBPTStopCont& sc) {
308 : // delete stops that are missing or have no edge
309 4795 : for (auto it = myPTStops.begin(); it != myPTStops.end();) {
310 : std::shared_ptr<NBPTStop> stop = *it;
311 10569 : if (sc.get(stop->getID()) == nullptr ||
312 3523 : ec.getByID(stop->getEdgeId()) == nullptr) {
313 393 : WRITE_WARNINGF(TL("Removed invalid stop '%' from line '%'."), stop->getID(), getLineID());
314 131 : it = myPTStops.erase(it);
315 : } else {
316 : it++;
317 : }
318 :
319 : }
320 1272 : }
321 :
322 :
323 : void
324 0 : NBPTLine::deleteDuplicateStops() {
325 : // delete subsequent stops that belong to the same stopArea
326 0 : long long int lastAreaID = -1;
327 0 : std::string lastName = "";
328 0 : for (auto it = myPTStops.begin(); it != myPTStops.end();) {
329 : std::shared_ptr<NBPTStop> stop = *it;
330 0 : if (lastAreaID != -1 && stop->getAreaID() == lastAreaID) {
331 0 : WRITE_WARNINGF(TL("Removed duplicate stop '%' at area '%' from line '%'."), stop->getID(), toString(lastAreaID), getLineID());
332 0 : it = myPTStops.erase(it);
333 0 : } else if (lastName != "" && stop->getName() == lastName) {
334 0 : WRITE_WARNINGF(TL("Removed duplicate stop '%' named '%' from line '%'."), stop->getID(), lastName, getLineID());
335 0 : it = myPTStops.erase(it);
336 : } else {
337 : it++;
338 : }
339 0 : lastAreaID = stop->getAreaID();
340 0 : lastName = stop->getName();
341 : }
342 0 : }
343 :
344 :
345 : void
346 1133 : NBPTLine::removeInvalidEdges(const NBEdgeCont& ec) {
347 20323 : for (int i = 0; i < (int)myRoute.size();) {
348 19190 : const std::pair<NBEdge*, NBEdge*>* split = ec.getSplit(myRoute[i]);
349 : if (split != nullptr) {
350 0 : myRoute[i] = split->first;
351 0 : myRoute.insert(myRoute.begin() + i + 1, split->second);
352 19190 : } else if (ec.retrieve(myRoute[i]->getID()) == nullptr) {
353 2361 : myRoute.erase(myRoute.begin() + i);
354 : } else {
355 16829 : i++;
356 : }
357 : }
358 1133 : }
359 :
360 :
361 : /****************************************************************************/
|