Eclipse SUMO - Simulation of Urban MObility
IntermodalNetwork.h
Go to the documentation of this file.
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 /****************************************************************************/
20 // The Edge definition for the Intermodal Router
21 /****************************************************************************/
22 #pragma once
23 #include <config.h>
24 
25 #include <string>
26 #include <vector>
27 #include <algorithm>
28 #include <assert.h>
30 #include <utils/common/Named.h>
31 #include <utils/common/SUMOTime.h>
32 #include <utils/common/ToString.h>
33 #include <utils/geom/Position.h>
35 #include "AccessEdge.h"
36 #include "CarEdge.h"
37 #include "IntermodalEdge.h"
38 #include "PedestrianEdge.h"
39 #include "PublicTransportEdge.h"
40 #include "StopEdge.h"
41 
42 //#define IntermodalRouter_DEBUG_NETWORK
43 //#define IntermodalRouter_DEBUG_ACCESS
44 
45 
46 // ===========================================================================
47 // class definitions
48 // ===========================================================================
50 template<class E, class L, class N, class V>
52 private:
57  typedef std::pair<_IntermodalEdge*, _IntermodalEdge*> EdgePair;
58 
59 public:
66  PT_STOPS = 2,
76  TAXI_DROPOFF_PT = 64
77  };
78 
79  /* @brief build the pedestrian part of the intermodal network (once)
80  * @param edges The list of MSEdge or ROEdge to build from
81  * @param numericalID the start number for the creation of new edges
82  */
83  IntermodalNetwork(const std::vector<E*>& edges, const bool pedestrianOnly, const int carWalkTransfer = 0)
84  : myNumericalID(0), myCarWalkTransfer(carWalkTransfer) {
85 #ifdef IntermodalRouter_DEBUG_NETWORK
86  std::cout << "initIntermodalNetwork\n";
87 #endif
88  // build the pedestrian edges and the depart / arrival connectors with lookup tables
89  bool haveSeenWalkingArea = false;
90  for (const E* const edge : edges) {
91  if (edge->isTazConnector()) {
92  // only a single edge
93  _AccessEdge* access = new _AccessEdge(myNumericalID++, edge->getID(), edge);
94  addEdge(access);
95  myDepartLookup[edge].push_back(access);
96  myArrivalLookup[edge].push_back(access);
97  } else {
98  const L* lane = getSidewalk<E, L>(edge);
99  if (lane != 0) {
100  if (edge->isWalkingArea()) {
101  // only a single edge
102  addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, true));
103  myBidiLookup[edge] = std::make_pair(myEdges.back(), myEdges.back());
104  myDepartLookup[edge].push_back(myEdges.back());
105  myArrivalLookup[edge].push_back(myEdges.back());
106  haveSeenWalkingArea = true;
107  } else { // regular edge or crossing
108  // forward and backward edges
109  addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, true));
110  addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, false));
111  myBidiLookup[edge] = std::make_pair(myEdges[myNumericalID - 2], myEdges.back());
112  }
113  }
114  if (!edge->isWalkingArea()) {
115  // depart and arrival edges (the router can decide the initial direction to take and the direction to arrive from)
116  _IntermodalEdge* const departConn = new _IntermodalEdge(edge->getID() + "_depart_connector", myNumericalID++, edge, "!connector");
117  _IntermodalEdge* const arrivalConn = new _IntermodalEdge(edge->getID() + "_arrival_connector", myNumericalID++, edge, "!connector");
118  addConnectors(departConn, arrivalConn, 0);
119  }
120  }
121  }
122 
123  // build the walking connectors if there are no walking areas
124  for (const E* const edge : edges) {
125  if (edge->isTazConnector() || edge->isInternal()) {
126  continue;
127  }
128  if (haveSeenWalkingArea) {
129  // 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
130  if (!pedestrianOnly && getSidewalk<E, L>(edge) == nullptr) {
131  const N* const node = edge->getToJunction();
132  if (myWalkingConnectorLookup.count(node) == 0) {
133  addEdge(new _IntermodalEdge(node->getID() + "_walking_connector", myNumericalID++, nullptr, "!connector"));
134  myWalkingConnectorLookup[node] = myEdges.back();
135  }
136  }
137  } else {
138  for (const N* const node : {
139  edge->getFromJunction(), edge->getToJunction()
140  }) {
141  if (myWalkingConnectorLookup.count(node) == 0) {
142  addEdge(new _IntermodalEdge(node->getID() + "_walking_connector", myNumericalID++, nullptr, "!connector"));
143  myWalkingConnectorLookup[node] = myEdges.back();
144  }
145  }
146  }
147  }
148  // build the connections
149  for (const E* const edge : edges) {
150  if (edge->isTazConnector()) {
151  // since pedestrians walk in both directions, also allow departing at sinks and arriving at sources
152  _IntermodalEdge* const tazDepart = getDepartConnector(edge);
153  _IntermodalEdge* const tazArrive = getArrivalConnector(edge);
154  const E* other = edge->getOtherTazConnector();
155  _IntermodalEdge* const otherTazDepart = other != nullptr ? getDepartConnector(other) : tazDepart;
156  _IntermodalEdge* const otherTazArrive = other != nullptr ? getArrivalConnector(other) : tazArrive;
157  for (const E* out : edge->getSuccessors()) {
158  if (out->isNormal()) {
159  tazDepart->addSuccessor(getDepartConnector(out));
160  getArrivalConnector(out)->addSuccessor(otherTazArrive);
161  }
162  }
163  for (const E* in : edge->getPredecessors()) {
164  if (in->isNormal()) {
165  getArrivalConnector(in)->addSuccessor(tazArrive);
166  otherTazDepart->addSuccessor(getDepartConnector(in));
167  }
168  }
169  continue;
170  }
171  const L* const sidewalk = getSidewalk<E, L>(edge);
172  if (sidewalk == nullptr) {
173  continue;
174  }
175  // find all incoming and outgoing lanes for the sidewalk and
176  // connect the corresponding IntermodalEdges
177  const EdgePair& pair = getBothDirections(edge);
178 #ifdef IntermodalRouter_DEBUG_NETWORK
179  std::cout << " building connections from " << sidewalk->getID() << "\n";
180 #endif
181  if (haveSeenWalkingArea) {
182  const std::vector<std::pair<const L*, const E*> > outgoing = sidewalk->getOutgoingViaLanes();
183  // if one of the outgoing lanes is a walking area it must be used.
184  // All other connections shall be ignored
185  // if it has no outgoing walking area, it probably is a walking area itself
186  bool hasWalkingArea = false;
187  for (const auto& target : outgoing) {
188  if (target.first->getEdge().isWalkingArea()) {
189  hasWalkingArea = true;
190  break;
191  }
192  }
193  for (const auto& target : outgoing) {
194  const E* const targetEdge = &(target.first->getEdge());
195  const bool used = (target.first == getSidewalk<E, L>(targetEdge)
196  && (!hasWalkingArea || targetEdge->isWalkingArea()));
197 #ifdef IntermodalRouter_DEBUG_NETWORK
198  const L* potTarget = getSidewalk<E, L>(targetEdge);
199  std::cout << " lane=" << (potTarget == 0 ? "NULL" : potTarget->getID()) << (used ? "(used)" : "") << "\n";
200 #endif
201  if (used) {
202  const EdgePair& targetPair = getBothDirections(targetEdge);
203  pair.first->addSuccessor(targetPair.first);
204  targetPair.second->addSuccessor(pair.second);
205 #ifdef IntermodalRouter_DEBUG_NETWORK
206  std::cout << " " << pair.first->getID() << " -> " << targetPair.first->getID() << "\n";
207  std::cout << " " << targetPair.second->getID() << " -> " << pair.second->getID() << "\n";
208 #endif
209  }
210  }
211  }
212  // We may have a network without pedestrian structures or a car-only edge.
213  // In the first case we assume that all sidewalks at a junction are interconnected,
214  // in the second we connect all car-only edges to all sidewalks.
215  _IntermodalEdge* const toNodeConn = myWalkingConnectorLookup[edge->getToJunction()];
216  if (toNodeConn != nullptr) {
217  // Check for the outgoing vias and use the shortest one as an approximation
218  const std::vector<std::pair<const L*, const E*> > outgoing = sidewalk->getOutgoingViaLanes();
219  double minViaLength = std::numeric_limits<double>::max();
220  const E* minVia = nullptr;
221  for (const auto& target : outgoing) {
222  if (target.second != nullptr && target.second->getLength() < minViaLength) {
223  minViaLength = target.second->getLength();
224  minVia = target.second;
225  }
226  }
227  EdgePair interVia = std::make_pair(nullptr, nullptr);
228  if (minVia != nullptr) {
229  const auto it = myBidiLookup.find(minVia);
230  if (it != myBidiLookup.end()) {
231  interVia = it->second;
232  }
233  }
234  if (!haveSeenWalkingArea) {
235  // if we have walking areas we should use them and not the connector
236  pair.first->addSuccessor(toNodeConn, interVia.first);
237  }
238  toNodeConn->addSuccessor(pair.second, interVia.second);
239  }
240  _IntermodalEdge* const fromNodeConn = myWalkingConnectorLookup[edge->getFromJunction()];
241  if (fromNodeConn != nullptr) {
242  if (!haveSeenWalkingArea) {
243  pair.second->addSuccessor(fromNodeConn);
244  }
245  fromNodeConn->addSuccessor(pair.first);
246  }
247  if (!edge->isWalkingArea()) {
248  // build connections from depart connector
249  _IntermodalEdge* startConnector = getDepartConnector(edge);
250  startConnector->addSuccessor(pair.first);
251  startConnector->addSuccessor(pair.second);
252  // build connections to arrival connector
253  _IntermodalEdge* endConnector = getArrivalConnector(edge);
254  pair.first->addSuccessor(endConnector);
255  pair.second->addSuccessor(endConnector);
256 #ifdef IntermodalRouter_DEBUG_NETWORK
257  std::cout << " " << startConnector->getID() << " -> " << pair.first->getID() << "\n";
258  std::cout << " " << startConnector->getID() << " -> " << pair.second->getID() << "\n";
259  std::cout << " " << pair.first->getID() << " -> " << endConnector->getID() << "\n";
260  std::cout << " " << pair.second->getID() << " -> " << endConnector->getID() << "\n";
261 #endif
262  }
263  }
264  }
265 
267  for (typename std::vector<_IntermodalEdge*>::iterator it = myEdges.begin(); it != myEdges.end(); ++it) {
268  delete *it;
269  }
270  }
271 
272  void addEdge(_IntermodalEdge* edge) {
273  while ((int)myEdges.size() <= edge->getNumericalID()) {
274  myEdges.push_back(0);
275  }
276  myEdges[edge->getNumericalID()] = edge;
277  }
278 
279  void addConnectors(_IntermodalEdge* const depConn, _IntermodalEdge* const arrConn, const int index) {
280  addEdge(depConn);
281  addEdge(arrConn);
282  myDepartLookup[depConn->getEdge()].insert(myDepartLookup[depConn->getEdge()].begin() + index, depConn);
283  myArrivalLookup[arrConn->getEdge()].insert(myArrivalLookup[arrConn->getEdge()].begin() + index, arrConn);
284  }
285 
286  const std::vector<_IntermodalEdge*>& getAllEdges() {
287  return myEdges;
288  }
289 
291  const EdgePair& getBothDirections(const E* e) const {
292  typename std::map<const E*, EdgePair>::const_iterator it = myBidiLookup.find(e);
293  if (it == myBidiLookup.end()) {
294  assert(false);
295  throw ProcessError(TLF("Edge '%' not found in intermodal network.'", e->getID()));
296  }
297  return (*it).second;
298  }
299 
301  const _IntermodalEdge* getDepartEdge(const E* e, const double pos) const {
302  typename std::map<const E*, std::vector<_IntermodalEdge*> >::const_iterator it = myDepartLookup.find(e);
303  if (it == myDepartLookup.end()) {
304  throw ProcessError(TLF("Depart edge '%' not found in intermodal network.", e->getID()));
305  }
306  if ((e->getPermissions() & SVC_PEDESTRIAN) == 0) {
307  // use most specific split (best trainStop, quay etc)
308  double bestDist = std::numeric_limits<double>::max();
309  const _IntermodalEdge* best = nullptr;
310  for (const _IntermodalEdge* const split : it->second) {
311  if (pos >= split->getStartPos() - POSITION_EPS && pos <= split->getEndPos() + POSITION_EPS) {
312  const double dist = split->getEndPos() - split->getStartPos();
313  if (dist < bestDist) {
314  bestDist = dist;
315  best = split;
316  }
317  }
318  }
319  assert(best != nullptr);
320  return best;
321  } else {
322  // use next downstream edge
323  const std::vector<_IntermodalEdge*>& splitList = it->second;
324  typename std::vector<_IntermodalEdge*>::const_iterator splitIt = splitList.begin();
325  double totalLength = 0.;
326  while (splitIt + 1 != splitList.end() && totalLength + (*splitIt)->getLength() < pos) {
327  totalLength += (*splitIt)->getLength();
328  ++splitIt;
329  }
330  return *splitIt;
331  }
332  }
333 
335  _IntermodalEdge* getDepartConnector(const E* e, const int splitIndex = 0) const {
336  typename std::map<const E*, std::vector<_IntermodalEdge*> >::const_iterator it = myDepartLookup.find(e);
337  if (it == myDepartLookup.end()) {
338  throw ProcessError(TLF("Depart edge '%' not found in intermodal network.", e->getID()));
339  }
340  if (splitIndex >= (int)it->second.size()) {
341  throw ProcessError("Split index " + toString(splitIndex) + " invalid for depart edge '" + e->getID() + "' .");
342  }
343  return it->second[splitIndex];
344  }
345 
347  _IntermodalEdge* getArrivalEdge(const E* e, const double pos) const {
348  typename std::map<const E*, std::vector<_IntermodalEdge*> >::const_iterator it = myArrivalLookup.find(e);
349  if (it == myArrivalLookup.end()) {
350  throw ProcessError(TLF("Arrival edge '%' not found in intermodal network.", e->getID()));
351  }
352  const std::vector<_IntermodalEdge*>& splitList = it->second;
353  typename std::vector<_IntermodalEdge*>::const_iterator splitIt = splitList.begin();
354  double totalLength = 0.;
355  while (splitIt != splitList.end() && totalLength + (*splitIt)->getLength() < pos) {
356  totalLength += (*splitIt)->getLength();
357  ++splitIt;
358  }
359  return *splitIt;
360  }
361 
363  _IntermodalEdge* getArrivalConnector(const E* e, const int splitIndex = 0) const {
364  return myArrivalLookup.find(e)->second[splitIndex];
365  }
366 
369  typename std::map<const N*, _IntermodalEdge*>::const_iterator it = myWalkingConnectorLookup.find(e->getToJunction());
370  if (it == myWalkingConnectorLookup.end()) {
371  const L* const sidewalk = getSidewalk<E, L>(e);
372  if (e->isInternal() || sidewalk == 0) {
373  return 0;
374  }
375  for (const auto& target : sidewalk->getOutgoingViaLanes()) {
376  if (target.first->getEdge().isWalkingArea()) {
377  return getBothDirections(&target.first->getEdge()).first;
378  }
379  }
380  return 0;
381  }
382  return it->second;
383  }
384 
385  void addCarEdges(const std::vector<E*>& edges, double taxiWait) {
386  for (const E* const edge : edges) {
387  if (edge->getFunction() == SumoXMLEdgeFunc::NORMAL || edge->getFunction() == SumoXMLEdgeFunc::INTERNAL) {
388  myCarLookup[edge] = new CarEdge<E, L, N, V>(myNumericalID++, edge);
389  addEdge(myCarLookup[edge]);
390  }
391  }
392  for (const auto& edgePair : myCarLookup) {
393  _IntermodalEdge* const carEdge = edgePair.second;
394  // connectivity within the car network
395  for (const auto& suc : edgePair.first->getViaSuccessors()) {
396  _IntermodalEdge* const sucCarEdge = getCarEdge(suc.first);
397  _IntermodalEdge* const sucViaEdge = getCarEdge(suc.second);
398  if (sucCarEdge != nullptr) {
399  carEdge->addSuccessor(sucCarEdge, sucViaEdge);
400  }
401  }
402  // connectivity to the pedestrian network (only for normal edges)
403  if (edgePair.first->getFunction() != SumoXMLEdgeFunc::NORMAL) {
404  continue;
405  }
406  if ((myCarWalkTransfer & ALL_JUNCTIONS) != 0) {
407  _IntermodalEdge* const walkCon = getWalkingConnector(edgePair.first);
408  if (walkCon != 0) {
409  carEdge->addSuccessor(walkCon);
410  } else {
411  // we are on an edge where pedestrians are forbidden and want to continue on an arbitrary pedestrian edge
412  for (const E* const out : edgePair.first->getToJunction()->getOutgoing()) {
413  if (!out->isInternal() && !out->isTazConnector() && getSidewalk<E, L>(out) != 0) {
414  carEdge->addSuccessor(getBothDirections(out).first);
415  }
416  }
417  for (const E* const in : edgePair.first->getToJunction()->getIncoming()) {
418  if (!in->isInternal() && !in->isTazConnector() && getSidewalk<E, L>(in) != 0) {
419  carEdge->addSuccessor(getBothDirections(in).second);
420  }
421  }
422  }
423  }
425  // add access edges that allow exiting a taxi
426  _IntermodalEdge* const walkCon = getWalkingConnector(edgePair.first);
427  if (walkCon != 0) {
428  addRestrictedCarExit(carEdge, walkCon, SVC_TAXI);
429  } else {
430  // we are on an edge where pedestrians are forbidden and want to continue on an arbitrary pedestrian edge
431  for (const E* const out : edgePair.first->getToJunction()->getOutgoing()) {
432  if (!out->isInternal() && !out->isTazConnector() && getSidewalk<E, L>(out) != 0) {
433  addRestrictedCarExit(carEdge, getBothDirections(out).first, SVC_TAXI);
434  }
435  }
436  for (const E* const in : edgePair.first->getToJunction()->getIncoming()) {
437  if (!in->isInternal() && !in->isTazConnector() && getSidewalk<E, L>(in) != 0) {
438  addRestrictedCarExit(carEdge, getBothDirections(in).second, SVC_TAXI);
439  }
440  }
441  }
442  }
443  // use intermediate access edge that prevents taxi departure
444  _IntermodalEdge* departConn = getDepartConnector(edgePair.first);
445  _AccessEdge* access = new _AccessEdge(myNumericalID++, departConn, carEdge, 0, (SVCAll & ~SVC_TAXI));
446  addEdge(access);
447  departConn->addSuccessor(access);
448  access->addSuccessor(carEdge);
449  if ((myCarWalkTransfer & TAXI_PICKUP_PT) == 0) {
450  // taxi may depart anywhere but there is a time penalty
451  _AccessEdge* taxiAccess = new _AccessEdge(myNumericalID++, departConn, carEdge, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
452  addEdge(taxiAccess);
453  departConn->addSuccessor(taxiAccess);
454  taxiAccess->addSuccessor(carEdge);
455  }
456  if ((myCarWalkTransfer & TAXI_DROPOFF_PT) == 0) {
457  // taxi (as all other cars) may arrive anywhere
458  carEdge->addSuccessor(getArrivalConnector(edgePair.first));
459  } else {
460  // use intermediate access edge that prevents taxi arrival
461  addRestrictedCarExit(carEdge, getArrivalConnector(edgePair.first), (SVCAll & ~SVC_TAXI));
462  }
463  }
464  }
465 
467  _IntermodalEdge* getCarEdge(const E* e) const {
468  if (e == nullptr) {
469  return nullptr;
470  }
471  auto it = myCarLookup.find(e);
472  if (it == myCarLookup.end()) {
473  return nullptr;
474  }
475  return it->second;
476  }
477 
479  _IntermodalEdge* getStopEdge(const std::string& stopId) const {
480  auto it = myStopConnections.find(stopId);
481  if (it == myStopConnections.end()) {
482  return nullptr;
483  }
484  return it->second;
485  }
486 
504  void addAccess(const std::string& stopId, const E* stopEdge, const double startPos, const double endPos, const double length, const SumoXMLTag category, bool isAccess, double taxiWait) {
505  assert(stopEdge != nullptr);
506  const bool transferCarWalk = ((category == SUMO_TAG_PARKING_AREA && (myCarWalkTransfer & PARKING_AREAS) != 0) ||
507  (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & PT_STOPS) != 0));
508  const bool transferTaxiWalk = (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_DROPOFF_PT) != 0);
509  const bool transferWalkTaxi = (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_PICKUP_PT) != 0);
510  const double pos = (startPos + endPos) / 2.;
511 #ifdef IntermodalRouter_DEBUG_ACCESS
512  std::cout << "addAccess stopId=" << stopId << " stopEdge=" << stopEdge->getID() << " pos=" << pos << " length=" << length << " tag=" << toString(category)
513  << " access=" << isAccess << " tWait=" << taxiWait << "\n";
514 #endif
515  if (myStopConnections.count(stopId) == 0) {
516  myStopConnections[stopId] = new StopEdge<E, L, N, V>(stopId, myNumericalID++, stopEdge, startPos, endPos);
517  addEdge(myStopConnections[stopId]);
518  }
519  _IntermodalEdge* const stopConn = myStopConnections[stopId];
520  const L* lane = getSidewalk<E, L>(stopEdge);
521  if (lane != nullptr) {
522  const std::pair<_IntermodalEdge*, _IntermodalEdge*>& pair = getBothDirections(stopEdge);
523  double relPos;
524  bool needSplit;
525  const int splitIndex = findSplitIndex(pair.first, pos, relPos, needSplit);
526  _IntermodalEdge* const fwdSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, true, pos) : nullptr;
527  splitEdge(pair.first, splitIndex, fwdSplit, relPos, length, needSplit, stopConn);
528  _IntermodalEdge* const backSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, false, pos) : nullptr;
529  splitEdge(pair.second, splitIndex, backSplit, relPos, length, needSplit, stopConn, false);
530  _IntermodalEdge* carSplit = nullptr;
531  if (myCarLookup.count(stopEdge) > 0) {
532  if (needSplit) {
533  carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
534  }
535  splitEdge(myCarLookup[stopEdge], splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false, transferCarWalk);
536  }
537  if (needSplit) {
538  if (carSplit != nullptr && (transferCarWalk || transferTaxiWalk)) {
539  // adding access from car to walk
540  _IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
541  for (_IntermodalEdge* conn : {
542  fwdSplit, backSplit
543  }) {
544  if (transferCarWalk) {
545  _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, conn, length);
546  addEdge(access);
547  beforeSplit->addSuccessor(access);
548  access->addSuccessor(conn);
549  } else if (transferTaxiWalk) {
550  addRestrictedCarExit(beforeSplit, stopConn, SVC_TAXI);
551  }
552  }
553  }
554  if (carSplit != nullptr && transferWalkTaxi && !isAccess) {
555  _AccessEdge* access = new _AccessEdge(myNumericalID++, stopConn, carSplit, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
556  addEdge(access);
557  stopConn->addSuccessor(access);
558  access->addSuccessor(carSplit);
559  }
560 
561  // fixing depart connections for the forward pedestrian, the backward pedestrian and the car edge
562  _IntermodalEdge* const prevDep = getDepartConnector(stopEdge, splitIndex);
563  const std::vector<_IntermodalEdge*>& backSplitList = myAccessSplits[pair.second];
564  _IntermodalEdge* const backBeforeSplit = backSplitList[backSplitList.size() - 2 - splitIndex];
565  _IntermodalEdge* const depConn = new _IntermodalEdge(stopEdge->getID() + "_depart_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
566  depConn->addSuccessor(fwdSplit);
567  depConn->addSuccessor(backBeforeSplit);
568  depConn->setLength(fwdSplit->getLength());
569  prevDep->removeSuccessor(backBeforeSplit);
570  prevDep->addSuccessor(backSplit);
571  prevDep->setLength(backSplit->getLength());
572  if (carSplit != nullptr) {
573  depConn->addSuccessor(carSplit);
574  }
575 
576  // fixing arrival connections for the forward pedestrian, the backward pedestrian and the car edge
577  _IntermodalEdge* const prevArr = getArrivalConnector(stopEdge, splitIndex);
578  _IntermodalEdge* const fwdBeforeSplit = myAccessSplits[pair.first][splitIndex];
579  _IntermodalEdge* const arrConn = new _IntermodalEdge(stopEdge->getID() + "_arrival_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
580  fwdSplit->addSuccessor(arrConn);
581  backBeforeSplit->addSuccessor(arrConn);
582  arrConn->setLength(fwdSplit->getLength());
583  fwdSplit->removeSuccessor(prevArr);
584  fwdBeforeSplit->addSuccessor(prevArr);
585  prevArr->setLength(backSplit->getLength());
586  if (carSplit != nullptr) {
587  if (carSplit->removeSuccessor(prevArr)) {
588  carSplit->addSuccessor(arrConn);
589  myAccessSplits[myCarLookup[stopEdge]][splitIndex]->addSuccessor(prevArr);
590  }
591  }
592  addConnectors(depConn, arrConn, splitIndex + 1);
593  }
594  } else {
595  // pedestrians cannot walk here:
596  // add stop edge as depart connector so that pedestrians may start at the stop
597  std::vector<_IntermodalEdge*>& splitList = myDepartLookup[stopEdge];
598  assert(splitList.size() > 0);
599  typename std::vector<_IntermodalEdge*>::iterator splitIt = splitList.begin();
600  while (splitIt != splitList.end() && startPos > (*splitIt)->getEndPos()) {
601  ++splitIt;
602  }
603  splitList.insert(splitIt, stopConn);
604 
605  if (!isAccess && (transferWalkTaxi || transferCarWalk || transferTaxiWalk)) {
606  _IntermodalEdge* carEdge = myCarLookup[stopEdge];
607  double relPos;
608  bool needSplit;
609  const int splitIndex = findSplitIndex(carEdge, pos, relPos, needSplit);
610  if (needSplit) {
611  _IntermodalEdge* carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
612  splitEdge(carEdge, splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false, false);
613 
614  if (transferCarWalk || transferTaxiWalk) {
615  // adding access from car to walk
616  _IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
617  if (transferCarWalk) {
618  _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, stopConn, length);
619  addEdge(access);
620  beforeSplit->addSuccessor(access);
621  access->addSuccessor(stopConn);
622  } else if (transferTaxiWalk) {
623  addRestrictedCarExit(beforeSplit, stopConn, SVC_TAXI);
624  }
625  }
626  if (transferWalkTaxi) {
627  _AccessEdge* access = new _AccessEdge(myNumericalID++, stopConn, carSplit, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
628  addEdge(access);
629  stopConn->addSuccessor(access);
630  access->addSuccessor(carSplit);
631  }
632  }
633  }
634  }
635  }
636 
637  void addSchedule(const SUMOVehicleParameter& pars, const std::vector<SUMOVehicleParameter::Stop>* addStops = nullptr) {
638  SUMOTime lastUntil = 0;
639  std::vector<SUMOVehicleParameter::Stop> validStops;
640  if (addStops != nullptr) {
641  // stops are part of a stand-alone route. until times are offsets from vehicle departure
642  for (const SUMOVehicleParameter::Stop& stop : *addStops) {
643  if (myStopConnections.count(stop.busstop) > 0) {
644  // compute stop times for the first vehicle
645  const SUMOTime newUntil = stop.until + pars.depart;
646  if (newUntil >= lastUntil) {
647  validStops.push_back(stop);
648  validStops.back().until = newUntil;
649  lastUntil = newUntil;
650  } else {
651  WRITE_WARNINGF(TL("Ignoring unordered stop at '%' until % for vehicle '%'."), stop.busstop, time2string(stop.until), pars.id);
652  }
653  }
654  }
655  }
656  for (const SUMOVehicleParameter::Stop& stop : pars.stops) {
657  // stops are part of the vehicle until times are absolute times for the first vehicle
658  if (myStopConnections.count(stop.busstop) > 0 && stop.until >= lastUntil) {
659  validStops.push_back(stop);
660  lastUntil = stop.until;
661  } else {
662  if (stop.busstop != "" && stop.until >= 0) {
663  WRITE_WARNINGF(TL("Ignoring stop at '%' until % for vehicle '%'."), stop.busstop, time2string(stop.until), pars.id);
664  }
665  }
666  }
667  if (validStops.size() < 2 && pars.line != "taxi") {
668  WRITE_WARNINGF(TL("Not using public transport line '%' for routing persons. It has less than two usable stops."), pars.line);
669  return;
670  }
671 
672  typename std::vector<_PTEdge*>& lineEdges = myPTLines[pars.line];
673  if (lineEdges.empty()) {
674  _IntermodalEdge* lastStop = nullptr;
675  Position lastPos;
676  SUMOTime lastTime = 0;
677  for (const SUMOVehicleParameter::Stop& s : validStops) {
678  _IntermodalEdge* currStop = myStopConnections[s.busstop];
679  Position stopPos = E::getStopPosition(s);
680  if (lastStop != nullptr) {
681  _PTEdge* const newEdge = new _PTEdge(s.busstop, myNumericalID++, lastStop, currStop->getEdge(), pars.line, lastPos.distanceTo(stopPos));
682  addEdge(newEdge);
683  newEdge->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s.until - lastTime);
684  lastStop->addSuccessor(newEdge);
685  newEdge->addSuccessor(currStop);
686  lineEdges.push_back(newEdge);
687  }
688  lastTime = s.until;
689  lastStop = currStop;
690  lastPos = stopPos;
691  }
692  if (pars.line != "taxi" && validStops.front().busstop == validStops.back().busstop) {
693  myLoopedLines.insert(pars.line);
694  }
695  } else {
696  if (validStops.size() != lineEdges.size() + 1) {
697  WRITE_WARNINGF("Number of stops for public transport line '%' does not match earlier definitions, ignoring schedule.", pars.line);
698  return;
699  }
700  if (lineEdges.front()->getEntryStop() != myStopConnections[validStops.front().busstop]) {
701  WRITE_WARNINGF("Different stop for '%' compared to earlier definitions, ignoring schedule.", pars.line);
702  return;
703  }
704  typename std::vector<_PTEdge*>::const_iterator lineEdge = lineEdges.begin();
705  typename std::vector<SUMOVehicleParameter::Stop>::const_iterator s = validStops.begin() + 1;
706  for (; s != validStops.end(); ++s, ++lineEdge) {
707  if ((*lineEdge)->getSuccessors(SVC_IGNORING)[0] != myStopConnections[s->busstop]) {
708  WRITE_WARNINGF("Different stop for '%' compared to earlier definitions, ignoring schedule.", pars.line);
709  return;
710  }
711  }
712  SUMOTime lastTime = validStops.front().until;
713  if (lineEdges.front()->hasSchedule(lastTime)) {
714  WRITE_WARNINGF("Duplicate schedule for '%' at time=%.", pars.line, time2string(lastTime));
715  }
716  for (lineEdge = lineEdges.begin(), s = validStops.begin() + 1; lineEdge != lineEdges.end(); ++lineEdge, ++s) {
717  (*lineEdge)->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s->until - lastTime);
718  lastTime = s->until;
719  }
720  }
721  }
722 
727  void addCarAccess(const E* edge, SUMOVehicleClass svc, double traveltime) {
728  assert(edge != nullptr);
729  assert(myCarLookup.count(edge) != 0);
730  assert(myBidiLookup.count(edge) != 0);
731  EdgePair pedestrianEdges = myBidiLookup[edge];
732  _IntermodalEdge* carEdge = myCarLookup[edge];
733  _AccessEdge* access = new _AccessEdge(myNumericalID++, pedestrianEdges.first, carEdge, 0, svc, SVC_IGNORING, traveltime);
734  addEdge(access);
735  pedestrianEdges.first->addSuccessor(access);
736  pedestrianEdges.second->addSuccessor(access);
737  access->addSuccessor(carEdge);
738  }
739 
746  _AccessEdge* access = new _AccessEdge(myNumericalID++, from, to, 0, SVC_IGNORING, vehicleRestriction);
747  addEdge(access);
748  from->addSuccessor(access);
749  access->addSuccessor(to);
750  }
751 
752  bool isLooped(const std::string lineID) const {
753  return myLoopedLines.count(lineID) != 0;
754  }
755 
756 private:
768  int findSplitIndex(_IntermodalEdge* const toSplit, const double pos, double& relPos, bool& needSplit) const {
769  relPos = pos;
770  needSplit = true;
771  int splitIndex = 0;
772  const auto& splitList = myAccessSplits.find(toSplit);
773  if (splitList != myAccessSplits.end() && !splitList->second.empty()) {
774  for (const _IntermodalEdge* const split : splitList->second) {
775  if (relPos < split->getLength() + POSITION_EPS) {
776  break;
777  }
778  relPos -= split->getLength();
779  splitIndex++;
780  }
781  assert(splitIndex < (int)splitList->second.size());
782  if (splitIndex + 1 < (int)splitList->second.size() && fabs(relPos - splitList->second[splitIndex]->getLength()) < POSITION_EPS) {
783  needSplit = false;
784  }
785  }
786  return splitIndex;
787  }
788 
801  void splitEdge(_IntermodalEdge* const toSplit, int splitIndex,
802  _IntermodalEdge* afterSplit, const double relPos, const double length, const bool needSplit,
803  _IntermodalEdge* const stopConn, const bool forward = true, const bool addExit = true, const bool addEntry = true) {
804  std::vector<_IntermodalEdge*>& splitList = myAccessSplits[toSplit];
805  if (splitList.empty()) {
806  splitList.push_back(toSplit);
807  }
808  if (!forward) {
809  splitIndex = (int)splitList.size() - 1 - splitIndex;
810  if (!needSplit) {
811  splitIndex--;
812  }
813  }
814  _IntermodalEdge* beforeSplit = splitList[splitIndex];
815  if (needSplit) {
816  addEdge(afterSplit);
817  beforeSplit->transferSuccessors(afterSplit);
818  beforeSplit->addSuccessor(afterSplit);
819  if (forward) {
820  afterSplit->setLength(MAX2(0.0, beforeSplit->getLength() - relPos));
821  beforeSplit->setLength(relPos);
822  } else {
823  afterSplit->setLength(relPos);
824  beforeSplit->setLength(MAX2(0.0, beforeSplit->getLength() - relPos));
825  // rename backward edges for easier referencing
826  const std::string newID = beforeSplit->getID();
827  beforeSplit->setID(afterSplit->getID());
828  afterSplit->setID(newID);
829  }
830  splitList.insert(splitList.begin() + splitIndex + 1, afterSplit);
831  } else {
832  // don't split, use the present split edges
833  afterSplit = splitList[splitIndex + 1];
834  }
835  // add access to / from edge
836  if (addEntry) {
837  _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, stopConn, length);
838  addEdge(access);
839  beforeSplit->addSuccessor(access);
840  access->addSuccessor(stopConn);
841  }
842  if (addExit) {
843  // pedestrian case only, exit from public to pedestrian
844  _AccessEdge* exit = new _AccessEdge(myNumericalID++, stopConn, afterSplit, length);
845  addEdge(exit);
846  stopConn->addSuccessor(exit);
847  exit->addSuccessor(afterSplit);
848  }
849  }
850 
851 
852 private:
854  std::vector<_IntermodalEdge*> myEdges;
855 
857  std::map<const E*, EdgePair> myBidiLookup;
858 
860  std::map<const E*, std::vector<_IntermodalEdge*> > myDepartLookup;
861 
863  std::map<const E*, std::vector<_IntermodalEdge*> > myArrivalLookup;
864 
866  std::map<const N*, _IntermodalEdge*> myWalkingConnectorLookup;
867 
869  std::map<const E*, _IntermodalEdge*, ComparatorNumericalIdLess> myCarLookup;
870 
872  std::map<std::string, std::vector<_PTEdge*> > myPTLines;
873 
875  std::map<std::string, _IntermodalEdge*> myStopConnections;
876 
878  std::map<_IntermodalEdge*, std::vector<_IntermodalEdge*> > myAccessSplits;
879 
881  std::set<std::string > myLoopedLines;
882 
884  const int myCarWalkTransfer;
885 
886 private:
889 
890 };
long long int SUMOTime
Definition: GUI.h:35
std::vector< std::string > & split(const std::string &s, char delim, std::vector< std::string > &elems)
#define WRITE_WARNINGF(...)
Definition: MsgHandler.h:296
#define TL(string)
Definition: MsgHandler.h:315
#define TLF(string,...)
Definition: MsgHandler.h:317
std::string time2string(SUMOTime t, bool humanReadable)
convert SUMOTime to string (independently of global format setting)
Definition: SUMOTime.cpp:69
const SVCPermissions SVCAll
all VClasses are allowed
long long int SVCPermissions
bitset where each bit declares whether a certain SVC may use this edge/lane
SUMOVehicleClass
Definition of vehicle classes to differ between different lane usage and authority types.
@ SVC_IGNORING
vehicles ignoring classes
@ SVC_TAXI
vehicle is a taxi
@ SVC_PEDESTRIAN
pedestrian
SumoXMLTag
Numbers representing SUMO-XML - element names.
@ SUMO_TAG_BUS_STOP
A bus stop.
@ SUMO_TAG_PARKING_AREA
A parking area.
T MAX2(T a, T b)
Definition: StdDefs.h:82
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:46
the access edge connecting different modes that is given to the internal router (SUMOAbstractRouter)
Definition: AccessEdge.h:31
the car edge type that is given to the internal router (SUMOAbstractRouter)
Definition: CarEdge.h:34
the base edge type that is given to the internal router (SUMOAbstractRouter)
void setLength(const double length)
void transferSuccessors(IntermodalEdge *to)
bool removeSuccessor(const IntermodalEdge *const edge)
void addSuccessor(IntermodalEdge *const s, IntermodalEdge *const via=nullptr)
const E * getEdge() const
double getLength() const
required by DijkstraRouter et al for external effort computation
int getNumericalID() const
the intermodal network storing edges, connections and the mappings to the "real" edges
_IntermodalEdge * getWalkingConnector(const E *e) const
Returns the outgoing pedestrian edge, which is either a walking area or a walking connector.
PublicTransportEdge< E, L, N, V > _PTEdge
std::map< const E *, _IntermodalEdge *, ComparatorNumericalIdLess > myCarLookup
retrieve the car edge for the given input edge E
void addCarAccess(const E *edge, SUMOVehicleClass svc, double traveltime)
Adds access edges for transfering from walking to vehicle use.
void addAccess(const std::string &stopId, const E *stopEdge, const double startPos, const double endPos, const double length, const SumoXMLTag category, bool isAccess, double taxiWait)
Adds access edges for stopping places to the intermodal network.
std::map< const E *, std::vector< _IntermodalEdge * > > myArrivalLookup
retrieve the arrival edges for the given input edge E
std::map< const N *, _IntermodalEdge * > myWalkingConnectorLookup
the walking connector edge (fake walking area)
std::map< std::string, _IntermodalEdge * > myStopConnections
retrieve the representing edge for the given stopping place
std::map< _IntermodalEdge *, std::vector< _IntermodalEdge * > > myAccessSplits
retrieve the splitted edges for the given "original"
_IntermodalEdge * getArrivalConnector(const E *e, const int splitIndex=0) const
Returns the arriving intermodal connector at the given split offset.
const EdgePair & getBothDirections(const E *e) const
Returns the pair of forward and backward edge.
const std::vector< _IntermodalEdge * > & getAllEdges()
_IntermodalEdge * getArrivalEdge(const E *e, const double pos) const
Returns the arriving intermodal edge.
_IntermodalEdge * getStopEdge(const std::string &stopId) const
Returns the associated stop edge.
void addEdge(_IntermodalEdge *edge)
std::vector< _IntermodalEdge * > myEdges
the edge dictionary
IntermodalNetwork & operator=(const IntermodalNetwork &s)
Invalidated assignment operator.
AccessEdge< E, L, N, V > _AccessEdge
ModeChangeOptions
where mode changes are possible
@ TAXI_PICKUP_ANYWHERE
taxi customer may be picked up anywhere
@ TAXI_DROPOFF_ANYWHERE
taxi customer may exit anywhere
@ PARKING_AREAS
parking areas
@ ALL_JUNCTIONS
junctions with edges allowing the additional mode
@ TAXI_PICKUP_PT
taxi customer may be picked up at public transport stop
@ PT_STOPS
public transport stops and access
@ TAXI_DROPOFF_PT
taxi customer may be picked up at public transport stop
std::map< const E *, EdgePair > myBidiLookup
retrieve the forward and backward edge for the given input edge E
std::set< std::string > myLoopedLines
looped lines need extra checking when building itineraries
bool isLooped(const std::string lineID) const
std::map< std::string, std::vector< _PTEdge * > > myPTLines
retrieve the public transport edges for the given line
void addRestrictedCarExit(_IntermodalEdge *from, _IntermodalEdge *to, SVCPermissions vehicleRestriction)
Adds access edges for transfering from driving to walking that are only usable by a particular vehicl...
void addConnectors(_IntermodalEdge *const depConn, _IntermodalEdge *const arrConn, const int index)
int findSplitIndex(_IntermodalEdge *const toSplit, const double pos, double &relPos, bool &needSplit) const
Returns where to insert or use the split edge.
void addCarEdges(const std::vector< E * > &edges, double taxiWait)
PedestrianEdge< E, L, N, V > _PedestrianEdge
void splitEdge(_IntermodalEdge *const toSplit, int splitIndex, _IntermodalEdge *afterSplit, const double relPos, const double length, const bool needSplit, _IntermodalEdge *const stopConn, const bool forward=true, const bool addExit=true, const bool addEntry=true)
Splits an edge (if necessary) and connects it to a stopping edge.
IntermodalNetwork(const std::vector< E * > &edges, const bool pedestrianOnly, const int carWalkTransfer=0)
IntermodalEdge< E, L, N, V > _IntermodalEdge
_IntermodalEdge * getDepartConnector(const E *e, const int splitIndex=0) const
Returns the departing intermodal connector at the given split offset.
std::map< const E *, std::vector< _IntermodalEdge * > > myDepartLookup
retrieve the depart edges for the given input edge E
_IntermodalEdge * getCarEdge(const E *e) const
Returns the associated car edge.
void addSchedule(const SUMOVehicleParameter &pars, const std::vector< SUMOVehicleParameter::Stop > *addStops=nullptr)
std::pair< _IntermodalEdge *, _IntermodalEdge * > EdgePair
const _IntermodalEdge * getDepartEdge(const E *e, const double pos) const
Returns the departing intermodal edge.
virtual void setID(const std::string &newID)
resets the id
Definition: Named.h:82
const std::string & getID() const
Returns the id.
Definition: Named.h:74
the pedestrian edge type that is given to the internal router (SUMOAbstractRouter)
A point in 2D or 3D with translation and scaling methods.
Definition: Position.h:37
double distanceTo(const Position &p2) const
returns the euclidean distance in 3 dimension
Definition: Position.h:261
the public transport edge type connecting the stop edges
void addSchedule(const std::string id, const SUMOTime begin, const int repetitionNumber, const SUMOTime period, const SUMOTime travelTime)
Definition of vehicle stop (position and duration)
SUMOTime until
The time at which the vehicle may continue its journey.
std::string busstop
(Optional) bus stop if one is assigned to the stop
Structure representing possible vehicle parameter.
SUMOTime repetitionOffset
The time offset between vehicle reinsertions.
std::string id
The vehicle's id.
std::vector< Stop > stops
List of the stops the vehicle will make, TraCI may add entries here.
std::string line
The vehicle's line (mainly for public transport)
the stop edge type representing bus and train stops
Definition: StopEdge.h:31