Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/router/ROEdge.cpp
169665 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2002-2025 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
/****************************************************************************/
14
/// @file ROEdge.cpp
15
/// @author Daniel Krajzewicz
16
/// @author Jakob Erdmann
17
/// @author Christian Roessel
18
/// @author Michael Behrisch
19
/// @author Melanie Knocke
20
/// @author Yun-Pang Floetteroed
21
/// @author Ruediger Ebendt
22
/// @date Sept 2002
23
///
24
// A basic edge for routing applications
25
/****************************************************************************/
26
#include <config.h>
27
28
#include <utils/common/MsgHandler.h>
29
#include <utils/common/ToString.h>
30
#include <algorithm>
31
#include <cassert>
32
#include <iostream>
33
#include <utils/vehicle/SUMOVTypeParameter.h>
34
#include <utils/emissions/PollutantsInterface.h>
35
#include <utils/emissions/HelpersHarmonoise.h>
36
#include "ROLane.h"
37
#include "RONet.h"
38
#include "ROVehicle.h"
39
#include "ROEdge.h"
40
41
42
// ===========================================================================
43
// static member definitions
44
// ===========================================================================
45
bool ROEdge::myInterpolate = false;
46
bool ROEdge::myHaveTTWarned = false;
47
bool ROEdge::myHaveEWarned = false;
48
ROEdgeVector ROEdge::myEdges;
49
double ROEdge::myPriorityFactor(0);
50
double ROEdge::myMinEdgePriority(std::numeric_limits<double>::max());
51
double ROEdge::myEdgePriorityRange(0);
52
53
54
// ===========================================================================
55
// method definitions
56
// ===========================================================================
57
ROEdge::ROEdge(const std::string& id, RONode* from, RONode* to, int index, const int priority, const std::string& type) :
58
Named(id),
59
myFromJunction(from),
60
myToJunction(to),
61
myIndex(index),
62
myPriority(priority),
63
myType(type),
64
mySpeed(-1),
65
myLength(0),
66
myAmSink(false),
67
myAmSource(false),
68
myUsingTTTimeLine(false),
69
myUsingETimeLine(false),
70
myRestrictions(nullptr),
71
myCombinedPermissions(0),
72
myOtherTazConnector(nullptr),
73
myTimePenalty(0) {
74
while ((int)myEdges.size() <= index) {
75
myEdges.push_back(0);
76
}
77
myEdges[index] = this;
78
if (from == nullptr && to == nullptr) {
79
// TAZ edge, no lanes
80
myCombinedPermissions = SVCAll;
81
} else {
82
// TODO we should not calculate the boundary here, the position for the nodes is not valid yet
83
myBoundary.add(from->getPosition());
84
myBoundary.add(to->getPosition());
85
}
86
}
87
88
89
ROEdge::ROEdge(const std::string& id, const RONode* from, const RONode* to, SVCPermissions p) :
90
Named(id),
91
myFromJunction(const_cast<RONode*>(from)),
92
myToJunction(const_cast<RONode*>(to)),
93
myIndex(-1),
94
myPriority(0),
95
myType(""),
96
mySpeed(std::numeric_limits<double>::max()),
97
myLength(0),
98
myAmSink(false),
99
myAmSource(false),
100
myUsingTTTimeLine(false),
101
myUsingETimeLine(false),
102
myCombinedPermissions(p),
103
myOtherTazConnector(nullptr),
104
myTimePenalty(0)
105
{ }
106
107
108
ROEdge::~ROEdge() {
109
for (ROLane* const lane : myLanes) {
110
delete lane;
111
}
112
delete myReversedRoutingEdge;
113
delete myFlippedRoutingEdge;
114
delete myRailwayRoutingEdge;
115
}
116
117
118
void
119
ROEdge::addLane(ROLane* lane) {
120
const double speed = lane->getSpeed();
121
if (speed > mySpeed) {
122
mySpeed = speed;
123
myLength = lane->getLength();
124
}
125
mySpeed = speed > mySpeed ? speed : mySpeed;
126
myLanes.push_back(lane);
127
128
// integrate new allowed classes
129
myCombinedPermissions |= lane->getPermissions();
130
}
131
132
133
void
134
ROEdge::addSuccessor(ROEdge* s, ROEdge* via, std::string) {
135
if (isInternal()) {
136
// for internal edges after an internal junction,
137
// this is called twice and only the second call counts
138
myFollowingEdges.clear();
139
myFollowingViaEdges.clear();
140
}
141
if (find(myFollowingEdges.begin(), myFollowingEdges.end(), s) == myFollowingEdges.end()) {
142
myFollowingEdges.push_back(s);
143
myFollowingViaEdges.push_back(std::make_pair(s, via));
144
if (isTazConnector() && s->getFromJunction() != nullptr) {
145
myBoundary.add(s->getFromJunction()->getPosition());
146
}
147
if (!isInternal()) {
148
s->myApproachingEdges.push_back(this);
149
if (s->isTazConnector() && getToJunction() != nullptr) {
150
s->myBoundary.add(getToJunction()->getPosition());
151
}
152
}
153
if (via != nullptr) {
154
if (via->myApproachingEdges.size() == 0) {
155
via->myApproachingEdges.push_back(this);
156
}
157
}
158
}
159
}
160
161
162
void
163
ROEdge::addEffort(double value, double timeBegin, double timeEnd) {
164
myEfforts.add(timeBegin, timeEnd, value);
165
myUsingETimeLine = true;
166
}
167
168
169
void
170
ROEdge::addTravelTime(double value, double timeBegin, double timeEnd) {
171
myTravelTimes.add(timeBegin, timeEnd, value);
172
myUsingTTTimeLine = true;
173
}
174
175
176
double
177
ROEdge::getEffort(const ROVehicle* const veh, double time) const {
178
double ret = 0;
179
if (!getStoredEffort(time, ret)) {
180
return myLength / MIN2(veh->getMaxSpeed(), getVClassMaxSpeed(veh->getVClass())) + myTimePenalty;
181
}
182
return ret;
183
}
184
185
186
double
187
ROEdge::getDistanceTo(const ROEdge* other, const bool doBoundaryEstimate) const {
188
assert(this != other);
189
if (doBoundaryEstimate) {
190
return myBoundary.distanceTo2D(other->myBoundary);
191
}
192
if (isTazConnector()) {
193
if (other->isTazConnector()) {
194
return myBoundary.distanceTo2D(other->myBoundary);
195
}
196
return myBoundary.distanceTo2D(other->getFromJunction()->getPosition());
197
}
198
if (other->isTazConnector()) {
199
return other->myBoundary.distanceTo2D(getToJunction()->getPosition());
200
}
201
return getLanes()[0]->getShape()[-1].distanceTo2D(other->getLanes()[0]->getShape()[0]);
202
//return getToJunction()->getPosition().distanceTo2D(other->getFromJunction()->getPosition());
203
}
204
205
206
bool
207
ROEdge::hasLoadedTravelTime(double time) const {
208
return myUsingTTTimeLine && myTravelTimes.describesTime(time);
209
}
210
211
212
double
213
ROEdge::getTravelTime(const ROVehicle* const veh, double time) const {
214
if (myUsingTTTimeLine) {
215
if (myTravelTimes.describesTime(time)) {
216
double lineTT = myTravelTimes.getValue(time);
217
if (myInterpolate) {
218
const double inTT = lineTT;
219
const double split = (double)(myTravelTimes.getSplitTime(time, time + inTT) - time);
220
if (split >= 0) {
221
lineTT = myTravelTimes.getValue(time + inTT) * ((double)1. - split / inTT) + split;
222
}
223
}
224
return MAX2(getMinimumTravelTime(veh), lineTT);
225
} else {
226
if (!myHaveTTWarned) {
227
WRITE_WARNINGF(TL("No interval matches passed time=% in edge '%'.\n Using edge's length / max speed."), time, myID);
228
myHaveTTWarned = true;
229
}
230
}
231
}
232
const double speed = veh != nullptr ? MIN2(veh->getMaxSpeed(), veh->getType()->speedFactor.getParameter(0) * getVClassMaxSpeed(veh->getVClass())) : mySpeed;
233
return myLength / speed + myTimePenalty;
234
}
235
236
237
double
238
ROEdge::getNoiseEffort(const ROEdge* const edge, const ROVehicle* const veh, double time) {
239
double ret = 0;
240
if (!edge->getStoredEffort(time, ret)) {
241
const double v = MIN2(veh->getMaxSpeed(), edge->getVClassMaxSpeed(veh->getVClass()));
242
ret = HelpersHarmonoise::computeNoise(veh->getType()->emissionClass, v, 0);
243
}
244
return ret;
245
}
246
247
248
bool
249
ROEdge::getStoredEffort(double time, double& ret) const {
250
if (myUsingETimeLine) {
251
if (!myEfforts.describesTime(time)) {
252
if (!myHaveEWarned) {
253
WRITE_WARNINGF(TL("No interval matches passed time=% in edge '%'.\n Using edge's length / edge's speed."), time, myID);
254
myHaveEWarned = true;
255
}
256
return false;
257
}
258
if (myInterpolate) {
259
const double inTT = myTravelTimes.getValue(time);
260
const double ratio = (myEfforts.getSplitTime(time, time + inTT) - time) / inTT;
261
if (ratio >= 0.) {
262
ret = ratio * myEfforts.getValue(time) + (1. - ratio) * myEfforts.getValue(time + inTT);
263
return true;
264
}
265
}
266
ret = myEfforts.getValue(time);
267
return true;
268
}
269
return false;
270
}
271
272
273
int
274
ROEdge::getNumSuccessors() const {
275
if (myAmSink) {
276
return 0;
277
}
278
return (int) myFollowingEdges.size();
279
}
280
281
282
int
283
ROEdge::getNumPredecessors() const {
284
if (myAmSource) {
285
return 0;
286
}
287
return (int) myApproachingEdges.size();
288
}
289
290
291
const ROEdge*
292
ROEdge::getNormalBefore() const {
293
const ROEdge* result = this;
294
while (result->isInternal()) {
295
assert(myApproachingEdges.size() == 1);
296
result = result->myApproachingEdges.front();
297
}
298
return result;
299
}
300
301
302
303
const ROEdge*
304
ROEdge::getNormalAfter() const {
305
const ROEdge* result = this;
306
while (result->isInternal()) {
307
assert(myFollowingEdges.size() == 1);
308
result = result->myFollowingEdges.front();
309
}
310
return result;
311
}
312
313
314
void
315
ROEdge::buildTimeLines(const std::string& measure, const bool boundariesOverride) {
316
if (myUsingETimeLine) {
317
double value = myLength / mySpeed;
318
const SUMOEmissionClass c = PollutantsInterface::getClassByName("HBEFA4/default");
319
if (measure == "CO") {
320
value = PollutantsInterface::compute(c, PollutantsInterface::CO, mySpeed, 0, 0, nullptr) * value; // @todo: give correct slope
321
}
322
if (measure == "CO2") {
323
value = PollutantsInterface::compute(c, PollutantsInterface::CO2, mySpeed, 0, 0, nullptr) * value; // @todo: give correct slope
324
}
325
if (measure == "HC") {
326
value = PollutantsInterface::compute(c, PollutantsInterface::HC, mySpeed, 0, 0, nullptr) * value; // @todo: give correct slope
327
}
328
if (measure == "PMx") {
329
value = PollutantsInterface::compute(c, PollutantsInterface::PM_X, mySpeed, 0, 0, nullptr) * value; // @todo: give correct slope
330
}
331
if (measure == "NOx") {
332
value = PollutantsInterface::compute(c, PollutantsInterface::NO_X, mySpeed, 0, 0, nullptr) * value; // @todo: give correct slope
333
}
334
if (measure == "fuel") {
335
value = PollutantsInterface::compute(c, PollutantsInterface::FUEL, mySpeed, 0, 0, nullptr) * value; // @todo: give correct slope
336
}
337
if (measure == "electricity") {
338
value = PollutantsInterface::compute(c, PollutantsInterface::ELEC, mySpeed, 0, 0, nullptr) * value; // @todo: give correct slope
339
}
340
myEfforts.fillGaps(value, boundariesOverride);
341
}
342
if (myUsingTTTimeLine) {
343
myTravelTimes.fillGaps(myLength / mySpeed + myTimePenalty, boundariesOverride);
344
}
345
}
346
347
348
void
349
ROEdge::cacheParamRestrictions(const std::vector<std::string>& restrictionKeys) {
350
for (const std::string& key : restrictionKeys) {
351
const std::string value = getParameter(key, "1e40");
352
myParamRestrictions.push_back(StringUtils::toDouble(value));
353
}
354
}
355
356
357
double
358
ROEdge::getLengthGeometryFactor() const {
359
return myLanes.empty() ? 1. : myLanes[0]->getShape().length() / myLanes[0]->getLength();
360
}
361
362
363
bool
364
ROEdge::allFollowersProhibit(const ROVehicle* const vehicle) const {
365
for (ROEdgeVector::const_iterator i = myFollowingEdges.begin(); i != myFollowingEdges.end(); ++i) {
366
if (!(*i)->prohibits(vehicle)) {
367
return false;
368
}
369
}
370
return true;
371
}
372
373
374
const ROEdgeVector&
375
ROEdge::getAllEdges() {
376
return myEdges;
377
}
378
379
380
const Position
381
ROEdge::getStopPosition(const SUMOVehicleParameter::Stop& stop) {
382
const double middle = (stop.endPos + stop.startPos) / 2.;
383
const ROEdge* const edge = RONet::getInstance()->getEdge(SUMOXMLDefinitions::getEdgeIDFromLane(stop.lane));
384
return (edge->getFromJunction()->getPosition() + edge->getToJunction()->getPosition()) * (middle / edge->getLength());
385
}
386
387
388
const ROEdgeVector&
389
ROEdge::getSuccessors(SUMOVehicleClass vClass) const {
390
if (vClass == SVC_IGNORING || !RONet::getInstance()->hasPermissions() || isTazConnector()) {
391
return myFollowingEdges;
392
}
393
#ifdef HAVE_FOX
394
FXMutexLock locker(myLock);
395
#endif
396
std::map<SUMOVehicleClass, ROEdgeVector>::const_iterator i = myClassesSuccessorMap.find(vClass);
397
if (i != myClassesSuccessorMap.end()) {
398
// can use cached value
399
return i->second;
400
}
401
// this vClass is requested for the first time. rebuild all successors
402
std::set<ROEdge*> followers;
403
for (const ROLane* const lane : myLanes) {
404
if ((lane->getPermissions() & vClass) != 0) {
405
for (const auto& next : lane->getOutgoingViaLanes()) {
406
if ((next.first->getPermissions() & vClass) != 0 && (next.second == nullptr || (next.second->getPermissions() & vClass) != 0)) {
407
followers.insert(&next.first->getEdge());
408
}
409
}
410
}
411
}
412
// also add district edges (they are not connected at the lane level
413
for (ROEdgeVector::const_iterator it = myFollowingEdges.begin(); it != myFollowingEdges.end(); ++it) {
414
if ((*it)->isTazConnector()) {
415
followers.insert(*it);
416
}
417
}
418
myClassesSuccessorMap[vClass].insert(myClassesSuccessorMap[vClass].begin(),
419
followers.begin(), followers.end());
420
return myClassesSuccessorMap[vClass];
421
}
422
423
424
const ROConstEdgePairVector&
425
ROEdge::getViaSuccessors(SUMOVehicleClass vClass, bool /*ignoreTransientPermissions*/) const {
426
if (vClass == SVC_IGNORING || !RONet::getInstance()->hasPermissions() || isTazConnector()) {
427
return myFollowingViaEdges;
428
}
429
#ifdef HAVE_FOX
430
FXMutexLock locker(myLock);
431
#endif
432
std::map<SUMOVehicleClass, ROConstEdgePairVector>::const_iterator i = myClassesViaSuccessorMap.find(vClass);
433
if (i != myClassesViaSuccessorMap.end()) {
434
// can use cached value
435
return i->second;
436
}
437
// this vClass is requested for the first time. rebuild all successors
438
std::set<std::pair<const ROEdge*, const ROEdge*> > followers;
439
for (const ROLane* const lane : myLanes) {
440
if ((lane->getPermissions() & vClass) != 0) {
441
for (const auto& next : lane->getOutgoingViaLanes()) {
442
if ((next.first->getPermissions() & vClass) != 0 && (next.second == nullptr || (next.second->getPermissions() & vClass) != 0)) {
443
followers.insert(std::make_pair(&next.first->getEdge(), next.second));
444
}
445
}
446
}
447
}
448
// also add district edges (they are not connected at the lane level
449
for (const ROEdge* e : myFollowingEdges) {
450
if (e->isTazConnector()) {
451
followers.insert(std::make_pair(e, e));
452
}
453
}
454
myClassesViaSuccessorMap[vClass].insert(myClassesViaSuccessorMap[vClass].begin(),
455
followers.begin(), followers.end());
456
return myClassesViaSuccessorMap[vClass];
457
}
458
459
460
bool
461
ROEdge::isConnectedTo(const ROEdge& e, const SUMOVehicleClass vClass, bool ignoreTransientPermissions) const {
462
// @todo needs to be used with #12501
463
UNUSED_PARAMETER(ignoreTransientPermissions);
464
const ROEdgeVector& followers = getSuccessors(vClass);
465
return std::find(followers.begin(), followers.end(), &e) != followers.end();
466
}
467
468
bool
469
ROEdge::initPriorityFactor(double priorityFactor) {
470
myPriorityFactor = priorityFactor;
471
double maxEdgePriority = -std::numeric_limits<double>::max();
472
for (ROEdge* edge : myEdges) {
473
maxEdgePriority = MAX2(maxEdgePriority, (double)edge->getPriority());
474
myMinEdgePriority = MIN2(myMinEdgePriority, (double)edge->getPriority());
475
}
476
myEdgePriorityRange = maxEdgePriority - myMinEdgePriority;
477
if (myEdgePriorityRange == 0) {
478
WRITE_WARNING(TL("Option weights.priority-factor does not take effect because all edges have the same priority."));
479
myPriorityFactor = 0;
480
return false;
481
}
482
return true;
483
}
484
485
486
/****************************************************************************/
487
488