LCOV - code coverage report
Current view: top level - src/libsumo - Helper.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 826 889 92.9 %
Date: 2024-04-27 15:34:54 Functions: 58 63 92.1 %

          Line data    Source code
       1             : /****************************************************************************/
       2             : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3             : // Copyright (C) 2017-2024 German Aerospace Center (DLR) and others.
       4             : // This program and the accompanying materials are made available under the
       5             : // terms of the Eclipse Public License 2.0 which is available at
       6             : // https://www.eclipse.org/legal/epl-2.0/
       7             : // This Source Code may also be made available under the following Secondary
       8             : // Licenses when the conditions for such availability set forth in the Eclipse
       9             : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10             : // or later which is available at
      11             : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12             : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13             : /****************************************************************************/
      14             : /// @file    Helper.cpp
      15             : /// @author  Laura Bieker-Walz
      16             : /// @author  Robert Hilbrich
      17             : /// @author  Leonhard Luecken
      18             : /// @date    15.09.2017
      19             : ///
      20             : // C++ TraCI client API implementation
      21             : /****************************************************************************/
      22             : #include <config.h>
      23             : 
      24             : #include <cstring>
      25             : #include <utils/geom/GeomHelper.h>
      26             : #include <utils/geom/GeoConvHelper.h>
      27             : #include <microsim/MSNet.h>
      28             : #include <microsim/MSVehicleControl.h>
      29             : #include <microsim/MSEdgeControl.h>
      30             : #include <microsim/MSInsertionControl.h>
      31             : #include <microsim/MSEdge.h>
      32             : #include <microsim/MSLane.h>
      33             : #include <microsim/MSLink.h>
      34             : #include <microsim/MSStoppingPlace.h>
      35             : #include <microsim/MSVehicle.h>
      36             : #include <microsim/output/MSE3Collector.h>
      37             : #include <microsim/transportables/MSTransportable.h>
      38             : #include <microsim/transportables/MSTransportableControl.h>
      39             : #include <microsim/transportables/MSPerson.h>
      40             : #include <microsim/lcmodels/MSAbstractLaneChangeModel.h>
      41             : #include <libsumo/StorageHelper.h>
      42             : #include <libsumo/TraCIDefs.h>
      43             : #include <libsumo/BusStop.h>
      44             : #include <libsumo/Calibrator.h>
      45             : #include <libsumo/ChargingStation.h>
      46             : #include <libsumo/Edge.h>
      47             : #ifdef HAVE_LIBSUMOGUI
      48             : #include <libsumo/GUI.h>
      49             : #endif
      50             : #include <libsumo/InductionLoop.h>
      51             : #include <libsumo/Junction.h>
      52             : #include <libsumo/Lane.h>
      53             : #include <libsumo/LaneArea.h>
      54             : #include <libsumo/MeanData.h>
      55             : #include <libsumo/MultiEntryExit.h>
      56             : #include <libsumo/OverheadWire.h>
      57             : #include <libsumo/ParkingArea.h>
      58             : #include <libsumo/Person.h>
      59             : #include <libsumo/POI.h>
      60             : #include <libsumo/Polygon.h>
      61             : #include <libsumo/Rerouter.h>
      62             : #include <libsumo/Route.h>
      63             : #include <libsumo/RouteProbe.h>
      64             : #include <libsumo/Simulation.h>
      65             : #include <libsumo/TrafficLight.h>
      66             : #include <libsumo/VariableSpeedSign.h>
      67             : #include <libsumo/Vehicle.h>
      68             : #include <libsumo/VehicleType.h>
      69             : #include <libsumo/TraCIConstants.h>
      70             : #include "Helper.h"
      71             : 
      72             : #define FAR_AWAY 1000.0
      73             : 
      74             : //#define DEBUG_MOVEXY
      75             : //#define DEBUG_MOVEXY_ANGLE
      76             : //#define DEBUG_SURROUNDING
      77             : 
      78             : namespace libsumo {
      79             : // ===========================================================================
      80             : // static member initializations
      81             : // ===========================================================================
      82             : std::vector<Subscription> Helper::mySubscriptions;
      83             : Subscription* Helper::myLastContextSubscription = nullptr;
      84             : std::map<int, std::shared_ptr<VariableWrapper> > Helper::myWrapper;
      85             : Helper::VehicleStateListener Helper::myVehicleStateListener;
      86             : Helper::TransportableStateListener Helper::myTransportableStateListener;
      87             : LANE_RTREE_QUAL* Helper::myLaneTree;
      88             : std::map<std::string, MSVehicle*> Helper::myRemoteControlledVehicles;
      89             : std::map<std::string, MSPerson*> Helper::myRemoteControlledPersons;
      90             : 
      91             : 
      92             : // ===========================================================================
      93             : // static member definitions
      94             : // ===========================================================================
      95             : 
      96             : void
      97           0 : Helper::debugPrint(const SUMOTrafficObject* veh) {
      98           0 :     if (veh != nullptr) {
      99           0 :         if (veh->isVehicle()) {
     100           0 :             std::cout << "  '" << veh->getID() << "' on lane '" << ((SUMOVehicle*)veh)->getLane()->getID() << "'\n";
     101             :         } else {
     102           0 :             std::cout << "  '" << veh->getID() << "' on edge '" << veh->getEdge()->getID() << "'\n";
     103             :         }
     104             :     }
     105           0 : }
     106             : 
     107             : 
     108             : void
     109        2848 : Helper::subscribe(const int commandId, const std::string& id, const std::vector<int>& variables,
     110             :                   const double beginTime, const double endTime, const libsumo::TraCIResults& params,
     111             :                   const int contextDomain, const double range) {
     112        2848 :     myLastContextSubscription = nullptr;
     113        2848 :     if (variables.empty()) {
     114        1397 :         for (std::vector<libsumo::Subscription>::iterator j = mySubscriptions.begin(); j != mySubscriptions.end();) {
     115         713 :             if (j->id == id && j->commandId == commandId && j->contextDomain == contextDomain) {
     116             :                 j = mySubscriptions.erase(j);
     117             :             } else {
     118             :                 ++j;
     119             :             }
     120             :         }
     121         684 :         return;
     122             :     }
     123             :     std::vector<std::shared_ptr<tcpip::Storage> > parameters;
     124        4511 :     for (const int var : variables) {
     125             :         const auto& p = params.find(var);
     126        2347 :         if (p == params.end()) {
     127        2299 :             parameters.push_back(std::make_shared<tcpip::Storage>());
     128             :         } else {
     129          96 :             parameters.push_back(libsumo::StorageHelper::toStorage(*p->second));
     130             :         }
     131             :     }
     132        2164 :     const SUMOTime begin = beginTime == INVALID_DOUBLE_VALUE ? 0 : TIME2STEPS(beginTime);
     133        2164 :     const SUMOTime end = endTime == INVALID_DOUBLE_VALUE || endTime > STEPS2TIME(SUMOTime_MAX) ? SUMOTime_MAX : TIME2STEPS(endTime);
     134        2164 :     libsumo::Subscription s(commandId, id, variables, parameters, begin, end, contextDomain, range);
     135        2164 :     if (commandId == libsumo::CMD_SUBSCRIBE_SIM_CONTEXT) {
     136          98 :         s.range = std::numeric_limits<double>::max();
     137             :     }
     138        2164 :     if (s.variables.size() == 1 && s.variables.front() == -1) {
     139             :         s.variables.clear();
     140             :     }
     141        2164 :     handleSingleSubscription(s);
     142        1691 :     libsumo::Subscription* modifiedSubscription = nullptr;
     143        1691 :     needNewSubscription(s, mySubscriptions, modifiedSubscription);
     144        1691 :     if (modifiedSubscription->isVehicleToVehicleContextSubscription()
     145             :             || modifiedSubscription->isVehicleToPersonContextSubscription()) {
     146             :         // Set last modified vehicle context subscription active for filter modifications
     147         138 :         myLastContextSubscription = modifiedSubscription;
     148             :     }
     149        2637 : }
     150             : 
     151             : 
     152             : void
     153      156459 : Helper::handleSubscriptions(const SUMOTime t) {
     154     1255395 :     for (auto& wrapper : myWrapper) {
     155     1098936 :         wrapper.second->clear();
     156             :     }
     157      248127 :     for (std::vector<libsumo::Subscription>::iterator i = mySubscriptions.begin(); i != mySubscriptions.end();) {
     158             :         const libsumo::Subscription& s = *i;
     159       91668 :         const bool isArrivedVehicle = (s.commandId == CMD_SUBSCRIBE_VEHICLE_VARIABLE || s.commandId == CMD_SUBSCRIBE_VEHICLE_CONTEXT)
     160       91668 :                                       && (find(getVehicleStateChanges(MSNet::VehicleState::ARRIVED).begin(), getVehicleStateChanges(MSNet::VehicleState::ARRIVED).end(), s.id) != getVehicleStateChanges(MSNet::VehicleState::ARRIVED).end());
     161       91668 :         const bool isArrivedPerson = (s.commandId == libsumo::CMD_SUBSCRIBE_PERSON_VARIABLE || s.commandId == libsumo::CMD_SUBSCRIBE_PERSON_CONTEXT)
     162       91668 :                                      && MSNet::getInstance()->getPersonControl().get(s.id) == nullptr;
     163       91668 :         if (s.endTime < t || isArrivedVehicle || isArrivedPerson) {
     164             :             i = mySubscriptions.erase(i);
     165         106 :             continue;
     166             :         }
     167             :         ++i;
     168             :     }
     169      248021 :     for (const libsumo::Subscription& s : mySubscriptions) {
     170       91562 :         if (s.beginTime <= t) {
     171       91562 :             handleSingleSubscription(s);
     172             :         }
     173             :     }
     174      156459 : }
     175             : 
     176             : 
     177             : bool
     178        9524 : Helper::needNewSubscription(libsumo::Subscription& s, std::vector<Subscription>& subscriptions, libsumo::Subscription*& modifiedSubscription) {
     179     1123167 :     for (libsumo::Subscription& o : subscriptions) {
     180     1110154 :         if (s.commandId == o.commandId && s.id == o.id &&
     181        1946 :                 s.beginTime == o.beginTime && s.endTime == o.endTime &&
     182     1117403 :                 s.contextDomain == o.contextDomain && s.range == o.range) {
     183             :             std::vector<std::shared_ptr<tcpip::Storage> >::const_iterator k = s.parameters.begin();
     184        3696 :             for (const int v : s.variables) {
     185        1851 :                 const int offset = (int)(std::find(o.variables.begin(), o.variables.end(), v) - o.variables.begin());
     186        1851 :                 if (offset == (int)o.variables.size() || o.parameters[offset]->size() != (*k)->size() || !std::equal((*k)->begin(), (*k)->end(), o.parameters[offset]->begin())) {
     187          53 :                     o.variables.push_back(v);
     188          53 :                     o.parameters.push_back(*k);
     189             :                 }
     190             :                 ++k;
     191             :             }
     192        1845 :             modifiedSubscription = &o;
     193             :             return false;
     194             :         }
     195             :     }
     196        7679 :     subscriptions.push_back(s);
     197        7679 :     modifiedSubscription = &subscriptions.back();
     198        7679 :     return true;
     199             : }
     200             : 
     201             : 
     202             : void
     203       36934 : Helper::clearSubscriptions() {
     204             :     mySubscriptions.clear();
     205       36934 :     myLastContextSubscription = nullptr;
     206       36934 : }
     207             : 
     208             : 
     209             : Subscription*
     210         371 : Helper::addSubscriptionFilter(SubscriptionFilterType filter) {
     211         371 :     if (myLastContextSubscription != nullptr) {
     212         370 :         myLastContextSubscription->activeFilters |= filter;
     213             :     } else {
     214             :         // The following code relies on the fact that the filter is 2^(filterType-1),
     215             :         // see Subscription.h and the corresponding TraCIConstants.h.
     216             :         // It is only for getting similar error messages with libsumo and traci.
     217           1 :         int index = (int)filter;
     218             :         int filterType = 0;
     219           1 :         if (index != 0) {
     220             :             ++filterType;
     221          11 :             while (index >>= 1) {
     222          10 :                 ++filterType;
     223             :             }
     224             :         }
     225           2 :         throw TraCIException("No previous vehicle context subscription exists to apply filter type " + toHex(filterType, 2));
     226             :     }
     227         370 :     return myLastContextSubscription;
     228             : }
     229             : 
     230             : 
     231             : void
     232       93726 : Helper::handleSingleSubscription(const Subscription& s) {
     233       93726 :     const int getCommandId = s.contextDomain > 0 ? s.contextDomain : s.commandId - 0x30;
     234             :     std::set<std::string> objIDs;
     235       93726 :     if (s.contextDomain > 0) {
     236       59644 :         if ((s.activeFilters & SUBS_FILTER_NO_RTREE) == 0) {
     237       57948 :             PositionVector shape;
     238       57948 :             findObjectShape(s.commandId, s.id, shape);
     239       57948 :             collectObjectIDsInRange(s.contextDomain, shape, s.range, objIDs);
     240       57948 :         }
     241       59171 :         applySubscriptionFilters(s, objIDs);
     242             :     } else {
     243       34082 :         objIDs.insert(s.id);
     244             :     }
     245       93253 :     if (myWrapper.empty()) {
     246         267 :         myWrapper[libsumo::CMD_GET_BUSSTOP_VARIABLE] = BusStop::makeWrapper();
     247         267 :         myWrapper[libsumo::CMD_GET_CALIBRATOR_VARIABLE] = Calibrator::makeWrapper();
     248         267 :         myWrapper[libsumo::CMD_GET_CHARGINGSTATION_VARIABLE] = ChargingStation::makeWrapper();
     249         267 :         myWrapper[libsumo::CMD_GET_EDGE_VARIABLE] = Edge::makeWrapper();
     250             : #ifdef HAVE_LIBSUMOGUI
     251         267 :         myWrapper[libsumo::CMD_GET_GUI_VARIABLE] = GUI::makeWrapper();
     252             : #endif
     253         267 :         myWrapper[libsumo::CMD_GET_INDUCTIONLOOP_VARIABLE] = InductionLoop::makeWrapper();
     254         267 :         myWrapper[libsumo::CMD_GET_JUNCTION_VARIABLE] = Junction::makeWrapper();
     255         267 :         myWrapper[libsumo::CMD_GET_LANE_VARIABLE] = Lane::makeWrapper();
     256         267 :         myWrapper[libsumo::CMD_GET_LANEAREA_VARIABLE] = LaneArea::makeWrapper();
     257         267 :         myWrapper[libsumo::CMD_GET_MEANDATA_VARIABLE] = MeanData::makeWrapper();
     258         267 :         myWrapper[libsumo::CMD_GET_MULTIENTRYEXIT_VARIABLE] = MultiEntryExit::makeWrapper();
     259         267 :         myWrapper[libsumo::CMD_GET_OVERHEADWIRE_VARIABLE] = OverheadWire::makeWrapper();
     260         267 :         myWrapper[libsumo::CMD_GET_PARKINGAREA_VARIABLE] = ParkingArea::makeWrapper();
     261         267 :         myWrapper[libsumo::CMD_GET_PERSON_VARIABLE] = Person::makeWrapper();
     262         267 :         myWrapper[libsumo::CMD_GET_POI_VARIABLE] = POI::makeWrapper();
     263         267 :         myWrapper[libsumo::CMD_GET_POLYGON_VARIABLE] = Polygon::makeWrapper();
     264         267 :         myWrapper[libsumo::CMD_GET_REROUTER_VARIABLE] = Rerouter::makeWrapper();
     265         267 :         myWrapper[libsumo::CMD_GET_ROUTE_VARIABLE] = Route::makeWrapper();
     266         267 :         myWrapper[libsumo::CMD_GET_ROUTEPROBE_VARIABLE] = RouteProbe::makeWrapper();
     267         267 :         myWrapper[libsumo::CMD_GET_SIM_VARIABLE] = Simulation::makeWrapper();
     268         267 :         myWrapper[libsumo::CMD_GET_TL_VARIABLE] = TrafficLight::makeWrapper();
     269         267 :         myWrapper[libsumo::CMD_GET_VARIABLESPEEDSIGN_VARIABLE] = VariableSpeedSign::makeWrapper();
     270         267 :         myWrapper[libsumo::CMD_GET_VEHICLE_VARIABLE] = Vehicle::makeWrapper();
     271        1007 :         myWrapper[libsumo::CMD_GET_VEHICLETYPE_VARIABLE] = VehicleType::makeWrapper();
     272             :     }
     273             :     auto wrapper = myWrapper.find(getCommandId);
     274       93253 :     if (wrapper == myWrapper.end()) {
     275           0 :         throw TraCIException("Unsupported command specified");
     276             :     }
     277             :     std::shared_ptr<VariableWrapper> handler = wrapper->second;
     278             :     VariableWrapper* container = handler.get();
     279       93253 :     if (s.contextDomain > 0) {
     280       59171 :         auto containerWrapper = myWrapper.find(s.commandId + 0x20);
     281       59171 :         if (containerWrapper == myWrapper.end()) {
     282           0 :             throw TraCIException("Unsupported domain specified");
     283             :         }
     284             :         container = containerWrapper->second.get();
     285       59171 :         container->setContext(&s.id);
     286             :     } else {
     287       34082 :         container->setContext(nullptr);
     288             :     }
     289      317230 :     for (const std::string& objID : objIDs) {
     290      223977 :         if (!s.variables.empty()) {
     291             :             std::vector<std::shared_ptr<tcpip::Storage> >::const_iterator k = s.parameters.begin();
     292      493158 :             for (const int variable : s.variables) {
     293      271027 :                 if (s.contextDomain > 0 && variable == libsumo::TRACI_ID_LIST) {
     294        4382 :                     container->empty(objID);
     295             :                 } else {
     296      266645 :                     (*k)->resetPos();
     297      266645 :                     handler->handle(objID, variable, container, k->get());
     298             :                     ++k;
     299             :                 }
     300             :             }
     301             :         } else {
     302        1846 :             if (s.contextDomain == 0 && getCommandId == libsumo::CMD_GET_VEHICLE_VARIABLE) {
     303             :                 // default for vehicles is edge id and lane position
     304         654 :                 handler->handle(objID, VAR_ROAD_ID, container, nullptr);
     305         654 :                 handler->handle(objID, VAR_LANEPOSITION, container, nullptr);
     306        1192 :             } else if (s.contextDomain > 0) {
     307             :                 // default for contexts is an empty map (similar to id list)
     308          34 :                 container->empty(objID);
     309        1158 :             } else if (!handler->handle(objID, libsumo::LAST_STEP_VEHICLE_NUMBER, container, nullptr)) {
     310             :                 // default for detectors is vehicle number, for all others id list
     311         612 :                 handler->handle(objID, libsumo::TRACI_ID_LIST, container, nullptr);
     312             :             }
     313             :         }
     314             :     }
     315       93253 : }
     316             : 
     317             : 
     318             : void
     319        1660 : Helper::fuseLaneCoverage(std::shared_ptr<LaneCoverageInfo> aggregatedLaneCoverage, const std::shared_ptr<LaneCoverageInfo> newLaneCoverage) {
     320        7405 :     for (auto& p : *newLaneCoverage) {
     321        5745 :         const MSLane* lane = p.first;
     322        5745 :         if (aggregatedLaneCoverage->find(lane) == aggregatedLaneCoverage->end()) {
     323             :             // Lane has no coverage in aggregatedLaneCoverage, yet
     324        5725 :             (*aggregatedLaneCoverage)[lane] = (*newLaneCoverage)[lane];
     325             :         } else {
     326             :             // Lane is covered in aggregatedLaneCoverage as well
     327          20 :             std::pair<double, double>& range1 = (*aggregatedLaneCoverage)[lane];
     328          20 :             std::pair<double, double>& range2 = (*newLaneCoverage)[lane];
     329          35 :             std::pair<double, double> hull = std::make_pair(MIN2(range1.first, range2.first), MAX2(range1.second, range2.second));
     330          20 :             (*aggregatedLaneCoverage)[lane] = hull;
     331             :         }
     332             :     }
     333        1660 : }
     334             : 
     335             : 
     336             : TraCIPositionVector
     337       10369 : Helper::makeTraCIPositionVector(const PositionVector& positionVector) {
     338             :     TraCIPositionVector tp;
     339       53668 :     for (int i = 0; i < (int)positionVector.size(); ++i) {
     340       86598 :         tp.value.push_back(makeTraCIPosition(positionVector[i]));
     341             :     }
     342       10369 :     return tp;
     343             : }
     344             : 
     345             : 
     346             : PositionVector
     347         278 : Helper::makePositionVector(const TraCIPositionVector& vector) {
     348         278 :     PositionVector pv;
     349        8807 :     for (const TraCIPosition& pos : vector.value) {
     350        8531 :         if (std::isnan(pos.x) || std::isnan(pos.y)) {
     351           4 :             throw libsumo::TraCIException("NaN-Value in shape.");
     352             :         }
     353        8529 :         pv.push_back(Position(pos.x, pos.y));
     354             :     }
     355         276 :     return pv;
     356           2 : }
     357             : 
     358             : 
     359             : TraCIColor
     360        1384 : Helper::makeTraCIColor(const RGBColor& color) {
     361             :     TraCIColor tc;
     362        1384 :     tc.a = color.alpha();
     363        1384 :     tc.b = color.blue();
     364        1384 :     tc.g = color.green();
     365        1384 :     tc.r = color.red();
     366        1384 :     return tc;
     367             : }
     368             : 
     369             : 
     370             : RGBColor
     371         434 : Helper::makeRGBColor(const TraCIColor& c) {
     372         434 :     return RGBColor((unsigned char)c.r, (unsigned char)c.g, (unsigned char)c.b, (unsigned char)c.a);
     373             : }
     374             : 
     375             : 
     376             : TraCIPosition
     377     2310956 : Helper::makeTraCIPosition(const Position& position, const bool includeZ) {
     378     2310956 :     TraCIPosition p;
     379     2310956 :     p.x = position.x();
     380     2310956 :     p.y = position.y();
     381     2310956 :     p.z = includeZ ? position.z() : INVALID_DOUBLE_VALUE;
     382     2310956 :     return p;
     383             : }
     384             : 
     385             : 
     386             : Position
     387           0 : Helper::makePosition(const TraCIPosition& tpos) {
     388           0 :     return Position(tpos.x, tpos.y, tpos.z);
     389             : }
     390             : 
     391             : 
     392             : MSEdge*
     393        3937 : Helper::getEdge(const std::string& edgeID) {
     394        3937 :     MSEdge* edge = MSEdge::dictionary(edgeID);
     395        3937 :     if (edge == nullptr) {
     396           0 :         throw TraCIException("Referenced edge '" + edgeID + "' is not known.");
     397             :     }
     398        3937 :     return edge;
     399             : }
     400             : 
     401             : 
     402             : const MSLane*
     403         832 : Helper::getLaneChecking(const std::string& edgeID, int laneIndex, double pos) {
     404         832 :     const MSEdge* edge = MSEdge::dictionary(edgeID);
     405         832 :     if (edge == nullptr) {
     406           2 :         throw TraCIException("Unknown edge " + edgeID);
     407             :     }
     408         831 :     if (laneIndex < 0 || laneIndex >= (int)edge->getLanes().size()) {
     409           2 :         throw TraCIException("Invalid lane index for " + edgeID);
     410             :     }
     411         830 :     const MSLane* lane = edge->getLanes()[laneIndex];
     412         830 :     if (pos < 0 || pos > lane->getLength()) {
     413           6 :         throw TraCIException("Position on lane invalid");
     414             :     }
     415         827 :     return lane;
     416             : }
     417             : 
     418             : 
     419             : std::pair<MSLane*, double>
     420         655 : Helper::convertCartesianToRoadMap(const Position& pos, const SUMOVehicleClass vClass) {
     421         655 :     const PositionVector shape({ pos });
     422             :     std::pair<MSLane*, double> result(nullptr, -1);
     423             :     double range = 1000.;
     424         655 :     const Boundary& netBounds = GeoConvHelper::getFinal().getConvBoundary();
     425         655 :     const double maxRange = MAX2(1001., netBounds.getWidth() + netBounds.getHeight() + netBounds.distanceTo2D(pos));
     426         726 :     while (range < maxRange) {
     427             :         std::set<const Named*> lanes;
     428         726 :         collectObjectsInRange(libsumo::CMD_GET_LANE_VARIABLE, shape, range, lanes);
     429             :         double minDistance = std::numeric_limits<double>::max();
     430       24262 :         for (const Named* named : lanes) {
     431       23536 :             MSLane* lane = const_cast<MSLane*>(dynamic_cast<const MSLane*>(named));
     432       23536 :             if (lane->allowsVehicleClass(vClass)) {
     433             :                 // @todo this may be a place where 3D is required but 2D is used
     434       23530 :                 const double newDistance = lane->getShape().distance2D(pos);
     435       23530 :                 if (newDistance < minDistance ||
     436             :                         (newDistance == minDistance
     437         861 :                          && result.first != nullptr
     438         861 :                          && lane->getID() < result.first->getID())) {
     439             :                     minDistance = newDistance;
     440             :                     result.first = lane;
     441             :                 }
     442             :             }
     443             :         }
     444         726 :         if (minDistance < std::numeric_limits<double>::max()) {
     445         655 :             result.second = result.first->interpolateGeometryPosToLanePos(result.first->getShape().nearest_offset_to_point2D(pos, false));
     446             :             break;
     447             :         }
     448          71 :         range *= 2;
     449             :     }
     450         655 :     return result;
     451         655 : }
     452             : 
     453             : 
     454             : double
     455         271 : Helper::getDrivingDistance(std::pair<const MSLane*, double>& roadPos1, std::pair<const MSLane*, double>& roadPos2) {
     456         271 :     if (roadPos1.first == roadPos2.first && roadPos1.second <= roadPos2.second) {
     457             :         // same edge
     458         102 :         return roadPos2.second - roadPos1.second;
     459             :     }
     460             :     double distance = 0.0;
     461             :     ConstMSEdgeVector newRoute;
     462         206 :     while (roadPos2.first->isInternal() && roadPos2.first != roadPos1.first) {
     463          37 :         distance += roadPos2.second;
     464          37 :         roadPos2.first = roadPos2.first->getLogicalPredecessorLane();
     465          37 :         roadPos2.second = roadPos2.first->getLength();
     466             :     }
     467         338 :     MSNet::getInstance()->getRouterTT(0).compute(&roadPos1.first->getEdge(), &roadPos2.first->getEdge(), nullptr, SIMSTEP, newRoute, true);
     468         169 :     if (newRoute.empty()) {
     469             :         return libsumo::INVALID_DOUBLE_VALUE;
     470             :     }
     471         314 :     MSRoute route("", newRoute, false, nullptr, std::vector<SUMOVehicleParameter::Stop>());
     472         157 :     return distance + route.getDistanceBetween(roadPos1.second, roadPos2.second, &roadPos1.first->getEdge(), &roadPos2.first->getEdge());
     473         157 : }
     474             : 
     475             : 
     476             : MSBaseVehicle*
     477    19293768 : Helper::getVehicle(const std::string& id) {
     478    19293768 :     SUMOVehicle* sumoVehicle = MSNet::getInstance()->getVehicleControl().getVehicle(id);
     479    19293768 :     if (sumoVehicle == nullptr) {
     480         212 :         throw TraCIException("Vehicle '" + id + "' is not known.");
     481             :     }
     482    19293662 :     MSBaseVehicle* v = dynamic_cast<MSBaseVehicle*>(sumoVehicle);
     483    19293662 :     if (v == nullptr) {
     484           0 :         throw TraCIException("Vehicle '" + id + "' is not a proper vehicle.");
     485             :     }
     486    19293662 :     return v;
     487             : }
     488             : 
     489             : 
     490             : MSPerson*
     491      206702 : Helper::getPerson(const std::string& personID) {
     492      206702 :     MSTransportableControl& c = MSNet::getInstance()->getPersonControl();
     493      206702 :     MSPerson* p = dynamic_cast<MSPerson*>(c.get(personID));
     494      206411 :     if (p == nullptr) {
     495         582 :         throw TraCIException("Person '" + personID + "' is not known");
     496             :     }
     497      206411 :     return p;
     498             : }
     499             : 
     500             : SUMOTrafficObject*
     501        2275 : Helper::getTrafficObject(int domain, const std::string& id) {
     502        2275 :     if (domain == CMD_GET_VEHICLE_VARIABLE) {
     503        2240 :         return getVehicle(id);
     504          35 :     } else if (domain == CMD_GET_PERSON_VARIABLE) {
     505          35 :         return getPerson(id);
     506             :     } else {
     507           0 :         throw TraCIException("Cannot retrieve traffic object for domain " + toString(domain));
     508             :     }
     509             : }
     510             : 
     511             : const MSVehicleType&
     512       29442 : Helper::getVehicleType(const std::string& vehicleID) {
     513       29442 :     return getVehicle(vehicleID)->getVehicleType();
     514             : }
     515             : 
     516             : 
     517             : MSTLLogicControl::TLSLogicVariants&
     518      174170 : Helper::getTLS(const std::string& id) {
     519      174170 :     if (!MSNet::getInstance()->getTLSControl().knows(id)) {
     520           6 :         throw TraCIException("Traffic light '" + id + "' is not known");
     521             :     }
     522      174167 :     return MSNet::getInstance()->getTLSControl().get(id);
     523             : }
     524             : 
     525             : 
     526             : MSStoppingPlace*
     527        1663 : Helper::getStoppingPlace(const std::string& id, const SumoXMLTag type) {
     528        1663 :     MSStoppingPlace* s = MSNet::getInstance()->getStoppingPlace(id, type);
     529        1663 :     if (s == nullptr) {
     530           0 :         throw TraCIException(toString(type) + " '" + id + "' is not known");
     531             :     }
     532        1663 :     return s;
     533             : }
     534             : 
     535             : 
     536             : SUMOVehicleParameter::Stop
     537       89016 : Helper::buildStopParameters(const std::string& edgeOrStoppingPlaceID,
     538             :                             double pos, int laneIndex, double startPos, int flags, double duration, double until) {
     539       89016 :     SUMOVehicleParameter::Stop newStop;
     540       89022 :     newStop.duration = duration == INVALID_DOUBLE_VALUE ? SUMOTime_MAX : TIME2STEPS(duration);
     541       89039 :     newStop.until = until == INVALID_DOUBLE_VALUE ? -1 : TIME2STEPS(until);
     542       89016 :     newStop.index = STOP_INDEX_FIT;
     543       89016 :     if (newStop.duration >= 0) {
     544       89010 :         newStop.parametersSet |= STOP_DURATION_SET;
     545             :     }
     546       89016 :     if (newStop.until >= 0) {
     547           6 :         newStop.parametersSet |= STOP_UNTIL_SET;
     548             :     }
     549       89016 :     if ((flags & 1) != 0) {
     550          62 :         newStop.parking = ParkingType::OFFROAD;
     551          62 :         newStop.parametersSet |= STOP_PARKING_SET;
     552             :     }
     553       89016 :     if ((flags & 2) != 0) {
     554           0 :         newStop.triggered = true;
     555           0 :         newStop.parametersSet |= STOP_TRIGGER_SET;
     556             :     }
     557       89016 :     if ((flags & 4) != 0) {
     558           0 :         newStop.containerTriggered = true;
     559           0 :         newStop.parametersSet |= STOP_CONTAINER_TRIGGER_SET;
     560             :     }
     561             : 
     562       89016 :     SumoXMLTag stoppingPlaceType = SUMO_TAG_NOTHING;
     563       89016 :     if ((flags & 8) != 0) {
     564         330 :         stoppingPlaceType = SUMO_TAG_BUS_STOP;
     565             :     }
     566       89016 :     if ((flags & 16) != 0) {
     567           0 :         stoppingPlaceType = SUMO_TAG_CONTAINER_STOP;
     568             :     }
     569       89016 :     if ((flags & 32) != 0) {
     570           0 :         stoppingPlaceType = SUMO_TAG_CHARGING_STATION;
     571             :     }
     572       89016 :     if ((flags & 64) != 0) {
     573          10 :         stoppingPlaceType = SUMO_TAG_PARKING_AREA;
     574             :     }
     575       89016 :     if ((flags & 128) != 0) {
     576           0 :         stoppingPlaceType = SUMO_TAG_OVERHEAD_WIRE_SEGMENT;
     577             :     }
     578             : 
     579       89016 :     if (stoppingPlaceType != SUMO_TAG_NOTHING) {
     580         340 :         MSStoppingPlace* bs = MSNet::getInstance()->getStoppingPlace(edgeOrStoppingPlaceID, stoppingPlaceType);
     581         340 :         if (bs == nullptr) {
     582           0 :             throw TraCIException("The " + toString(stoppingPlaceType) + " '" + edgeOrStoppingPlaceID + "' is not known");
     583             :         }
     584         340 :         newStop.lane = bs->getLane().getID();
     585         340 :         newStop.edge = bs->getLane().getEdge().getID();
     586         340 :         newStop.endPos = bs->getEndLanePosition();
     587         340 :         newStop.startPos = bs->getBeginLanePosition();
     588         340 :         switch (stoppingPlaceType) {
     589         330 :             case SUMO_TAG_BUS_STOP:
     590         330 :                 newStop.busstop = edgeOrStoppingPlaceID;
     591             :                 break;
     592           0 :             case SUMO_TAG_CONTAINER_STOP:
     593           0 :                 newStop.containerstop = edgeOrStoppingPlaceID;
     594             :                 break;
     595           0 :             case SUMO_TAG_CHARGING_STATION:
     596           0 :                 newStop.chargingStation = edgeOrStoppingPlaceID;
     597             :                 break;
     598          10 :             case SUMO_TAG_PARKING_AREA:
     599          10 :                 newStop.parkingarea = edgeOrStoppingPlaceID;
     600             :                 break;
     601           0 :             case SUMO_TAG_OVERHEAD_WIRE_SEGMENT:
     602           0 :                 newStop.overheadWireSegment = edgeOrStoppingPlaceID;
     603             :                 break;
     604           0 :             default:
     605           0 :                 throw TraCIException("Unknown stopping place type '" + toString(stoppingPlaceType) + "'.");
     606             :         }
     607             :     } else {
     608       88676 :         if (startPos == INVALID_DOUBLE_VALUE) {
     609       88652 :             startPos = MAX2(0.0, pos - POSITION_EPS);
     610             :         }
     611       88676 :         if (startPos < 0.) {
     612           0 :             throw TraCIException("Position on lane must not be negative.");
     613             :         }
     614       88676 :         if (pos < startPos) {
     615           0 :             throw TraCIException("End position on lane must be after start position.");
     616             :         }
     617             :         // get the actual lane that is referenced by laneIndex
     618       88676 :         MSEdge* road = MSEdge::dictionary(edgeOrStoppingPlaceID);
     619       88676 :         if (road == nullptr) {
     620          10 :             throw TraCIException("Edge '" + edgeOrStoppingPlaceID + "' is not known.");
     621             :         }
     622             :         const std::vector<MSLane*>& allLanes = road->getLanes();
     623       88671 :         if ((laneIndex < 0) || laneIndex >= (int)(allLanes.size())) {
     624           0 :             throw TraCIException("No lane with index '" + toString(laneIndex) + "' on edge '" + edgeOrStoppingPlaceID + "'.");
     625             :         }
     626       88671 :         newStop.lane = allLanes[laneIndex]->getID();
     627       88671 :         newStop.edge = allLanes[laneIndex]->getEdge().getID();
     628       88671 :         newStop.endPos = pos;
     629       88671 :         newStop.startPos = startPos;
     630       88671 :         newStop.parametersSet |= STOP_START_SET | STOP_END_SET;
     631             :     }
     632       89011 :     return newStop;
     633           5 : }
     634             : 
     635             : 
     636             : TraCINextStopData
     637       10802 : Helper::buildStopData(const SUMOVehicleParameter::Stop& stopPar) {
     638       10802 :     std::string stoppingPlaceID = "";
     639       10802 :     if (stopPar.busstop != "") {
     640             :         stoppingPlaceID = stopPar.busstop;
     641             :     }
     642       10802 :     if (stopPar.containerstop != "") {
     643             :         stoppingPlaceID = stopPar.containerstop;
     644             :     }
     645       10802 :     if (stopPar.parkingarea != "") {
     646             :         stoppingPlaceID = stopPar.parkingarea;
     647             :     }
     648       10802 :     if (stopPar.chargingStation != "") {
     649             :         stoppingPlaceID = stopPar.chargingStation;
     650             :     }
     651       10802 :     if (stopPar.overheadWireSegment != "") {
     652             :         stoppingPlaceID = stopPar.overheadWireSegment;
     653             :     }
     654             : 
     655       10802 :     return TraCINextStopData(stopPar.lane,
     656       10802 :                              stopPar.startPos,
     657       10802 :                              stopPar.endPos,
     658             :                              stoppingPlaceID,
     659             :                              stopPar.getFlags(),
     660             :                              // negative duration is permitted to indicate that a vehicle cannot
     661             :                              // re-enter traffic after parking
     662       20989 :                              stopPar.duration != -1 ? STEPS2TIME(stopPar.duration) : INVALID_DOUBLE_VALUE,
     663       16943 :                              stopPar.until >= 0 ? STEPS2TIME(stopPar.until) : INVALID_DOUBLE_VALUE,
     664       14078 :                              stopPar.arrival >= 0 ? STEPS2TIME(stopPar.arrival) : INVALID_DOUBLE_VALUE,
     665       13336 :                              stopPar.started >= 0 ? STEPS2TIME(stopPar.started) : INVALID_DOUBLE_VALUE,
     666       11797 :                              stopPar.ended >= 0 ? STEPS2TIME(stopPar.ended) : INVALID_DOUBLE_VALUE,
     667       10802 :                              stopPar.split,
     668       10802 :                              stopPar.join,
     669       10802 :                              stopPar.actType,
     670       10802 :                              stopPar.tripId,
     671       10802 :                              stopPar.line,
     672       10802 :                              stopPar.speed);
     673             : }
     674             : 
     675             : 
     676             : void
     677       35156 : Helper::cleanup() {
     678             :     // clean up NamedRTrees
     679       35156 :     InductionLoop::cleanup();
     680       35156 :     Junction::cleanup();
     681       35156 :     LaneArea::cleanup();
     682       35156 :     POI::cleanup();
     683       35156 :     Polygon::cleanup();
     684       35156 :     Helper::clearStateChanges();
     685       35156 :     Helper::clearSubscriptions();
     686       35156 :     delete myLaneTree;
     687       35156 :     myLaneTree = nullptr;
     688       35156 : }
     689             : 
     690             : 
     691             : void
     692        1022 : Helper::registerStateListener() {
     693        1022 :     if (MSNet::hasInstance()) {
     694        1022 :         MSNet::getInstance()->addVehicleStateListener(&myVehicleStateListener);
     695        1022 :         MSNet::getInstance()->addTransportableStateListener(&myTransportableStateListener);
     696             :     }
     697        1022 : }
     698             : 
     699             : 
     700             : const std::vector<std::string>&
     701      140985 : Helper::getVehicleStateChanges(const MSNet::VehicleState state) {
     702      140985 :     return myVehicleStateListener.myVehicleStateChanges[state];
     703             : }
     704             : 
     705             : 
     706             : const std::vector<std::string>&
     707          24 : Helper::getTransportableStateChanges(const MSNet::TransportableState state) {
     708          24 :     return myTransportableStateListener.myTransportableStateChanges[state];
     709             : }
     710             : 
     711             : 
     712             : void
     713      191837 : Helper::clearStateChanges() {
     714      734783 :     for (auto& i : myVehicleStateListener.myVehicleStateChanges) {
     715             :         i.second.clear();
     716             :     }
     717      253661 :     for (auto& i : myTransportableStateListener.myTransportableStateChanges) {
     718             :         i.second.clear();
     719             :     }
     720      191837 : }
     721             : 
     722             : 
     723             : MSCalibrator::AspiredState
     724          90 : Helper::getCalibratorState(const MSCalibrator* c) {
     725             :     try {
     726          90 :         return c->getCurrentStateInterval();
     727           0 :     } catch (ProcessError& e) {
     728           0 :         throw TraCIException(e.what());
     729           0 :     }
     730             : }
     731             : 
     732             : 
     733             : void
     734      145455 : Helper::findObjectShape(int domain, const std::string& id, PositionVector& shape) {
     735      145455 :     switch (domain) {
     736         185 :         case libsumo::CMD_SUBSCRIBE_BUSSTOP_CONTEXT: {
     737         185 :             MSStoppingPlace* const stop = getStoppingPlace(id, SUMO_TAG_BUS_STOP);
     738         185 :             shape.push_back(stop->getLane().getShape().positionAtOffset(stop->getBeginLanePosition()));
     739         185 :             shape.push_back(stop->getLane().getShape().positionAtOffset(stop->getEndLanePosition()));
     740         185 :             break;
     741             :         }
     742         185 :         case libsumo::CMD_SUBSCRIBE_CALIBRATOR_CONTEXT: {
     743         185 :             MSCalibrator* const calib = Calibrator::getCalibrator(id);
     744         185 :             shape.push_back(calib->getLane()->getShape()[0]);
     745         185 :             break;
     746             :         }
     747         185 :         case libsumo::CMD_SUBSCRIBE_CHARGINGSTATION_CONTEXT: {
     748         185 :             MSStoppingPlace* const stop = getStoppingPlace(id, SUMO_TAG_CHARGING_STATION);
     749         185 :             shape.push_back(stop->getLane().getShape().positionAtOffset(stop->getBeginLanePosition()));
     750         185 :             shape.push_back(stop->getLane().getShape().positionAtOffset(stop->getEndLanePosition()));
     751         185 :             break;
     752             :         }
     753       20227 :         case libsumo::CMD_SUBSCRIBE_EDGE_CONTEXT:
     754       20227 :             Edge::storeShape(id, shape);
     755       20227 :             break;
     756         185 :         case libsumo::CMD_SUBSCRIBE_INDUCTIONLOOP_CONTEXT:
     757         185 :             InductionLoop::storeShape(id, shape);
     758         185 :             break;
     759       20215 :         case libsumo::CMD_SUBSCRIBE_JUNCTION_CONTEXT:
     760       20215 :             Junction::storeShape(id, shape);
     761       20215 :             break;
     762       20256 :         case libsumo::CMD_SUBSCRIBE_LANE_CONTEXT:
     763       20256 :             Lane::storeShape(id, shape);
     764       20256 :             break;
     765         185 :         case libsumo::CMD_SUBSCRIBE_LANEAREA_CONTEXT:
     766         185 :             LaneArea::storeShape(id, shape);
     767         185 :             break;
     768         185 :         case libsumo::CMD_SUBSCRIBE_MULTIENTRYEXIT_CONTEXT: {
     769         185 :             MSE3Collector* const det = MultiEntryExit::getDetector(id);
     770         370 :             for (const MSCrossSection& cs : det->getEntries()) {
     771         370 :                 shape.push_back(cs.myLane->getShape().positionAtOffset(cs.myPosition));
     772             :             }
     773         370 :             for (const MSCrossSection& cs : det->getExits()) {
     774         370 :                 shape.push_back(cs.myLane->getShape().positionAtOffset(cs.myPosition));
     775             :             }
     776             :             break;
     777             :         }
     778         185 :         case libsumo::CMD_SUBSCRIBE_PARKINGAREA_CONTEXT: {
     779         185 :             MSStoppingPlace* const stop = getStoppingPlace(id, SUMO_TAG_PARKING_AREA);
     780         185 :             shape.push_back(stop->getLane().getShape().positionAtOffset(stop->getBeginLanePosition()));
     781         185 :             shape.push_back(stop->getLane().getShape().positionAtOffset(stop->getEndLanePosition()));
     782         185 :             break;
     783             :         }
     784       20219 :         case libsumo::CMD_SUBSCRIBE_PERSON_CONTEXT:
     785       20219 :             Person::storeShape(id, shape);
     786       20219 :             break;
     787       20371 :         case libsumo::CMD_SUBSCRIBE_POI_CONTEXT:
     788       20371 :             POI::storeShape(id, shape);
     789       20371 :             break;
     790       20257 :         case libsumo::CMD_SUBSCRIBE_POLYGON_CONTEXT:
     791       20257 :             Polygon::storeShape(id, shape);
     792       20257 :             break;
     793       21199 :         case libsumo::CMD_SUBSCRIBE_VEHICLE_CONTEXT:
     794       21199 :             Vehicle::storeShape(id, shape);
     795       21199 :             break;
     796        1416 :         default:
     797        1416 :             Simulation::storeShape(shape);
     798        1416 :             break;
     799             :     }
     800      145455 : }
     801             : 
     802             : 
     803             : void
     804      145455 : Helper::collectObjectIDsInRange(int domain, const PositionVector& shape, double range, std::set<std::string>& into) {
     805             :     std::set<const Named*> objects;
     806      145455 :     collectObjectsInRange(domain, shape, range, objects);
     807      610529 :     for (const Named* obj : objects) {
     808             :         into.insert(obj->getID());
     809             :     }
     810      144289 : }
     811             : 
     812             : 
     813             : void
     814      155024 : Helper::collectObjectsInRange(int domain, const PositionVector& shape, double range, std::set<const Named*>& into) {
     815      155024 :     const Boundary b = shape.getBoxBoundary().grow(range);
     816      155024 :     const float cmin[2] = {(float) b.xmin(), (float) b.ymin()};
     817      155024 :     const float cmax[2] = {(float) b.xmax(), (float) b.ymax()};
     818      155024 :     switch (domain) {
     819         212 :         case libsumo::CMD_GET_BUSSTOP_VARIABLE:
     820         848 :             for (const auto& stop : MSNet::getInstance()->getStoppingPlaces(SUMO_TAG_BUS_STOP)) {
     821         636 :                 if (shape.distance2D(stop.second->getCenterPos()) <= range) {
     822         284 :                     into.insert(stop.second);
     823             :                 }
     824             :             }
     825             :             break;
     826         212 :         case libsumo::CMD_GET_CHARGINGSTATION_VARIABLE:
     827         848 :             for (const auto& stop : MSNet::getInstance()->getStoppingPlaces(SUMO_TAG_CHARGING_STATION)) {
     828         636 :                 if (shape.distance2D(stop.second->getCenterPos()) <= range) {
     829         282 :                     into.insert(stop.second);
     830             :                 }
     831             :             }
     832             :             break;
     833             :         case libsumo::CMD_GET_CALIBRATOR_VARIABLE:
     834         636 :             for (const auto& calib : MSCalibrator::getInstances()) {
     835         424 :                 if (shape.distance2D(calib.second->getLane()->getShape()[0]) <= range) {
     836        1390 :                     into.insert(calib.second);
     837             :                 }
     838             :             }
     839             :             break;
     840         212 :         case libsumo::CMD_GET_INDUCTIONLOOP_VARIABLE:
     841         212 :             InductionLoop::getTree()->Search(cmin, cmax, Named::StoringVisitor(into));
     842         212 :             break;
     843         212 :         case libsumo::CMD_GET_JUNCTION_VARIABLE:
     844         212 :             Junction::getTree()->Search(cmin, cmax, Named::StoringVisitor(into));
     845         212 :             break;
     846         212 :         case libsumo::CMD_GET_LANEAREA_VARIABLE:
     847         212 :             LaneArea::getTree()->Search(cmin, cmax, Named::StoringVisitor(into));
     848         212 :             break;
     849         212 :         case libsumo::CMD_GET_PARKINGAREA_VARIABLE: {
     850         848 :             for (const auto& stop : MSNet::getInstance()->getStoppingPlaces(SUMO_TAG_PARKING_AREA)) {
     851         636 :                 if (shape.distance2D(stop.second->getCenterPos()) <= range) {
     852         279 :                     into.insert(stop.second);
     853             :                 }
     854             :             }
     855             :             break;
     856             :         }
     857         254 :         case libsumo::CMD_GET_POI_VARIABLE:
     858         254 :             POI::getTree()->Search(cmin, cmax, Named::StoringVisitor(into));
     859         254 :             break;
     860         236 :         case libsumo::CMD_GET_POLYGON_VARIABLE:
     861         236 :             Polygon::getTree()->Search(cmin, cmax, Named::StoringVisitor(into));
     862         236 :             break;
     863      151884 :         case libsumo::CMD_GET_EDGE_VARIABLE:
     864             :         case libsumo::CMD_GET_LANE_VARIABLE:
     865             :         case libsumo::CMD_GET_PERSON_VARIABLE:
     866             :         case libsumo::CMD_GET_VEHICLE_VARIABLE: {
     867      151884 :             if (myLaneTree == nullptr) {
     868         634 :                 myLaneTree = new LANE_RTREE_QUAL(&MSLane::visit);
     869         634 :                 MSLane::fill(*myLaneTree);
     870             :             }
     871      151884 :             MSLane::StoringVisitor lsv(into, shape, range, domain);
     872      151884 :             myLaneTree->Search(cmin, cmax, lsv);
     873             :         }
     874      151884 :         break;
     875        1166 :         default:
     876        2332 :             throw TraCIException("Infeasible context domain (" + toString(domain) + ")");
     877             :             break;
     878             :     }
     879      155024 : }
     880             : 
     881             : 
     882             : 
     883             : void
     884      148119 : Helper::applySubscriptionFilters(const Subscription& s, std::set<std::string>& objIDs) {
     885             : #ifdef DEBUG_SURROUNDING
     886             :     MSBaseVehicle* _veh = getVehicle(s.id);
     887             :     std::cout << SIMTIME << " applySubscriptionFilters for vehicle '" << _veh->getID() << "' on lane '" << _veh->getLane()->getID() << "'"
     888             :               << "\n       on edge '" << _veh->getLane()->getEdge().getID() << "' (" << toString(_veh->getLane()->getEdge().getLanes()) << ")\n"
     889             :               << "objIDs = " << toString(objIDs) << std::endl;
     890             : #endif
     891             : 
     892      148119 :     if (s.activeFilters == 0) {
     893             :         // No filters set
     894      143839 :         return;
     895             :     }
     896             : 
     897        4285 :     MSVehicle* v = dynamic_cast<MSVehicle*>(getVehicle(s.id));
     898             : 
     899             :     // Whether vehicles on opposite lanes shall be taken into account
     900        4285 :     const bool disregardOppositeDirection = (s.activeFilters & SUBS_FILTER_NOOPPOSITE) != 0;
     901             : 
     902             :     // Check filter specification consistency
     903        4285 :     if (disregardOppositeDirection && (s.activeFilters & SUBS_FILTER_NO_RTREE) == 0) {
     904           0 :         WRITE_WARNINGF(TL("Ignoring veh '%' no-opposite subscription filter for geographic range object collection. Consider using the 'lanes' filter."), v->getID())
     905             :     }
     906             :     // TODO: Treat case, where ego vehicle is currently on opposite lane
     907             : 
     908             :     std::set<const SUMOTrafficObject*> vehs;
     909        4285 :     if (s.activeFilters & SUBS_FILTER_NO_RTREE) {
     910             :         // Set defaults for upstream/downstream/lateral distances
     911        4250 :         double downstreamDist = s.range, upstreamDist = s.range, lateralDist = s.range;
     912        4250 :         if (s.activeFilters & SUBS_FILTER_DOWNSTREAM_DIST) {
     913             :             // Specifies maximal downstream distance for vehicles in context subscription result
     914        3440 :             downstreamDist = s.filterDownstreamDist;
     915             :         }
     916        4250 :         if (s.activeFilters & SUBS_FILTER_UPSTREAM_DIST) {
     917             :             // Specifies maximal downstream distance for vehicles in context subscription result
     918        2895 :             upstreamDist = s.filterUpstreamDist;
     919             :         }
     920        4250 :         if (s.activeFilters & SUBS_FILTER_LATERAL_DIST) {
     921             :             // Specifies maximal lateral distance for vehicles in context subscription result
     922         425 :             lateralDist = s.filterLateralDist;
     923             :         }
     924        4250 :         if (v == nullptr) {
     925           0 :             throw TraCIException("Subscription filter not yet implemented for meso vehicle");
     926             :         }
     927        4250 :         if (!v->isOnRoad()) {
     928           5 :             return;
     929             :         }
     930        4245 :         const MSLane* vehLane = v->getLane();
     931        4245 :         if (vehLane == nullptr) {
     932             :             return;
     933             :         }
     934             :         MSEdge* vehEdge = &vehLane->getEdge();
     935             :         std::vector<int> filterLanes;
     936        4245 :         if ((s.activeFilters & SUBS_FILTER_LANES) == 0) {
     937             :             // No lane indices are specified (but downstream and/or upstream distance)
     938             :             //   -> use only vehicle's current lane as origin for the searches
     939        1930 :             filterLanes = {0};
     940             :             // Lane indices must be specified when leader/follower information is requested.
     941             :             assert((s.activeFilters & SUBS_FILTER_LEAD_FOLLOW) == 0);
     942             :         } else {
     943        3280 :             filterLanes = s.filterLanes;
     944             :         }
     945             : 
     946             : #ifdef DEBUG_SURROUNDING
     947             :         std::cout << "Filter lanes: " << toString(filterLanes) << std::endl;
     948             :         std::cout << "Downstream distance: " << downstreamDist << std::endl;
     949             :         std::cout << "Upstream distance: " << upstreamDist << std::endl;
     950             :         std::cout << "Lateral distance: " << lateralDist << std::endl;
     951             : #endif
     952             : 
     953        4245 :         if ((s.activeFilters & SUBS_FILTER_MANEUVER) != 0) {
     954             :             // Maneuver filters disables road net search for all surrounding vehicles
     955        1805 :             if ((s.activeFilters & SUBS_FILTER_LEAD_FOLLOW) != 0) {
     956             :                 // Return leader and follower on the specified lanes in context subscription result.
     957        2160 :                 for (int offset : filterLanes) {
     958        1440 :                     MSLane* lane = v->getLane()->getParallelLane(offset, false);
     959        1440 :                     if (lane != nullptr) {
     960             :                         // this is a non-opposite lane
     961        1285 :                         MSVehicle* leader = lane->getLeader(v, v->getPositionOnLane(), v->getBestLanesContinuation(lane), downstreamDist).first;
     962        1285 :                         MSVehicle* follower = lane->getFollower(v, v->getPositionOnLane(), upstreamDist,
     963        1285 :                                                                 MSLane::MinorLinkMode::FOLLOW_ALWAYS).first;
     964        1285 :                         vehs.insert(vehs.end(), leader);
     965        1285 :                         vehs.insert(vehs.end(), follower);
     966             : 
     967             : #ifdef DEBUG_SURROUNDING
     968             :                         std::cout << "Lane at index " << offset << ": '" << lane->getID() << std::endl;
     969             :                         std::cout << "Leader: '" << (leader != nullptr ? leader->getID() : "NULL") << "'" << std::endl;
     970             :                         std::cout << "Follower: '" << (follower != nullptr ? follower->getID() : "NULL") << "'" << std::endl;
     971             : #endif
     972             : 
     973         155 :                     } else if (!disregardOppositeDirection && offset > 0) { // TODO: offset<0 may indicate opposite query when vehicle is on opposite itself
     974             :                         // check whether ix points to an opposite lane
     975         155 :                         const MSEdge* opposite = vehEdge->getOppositeEdge();
     976         155 :                         if (opposite == nullptr) {
     977             : #ifdef DEBUG_SURROUNDING
     978             :                             std::cout << "No lane at index " << offset << std::endl;
     979             : #endif
     980             :                             // no opposite edge
     981           0 :                             continue;
     982             :                         }
     983             :                         // Index of opposite lane at relative offset
     984         155 :                         const int ix_opposite = (int)opposite->getLanes().size() - 1 - (vehLane->getIndex() + offset - (int)vehEdge->getLanes().size());
     985         155 :                         if (ix_opposite < 0) {
     986             : #ifdef DEBUG_SURROUNDING
     987             :                             std::cout << "No lane on opposite at index " << ix_opposite << std::endl;
     988             : #endif
     989             :                             // no opposite edge
     990           0 :                             continue;
     991             :                         }
     992         155 :                         lane = opposite->getLanes()[ix_opposite];
     993             :                         // Search vehs along opposite lanes (swap upstream and downstream distance)
     994             :                         // XXX transformations for curved geometries
     995         155 :                         double posOnOpposite = MAX2(0., opposite->getLength() - v->getPositionOnLane());
     996             :                         // Get leader on opposite
     997         155 :                         vehs.insert(vehs.end(), lane->getFollower(v, posOnOpposite, downstreamDist, MSLane::MinorLinkMode::FOLLOW_NEVER).first);
     998             :                         // Get follower (no search on consecutive lanes
     999         155 :                         vehs.insert(vehs.end(), lane->getLeader(v, posOnOpposite - v->getLength(), std::vector<MSLane*>()).first);
    1000             :                     }
    1001             :                 }
    1002             :             }
    1003             : 
    1004        1805 :             if (s.activeFilters & SUBS_FILTER_TURN) {
    1005        1090 :                 applySubscriptionFilterTurn(s, vehs);
    1006        1090 :                 if (s.activeFilters & SUBS_FILTER_LANES) {
    1007         275 :                     applySubscriptionFilterLanes(s, vehs, filterLanes, downstreamDist, upstreamDist, disregardOppositeDirection);
    1008             :                 }
    1009        1090 :                 if (s.activeFilters & SUBS_FILTER_LATERAL_DIST) {
    1010         270 :                     applySubscriptionFilterLateralDistance(s, vehs, downstreamDist, upstreamDist, lateralDist);
    1011             :                 }
    1012             :             }
    1013             : #ifdef DEBUG_SURROUNDING
    1014             :             std::cout << SIMTIME << " applySubscriptionFilters() for veh '" << v->getID() << "'. Found the following vehicles:\n";
    1015             :             for (auto veh : vehs) {
    1016             :                 debugPrint(veh);
    1017             :             }
    1018             : #endif
    1019        2440 :         } else if (s.activeFilters & SUBS_FILTER_LATERAL_DIST) {
    1020         150 :             applySubscriptionFilterLateralDistance(s, vehs, downstreamDist, upstreamDist, lateralDist);
    1021             :         } else {
    1022             :             // No maneuver or lateral distance filters requested, but only lanes filter (directly, or indirectly by specifying downstream or upstream distance)
    1023        2290 :             applySubscriptionFilterLanes(s, vehs, filterLanes, downstreamDist, upstreamDist, disregardOppositeDirection);
    1024             :         }
    1025             :         // Write vehs IDs in objIDs
    1026       15709 :         for (const SUMOTrafficObject* veh : vehs) {
    1027       11464 :             if (veh != nullptr) {
    1028             :                 objIDs.insert(objIDs.end(), veh->getID());
    1029             :             }
    1030             :         }
    1031             :     }
    1032             : 
    1033        4280 :     if (s.activeFilters & SUBS_FILTER_VCLASS) {
    1034             :         // Only return vehicles of the given vClass in context subscription result
    1035             :         auto i = objIDs.begin();
    1036         749 :         while (i != objIDs.end()) {
    1037         584 :             MSBaseVehicle* veh = getVehicle(*i);
    1038         584 :             if ((veh->getVehicleType().getVehicleClass() & s.filterVClasses) == 0) {
    1039             :                 i = objIDs.erase(i);
    1040             :             } else {
    1041             :                 ++i;
    1042             :             }
    1043             :         }
    1044             :     }
    1045        4280 :     if (s.activeFilters & SUBS_FILTER_VTYPE) {
    1046             :         // Only return vehicles of the given vType in context subscription result
    1047             :         auto i = objIDs.begin();
    1048         749 :         while (i != objIDs.end()) {
    1049         584 :             MSBaseVehicle* veh = getVehicle(*i);
    1050         584 :             if (s.filterVTypes.find(veh->getVehicleType().getID()) == s.filterVTypes.end()) {
    1051             :                 i = objIDs.erase(i);
    1052             :             } else {
    1053             :                 ++i;
    1054             :             }
    1055             :         }
    1056             :     }
    1057        4280 :     if (s.activeFilters & SUBS_FILTER_FIELD_OF_VISION) {
    1058             :         // Only return vehicles within field of vision in context subscription result
    1059          45 :         applySubscriptionFilterFieldOfVision(s, objIDs);
    1060             :     }
    1061             : }
    1062             : 
    1063             : void
    1064        2565 : Helper::applySubscriptionFilterLanes(const Subscription& s, std::set<const SUMOTrafficObject*>& vehs, std::vector<int>& filterLanes, double downstreamDist,
    1065             :                                      double upstreamDist, bool disregardOppositeDirection) {
    1066             :     if (!s.isVehicleToVehicleContextSubscription()) {
    1067           0 :         WRITE_WARNINGF(TL("Lanes filter is only feasible for context domain 'vehicle' (current is '%'), ignoring filter..."), toHex(s.contextDomain, 2));
    1068           0 :         return;
    1069             :     }
    1070             :     assert(filterLanes.size() > 0);
    1071        2565 :     MSVehicle* v = dynamic_cast<MSVehicle*>(getVehicle(s.id));
    1072        2565 :     const MSLane* vehLane = v->getLane();
    1073             :     MSEdge* vehEdge = &vehLane->getEdge();
    1074             :     // This is to remember the lanes checked in the driving direction of the vehicle (their opposites can be added in a second pass)
    1075             :     auto checkedLanesInDrivingDir = std::make_shared<LaneCoverageInfo>();
    1076        7675 :     for (int offset : filterLanes) {
    1077        5110 :         MSLane* lane = vehLane->getParallelLane(offset, false);
    1078        5110 :         if (lane != nullptr) {
    1079             : #ifdef DEBUG_SURROUNDING
    1080             :             std::cout << "Checking for surrounding vehicles starting on lane '" << lane->getID() << "' at index " << offset << std::endl;
    1081             : #endif
    1082             :             // Search vehs along this lane
    1083             :             // (Coverage info is collected per origin lane since lanes reached from neighboring lanes may have different distances
    1084             :             // and aborting at previously scanned when coming from a closer origin may prevent scanning of parts that should be included.)
    1085             :             std::shared_ptr<LaneCoverageInfo> checkedLanes = std::make_shared<LaneCoverageInfo>();
    1086             :             const std::set<MSVehicle*> new_vehs =
    1087        3320 :                 lane->getSurroundingVehicles(v->getPositionOnLane(), downstreamDist, upstreamDist + v->getLength(), checkedLanes);
    1088        1660 :             vehs.insert(new_vehs.begin(), new_vehs.end());
    1089        4980 :             fuseLaneCoverage(checkedLanesInDrivingDir, checkedLanes);
    1090        3450 :         } else if (!disregardOppositeDirection && offset > 0) {
    1091             :             // Check opposite edge, too
    1092             :             assert(vehLane->getIndex() + offset >= (int)vehEdge->getLanes().size());  // index points beyond this edge
    1093        2505 :             const MSEdge* opposite = vehEdge->getOppositeEdge();
    1094        2505 :             if (opposite == nullptr) {
    1095             : #ifdef DEBUG_SURROUNDING
    1096             :                 std::cout << "No opposite edge, thus no lane at index " << offset << std::endl;
    1097             : #endif
    1098             :                 // no opposite edge
    1099         450 :                 continue;
    1100             :             }
    1101             :             // Index of opposite lane at relative offset
    1102        2505 :             const int ix_opposite = (int)opposite->getLanes().size() - 1 - (vehLane->getIndex() + offset - (int)vehEdge->getLanes().size());
    1103        2505 :             if (ix_opposite < 0) {
    1104             : #ifdef DEBUG_SURROUNDING
    1105             :                 std::cout << "No lane on opposite at index " << ix_opposite << std::endl;
    1106             : #endif
    1107             :                 // no opposite edge
    1108         450 :                 continue;
    1109             :             }
    1110        2055 :             lane = opposite->getLanes()[ix_opposite];
    1111             :             // Search vehs along opposite lanes (swap upstream and downstream distance)
    1112        2055 :             const std::set<MSVehicle*> new_vehs = lane->getSurroundingVehicles(lane->getLength() - v->getPositionOnLane(), upstreamDist + v->getLength(),
    1113        4110 :                                                   downstreamDist, std::make_shared<LaneCoverageInfo>());
    1114        2055 :             vehs.insert(new_vehs.begin(), new_vehs.end());
    1115             :         }
    1116             : #ifdef DEBUG_SURROUNDING
    1117             :         else {
    1118             :             std::cout << "No lane at index " << offset << std::endl;
    1119             :         }
    1120             : #endif
    1121             : 
    1122        4660 :         if (!disregardOppositeDirection) {
    1123             :             // If opposite should be checked, do this for each lane of the search tree in checkedLanesInDrivingDir
    1124             :             // (For instance, some opposite lanes of these would not be obtained if the ego lane does not have an opposite.)
    1125             : 
    1126             :             // Number of opposite lanes to be checked (assumes filterLanes.size()>0, see assertion above) determined as hypothetical offset
    1127             :             // overlap into opposing edge from the vehicle's current lane.
    1128             :             // TODO: offset<0 may indicate opposite query when vehicle is on opposite itself (-> use min_element(filterLanes...) instead, etc)
    1129        4175 :             const int nOpp = MAX2(0, (*std::max_element(filterLanes.begin(), filterLanes.end())) - ((int)vehEdge->getLanes().size() - 1 - vehLane->getIndex()));
    1130             :             // Collect vehicles from opposite lanes
    1131        4175 :             if (nOpp > 0) {
    1132        8506 :                 for (auto& laneCov : *checkedLanesInDrivingDir) {
    1133        5086 :                     const MSLane* const l = laneCov.first;
    1134        5086 :                     if (l == nullptr || l->getEdge().getOppositeEdge() == nullptr) {
    1135             :                         continue;
    1136             :                     }
    1137        3618 :                     const MSEdge* opposite = l->getEdge().getOppositeEdge();
    1138             :                     const std::pair<double, double>& range = laneCov.second;
    1139             :                     auto leftMostOppositeLaneIt = opposite->getLanes().rbegin();
    1140        7236 :                     for (auto oppositeLaneIt = leftMostOppositeLaneIt; oppositeLaneIt != opposite->getLanes().rend(); ++oppositeLaneIt) {
    1141        5798 :                         if ((int)(oppositeLaneIt - leftMostOppositeLaneIt) == nOpp) {
    1142             :                             break;
    1143             :                         }
    1144             :                         // Add vehicles from corresponding range on opposite direction
    1145        3618 :                         const MSLane* oppositeLane = *oppositeLaneIt;
    1146        3618 :                         auto new_vehs = oppositeLane->getVehiclesInRange(l->getLength() - range.second, l->getLength() - range.first);
    1147        3618 :                         vehs.insert(new_vehs.begin(), new_vehs.end());
    1148             :                     }
    1149             :                 }
    1150             :             }
    1151             :         }
    1152             : #ifdef DEBUG_SURROUNDING
    1153             :         std::cout << SIMTIME << " applySubscriptionFilterLanes() for veh '" << v->getID() << "', lane offset '" << offset << "'. Found the following vehicles so far:\n";
    1154             :         for (auto veh : vehs) {
    1155             :             debugPrint(veh);
    1156             :         }
    1157             : #endif
    1158             :     }
    1159             : }
    1160             : 
    1161             : void
    1162        1090 : Helper::applySubscriptionFilterTurn(const Subscription& s, std::set<const SUMOTrafficObject*>& vehs) {
    1163             :     if (!s.isVehicleToVehicleContextSubscription()) {
    1164         280 :         WRITE_WARNINGF(TL("Turn filter is only feasible for context domain 'vehicle' (current is '%'), ignoring filter..."), toHex(s.contextDomain, 2));
    1165         140 :         return;
    1166             :     }
    1167             :     // Get upcoming junctions and vialanes within downstream distance, where foe links exist or at least the link direction is not straight
    1168         950 :     MSVehicle* v = dynamic_cast<MSVehicle*>(getVehicle(s.id));
    1169         950 :     const MSLane* lane = v->getLane();
    1170         950 :     std::vector<const MSLink*> links = lane->getUpcomingLinks(v->getPositionOnLane(), s.filterDownstreamDist, v->getBestLanesContinuation());
    1171             : #ifdef DEBUG_SURROUNDING
    1172             :     std::cout << "Applying turn filter for vehicle '" << v->getID() << "'\n Gathering foes ..." << std::endl;
    1173             : #endif
    1174             :     // Iterate through junctions and find approaching foes within foeDistToJunction.
    1175        1710 :     for (auto& l : links) {
    1176             : #ifdef DEBUG_SURROUNDING
    1177             :         std::cout << "  On junction '" << l->getJunction()->getID() << "' (no. foe links = " << l->getFoeLinks().size() << "):" << std::endl;
    1178             : #endif
    1179        5740 :         for (auto& foeLane : l->getFoeLanes()) {
    1180        4980 :             if (foeLane->getEdge().isCrossing()) {
    1181             : #ifdef DEBUG_SURROUNDING
    1182             :                 std::cout << "   skipping crossing foeLane '" << foeLane->getID() << "'" << std::endl;
    1183             : #endif
    1184         150 :                 continue;
    1185             :             }
    1186             : #ifdef DEBUG_SURROUNDING
    1187             :             std::cout << "   foeLane '" << foeLane->getID() << "'" << std::endl;
    1188             : #endif
    1189             :             // Check vehicles approaching the entry link corresponding to this lane
    1190        4830 :             const MSLink* foeLink = foeLane->getEntryLink();
    1191        6320 :             for (auto& vi : foeLink->getApproaching()) {
    1192        1490 :                 if (vi.second.dist <= s.filterFoeDistToJunction) {
    1193             : #ifdef DEBUG_SURROUNDING
    1194             :                     std::cout << "    Approaching foeLane entry link '" << vi.first->getID() << "'" << std::endl;
    1195             : #endif
    1196        1135 :                     vehs.insert(vehs.end(), dynamic_cast<const MSVehicle*>(vi.first));
    1197             :                 }
    1198             :             }
    1199             :             // add vehicles currently on the junction
    1200        4850 :             for (const MSVehicle* foe : foeLane->getVehiclesSecure()) {
    1201             : #ifdef DEBUG_SURROUNDING
    1202             :                 std::cout << "    On foeLane '" << foe->getID() << "'" << std::endl;
    1203             : #endif
    1204          20 :                 vehs.insert(vehs.end(), foe);
    1205             :             }
    1206        4830 :             foeLane->releaseVehicles();
    1207        9660 :             for (auto& laneInfo : foeLane->getIncomingLanes()) {
    1208        4830 :                 const MSLane* foeLanePred = laneInfo.lane;
    1209        4830 :                 if (foeLanePred->isInternal()) {
    1210             : #ifdef DEBUG_SURROUNDING
    1211             :                     std::cout << "     foeLanePred '" << foeLanePred->getID() << "'" << std::endl;
    1212             : #endif
    1213        1135 :                     for (const MSVehicle* foe : foeLanePred->getVehiclesSecure()) {
    1214             : #ifdef DEBUG_SURROUNDING
    1215             :                         std::cout << "      On foeLanePred '" << foe->getID() << "'" << std::endl;
    1216             : #endif
    1217         155 :                         vehs.insert(vehs.end(), foe);
    1218             :                     }
    1219         980 :                     foeLanePred->releaseVehicles();
    1220             :                 }
    1221             :             }
    1222             :         }
    1223             :     }
    1224             : }
    1225             : 
    1226             : void
    1227          45 : Helper::applySubscriptionFilterFieldOfVision(const Subscription& s, std::set<std::string>& objIDs) {
    1228          45 :     if (s.filterFieldOfVisionOpeningAngle <= 0. || s.filterFieldOfVisionOpeningAngle >= 360.) {
    1229          10 :         WRITE_WARNINGF(TL("Field of vision opening angle ('%') should be within interval (0, 360), ignoring filter..."), s.filterFieldOfVisionOpeningAngle);
    1230          10 :         return;
    1231             :     }
    1232             : 
    1233          35 :     MSBaseVehicle* egoVehicle = getVehicle(s.id);
    1234          35 :     Position egoPosition = egoVehicle->getPosition();
    1235          35 :     double openingAngle = DEG2RAD(s.filterFieldOfVisionOpeningAngle);
    1236             : 
    1237             : #ifdef DEBUG_SURROUNDING
    1238             :     std::cout << "FOVFILTER: ego direction = " << toString(RAD2DEG(egoVehicle->getAngle())) << " (deg)" << std::endl;
    1239             : #endif
    1240             : 
    1241             :     auto i = objIDs.begin();
    1242         134 :     while (i != objIDs.end()) {
    1243          99 :         if (s.id.compare(*i) == 0) { // skip if this is the ego vehicle
    1244             :             ++i;
    1245          29 :             continue;
    1246             :         }
    1247          70 :         SUMOTrafficObject* obj = getTrafficObject(s.contextDomain, *i);
    1248          70 :         double angleEgoToVeh = egoPosition.angleTo2D(obj->getPosition());
    1249          70 :         double alpha = GeomHelper::angleDiff(egoVehicle->getAngle(), angleEgoToVeh);
    1250             : 
    1251             : #ifdef DEBUG_SURROUNDING
    1252             :         const std::string objType = s.isVehicleToPersonContextSubscription() ? "person" : "veh";
    1253             :         std::cout << "FOVFILTER: " << objType << " '" << *i << "' dist  = " << toString(egoPosition.distanceTo2D(obj->getPosition())) << std::endl;
    1254             :         std::cout << "FOVFILTER: " << objType << " '" << *i << "' alpha = " << toString(RAD2DEG(alpha)) << " (deg)" << std::endl;
    1255             : #endif
    1256             : 
    1257          70 :         if (abs(alpha) > openingAngle * 0.5) {
    1258             :             i = objIDs.erase(i);
    1259             :         } else {
    1260             :             ++i;
    1261             :         }
    1262             :     }
    1263             : }
    1264             : 
    1265             : void
    1266         420 : Helper::applySubscriptionFilterLateralDistance(const Subscription& s, std::set<const SUMOTrafficObject*>& vehs, double downstreamDist, double upstreamDist,
    1267             :         double lateralDist) {
    1268             :     // collect all vehicles within maximum range of interest to get an upper bound
    1269         420 :     PositionVector vehShape;
    1270         420 :     findObjectShape(s.commandId, s.id, vehShape);
    1271             :     double range = MAX3(downstreamDist, upstreamDist, lateralDist);
    1272             :     std::set<std::string> objIDs;
    1273         420 :     collectObjectIDsInRange(s.contextDomain, vehShape, range, objIDs);
    1274             : 
    1275             : #ifdef DEBUG_SURROUNDING
    1276             :     std::cout << "FILTER_LATERAL_DIST: collected object IDs (range " << range << "):" << std::endl;
    1277             :     for (std::string i : objIDs) {
    1278             :         std::cout << i << std::endl;
    1279             :     }
    1280             : #endif
    1281             : 
    1282         420 :     MSVehicle* v = dynamic_cast<MSVehicle*>(getVehicle(s.id));
    1283             : #ifdef DEBUG_SURROUNDING
    1284             :     std::cout << "FILTER_LATERAL_DIST: myLane is '" << v->getLane()->getID() << "', pos " << v->getPositionOnLane() << std::endl;
    1285             :     std::cout << "FILTER_LATERAL_DIST: opposite lane is '" << v->getLane()->getParallelOpposite()->getID() << "'" << std::endl;
    1286             : #endif
    1287         420 :     double frontPosOnLane = v->getPositionOnLane();
    1288         420 :     if (v->getLaneChangeModel().isOpposite()) {
    1289          30 :         frontPosOnLane = v->getLane()->getOppositePos(frontPosOnLane);
    1290             :     }
    1291             :     // 1st pass: downstream (make sure that the whole length of the vehicle is included in the match)
    1292         420 :     const double backPosOnLane = MAX2(0.0, frontPosOnLane - v->getVehicleType().getLength());
    1293         420 :     applySubscriptionFilterLateralDistanceSinglePass(s, objIDs, vehs, v->getUpcomingLanesUntil(downstreamDist), backPosOnLane, v->getLateralPositionOnLane(),
    1294             :             true);
    1295             :     // 2nd pass: upstream
    1296         840 :     applySubscriptionFilterLateralDistanceSinglePass(s, objIDs, vehs, v->getPastLanesUntil(upstreamDist), frontPosOnLane, v->getLateralPositionOnLane(), false);
    1297         420 : }
    1298             : 
    1299             : void
    1300         840 : Helper::applySubscriptionFilterLateralDistanceSinglePass(const Subscription& s, std::set<std::string>& objIDs,
    1301             :         std::set<const SUMOTrafficObject*>& vehs,
    1302             :         const std::vector<const MSLane*>& lanes, double posOnLane, double posLat, bool isDownstream) {
    1303         840 :     const double streamDist = isDownstream ? s.filterDownstreamDist : s.filterUpstreamDist;
    1304             :     double distRemaining = streamDist;
    1305             :     bool isFirstLane = true;
    1306         840 :     PositionVector combinedShape;
    1307        1565 :     for (const MSLane* lane : lanes) {
    1308             : #ifdef DEBUG_SURROUNDING
    1309             :         std::cout << "FILTER_LATERAL_DIST: current lane " << (isDownstream ? "down" : "up") << " is '" << lane->getID() << "', length " << lane->getLength()
    1310             :                   << ", pos " << posOnLane << ", distRemaining " << distRemaining << std::endl;
    1311             : #endif
    1312             :         PositionVector laneShape = lane->getShape();
    1313        1355 :         if (isFirstLane) {
    1314             :             isFirstLane = false;
    1315             :             double geometryPos = lane->interpolateLanePosToGeometryPos(posOnLane);
    1316         705 :             if (geometryPos <= POSITION_EPS) {
    1317          50 :                 if (!isDownstream) {
    1318             :                     continue;
    1319             :                 }
    1320             :             } else {
    1321         655 :                 if (geometryPos >= laneShape.length() - POSITION_EPS) {
    1322           0 :                     laneShape = isDownstream ? PositionVector() : laneShape;
    1323             :                 } else {
    1324         655 :                     auto pair = laneShape.splitAt(geometryPos, false);
    1325         655 :                     laneShape = isDownstream ? pair.second : pair.first;
    1326             :                 }
    1327             :             }
    1328             :         }
    1329        1355 :         double laneLength = lane->interpolateGeometryPosToLanePos(laneShape.length());
    1330        1355 :         if (distRemaining - laneLength < 0.) {
    1331         610 :             double geometryPos = lane->interpolateLanePosToGeometryPos(isDownstream ? distRemaining : laneLength - distRemaining);
    1332         610 :             if (geometryPos > POSITION_EPS && geometryPos < laneShape.length() - POSITION_EPS) {
    1333         610 :                 auto pair = laneShape.splitAt(geometryPos, false);
    1334         610 :                 laneShape = isDownstream ? pair.first : pair.second;
    1335             :             }
    1336             :         }
    1337             :         distRemaining -= laneLength;
    1338             :         try {
    1339        1355 :             laneShape.move2side(-posLat);
    1340           0 :         } catch (ProcessError&) {
    1341           0 :             WRITE_WARNINGF(TL("addSubscriptionFilterLateralDistance could not determine shape of lane '%' with a lateral shift of %."),
    1342             :                            lane->getID(), toString(posLat));
    1343           0 :         }
    1344             : #ifdef DEBUG_SURROUNDING
    1345             :         std::cout << "   posLat=" << posLat << " laneShape=" << laneShape << "\n";
    1346             : #endif
    1347        1355 :         if (isDownstream) {
    1348         885 :             combinedShape.append(laneShape);
    1349             :         } else {
    1350         470 :             combinedShape.prepend(laneShape);
    1351             :         }
    1352        1355 :         if (distRemaining <= POSITION_EPS) {
    1353             :             break;
    1354             :         }
    1355        1355 :     }
    1356             : #ifdef DEBUG_SURROUNDING
    1357             :     std::cout << " combinedShape=" << combinedShape << "\n";
    1358             : #endif
    1359             :     // check remaining objects' distances to the combined shape
    1360             :     auto i = objIDs.begin();
    1361        3045 :     while (i != objIDs.end()) {
    1362        2205 :         SUMOTrafficObject* obj = getTrafficObject(s.contextDomain, *i);
    1363        2205 :         double minPerpendicularDist = combinedShape.distance2D(obj->getPosition(), true);
    1364             : #ifdef DEBUG_SURROUNDING
    1365             :         std::cout << (isDownstream ? "DOWN" : "UP") << " obj " << obj->getID() << " perpendicular dist=" << minPerpendicularDist << " filterLateralDist=" << s.filterLateralDist << "\n";
    1366             : #endif
    1367        2205 :         if ((minPerpendicularDist != GeomHelper::INVALID_OFFSET) && (minPerpendicularDist <= s.filterLateralDist)) {
    1368             :             vehs.insert(obj);
    1369             :             i = objIDs.erase(i);
    1370             :         } else {
    1371             :             ++i;
    1372             :         }
    1373             :     }
    1374         840 : }
    1375             : 
    1376             : void
    1377        8769 : Helper::setRemoteControlled(MSVehicle* v, Position xyPos, MSLane* l, double pos, double posLat, double angle,
    1378             :                             int edgeOffset, ConstMSEdgeVector route, SUMOTime t) {
    1379        8769 :     myRemoteControlledVehicles[v->getID()] = v;
    1380        8769 :     v->getInfluencer().setRemoteControlled(xyPos, l, pos, posLat, angle, edgeOffset, route, t);
    1381        8769 : }
    1382             : 
    1383             : void
    1384       16339 : Helper::setRemoteControlled(MSPerson* p, Position xyPos, MSLane* l, double pos, double posLat, double angle,
    1385             :                             int edgeOffset, ConstMSEdgeVector route, SUMOTime t) {
    1386       16339 :     myRemoteControlledPersons[p->getID()] = p;
    1387       16339 :     p->getInfluencer().setRemoteControlled(xyPos, l, pos, posLat, angle, edgeOffset, route, t);
    1388       16339 : }
    1389             : 
    1390             : 
    1391             : int
    1392    74944773 : Helper::postProcessRemoteControl() {
    1393             :     int numControlled = 0;
    1394    74953512 :     for (auto& controlled : myRemoteControlledVehicles) {
    1395        8739 :         if (MSNet::getInstance()->getVehicleControl().getVehicle(controlled.first) != nullptr) {
    1396        8739 :             controlled.second->getInfluencer().postProcessRemoteControl(controlled.second);
    1397        8739 :             numControlled++;
    1398             :         } else {
    1399           0 :             WRITE_WARNINGF(TL("Vehicle '%' was removed though being controlled by TraCI"), controlled.first);
    1400             :         }
    1401             :     }
    1402             :     myRemoteControlledVehicles.clear();
    1403    74961112 :     for (auto& controlled : myRemoteControlledPersons) {
    1404       16339 :         if (MSNet::getInstance()->getPersonControl().get(controlled.first) != nullptr) {
    1405       16339 :             controlled.second->getInfluencer().postProcessRemoteControl(controlled.second);
    1406       16339 :             numControlled++;
    1407             :         } else {
    1408           0 :             WRITE_WARNINGF(TL("Person '%' was removed though being controlled by TraCI"), controlled.first);
    1409             :         }
    1410             :     }
    1411             :     myRemoteControlledPersons.clear();
    1412    74944773 :     return numControlled;
    1413             : }
    1414             : 
    1415             : 
    1416             : bool
    1417        5632 : Helper::moveToXYMap(const Position& pos, double maxRouteDistance, bool mayLeaveNetwork, const std::string& origID, const double angle,
    1418             :                     double speed, const ConstMSEdgeVector& currentRoute, const int routePosition, const MSLane* currentLane, double currentLanePos, bool onRoad,
    1419             :                     SUMOVehicleClass vClass, bool setLateralPos,
    1420             :                     double& bestDistance, MSLane** lane, double& lanePos, int& routeOffset, ConstMSEdgeVector& edges) {
    1421             :     // collect edges around the vehicle/person
    1422             : #ifdef DEBUG_MOVEXY
    1423             :     std::cout << SIMTIME << " moveToXYMap pos=" << pos << " angle=" << angle << " vClass=" << toString(vClass) << "\n";
    1424             : #endif
    1425        5632 :     const MSEdge* const currentRouteEdge = currentRoute[routePosition];
    1426             :     std::set<const Named*> into;
    1427        5632 :     PositionVector shape;
    1428        5632 :     shape.push_back(pos);
    1429        5632 :     collectObjectsInRange(libsumo::CMD_GET_EDGE_VARIABLE, shape, maxRouteDistance, into);
    1430             :     double maxDist = 0;
    1431             :     std::map<MSLane*, LaneUtility, ComparatorNumericalIdLess> lane2utility;
    1432             :     // compute utility for all candidate edges
    1433      234148 :     for (const Named* namedEdge : into) {
    1434      228516 :         const MSEdge* e = dynamic_cast<const MSEdge*>(namedEdge);
    1435      228516 :         if ((e->getPermissions() & vClass) != vClass) {
    1436       19876 :             continue;
    1437             :         }
    1438      208640 :         const MSEdge* prevEdge = nullptr;
    1439             :         const MSEdge* nextEdge = nullptr;
    1440             :         bool onRoute = false;
    1441             :         // the next if/the clause sets "onRoute", "prevEdge", and "nextEdge", depending on
    1442             :         //  whether the currently seen edge is an internal one or a normal one
    1443      208640 :         if (e->isWalkingArea() || e->isCrossing()) {
    1444             :             // find current intersection along the route
    1445             :             const MSJunction* junction = e->getFromJunction();
    1446       16856 :             for (int i = routePosition; i < (int)currentRoute.size(); i++) {
    1447       11943 :                 const MSEdge* cand = currentRoute[i];
    1448       11943 :                 if (cand->getToJunction() == junction) {
    1449        5709 :                     prevEdge = cand;
    1450        5709 :                     if (i + 1 < (int)currentRoute.size()) {
    1451             :                         onRoute = true;
    1452        4521 :                         nextEdge = currentRoute[i + 1];
    1453             :                     }
    1454             :                     break;
    1455             :                 }
    1456             :             }
    1457       10622 :             if (!onRoute) {
    1458             :                 // search backward
    1459        6983 :                 for (int i = routePosition - 1; i >= 0; i--) {
    1460        4482 :                     const MSEdge* cand = currentRoute[i];
    1461        4482 :                     if (cand->getToJunction() == junction) {
    1462             :                         onRoute = true;
    1463        3600 :                         prevEdge = cand;
    1464        3600 :                         nextEdge = currentRoute[i + 1];
    1465        3600 :                         break;
    1466             :                     }
    1467             :                 }
    1468             :             }
    1469       10622 :             if (prevEdge == nullptr) {
    1470             :                 // use arbitrary predecessor
    1471        1313 :                 if (e->getPredecessors().size() > 0) {
    1472        1313 :                     prevEdge = e->getPredecessors().front();
    1473           0 :                 } else if (e->getSuccessors().size() > 1) {
    1474           0 :                     for (MSEdge* e2 : e->getSuccessors()) {
    1475           0 :                         if (e2 != nextEdge) {
    1476           0 :                             prevEdge = e2;
    1477           0 :                             break;
    1478             :                         }
    1479             :                     }
    1480             :                 }
    1481             :             }
    1482       10622 :             if (nextEdge == nullptr) {
    1483        2501 :                 if (e->getSuccessors().size() > 0) {
    1484        2435 :                     nextEdge = e->getSuccessors().front();
    1485          66 :                 } else if (e->getPredecessors().size() > 1) {
    1486          66 :                     for (MSEdge* e2 : e->getPredecessors()) {
    1487          66 :                         if (e2 != prevEdge) {
    1488             :                             nextEdge = e2;
    1489             :                             break;
    1490             :                         }
    1491             :                     }
    1492             :                 }
    1493             :             }
    1494             : #ifdef DEBUG_MOVEXY_ANGLE
    1495             :             std::cout << "walkingarea/crossing:" << e->getID() << " prev:" << Named::getIDSecure(prevEdge) << " next:" << Named::getIDSecure(nextEdge)
    1496             :                       << " pred=" << toString(e->getPredecessors()) << " succ=" << toString(e->getSuccessors())
    1497             :                       << "\n";
    1498             : #endif
    1499      198018 :         } else if (e->isNormal()) {
    1500             :             // a normal edge
    1501             :             //
    1502             :             // check whether the currently seen edge is in the vehicle's route
    1503             :             //  - either the one it's on or one of the next edges
    1504             :             ConstMSEdgeVector::const_iterator searchStart = currentRoute.begin() + routePosition;
    1505       51332 :             if (onRoad && currentLane->getEdge().isInternal()) {
    1506             :                 ++searchStart;
    1507             :             }
    1508       51332 :             ConstMSEdgeVector::const_iterator edgePos = std::find(searchStart, currentRoute.end(), e);
    1509             :             onRoute = edgePos != currentRoute.end(); // no? -> onRoute is false
    1510       51332 :             if (edgePos == currentRoute.end() - 1 && currentRouteEdge == e) {
    1511             :                 // onRoute is false as well if the vehicle is beyond the edge
    1512        2223 :                 onRoute &= currentRouteEdge->getLength() > currentLanePos + SPEED2DIST(speed);
    1513             :             }
    1514             :             // save prior and next edges
    1515       51332 :             prevEdge = e;
    1516       51332 :             nextEdge = !onRoute || edgePos == currentRoute.end() - 1 ? nullptr : *(edgePos + 1);
    1517             : #ifdef DEBUG_MOVEXY_ANGLE
    1518             :             std::cout << "normal:" << e->getID() << " prev:" << Named::getIDSecure(prevEdge) << " next:" << Named::getIDSecure(nextEdge) << "\n";
    1519             : #endif
    1520      146686 :         } else if (e->isInternal()) {
    1521             :             // an internal edge
    1522             :             // get the previous edge
    1523      146686 :             prevEdge = e;
    1524      339646 :             while (prevEdge != nullptr && prevEdge->isInternal()) {
    1525      192960 :                 MSLane* l = prevEdge->getLanes()[0];
    1526      192960 :                 l = l->getLogicalPredecessorLane();
    1527      385920 :                 prevEdge = l == nullptr ? nullptr : &l->getEdge();
    1528             :             }
    1529             :             // check whether the previous edge is on the route (was on the route)
    1530      146686 :             ConstMSEdgeVector::const_iterator prevEdgePos = std::find(currentRoute.begin() + routePosition, currentRoute.end(), prevEdge);
    1531             :             nextEdge = e;
    1532      293372 :             while (nextEdge != nullptr && nextEdge->isInternal()) {
    1533      146686 :                 nextEdge = nextEdge->getSuccessors()[0]; // should be only one for an internal edge
    1534             :             }
    1535      146686 :             if (prevEdgePos != currentRoute.end() && (prevEdgePos + 1) != currentRoute.end()) {
    1536       17555 :                 onRoute = *(prevEdgePos + 1) == nextEdge;
    1537             :             }
    1538             : #ifdef DEBUG_MOVEXY_ANGLE
    1539             :             std::cout << "internal:" << e->getID() << " prev:" << Named::getIDSecure(prevEdge) << " next:" << Named::getIDSecure(nextEdge) << "\n";
    1540             : #endif
    1541             :         }
    1542             : 
    1543             : 
    1544             :         // weight the lanes...
    1545             :         const bool perpendicular = false;
    1546      479010 :         for (MSLane* const l : e->getLanes()) {
    1547      270370 :             if (!l->allowsVehicleClass(vClass)) {
    1548       28976 :                 continue;
    1549             :             }
    1550      241682 :             if (l->getShape().length() == 0) {
    1551             :                 // mapping to shapeless lanes is a bad idea
    1552         288 :                 continue;
    1553             :             }
    1554             :             double langle = 180.;
    1555             :             double dist = FAR_AWAY;
    1556             :             double perpendicularDist = FAR_AWAY;
    1557             :             // add some slack to avoid issues from tiny gaps between consecutive lanes
    1558             :             // except when simulating with high precision where the slack can throw of mapping
    1559      241394 :             const double slack = POSITION_EPS * TS;
    1560      241394 :             PositionVector laneShape = l->getShape();
    1561      241394 :             laneShape.extrapolate2D(slack);
    1562      241394 :             double off = laneShape.nearest_offset_to_point2D(pos, true);
    1563      241394 :             if (off != GeomHelper::INVALID_OFFSET) {
    1564       69314 :                 perpendicularDist = laneShape.distance2D(pos, true);
    1565             :             }
    1566      241394 :             off = l->getShape().nearest_offset_to_point2D(pos, perpendicular);
    1567      241394 :             if (off != GeomHelper::INVALID_OFFSET) {
    1568      241394 :                 dist = l->getShape().distance2D(pos, perpendicular);
    1569      241394 :                 langle = GeomHelper::naviDegree(l->getShape().rotationAtOffset(off));
    1570             :             }
    1571             :             // cannot trust lanePos on walkingArea
    1572      241394 :             bool sameEdge = onRoad && e == &currentLane->getEdge() && currentRouteEdge->getLength() > currentLanePos + SPEED2DIST(speed) && !e->isWalkingArea();
    1573             :             /*
    1574             :             const MSEdge* rNextEdge = nextEdge;
    1575             :             while(rNextEdge==0&&lane->getEdge().getPurpose()==MSEdge::EDGEFUNCTION_INTERNAL) {
    1576             :                 MSLane* next = lane->getLinkCont()[0]->getLane();
    1577             :                 rNextEdge = next == 0 ? 0 : &next->getEdge();
    1578             :             }
    1579             :             */
    1580             :             double dist2 = dist;
    1581      241394 :             if (mayLeaveNetwork && fabs(dist - perpendicularDist) > slack) {
    1582             :                 // ambiguous mapping. Don't trust this
    1583             :                 dist2 = FAR_AWAY;
    1584             :             }
    1585      241394 :             const double angleDiff = (angle == INVALID_DOUBLE_VALUE || l->getEdge().isWalkingArea() ? 0 : GeomHelper::getMinAngleDiff(angle, langle));
    1586             : #ifdef DEBUG_MOVEXY_ANGLE
    1587             :             std::cout << std::setprecision(gPrecision)
    1588             :                       << " candLane=" << l->getID() << " lAngle=" << langle << " lLength=" << l->getLength()
    1589             :                       << " angleDiff=" << angleDiff
    1590             :                       << " off=" << off
    1591             :                       << " pDist=" << perpendicularDist
    1592             :                       << " dist=" << dist
    1593             :                       << " dist2=" << dist2
    1594             :                       << "\n";
    1595             :             std::cout << l->getID() << " param=" << l->getParameter(SUMO_PARAM_ORIGID, "") << " origID='" << origID << "\n";
    1596             : #endif
    1597             : 
    1598      482788 :             bool origIDMatch = l->getParameter(SUMO_PARAM_ORIGID, l->getID()) == origID;
    1599      241394 :             if (origIDMatch && setLateralPos
    1600      241394 :                     && perpendicularDist > l->getWidth() / 2) {
    1601             :                 origIDMatch = false;
    1602             :             }
    1603      241394 :             lane2utility.emplace(l, LaneUtility(
    1604             :                                      dist2, perpendicularDist, off, angleDiff,
    1605             :                                      origIDMatch,
    1606             :                                      onRoute, sameEdge, prevEdge, nextEdge));
    1607             :             // update scaling value
    1608             :             maxDist = MAX2(maxDist, MIN2(dist, SUMO_const_laneWidth));
    1609             : 
    1610      241394 :         }
    1611             :     }
    1612             : 
    1613             :     // get the best lane given the previously computed values
    1614             :     double bestValue = 0;
    1615        5632 :     MSLane* bestLane = nullptr;
    1616      247026 :     for (const auto& it : lane2utility) {
    1617      241394 :         MSLane* const l = it.first;
    1618             :         const LaneUtility& u = it.second;
    1619      241394 :         double distN = u.dist > 999 ? -10 : 1. - (u.dist / maxDist);
    1620      241394 :         double angleDiffN = 1. - (u.angleDiff / 180.);
    1621      241394 :         double idN = u.ID ? 1 : 0;
    1622      241394 :         double onRouteN = u.onRoute ? 1 : 0;
    1623      248830 :         double sameEdgeN = u.sameEdge ? MIN2(currentRouteEdge->getLength() / MAX2(NUMERICAL_EPS, speed), (double)1.) : 0;
    1624             :         // distance is more important than angle because the vehicle might be driving in the opposite direction
    1625             :         // also, distance becomes increasingly more important with lower step lengths (because position errors from one step to the next can result in large acceleration/speed errors)
    1626      241394 :         double value = (distN * .5 / TS
    1627      241394 :                         + angleDiffN * 0.35 /*.5 */
    1628      241394 :                         + idN * 1
    1629      241394 :                         + onRouteN * 0.1
    1630      241394 :                         + sameEdgeN * 0.1);
    1631             : #ifdef DEBUG_MOVEXY
    1632             :         std::cout << " x; l:" << l->getID() << " d:" << u.dist << " dN:" << distN << " aD:" << angleDiffN <<
    1633             :                   " ID:" << idN << " oRN:" << onRouteN << " sEN:" << sameEdgeN << " value:" << value << std::endl;
    1634             : #endif
    1635      241394 :         if (value > bestValue || bestLane == nullptr) {
    1636             :             bestValue = value;
    1637       40288 :             if (u.dist == FAR_AWAY) {
    1638       13662 :                 bestLane = nullptr;
    1639             :             } else {
    1640       26626 :                 bestLane = l;
    1641             :             }
    1642             :         }
    1643             :     }
    1644             :     // no best lane found, return
    1645        5632 :     if (bestLane == nullptr) {
    1646             :         return false;
    1647             :     }
    1648             :     const LaneUtility& u = lane2utility.find(bestLane)->second;
    1649        5462 :     bestDistance = u.dist;
    1650        5462 :     *lane = bestLane;
    1651        5462 :     lanePos = MAX2(0., MIN2(double((*lane)->getLength() - NUMERICAL_EPS),
    1652             :                             bestLane->interpolateGeometryPosToLanePos(
    1653             :                                 bestLane->getShape().nearest_offset_to_point25D(pos, false))));
    1654        5462 :     const MSEdge* prevEdge = u.prevEdge;
    1655        5462 :     if (u.onRoute) {
    1656        5287 :         ConstMSEdgeVector::const_iterator prevEdgePos = std::find(currentRoute.begin(), currentRoute.end(), prevEdge);
    1657        5287 :         routeOffset = (int)std::distance(currentRoute.begin(), prevEdgePos);
    1658             :         //std::cout << SIMTIME << "moveToXYMap currLane=" << currentLane->getID() << " routeOffset=" << routeOffset << " edges=" << toString(edges) << " bestLane=" << bestLane->getID() << " prevEdge=" << prevEdge->getID() << "\n";
    1659             :     } else {
    1660         175 :         edges.push_back(u.prevEdge);
    1661             :         /*
    1662             :            if(bestLane->getEdge().getPurpose()!=MSEdge::EDGEFUNCTION_INTERNAL) {
    1663             :            edges.push_back(&bestLane->getEdge());
    1664             :            }
    1665             :         */
    1666         175 :         if (u.nextEdge != nullptr) {
    1667          55 :             edges.push_back(u.nextEdge);
    1668             :         }
    1669         175 :         routeOffset = 0;
    1670             : #ifdef DEBUG_MOVEXY_ANGLE
    1671             :         std::cout << SIMTIME << " internal2: lane=" << bestLane->getID() << " prev=" << Named::getIDSecure(u.prevEdge) << " next=" << Named::getIDSecure(u.nextEdge) << "\n";
    1672             : #endif
    1673             :     }
    1674             :     return true;
    1675        5632 : }
    1676             : 
    1677             : 
    1678             : bool
    1679      980533 : Helper::findCloserLane(const MSEdge* edge, const Position& pos, SUMOVehicleClass vClass, double& bestDistance, MSLane** lane) {
    1680             :     // TODO maybe there is a way to abort this early if the lane already found is good enough but simply
    1681             :     // checking for bestDistance < POSITON_EPS gives ugly order dependencies (#7933), so think twice and profile first
    1682      980533 :     if (edge == nullptr) {
    1683             :         return false;
    1684             :     }
    1685             :     bool newBest = false;
    1686     1291953 :     for (MSLane* const candidateLane : edge->getLanes()) {
    1687      655045 :         if (!candidateLane->allowsVehicleClass(vClass)) {
    1688        6033 :             continue;
    1689             :         }
    1690      649012 :         if (candidateLane->getShape().length() == 0) {
    1691             :             // mapping to shapeless lanes is a bad idea
    1692         472 :             continue;
    1693             :         }
    1694      648540 :         const double dist = candidateLane->getShape().distance2D(pos); // get distance
    1695             : #ifdef DEBUG_MOVEXY
    1696             :         std::cout << "   b at lane " << candidateLane->getID() << " dist:" << dist << " best:" << bestDistance << std::endl;
    1697             : #endif
    1698      648540 :         if (dist < bestDistance || (dist == bestDistance && candidateLane->getNumericalID() < (*lane)->getNumericalID())) {
    1699             :             // is the new distance the best one? keep then...
    1700       23476 :             bestDistance = dist;
    1701       23476 :             *lane = candidateLane;
    1702             :             newBest = true;
    1703             :         }
    1704             :     }
    1705      636908 :     if (edge->isInternal() && edge->getNumLanes() > 1) {
    1706             :         // there is a parallel internal edge that isn't returned by getInternalFollowingEdge but is also usable for the same route
    1707        8152 :         for (const MSLane* const l : edge->getLanes()) {
    1708        5435 :             if (l->getIndex() == 0) {
    1709        2717 :                 continue;
    1710             :             }
    1711        5436 :             for (const MSLink* const link : l->getLinkCont()) {
    1712        2718 :                 if (link->isInternalJunctionLink()) {
    1713          21 :                     if (findCloserLane(&link->getViaLane()->getEdge(), pos, vClass, bestDistance, lane)) {
    1714             :                         newBest = true;
    1715             :                     }
    1716             :                 }
    1717             :             }
    1718             :         }
    1719             :     }
    1720             :     return newBest;
    1721             : }
    1722             : 
    1723             : 
    1724             : bool
    1725       19490 : Helper::moveToXYMap_matchingRoutePosition(const Position& pos, const std::string& origID,
    1726             :         const ConstMSEdgeVector& currentRoute, int routeIndex,
    1727             :         SUMOVehicleClass vClass, bool setLateralPos,
    1728             :         double& bestDistance, MSLane** lane, double& lanePos, int& routeOffset) {
    1729             : #ifdef DEBUG_MOVEXY
    1730             :     std::cout << SIMTIME << " moveToXYMap_matchingRoutePosition pos=" << pos << " vClass=" << toString(vClass) << "\n";
    1731             : #endif
    1732             :     //std::cout << "moveToXYMap_matchingRoutePosition pos=" << pos << "\n";
    1733       19490 :     routeOffset = 0;
    1734             :     // routes may be looped which makes routeOffset ambiguous. We first try to
    1735             :     // find the closest upcoming edge on the route and then look for closer passed edges
    1736             : 
    1737             :     // look forward along the route
    1738             :     const MSEdge* prev = nullptr;
    1739      271763 :     for (int i = routeIndex; i < (int)currentRoute.size(); ++i) {
    1740      252273 :         const MSEdge* cand = currentRoute[i];
    1741      683325 :         while (prev != nullptr) {
    1742             :             // check internal edge(s)
    1743      431052 :             const MSEdge* internalCand = prev->getInternalFollowingEdge(cand, vClass);
    1744             : #ifdef DEBUG_MOVEXY
    1745             :             std::cout << SIMTIME << "    prev=" << Named::getIDSecure(prev) << " cand=" << Named::getIDSecure(cand) << " internal=" << Named::getIDSecure(internalCand) << "\n";
    1746             : #endif
    1747      431052 :             findCloserLane(internalCand, pos, vClass, bestDistance, lane);
    1748             :             prev = internalCand;
    1749             :         }
    1750      252273 :         if (findCloserLane(cand, pos, vClass, bestDistance, lane)) {
    1751       20761 :             routeOffset = i;
    1752             :         }
    1753             :         prev = cand;
    1754             :     }
    1755             :     // look backward along the route
    1756       19490 :     const MSEdge* next = currentRoute[routeIndex];
    1757      130332 :     for (int i = routeIndex; i >= 0; --i) {
    1758      110842 :         const MSEdge* cand = currentRoute[i];
    1759             :         //std::cout << "  next=" << next->getID() << " cand=" << cand->getID() << " i=" << i << " routeIndex=" << routeIndex << "\n";
    1760             :         prev = cand;
    1761      293287 :         while (prev != nullptr) {
    1762             :             // check internal edge(s)
    1763      182445 :             const MSEdge* internalCand = prev->getInternalFollowingEdge(next, vClass);
    1764      182445 :             if (findCloserLane(internalCand, pos, vClass, bestDistance, lane)) {
    1765          12 :                 routeOffset = i;
    1766             :             }
    1767             :             prev = internalCand;
    1768             :         }
    1769      110842 :         if (findCloserLane(cand, pos, vClass, bestDistance, lane)) {
    1770           6 :             routeOffset = i;
    1771             :         }
    1772             :         next = cand;
    1773             :     }
    1774       19490 :     if (vClass == SVC_PEDESTRIAN) {
    1775             :         // consider all crossings and walkingareas along the route
    1776             :         std::map<const MSJunction*, int> routeJunctions;
    1777        1043 :         for (int i = 0; i < (int)currentRoute.size() - 1; ++i) {
    1778         516 :             routeJunctions[currentRoute[i]->getToJunction()] = i;
    1779             :         }
    1780             :         std::set<const Named*> into;
    1781         527 :         PositionVector shape;
    1782         527 :         shape.push_back(pos);
    1783         527 :         collectObjectsInRange(libsumo::CMD_GET_LANE_VARIABLE, shape, 100, into);
    1784       45031 :         for (const Named* named : into) {
    1785       44504 :             const MSLane* cand = dynamic_cast<const MSLane*>(named);
    1786       41576 :             if ((cand->getEdge().isWalkingArea() || cand->getEdge().isCrossing())
    1787       44504 :                     && routeJunctions.count(cand->getEdge().getToJunction()) != 0) {
    1788        3900 :                 if (findCloserLane(&cand->getEdge(), pos, vClass, bestDistance, lane)) {
    1789         270 :                     routeOffset = routeJunctions[cand->getEdge().getToJunction()];
    1790             :                 }
    1791             :             }
    1792             :         }
    1793         527 :     }
    1794             : 
    1795             :     assert(lane != nullptr);
    1796             :     // quit if no solution was found, reporting a failure
    1797       19490 :     if (lane == nullptr) {
    1798             : #ifdef DEBUG_MOVEXY
    1799             :         std::cout << "  b failed - no best route lane" << std::endl;
    1800             : #endif
    1801             :         return false;
    1802             :     }
    1803             : 
    1804             : 
    1805             :     // position may be inaccurate; let's check the given index, too
    1806             :     // @note: this is enabled for non-internal lanes only, as otherwise the position information may ambiguous
    1807       19490 :     if (!(*lane)->getEdge().isInternal()) {
    1808             :         const std::vector<MSLane*>& lanes = (*lane)->getEdge().getLanes();
    1809       36819 :         for (std::vector<MSLane*>::const_iterator i = lanes.begin(); i != lanes.end(); ++i) {
    1810       21113 :             if ((*i)->getParameter(SUMO_PARAM_ORIGID, (*i)->getID()) == origID) {
    1811        2652 :                 if (setLateralPos) {
    1812             :                     // vehicle might end up on top of another lane with a big
    1813             :                     // lateral offset to the lane with origID.
    1814        2595 :                     const double dist = (*i)->getShape().distance2D(pos); // get distance
    1815        2595 :                     if (dist < (*i)->getWidth() / 2) {
    1816        2589 :                         *lane = *i;
    1817        2589 :                         break;
    1818             :                     }
    1819             :                 } else {
    1820          57 :                     *lane = *i;
    1821          57 :                     break;
    1822             :                 }
    1823             :             }
    1824             :         }
    1825             :     }
    1826             :     // check position, stuff, we should have the best lane along the route
    1827       19490 :     lanePos = MAX2(0., MIN2(double((*lane)->getLength() - NUMERICAL_EPS),
    1828             :                             (*lane)->interpolateGeometryPosToLanePos(
    1829             :                                 (*lane)->getShape().nearest_offset_to_point25D(pos, false))));
    1830             :     //std::cout << SIMTIME << " moveToXYMap_matchingRoutePosition vehicle=" << veh.getID() << " currLane=" << veh.getLane()->getID() << " routeOffset=" << routeOffset << " edges=" << toString(edges) << " lane=" << (*lane)->getID() << "\n";
    1831             : #ifdef DEBUG_MOVEXY
    1832             :     std::cout << "  b ok lane " << (*lane)->getID() << " lanePos:" << lanePos << std::endl;
    1833             : #endif
    1834       19490 :     return true;
    1835             : }
    1836             : 
    1837             : 
    1838        6408 : Helper::SubscriptionWrapper::SubscriptionWrapper(VariableWrapper::SubscriptionHandler handler, SubscriptionResults& into, ContextSubscriptionResults& context)
    1839        6408 :     : VariableWrapper(handler), myResults(into), myContextResults(context), myActiveResults(&into) {
    1840             : 
    1841        6408 : }
    1842             : 
    1843             : 
    1844             : void
    1845       93253 : Helper::SubscriptionWrapper::setContext(const std::string* const refID) {
    1846       93253 :     myActiveResults = refID == nullptr ? &myResults : &myContextResults[*refID];
    1847       93253 : }
    1848             : 
    1849             : 
    1850             : void
    1851     1098936 : Helper::SubscriptionWrapper::clear() {
    1852     1098936 :     myActiveResults = &myResults;
    1853             :     myResults.clear();
    1854     1098936 :     myContextResults.clear();
    1855     1098936 : }
    1856             : 
    1857             : 
    1858             : bool
    1859       32738 : Helper::SubscriptionWrapper::wrapDouble(const std::string& objID, const int variable, const double value) {
    1860       32738 :     (*myActiveResults)[objID][variable] = std::make_shared<TraCIDouble>(value);
    1861       32738 :     return true;
    1862             : }
    1863             : 
    1864             : 
    1865             : bool
    1866       13748 : Helper::SubscriptionWrapper::wrapInt(const std::string& objID, const int variable, const int value) {
    1867       13748 :     (*myActiveResults)[objID][variable] = std::make_shared<TraCIInt>(value);
    1868       13748 :     return true;
    1869             : }
    1870             : 
    1871             : 
    1872             : bool
    1873       27128 : Helper::SubscriptionWrapper::wrapString(const std::string& objID, const int variable, const std::string& value) {
    1874       27128 :     (*myActiveResults)[objID][variable] = std::make_shared<TraCIString>(value);
    1875       27128 :     return true;
    1876             : }
    1877             : 
    1878             : 
    1879             : bool
    1880         714 : Helper::SubscriptionWrapper::wrapStringList(const std::string& objID, const int variable, const std::vector<std::string>& value) {
    1881             :     auto sl = std::make_shared<TraCIStringList>();
    1882         714 :     sl->value = value;
    1883         714 :     (*myActiveResults)[objID][variable] = sl;
    1884         714 :     return true;
    1885             : }
    1886             : 
    1887             : 
    1888             : bool
    1889           0 : Helper::SubscriptionWrapper::wrapDoubleList(const std::string& objID, const int variable, const std::vector<double>& value) {
    1890             :     auto sl = std::make_shared<TraCIDoubleList>();
    1891           0 :     sl->value = value;
    1892           0 :     (*myActiveResults)[objID][variable] = sl;
    1893           0 :     return true;
    1894             : }
    1895             : 
    1896             : 
    1897             : bool
    1898      185665 : Helper::SubscriptionWrapper::wrapPosition(const std::string& objID, const int variable, const TraCIPosition& value) {
    1899      185665 :     (*myActiveResults)[objID][variable] = std::make_shared<TraCIPosition>(value);
    1900      185665 :     return true;
    1901             : }
    1902             : 
    1903             : 
    1904             : bool
    1905           0 : Helper::SubscriptionWrapper::wrapPositionVector(const std::string& objID, const int variable, const TraCIPositionVector& value) {
    1906           0 :     (*myActiveResults)[objID][variable] = std::make_shared<TraCIPositionVector>(value);
    1907           0 :     return true;
    1908             : }
    1909             : 
    1910             : 
    1911             : bool
    1912           0 : Helper::SubscriptionWrapper::wrapColor(const std::string& objID, const int variable, const TraCIColor& value) {
    1913           0 :     (*myActiveResults)[objID][variable] = std::make_shared<TraCIColor>(value);
    1914           0 :     return true;
    1915             : }
    1916             : 
    1917             : 
    1918             : bool
    1919        9076 : Helper::SubscriptionWrapper::wrapStringDoublePair(const std::string& objID, const int variable, const std::pair<std::string, double>& value) {
    1920        9076 :     (*myActiveResults)[objID][variable] = std::make_shared<TraCIRoadPosition>(value.first, value.second);
    1921        9076 :     return true;
    1922             : }
    1923             : 
    1924             : 
    1925             : bool
    1926          42 : Helper::SubscriptionWrapper::wrapStringPair(const std::string& objID, const int variable, const std::pair<std::string, std::string>& value) {
    1927             :     auto sl = std::make_shared<TraCIStringList>();
    1928          42 :     sl->value.push_back(value.first);
    1929          42 :     sl->value.push_back(value.second);
    1930          42 :     (*myActiveResults)[objID][variable] = sl;
    1931          42 :     return true;
    1932             : }
    1933             : 
    1934             : 
    1935             : void
    1936        4416 : Helper::SubscriptionWrapper::empty(const std::string& objID) {
    1937        4416 :     (*myActiveResults)[objID]; // initiate the empty map to track the objectID for TRACI_ID_LIST context subscriptions
    1938        4416 : }
    1939             : 
    1940             : 
    1941             : void
    1942       42936 : Helper::VehicleStateListener::vehicleStateChanged(const SUMOVehicle* const vehicle, MSNet::VehicleState to, const std::string& /*info*/) {
    1943       42936 :     myVehicleStateChanges[to].push_back(vehicle->getID());
    1944       42936 : }
    1945             : 
    1946             : 
    1947             : void
    1948         789 : Helper::TransportableStateListener::transportableStateChanged(const MSTransportable* const transportable, MSNet::TransportableState to, const std::string& /*info*/) {
    1949         789 :     myTransportableStateChanges[to].push_back(transportable->getID());
    1950         789 : }
    1951             : 
    1952             : 
    1953             : }
    1954             : 
    1955             : 
    1956             : /****************************************************************************/

Generated by: LCOV version 1.14