Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2013-2025 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
14 : /// @file MSDevice_FCDReplay.cpp
15 : /// @author Michael Behrisch
16 : /// @date 01.03.2024
17 : ///
18 : // A device which replays recorded floating car data
19 : /****************************************************************************/
20 : #include <config.h>
21 :
22 : #include <utils/common/StaticCommand.h>
23 : #include <utils/geom/Position.h>
24 : #include <utils/options/OptionsCont.h>
25 : #include <utils/xml/SUMOSAXReader.h>
26 : #include <utils/xml/XMLSubSys.h>
27 : #include <libsumo/Vehicle.h>
28 : #include <microsim/MSNet.h>
29 : #include <microsim/MSEdge.h>
30 : #include <microsim/MSLane.h>
31 : #include <microsim/MSRoute.h>
32 : #include <microsim/MSEventControl.h>
33 : #include <microsim/MSInsertionControl.h>
34 : #include <microsim/transportables/MSTransportableControl.h>
35 : #include <microsim/transportables/MSStageDriving.h>
36 : #include <microsim/transportables/MSStageWaiting.h>
37 : #include <microsim/transportables/MSStageWalking.h>
38 : #include "MSTransportableDevice_FCDReplay.h"
39 : #include "MSDevice_FCDReplay.h"
40 :
41 :
42 : // ===========================================================================
43 : // static member initializations
44 : // ===========================================================================
45 : MSDevice_FCDReplay::FCDHandler* MSDevice_FCDReplay::myHandler = nullptr;
46 : SUMOSAXReader* MSDevice_FCDReplay::myParser = nullptr;
47 :
48 :
49 : // ===========================================================================
50 : // method definitions
51 : // ===========================================================================
52 : // ---------------------------------------------------------------------------
53 : // static initialisation methods
54 : // ---------------------------------------------------------------------------
55 : void
56 39784 : MSDevice_FCDReplay::insertOptions(OptionsCont& oc) {
57 39784 : oc.addOptionSubTopic("FCD Replay Device");
58 79568 : insertDefaultAssignmentOptions("fcd-replay", "FCD Replay Device", oc);
59 :
60 39784 : oc.doRegister("device.fcd-replay.file", new Option_FileName());
61 79568 : oc.addDescription("device.fcd-replay.file", "FCD Replay Device", TL("FCD file to read"));
62 39784 : }
63 :
64 :
65 : void
66 5371063 : MSDevice_FCDReplay::buildVehicleDevices(SUMOVehicle& v, std::vector<MSVehicleDevice*>& into) {
67 5371063 : OptionsCont& oc = OptionsCont::getOptions();
68 10742126 : if (equippedByDefaultAssignmentOptions(oc, "fcd-replay", v, oc.isSet("device.fcd-replay.file"))) {
69 36 : MSDevice_FCDReplay* device = new MSDevice_FCDReplay(v, "fcdReplay_" + v.getID());
70 36 : into.push_back(device);
71 : }
72 5371063 : }
73 :
74 :
75 : void
76 37650 : MSDevice_FCDReplay::init() {
77 37650 : delete myHandler;
78 37650 : myHandler = nullptr;
79 37650 : const OptionsCont& oc = OptionsCont::getOptions();
80 75300 : if (oc.isSet("device.fcd-replay.file")) {
81 24 : const std::string& filename = oc.getString("device.fcd-replay.file");
82 24 : myHandler = new MSDevice_FCDReplay::FCDHandler(filename);
83 24 : myParser = XMLSubSys::getSAXReader(*myHandler);
84 48 : if (!myParser->parseFirst(filename)) {
85 0 : throw ProcessError(TLF("Can not read XML-file '%'.", filename));
86 : }
87 24 : const SUMOTime inc = parseNext(SIMSTEP);
88 24 : MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(new MoveVehicles(), SIMSTEP + DELTA_T);
89 24 : if (inc > 0) {
90 8 : MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(new StaticCommand<MSDevice_FCDReplay>(&MSDevice_FCDReplay::parseNext),
91 4 : SIMSTEP + inc);
92 : }
93 : }
94 37650 : }
95 :
96 :
97 : SUMOTime
98 28 : MSDevice_FCDReplay::parseNext(SUMOTime t) {
99 28 : SUMOTime inc = string2time(OptionsCont::getOptions().getString("route-steps"));
100 : // make sure that we have always at least inc time steps buffered, so at time 200 we will parse 400 to 600
101 28 : const SUMOTime start = myHandler->getTime();
102 56340 : while (myHandler->getTime() < t + 2 * inc) {
103 56336 : if (!myParser->parseNext()) {
104 : inc = 0;
105 : break;
106 : }
107 : }
108 28 : myHandler->updateTrafficObjects(start);
109 28 : return inc;
110 : }
111 :
112 :
113 : // ---------------------------------------------------------------------------
114 : // MSDevice_FCDReplay-methods
115 : // ---------------------------------------------------------------------------
116 36 : MSDevice_FCDReplay::MSDevice_FCDReplay(SUMOVehicle& holder, const std::string& id) :
117 36 : MSVehicleDevice(holder, id) {
118 36 : }
119 :
120 :
121 72 : MSDevice_FCDReplay::~MSDevice_FCDReplay() {
122 72 : }
123 :
124 :
125 : void
126 3304 : MSDevice_FCDReplay::move(SUMOTime currentTime) {
127 3304 : if (myTrajectory == nullptr) {
128 8 : auto it = myHandler->getTrajectories().find(myHolder.getID());
129 8 : if (it == myHandler->getTrajectories().end()) {
130 : return;
131 : } else {
132 8 : const Trajectory& t = it->second;
133 : setTrajectory(&t);
134 : }
135 3296 : } else if (myTrajectoryIndex == (int)myTrajectory->size()) {
136 : // removal happens via the usual MSVehicle::hasArrived mechanism
137 : // TODO we may need to set an arrivalPos
138 : return;
139 : }
140 3272 : MSVehicle* v = dynamic_cast<MSVehicle*>(&myHolder);
141 3272 : const TrajectoryEntry& te = myTrajectory->at(myTrajectoryIndex);
142 3272 : if (v == nullptr || te.time > currentTime) {
143 : return;
144 : }
145 3260 : if (te.edgeOrLane != "") {
146 5400 : const std::string& edgeID = SUMOXMLDefinitions::getEdgeIDFromLane(te.edgeOrLane);
147 2700 : const int laneIdx = SUMOXMLDefinitions::getIndexFromLane(te.edgeOrLane);
148 2700 : libsumo::Vehicle::moveToXY(myHolder.getID(), edgeID, laneIdx, te.pos.x(), te.pos.y(), te.angle, 7);
149 560 : } else if (te.pos.x() != libsumo::INVALID_DOUBLE_VALUE) {
150 0 : libsumo::Vehicle::moveToXY(myHolder.getID(), "", -1, te.pos.x(), te.pos.y(), te.angle, 0);
151 : }
152 3260 : v->getInfluencer().setSpeedMode(0);
153 3260 : libsumo::Vehicle::setSpeed(myHolder.getID(), te.speed);
154 : // libsumo::Vehicle::changeLane(myHolder.getID(), laneIdx, TS);
155 3260 : myTrajectoryIndex++;
156 : }
157 :
158 :
159 : SUMOTime
160 3980 : MSDevice_FCDReplay::MoveVehicles::execute(SUMOTime currentTime) {
161 3980 : MSVehicleControl& c = MSNet::getInstance()->getVehicleControl();
162 9884 : for (MSVehicleControl::constVehIt i = c.loadedVehBegin(); i != c.loadedVehEnd(); ++i) {
163 5904 : MSDevice_FCDReplay* device = static_cast<MSDevice_FCDReplay*>(i->second->getDevice(typeid(MSDevice_FCDReplay)));
164 5904 : if (device != nullptr && i->second->hasDeparted()) {
165 3304 : device->move(currentTime);
166 : }
167 : }
168 3980 : return DELTA_T;
169 : }
170 :
171 :
172 : // ---------------------------------------------------------------------------
173 : // MSDevice_FCDReplay::FCDHandler-methods
174 : // ---------------------------------------------------------------------------
175 24 : MSDevice_FCDReplay::FCDHandler::FCDHandler(const std::string& file) :
176 : SUMOSAXHandler(file),
177 : MapMatcher(false, false,
178 24 : OptionsCont::getOptions().getFloat("mapmatch.distance"),
179 96 : MsgHandler::getErrorInstance()) {}
180 :
181 :
182 : void
183 24712 : MSDevice_FCDReplay::FCDHandler::myStartElement(int element, const SUMOSAXAttributes& attrs) {
184 24712 : bool ok = true;
185 24712 : switch (element) {
186 4072 : case SUMO_TAG_TIMESTEP:
187 4072 : myTime = attrs.getSUMOTimeReporting(SUMO_ATTR_TIME, "", ok);
188 : myPositions.clear();
189 24688 : return;
190 20616 : case SUMO_TAG_VEHICLE:
191 : case SUMO_TAG_PERSON: {
192 20616 : if (myTime >= SIMSTEP) {
193 20616 : const bool isPerson = element == SUMO_TAG_PERSON;
194 20616 : const std::string id = attrs.getString(SUMO_ATTR_ID);
195 : const Position xy = Position(attrs.getOpt<double>(SUMO_ATTR_X, id.c_str(), ok, libsumo::INVALID_DOUBLE_VALUE),
196 20616 : attrs.getOpt<double>(SUMO_ATTR_Y, id.c_str(), ok, libsumo::INVALID_DOUBLE_VALUE));
197 20616 : const std::string type = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, "");
198 44604 : const std::string edgeOrLane = attrs.getOpt<std::string>(isPerson ? SUMO_ATTR_EDGE : SUMO_ATTR_LANE, id.c_str(), ok, "");
199 20616 : const double speed = attrs.getOpt<double>(SUMO_ATTR_SPEED, id.c_str(), ok, libsumo::INVALID_DOUBLE_VALUE);
200 20616 : const double pos = attrs.getOpt<double>(SUMO_ATTR_POSITION, id.c_str(), ok, libsumo::INVALID_DOUBLE_VALUE);
201 20616 : const double angle = attrs.getOpt<double>(SUMO_ATTR_ANGLE, id.c_str(), ok, libsumo::INVALID_DOUBLE_VALUE);
202 20616 : std::string vehicle = attrs.getOpt<std::string>(SUMO_ATTR_VEHICLE, id.c_str(), ok, "");
203 20616 : if (isPerson) {
204 17244 : if (vehicle == "") {
205 : const auto& veh = myPositions.find(xy);
206 17244 : if (veh != myPositions.end()) {
207 2788 : vehicle = veh->second;
208 : }
209 : }
210 : } else {
211 3372 : myPositions[xy] = id;
212 : }
213 20616 : myTrajectories[id].push_back({myTime, xy, edgeOrLane, pos, speed, angle});
214 41232 : const MSEdge* edge = MSEdge::dictionary(isPerson ? edgeOrLane : SUMOXMLDefinitions::getEdgeIDFromLane(edgeOrLane));
215 20616 : if (edge == nullptr && edgeOrLane != "") {
216 0 : WRITE_WARNINGF(isPerson ? TL("Unknown edge '%' in fcd replay file for person '%'.") : TL("Unknown lane '%' in fcd replay file for vehicle '%'."), edgeOrLane, id);
217 : }
218 : if (myRoutes.count(id) == 0) {
219 200 : if (edgeOrLane == "") {
220 8 : const MSLane* const lane = getClosestLane(xy, SVC_PASSENGER);
221 8 : if (lane != nullptr) {
222 0 : myTrajectories[id].back().edgeOrLane = lane->getID();
223 0 : myTrajectories[id].back().lanePos = lane->interpolateGeometryPosToLanePos(
224 : lane->getShape().nearest_offset_to_point25D(xy, false));
225 0 : edge = &lane->getEdge();
226 0 : while (edge->isInternal()) { // we cannot use an internal edge for a route
227 0 : edge = edge->getSuccessors().front();
228 : }
229 : }
230 : }
231 600 : myRoutes[id] = std::make_tuple(myTime, type, isPerson, ConstMSEdgeVector{edge}, std::vector<StageStart>());
232 : } else {
233 20416 : ConstMSEdgeVector& route = std::get<3>(myRoutes[id]);
234 19856 : if (edge != nullptr && !edge->isInternal() && edge != route.back()) {
235 1288 : route.push_back(edge);
236 : }
237 : }
238 20616 : std::vector<StageStart>& vehicleUsage = std::get<4>(myRoutes[id]);
239 20616 : if ((vehicleUsage.empty() && vehicle != "") || (!vehicleUsage.empty() && vehicle != vehicleUsage.back().vehicle)) {
240 160 : vehicleUsage.push_back({vehicle, (int)myTrajectories[id].size() - 1, (int)std::get<3>(myRoutes[id]).size() - 1});
241 : }
242 : }
243 : return;
244 : }
245 : default:
246 : break;
247 : }
248 41312 : }
249 :
250 :
251 : MSTransportable::MSTransportablePlan*
252 188 : MSDevice_FCDReplay::FCDHandler::makePlan(const SUMOVehicleParameter& params, const ConstMSEdgeVector& route,
253 : const std::vector<StageStart>& stages, const Trajectory& t) {
254 188 : MSTransportable::MSTransportablePlan* plan = new MSTransportable::MSTransportablePlan();
255 376 : plan->push_back(new MSStageWaiting(route.front(), nullptr, 0, params.depart, params.departPos, "awaiting departure", true));
256 : int prevRouteOffset = 0;
257 188 : const MSEdge* start = route.front();
258 : std::string prevVeh;
259 316 : for (const auto& stageStart : stages) {
260 128 : if (stageStart.vehicle != prevVeh) {
261 128 : if (stageStart.trajectoryOffset != 0) {
262 128 : const MSEdge* prevEdge = MSEdge::dictionary(t[stageStart.trajectoryOffset - 1].edgeOrLane);
263 128 : if (prevVeh == "") {
264 : MSStoppingPlace* finalStop = nullptr;
265 64 : if (prevRouteOffset < (int)route.size() - 1 && (route[prevRouteOffset]->getPermissions() & SVC_PEDESTRIAN) == 0) {
266 0 : prevRouteOffset++; // skip the access
267 : }
268 64 : int offset = stageStart.routeOffset;
269 64 : if (offset < (int)route.size() - 1 && (route[offset]->getPermissions() & SVC_PEDESTRIAN) == SVC_PEDESTRIAN) {
270 0 : offset++; // a bus stop or two consecutive walks so include the edge in both
271 : } else {
272 : // may have an access, let's find the stop
273 64 : const std::string& stop = MSNet::getInstance()->getStoppingPlaceID(route[offset]->getLanes()[0], t[stageStart.trajectoryOffset].lanePos, SUMO_TAG_BUS_STOP);
274 64 : if (stop != "") {
275 64 : finalStop = MSNet::getInstance()->getStoppingPlace(stop, SUMO_TAG_BUS_STOP);
276 : }
277 : }
278 64 : ConstMSEdgeVector subRoute = ConstMSEdgeVector(route.begin() + prevRouteOffset, route.begin() + offset);
279 64 : plan->push_back(new MSStageWalking(params.id, subRoute, finalStop, -1, params.departSpeed, params.departPos, 0, 0));
280 64 : } else {
281 192 : plan->push_back(new MSStageDriving(start, prevEdge, nullptr, -1, 0, {prevVeh}));
282 : }
283 128 : start = MSEdge::dictionary(t[stageStart.trajectoryOffset].edgeOrLane);
284 : prevVeh = stageStart.vehicle;
285 128 : prevRouteOffset = stageStart.routeOffset;
286 : }
287 : }
288 : }
289 : // final stage
290 188 : if (prevVeh == "") {
291 188 : if (prevRouteOffset < (int)route.size() - 1 && (route[prevRouteOffset]->getPermissions() & SVC_PEDESTRIAN) == 0) {
292 64 : prevRouteOffset++;
293 : }
294 : int offset = (int)route.size() - 1;
295 188 : if ((route[offset]->getPermissions() & SVC_PEDESTRIAN) == SVC_PEDESTRIAN) {
296 : offset++;
297 : }
298 188 : if (prevRouteOffset < offset) {
299 : // otherwise we may be still in a vehicle or in access, skip the stage for now
300 188 : ConstMSEdgeVector subRoute = ConstMSEdgeVector(route.begin() + prevRouteOffset, route.begin() + offset);
301 188 : plan->push_back(new MSStageWalking(params.id, subRoute, nullptr, -1, params.departSpeed, params.departPos, 0, 0));
302 188 : }
303 : } else {
304 0 : plan->push_back(new MSStageDriving(start, route.back(), nullptr, -1, 0, {prevVeh}));
305 : }
306 188 : return plan;
307 : }
308 :
309 :
310 : ConstMSEdgeVector
311 32 : MSDevice_FCDReplay::FCDHandler::checkRoute(const ConstMSEdgeVector& edges, const SUMOVehicle* const vehicle) {
312 : ConstMSEdgeVector checkedRoute;
313 464 : for (const MSEdge* const e : edges) {
314 432 : if (checkedRoute.empty() || checkedRoute.back()->isConnectedTo(*e, vehicle->getVehicleType().getVehicleClass())) {
315 424 : checkedRoute.push_back(e);
316 : } else {
317 8 : const MSEdge* fromEdge = checkedRoute.back();
318 : checkedRoute.pop_back();
319 16 : if (!MSNet::getInstance()->getRouterTT(0).compute(fromEdge, e, vehicle, myTime, checkedRoute)) {
320 : // TODO maybe warn about disconnected route
321 4 : checkedRoute.push_back(fromEdge);
322 4 : checkedRoute.push_back(e);
323 : }
324 : // TODO check whether we introduced a big detour
325 : }
326 : }
327 32 : return checkedRoute;
328 0 : }
329 :
330 :
331 : void
332 28 : MSDevice_FCDReplay::FCDHandler::updateTrafficObjects(const SUMOTime intervalStart) {
333 280 : for (const auto& desc : myRoutes) {
334 252 : const std::string& id = desc.first;
335 252 : const bool isPerson = std::get<2>(desc.second);
336 : const ConstMSEdgeVector& routeEdges = std::get<3>(desc.second);
337 252 : Trajectory& t = myTrajectories[id];
338 252 : if (t.front().time >= intervalStart) {
339 : // new vehicle or person
340 200 : SUMOVehicleParameter* params = new SUMOVehicleParameter();
341 200 : params->id = id;
342 200 : params->depart = std::get<0>(desc.second);
343 200 : params->departPos = t.front().lanePos;
344 200 : params->departSpeed = t.front().speed;
345 : // params->arrivalPos = t.back().lanePos;
346 : std::string vType = std::get<1>(desc.second);
347 200 : if (vType == "") {
348 48 : vType = isPerson ? DEFAULT_PEDTYPE_ID : DEFAULT_VTYPE_ID;
349 : }
350 200 : MSVehicleType* vehicleType = MSNet::getInstance()->getVehicleControl().getVType(vType);
351 200 : if (vehicleType == nullptr) {
352 0 : throw ProcessError(TLF("Unknown vType '%'.", vType));
353 : }
354 200 : if (routeEdges.front() == nullptr) {
355 8 : if (isPerson) {
356 0 : WRITE_WARNINGF(TL("No edge in fcd replay file for person '%' at time %."), id, time2string(t.front().time));
357 : } else {
358 24 : WRITE_WARNINGF(TL("No lane in fcd replay file for vehicle '%' at time %."), id, time2string(t.front().time));
359 : }
360 8 : continue;
361 : }
362 192 : if (isPerson) {
363 164 : MSTransportable::MSTransportablePlan* plan = makePlan(*params, routeEdges, std::get<4>(desc.second), t);
364 : // plan completed, now build the person
365 164 : MSTransportable* person = MSNet::getInstance()->getPersonControl().buildPerson(params, vehicleType, plan, nullptr);
366 164 : person->getSingularType().setVClass(SVC_IGNORING);
367 164 : if (!MSNet::getInstance()->getPersonControl().add(person)) {
368 0 : throw ProcessError(TLF("Duplicate person '%'.", id));
369 : }
370 164 : MSTransportableDevice_FCDReplay* device = static_cast<MSTransportableDevice_FCDReplay*>(person->getDevice(typeid(MSTransportableDevice_FCDReplay)));
371 164 : if (device == nullptr) { // Person did not get a replay device
372 : // TODO delete person
373 0 : continue;
374 : }
375 : device->setTrajectory(&t);
376 : } else {
377 28 : const std::string dummyRouteID = "DUMMY_ROUTE_" + id;
378 : const StopParVector stops;
379 28 : ConstMSRoutePtr route = std::make_shared<MSRoute>(dummyRouteID, routeEdges, true, nullptr, stops);
380 56 : if (!MSRoute::dictionary(dummyRouteID, route)) {
381 0 : throw ProcessError(TLF("Could not add route '%'.", dummyRouteID));
382 : }
383 28 : if (t.front().edgeOrLane != "") {
384 28 : params->departPosProcedure = DepartPosDefinition::GIVEN;
385 28 : params->departLaneProcedure = DepartLaneDefinition::GIVEN;
386 28 : params->departLane = SUMOXMLDefinitions::getIndexFromLane(t.front().edgeOrLane);
387 : }
388 28 : SUMOVehicle* vehicle = MSNet::getInstance()->getVehicleControl().buildVehicle(params, route, vehicleType, false, MSVehicleControl::VehicleDefinitionSource::OTHER);
389 28 : if (!MSNet::getInstance()->getVehicleControl().addVehicle(id, vehicle)) {
390 0 : throw ProcessError(TLF("Duplicate vehicle '%'.", id));
391 : }
392 28 : MSNet::getInstance()->getInsertionControl().add(vehicle);
393 28 : MSDevice_FCDReplay* device = static_cast<MSDevice_FCDReplay*>(vehicle->getDevice(typeid(MSDevice_FCDReplay)));
394 0 : if (device == nullptr) { // Vehicle did not get a replay device
395 0 : MSNet::getInstance()->getVehicleControl().deleteVehicle(vehicle, true);
396 : continue;
397 : }
398 : device->setTrajectory(&t);
399 :
400 : // repair the route, cannot do this on parsing because a vehicle is needed
401 28 : ConstMSEdgeVector checkedRoute = checkRoute(routeEdges, vehicle);
402 28 : if (checkedRoute.size() != routeEdges.size()) {
403 8 : vehicle->replaceRouteEdges(checkedRoute, -1, 0, "FCDReplay", true);
404 : }
405 56 : }
406 52 : } else if (t.back().time >= intervalStart) {
407 : // new data for existing person / vehicle
408 28 : if (isPerson) {
409 24 : MSTransportable* person = MSNet::getInstance()->getPersonControl().get(id);
410 24 : if (person == nullptr) {
411 : // TODO this should not happen
412 0 : continue;
413 : }
414 : // TODO optimize: no need to regenerate the whole plan
415 24 : MSTransportable::MSTransportablePlan* plan = makePlan(person->getParameter(), routeEdges, std::get<4>(desc.second), t);
416 24 : const int stageIndex = person->getNumRemainingStages() - 1;
417 : MSStage* const final = person->getNextStage(stageIndex);
418 : bool append = false;
419 120 : for (MSStage* stage : *plan) {
420 96 : if (stage->getStageType() == final->getStageType() && stage->getFromEdge() == final->getFromEdge()) {
421 : // TODO: circular plans?
422 : append = true;
423 : }
424 72 : if (append) {
425 24 : person->appendStage(stage);
426 : }
427 : }
428 24 : person->removeStage(stageIndex);
429 : } else {
430 4 : SUMOVehicle* vehicle = MSNet::getInstance()->getVehicleControl().getVehicle(id);
431 4 : ConstMSEdgeVector checkedRoute = checkRoute(routeEdges, vehicle);
432 4 : if ((int)checkedRoute.size() != vehicle->getRoute().size()) {
433 8 : vehicle->replaceRouteEdges(checkedRoute, -1, 0, "FCDReplay", true);
434 : }
435 4 : }
436 : }
437 : }
438 28 : }
439 :
440 :
441 : void
442 4 : MSDevice_FCDReplay::FCDHandler::initLaneTree(NamedRTree* tree) {
443 64 : for (const auto& edge : MSEdge::getAllEdges()) {
444 156 : for (MSLane* lane : edge->getLanes()) {
445 96 : Boundary b = lane->getShape().getBoxBoundary();
446 96 : const float cmin[2] = {(float) b.xmin(), (float) b.ymin()};
447 96 : const float cmax[2] = {(float) b.xmax(), (float) b.ymax()};
448 96 : tree->Insert(cmin, cmax, lane);
449 : }
450 : }
451 4 : }
452 :
453 :
454 : MSEdge*
455 0 : MSDevice_FCDReplay::FCDHandler::retrieveEdge(const std::string& id) {
456 0 : return MSEdge::dictionary(id);
457 : }
458 :
459 :
460 : /****************************************************************************/
|