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.org/sumo
3 // Copyright (C) 2001-2020 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 // function definitions
48 // ===========================================================================
49 template <class E, class L>
50 inline const L* getSidewalk(const E* edge) {
51  if (edge == nullptr) {
52  return nullptr;
53  }
54  // prefer lanes that are exclusive to pedestrians
55  const std::vector<L*>& lanes = edge->getLanes();
56  for (const L* const lane : lanes) {
57  if (lane->getPermissions() == SVC_PEDESTRIAN) {
58  return lane;
59  }
60  }
61  for (const L* const lane : lanes) {
62  if (lane->allowsVehicleClass(SVC_PEDESTRIAN)) {
63  return lane;
64  }
65  }
66  return nullptr;
67 }
68 
69 
70 // ===========================================================================
71 // class definitions
72 // ===========================================================================
74 template<class E, class L, class N, class V>
76 private:
81  typedef std::pair<_IntermodalEdge*, _IntermodalEdge*> EdgePair;
82 
83 public:
90  PT_STOPS = 2,
100  TAXI_DROPOFF_PT = 64
101  };
102 
103  /* @brief build the pedestrian part of the intermodal network (once)
104  * @param edges The list of MSEdge or ROEdge to build from
105  * @param numericalID the start number for the creation of new edges
106  */
107  IntermodalNetwork(const std::vector<E*>& edges, const bool pedestrianOnly, const int carWalkTransfer = 0)
108  : myNumericalID(0), myCarWalkTransfer(carWalkTransfer) {
109 #ifdef IntermodalRouter_DEBUG_NETWORK
110  std::cout << "initIntermodalNetwork\n";
111 #endif
112  // build the pedestrian edges and the depart / arrival connectors with lookup tables
113  bool haveSeenWalkingArea = false;
114  for (const E* const edge : edges) {
115  if (edge->isTazConnector()) {
116  // only a single edge
117  _AccessEdge* access = new _AccessEdge(myNumericalID++, edge->getID(), edge);
118  addEdge(access);
119  myDepartLookup[edge].push_back(access);
120  myArrivalLookup[edge].push_back(access);
121  } else {
122  const L* lane = getSidewalk<E, L>(edge);
123  if (lane != 0) {
124  if (edge->isWalkingArea()) {
125  // only a single edge
126  addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, true));
127  myBidiLookup[edge] = std::make_pair(myEdges.back(), myEdges.back());
128  myDepartLookup[edge].push_back(myEdges.back());
129  myArrivalLookup[edge].push_back(myEdges.back());
130  haveSeenWalkingArea = true;
131  } else { // regular edge or crossing
132  // forward and backward edges
133  addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, true));
134  addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, false));
135  myBidiLookup[edge] = std::make_pair(myEdges[myNumericalID - 2], myEdges.back());
136  }
137  }
138  if (!edge->isWalkingArea()) {
139  // depart and arrival edges (the router can decide the initial direction to take and the direction to arrive from)
140  _IntermodalEdge* const departConn = new _IntermodalEdge(edge->getID() + "_depart_connector", myNumericalID++, edge, "!connector");
141  _IntermodalEdge* const arrivalConn = new _IntermodalEdge(edge->getID() + "_arrival_connector", myNumericalID++, edge, "!connector");
142  addConnectors(departConn, arrivalConn, 0);
143  }
144  }
145  }
146 
147  // build the walking connectors if there are no walking areas
148  for (const E* const edge : edges) {
149  if (edge->isTazConnector() || edge->isInternal()) {
150  continue;
151  }
152  if (haveSeenWalkingArea) {
153  // 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
154  if (!pedestrianOnly && getSidewalk<E, L>(edge) == nullptr) {
155  const N* const node = edge->getToJunction();
156  if (myWalkingConnectorLookup.count(node) == 0) {
157  addEdge(new _IntermodalEdge(node->getID() + "_walking_connector", myNumericalID++, nullptr, "!connector"));
158  myWalkingConnectorLookup[node] = myEdges.back();
159  }
160  }
161  } else {
162  for (const N* const node : {
163  edge->getFromJunction(), edge->getToJunction()
164  }) {
165  if (myWalkingConnectorLookup.count(node) == 0) {
166  addEdge(new _IntermodalEdge(node->getID() + "_walking_connector", myNumericalID++, nullptr, "!connector"));
167  myWalkingConnectorLookup[node] = myEdges.back();
168  }
169  }
170  }
171  }
172  // build the connections
173  for (const E* const edge : edges) {
174  if (edge->isTazConnector()) {
175  // since pedestrians walk in both directions, also allow departing at sinks and arriving at sources
176  _IntermodalEdge* const tazDepart = getDepartConnector(edge);
177  _IntermodalEdge* const tazArrive = getArrivalConnector(edge);
178  const E* other = edge->getOtherTazConnector();
179  _IntermodalEdge* const otherTazDepart = other != nullptr ? getDepartConnector(other) : tazDepart;
180  _IntermodalEdge* const otherTazArrive = other != nullptr ? getArrivalConnector(other) : tazArrive;
181  for (const E* out : edge->getSuccessors()) {
182  tazDepart->addSuccessor(getDepartConnector(out));
183  getArrivalConnector(out)->addSuccessor(otherTazArrive);
184  }
185  for (const E* in : edge->getPredecessors()) {
186  getArrivalConnector(in)->addSuccessor(tazArrive);
187  otherTazDepart->addSuccessor(getDepartConnector(in));
188  }
189  continue;
190  }
191  const L* const sidewalk = getSidewalk<E, L>(edge);
192  if (sidewalk == nullptr) {
193  continue;
194  }
195  // find all incoming and outgoing lanes for the sidewalk and
196  // connect the corresponding IntermodalEdges
197  const EdgePair& pair = getBothDirections(edge);
198 #ifdef IntermodalRouter_DEBUG_NETWORK
199  std::cout << " building connections from " << sidewalk->getID() << "\n";
200 #endif
201  if (haveSeenWalkingArea) {
202  const std::vector<std::pair<const L*, const E*> > outgoing = sidewalk->getOutgoingViaLanes();
203  // if one of the outgoing lanes is a walking area it must be used.
204  // All other connections shall be ignored
205  // if it has no outgoing walking area, it probably is a walking area itself
206  bool hasWalkingArea = false;
207  for (const auto& target : outgoing) {
208  if (target.first->getEdge().isWalkingArea()) {
209  hasWalkingArea = true;
210  break;
211  }
212  }
213  for (const auto& target : outgoing) {
214  const E* const targetEdge = &(target.first->getEdge());
215  const bool used = (target.first == getSidewalk<E, L>(targetEdge)
216  && (!hasWalkingArea || targetEdge->isWalkingArea()));
217 #ifdef IntermodalRouter_DEBUG_NETWORK
218  const L* potTarget = getSidewalk<E, L>(targetEdge);
219  std::cout << " lane=" << (potTarget == 0 ? "NULL" : potTarget->getID()) << (used ? "(used)" : "") << "\n";
220 #endif
221  if (used) {
222  const EdgePair& targetPair = getBothDirections(targetEdge);
223  pair.first->addSuccessor(targetPair.first);
224  targetPair.second->addSuccessor(pair.second);
225 #ifdef IntermodalRouter_DEBUG_NETWORK
226  std::cout << " " << pair.first->getID() << " -> " << targetPair.first->getID() << "\n";
227  std::cout << " " << targetPair.second->getID() << " -> " << pair.second->getID() << "\n";
228 #endif
229  }
230  }
231  }
232  // We may have a network without pedestrian structures or a car-only edge.
233  // In the first case we assume that all sidewalks at a junction are interconnected,
234  // in the second we connect all car-only edges to all sidewalks.
235  _IntermodalEdge* const toNodeConn = myWalkingConnectorLookup[edge->getToJunction()];
236  if (toNodeConn != nullptr) {
237  // Check for the outgoing vias and use the shortest one as an approximation
238  const std::vector<std::pair<const L*, const E*> > outgoing = sidewalk->getOutgoingViaLanes();
239  double minViaLength = std::numeric_limits<double>::max();
240  const E* minVia = nullptr;
241  for (const auto& target : outgoing) {
242  if (target.second != nullptr && target.second->getLength() < minViaLength) {
243  minViaLength = target.second->getLength();
244  minVia = target.second;
245  }
246  }
247  EdgePair interVia = std::make_pair(nullptr, nullptr);
248  if (minVia != nullptr) {
249  const auto it = myBidiLookup.find(minVia);
250  if (it != myBidiLookup.end()) {
251  interVia = it->second;
252  }
253  }
254  if (!haveSeenWalkingArea) {
255  // if we have walking areas we should use them and not the connector
256  pair.first->addSuccessor(toNodeConn, interVia.first);
257  }
258  toNodeConn->addSuccessor(pair.second, interVia.second);
259  }
260  _IntermodalEdge* const fromNodeConn = myWalkingConnectorLookup[edge->getFromJunction()];
261  if (fromNodeConn != nullptr) {
262  if (!haveSeenWalkingArea) {
263  pair.second->addSuccessor(fromNodeConn);
264  }
265  fromNodeConn->addSuccessor(pair.first);
266  }
267  if (!edge->isWalkingArea()) {
268  // build connections from depart connector
269  _IntermodalEdge* startConnector = getDepartConnector(edge);
270  startConnector->addSuccessor(pair.first);
271  startConnector->addSuccessor(pair.second);
272  // build connections to arrival connector
273  _IntermodalEdge* endConnector = getArrivalConnector(edge);
274  pair.first->addSuccessor(endConnector);
275  pair.second->addSuccessor(endConnector);
276  }
277 #ifdef IntermodalRouter_DEBUG_NETWORK
278  std::cout << " " << startConnector->getID() << " -> " << pair.first->getID() << "\n";
279  std::cout << " " << startConnector->getID() << " -> " << pair.second->getID() << "\n";
280  std::cout << " " << pair.first->getID() << " -> " << endConnector->getID() << "\n";
281  std::cout << " " << pair.second->getID() << " -> " << endConnector->getID() << "\n";
282 #endif
283  }
284  }
285 
287  for (typename std::vector<_IntermodalEdge*>::iterator it = myEdges.begin(); it != myEdges.end(); ++it) {
288  delete *it;
289  }
290  }
291 
292  void addEdge(_IntermodalEdge* edge) {
293  while ((int)myEdges.size() <= edge->getNumericalID()) {
294  myEdges.push_back(0);
295  }
296  myEdges[edge->getNumericalID()] = edge;
297  }
298 
299  void addConnectors(_IntermodalEdge* const depConn, _IntermodalEdge* const arrConn, const int index) {
300  addEdge(depConn);
301  addEdge(arrConn);
302  myDepartLookup[depConn->getEdge()].insert(myDepartLookup[depConn->getEdge()].begin() + index, depConn);
303  myArrivalLookup[arrConn->getEdge()].insert(myArrivalLookup[arrConn->getEdge()].begin() + index, arrConn);
304  }
305 
306  const std::vector<_IntermodalEdge*>& getAllEdges() {
307  return myEdges;
308  }
309 
311  const EdgePair& getBothDirections(const E* e) const {
312  typename std::map<const E*, EdgePair>::const_iterator it = myBidiLookup.find(e);
313  if (it == myBidiLookup.end()) {
314  assert(false);
315  throw ProcessError("Edge '" + e->getID() + "' not found in intermodal network.'");
316  }
317  return (*it).second;
318  }
319 
321  const _IntermodalEdge* getDepartEdge(const E* e, const double pos) const {
322  typename std::map<const E*, std::vector<_IntermodalEdge*> >::const_iterator it = myDepartLookup.find(e);
323  if (it == myDepartLookup.end()) {
324  throw ProcessError("Depart edge '" + e->getID() + "' not found in intermodal network.");
325  }
326  if ((e->getPermissions() & SVC_PEDESTRIAN) == 0) {
327  // use closest split (best trainStop, quay etc)
328  double totalLength = 0.;
329  double bestDist = std::numeric_limits<double>::max();
330  const _IntermodalEdge* best = nullptr;
331  for (const _IntermodalEdge* split : it->second) {
332  totalLength += split->getLength();
333  double dist = fabs(totalLength - pos);
334  if (dist < bestDist) {
335  // make sure to use a stop rather than the final departConnector since walking is not possible
336  if (bestDist != std::numeric_limits<double>::max() && split == it->second.back()) {
337  break;
338  }
339  bestDist = dist;
340  best = split;
341  } else {
342  break;
343  }
344  }
345  assert(best != 0);
346  return best;
347  } else {
348  // use next downstream edge
349  const std::vector<_IntermodalEdge*>& splitList = it->second;
350  typename std::vector<_IntermodalEdge*>::const_iterator splitIt = splitList.begin();
351  double totalLength = 0.;
352  while (splitIt + 1 != splitList.end() && totalLength + (*splitIt)->getLength() < pos) {
353  totalLength += (*splitIt)->getLength();
354  ++splitIt;
355  }
356  return *splitIt;
357  }
358  }
359 
361  _IntermodalEdge* getDepartConnector(const E* e, const int splitIndex = 0) const {
362  typename std::map<const E*, std::vector<_IntermodalEdge*> >::const_iterator it = myDepartLookup.find(e);
363  if (it == myDepartLookup.end()) {
364  throw ProcessError("Depart edge '" + e->getID() + "' not found in intermodal network.");
365  }
366  if (splitIndex >= (int)it->second.size()) {
367  throw ProcessError("Split index " + toString(splitIndex) + " invalid for depart edge '" + e->getID() + "' .");
368  }
369  return it->second[splitIndex];
370  }
371 
373  _IntermodalEdge* getArrivalEdge(const E* e, const double pos) const {
374  typename std::map<const E*, std::vector<_IntermodalEdge*> >::const_iterator it = myArrivalLookup.find(e);
375  if (it == myArrivalLookup.end()) {
376  throw ProcessError("Arrival edge '" + e->getID() + "' not found in intermodal network.");
377  }
378  const std::vector<_IntermodalEdge*>& splitList = it->second;
379  typename std::vector<_IntermodalEdge*>::const_iterator splitIt = splitList.begin();
380  double totalLength = 0.;
381  while (splitIt != splitList.end() && totalLength + (*splitIt)->getLength() < pos) {
382  totalLength += (*splitIt)->getLength();
383  ++splitIt;
384  }
385  return *splitIt;
386  }
387 
389  _IntermodalEdge* getArrivalConnector(const E* e, const int splitIndex = 0) const {
390  return myArrivalLookup.find(e)->second[splitIndex];
391  }
392 
395  typename std::map<const N*, _IntermodalEdge*>::const_iterator it = myWalkingConnectorLookup.find(e->getToJunction());
396  if (it == myWalkingConnectorLookup.end()) {
397  const L* const sidewalk = getSidewalk<E, L>(e);
398  if (e->isInternal() || sidewalk == 0) {
399  return 0;
400  }
401  for (const auto& target : sidewalk->getOutgoingViaLanes()) {
402  if (target.first->getEdge().isWalkingArea()) {
403  return getBothDirections(&target.first->getEdge()).first;
404  }
405  }
406  return 0;
407  }
408  return it->second;
409  }
410 
411  void addCarEdges(const std::vector<E*>& edges, double taxiWait) {
412  for (const E* const edge : edges) {
413  if (edge->getFunction() == SumoXMLEdgeFunc::NORMAL || edge->getFunction() == SumoXMLEdgeFunc::INTERNAL) {
414  myCarLookup[edge] = new CarEdge<E, L, N, V>(myNumericalID++, edge);
415  addEdge(myCarLookup[edge]);
416  }
417  }
418  for (const auto& edgePair : myCarLookup) {
419  _IntermodalEdge* const carEdge = edgePair.second;
420  // connectivity within the car network
421  for (const auto& suc : edgePair.first->getViaSuccessors()) {
422  _IntermodalEdge* const sucCarEdge = getCarEdge(suc.first);
423  _IntermodalEdge* const sucViaEdge = getCarEdge(suc.second);
424  if (sucCarEdge != nullptr) {
425  carEdge->addSuccessor(sucCarEdge, sucViaEdge);
426  }
427  }
428  // connectivity to the pedestrian network (only for normal edges)
429  if (edgePair.first->getFunction() != SumoXMLEdgeFunc::NORMAL) {
430  continue;
431  }
432  if ((myCarWalkTransfer & ALL_JUNCTIONS) != 0) {
433  _IntermodalEdge* const walkCon = getWalkingConnector(edgePair.first);
434  if (walkCon != 0) {
435  carEdge->addSuccessor(walkCon);
436  } else {
437  // we are on an edge where pedestrians are forbidden and want to continue on an arbitrary pedestrian edge
438  for (const E* const out : edgePair.first->getToJunction()->getOutgoing()) {
439  if (!out->isInternal() && !out->isTazConnector() && getSidewalk<E, L>(out) != 0) {
440  carEdge->addSuccessor(getBothDirections(out).first);
441  }
442  }
443  for (const E* const in : edgePair.first->getToJunction()->getIncoming()) {
444  if (!in->isInternal() && !in->isTazConnector() && getSidewalk<E, L>(in) != 0) {
445  carEdge->addSuccessor(getBothDirections(in).second);
446  }
447  }
448  }
449  }
451  // add access edges that allow exiting a taxi
452  _IntermodalEdge* const walkCon = getWalkingConnector(edgePair.first);
453  if (walkCon != 0) {
454  addRestrictedCarExit(carEdge, walkCon, SVC_TAXI);
455  } else {
456  // we are on an edge where pedestrians are forbidden and want to continue on an arbitrary pedestrian edge
457  for (const E* const out : edgePair.first->getToJunction()->getOutgoing()) {
458  if (!out->isInternal() && !out->isTazConnector() && getSidewalk<E, L>(out) != 0) {
459  addRestrictedCarExit(carEdge, getBothDirections(out).first, SVC_TAXI);
460  }
461  }
462  for (const E* const in : edgePair.first->getToJunction()->getIncoming()) {
463  if (!in->isInternal() && !in->isTazConnector() && getSidewalk<E, L>(in) != 0) {
464  addRestrictedCarExit(carEdge, getBothDirections(in).second, SVC_TAXI);
465  }
466  }
467  }
468  }
469  // use intermediate access edge that prevents taxi departure
470  _IntermodalEdge* departConn = getDepartConnector(edgePair.first);
471  _AccessEdge* access = new _AccessEdge(myNumericalID++, departConn, carEdge, 0, (SVCAll & ~SVC_TAXI));
472  addEdge(access);
473  departConn->addSuccessor(access);
474  access->addSuccessor(carEdge);
475  if ((myCarWalkTransfer & TAXI_PICKUP_PT) == 0) {
476  // taxi may depart anywhere but there is a time penalty
477  _AccessEdge* taxiAccess = new _AccessEdge(myNumericalID++, departConn, carEdge, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
478  addEdge(taxiAccess);
479  departConn->addSuccessor(taxiAccess);
480  taxiAccess->addSuccessor(carEdge);
481  }
482  if ((myCarWalkTransfer & TAXI_DROPOFF_PT) == 0) {
483  // taxi (as all other cars) may arrive anywhere
484  carEdge->addSuccessor(getArrivalConnector(edgePair.first));
485  } else {
486  // use intermediate access edge that prevents taxi arrival
487  addRestrictedCarExit(carEdge, getArrivalConnector(edgePair.first), (SVCAll & ~SVC_TAXI));
488  }
489  }
490  }
491 
493  _IntermodalEdge* getCarEdge(const E* e) const {
494  if (e == nullptr) {
495  return nullptr;
496  }
497  auto it = myCarLookup.find(e);
498  if (it == myCarLookup.end()) {
499  return nullptr;
500  }
501  return it->second;
502  }
503 
505  _IntermodalEdge* getStopEdge(const std::string& stopId) const {
506  auto it = myStopConnections.find(stopId);
507  if (it == myStopConnections.end()) {
508  return nullptr;
509  }
510  return it->second;
511  }
512 
528  void addAccess(const std::string& stopId, const E* stopEdge, const double pos, const double length, const SumoXMLTag category, bool isAccess, double taxiWait) {
529  assert(stopEdge != nullptr);
530  const bool transferCarWalk = ((category == SUMO_TAG_PARKING_AREA && (myCarWalkTransfer & PARKING_AREAS) != 0) ||
531  (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & PT_STOPS) != 0));
532  const bool transferTaxiWalk = (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_DROPOFF_PT) != 0);
533  const bool transferWalkTaxi = (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_PICKUP_PT) != 0);
534 #ifdef IntermodalRouter_DEBUG_ACCESS
535  std::cout << "addAccess stopId=" << stopId << " stopEdge=" << stopEdge->getID() << " pos=" << pos << " length=" << length << " cat=" << category << "\n";
536 #endif
537  if (myStopConnections.count(stopId) == 0) {
538  myStopConnections[stopId] = new StopEdge<E, L, N, V>(stopId, myNumericalID++, stopEdge);
539  addEdge(myStopConnections[stopId]);
540  }
541  _IntermodalEdge* const stopConn = myStopConnections[stopId];
542  const L* lane = getSidewalk<E, L>(stopEdge);
543  if (lane != nullptr) {
544  const std::pair<_IntermodalEdge*, _IntermodalEdge*>& pair = getBothDirections(stopEdge);
545  double relPos;
546  bool needSplit;
547  const int splitIndex = findSplitIndex(pair.first, pos, relPos, needSplit);
548  _IntermodalEdge* const fwdSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, true, pos) : nullptr;
549  splitEdge(pair.first, splitIndex, fwdSplit, relPos, length, needSplit, stopConn);
550  _IntermodalEdge* const backSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, false, pos) : nullptr;
551  splitEdge(pair.second, splitIndex, backSplit, relPos, length, needSplit, stopConn, false);
552  _IntermodalEdge* carSplit = nullptr;
553  if (myCarLookup.count(stopEdge) > 0) {
554  if (needSplit) {
555  carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
556  }
557  splitEdge(myCarLookup[stopEdge], splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false, transferCarWalk);
558  }
559  if (needSplit) {
560  if (carSplit != nullptr && (transferCarWalk || transferTaxiWalk)) {
561  // adding access from car to walk
562  _IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
563  for (_IntermodalEdge* conn : {
564  fwdSplit, backSplit
565  }) {
566  if (transferCarWalk) {
567  _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, conn, length);
568  addEdge(access);
569  beforeSplit->addSuccessor(access);
570  access->addSuccessor(conn);
571  } else {
572  addRestrictedCarExit(beforeSplit, conn, SVC_TAXI);
573  }
574  }
575  }
576  if (carSplit != nullptr && transferWalkTaxi && !isAccess) {
577  _AccessEdge* access = new _AccessEdge(myNumericalID++, stopConn, carSplit, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
578  addEdge(access);
579  stopConn->addSuccessor(access);
580  access->addSuccessor(carSplit);
581  }
582 
583  // fixing depart connections for the forward pedestrian, the backward pedestrian and the car edge
584  _IntermodalEdge* const prevDep = getDepartConnector(stopEdge, splitIndex);
585  const std::vector<_IntermodalEdge*>& backSplitList = myAccessSplits[pair.second];
586  _IntermodalEdge* const backBeforeSplit = backSplitList[backSplitList.size() - 2 - splitIndex];
587  _IntermodalEdge* const depConn = new _IntermodalEdge(stopEdge->getID() + "_depart_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
588  depConn->addSuccessor(fwdSplit);
589  depConn->addSuccessor(backBeforeSplit);
590  depConn->setLength(fwdSplit->getLength());
591  prevDep->removeSuccessor(backBeforeSplit);
592  prevDep->addSuccessor(backSplit);
593  prevDep->setLength(backSplit->getLength());
594  if (carSplit != nullptr) {
595  depConn->addSuccessor(carSplit);
596  }
597 
598  // fixing arrival connections for the forward pedestrian, the backward pedestrian and the car edge
599  _IntermodalEdge* const prevArr = getArrivalConnector(stopEdge, splitIndex);
600  _IntermodalEdge* const fwdBeforeSplit = myAccessSplits[pair.first][splitIndex];
601  _IntermodalEdge* const arrConn = new _IntermodalEdge(stopEdge->getID() + "_arrival_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
602  fwdSplit->addSuccessor(arrConn);
603  backBeforeSplit->addSuccessor(arrConn);
604  arrConn->setLength(fwdSplit->getLength());
605  fwdSplit->removeSuccessor(prevArr);
606  fwdBeforeSplit->addSuccessor(prevArr);
607  prevArr->setLength(backSplit->getLength());
608  if (carSplit != nullptr) {
609  if (carSplit->removeSuccessor(prevArr)) {
610  carSplit->addSuccessor(arrConn);
611  myAccessSplits[myCarLookup[stopEdge]][splitIndex]->addSuccessor(prevArr);
612  }
613  }
614  addConnectors(depConn, arrConn, splitIndex + 1);
615  }
616  } else {
617  // pedestrians cannot walk here:
618  // add depart connectors on the stop edge so that pedestrians may start at the stop
619  std::vector<_IntermodalEdge*>& splitList = myDepartLookup[stopEdge];
620  assert(splitList.size() > 0);
621  typename std::vector<_IntermodalEdge*>::iterator splitIt = splitList.begin();
622  double totalLength = 0.;
623  _IntermodalEdge* last = nullptr;
624  while (splitIt != splitList.end() && totalLength < pos) {
625  totalLength += (*splitIt)->getLength();
626  last = *splitIt;
627  ++splitIt;
628  }
629  // insert before last
630  const double newLength = pos - (totalLength - last->getLength());
631  stopConn->setLength(newLength);
632  splitList.insert(splitIt - 1, stopConn);
633  // correct length of subsequent edge
634  last->setLength(last->getLength() - newLength);
635 #ifdef IntermodalRouter_DEBUG_ACCESS
636  std::cout << " splitList:\n";
637  for (auto conEdge : splitList) {
638  std::cout << " " << conEdge->getID() << " length=" << conEdge->getLength() << "\n";
639  }
640 #endif
641  }
642  }
643 
644  void addSchedule(const SUMOVehicleParameter& pars, const std::vector<SUMOVehicleParameter::Stop>* addStops = nullptr) {
645  SUMOTime lastUntil = 0;
646  std::vector<SUMOVehicleParameter::Stop> validStops;
647  if (addStops != nullptr) {
648  // stops are part of a stand-alone route. until times are offsets from vehicle departure
649  for (const SUMOVehicleParameter::Stop& stop : *addStops) {
650  if (myStopConnections.count(stop.busstop) > 0) {
651  // compute stop times for the first vehicle
652  const SUMOTime newUntil = stop.until + pars.depart;
653  if (newUntil >= lastUntil) {
654  validStops.push_back(stop);
655  validStops.back().until = newUntil;
656  lastUntil = newUntil;
657  } else {
658  WRITE_WARNING("Ignoring unordered stop at '" + stop.busstop + "' until " + time2string(stop.until) + " for vehicle '" + pars.id + "'.");
659  }
660  }
661  }
662  }
663  for (const SUMOVehicleParameter::Stop& stop : pars.stops) {
664  // stops are part of the vehicle until times are absolute times for the first vehicle
665  if (myStopConnections.count(stop.busstop) > 0 && stop.until >= lastUntil) {
666  validStops.push_back(stop);
667  lastUntil = stop.until;
668  } else {
669  if (stop.busstop != "" && stop.until >= 0) {
670  WRITE_WARNING("Ignoring stop at '" + stop.busstop + "' until " + time2string(stop.until) + " for vehicle '" + pars.id + "'.");
671  }
672  }
673  }
674  if (validStops.size() < 2 && pars.line != "taxi") {
675  WRITE_WARNING("Not using public transport line '" + pars.line + "' for routing persons. It has less than two usable stops.");
676  return;
677  }
678 
679  typename std::vector<_PTEdge*>& lineEdges = myPTLines[pars.line];
680  if (lineEdges.empty()) {
681  _IntermodalEdge* lastStop = nullptr;
682  Position lastPos;
683  SUMOTime lastTime = 0;
684  for (const SUMOVehicleParameter::Stop& s : validStops) {
685  _IntermodalEdge* currStop = myStopConnections[s.busstop];
686  Position stopPos = E::getStopPosition(s);
687  if (lastStop != nullptr) {
688  _PTEdge* const newEdge = new _PTEdge(s.busstop, myNumericalID++, lastStop, currStop->getEdge(), pars.line, lastPos.distanceTo(stopPos));
689  addEdge(newEdge);
690  newEdge->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s.until - lastTime);
691  lastStop->addSuccessor(newEdge);
692  newEdge->addSuccessor(currStop);
693  lineEdges.push_back(newEdge);
694  }
695  lastTime = s.until;
696  lastStop = currStop;
697  lastPos = stopPos;
698  }
699  } else {
700  if (validStops.size() != lineEdges.size() + 1) {
701  WRITE_WARNING("Number of stops for public transport line '" + pars.line + "' does not match earlier definitions, ignoring schedule.");
702  return;
703  }
704  if (lineEdges.front()->getEntryStop() != myStopConnections[validStops.front().busstop]) {
705  WRITE_WARNING("Different stop for '" + pars.line + "' compared to earlier definitions, ignoring schedule.");
706  return;
707  }
708  typename std::vector<_PTEdge*>::const_iterator lineEdge = lineEdges.begin();
709  typename std::vector<SUMOVehicleParameter::Stop>::const_iterator s = validStops.begin() + 1;
710  for (; s != validStops.end(); ++s, ++lineEdge) {
711  if ((*lineEdge)->getSuccessors(SVC_IGNORING)[0] != myStopConnections[s->busstop]) {
712  WRITE_WARNING("Different stop for '" + pars.line + "' compared to earlier definitions, ignoring schedule.");
713  return;
714  }
715  }
716  SUMOTime lastTime = validStops.front().until;
717  if (lineEdges.front()->hasSchedule(lastTime)) {
718  WRITE_WARNING("Duplicate schedule for '" + pars.line + "' at time " + time2string(lastTime) + ".");
719  }
720  for (lineEdge = lineEdges.begin(), s = validStops.begin() + 1; lineEdge != lineEdges.end(); ++lineEdge, ++s) {
721  (*lineEdge)->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s->until - lastTime);
722  lastTime = s->until;
723  }
724  }
725  }
726 
731  void addCarAccess(const E* edge, SUMOVehicleClass svc, double traveltime) {
732  assert(edge != nullptr);
733  assert(myCarLookup.count(edge) != 0);
734  assert(myBidiLookup.count(edge) != 0);
735  EdgePair pedestrianEdges = myBidiLookup[edge];
736  _IntermodalEdge* carEdge = myCarLookup[edge];
737  _AccessEdge* access = new _AccessEdge(myNumericalID++, pedestrianEdges.first, carEdge, 0, svc, SVC_IGNORING, traveltime);
738  addEdge(access);
739  pedestrianEdges.first->addSuccessor(access);
740  pedestrianEdges.second->addSuccessor(access);
741  access->addSuccessor(carEdge);
742  }
743 
750  _AccessEdge* access = new _AccessEdge(myNumericalID++, from, to, 0, SVC_IGNORING, vehicleRestriction);
751  addEdge(access);
752  from->addSuccessor(access);
753  access->addSuccessor(to);
754  }
755 
756 private:
768  int findSplitIndex(_IntermodalEdge* const toSplit, const double pos, double& relPos, bool& needSplit) {
769  relPos = pos;
770  needSplit = true;
771  int splitIndex = 0;
772  std::vector<_IntermodalEdge*>& splitList = myAccessSplits[toSplit];
773  if (!splitList.empty()) {
774  for (const _IntermodalEdge* const split : splitList) {
775  if (relPos < split->getLength() + POSITION_EPS) {
776  break;
777  }
778  relPos -= split->getLength();
779  splitIndex++;
780  }
781  assert(splitIndex < (int)splitList.size());
782  if (splitIndex + 1 < (int)splitList.size() && fabs(relPos - splitList[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(beforeSplit->getLength() - relPos);
821  beforeSplit->setLength(relPos);
822  } else {
823  afterSplit->setLength(relPos);
824  beforeSplit->setLength(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  const int myCarWalkTransfer;
882 
883 private:
886 
887 };
const L * getSidewalk(const E *edge)
std::vector< std::string > & split(const std::string &s, char delim, std::vector< std::string > &elems)
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:276
std::string time2string(SUMOTime t)
convert SUMOTime to string
Definition: SUMOTime.cpp:68
long long int SUMOTime
Definition: SUMOTime.h:31
const SVCPermissions SVCAll
all VClasses are allowed
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
int SVCPermissions
bitset where each bit declares whether a certain SVC may use this edge/lane
SumoXMLTag
Numbers representing SUMO-XML - element names.
@ SUMO_TAG_BUS_STOP
A bus stop.
@ SUMO_TAG_PARKING_AREA
A parking area.
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:44
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 pos, 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::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)
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
int findSplitIndex(_IntermodalEdge *const toSplit, const double pos, double &relPos, bool &needSplit)
Returns where to insert or use the split edge.
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:81
const std::string & getID() const
Returns the id.
Definition: Named.h:73
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:36
double distanceTo(const Position &p2) const
returns the euclidean distance in 3 dimension
Definition: Position.h:231
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