1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file IntermodalNetwork.h
11 /// @author Jakob Erdmann
12 /// @author Michael Behrisch
13 /// @author Robert Hilbrich
14 /// @date Mon, 03 March 2014
15 /// @version $Id$
16 ///
17 // The Edge definition for the Intermodal Router
18 /****************************************************************************/
19 #ifndef IntermodalNetwork_h
20 #define IntermodalNetwork_h
21
22
23 // ===========================================================================
24 // included modules
25 // ===========================================================================
26 #include <config.h>
27
28 #include <string>
29 #include <vector>
30 #include <algorithm>
31 #include <assert.h>
32 #include <utils/common/MsgHandler.h>
33 #include <utils/common/Named.h>
34 #include <utils/common/SUMOTime.h>
35 #include <utils/common/ToString.h>
36 #include <utils/geom/Position.h>
37 #include <utils/vehicle/SUMOVehicleParameter.h>
38 #include "AccessEdge.h"
39 #include "CarEdge.h"
40 #include "IntermodalEdge.h"
41 #include "PedestrianEdge.h"
42 #include "PublicTransportEdge.h"
43 #include "StopEdge.h"
44
45 //#define IntermodalRouter_DEBUG_NETWORK
46
47
48 // ===========================================================================
49 // function definitions
50 // ===========================================================================
51 template <class E, class L>
getSidewalk(const E * edge)52 inline const L* getSidewalk(const E* edge) {
53 if (edge == nullptr) {
54 return nullptr;
55 }
56 // prefer lanes that are exclusive to pedestrians
57 const std::vector<L*>& lanes = edge->getLanes();
58 for (const L* const lane : lanes) {
59 if (lane->getPermissions() == SVC_PEDESTRIAN) {
60 return lane;
61 }
62 }
63 for (const L* const lane : lanes) {
64 if (lane->allowsVehicleClass(SVC_PEDESTRIAN)) {
65 return lane;
66 }
67 }
68 return nullptr;
69 }
70
71
72 // ===========================================================================
73 // class definitions
74 // ===========================================================================
75 /// @brief the intermodal network storing edges, connections and the mappings to the "real" edges
76 template<class E, class L, class N, class V>
77 class IntermodalNetwork {
78 private:
79 typedef IntermodalEdge<E, L, N, V> _IntermodalEdge;
80 typedef AccessEdge<E, L, N, V> _AccessEdge;
81 typedef PedestrianEdge<E, L, N, V> _PedestrianEdge;
82 typedef PublicTransportEdge<E, L, N, V> _PTEdge;
83 typedef std::pair<_IntermodalEdge*, _IntermodalEdge*> EdgePair;
84
85 public:
86 /** @brief where mode changes are possible
87 */
88 enum ModeChangeOptions {
89 /// @brief parking areas
90 PARKING_AREAS = 1,
91 /// @brief public transport stops and access
92 PT_STOPS = 2,
93 /// @brief junctions with edges allowing the additional mode
94 ALL_JUNCTIONS = 4
95 };
96
97 /* @brief build the pedestrian part of the intermodal network (once)
98 * @param edges The list of MSEdge or ROEdge to build from
99 * @param numericalID the start number for the creation of new edges
100 */
101 IntermodalNetwork(const std::vector<E*>& edges, const bool pedestrianOnly, const int carWalkTransfer = 0)
102 : myNumericalID(0), myCarWalkTransfer(carWalkTransfer) {
103 #ifdef IntermodalRouter_DEBUG_NETWORK
104 std::cout << "initIntermodalNetwork\n";
105 #endif
106 // build the pedestrian edges and the depart / arrival connectors with lookup tables
107 bool haveSeenWalkingArea = false;
108 for (const E* const edge : edges) {
109 if (edge->isTazConnector()) {
110 continue;
111 }
112 const L* lane = getSidewalk<E, L>(edge);
113 if (lane != 0) {
114 if (edge->isWalkingArea()) {
115 // only a single edge
116 addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, true));
117 myBidiLookup[edge] = std::make_pair(myEdges.back(), myEdges.back());
118 myDepartLookup[edge].push_back(myEdges.back());
119 myArrivalLookup[edge].push_back(myEdges.back());
120 haveSeenWalkingArea = true;
121 } else { // regular edge or crossing
122 // forward and backward edges
123 addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, true));
124 addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, false));
125 myBidiLookup[edge] = std::make_pair(myEdges[myNumericalID - 2], myEdges.back());
126 }
127 }
128 if (!edge->isWalkingArea()) {
129 // depart and arrival edges (the router can decide the initial direction to take and the direction to arrive from)
130 _IntermodalEdge* const departConn = new _IntermodalEdge(edge->getID() + "_depart_connector", myNumericalID++, edge, "!connector");
131 _IntermodalEdge* const arrivalConn = new _IntermodalEdge(edge->getID() + "_arrival_connector", myNumericalID++, edge, "!connector");
132 addConnectors(departConn, arrivalConn, 0);
133 }
134 }
135
136 // build the walking connectors if there are no walking areas
137 for (const E* const edge : edges) {
138 if (edge->isTazConnector() || edge->isInternal()) {
139 continue;
140 }
141 if (haveSeenWalkingArea) {
142 // connectivity needs to be ensured only in the real intermodal case, for simple pedestrian routing we don't have connectors if we have walking areas
143 if (!pedestrianOnly && getSidewalk<E, L>(edge) == nullptr) {
144 const N* const node = edge->getToJunction();
145 if (myWalkingConnectorLookup.count(node) == 0) {
146 addEdge(new _IntermodalEdge(node->getID() + "_walking_connector", myNumericalID++, nullptr, "!connector"));
147 myWalkingConnectorLookup[node] = myEdges.back();
148 }
149 }
150 } else {
151 for (const N* const node : {
152 edge->getFromJunction(), edge->getToJunction()
153 }) {
154 if (myWalkingConnectorLookup.count(node) == 0) {
155 addEdge(new _IntermodalEdge(node->getID() + "_walking_connector", myNumericalID++, nullptr, "!connector"));
156 myWalkingConnectorLookup[node] = myEdges.back();
157 }
158 }
159 }
160 }
161 // build the connections
162 for (const E* const edge : edges) {
163 const L* const sidewalk = getSidewalk<E, L>(edge);
164 if (sidewalk == nullptr) {
165 continue;
166 }
167 // find all incoming and outgoing lanes for the sidewalk and
168 // connect the corresponding IntermodalEdges
169 const EdgePair& pair = getBothDirections(edge);
170 #ifdef IntermodalRouter_DEBUG_NETWORK
171 std::cout << " building connections from " << sidewalk->getID() << "\n";
172 #endif
173 if (haveSeenWalkingArea) {
174 const std::vector<std::pair<const L*, const E*> > outgoing = sidewalk->getOutgoingViaLanes();
175 // if one of the outgoing lanes is a walking area it must be used.
176 // All other connections shall be ignored
177 // if it has no outgoing walking area, it probably is a walking area itself
178 bool hasWalkingArea = false;
179 for (const auto& target : outgoing) {
180 if (target.first->getEdge().isWalkingArea()) {
181 hasWalkingArea = true;
182 break;
183 }
184 }
185 for (const auto& target : outgoing) {
186 const E* const targetEdge = &(target.first->getEdge());
187 const bool used = (target.first == getSidewalk<E, L>(targetEdge)
188 && (!hasWalkingArea || targetEdge->isWalkingArea()));
189 #ifdef IntermodalRouter_DEBUG_NETWORK
190 const L* potTarget = getSidewalk<E, L>(targetEdge);
191 std::cout << " lane=" << (potTarget == 0 ? "NULL" : potTarget->getID()) << (used ? "(used)" : "") << "\n";
192 #endif
193 if (used) {
194 const EdgePair& targetPair = getBothDirections(targetEdge);
195 pair.first->addSuccessor(targetPair.first);
196 targetPair.second->addSuccessor(pair.second);
197 #ifdef IntermodalRouter_DEBUG_NETWORK
198 std::cout << " " << pair.first->getID() << " -> " << targetPair.first->getID() << "\n";
199 std::cout << " " << targetPair.second->getID() << " -> " << pair.second->getID() << "\n";
200 #endif
201 }
202 }
203 }
204 // We may have a network without pedestrian structures or a car-only edge.
205 // In the first case we assume that all sidewalks at a junction are interconnected,
206 // in the second we connect all car-only edges to all sidewalks.
207 _IntermodalEdge* const toNodeConn = myWalkingConnectorLookup[edge->getToJunction()];
208 if (toNodeConn != nullptr) {
209 // Check for the outgoing vias and use the shortest one as an approximation
210 const std::vector<std::pair<const L*, const E*> > outgoing = sidewalk->getOutgoingViaLanes();
211 double minViaLength = std::numeric_limits<double>::max();
212 const E* minVia = nullptr;
213 for (const auto& target : outgoing) {
214 if (target.second != nullptr && target.second->getLength() < minViaLength) {
215 minViaLength = target.second->getLength();
216 minVia = target.second;
217 }
218 }
219 EdgePair interVia = std::make_pair(nullptr, nullptr);
220 if (minVia != nullptr) {
221 const auto it = myBidiLookup.find(minVia);
222 if (it != myBidiLookup.end()) {
223 interVia = it->second;
224 }
225 }
226 if (!haveSeenWalkingArea) {
227 // if we have walking areas we should use them and not the connector
228 pair.first->addSuccessor(toNodeConn, interVia.first);
229 }
230 toNodeConn->addSuccessor(pair.second, interVia.second);
231 }
232 _IntermodalEdge* const fromNodeConn = myWalkingConnectorLookup[edge->getFromJunction()];
233 if (fromNodeConn != nullptr) {
234 if (!haveSeenWalkingArea) {
235 pair.second->addSuccessor(fromNodeConn);
236 }
237 fromNodeConn->addSuccessor(pair.first);
238 }
239 if (!edge->isWalkingArea()) {
240 // build connections from depart connector
241 _IntermodalEdge* startConnector = getDepartConnector(edge);
242 startConnector->addSuccessor(pair.first);
243 startConnector->addSuccessor(pair.second);
244 // build connections to arrival connector
245 _IntermodalEdge* endConnector = getArrivalConnector(edge);
246 pair.first->addSuccessor(endConnector);
247 pair.second->addSuccessor(endConnector);
248 }
249 #ifdef IntermodalRouter_DEBUG_NETWORK
250 std::cout << " " << startConnector->getID() << " -> " << pair.first->getID() << "\n";
251 std::cout << " " << startConnector->getID() << " -> " << pair.second->getID() << "\n";
252 std::cout << " " << pair.first->getID() << " -> " << endConnector->getID() << "\n";
253 std::cout << " " << pair.second->getID() << " -> " << endConnector->getID() << "\n";
254 #endif
255 }
256 }
257
~IntermodalNetwork()258 ~IntermodalNetwork() {
259 for (typename std::vector<_IntermodalEdge*>::iterator it = myEdges.begin(); it != myEdges.end(); ++it) {
260 delete *it;
261 }
262 }
263
addEdge(_IntermodalEdge * edge)264 void addEdge(_IntermodalEdge* edge) {
265 while ((int)myEdges.size() <= edge->getNumericalID()) {
266 myEdges.push_back(0);
267 }
268 myEdges[edge->getNumericalID()] = edge;
269 }
270
addConnectors(_IntermodalEdge * const depConn,_IntermodalEdge * const arrConn,const int index)271 void addConnectors(_IntermodalEdge* const depConn, _IntermodalEdge* const arrConn, const int index) {
272 addEdge(depConn);
273 addEdge(arrConn);
274 myDepartLookup[depConn->getEdge()].insert(myDepartLookup[depConn->getEdge()].begin() + index, depConn);
275 myArrivalLookup[arrConn->getEdge()].insert(myArrivalLookup[arrConn->getEdge()].begin() + index, arrConn);
276 }
277
getAllEdges()278 const std::vector<_IntermodalEdge*>& getAllEdges() {
279 return myEdges;
280 }
281
282 /// @brief Returns the pair of forward and backward edge
getBothDirections(const E * e)283 const EdgePair& getBothDirections(const E* e) const {
284 typename std::map<const E*, EdgePair>::const_iterator it = myBidiLookup.find(e);
285 if (it == myBidiLookup.end()) {
286 assert(false);
287 throw ProcessError("Edge '" + e->getID() + "' not found in intermodal network.'");
288 }
289 return (*it).second;
290 }
291
292 /// @brief Returns the departing intermodal edge
getDepartEdge(const E * e,const double pos)293 const _IntermodalEdge* getDepartEdge(const E* e, const double pos) const {
294 typename std::map<const E*, std::vector<_IntermodalEdge*> >::const_iterator it = myDepartLookup.find(e);
295 if (it == myDepartLookup.end()) {
296 throw ProcessError("Depart edge '" + e->getID() + "' not found in intermodal network.");
297 }
298 double totalLength = 0.;
299 double bestDist = std::numeric_limits<double>::max();
300 const _IntermodalEdge* best = nullptr;
301 for (const _IntermodalEdge* split : it->second) {
302 totalLength += split->getLength();
303 double dist = fabs(totalLength - pos);
304 if (dist < bestDist) {
305 bestDist = dist;
306 best = split;
307 } else {
308 break;
309 }
310 }
311 assert(best != 0);
312 return best;
313 }
314
315 /// @brief Returns the departing intermodal connector at the given split offset
316 _IntermodalEdge* getDepartConnector(const E* e, const int splitIndex = 0) const {
317 typename std::map<const E*, std::vector<_IntermodalEdge*> >::const_iterator it = myDepartLookup.find(e);
318 if (it == myDepartLookup.end()) {
319 throw ProcessError("Depart edge '" + e->getID() + "' not found in intermodal network.");
320 }
321 if (splitIndex >= (int)it->second.size()) {
322 throw ProcessError("Split index " + toString(splitIndex) + " invalid for depart edge '" + e->getID() + "' .");
323 }
324 return it->second[splitIndex];
325 }
326
327 /// @brief Returns the arriving intermodal edge
getArrivalEdge(const E * e,const double pos)328 _IntermodalEdge* getArrivalEdge(const E* e, const double pos) const {
329 typename std::map<const E*, std::vector<_IntermodalEdge*> >::const_iterator it = myArrivalLookup.find(e);
330 if (it == myArrivalLookup.end()) {
331 throw ProcessError("Arrival edge '" + e->getID() + "' not found in intermodal network.");
332 }
333 const std::vector<_IntermodalEdge*>& splitList = it->second;
334 typename std::vector<_IntermodalEdge*>::const_iterator splitIt = splitList.begin();
335 double totalLength = 0.;
336 while (splitIt != splitList.end() && totalLength + (*splitIt)->getLength() < pos) {
337 totalLength += (*splitIt)->getLength();
338 ++splitIt;
339 }
340 return *splitIt;
341 }
342
343 /// @brief Returns the arriving intermodal connector at the given split offset
344 _IntermodalEdge* getArrivalConnector(const E* e, const int splitIndex = 0) const {
345 return myArrivalLookup.find(e)->second[splitIndex];
346 }
347
348 /// @brief Returns the outgoing pedestrian edge, which is either a walking area or a walking connector
getWalkingConnector(const E * e)349 _IntermodalEdge* getWalkingConnector(const E* e) const {
350 typename std::map<const N*, _IntermodalEdge*>::const_iterator it = myWalkingConnectorLookup.find(e->getToJunction());
351 if (it == myWalkingConnectorLookup.end()) {
352 const L* const sidewalk = getSidewalk<E, L>(e);
353 if (e->isInternal() || sidewalk == 0) {
354 return 0;
355 }
356 for (const auto& target : sidewalk->getOutgoingViaLanes()) {
357 if (target.first->getEdge().isWalkingArea()) {
358 return getBothDirections(&target.first->getEdge()).first;
359 }
360 }
361 return 0;
362 }
363 return it->second;
364 }
365
addCarEdges(const std::vector<E * > & edges)366 void addCarEdges(const std::vector<E*>& edges) {
367 for (const E* const edge : edges) {
368 if (edge->getFunction() == EDGEFUNC_NORMAL || edge->getFunction() == EDGEFUNC_INTERNAL) {
369 myCarLookup[edge] = new CarEdge<E, L, N, V>(myNumericalID++, edge);
370 addEdge(myCarLookup[edge]);
371 }
372 }
373 for (const auto& edgePair : myCarLookup) {
374 _IntermodalEdge* const carEdge = edgePair.second;
375 for (const auto& suc : edgePair.first->getViaSuccessors()) {
376 _IntermodalEdge* const sucCarEdge = getCarEdge(suc.first);
377 _IntermodalEdge* const sucViaEdge = getCarEdge(suc.second);
378 if (sucCarEdge != nullptr) {
379 carEdge->addSuccessor(sucCarEdge, sucViaEdge);
380 }
381 }
382 if ((myCarWalkTransfer & ALL_JUNCTIONS) != 0) {
383 _IntermodalEdge* const walkCon = getWalkingConnector(edgePair.first);
384 if (walkCon != 0) {
385 carEdge->addSuccessor(walkCon);
386 } else {
387 // we are on an edge where pedestrians are forbidden and want to continue on an arbitrary pedestrian edge
388 for (const E* const out : edgePair.first->getToJunction()->getOutgoing()) {
389 if (!out->isInternal() && !out->isTazConnector() && getSidewalk<E, L>(out) != 0) {
390 carEdge->addSuccessor(getBothDirections(out).first);
391 }
392 }
393 for (const E* const in : edgePair.first->getToJunction()->getIncoming()) {
394 if (!in->isInternal() && !in->isTazConnector() && getSidewalk<E, L>(in) != 0) {
395 carEdge->addSuccessor(getBothDirections(in).second);
396 }
397 }
398 }
399 }
400 getDepartConnector(edgePair.first)->addSuccessor(carEdge);
401 carEdge->addSuccessor(getArrivalConnector(edgePair.first));
402 }
403 }
404
405 /// @brief Returns the associated car edge
getCarEdge(const E * e)406 _IntermodalEdge* getCarEdge(const E* e) const {
407 typename std::map<const E*, _IntermodalEdge*>::const_iterator it = myCarLookup.find(e);
408 if (it == myCarLookup.end()) {
409 return nullptr;
410 }
411 return it->second;
412 }
413
414 /// @brief Returns the associated stop edge
getStopEdge(const std::string & stopId)415 _IntermodalEdge* getStopEdge(const std::string& stopId) const {
416 auto it = myStopConnections.find(stopId);
417 if (it == myStopConnections.end()) {
418 return nullptr;
419 }
420 return it->second;
421 }
422
423 /** @brief Adds access edges for stopping places to the intermodal network
424 *
425 * This method creates an intermodal stop edge to represent the stopping place
426 * (if not present yet) and determines the edges which need to be splitted (usually the forward
427 * and the backward pedestrian edges and the car edge) and calls splitEdge for the
428 * actual split and the connection of the stop edge with access edges. After that it adds and adapts
429 * the depart and arrival connectors to the new edge(s).
430 *
431 * @param[in] stopId The id of the stop to add
432 * @param[in] stopEdge The edge on which the stop is located
433 * @param[in] pos The relative position on the edge where the stop is located
434 * @param[in] category The type of stop
435 */
addAccess(const std::string & stopId,const E * stopEdge,const double pos,const double length,const SumoXMLTag category)436 void addAccess(const std::string& stopId, const E* stopEdge, const double pos, const double length, const SumoXMLTag category) {
437 assert(stopEdge != 0);
438 //std::cout << "addAccess stopId=" << stopId << " stopEdge=" << stopEdge->getID() << " pos=" << pos << " length=" << length << " cat=" << category << "\n";
439 if (myStopConnections.count(stopId) == 0) {
440 myStopConnections[stopId] = new StopEdge<E, L, N, V>(stopId, myNumericalID++, stopEdge);
441 addEdge(myStopConnections[stopId]);
442 }
443 _IntermodalEdge* const stopConn = myStopConnections[stopId];
444 const L* lane = getSidewalk<E, L>(stopEdge);
445 if (lane != 0) {
446 const std::pair<_IntermodalEdge*, _IntermodalEdge*>& pair = getBothDirections(stopEdge);
447 double relPos;
448 bool needSplit;
449 const int splitIndex = findSplitIndex(pair.first, pos, relPos, needSplit);
450 _IntermodalEdge* const fwdSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, true, pos) : nullptr;
451 splitEdge(pair.first, splitIndex, fwdSplit, relPos, length, needSplit, stopConn);
452 _IntermodalEdge* const backSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, false, pos) : nullptr;
453 splitEdge(pair.second, splitIndex, backSplit, relPos, length, needSplit, stopConn, false);
454 _IntermodalEdge* carSplit = nullptr;
455 if (myCarLookup.count(stopEdge) > 0) {
456 if (needSplit) {
457 carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
458 }
459 splitEdge(myCarLookup[stopEdge], splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false);
460 }
461 if (needSplit) {
462 if (carSplit != nullptr && ((category == SUMO_TAG_PARKING_AREA && (myCarWalkTransfer & PARKING_AREAS) != 0) || (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & PT_STOPS) != 0))) {
463 // adding access from car to walk
464 _IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
465 for (_IntermodalEdge* conn : {
466 fwdSplit, backSplit
467 }) {
468 _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, conn, length);
469 addEdge(access);
470 beforeSplit->addSuccessor(access);
471 access->addSuccessor(conn);
472 }
473 }
474
475 // fixing depart connections for the forward pedestrian, the backward pedestrian and the car edge
476 _IntermodalEdge* const prevDep = getDepartConnector(stopEdge, splitIndex);
477 const std::vector<_IntermodalEdge*>& backSplitList = myAccessSplits[pair.second];
478 _IntermodalEdge* const backBeforeSplit = backSplitList[backSplitList.size() - 2 - splitIndex];
479 _IntermodalEdge* const depConn = new _IntermodalEdge(stopEdge->getID() + "_depart_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
480 depConn->addSuccessor(fwdSplit);
481 depConn->addSuccessor(backBeforeSplit);
482 depConn->setLength(fwdSplit->getLength());
483 prevDep->removeSuccessor(backBeforeSplit);
484 prevDep->addSuccessor(backSplit);
485 prevDep->setLength(backSplit->getLength());
486 if (carSplit != nullptr) {
487 depConn->addSuccessor(carSplit);
488 }
489
490 // fixing arrival connections for the forward pedestrian, the backward pedestrian and the car edge
491 _IntermodalEdge* const prevArr = getArrivalConnector(stopEdge, splitIndex);
492 _IntermodalEdge* const fwdBeforeSplit = myAccessSplits[pair.first][splitIndex];
493 _IntermodalEdge* const arrConn = new _IntermodalEdge(stopEdge->getID() + "_arrival_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
494 fwdSplit->addSuccessor(arrConn);
495 backBeforeSplit->addSuccessor(arrConn);
496 arrConn->setLength(fwdSplit->getLength());
497 fwdSplit->removeSuccessor(prevArr);
498 fwdBeforeSplit->addSuccessor(prevArr);
499 prevArr->setLength(backSplit->getLength());
500 if (carSplit != nullptr) {
501 carSplit->addSuccessor(arrConn);
502 carSplit->removeSuccessor(prevArr);
503 myAccessSplits[myCarLookup[stopEdge]][splitIndex]->addSuccessor(prevArr);
504 }
505 addConnectors(depConn, arrConn, splitIndex + 1);
506 }
507 } else {
508 // pedestrians cannot walk here:
509 // add depart connectors on the stop edge so that pedestrians may start at the stop
510 auto& splitList = myDepartLookup[stopEdge];
511 assert(splitList.size() > 0);
512 typename std::vector<_IntermodalEdge*>::iterator splitIt = splitList.begin();
513 double totalLength = 0.;
514 _IntermodalEdge* last = nullptr;
515 while (splitIt != splitList.end() && totalLength < pos) {
516 totalLength += (*splitIt)->getLength();
517 last = *splitIt;
518 ++splitIt;
519 }
520 auto* stopConnector = myStopConnections[stopId];
521 // insert before last
522 const double newLength = pos - (totalLength - last->getLength());
523 stopConnector->setLength(newLength);
524 splitList.insert(splitIt - 1, stopConnector);
525 // correct length of subsequent edge
526 last->setLength(last->getLength() - newLength);
527 }
528 }
529
530 void addSchedule(const SUMOVehicleParameter& pars, const std::vector<SUMOVehicleParameter::Stop>* addStops = nullptr) {
531 SUMOTime lastUntil = 0;
532 std::vector<SUMOVehicleParameter::Stop> validStops;
533 if (addStops != nullptr) {
534 // stops are part of a stand-alone route. until times are offsets from vehicle departure
535 for (const SUMOVehicleParameter::Stop& stop : *addStops) {
536 if (myStopConnections.count(stop.busstop) > 0) {
537 // compute stop times for the first vehicle
538 const SUMOTime newUntil = stop.until + pars.depart;
539 if (newUntil >= lastUntil) {
540 validStops.push_back(stop);
541 validStops.back().until = newUntil;
542 lastUntil = newUntil;
543 } else {
544 WRITE_WARNING("Ignoring unordered stop at '" + stop.busstop + "' until " + time2string(stop.until) + " for vehicle '" + pars.id + "'.");
545 }
546 }
547 }
548 }
549 for (const SUMOVehicleParameter::Stop& stop : pars.stops) {
550 // stops are part of the vehicle until times are absolute times for the first vehicle
551 if (myStopConnections.count(stop.busstop) > 0 && stop.until >= lastUntil) {
552 validStops.push_back(stop);
553 lastUntil = stop.until;
554 } else {
555 WRITE_WARNING("Ignoring stop at '" + stop.busstop + "' until " + time2string(stop.until) + " for vehicle '" + pars.id + "'.");
556 }
557 }
558 if (validStops.size() < 2) {
559 WRITE_WARNING("Not using public transport line '" + pars.line + "' for routing persons. It has less than two usable stops.");
560 return;
561 }
562
563 typename std::vector<_PTEdge*>& lineEdges = myPTLines[pars.line];
564 if (lineEdges.empty()) {
565 _IntermodalEdge* lastStop = nullptr;
566 Position lastPos;
567 SUMOTime lastTime = 0;
568 for (const SUMOVehicleParameter::Stop& s : validStops) {
569 _IntermodalEdge* currStop = myStopConnections[s.busstop];
570 Position stopPos = E::getStopPosition(s);
571 if (lastStop != nullptr) {
572 _PTEdge* const newEdge = new _PTEdge(s.busstop, myNumericalID++, lastStop, currStop->getEdge(), pars.line, lastPos.distanceTo(stopPos));
573 addEdge(newEdge);
574 newEdge->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s.until - lastTime);
575 lastStop->addSuccessor(newEdge);
576 newEdge->addSuccessor(currStop);
577 lineEdges.push_back(newEdge);
578 }
579 lastTime = s.until;
580 lastStop = currStop;
581 lastPos = stopPos;
582 }
583 } else {
584 if (validStops.size() != lineEdges.size() + 1) {
585 WRITE_WARNING("Number of stops for public transport line '" + pars.line + "' does not match earlier definitions, ignoring schedule.");
586 return;
587 }
588 if (lineEdges.front()->getEntryStop() != myStopConnections[validStops.front().busstop]) {
589 WRITE_WARNING("Different stop for '" + pars.line + "' compared to earlier definitions, ignoring schedule.");
590 return;
591 }
592 typename std::vector<_PTEdge*>::const_iterator lineEdge = lineEdges.begin();
593 typename std::vector<SUMOVehicleParameter::Stop>::const_iterator s = validStops.begin() + 1;
594 for (; s != validStops.end(); ++s, ++lineEdge) {
595 if ((*lineEdge)->getSuccessors(SVC_IGNORING)[0] != myStopConnections[s->busstop]) {
596 WRITE_WARNING("Different stop for '" + pars.line + "' compared to earlier definitions, ignoring schedule.");
597 return;
598 }
599 }
600 SUMOTime lastTime = validStops.front().until;
601 if (lineEdges.front()->hasSchedule(lastTime)) {
602 WRITE_WARNING("Duplicate schedule for '" + pars.line + "' at time " + time2string(lastTime) + ".");
603 }
604 for (lineEdge = lineEdges.begin(), s = validStops.begin() + 1; lineEdge != lineEdges.end(); ++lineEdge, ++s) {
605 (*lineEdge)->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s->until - lastTime);
606 lastTime = s->until;
607 }
608 }
609 }
610
611
612 private:
613 /** @brief Returns where to insert or use the split edge
614 *
615 * This method determines whether an edge needs to be split at the given position
616 * (if there is not already a split nearby) and returns the corresponding index in the split list.
617 *
618 * @param[in] toSplit The first edge in the split list
619 * @param[in] pos The relative position on the edge where the stop is located
620 * @param[out] relPos The relative position on the splitted edge
621 * @param[out] needSplit whether a new split is needed or we reuse an exisiting one
622 * @return the index in the split list where the split edge needs to be added or reused
623 */
findSplitIndex(_IntermodalEdge * const toSplit,const double pos,double & relPos,bool & needSplit)624 int findSplitIndex(_IntermodalEdge* const toSplit, const double pos, double& relPos, bool& needSplit) {
625 relPos = pos;
626 needSplit = true;
627 int splitIndex = 0;
628 std::vector<_IntermodalEdge*>& splitList = myAccessSplits[toSplit];
629 if (!splitList.empty()) {
630 for (const _IntermodalEdge* const split : splitList) {
631 if (relPos < split->getLength() + POSITION_EPS) {
632 break;
633 }
634 relPos -= split->getLength();
635 splitIndex++;
636 }
637 assert(splitIndex < (int)splitList.size());
638 if (splitIndex + 1 < (int)splitList.size() && fabs(relPos - splitList[splitIndex]->getLength()) < POSITION_EPS) {
639 needSplit = false;
640 }
641 }
642 return splitIndex;
643 }
644
645 /** @brief Splits an edge (if necessary) and connects it to a stopping edge
646 *
647 * This method determines whether an edge needs to be split at the given position
648 * (if there is not already a split nearby) and connects the stop edge via new access edges.
649 *
650 * @param[in] toSplit The first edge in the split list
651 * @param[in] afterSplit The edge to add if a split is performed
652 * @param[in] pos The relative position on the edge where the stop is located
653 * @param[in] stopConn The stop edge to connect to
654 * @param[in] forward whether we are aplitting a forward edge (backward edges get different names)
655 * @param[in] addExit whether we can just enter the stop or exit as well (cars should not exit yet)
656 */
657 void splitEdge(_IntermodalEdge* const toSplit, int splitIndex,
658 _IntermodalEdge* afterSplit, const double relPos, const double length, const bool needSplit,
659 _IntermodalEdge* const stopConn, const bool forward = true, const bool addExit = true) {
660 std::vector<_IntermodalEdge*>& splitList = myAccessSplits[toSplit];
661 if (splitList.empty()) {
662 splitList.push_back(toSplit);
663 }
664 if (!forward) {
665 splitIndex = (int)splitList.size() - 1 - splitIndex;
666 if (!needSplit) {
667 splitIndex--;
668 }
669 }
670 _IntermodalEdge* beforeSplit = splitList[splitIndex];
671 if (needSplit) {
672 addEdge(afterSplit);
673 beforeSplit->transferSuccessors(afterSplit);
674 beforeSplit->addSuccessor(afterSplit);
675 if (forward) {
676 afterSplit->setLength(beforeSplit->getLength() - relPos);
677 beforeSplit->setLength(relPos);
678 } else {
679 afterSplit->setLength(relPos);
680 beforeSplit->setLength(beforeSplit->getLength() - relPos);
681 // rename backward edges for easier referencing
682 const std::string newID = beforeSplit->getID();
683 beforeSplit->setID(afterSplit->getID());
684 afterSplit->setID(newID);
685 }
686 splitList.insert(splitList.begin() + splitIndex + 1, afterSplit);
687 } else {
688 // don't split, use the present split edges
689 afterSplit = splitList[splitIndex + 1];
690 }
691 // add access to / from edge
692 _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, stopConn, length);
693 addEdge(access);
694 beforeSplit->addSuccessor(access);
695 access->addSuccessor(stopConn);
696 if (addExit) {
697 // pedestrian case only, exit from public to pedestrian
698 _AccessEdge* exit = new _AccessEdge(myNumericalID++, stopConn, afterSplit, length);
699 addEdge(exit);
700 stopConn->addSuccessor(exit);
701 exit->addSuccessor(afterSplit);
702 }
703 }
704
705
706 private:
707 /// @brief the edge dictionary
708 std::vector<_IntermodalEdge*> myEdges;
709
710 /// @brief retrieve the forward and backward edge for the given input edge E
711 std::map<const E*, EdgePair> myBidiLookup;
712
713 /// @brief retrieve the depart edges for the given input edge E
714 std::map<const E*, std::vector<_IntermodalEdge*> > myDepartLookup;
715
716 /// @brief retrieve the arrival edges for the given input edge E
717 std::map<const E*, std::vector<_IntermodalEdge*> > myArrivalLookup;
718
719 /// @brief the walking connector edge (fake walking area)
720 std::map<const N*, _IntermodalEdge*> myWalkingConnectorLookup;
721
722 /// @brief retrieve the car edge for the given input edge E
723 std::map<const E*, _IntermodalEdge*> myCarLookup;
724
725 /// @brief retrieve the public transport edges for the given line
726 std::map<std::string, std::vector<_PTEdge*> > myPTLines;
727
728 /// @brief retrieve the representing edge for the given stopping place
729 std::map<std::string, _IntermodalEdge*> myStopConnections;
730
731 /// @brief retrieve the splitted edges for the given "original"
732 std::map<_IntermodalEdge*, std::vector<_IntermodalEdge*> > myAccessSplits;
733
734 int myNumericalID;
735 const int myCarWalkTransfer;
736
737 private:
738 /// @brief Invalidated assignment operator
739 IntermodalNetwork& operator=(const IntermodalNetwork& s);
740
741 };
742
743
744 #endif
745
746 /****************************************************************************/
747