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 1764 : 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 1764 : SUMOVehicleClass vClass, RGBColor color) :
39 1764 : myName(name),
40 1764 : myType(type),
41 1764 : myPTLineId(id),
42 1764 : myRef(ref != "" ? ref : name),
43 1764 : myColor(color),
44 1764 : myInterval(interval),
45 1764 : myNightService(nightService),
46 1764 : myVClass(vClass),
47 1764 : myNumOfStops(0),
48 1764 : myMissingStopsBefore(0),
49 1764 : myMissingStopsAfter(0)
50 1764 : { }
51 :
52 :
53 : void
54 4300 : NBPTLine::addPTStop(std::shared_ptr<NBPTStop> pStop) {
55 18330 : 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 118 : if (myPTStops.back()->isPlatform() && !pStop->isPlatform()) {
58 : myPTStops.pop_back();
59 108 : } else if (pStop->isPlatform()) {
60 : return;
61 : }
62 : }
63 4214 : myPTStops.push_back(pStop);
64 : }
65 :
66 :
67 : const std::vector<std::shared_ptr<NBPTStop> >&
68 51540 : NBPTLine::getStops() {
69 51540 : return myPTStops;
70 : }
71 :
72 :
73 : void
74 437 : NBPTLine::write(OutputDevice& device) {
75 437 : device.openTag(SUMO_TAG_PT_LINE);
76 437 : device.writeAttr(SUMO_ATTR_ID, myPTLineId);
77 437 : if (!myName.empty()) {
78 870 : device.writeAttr(SUMO_ATTR_NAME, StringUtils::escapeXML(myName));
79 : }
80 :
81 437 : device.writeAttr(SUMO_ATTR_LINE, StringUtils::escapeXML(myRef));
82 437 : device.writeAttr(SUMO_ATTR_TYPE, myType);
83 437 : device.writeAttr(SUMO_ATTR_VCLASS, toString(myVClass));
84 437 : if (myInterval > 0) {
85 : // write seconds
86 306 : device.writeAttr(SUMO_ATTR_PERIOD, 60 * myInterval);
87 : }
88 437 : if (myNightService != "") {
89 20 : device.writeAttr("nightService", myNightService);
90 : }
91 :
92 437 : if (myColor.isValid()) {
93 : device.writeAttr(SUMO_ATTR_COLOR, myColor);
94 : }
95 437 : device.writeAttr("completeness", (double)myPTStops.size() / myNumOfStops);
96 437 : if (myMissingStopsBefore != 0) {
97 670 : device.writeAttr("missingBefore", myMissingStopsBefore);
98 : }
99 437 : if (myMissingStopsAfter != 0) {
100 686 : device.writeAttr("missingAfter", myMissingStopsAfter);
101 : }
102 :
103 437 : if (!myRoute.empty()) {
104 394 : device.openTag(SUMO_TAG_ROUTE);
105 394 : device.writeAttr(SUMO_ATTR_EDGES, myRoute);
106 788 : device.closeTag();
107 : }
108 :
109 1811 : for (auto& myPTStop : myPTStops) {
110 1374 : device.openTag(SUMO_TAG_BUS_STOP);
111 2748 : device.writeAttr(SUMO_ATTR_ID, myPTStop->getID());
112 2748 : device.writeAttr(SUMO_ATTR_NAME, StringUtils::escapeXML(myPTStop->getName()));
113 2748 : device.closeTag();
114 : }
115 437 : device.closeTag();
116 :
117 437 : }
118 :
119 :
120 : void
121 138896 : NBPTLine::addWayNode(long long int way, long long int node) {
122 138896 : std::string wayStr = toString(way);
123 138896 : if (wayStr != myCurrentWay) {
124 : myCurrentWay = wayStr;
125 22138 : myWays.push_back(wayStr);
126 : }
127 138896 : myWayNodes[wayStr].push_back(node);
128 138896 : }
129 :
130 :
131 : const std::vector<long long int>*
132 10275 : NBPTLine::getWayNodes(std::string wayId) {
133 10275 : if (myWayNodes.find(wayId) != myWayNodes.end()) {
134 10275 : return &myWayNodes[wayId];
135 : }
136 : return nullptr;
137 : }
138 :
139 :
140 : void
141 1636 : NBPTLine::setEdges(const std::vector<NBEdge*>& edges) {
142 1636 : myRoute = edges;
143 : // ensure permissions
144 49037 : for (NBEdge* e : edges) {
145 47401 : SVCPermissions permissions = e->getPermissions();
146 47401 : if ((permissions & myVClass) != myVClass) {
147 : SVCPermissions nVuln = ~(SVC_PEDESTRIAN | SVC_BICYCLE);
148 78 : 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 71 : for (int i = 0; i < (int)e->getNumLanes(); i++) {
154 71 : if ((e->getPermissions(i) & nVuln) != 0) {
155 71 : e->allowVehicleClass(i, myVClass);
156 : break;
157 : }
158 : }
159 : }
160 : }
161 : }
162 1636 : }
163 :
164 :
165 : void
166 1765 : NBPTLine::setNumOfStops(int numStops, int missingBefore, int missingAfter) {
167 1765 : myNumOfStops = numStops;
168 1765 : myMissingStopsBefore = missingBefore;
169 1765 : myMissingStopsAfter = missingAfter;
170 1765 : }
171 :
172 :
173 : const std::vector<NBEdge*>&
174 4314 : NBPTLine::getRoute() const {
175 4314 : return myRoute;
176 : }
177 :
178 :
179 : std::vector<std::pair<NBEdge*, std::string> >
180 240 : NBPTLine::getStopEdges(const NBEdgeCont& ec) const {
181 : std::vector<std::pair<NBEdge*, std::string> > result;
182 1201 : for (std::shared_ptr<NBPTStop> stop : myPTStops) {
183 961 : NBEdge* e = ec.retrieve(stop->getEdgeId());
184 961 : if (e != nullptr) {
185 1922 : result.push_back({e, stop->getID()});
186 : }
187 : }
188 240 : return result;
189 0 : }
190 :
191 :
192 : NBEdge*
193 240 : NBPTLine::getRouteStart(const NBEdgeCont& ec) const {
194 : std::vector<NBEdge*> validEdges;
195 : // filter out edges that have been removed due to joining junctions
196 8924 : for (NBEdge* e : myRoute) {
197 8684 : if (ec.retrieve(e->getID())) {
198 8684 : validEdges.push_back(e);
199 : }
200 : }
201 240 : if (validEdges.size() == 0) {
202 : return nullptr;
203 : }
204 : // filter out edges after the first stop
205 228 : if (myPTStops.size() > 0) {
206 227 : NBEdge* firstStopEdge = ec.retrieve(myPTStops.front()->getEdgeId());
207 227 : if (firstStopEdge == nullptr) {
208 0 : WRITE_WARNINGF(TL("Could not retrieve edge '%' for first stop of line '%'."), myPTStops.front()->getEdgeId(), myPTLineId);
209 10 : return nullptr;
210 :
211 : }
212 227 : auto it = std::find(validEdges.begin(), validEdges.end(), firstStopEdge);
213 227 : if (it == validEdges.end()) {
214 40 : WRITE_WARNINGF(TL("First stop edge '%' is not part of the route of line '%'."), firstStopEdge->getID(), myPTLineId);
215 10 : return nullptr;
216 : }
217 : }
218 218 : return validEdges.front();
219 : }
220 :
221 :
222 : NBEdge*
223 240 : NBPTLine::getRouteEnd(const NBEdgeCont& ec) const {
224 : std::vector<NBEdge*> validEdges;
225 : // filter out edges that have been removed due to joining junctions
226 8924 : for (NBEdge* e : myRoute) {
227 8684 : if (ec.retrieve(e->getID())) {
228 8684 : validEdges.push_back(e);
229 : }
230 : }
231 240 : if (validEdges.size() == 0) {
232 : return nullptr;
233 : }
234 : // filter out edges after the last stop
235 228 : if (myPTStops.size() > 0) {
236 227 : NBEdge* lastStopEdge = ec.retrieve(myPTStops.back()->getEdgeId());
237 227 : if (lastStopEdge == nullptr) {
238 0 : WRITE_WARNINGF(TL("Could not retrieve edge '%' for last stop of line '%'."), myPTStops.back()->getEdgeId(), myPTLineId);
239 15 : return nullptr;
240 :
241 : }
242 227 : auto it = std::find(validEdges.begin(), validEdges.end(), lastStopEdge);
243 227 : if (it == validEdges.end()) {
244 60 : WRITE_WARNINGF(TL("Last stop edge '%' is not part of the route of line '%'."), lastStopEdge->getID(), myPTLineId);
245 15 : return nullptr;
246 : }
247 : }
248 213 : return validEdges.back();
249 : }
250 :
251 :
252 : bool
253 233 : NBPTLine::isConsistent(std::vector<NBEdge*> stops) const {
254 233 : if (myRoute.empty() || stops.empty()) {
255 : return true;
256 : }
257 220 : if (stops.size() > 1 && stops.front() == stops.back()) {
258 : // circular route where we don't expect the route edges to occur twice
259 11 : 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 7812 : for (const NBEdge* const e : myRoute) {
267 8700 : while (stopIt != stops.end() && e == *stopIt) {
268 : ++stopIt;
269 : }
270 7799 : 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 9628 : NBPTLine::replaceEdge(const std::string& edgeID, const EdgeVector& replacement) {
290 9628 : EdgeVector oldRoute = myRoute;
291 : myRoute.clear();
292 393216 : for (NBEdge* e : oldRoute) {
293 383588 : if (e->getID() == edgeID) {
294 19818 : for (NBEdge* e2 : replacement) {
295 10160 : if (myRoute.empty() || myRoute.back() != e2) {
296 880 : myRoute.push_back(e2);
297 : }
298 : }
299 : } else {
300 373930 : myRoute.push_back(e);
301 : }
302 : }
303 9628 : }
304 :
305 :
306 : void
307 1685 : NBPTLine::deleteInvalidStops(const NBEdgeCont& ec, const NBPTStopCont& sc) {
308 : // delete stops that are missing or have no edge
309 6811 : for (auto it = myPTStops.begin(); it != myPTStops.end();) {
310 : std::shared_ptr<NBPTStop> stop = *it;
311 15379 : if (sc.get(stop->getID()) == nullptr ||
312 5122 : ec.getByID(stop->getEdgeId()) == nullptr) {
313 462 : WRITE_WARNINGF(TL("Removed invalid stop '%' from line '%'."), stop->getID(), getLineID());
314 154 : it = myPTStops.erase(it);
315 : } else {
316 : it++;
317 : }
318 :
319 : }
320 1685 : }
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 1462 : NBPTLine::removeInvalidEdges(const NBEdgeCont& ec) {
347 29791 : for (int i = 0; i < (int)myRoute.size();) {
348 28329 : 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 28329 : } else if (ec.retrieve(myRoute[i]->getID()) == nullptr) {
353 4010 : myRoute.erase(myRoute.begin() + i);
354 : } else {
355 24319 : i++;
356 : }
357 : }
358 1462 : }
359 :
360 :
361 : /****************************************************************************/
|