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