Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2024 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 1354 : 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 1354 : SUMOVehicleClass vClass, RGBColor color) :
39 1354 : myName(name),
40 1354 : myType(type),
41 1354 : myPTLineId(id),
42 1354 : myRef(ref != "" ? ref : name),
43 1354 : myColor(color),
44 1354 : myInterval(interval),
45 1354 : myNightService(nightService),
46 1354 : myVClass(vClass),
47 1354 : myNumOfStops(0),
48 1354 : myMissingStopsBefore(0),
49 1354 : myMissingStopsAfter(0)
50 1354 : { }
51 :
52 :
53 : void
54 2972 : NBPTLine::addPTStop(std::shared_ptr<NBPTStop> pStop) {
55 8574 : 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 2886 : myPTStops.push_back(pStop);
64 : }
65 :
66 :
67 : const std::vector<std::shared_ptr<NBPTStop> >&
68 38460 : NBPTLine::getStops() {
69 38460 : return myPTStops;
70 : }
71 :
72 :
73 : void
74 359 : NBPTLine::write(OutputDevice& device) {
75 359 : device.openTag(SUMO_TAG_PT_LINE);
76 359 : device.writeAttr(SUMO_ATTR_ID, myPTLineId);
77 359 : if (!myName.empty()) {
78 714 : device.writeAttr(SUMO_ATTR_NAME, StringUtils::escapeXML(myName));
79 : }
80 :
81 359 : device.writeAttr(SUMO_ATTR_LINE, StringUtils::escapeXML(myRef));
82 359 : device.writeAttr(SUMO_ATTR_TYPE, myType);
83 359 : device.writeAttr(SUMO_ATTR_VCLASS, toString(myVClass));
84 359 : if (myInterval > 0) {
85 : // write seconds
86 204 : device.writeAttr(SUMO_ATTR_PERIOD, 60 * myInterval);
87 : }
88 359 : if (myNightService != "") {
89 28 : device.writeAttr("nightService", myNightService);
90 : }
91 :
92 359 : if (myColor.isValid()) {
93 : device.writeAttr(SUMO_ATTR_COLOR, myColor);
94 : }
95 359 : device.writeAttr("completeness", (double)myPTStops.size() / myNumOfStops);
96 359 : if (myMissingStopsBefore != 0) {
97 552 : device.writeAttr("missingBefore", myMissingStopsBefore);
98 : }
99 359 : if (myMissingStopsAfter != 0) {
100 568 : device.writeAttr("missingAfter", myMissingStopsAfter);
101 : }
102 :
103 359 : if (!myRoute.empty()) {
104 316 : device.openTag(SUMO_TAG_ROUTE);
105 316 : device.writeAttr(SUMO_ATTR_EDGES, myRoute);
106 632 : device.closeTag();
107 : }
108 :
109 1429 : for (auto& myPTStop : myPTStops) {
110 1070 : device.openTag(SUMO_TAG_BUS_STOP);
111 2140 : device.writeAttr(SUMO_ATTR_ID, myPTStop->getID());
112 1070 : device.writeAttr(SUMO_ATTR_NAME, StringUtils::escapeXML(myPTStop->getName()));
113 2140 : device.closeTag();
114 : }
115 359 : device.closeTag();
116 :
117 359 : }
118 :
119 :
120 : void
121 89451 : NBPTLine::addWayNode(long long int way, long long int node) {
122 89451 : std::string wayStr = toString(way);
123 89451 : if (wayStr != myCurrentWay) {
124 : myCurrentWay = wayStr;
125 13832 : myWays.push_back(wayStr);
126 : }
127 89451 : myWayNodes[wayStr].push_back(node);
128 89451 : }
129 :
130 :
131 : const std::vector<long long int>*
132 6625 : NBPTLine::getWayNodes(std::string wayId) {
133 6625 : if (myWayNodes.find(wayId) != myWayNodes.end()) {
134 6625 : return &myWayNodes[wayId];
135 : }
136 : return nullptr;
137 : }
138 :
139 :
140 : void
141 1189 : NBPTLine::setEdges(const std::vector<NBEdge*>& edges) {
142 1189 : myRoute = edges;
143 : // ensure permissions
144 31339 : for (NBEdge* e : edges) {
145 30150 : SVCPermissions permissions = e->getPermissions();
146 30150 : if ((permissions & myVClass) != myVClass) {
147 : SVCPermissions nVuln = ~(SVC_PEDESTRIAN | SVC_BICYCLE);
148 68 : if (permissions != 0 && (permissions & nVuln) == 0) {
149 : // this is a footpath or sidewalk. Add another lane
150 7 : 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 1189 : }
163 :
164 :
165 : void
166 1355 : NBPTLine::setNumOfStops(int numStops, int missingBefore, int missingAfter) {
167 1355 : myNumOfStops = numStops;
168 1355 : myMissingStopsBefore = missingBefore;
169 1355 : myMissingStopsAfter = missingAfter;
170 1355 : }
171 :
172 :
173 : const std::vector<NBEdge*>&
174 3202 : NBPTLine::getRoute() const {
175 3202 : return myRoute;
176 : }
177 :
178 :
179 : std::vector<std::pair<NBEdge*, std::string> >
180 152 : NBPTLine::getStopEdges(const NBEdgeCont& ec) const {
181 : std::vector<std::pair<NBEdge*, std::string> > result;
182 785 : for (std::shared_ptr<NBPTStop> stop : myPTStops) {
183 633 : NBEdge* e = ec.retrieve(stop->getEdgeId());
184 633 : if (e != nullptr) {
185 1266 : result.push_back({e, stop->getID()});
186 : }
187 : }
188 152 : return result;
189 0 : }
190 :
191 :
192 : NBEdge*
193 152 : NBPTLine::getRouteStart(const NBEdgeCont& ec) const {
194 : std::vector<NBEdge*> validEdges;
195 : // filter out edges that have been removed due to joining junctions
196 5450 : for (NBEdge* e : myRoute) {
197 5298 : if (ec.retrieve(e->getID())) {
198 5298 : validEdges.push_back(e);
199 : }
200 : }
201 152 : if (validEdges.size() == 0) {
202 : return nullptr;
203 : }
204 : // filter out edges after the first stop
205 140 : if (myPTStops.size() > 0) {
206 139 : NBEdge* firstStopEdge = ec.retrieve(myPTStops.front()->getEdgeId());
207 139 : 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 139 : auto it = std::find(validEdges.begin(), validEdges.end(), firstStopEdge);
213 139 : 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 131 : return validEdges.front();
219 152 : }
220 :
221 :
222 : NBEdge*
223 152 : NBPTLine::getRouteEnd(const NBEdgeCont& ec) const {
224 : std::vector<NBEdge*> validEdges;
225 : // filter out edges that have been removed due to joining junctions
226 5450 : for (NBEdge* e : myRoute) {
227 5298 : if (ec.retrieve(e->getID())) {
228 5298 : validEdges.push_back(e);
229 : }
230 : }
231 152 : if (validEdges.size() == 0) {
232 : return nullptr;
233 : }
234 : // filter out edges after the last stop
235 140 : if (myPTStops.size() > 0) {
236 139 : NBEdge* lastStopEdge = ec.retrieve(myPTStops.back()->getEdgeId());
237 139 : 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 139 : auto it = std::find(validEdges.begin(), validEdges.end(), lastStopEdge);
243 139 : 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 129 : return validEdges.back();
249 152 : }
250 :
251 :
252 : bool
253 145 : NBPTLine::isConsistent(std::vector<NBEdge*> stops) const {
254 145 : if (myRoute.empty() || stops.empty()) {
255 : return true;
256 : }
257 132 : 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 4747 : for (const NBEdge* const e : myRoute) {
267 5321 : while (stopIt != stops.end() && e == *stopIt) {
268 : ++stopIt;
269 : }
270 4739 : 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 5121 : NBPTLine::replaceEdge(const std::string& edgeID, const EdgeVector& replacement) {
290 5121 : EdgeVector oldRoute = myRoute;
291 : myRoute.clear();
292 203223 : for (NBEdge* e : oldRoute) {
293 198102 : if (e->getID() == edgeID) {
294 10774 : for (NBEdge* e2 : replacement) {
295 5638 : if (myRoute.empty() || myRoute.back() != e2) {
296 752 : myRoute.push_back(e2);
297 : }
298 : }
299 : } else {
300 192966 : myRoute.push_back(e);
301 : }
302 : }
303 5121 : }
304 :
305 :
306 : void
307 1233 : NBPTLine::deleteInvalidStops(const NBEdgeCont& ec, const NBPTStopCont& sc) {
308 : // delete stops that are missing or have no edge
309 4708 : for (auto it = myPTStops.begin(); it != myPTStops.end();) {
310 : std::shared_ptr<NBPTStop> stop = *it;
311 10421 : if (sc.get(stop->getID()) == nullptr ||
312 3471 : ec.getByID(stop->getEdgeId()) == nullptr) {
313 444 : WRITE_WARNINGF(TL("Removed invalid stop '%' from line '%'."), stop->getID(), getLineID());
314 148 : it = myPTStops.erase(it);
315 : } else {
316 : it++;
317 : }
318 :
319 : }
320 1233 : }
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 1096 : NBPTLine::removeInvalidEdges(const NBEdgeCont& ec) {
347 19952 : for (int i = 0; i < (int)myRoute.size();) {
348 18856 : 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 18856 : } else if (ec.retrieve(myRoute[i]->getID()) == nullptr) {
353 2321 : myRoute.erase(myRoute.begin() + i);
354 : } else {
355 16535 : i++;
356 : }
357 : }
358 1096 : }
359 :
360 :
361 : /****************************************************************************/
|