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-2022 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  tazDepart->addSuccessor(getDepartConnector(out));
159  getArrivalConnector(out)->addSuccessor(otherTazArrive);
160  }
161  for (const E* in : edge->getPredecessors()) {
162  getArrivalConnector(in)->addSuccessor(tazArrive);
163  otherTazDepart->addSuccessor(getDepartConnector(in));
164  }
165  continue;
166  }
167  const L* const sidewalk = getSidewalk<E, L>(edge);
168  if (sidewalk == nullptr) {
169  continue;
170  }
171  // find all incoming and outgoing lanes for the sidewalk and
172  // connect the corresponding IntermodalEdges
173  const EdgePair& pair = getBothDirections(edge);
174 #ifdef IntermodalRouter_DEBUG_NETWORK
175  std::cout << " building connections from " << sidewalk->getID() << "\n";
176 #endif
177  if (haveSeenWalkingArea) {
178  const std::vector<std::pair<const L*, const E*> > outgoing = sidewalk->getOutgoingViaLanes();
179  // if one of the outgoing lanes is a walking area it must be used.
180  // All other connections shall be ignored
181  // if it has no outgoing walking area, it probably is a walking area itself
182  bool hasWalkingArea = false;
183  for (const auto& target : outgoing) {
184  if (target.first->getEdge().isWalkingArea()) {
185  hasWalkingArea = true;
186  break;
187  }
188  }
189  for (const auto& target : outgoing) {
190  const E* const targetEdge = &(target.first->getEdge());
191  const bool used = (target.first == getSidewalk<E, L>(targetEdge)
192  && (!hasWalkingArea || targetEdge->isWalkingArea()));
193 #ifdef IntermodalRouter_DEBUG_NETWORK
194  const L* potTarget = getSidewalk<E, L>(targetEdge);
195  std::cout << " lane=" << (potTarget == 0 ? "NULL" : potTarget->getID()) << (used ? "(used)" : "") << "\n";
196 #endif
197  if (used) {
198  const EdgePair& targetPair = getBothDirections(targetEdge);
199  pair.first->addSuccessor(targetPair.first);
200  targetPair.second->addSuccessor(pair.second);
201 #ifdef IntermodalRouter_DEBUG_NETWORK
202  std::cout << " " << pair.first->getID() << " -> " << targetPair.first->getID() << "\n";
203  std::cout << " " << targetPair.second->getID() << " -> " << pair.second->getID() << "\n";
204 #endif
205  }
206  }
207  }
208  // We may have a network without pedestrian structures or a car-only edge.
209  // In the first case we assume that all sidewalks at a junction are interconnected,
210  // in the second we connect all car-only edges to all sidewalks.
211  _IntermodalEdge* const toNodeConn = myWalkingConnectorLookup[edge->getToJunction()];
212  if (toNodeConn != nullptr) {
213  // Check for the outgoing vias and use the shortest one as an approximation
214  const std::vector<std::pair<const L*, const E*> > outgoing = sidewalk->getOutgoingViaLanes();
215  double minViaLength = std::numeric_limits<double>::max();
216  const E* minVia = nullptr;
217  for (const auto& target : outgoing) {
218  if (target.second != nullptr && target.second->getLength() < minViaLength) {
219  minViaLength = target.second->getLength();
220  minVia = target.second;
221  }
222  }
223  EdgePair interVia = std::make_pair(nullptr, nullptr);
224  if (minVia != nullptr) {
225  const auto it = myBidiLookup.find(minVia);
226  if (it != myBidiLookup.end()) {
227  interVia = it->second;
228  }
229  }
230  if (!haveSeenWalkingArea) {
231  // if we have walking areas we should use them and not the connector
232  pair.first->addSuccessor(toNodeConn, interVia.first);
233  }
234  toNodeConn->addSuccessor(pair.second, interVia.second);
235  }
236  _IntermodalEdge* const fromNodeConn = myWalkingConnectorLookup[edge->getFromJunction()];
237  if (fromNodeConn != nullptr) {
238  if (!haveSeenWalkingArea) {
239  pair.second->addSuccessor(fromNodeConn);
240  }
241  fromNodeConn->addSuccessor(pair.first);
242  }
243  if (!edge->isWalkingArea()) {
244  // build connections from depart connector
245  _IntermodalEdge* startConnector = getDepartConnector(edge);
246  startConnector->addSuccessor(pair.first);
247  startConnector->addSuccessor(pair.second);
248  // build connections to arrival connector
249  _IntermodalEdge* endConnector = getArrivalConnector(edge);
250  pair.first->addSuccessor(endConnector);
251  pair.second->addSuccessor(endConnector);
252  }
253 #ifdef IntermodalRouter_DEBUG_NETWORK
254  std::cout << " " << startConnector->getID() << " -> " << pair.first->getID() << "\n";
255  std::cout << " " << startConnector->getID() << " -> " << pair.second->getID() << "\n";
256  std::cout << " " << pair.first->getID() << " -> " << endConnector->getID() << "\n";
257  std::cout << " " << pair.second->getID() << " -> " << endConnector->getID() << "\n";
258 #endif
259  }
260  }
261 
263  for (typename std::vector<_IntermodalEdge*>::iterator it = myEdges.begin(); it != myEdges.end(); ++it) {
264  delete *it;
265  }
266  }
267 
268  void addEdge(_IntermodalEdge* edge) {
269  while ((int)myEdges.size() <= edge->getNumericalID()) {
270  myEdges.push_back(0);
271  }
272  myEdges[edge->getNumericalID()] = edge;
273  }
274 
275  void addConnectors(_IntermodalEdge* const depConn, _IntermodalEdge* const arrConn, const int index) {
276  addEdge(depConn);
277  addEdge(arrConn);
278  myDepartLookup[depConn->getEdge()].insert(myDepartLookup[depConn->getEdge()].begin() + index, depConn);
279  myArrivalLookup[arrConn->getEdge()].insert(myArrivalLookup[arrConn->getEdge()].begin() + index, arrConn);
280  }
281 
282  const std::vector<_IntermodalEdge*>& getAllEdges() {
283  return myEdges;
284  }
285 
287  const EdgePair& getBothDirections(const E* e) const {
288  typename std::map<const E*, EdgePair>::const_iterator it = myBidiLookup.find(e);
289  if (it == myBidiLookup.end()) {
290  assert(false);
291  throw ProcessError("Edge '" + e->getID() + "' not found in intermodal network.'");
292  }
293  return (*it).second;
294  }
295 
297  const _IntermodalEdge* getDepartEdge(const E* e, const double pos) const {
298  typename std::map<const E*, std::vector<_IntermodalEdge*> >::const_iterator it = myDepartLookup.find(e);
299  if (it == myDepartLookup.end()) {
300  throw ProcessError("Depart edge '" + e->getID() + "' not found in intermodal network.");
301  }
302  if ((e->getPermissions() & SVC_PEDESTRIAN) == 0) {
303  // use most specific split (best trainStop, quay etc)
304  double bestDist = std::numeric_limits<double>::max();
305  const _IntermodalEdge* best = nullptr;
306  for (const _IntermodalEdge* const split : it->second) {
307  if (pos >= split->getStartPos() - POSITION_EPS && pos <= split->getEndPos() + POSITION_EPS) {
308  const double dist = split->getEndPos() - split->getStartPos();
309  if (dist < bestDist) {
310  bestDist = dist;
311  best = split;
312  }
313  }
314  }
315  assert(best != nullptr);
316  return best;
317  } else {
318  // use next downstream edge
319  const std::vector<_IntermodalEdge*>& splitList = it->second;
320  typename std::vector<_IntermodalEdge*>::const_iterator splitIt = splitList.begin();
321  double totalLength = 0.;
322  while (splitIt + 1 != splitList.end() && totalLength + (*splitIt)->getLength() < pos) {
323  totalLength += (*splitIt)->getLength();
324  ++splitIt;
325  }
326  return *splitIt;
327  }
328  }
329 
331  _IntermodalEdge* getDepartConnector(const E* e, const int splitIndex = 0) const {
332  typename std::map<const E*, std::vector<_IntermodalEdge*> >::const_iterator it = myDepartLookup.find(e);
333  if (it == myDepartLookup.end()) {
334  throw ProcessError("Depart edge '" + e->getID() + "' not found in intermodal network.");
335  }
336  if (splitIndex >= (int)it->second.size()) {
337  throw ProcessError("Split index " + toString(splitIndex) + " invalid for depart edge '" + e->getID() + "' .");
338  }
339  return it->second[splitIndex];
340  }
341 
343  _IntermodalEdge* getArrivalEdge(const E* e, const double pos) const {
344  typename std::map<const E*, std::vector<_IntermodalEdge*> >::const_iterator it = myArrivalLookup.find(e);
345  if (it == myArrivalLookup.end()) {
346  throw ProcessError("Arrival edge '" + e->getID() + "' not found in intermodal network.");
347  }
348  const std::vector<_IntermodalEdge*>& splitList = it->second;
349  typename std::vector<_IntermodalEdge*>::const_iterator splitIt = splitList.begin();
350  double totalLength = 0.;
351  while (splitIt != splitList.end() && totalLength + (*splitIt)->getLength() < pos) {
352  totalLength += (*splitIt)->getLength();
353  ++splitIt;
354  }
355  return *splitIt;
356  }
357 
359  _IntermodalEdge* getArrivalConnector(const E* e, const int splitIndex = 0) const {
360  return myArrivalLookup.find(e)->second[splitIndex];
361  }
362 
365  typename std::map<const N*, _IntermodalEdge*>::const_iterator it = myWalkingConnectorLookup.find(e->getToJunction());
366  if (it == myWalkingConnectorLookup.end()) {
367  const L* const sidewalk = getSidewalk<E, L>(e);
368  if (e->isInternal() || sidewalk == 0) {
369  return 0;
370  }
371  for (const auto& target : sidewalk->getOutgoingViaLanes()) {
372  if (target.first->getEdge().isWalkingArea()) {
373  return getBothDirections(&target.first->getEdge()).first;
374  }
375  }
376  return 0;
377  }
378  return it->second;
379  }
380 
381  void addCarEdges(const std::vector<E*>& edges, double taxiWait) {
382  for (const E* const edge : edges) {
383  if (edge->getFunction() == SumoXMLEdgeFunc::NORMAL || edge->getFunction() == SumoXMLEdgeFunc::INTERNAL) {
384  myCarLookup[edge] = new CarEdge<E, L, N, V>(myNumericalID++, edge);
385  addEdge(myCarLookup[edge]);
386  }
387  }
388  for (const auto& edgePair : myCarLookup) {
389  _IntermodalEdge* const carEdge = edgePair.second;
390  // connectivity within the car network
391  for (const auto& suc : edgePair.first->getViaSuccessors()) {
392  _IntermodalEdge* const sucCarEdge = getCarEdge(suc.first);
393  _IntermodalEdge* const sucViaEdge = getCarEdge(suc.second);
394  if (sucCarEdge != nullptr) {
395  carEdge->addSuccessor(sucCarEdge, sucViaEdge);
396  }
397  }
398  // connectivity to the pedestrian network (only for normal edges)
399  if (edgePair.first->getFunction() != SumoXMLEdgeFunc::NORMAL) {
400  continue;
401  }
402  if ((myCarWalkTransfer & ALL_JUNCTIONS) != 0) {
403  _IntermodalEdge* const walkCon = getWalkingConnector(edgePair.first);
404  if (walkCon != 0) {
405  carEdge->addSuccessor(walkCon);
406  } else {
407  // we are on an edge where pedestrians are forbidden and want to continue on an arbitrary pedestrian edge
408  for (const E* const out : edgePair.first->getToJunction()->getOutgoing()) {
409  if (!out->isInternal() && !out->isTazConnector() && getSidewalk<E, L>(out) != 0) {
410  carEdge->addSuccessor(getBothDirections(out).first);
411  }
412  }
413  for (const E* const in : edgePair.first->getToJunction()->getIncoming()) {
414  if (!in->isInternal() && !in->isTazConnector() && getSidewalk<E, L>(in) != 0) {
415  carEdge->addSuccessor(getBothDirections(in).second);
416  }
417  }
418  }
419  }
421  // add access edges that allow exiting a taxi
422  _IntermodalEdge* const walkCon = getWalkingConnector(edgePair.first);
423  if (walkCon != 0) {
424  addRestrictedCarExit(carEdge, walkCon, SVC_TAXI);
425  } else {
426  // we are on an edge where pedestrians are forbidden and want to continue on an arbitrary pedestrian edge
427  for (const E* const out : edgePair.first->getToJunction()->getOutgoing()) {
428  if (!out->isInternal() && !out->isTazConnector() && getSidewalk<E, L>(out) != 0) {
429  addRestrictedCarExit(carEdge, getBothDirections(out).first, SVC_TAXI);
430  }
431  }
432  for (const E* const in : edgePair.first->getToJunction()->getIncoming()) {
433  if (!in->isInternal() && !in->isTazConnector() && getSidewalk<E, L>(in) != 0) {
434  addRestrictedCarExit(carEdge, getBothDirections(in).second, SVC_TAXI);
435  }
436  }
437  }
438  }
439  // use intermediate access edge that prevents taxi departure
440  _IntermodalEdge* departConn = getDepartConnector(edgePair.first);
441  _AccessEdge* access = new _AccessEdge(myNumericalID++, departConn, carEdge, 0, (SVCAll & ~SVC_TAXI));
442  addEdge(access);
443  departConn->addSuccessor(access);
444  access->addSuccessor(carEdge);
445  if ((myCarWalkTransfer & TAXI_PICKUP_PT) == 0) {
446  // taxi may depart anywhere but there is a time penalty
447  _AccessEdge* taxiAccess = new _AccessEdge(myNumericalID++, departConn, carEdge, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
448  addEdge(taxiAccess);
449  departConn->addSuccessor(taxiAccess);
450  taxiAccess->addSuccessor(carEdge);
451  }
452  if ((myCarWalkTransfer & TAXI_DROPOFF_PT) == 0) {
453  // taxi (as all other cars) may arrive anywhere
454  carEdge->addSuccessor(getArrivalConnector(edgePair.first));
455  } else {
456  // use intermediate access edge that prevents taxi arrival
457  addRestrictedCarExit(carEdge, getArrivalConnector(edgePair.first), (SVCAll & ~SVC_TAXI));
458  }
459  }
460  }
461 
463  _IntermodalEdge* getCarEdge(const E* e) const {
464  if (e == nullptr) {
465  return nullptr;
466  }
467  auto it = myCarLookup.find(e);
468  if (it == myCarLookup.end()) {
469  return nullptr;
470  }
471  return it->second;
472  }
473 
475  _IntermodalEdge* getStopEdge(const std::string& stopId) const {
476  auto it = myStopConnections.find(stopId);
477  if (it == myStopConnections.end()) {
478  return nullptr;
479  }
480  return it->second;
481  }
482 
500  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) {
501  assert(stopEdge != nullptr);
502  const bool transferCarWalk = ((category == SUMO_TAG_PARKING_AREA && (myCarWalkTransfer & PARKING_AREAS) != 0) ||
503  (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & PT_STOPS) != 0));
504  const bool transferTaxiWalk = (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_DROPOFF_PT) != 0);
505  const bool transferWalkTaxi = (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_PICKUP_PT) != 0);
506  const double pos = (startPos + endPos) / 2.;
507 #ifdef IntermodalRouter_DEBUG_ACCESS
508  std::cout << "addAccess stopId=" << stopId << " stopEdge=" << stopEdge->getID() << " pos=" << pos << " length=" << length << " cat=" << category << "\n";
509 #endif
510  if (myStopConnections.count(stopId) == 0) {
511  myStopConnections[stopId] = new StopEdge<E, L, N, V>(stopId, myNumericalID++, stopEdge, startPos, endPos);
512  addEdge(myStopConnections[stopId]);
513  }
514  _IntermodalEdge* const stopConn = myStopConnections[stopId];
515  const L* lane = getSidewalk<E, L>(stopEdge);
516  if (lane != nullptr) {
517  const std::pair<_IntermodalEdge*, _IntermodalEdge*>& pair = getBothDirections(stopEdge);
518  double relPos;
519  bool needSplit;
520  const int splitIndex = findSplitIndex(pair.first, pos, relPos, needSplit);
521  _IntermodalEdge* const fwdSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, true, pos) : nullptr;
522  splitEdge(pair.first, splitIndex, fwdSplit, relPos, length, needSplit, stopConn);
523  _IntermodalEdge* const backSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, false, pos) : nullptr;
524  splitEdge(pair.second, splitIndex, backSplit, relPos, length, needSplit, stopConn, false);
525  _IntermodalEdge* carSplit = nullptr;
526  if (myCarLookup.count(stopEdge) > 0) {
527  if (needSplit) {
528  carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
529  }
530  splitEdge(myCarLookup[stopEdge], splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false, transferCarWalk);
531  }
532  if (needSplit) {
533  if (carSplit != nullptr && (transferCarWalk || transferTaxiWalk)) {
534  // adding access from car to walk
535  _IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
536  for (_IntermodalEdge* conn : {
537  fwdSplit, backSplit
538  }) {
539  if (transferCarWalk) {
540  _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, conn, length);
541  addEdge(access);
542  beforeSplit->addSuccessor(access);
543  access->addSuccessor(conn);
544  } else {
545  addRestrictedCarExit(beforeSplit, conn, SVC_TAXI);
546  }
547  }
548  }
549  if (carSplit != nullptr && transferWalkTaxi && !isAccess) {
550  _AccessEdge* access = new _AccessEdge(myNumericalID++, stopConn, carSplit, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
551  addEdge(access);
552  stopConn->addSuccessor(access);
553  access->addSuccessor(carSplit);
554  }
555 
556  // fixing depart connections for the forward pedestrian, the backward pedestrian and the car edge
557  _IntermodalEdge* const prevDep = getDepartConnector(stopEdge, splitIndex);
558  const std::vector<_IntermodalEdge*>& backSplitList = myAccessSplits[pair.second];
559  _IntermodalEdge* const backBeforeSplit = backSplitList[backSplitList.size() - 2 - splitIndex];
560  _IntermodalEdge* const depConn = new _IntermodalEdge(stopEdge->getID() + "_depart_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
561  depConn->addSuccessor(fwdSplit);
562  depConn->addSuccessor(backBeforeSplit);
563  depConn->setLength(fwdSplit->getLength());
564  prevDep->removeSuccessor(backBeforeSplit);
565  prevDep->addSuccessor(backSplit);
566  prevDep->setLength(backSplit->getLength());
567  if (carSplit != nullptr) {
568  depConn->addSuccessor(carSplit);
569  }
570 
571  // fixing arrival connections for the forward pedestrian, the backward pedestrian and the car edge
572  _IntermodalEdge* const prevArr = getArrivalConnector(stopEdge, splitIndex);
573  _IntermodalEdge* const fwdBeforeSplit = myAccessSplits[pair.first][splitIndex];
574  _IntermodalEdge* const arrConn = new _IntermodalEdge(stopEdge->getID() + "_arrival_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
575  fwdSplit->addSuccessor(arrConn);
576  backBeforeSplit->addSuccessor(arrConn);
577  arrConn->setLength(fwdSplit->getLength());
578  fwdSplit->removeSuccessor(prevArr);
579  fwdBeforeSplit->addSuccessor(prevArr);
580  prevArr->setLength(backSplit->getLength());
581  if (carSplit != nullptr) {
582  if (carSplit->removeSuccessor(prevArr)) {
583  carSplit->addSuccessor(arrConn);
584  myAccessSplits[myCarLookup[stopEdge]][splitIndex]->addSuccessor(prevArr);
585  }
586  }
587  addConnectors(depConn, arrConn, splitIndex + 1);
588  }
589  } else {
590  // pedestrians cannot walk here:
591  // add stop edge as depart connector so that pedestrians may start at the stop
592  std::vector<_IntermodalEdge*>& splitList = myDepartLookup[stopEdge];
593  assert(splitList.size() > 0);
594  typename std::vector<_IntermodalEdge*>::iterator splitIt = splitList.begin();
595  while (splitIt != splitList.end() && startPos > (*splitIt)->getEndPos()) {
596  ++splitIt;
597  }
598  splitList.insert(splitIt, stopConn);
599  }
600  }
601 
602  void addSchedule(const SUMOVehicleParameter& pars, const std::vector<SUMOVehicleParameter::Stop>* addStops = nullptr) {
603  SUMOTime lastUntil = 0;
604  std::vector<SUMOVehicleParameter::Stop> validStops;
605  if (addStops != nullptr) {
606  // stops are part of a stand-alone route. until times are offsets from vehicle departure
607  for (const SUMOVehicleParameter::Stop& stop : *addStops) {
608  if (myStopConnections.count(stop.busstop) > 0) {
609  // compute stop times for the first vehicle
610  const SUMOTime newUntil = stop.until + pars.depart;
611  if (newUntil >= lastUntil) {
612  validStops.push_back(stop);
613  validStops.back().until = newUntil;
614  lastUntil = newUntil;
615  } else {
616  WRITE_WARNING("Ignoring unordered stop at '" + stop.busstop + "' until " + time2string(stop.until) + " for vehicle '" + pars.id + "'.");
617  }
618  }
619  }
620  }
621  for (const SUMOVehicleParameter::Stop& stop : pars.stops) {
622  // stops are part of the vehicle until times are absolute times for the first vehicle
623  if (myStopConnections.count(stop.busstop) > 0 && stop.until >= lastUntil) {
624  validStops.push_back(stop);
625  lastUntil = stop.until;
626  } else {
627  if (stop.busstop != "" && stop.until >= 0) {
628  WRITE_WARNING("Ignoring stop at '" + stop.busstop + "' until " + time2string(stop.until) + " for vehicle '" + pars.id + "'.");
629  }
630  }
631  }
632  if (validStops.size() < 2 && pars.line != "taxi") {
633  WRITE_WARNING("Not using public transport line '" + pars.line + "' for routing persons. It has less than two usable stops.");
634  return;
635  }
636 
637  typename std::vector<_PTEdge*>& lineEdges = myPTLines[pars.line];
638  if (lineEdges.empty()) {
639  _IntermodalEdge* lastStop = nullptr;
640  Position lastPos;
641  SUMOTime lastTime = 0;
642  for (const SUMOVehicleParameter::Stop& s : validStops) {
643  _IntermodalEdge* currStop = myStopConnections[s.busstop];
644  Position stopPos = E::getStopPosition(s);
645  if (lastStop != nullptr) {
646  _PTEdge* const newEdge = new _PTEdge(s.busstop, myNumericalID++, lastStop, currStop->getEdge(), pars.line, lastPos.distanceTo(stopPos));
647  addEdge(newEdge);
648  newEdge->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s.until - lastTime);
649  lastStop->addSuccessor(newEdge);
650  newEdge->addSuccessor(currStop);
651  lineEdges.push_back(newEdge);
652  }
653  lastTime = s.until;
654  lastStop = currStop;
655  lastPos = stopPos;
656  }
657  } else {
658  if (validStops.size() != lineEdges.size() + 1) {
659  WRITE_WARNINGF("Number of stops for public transport line '%' does not match earlier definitions, ignoring schedule.", pars.line);
660  return;
661  }
662  if (lineEdges.front()->getEntryStop() != myStopConnections[validStops.front().busstop]) {
663  WRITE_WARNINGF("Different stop for '%' compared to earlier definitions, ignoring schedule.", pars.line);
664  return;
665  }
666  typename std::vector<_PTEdge*>::const_iterator lineEdge = lineEdges.begin();
667  typename std::vector<SUMOVehicleParameter::Stop>::const_iterator s = validStops.begin() + 1;
668  for (; s != validStops.end(); ++s, ++lineEdge) {
669  if ((*lineEdge)->getSuccessors(SVC_IGNORING)[0] != myStopConnections[s->busstop]) {
670  WRITE_WARNINGF("Different stop for '%' compared to earlier definitions, ignoring schedule.", pars.line);
671  return;
672  }
673  }
674  SUMOTime lastTime = validStops.front().until;
675  if (lineEdges.front()->hasSchedule(lastTime)) {
676  WRITE_WARNINGF("Duplicate schedule for '%' at time=%.", pars.line, time2string(lastTime));
677  }
678  for (lineEdge = lineEdges.begin(), s = validStops.begin() + 1; lineEdge != lineEdges.end(); ++lineEdge, ++s) {
679  (*lineEdge)->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s->until - lastTime);
680  lastTime = s->until;
681  }
682  }
683  }
684 
689  void addCarAccess(const E* edge, SUMOVehicleClass svc, double traveltime) {
690  assert(edge != nullptr);
691  assert(myCarLookup.count(edge) != 0);
692  assert(myBidiLookup.count(edge) != 0);
693  EdgePair pedestrianEdges = myBidiLookup[edge];
694  _IntermodalEdge* carEdge = myCarLookup[edge];
695  _AccessEdge* access = new _AccessEdge(myNumericalID++, pedestrianEdges.first, carEdge, 0, svc, SVC_IGNORING, traveltime);
696  addEdge(access);
697  pedestrianEdges.first->addSuccessor(access);
698  pedestrianEdges.second->addSuccessor(access);
699  access->addSuccessor(carEdge);
700  }
701 
708  _AccessEdge* access = new _AccessEdge(myNumericalID++, from, to, 0, SVC_IGNORING, vehicleRestriction);
709  addEdge(access);
710  from->addSuccessor(access);
711  access->addSuccessor(to);
712  }
713 
714 private:
726  int findSplitIndex(_IntermodalEdge* const toSplit, const double pos, double& relPos, bool& needSplit) const {
727  relPos = pos;
728  needSplit = true;
729  int splitIndex = 0;
730  const auto& splitList = myAccessSplits.find(toSplit);
731  if (splitList != myAccessSplits.end() && !splitList->second.empty()) {
732  for (const _IntermodalEdge* const split : splitList->second) {
733  if (relPos < split->getLength() + POSITION_EPS) {
734  break;
735  }
736  relPos -= split->getLength();
737  splitIndex++;
738  }
739  assert(splitIndex < (int)splitList->second.size());
740  if (splitIndex + 1 < (int)splitList->second.size() && fabs(relPos - splitList->second[splitIndex]->getLength()) < POSITION_EPS) {
741  needSplit = false;
742  }
743  }
744  return splitIndex;
745  }
746 
759  void splitEdge(_IntermodalEdge* const toSplit, int splitIndex,
760  _IntermodalEdge* afterSplit, const double relPos, const double length, const bool needSplit,
761  _IntermodalEdge* const stopConn, const bool forward = true, const bool addExit = true, const bool addEntry = true) {
762  std::vector<_IntermodalEdge*>& splitList = myAccessSplits[toSplit];
763  if (splitList.empty()) {
764  splitList.push_back(toSplit);
765  }
766  if (!forward) {
767  splitIndex = (int)splitList.size() - 1 - splitIndex;
768  if (!needSplit) {
769  splitIndex--;
770  }
771  }
772  _IntermodalEdge* beforeSplit = splitList[splitIndex];
773  if (needSplit) {
774  addEdge(afterSplit);
775  beforeSplit->transferSuccessors(afterSplit);
776  beforeSplit->addSuccessor(afterSplit);
777  if (forward) {
778  afterSplit->setLength(beforeSplit->getLength() - relPos);
779  beforeSplit->setLength(relPos);
780  } else {
781  afterSplit->setLength(relPos);
782  beforeSplit->setLength(beforeSplit->getLength() - relPos);
783  // rename backward edges for easier referencing
784  const std::string newID = beforeSplit->getID();
785  beforeSplit->setID(afterSplit->getID());
786  afterSplit->setID(newID);
787  }
788  splitList.insert(splitList.begin() + splitIndex + 1, afterSplit);
789  } else {
790  // don't split, use the present split edges
791  afterSplit = splitList[splitIndex + 1];
792  }
793  // add access to / from edge
794  if (addEntry) {
795  _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, stopConn, length);
796  addEdge(access);
797  beforeSplit->addSuccessor(access);
798  access->addSuccessor(stopConn);
799  }
800  if (addExit) {
801  // pedestrian case only, exit from public to pedestrian
802  _AccessEdge* exit = new _AccessEdge(myNumericalID++, stopConn, afterSplit, length);
803  addEdge(exit);
804  stopConn->addSuccessor(exit);
805  exit->addSuccessor(afterSplit);
806  }
807  }
808 
809 
810 private:
812  std::vector<_IntermodalEdge*> myEdges;
813 
815  std::map<const E*, EdgePair> myBidiLookup;
816 
818  std::map<const E*, std::vector<_IntermodalEdge*> > myDepartLookup;
819 
821  std::map<const E*, std::vector<_IntermodalEdge*> > myArrivalLookup;
822 
824  std::map<const N*, _IntermodalEdge*> myWalkingConnectorLookup;
825 
827  std::map<const E*, _IntermodalEdge*, ComparatorNumericalIdLess> myCarLookup;
828 
830  std::map<std::string, std::vector<_PTEdge*> > myPTLines;
831 
833  std::map<std::string, _IntermodalEdge*> myStopConnections;
834 
836  std::map<_IntermodalEdge*, std::vector<_IntermodalEdge*> > myAccessSplits;
837 
839  const int myCarWalkTransfer;
840 
841 private:
844 
845 };
std::vector< std::string > & split(const std::string &s, char delim, std::vector< std::string > &elems)
#define WRITE_WARNINGF(...)
Definition: MsgHandler.h:281
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:280
std::string time2string(SUMOTime t)
convert SUMOTime to string
Definition: SUMOTime.cpp:68
long long int SUMOTime
Definition: SUMOTime.h:32
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: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::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:242
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