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