Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2012-2026 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 MSFCDExport.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Mario Krumnow
18 : /// @author Michael Behrisch
19 : /// @date 2012-04-26
20 : ///
21 : // Realises dumping Floating Car Data (FCD) Data
22 : /****************************************************************************/
23 : #include <config.h>
24 :
25 : #include <utils/iodevices/OutputDevice.h>
26 : #include <utils/options/OptionsCont.h>
27 : #include <utils/geom/GeoConvHelper.h>
28 : #include <utils/geom/GeomHelper.h>
29 : #include <utils/shapes/SUMOPolygon.h>
30 : #include <libsumo/Helper.h>
31 : #include <microsim/devices/MSDevice_FCD.h>
32 : #include <microsim/devices/MSTransportableDevice_FCD.h>
33 : #include <microsim/MSEdgeControl.h>
34 : #include <microsim/MSEdge.h>
35 : #include <microsim/MSLane.h>
36 : #include <microsim/MSGlobals.h>
37 : #include <microsim/MSNet.h>
38 : #include <microsim/MSVehicle.h>
39 : #include <microsim/lcmodels/MSAbstractLaneChangeModel.h>
40 : #include <microsim/transportables/MSPerson.h>
41 : #include <microsim/transportables/MSTransportableControl.h>
42 : #include <microsim/MSVehicleControl.h>
43 : #include <mesosim/MEVehicle.h>
44 : #include "MSEmissionExport.h"
45 : #include "MSFCDExport.h"
46 :
47 :
48 : // ===========================================================================
49 : // method definitions
50 : // ===========================================================================
51 : void
52 7289077 : MSFCDExport::write(OutputDevice& of, const SUMOTime timestep, const SumoXMLTag tag) {
53 7289077 : MSDevice_FCD::initOnce();
54 : const SUMOTime period = MSDevice_FCD::getPeriod();
55 : const SUMOTime begin = MSDevice_FCD::getBegin();
56 7289077 : if ((period > 0 && (timestep - begin) % period != 0) || timestep < begin) {
57 57722 : return;
58 : }
59 : const SumoXMLAttrMask& mask = MSDevice_FCD::getWrittenAttributes();
60 : const bool useGeo = MSDevice_FCD::useGeo();
61 : const bool useUTM = MSDevice_FCD::useUTM();
62 : const double maxLeaderDistance = MSDevice_FCD::getMaxLeaderDistance();
63 : const std::vector<std::string>& params = MSDevice_FCD::getParamsToWrite();
64 7231355 : MSNet* net = MSNet::getInstance();
65 : MSVehicleControl& vc = net->getVehicleControl();
66 : const double radius = MSDevice_FCD::getRadius();
67 7231355 : const bool filter = MSDevice_FCD::getEdgeFilter().size() > 0;
68 : const bool shapeFilter = MSDevice_FCD::hasShapeFilter();
69 : std::set<const Named*> inRadius;
70 7231355 : if (radius > 0) {
71 : // collect all vehicles in radius around equipped vehicles
72 4152 : for (MSVehicleControl::constVehIt it = vc.loadedVehBegin(); it != vc.loadedVehEnd(); ++it) {
73 2738 : const SUMOVehicle* veh = it->second;
74 2738 : if (isVisible(veh) && hasOwnOutput(veh, filter, shapeFilter)) {
75 1342 : PositionVector shape;
76 1342 : shape.push_back(veh->getPosition());
77 1342 : libsumo::Helper::collectObjectsInRange(libsumo::CMD_GET_VEHICLE_VARIABLE, shape, radius, inRadius);
78 1342 : libsumo::Helper::collectObjectsInRange(libsumo::CMD_GET_PERSON_VARIABLE, shape, radius, inRadius);
79 1342 : }
80 : }
81 : }
82 :
83 7231355 : bool wroteTimestep = false;
84 18030950 : auto openTimestep = [&]() {
85 18030950 : if (!wroteTimestep) {
86 7230243 : of.openTag("timestep").writeTime(SUMO_ATTR_TIME, timestep);
87 7230243 : wroteTimestep = true;
88 : }
89 25262305 : };
90 7231355 : if (!MSDevice_FCD::skipEmpty()) {
91 7229953 : openTimestep();
92 : }
93 17994629 : for (MSVehicleControl::constVehIt it = vc.loadedVehBegin(); it != vc.loadedVehEnd(); ++it) {
94 10763274 : const SUMOVehicle* const veh = it->second;
95 10763274 : if (isVisible(veh)) {
96 17799106 : const bool hasOutput = (tag == SUMO_TAG_NOTHING || tag == SUMO_TAG_VEHICLE) && hasOwnOutput(veh, filter, shapeFilter, (radius > 0 && inRadius.count(veh) > 0));
97 : if (hasOutput) {
98 8321995 : openTimestep();
99 8321995 : const MSVehicle* const microVeh = MSGlobals::gUseMesoSim ? nullptr : static_cast<const MSVehicle*>(veh);
100 8321995 : Position pos = veh->getPosition();
101 8321995 : if (useGeo) {
102 2696 : of.setPrecision(gPrecisionGeo);
103 2696 : GeoConvHelper::getFinal().cartesian2geo(pos);
104 8319299 : } else if (useUTM) {
105 90 : pos.sub(GeoConvHelper::getFinal().getOffset());
106 : }
107 8321995 : of.openTag(SUMO_TAG_VEHICLE);
108 8321995 : of.writeAttr(SUMO_ATTR_ID, veh->getID());
109 8321995 : of.writeOptionalAttr(SUMO_ATTR_X, pos.x(), mask);
110 8321995 : of.writeOptionalAttr(SUMO_ATTR_Y, pos.y(), mask);
111 8321995 : of.setPrecision(gPrecision);
112 8321995 : of.writeOptionalAttr(SUMO_ATTR_Z, pos.z(), mask);
113 8321995 : of.writeFuncAttr(SUMO_ATTR_ANGLE, [ = ]() {
114 3849943 : return GeomHelper::naviDegree(veh->getAngle());
115 : }, mask);
116 8321995 : of.writeFuncAttr(SUMO_ATTR_TYPE, [ = ]() {
117 7941751 : return veh->getVehicleType().getID();
118 : }, mask);
119 8321995 : of.writeFuncAttr(SUMO_ATTR_SPEED, [ = ]() {
120 4115194 : return veh->getSpeed();
121 : }, mask);
122 8321995 : of.writeFuncAttr(SUMO_ATTR_SPEEDREL, [ = ]() {
123 776 : const double speedLimit = veh->getEdge()->getSpeedLimit();
124 776 : return speedLimit > 0 ? veh->getSpeed() / speedLimit : 0.;
125 : }, mask);
126 8321995 : of.writeFuncAttr(SUMO_ATTR_POSITION, [ = ]() {
127 3983700 : return veh->getPositionOnLane();
128 : }, mask);
129 8321995 : of.writeFuncAttr(SUMO_ATTR_LANE, [ = ]() {
130 3939284 : return MSGlobals::gUseMesoSim ? "" : microVeh->getLane()->getID();
131 : }, mask, MSGlobals::gUseMesoSim);
132 16722397 : of.writeFuncAttr(SUMO_ATTR_EDGE, [ = ]() {
133 78407 : return veh->getCurrentEdge()->getID();
134 8321995 : }, mask, !MSGlobals::gUseMesoSim);
135 8321995 : of.writeFuncAttr(SUMO_ATTR_SLOPE, [ = ]() {
136 3847281 : return veh->getSlope();
137 : }, mask);
138 8321995 : if (!MSGlobals::gUseMesoSim) {
139 8239888 : of.writeFuncAttr(SUMO_ATTR_SIGNALS, [ = ]() {
140 14762 : return microVeh->getSignals();
141 : }, mask);
142 8239888 : of.writeFuncAttr(SUMO_ATTR_ACCELERATION, [ = ]() {
143 154653 : return microVeh->getAcceleration();
144 : }, mask);
145 8239888 : of.writeFuncAttr(SUMO_ATTR_ACCELERATION_LAT, [ = ]() {
146 55981 : return microVeh->getLaneChangeModel().getAccelerationLat();
147 : }, mask);
148 8239888 : of.writeFuncAttr(SUMO_ATTR_SPEED_VEC, [ = ]() {
149 844 : return GeomHelper::vectorize(microVeh->getSpeed(), microVeh->getAngle());
150 : }, mask);
151 8239888 : of.writeFuncAttr(SUMO_ATTR_ACCEL_VEC, [ = ]() {
152 844 : return GeomHelper::vectorize(microVeh->getAcceleration(), microVeh->getAngle());
153 : }, mask);
154 : }
155 8321995 : of.writeFuncAttr(SUMO_ATTR_DISTANCE, [ = ]() {
156 2134 : double lanePos = veh->getPositionOnLane();
157 2134 : if (!MSGlobals::gUseMesoSim && microVeh->getLane()->isInternal()) {
158 4 : lanePos = microVeh->getRoute().getDistanceBetween(0., lanePos, microVeh->getEdge()->getLanes()[0], microVeh->getLane(),
159 2 : microVeh->getRoutePosition());
160 : }
161 2134 : return veh->getEdge()->getDistanceAt(lanePos);
162 : }, mask);
163 8321995 : of.writeFuncAttr(SUMO_ATTR_ODOMETER, [ = ]() {
164 806 : return veh->getOdometer();
165 : }, mask);
166 8321995 : of.writeFuncAttr(SUMO_ATTR_POSITION_LAT, [ = ]() {
167 63170 : return veh->getLateralPositionOnLane();
168 : }, mask);
169 8321995 : if (!MSGlobals::gUseMesoSim) {
170 8239888 : of.writeFuncAttr(SUMO_ATTR_SPEED_LAT, [ = ]() {
171 30477 : return microVeh->getLaneChangeModel().getSpeedLat();
172 : }, mask);
173 : }
174 8321995 : if (maxLeaderDistance >= 0 && !MSGlobals::gUseMesoSim) {
175 19203 : const std::pair<const MSVehicle* const, double> leader = microVeh->getLeader(maxLeaderDistance);
176 19203 : if (leader.first != nullptr) {
177 13988 : of.writeFuncAttr(SUMO_ATTR_LEADER_ID, [ = ]() {
178 13388 : return leader.first->getID();
179 : }, mask);
180 13988 : of.writeFuncAttr(SUMO_ATTR_LEADER_SPEED, [ = ]() {
181 13388 : return leader.first->getSpeed();
182 : }, mask);
183 13988 : of.writeFuncAttr(SUMO_ATTR_LEADER_GAP, [ = ]() {
184 13988 : return leader.second + microVeh->getVehicleType().getMinGap();
185 : }, mask);
186 : } else {
187 5215 : of.writeFuncAttr(SUMO_ATTR_LEADER_ID, [ = ]() {
188 : return "";
189 : }, mask);
190 5215 : of.writeFuncAttr(SUMO_ATTR_LEADER_SPEED, [ = ]() {
191 : return -1;
192 : }, mask);
193 5215 : of.writeFuncAttr(SUMO_ATTR_LEADER_GAP, [ = ]() {
194 : return -1;
195 : }, mask);
196 : }
197 : }
198 8341008 : for (const std::string& key : params) {
199 : std::string error;
200 19013 : const std::string value = static_cast<const MSBaseVehicle*>(veh)->getPrefixedParameter(key, error);
201 19013 : if (value != "") {
202 28404 : of.writeAttr(StringUtils::escapeXML(key), StringUtils::escapeXML(value));
203 : }
204 : }
205 8321995 : of.writeFuncAttr(SUMO_ATTR_ARRIVALDELAY, [ = ]() {
206 5288 : const double arrivalDelay = static_cast<const MSBaseVehicle*>(veh)->getStopArrivalDelay();
207 5288 : if (arrivalDelay == INVALID_DOUBLE) {
208 : // no upcoming stop also means that there is no delay
209 2388 : return 0.;
210 : }
211 : return arrivalDelay;
212 : }, mask);
213 8321995 : of.writeFuncAttr(SUMO_ATTR_DELAY, [ = ]() {
214 806 : const double delay = static_cast<const MSBaseVehicle*>(veh)->getStopDelay();
215 806 : if (delay < 0) {
216 : // no upcoming stop also means that there is no delay
217 656 : return 0.;
218 : }
219 : return delay;
220 : }, mask);
221 8321995 : if (MSGlobals::gUseMesoSim) {
222 : const MEVehicle* mesoVeh = static_cast<const MEVehicle*>(veh);
223 82107 : of.writeFuncAttr(SUMO_ATTR_SEGMENT, [ = ]() {
224 5732 : return mesoVeh->getSegmentIndex();
225 : }, mask);
226 82107 : of.writeFuncAttr(SUMO_ATTR_QUEUE, [ = ]() {
227 5732 : return mesoVeh->getQueIndex();
228 : }, mask);
229 82107 : of.writeFuncAttr(SUMO_ATTR_ENTRYTIME, [ = ]() {
230 5732 : return mesoVeh->getLastEntryTimeSeconds();
231 : }, mask);
232 82107 : of.writeFuncAttr(SUMO_ATTR_EVENTTIME, [ = ]() {
233 5732 : return mesoVeh->getEventTimeSeconds();
234 : }, mask);
235 82107 : of.writeFuncAttr(SUMO_ATTR_BLOCKTIME, [ = ]() {
236 5732 : return mesoVeh->getBlockTime() == SUMOTime_MAX ? -1.0 : mesoVeh->getBlockTimeSeconds();
237 : }, mask);
238 : }
239 8321995 : of.writeFuncAttr(SUMO_ATTR_TAG, [ = ]() {
240 30 : return toString(SUMO_TAG_VEHICLE);
241 : }, mask);
242 8321995 : of.writeOptionalAttr(SUMO_ATTR_PERSON_NUMBER, veh->getPersonNumber(), mask);
243 8321995 : of.writeOptionalAttr(SUMO_ATTR_CONTAINER_NUMBER, veh->getContainerNumber(), mask);
244 8321995 : MSEmissionExport::writeEmissions(of, static_cast<const MSBaseVehicle*>(veh), false, mask);
245 16643990 : of.closeTag();
246 : }
247 : // write persons and containers in the vehicle
248 8899565 : if (tag == SUMO_TAG_NOTHING || tag == SUMO_TAG_PERSON) {
249 8899541 : const MSEdge* edge = MSGlobals::gUseMesoSim ? veh->getEdge() : &veh->getLane()->getEdge();
250 8944716 : for (const MSTransportable* const person : veh->getPersons()) {
251 45175 : if (hasOwnOutput(person, filter, shapeFilter, inRadius.count(person) > 0)) {
252 44509 : openTimestep();
253 44509 : writeTransportable(of, edge, person, veh, SUMO_TAG_PERSON, useGeo, mask);
254 : }
255 : }
256 8903091 : for (const MSTransportable* const container : veh->getContainers()) {
257 3550 : if (hasOwnOutput(container, filter, shapeFilter, inRadius.count(container) > 0)) {
258 3246 : openTimestep();
259 3246 : writeTransportable(of, edge, container, veh, SUMO_TAG_CONTAINER, useGeo, mask);
260 : }
261 : }
262 : }
263 : }
264 : }
265 7231355 : if (tag == SUMO_TAG_NOTHING || tag == SUMO_TAG_PERSON) {
266 7231331 : if (net->hasPersons() && net->getPersonControl().hasTransportables()) {
267 : // write persons who are not in a vehicle
268 129335051 : for (const MSEdge* const e : net->getEdgeControl().getEdges()) {
269 127065158 : if (filter && MSDevice_FCD::getEdgeFilter().count(e) == 0) {
270 108 : continue;
271 : }
272 129493392 : for (const MSTransportable* const person : e->getSortedPersons(timestep)) {
273 2428342 : if (hasOwnOutput(person, filter, shapeFilter, inRadius.count(person) > 0)) {
274 2427600 : openTimestep();
275 2427600 : writeTransportable(of, e, person, nullptr, SUMO_TAG_PERSON, useGeo, mask);
276 : }
277 127065050 : }
278 : }
279 : }
280 7231331 : if (net->hasContainers() && net->getContainerControl().hasTransportables()) {
281 : // write containers which are not in a vehicle
282 61833053 : for (const MSEdge* const e : net->getEdgeControl().getEdges()) {
283 57735136 : if (filter && MSDevice_FCD::getEdgeFilter().count(e) == 0) {
284 0 : continue;
285 : }
286 57738887 : for (MSTransportable* container : e->getSortedContainers(timestep)) {
287 3751 : if (hasOwnOutput(container, filter, shapeFilter, inRadius.count(container) > 0)) {
288 3647 : openTimestep();
289 3647 : writeTransportable(of, e, container, nullptr, SUMO_TAG_CONTAINER, useGeo, mask);
290 : }
291 57735136 : }
292 : }
293 : }
294 : }
295 7231355 : if (wroteTimestep) {
296 14460486 : of.closeTag();
297 : }
298 : }
299 :
300 :
301 : bool
302 10766012 : MSFCDExport::isVisible(const SUMOVehicle* veh) {
303 10766012 : return veh->isOnRoad() || veh->isParking() || veh->isRemoteControlled();
304 : }
305 :
306 :
307 : bool
308 8902249 : MSFCDExport::hasOwnOutput(const SUMOVehicle* veh, bool filter, bool shapeFilter, bool isInRadius) {
309 54 : return ((!filter || MSDevice_FCD::getEdgeFilter().count(veh->getEdge()) > 0)
310 8902225 : && (!shapeFilter || MSDevice_FCD::shapeFilter(veh))
311 17800102 : && ((veh->getDevice(typeid(MSDevice_FCD)) != nullptr) || isInRadius));
312 : }
313 :
314 :
315 : bool
316 2480818 : MSFCDExport::hasOwnOutput(const MSTransportable* p, bool filter, bool shapeFilter, bool isInRadius) {
317 54 : return ((!filter || MSDevice_FCD::getEdgeFilter().count(p->getEdge()) > 0)
318 2480790 : && (!shapeFilter || MSDevice_FCD::shapeFilter(p))
319 4961608 : && ((p->getDevice(typeid(MSTransportableDevice_FCD)) != nullptr) || isInRadius));
320 : }
321 :
322 :
323 : void
324 2479002 : MSFCDExport::writeTransportable(OutputDevice& of, const MSEdge* const e, const MSTransportable* const p, const SUMOVehicle* const v,
325 : const SumoXMLTag tag, const bool useGeo, const SumoXMLAttrMask mask) {
326 2479002 : Position pos = p->getPosition();
327 2479002 : if (useGeo) {
328 0 : of.setPrecision(gPrecisionGeo);
329 0 : GeoConvHelper::getFinal().cartesian2geo(pos);
330 : }
331 2479002 : of.openTag(tag);
332 2479002 : of.writeAttr(SUMO_ATTR_ID, p->getID());
333 2479002 : of.writeOptionalAttr(SUMO_ATTR_X, pos.x(), mask);
334 2479002 : of.writeOptionalAttr(SUMO_ATTR_Y, pos.y(), mask);
335 2479002 : of.setPrecision(gPrecision);
336 2479002 : of.writeOptionalAttr(SUMO_ATTR_Z, pos.z(), mask);
337 2479002 : of.writeOptionalAttr(SUMO_ATTR_ANGLE, GeomHelper::naviDegree(p->getAngle()), mask);
338 2479002 : of.writeOptionalAttr(SUMO_ATTR_TYPE, p->getVehicleType().getID(), mask);
339 2479002 : of.writeOptionalAttr(SUMO_ATTR_SPEED, p->getSpeed(), mask);
340 2479002 : of.writeOptionalAttr(SUMO_ATTR_SPEEDREL, e->getSpeedLimit() > 0 ? p->getSpeed() / e->getSpeedLimit() : 0., mask);
341 2479002 : of.writeOptionalAttr(SUMO_ATTR_POSITION, p->getEdgePos(), mask);
342 2479002 : of.writeOptionalAttr(SUMO_ATTR_LANE, "", mask, true);
343 2479002 : of.writeOptionalAttr(SUMO_ATTR_EDGE, e->getID(), mask);
344 2479002 : of.writeOptionalAttr(SUMO_ATTR_SLOPE, e->getLanes()[0]->getShape().slopeDegreeAtOffset(p->getEdgePos()), mask);
345 5005759 : of.writeOptionalAttr(SUMO_ATTR_VEHICLE, v == nullptr ? "" : v->getID(), mask);
346 2479002 : of.writeOptionalAttr(SUMO_ATTR_STAGE, p->getCurrentStageDescription(), mask);
347 2479002 : of.writeOptionalAttr(SUMO_ATTR_TAG, toString(tag), mask);
348 2479002 : of.closeTag();
349 2479002 : }
350 :
351 :
352 : /****************************************************************************/
|