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