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 : /****************************************************************************/
|