Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netwrite/NWWriter_SUMO.cpp
169666 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2001-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 NWWriter_SUMO.cpp
15
/// @author Daniel Krajzewicz
16
/// @author Jakob Erdmann
17
/// @author Michael Behrisch
18
/// @author Leonhard Luecken
19
/// @date Tue, 04.05.2011
20
///
21
// Exporter writing networks using the SUMO format
22
/****************************************************************************/
23
#include <config.h>
24
#include <cmath>
25
#include <algorithm>
26
#include <utils/options/OptionsCont.h>
27
#include <utils/iodevices/OutputDevice.h>
28
#include <utils/geom/GeoConvHelper.h>
29
#include <utils/common/ToString.h>
30
#include <utils/common/MsgHandler.h>
31
#include <utils/common/StringUtils.h>
32
#include <utils/common/SUMOVehicleClass.h>
33
#include <utils/geom/GeomConvHelper.h>
34
#include <netbuild/NBEdge.h>
35
#include <netbuild/NBEdgeCont.h>
36
#include <netbuild/NBNode.h>
37
#include <netbuild/NBNodeCont.h>
38
#include <netbuild/NBNetBuilder.h>
39
#include <netbuild/NBTrafficLightLogic.h>
40
#include <netbuild/NBDistrict.h>
41
#include <netbuild/NBHelpers.h>
42
#include "NWFrame.h"
43
#include "NWWriter_SUMO.h"
44
45
46
//#define DEBUG_OPPOSITE_INTERNAL
47
48
// ===========================================================================
49
// method definitions
50
// ===========================================================================
51
// ---------------------------------------------------------------------------
52
// static methods
53
// ---------------------------------------------------------------------------
54
void
55
NWWriter_SUMO::writeNetwork(const OptionsCont& oc, NBNetBuilder& nb) {
56
// check whether a sumo net-file shall be generated
57
if (!oc.isSet("output-file")) {
58
return;
59
}
60
OutputDevice& device = OutputDevice::getDevice(oc.getString("output-file"));
61
std::map<SumoXMLAttr, std::string> attrs;
62
attrs[SUMO_ATTR_VERSION] = toString(NETWORK_VERSION);
63
if (oc.getBool("lefthand") != oc.getBool("flip-y-axis")) {
64
attrs[SUMO_ATTR_LEFTHAND] = "true";
65
} else if (oc.getBool("lefthand")) {
66
// network was flipped, correct written link directions
67
OptionsCont::getOptions().resetWritable();
68
OptionsCont::getOptions().set("lefthand", "false");
69
}
70
LaneSpreadFunction defaultSpread = SUMOXMLDefinitions::LaneSpreadFunctions.get(oc.getString("default.spreadtype"));
71
const int cornerDetail = oc.getInt("junctions.corner-detail");
72
if (cornerDetail > 0) {
73
attrs[SUMO_ATTR_CORNERDETAIL] = toString(cornerDetail);
74
}
75
if (!oc.isDefault("junctions.internal-link-detail")) {
76
attrs[SUMO_ATTR_LINKDETAIL] = toString(oc.getInt("junctions.internal-link-detail"));
77
}
78
if (oc.getBool("rectangular-lane-cut")) {
79
attrs[SUMO_ATTR_RECTANGULAR_LANE_CUT] = "true";
80
}
81
if (oc.getBool("crossings.guess") || oc.getBool("walkingareas")) {
82
attrs[SUMO_ATTR_WALKINGAREAS] = "true";
83
}
84
if (oc.getFloat("junctions.limit-turn-speed") > 0) {
85
attrs[SUMO_ATTR_LIMIT_TURN_SPEED] = toString(oc.getFloat("junctions.limit-turn-speed"));
86
}
87
if (!oc.isDefault("check-lane-foes.all")) {
88
attrs[SUMO_ATTR_CHECKLANEFOES_ALL] = toString(oc.getBool("check-lane-foes.all"));
89
}
90
if (!oc.isDefault("check-lane-foes.roundabout")) {
91
attrs[SUMO_ATTR_CHECKLANEFOES_ROUNDABOUT] = toString(oc.getBool("check-lane-foes.roundabout"));
92
}
93
if (!oc.isDefault("tls.ignore-internal-junction-jam")) {
94
attrs[SUMO_ATTR_TLS_IGNORE_INTERNAL_JUNCTION_JAM] = toString(oc.getBool("tls.ignore-internal-junction-jam"));
95
}
96
if (defaultSpread != LaneSpreadFunction::RIGHT) {
97
attrs[SUMO_ATTR_SPREADTYPE] = oc.getString("default.spreadtype");
98
}
99
if (oc.exists("geometry.avoid-overlap") && !oc.getBool("geometry.avoid-overlap")) {
100
attrs[SUMO_ATTR_AVOID_OVERLAP] = toString(oc.getBool("geometry.avoid-overlap"));
101
}
102
if (oc.exists("junctions.higher-speed") && oc.getBool("junctions.higher-speed")) {
103
attrs[SUMO_ATTR_HIGHER_SPEED] = toString(oc.getBool("junctions.higher-speed"));
104
}
105
if (oc.exists("internal-junctions.vehicle-width") && !oc.isDefault("internal-junctions.vehicle-width")) {
106
attrs[SUMO_ATTR_INTERNAL_JUNCTIONS_VEHICLE_WIDTH] = toString(oc.getFloat("internal-junctions.vehicle-width"));
107
}
108
if (!oc.isDefault("junctions.minimal-shape")) {
109
attrs[SUMO_ATTR_JUNCTIONS_MINIMAL_SHAPE] = toString(oc.getBool("junctions.minimal-shape"));
110
}
111
if (!oc.isDefault("junctions.endpoint-shape")) {
112
attrs[SUMO_ATTR_JUNCTIONS_ENDPOINT_SHAPE] = toString(oc.getBool("junctions.endpoint-shape"));
113
}
114
device.writeXMLHeader("net", "net_file.xsd", attrs); // street names may contain non-ascii chars
115
device.lf();
116
// get involved container
117
const NBNodeCont& nc = nb.getNodeCont();
118
const NBEdgeCont& ec = nb.getEdgeCont();
119
const NBDistrictCont& dc = nb.getDistrictCont();
120
121
// write network offsets and projection
122
GeoConvHelper::writeLocation(device);
123
124
// write edge types and restrictions
125
std::set<std::string> usedTypes = ec.getUsedTypes();
126
nb.getTypeCont().writeEdgeTypes(device, usedTypes);
127
128
// write inner lanes
129
if (!oc.getBool("no-internal-links")) {
130
bool hadAny = false;
131
for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
132
hadAny |= writeInternalEdges(device, ec, *(*i).second);
133
}
134
if (hadAny) {
135
device.lf();
136
}
137
}
138
139
// write edges with lanes and connected edges
140
bool noNames = !oc.getBool("output.street-names");
141
for (std::map<std::string, NBEdge*>::const_iterator i = ec.begin(); i != ec.end(); ++i) {
142
writeEdge(device, *(*i).second, noNames, defaultSpread);
143
}
144
device.lf();
145
146
// write tls logics
147
writeTrafficLights(device, nb.getTLLogicCont());
148
149
// write the nodes (junctions)
150
for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
151
writeJunction(device, *(*i).second);
152
}
153
device.lf();
154
const bool includeInternal = !oc.getBool("no-internal-links");
155
if (includeInternal) {
156
// ... internal nodes if not unwanted
157
bool hadAny = false;
158
for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
159
hadAny |= writeInternalNodes(device, *(*i).second);
160
}
161
if (hadAny) {
162
device.lf();
163
}
164
}
165
166
// write the successors of lanes
167
int numConnections = 0;
168
for (std::map<std::string, NBEdge*>::const_iterator it_edge = ec.begin(); it_edge != ec.end(); it_edge++) {
169
NBEdge* from = it_edge->second;
170
const std::vector<NBEdge::Connection>& connections = from->getConnections();
171
numConnections += (int)connections.size();
172
for (const NBEdge::Connection& con : connections) {
173
writeConnection(device, *from, con, includeInternal);
174
}
175
}
176
if (numConnections > 0) {
177
device.lf();
178
}
179
if (includeInternal) {
180
// ... internal successors if not unwanted
181
bool hadAny = false;
182
for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
183
hadAny |= writeInternalConnections(device, *(*i).second);
184
}
185
if (hadAny) {
186
device.lf();
187
}
188
}
189
for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
190
NBNode* node = (*i).second;
191
// write connections from pedestrian crossings
192
std::vector<NBNode::Crossing*> crossings = node->getCrossings();
193
for (auto c : crossings) {
194
NWWriter_SUMO::writeInternalConnection(device, c->id, c->nextWalkingArea, 0, 0, "", LinkDirection::STRAIGHT, c->tlID, c->tlLinkIndex2);
195
}
196
// write connections from pedestrian walking areas
197
for (const NBNode::WalkingArea& wa : node->getWalkingAreas()) {
198
for (const std::string& cID : wa.nextCrossings) {
199
const NBNode::Crossing& nextCrossing = *node->getCrossing(cID);
200
// connection to next crossing (may be tls-controlled)
201
device.openTag(SUMO_TAG_CONNECTION);
202
device.writeAttr(SUMO_ATTR_FROM, wa.id);
203
device.writeAttr(SUMO_ATTR_TO, cID);
204
device.writeAttr(SUMO_ATTR_FROM_LANE, 0);
205
device.writeAttr(SUMO_ATTR_TO_LANE, 0);
206
if (nextCrossing.tlID != "") {
207
device.writeAttr(SUMO_ATTR_TLID, nextCrossing.tlID);
208
assert(nextCrossing.tlLinkIndex >= 0);
209
device.writeAttr(SUMO_ATTR_TLLINKINDEX, nextCrossing.tlLinkIndex);
210
}
211
device.writeAttr(SUMO_ATTR_DIR, LinkDirection::STRAIGHT);
212
device.writeAttr(SUMO_ATTR_STATE, nextCrossing.priority ? LINKSTATE_MAJOR : LINKSTATE_MINOR);
213
device.closeTag();
214
}
215
// optional connections from/to sidewalk
216
std::string edgeID;
217
int laneIndex;
218
for (const std::string& sw : wa.nextSidewalks) {
219
NBHelpers::interpretLaneID(sw, edgeID, laneIndex);
220
NWWriter_SUMO::writeInternalConnection(device, wa.id, edgeID, 0, laneIndex, "");
221
}
222
for (const std::string& sw : wa.prevSidewalks) {
223
NBHelpers::interpretLaneID(sw, edgeID, laneIndex);
224
NWWriter_SUMO::writeInternalConnection(device, edgeID, wa.id, laneIndex, 0, "");
225
}
226
}
227
}
228
229
// write loaded prohibitions
230
for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
231
writeProhibitions(device, i->second->getProhibitions());
232
}
233
234
// write roundabout information
235
writeRoundabouts(device, ec.getRoundabouts(), ec);
236
237
// write the districts
238
if (dc.size() != 0 && oc.isDefault("taz-output")) {
239
WRITE_WARNING(TL("Embedding TAZ-data inside the network is deprecated. Use option --taz-output instead"));
240
for (std::map<std::string, NBDistrict*>::const_iterator i = dc.begin(); i != dc.end(); i++) {
241
writeDistrict(device, *(*i).second);
242
}
243
device.lf();
244
}
245
device.close();
246
}
247
248
249
std::string
250
NWWriter_SUMO::getOppositeInternalID(const NBEdgeCont& ec, const NBEdge* from, const NBEdge::Connection& con, double& oppositeLength) {
251
const NBEdge::Lane& succ = con.toEdge->getLanes()[con.toLane];
252
const NBEdge::Lane& pred = from->getLanes()[con.fromLane];
253
const bool lefthand = OptionsCont::getOptions().getBool("lefthand");
254
if (succ.oppositeID != "" && succ.oppositeID != "-" && pred.oppositeID != "" && pred.oppositeID != "-") {
255
#ifdef DEBUG_OPPOSITE_INTERNAL
256
std::cout << "getOppositeInternalID con=" << con.getDescription(from) << " (" << con.getInternalLaneID() << ")\n";
257
#endif
258
// find the connection that connects succ.oppositeID to pred.oppositeID
259
const NBEdge* succOpp = ec.retrieve(succ.oppositeID.substr(0, succ.oppositeID.rfind("_")));
260
const NBEdge* predOpp = ec.retrieve(pred.oppositeID.substr(0, pred.oppositeID.rfind("_")));
261
assert(succOpp != 0);
262
assert(predOpp != 0);
263
const std::vector<NBEdge::Connection>& connections = succOpp->getConnections();
264
for (std::vector<NBEdge::Connection>::const_iterator it_c = connections.begin(); it_c != connections.end(); it_c++) {
265
const NBEdge::Connection& conOpp = *it_c;
266
if (succOpp != from // turnaround
267
&& predOpp == conOpp.toEdge
268
&& succOpp->getLaneID(conOpp.fromLane) == succ.oppositeID
269
&& predOpp->getLaneID(conOpp.toLane) == pred.oppositeID
270
&& from->getToNode()->getDirection(from, con.toEdge, lefthand) == LinkDirection::STRAIGHT
271
&& from->getToNode()->getDirection(succOpp, predOpp, lefthand) == LinkDirection::STRAIGHT
272
) {
273
#ifdef DEBUG_OPPOSITE_INTERNAL
274
std::cout << " found " << conOpp.getInternalLaneID() << "\n";
275
#endif
276
oppositeLength = conOpp.length;
277
return conOpp.getInternalLaneID();
278
} else {
279
/*
280
#ifdef DEBUG_OPPOSITE_INTERNAL
281
std::cout << " rejected " << conOpp.getInternalLaneID()
282
<< "\n succ.oppositeID=" << succ.oppositeID
283
<< "\n succOppLane=" << succOpp->getLaneID(conOpp.fromLane)
284
<< "\n pred.oppositeID=" << pred.oppositeID
285
<< "\n predOppLane=" << predOpp->getLaneID(conOpp.toLane)
286
<< "\n predOpp=" << predOpp->getID()
287
<< "\n conOppTo=" << conOpp.toEdge->getID()
288
<< "\n len1=" << con.shape.length()
289
<< "\n len2=" << conOpp.shape.length()
290
<< "\n";
291
#endif
292
*/
293
}
294
}
295
return "";
296
} else {
297
return "";
298
}
299
}
300
301
302
bool
303
NWWriter_SUMO::writeInternalEdges(OutputDevice& into, const NBEdgeCont& ec, const NBNode& n) {
304
bool ret = false;
305
const EdgeVector& incoming = n.getIncomingEdges();
306
// first pass: determine opposite internal edges and average their length
307
std::map<std::string, std::string> oppositeLaneID;
308
std::map<std::string, double> oppositeLengths;
309
for (NBEdge* e : incoming) {
310
for (const NBEdge::Connection& c : e->getConnections()) {
311
double oppositeLength = 0;
312
const std::string op = getOppositeInternalID(ec, e, c, oppositeLength);
313
oppositeLaneID[c.getInternalLaneID()] = op;
314
if (op != "") {
315
oppositeLengths[c.id] = oppositeLength;
316
}
317
}
318
}
319
if (oppositeLengths.size() > 0) {
320
for (NBEdge* e : incoming) {
321
for (NBEdge::Connection& c : e->getConnections()) {
322
if (oppositeLengths.count(c.id) > 0) {
323
c.length = (c.length + oppositeLengths[c.id]) / 2;
324
}
325
}
326
}
327
}
328
329
for (NBEdge* e : incoming) {
330
const std::vector<NBEdge::Connection>& elv = e->getConnections();
331
if (elv.size() > 0) {
332
bool haveVia = false;
333
std::string edgeID = "";
334
double bidiLength = -1;
335
// second pass: write non-via edges
336
for (const NBEdge::Connection& k : elv) {
337
if (k.toEdge == nullptr) {
338
assert(false); // should never happen. tell me when it does
339
continue;
340
}
341
if (edgeID != k.id) {
342
if (edgeID != "") {
343
// close the previous edge
344
into.closeTag();
345
}
346
edgeID = k.id;
347
into.openTag(SUMO_TAG_EDGE);
348
into.writeAttr(SUMO_ATTR_ID, edgeID);
349
into.writeAttr(SUMO_ATTR_FUNCTION, SumoXMLEdgeFunc::INTERNAL);
350
if (k.edgeType != "") {
351
into.writeAttr(SUMO_ATTR_TYPE, k.edgeType);
352
}
353
bidiLength = -1;
354
if (e->getBidiEdge() && k.toEdge->getBidiEdge() &&
355
e != k.toEdge->getTurnDestination(true)) {
356
const std::string bidiEdge = getInternalBidi(e, k, bidiLength);
357
if (bidiEdge != "") {
358
into.writeAttr(SUMO_ATTR_BIDI, bidiEdge);
359
}
360
}
361
// open a new edge
362
}
363
// to avoid changing to an internal lane which has a successor
364
// with the wrong permissions we need to inherit them from the successor
365
const NBEdge::Lane& successor = k.toEdge->getLanes()[k.toLane];
366
SVCPermissions permissions = (k.permissions != SVC_UNSPECIFIED) ? k.permissions : (
367
successor.permissions & e->getPermissions(k.fromLane));
368
SVCPermissions changeLeft = k.changeLeft != SVC_UNSPECIFIED ? k.changeLeft : SVCAll;
369
SVCPermissions changeRight = k.changeRight != SVC_UNSPECIFIED ? k.changeRight : SVCAll;
370
const double width = e->getInternalLaneWidth(n, k, successor, false);
371
const double length = bidiLength > 0 ? bidiLength : k.length;
372
writeLane(into, k.getInternalLaneID(), k.vmax, k.friction,
373
permissions, successor.preferred,
374
changeLeft, changeRight,
375
NBEdge::UNSPECIFIED_OFFSET, NBEdge::UNSPECIFIED_OFFSET,
376
StopOffset(), width, k.shape, &k,
377
length, k.internalLaneIndex, oppositeLaneID[k.getInternalLaneID()], "");
378
haveVia = haveVia || k.haveVia;
379
}
380
ret = true;
381
into.closeTag(); // close the last edge
382
// third pass: write via edges
383
if (haveVia) {
384
std::string viaEdgeID = "";
385
for (const NBEdge::Connection& k : elv) {
386
if (!k.haveVia) {
387
continue;
388
}
389
if (k.toEdge == nullptr) {
390
assert(false); // should never happen. tell me when it does
391
continue;
392
}
393
if (viaEdgeID != k.viaID) {
394
if (viaEdgeID != "") {
395
// close the previous edge
396
into.closeTag();
397
}
398
viaEdgeID = k.viaID;
399
// open a new edge
400
into.openTag(SUMO_TAG_EDGE);
401
into.writeAttr(SUMO_ATTR_ID, viaEdgeID);
402
into.writeAttr(SUMO_ATTR_FUNCTION, SumoXMLEdgeFunc::INTERNAL);
403
if (k.edgeType != "") {
404
into.writeAttr(SUMO_ATTR_TYPE, k.edgeType);
405
}
406
}
407
const NBEdge::Lane& successor = k.toEdge->getLanes()[k.toLane];
408
SVCPermissions permissions = (k.permissions != SVC_UNSPECIFIED) ? k.permissions : (
409
successor.permissions & e->getPermissions(k.fromLane));
410
const double width = e->getInternalLaneWidth(n, k, successor, true);
411
writeLane(into, k.getInternalViaLaneID(), k.vmax, k.friction, permissions, successor.preferred,
412
SVCAll, SVCAll, // #XXX todo
413
NBEdge::UNSPECIFIED_OFFSET, NBEdge::UNSPECIFIED_OFFSET,
414
StopOffset(), width, k.viaShape, &k,
415
MAX2(k.viaLength, POSITION_EPS), // microsim needs positive length
416
0, "", "");
417
}
418
into.closeTag();
419
}
420
}
421
}
422
// write pedestrian crossings
423
const double crossingSpeed = OptionsCont::getOptions().getFloat("default.crossing-speed");
424
for (auto c : n.getCrossings()) {
425
into.openTag(SUMO_TAG_EDGE);
426
into.writeAttr(SUMO_ATTR_ID, c->id);
427
into.writeAttr(SUMO_ATTR_FUNCTION, SumoXMLEdgeFunc::CROSSING);
428
into.writeAttr(SUMO_ATTR_CROSSING_EDGES, c->edges);
429
writeLane(into, c->id + "_0", crossingSpeed, NBEdge::UNSPECIFIED_FRICTION, SVC_PEDESTRIAN, 0, SVCAll, SVCAll,
430
NBEdge::UNSPECIFIED_OFFSET, NBEdge::UNSPECIFIED_OFFSET,
431
StopOffset(), c->width, c->shape, c,
432
MAX2(c->shape.length(), POSITION_EPS), 0, "", "", false, c->customShape.size() != 0, c->outlineShape);
433
into.closeTag();
434
}
435
// write pedestrian walking areas
436
const double walkingareaSpeed = OptionsCont::getOptions().getFloat("default.walkingarea-speed");
437
const std::vector<NBNode::WalkingArea>& WalkingAreas = n.getWalkingAreas();
438
for (std::vector<NBNode::WalkingArea>::const_iterator it = WalkingAreas.begin(); it != WalkingAreas.end(); it++) {
439
const NBNode::WalkingArea& wa = *it;
440
into.openTag(SUMO_TAG_EDGE);
441
into.writeAttr(SUMO_ATTR_ID, wa.id);
442
into.writeAttr(SUMO_ATTR_FUNCTION, SumoXMLEdgeFunc::WALKINGAREA);
443
writeLane(into, wa.id + "_0", walkingareaSpeed, NBEdge::UNSPECIFIED_FRICTION, SVC_PEDESTRIAN, 0, SVCAll, SVCAll,
444
NBEdge::UNSPECIFIED_OFFSET, NBEdge::UNSPECIFIED_OFFSET,
445
StopOffset(), wa.width, wa.shape, nullptr, wa.length, 0, "", "", false, wa.hasCustomShape);
446
into.closeTag();
447
}
448
return ret;
449
}
450
451
452
std::string
453
NWWriter_SUMO::getInternalBidi(const NBEdge* e, const NBEdge::Connection& k, double& length) {
454
const NBEdge* fromBidi = e->getTurnDestination(true);
455
const NBEdge* toBidi = k.toEdge->getTurnDestination(true);
456
const std::vector<NBEdge::Connection> cons = toBidi->getConnectionsFromLane(-1, fromBidi, -1);
457
if (cons.size() > 0) {
458
if (e->getNumLanes() == 1 && k.toEdge->getNumLanes() == 1 && fromBidi->getNumLanes() == 1 && toBidi->getNumLanes() == 1) {
459
length = (k.length + cons.back().length) / 2;
460
return cons.back().id;
461
}
462
// do a more careful check in case there are parallel internal edges
463
// note: k is the first connection with the new id
464
for (const NBEdge::Connection& c : e->getConnections()) {
465
if (c.id == k.id) {
466
PositionVector rShape = c.shape.reverse();
467
for (const NBEdge::Connection& k2 : cons) {
468
if (k2.shape.almostSame(rShape, POSITION_EPS)) {
469
length = (c.length + k2.length) / 2;
470
return k2.id;
471
}
472
}
473
}
474
}
475
} else {
476
WRITE_WARNINGF(TL("Could not find bidi-connection for edge '%'"), k.id)
477
}
478
return "";
479
}
480
481
void
482
NWWriter_SUMO::writeEdge(OutputDevice& into, const NBEdge& e, bool noNames, LaneSpreadFunction defaultSpread) {
483
// write the edge's begin
484
into.openTag(SUMO_TAG_EDGE).writeAttr(SUMO_ATTR_ID, e.getID());
485
into.writeAttr(SUMO_ATTR_FROM, e.getFromNode()->getID());
486
into.writeAttr(SUMO_ATTR_TO, e.getToNode()->getID());
487
if (!noNames && e.getStreetName() != "") {
488
into.writeAttr(SUMO_ATTR_NAME, StringUtils::escapeXML(e.getStreetName()));
489
}
490
into.writeAttr(SUMO_ATTR_PRIORITY, e.getPriority());
491
if (e.getTypeID() != "") {
492
into.writeAttr(SUMO_ATTR_TYPE, e.getTypeID());
493
}
494
if (e.isMacroscopicConnector()) {
495
into.writeAttr(SUMO_ATTR_FUNCTION, SumoXMLEdgeFunc::CONNECTOR);
496
}
497
// write the spread type if not default ("right")
498
if (e.getLaneSpreadFunction() != defaultSpread) {
499
into.writeAttr(SUMO_ATTR_SPREADTYPE, e.getLaneSpreadFunction());
500
}
501
if (e.hasLoadedLength()) {
502
into.writeAttr(SUMO_ATTR_LENGTH, e.getLoadedLength());
503
}
504
if (!e.hasDefaultGeometry()) {
505
into.writeAttr(SUMO_ATTR_SHAPE, e.getGeometry());
506
}
507
if (e.getEdgeStopOffset().isDefined()) {
508
writeStopOffsets(into, e.getEdgeStopOffset());
509
}
510
if (e.getBidiEdge()) {
511
into.writeAttr(SUMO_ATTR_BIDI, e.getBidiEdge()->getID());
512
}
513
if (e.getDistance() != 0) {
514
into.writeAttr(SUMO_ATTR_DISTANCE, e.getDistance());
515
}
516
517
// write the lanes
518
const std::vector<NBEdge::Lane>& lanes = e.getLanes();
519
520
double length = e.getFinalLength();
521
if (e.getBidiEdge() != nullptr) {
522
length = (length + e.getBidiEdge()->getFinalLength()) / 2;
523
}
524
double startOffset = e.isBidiRail() ? e.getTurnDestination(true)->getEndOffset() : 0;
525
for (int i = 0; i < (int) lanes.size(); i++) {
526
const NBEdge::Lane& l = lanes[i];
527
StopOffset stopOffset;
528
if (l.laneStopOffset != e.getEdgeStopOffset()) {
529
stopOffset = l.laneStopOffset;
530
}
531
writeLane(into, e.getLaneID(i), l.speed, l.friction,
532
l.permissions, l.preferred,
533
l.changeLeft, l.changeRight,
534
startOffset, l.endOffset,
535
stopOffset, l.width, l.shape, &l,
536
length, i, l.oppositeID, l.type, l.accelRamp, l.customShape.size() > 0);
537
}
538
// close the edge
539
e.writeParams(into);
540
into.closeTag();
541
}
542
543
544
void
545
NWWriter_SUMO::writeLane(OutputDevice& into, const std::string& lID,
546
double speed, double friction,
547
SVCPermissions permissions, SVCPermissions preferred,
548
SVCPermissions changeLeft, SVCPermissions changeRight,
549
double startOffset, double endOffset,
550
const StopOffset& stopOffset, double width, PositionVector shape,
551
const Parameterised* params, double length, int index,
552
const std::string& oppositeID,
553
const std::string& type,
554
bool accelRamp, bool customShape,
555
const PositionVector& outlineShape) {
556
// output the lane's attributes
557
into.openTag(SUMO_TAG_LANE).writeAttr(SUMO_ATTR_ID, lID);
558
// the first lane of an edge will be the depart lane
559
into.writeAttr(SUMO_ATTR_INDEX, index);
560
// write the list of allowed/disallowed vehicle classes
561
if (permissions != SVC_UNSPECIFIED) {
562
writePermissions(into, permissions);
563
}
564
writePreferences(into, preferred);
565
// some further information
566
into.writeAttr(SUMO_ATTR_SPEED, MAX2(0.0, speed));
567
if (friction != NBEdge::UNSPECIFIED_FRICTION) {
568
into.writeAttr(SUMO_ATTR_FRICTION, friction);
569
}
570
into.writeAttr(SUMO_ATTR_LENGTH, length);
571
if (endOffset != NBEdge::UNSPECIFIED_OFFSET) {
572
into.writeAttr(SUMO_ATTR_ENDOFFSET, endOffset);
573
}
574
if (width != NBEdge::UNSPECIFIED_WIDTH) {
575
into.writeAttr(SUMO_ATTR_WIDTH, width);
576
}
577
if (accelRamp) {
578
into.writeAttr<bool>(SUMO_ATTR_ACCELERATION, accelRamp);
579
}
580
if (customShape) {
581
into.writeAttr(SUMO_ATTR_CUSTOMSHAPE, true);
582
}
583
if (endOffset > 0 || startOffset > 0) {
584
startOffset = MIN2(startOffset, shape.length() - POSITION_EPS);
585
endOffset = MIN2(endOffset, shape.length() - startOffset - POSITION_EPS);
586
assert(startOffset + endOffset < shape.length());
587
shape = shape.getSubpart(startOffset, shape.length() - endOffset);
588
}
589
into.writeAttr(SUMO_ATTR_SHAPE, shape);
590
if (type != "") {
591
into.writeAttr(SUMO_ATTR_TYPE, type);
592
}
593
if (changeLeft != SVC_UNSPECIFIED && changeLeft != SVCAll && changeLeft != SVC_IGNORING) {
594
into.writeAttr(SUMO_ATTR_CHANGE_LEFT, getVehicleClassNames(changeLeft));
595
}
596
if (changeRight != SVC_UNSPECIFIED && changeRight != SVCAll && changeRight != SVC_IGNORING) {
597
into.writeAttr(SUMO_ATTR_CHANGE_RIGHT, getVehicleClassNames(changeRight));
598
}
599
if (stopOffset.isDefined()) {
600
writeStopOffsets(into, stopOffset);
601
}
602
if (outlineShape.size() != 0) {
603
into.writeAttr(SUMO_ATTR_OUTLINESHAPE, outlineShape);
604
}
605
606
if (oppositeID != "" && oppositeID != "-") {
607
into.openTag(SUMO_TAG_NEIGH);
608
into.writeAttr(SUMO_ATTR_LANE, oppositeID);
609
into.closeTag();
610
}
611
612
if (params != nullptr) {
613
params->writeParams(into);
614
}
615
616
into.closeTag();
617
}
618
619
620
void
621
NWWriter_SUMO::writeJunction(OutputDevice& into, const NBNode& n) {
622
// write the attributes
623
into.openTag(SUMO_TAG_JUNCTION).writeAttr(SUMO_ATTR_ID, n.getID());
624
into.writeAttr(SUMO_ATTR_TYPE, n.getType());
625
NWFrame::writePositionLong(n.getPosition(), into);
626
// write the incoming lanes
627
std::vector<std::string> incLanes;
628
const std::vector<NBEdge*>& incoming = n.getIncomingEdges();
629
for (std::vector<NBEdge*>::const_iterator i = incoming.begin(); i != incoming.end(); ++i) {
630
int noLanes = (*i)->getNumLanes();
631
for (int j = 0; j < noLanes; j++) {
632
incLanes.push_back((*i)->getLaneID(j));
633
}
634
}
635
std::vector<NBNode::Crossing*> crossings = n.getCrossings();
636
std::set<std::string> prevWAs;
637
// avoid duplicates
638
for (auto c : crossings) {
639
if (prevWAs.count(c->prevWalkingArea) == 0) {
640
incLanes.push_back(c->prevWalkingArea + "_0");
641
prevWAs.insert(c->prevWalkingArea);
642
}
643
}
644
into.writeAttr(SUMO_ATTR_INCLANES, incLanes);
645
// write the internal lanes
646
std::vector<std::string> intLanes;
647
if (!OptionsCont::getOptions().getBool("no-internal-links")) {
648
for (EdgeVector::const_iterator i = incoming.begin(); i != incoming.end(); i++) {
649
const std::vector<NBEdge::Connection>& elv = (*i)->getConnections();
650
for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
651
if ((*k).toEdge == nullptr) {
652
continue;
653
}
654
if (!(*k).haveVia) {
655
intLanes.push_back((*k).getInternalLaneID());
656
} else {
657
intLanes.push_back((*k).getInternalViaLaneID());
658
}
659
}
660
}
661
}
662
if (n.getType() != SumoXMLNodeType::DEAD_END && n.getType() != SumoXMLNodeType::NOJUNCTION) {
663
for (auto c : crossings) {
664
intLanes.push_back(c->id + "_0");
665
}
666
}
667
into.writeAttr(SUMO_ATTR_INTLANES, intLanes);
668
// close writing
669
into.writeAttr(SUMO_ATTR_SHAPE, n.getShape().simplified());
670
// write optional radius
671
if (n.getRadius() != NBNode::UNSPECIFIED_RADIUS) {
672
into.writeAttr(SUMO_ATTR_RADIUS, n.getRadius());
673
}
674
// specify whether a custom shape was used
675
if (n.hasCustomShape()) {
676
into.writeAttr(SUMO_ATTR_CUSTOMSHAPE, true);
677
}
678
if (n.getRightOfWay() != RightOfWay::DEFAULT) {
679
into.writeAttr<std::string>(SUMO_ATTR_RIGHT_OF_WAY, toString(n.getRightOfWay()));
680
}
681
if (n.getFringeType() != FringeType::DEFAULT) {
682
into.writeAttr<std::string>(SUMO_ATTR_FRINGE, toString(n.getFringeType()));
683
}
684
if (n.getName() != "") {
685
into.writeAttr<std::string>(SUMO_ATTR_NAME, StringUtils::escapeXML(n.getName()));
686
}
687
if (n.getType() != SumoXMLNodeType::DEAD_END) {
688
// write right-of-way logics
689
n.writeLogic(into);
690
}
691
n.writeParams(into);
692
into.closeTag();
693
}
694
695
696
bool
697
NWWriter_SUMO::writeInternalNodes(OutputDevice& into, const NBNode& n) {
698
bool ret = false;
699
const std::vector<NBEdge*>& incoming = n.getIncomingEdges();
700
// build the list of internal lane ids
701
std::vector<std::string> internalLaneIDs;
702
std::map<std::string, std::string> viaIDs;
703
for (const NBEdge* in : incoming) {
704
for (const auto& con : in->getConnections()) {
705
if (con.toEdge != nullptr) {
706
internalLaneIDs.push_back(con.getInternalLaneID());
707
if (con.viaID != "") {
708
viaIDs[con.getInternalLaneID()] = (con.getInternalViaLaneID());
709
}
710
}
711
}
712
}
713
for (auto c : n.getCrossings()) {
714
internalLaneIDs.push_back(c->id + "_0");
715
}
716
// write the internal nodes
717
for (const NBEdge* in : incoming) {
718
for (const auto& con : in->getConnections()) {
719
if (con.toEdge == nullptr || !con.haveVia) {
720
continue;
721
}
722
Position pos = con.shape[-1];
723
into.openTag(SUMO_TAG_JUNCTION).writeAttr(SUMO_ATTR_ID, con.getInternalViaLaneID());
724
into.writeAttr(SUMO_ATTR_TYPE, SumoXMLNodeType::INTERNAL);
725
NWFrame::writePositionLong(pos, into);
726
std::string incLanes = con.getInternalLaneID();
727
std::vector<std::string> foeIDs;
728
for (std::string incLane : con.foeIncomingLanes) {
729
if (incLane[0] == ':') {
730
// intersecting left turns
731
const int index = StringUtils::toInt(incLane.substr(1));
732
incLane = internalLaneIDs[index];
733
if (viaIDs[incLane] != "") {
734
foeIDs.push_back(viaIDs[incLane]);
735
}
736
}
737
incLanes += " " + incLane;
738
}
739
into.writeAttr(SUMO_ATTR_INCLANES, incLanes);
740
const std::vector<int>& foes = con.foeInternalLinks;
741
for (int foe : foes) {
742
foeIDs.push_back(internalLaneIDs[foe]);
743
}
744
into.writeAttr(SUMO_ATTR_INTLANES, joinToString(foeIDs, " "));
745
into.closeTag();
746
ret = true;
747
}
748
}
749
return ret;
750
}
751
752
753
void
754
NWWriter_SUMO::writeConnection(OutputDevice& into, const NBEdge& from, const NBEdge::Connection& c,
755
bool includeInternal, ConnectionStyle style, bool geoAccuracy) {
756
assert(c.toEdge != 0);
757
into.openTag(SUMO_TAG_CONNECTION);
758
into.writeAttr(SUMO_ATTR_FROM, from.getID());
759
into.writeAttr(SUMO_ATTR_TO, c.toEdge->getID());
760
into.writeAttr(SUMO_ATTR_FROM_LANE, c.fromLane);
761
into.writeAttr(SUMO_ATTR_TO_LANE, c.toLane);
762
if (style != TLL) {
763
if (c.mayDefinitelyPass) {
764
into.writeAttr(SUMO_ATTR_PASS, c.mayDefinitelyPass);
765
}
766
if (c.keepClear == KEEPCLEAR_FALSE) {
767
into.writeAttr<bool>(SUMO_ATTR_KEEP_CLEAR, false);
768
}
769
if (c.contPos != NBEdge::UNSPECIFIED_CONTPOS) {
770
into.writeAttr(SUMO_ATTR_CONTPOS, c.contPos);
771
}
772
if (c.permissions != SVC_UNSPECIFIED) {
773
writePermissions(into, c.permissions);
774
}
775
if (c.changeLeft != SVC_UNSPECIFIED && c.changeLeft != SVCAll && c.changeLeft != SVC_IGNORING) {
776
into.writeAttr(SUMO_ATTR_CHANGE_LEFT, getVehicleClassNames(c.changeLeft));
777
}
778
if (c.changeRight != SVC_UNSPECIFIED && c.changeRight != SVCAll && c.changeRight != SVC_IGNORING) {
779
into.writeAttr(SUMO_ATTR_CHANGE_RIGHT, getVehicleClassNames(c.changeRight));
780
}
781
if (c.speed != NBEdge::UNSPECIFIED_SPEED) {
782
into.writeAttr(SUMO_ATTR_SPEED, c.speed);
783
}
784
if (c.customLength != NBEdge::UNSPECIFIED_LOADED_LENGTH) {
785
into.writeAttr(SUMO_ATTR_LENGTH, c.customLength);
786
}
787
if (c.customShape.size() != 0) {
788
if (geoAccuracy) {
789
into.setPrecision(gPrecisionGeo);
790
}
791
into.writeAttr(SUMO_ATTR_SHAPE, c.customShape);
792
if (geoAccuracy) {
793
into.setPrecision();
794
}
795
}
796
if (c.uncontrolled != false) {
797
into.writeAttr(SUMO_ATTR_UNCONTROLLED, c.uncontrolled);
798
}
799
if (c.indirectLeft != false) {
800
into.writeAttr(SUMO_ATTR_INDIRECT, c.indirectLeft);
801
}
802
if (c.edgeType != "") {
803
into.writeAttr(SUMO_ATTR_TYPE, c.edgeType);
804
}
805
}
806
if (style != PLAIN) {
807
if (includeInternal) {
808
into.writeAttr(SUMO_ATTR_VIA, c.getInternalLaneID());
809
}
810
// set information about the controlling tl if any
811
if (c.tlID != "") {
812
into.writeAttr(SUMO_ATTR_TLID, c.tlID);
813
into.writeAttr(SUMO_ATTR_TLLINKINDEX, c.tlLinkIndex);
814
if (c.tlLinkIndex2 >= 0) {
815
into.writeAttr(SUMO_ATTR_TLLINKINDEX2, c.tlLinkIndex2);
816
}
817
}
818
}
819
if (style != TLL) {
820
if (style == SUMONET) {
821
// write the direction information
822
LinkDirection dir = from.getToNode()->getDirection(&from, c.toEdge, OptionsCont::getOptions().getBool("lefthand"));
823
assert(dir != LinkDirection::NODIR);
824
into.writeAttr(SUMO_ATTR_DIR, toString(dir));
825
// write the state information
826
const LinkState linkState = from.getToNode()->getLinkState(
827
&from, c.toEdge, c.fromLane, c.toLane, c.mayDefinitelyPass, c.tlID);
828
into.writeAttr(SUMO_ATTR_STATE, linkState);
829
if (linkState == LINKSTATE_MINOR
830
&& c.visibility == NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE
831
&& c.toEdge->getJunctionPriority(c.toEdge->getToNode()) == NBEdge::JunctionPriority::ROUNDABOUT) {
832
const double visibilityDistance = OptionsCont::getOptions().getFloat("roundabouts.visibility-distance");
833
if (visibilityDistance != NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE) {
834
into.writeAttr(SUMO_ATTR_VISIBILITY_DISTANCE, visibilityDistance);
835
}
836
}
837
}
838
if (c.visibility != NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE) {
839
into.writeAttr(SUMO_ATTR_VISIBILITY_DISTANCE, c.visibility);
840
}
841
}
842
c.writeParams(into);
843
into.closeTag();
844
}
845
846
847
bool
848
NWWriter_SUMO::writeInternalConnections(OutputDevice& into, const NBNode& n) {
849
bool ret = false;
850
const bool lefthand = OptionsCont::getOptions().getBool("lefthand");
851
for (const NBEdge* const from : n.getIncomingEdges()) {
852
for (const NBEdge::Connection& c : from->getConnections()) {
853
LinkDirection dir = n.getDirection(from, c.toEdge, lefthand);
854
assert(c.toEdge != 0);
855
if (c.haveVia) {
856
// internal split with optional signal
857
std::string tlID = "";
858
int linkIndex2 = NBConnection::InvalidTlIndex;
859
if (c.tlLinkIndex2 != NBConnection::InvalidTlIndex) {
860
linkIndex2 = c.tlLinkIndex2;
861
tlID = c.tlID;
862
}
863
writeInternalConnection(into, c.id, c.toEdge->getID(), c.internalLaneIndex, c.toLane, c.getInternalViaLaneID(), dir, tlID, linkIndex2, false, c.visibility);
864
writeInternalConnection(into, c.viaID, c.toEdge->getID(), c.internalViaLaneIndex, c.toLane, "", dir, "", NBConnection::InvalidTlIndex,
865
n.brakeForCrossingOnExit(c.toEdge, dir, c.indirectLeft));
866
} else {
867
// no internal split
868
writeInternalConnection(into, c.id, c.toEdge->getID(), c.internalLaneIndex, c.toLane, "", dir);
869
}
870
ret = true;
871
}
872
}
873
return ret;
874
}
875
876
877
void
878
NWWriter_SUMO::writeInternalConnection(OutputDevice& into,
879
const std::string& from, const std::string& to,
880
int fromLane, int toLane, const std::string& via,
881
LinkDirection dir,
882
const std::string& tlID, int linkIndex,
883
bool minor,
884
double visibility) {
885
into.openTag(SUMO_TAG_CONNECTION);
886
into.writeAttr(SUMO_ATTR_FROM, from);
887
into.writeAttr(SUMO_ATTR_TO, to);
888
into.writeAttr(SUMO_ATTR_FROM_LANE, fromLane);
889
into.writeAttr(SUMO_ATTR_TO_LANE, toLane);
890
if (via != "") {
891
into.writeAttr(SUMO_ATTR_VIA, via);
892
}
893
if (tlID != "" && linkIndex != NBConnection::InvalidTlIndex) {
894
// used for the reverse direction of pedestrian crossings
895
into.writeAttr(SUMO_ATTR_TLID, tlID);
896
into.writeAttr(SUMO_ATTR_TLLINKINDEX, linkIndex);
897
}
898
into.writeAttr(SUMO_ATTR_DIR, dir);
899
into.writeAttr(SUMO_ATTR_STATE, ((via != "" || minor) ? "m" : "M"));
900
if (visibility != NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE) {
901
into.writeAttr(SUMO_ATTR_VISIBILITY_DISTANCE, visibility);
902
}
903
into.closeTag();
904
}
905
906
907
void
908
NWWriter_SUMO::writeRoundabouts(OutputDevice& into, const std::set<EdgeSet>& roundabouts,
909
const NBEdgeCont& ec) {
910
// make output deterministic
911
std::vector<std::vector<std::string> > edgeIDs;
912
for (std::set<EdgeSet>::const_iterator i = roundabouts.begin(); i != roundabouts.end(); ++i) {
913
std::vector<std::string> tEdgeIDs;
914
for (EdgeSet::const_iterator j = (*i).begin(); j != (*i).end(); ++j) {
915
// the edges may have been erased from NBEdgeCont but their pointers are still valid
916
// we verify their existance in writeRoundabout()
917
tEdgeIDs.push_back((*j)->getID());
918
}
919
std::sort(tEdgeIDs.begin(), tEdgeIDs.end());
920
edgeIDs.push_back(tEdgeIDs);
921
}
922
std::sort(edgeIDs.begin(), edgeIDs.end());
923
// write
924
for (std::vector<std::vector<std::string> >::const_iterator i = edgeIDs.begin(); i != edgeIDs.end(); ++i) {
925
writeRoundabout(into, *i, ec);
926
}
927
if (roundabouts.size() != 0) {
928
into.lf();
929
}
930
}
931
932
933
void
934
NWWriter_SUMO::writeRoundabout(OutputDevice& into, const std::vector<std::string>& edgeIDs,
935
const NBEdgeCont& ec) {
936
std::vector<std::string> validEdgeIDs;
937
std::vector<std::string> invalidEdgeIDs;
938
std::vector<std::string> nodeIDs;
939
for (std::vector<std::string>::const_iterator i = edgeIDs.begin(); i != edgeIDs.end(); ++i) {
940
const NBEdge* edge = ec.retrieve(*i);
941
if (edge != nullptr) {
942
nodeIDs.push_back(edge->getToNode()->getID());
943
validEdgeIDs.push_back(edge->getID());
944
} else {
945
invalidEdgeIDs.push_back(*i);
946
}
947
}
948
std::sort(nodeIDs.begin(), nodeIDs.end());
949
if (validEdgeIDs.size() > 0) {
950
into.openTag(SUMO_TAG_ROUNDABOUT);
951
into.writeAttr(SUMO_ATTR_NODES, joinToString(nodeIDs, " "));
952
into.writeAttr(SUMO_ATTR_EDGES, joinToString(validEdgeIDs, " "));
953
into.closeTag();
954
if (invalidEdgeIDs.size() > 0) {
955
WRITE_WARNING("Writing incomplete roundabout. Edges: '"
956
+ joinToString(invalidEdgeIDs, " ") + "' no longer exist'");
957
}
958
}
959
}
960
961
962
void
963
NWWriter_SUMO::writeDistrict(OutputDevice& into, const NBDistrict& d) {
964
std::vector<double> sourceW = d.getSourceWeights();
965
VectorHelper<double>::normaliseSum(sourceW, 1.0);
966
std::vector<double> sinkW = d.getSinkWeights();
967
VectorHelper<double>::normaliseSum(sinkW, 1.0);
968
// write the head and the id of the district
969
into.openTag(SUMO_TAG_TAZ).writeAttr(SUMO_ATTR_ID, d.getID());
970
if (d.getShape().size() > 0) {
971
into.writeAttr(SUMO_ATTR_SHAPE, d.getShape());
972
}
973
// write all sources
974
const std::vector<NBEdge*>& sources = d.getSourceEdges();
975
for (int i = 0; i < (int)sources.size(); i++) {
976
// write the head and the id of the source
977
into.openTag(SUMO_TAG_TAZSOURCE).writeAttr(SUMO_ATTR_ID, sources[i]->getID()).writeAttr(SUMO_ATTR_WEIGHT, sourceW[i]);
978
into.closeTag();
979
}
980
// write all sinks
981
const std::vector<NBEdge*>& sinks = d.getSinkEdges();
982
for (int i = 0; i < (int)sinks.size(); i++) {
983
// write the head and the id of the sink
984
into.openTag(SUMO_TAG_TAZSINK).writeAttr(SUMO_ATTR_ID, sinks[i]->getID()).writeAttr(SUMO_ATTR_WEIGHT, sinkW[i]);
985
into.closeTag();
986
}
987
// write the tail
988
into.closeTag();
989
}
990
991
992
std::string
993
NWWriter_SUMO::writeSUMOTime(SUMOTime steps) {
994
double time = STEPS2TIME(steps);
995
if (time == std::floor(time)) {
996
return toString(int(time));
997
} else {
998
return toString(time);
999
}
1000
}
1001
1002
void
1003
NWWriter_SUMO::writeProhibitions(OutputDevice& into, const NBConnectionProhibits& prohibitions) {
1004
for (NBConnectionProhibits::const_iterator j = prohibitions.begin(); j != prohibitions.end(); j++) {
1005
NBConnection prohibited = (*j).first;
1006
const NBConnectionVector& prohibiting = (*j).second;
1007
for (NBConnectionVector::const_iterator k = prohibiting.begin(); k != prohibiting.end(); k++) {
1008
NBConnection prohibitor = *k;
1009
into.openTag(SUMO_TAG_PROHIBITION);
1010
into.writeAttr(SUMO_ATTR_PROHIBITOR, prohibitionConnection(prohibitor));
1011
into.writeAttr(SUMO_ATTR_PROHIBITED, prohibitionConnection(prohibited));
1012
into.closeTag();
1013
}
1014
}
1015
}
1016
1017
1018
std::string
1019
NWWriter_SUMO::prohibitionConnection(const NBConnection& c) {
1020
return c.getFrom()->getID() + "->" + c.getTo()->getID();
1021
}
1022
1023
1024
void
1025
NWWriter_SUMO::writeTrafficLights(OutputDevice& into, const NBTrafficLightLogicCont& tllCont) {
1026
std::vector<NBTrafficLightLogic*> logics = tllCont.getComputed();
1027
for (NBTrafficLightLogic* logic : logics) {
1028
writeTrafficLight(into, logic);
1029
// only raise warnings on write instead of on compute (to avoid cluttering netedit)
1030
NBTrafficLightDefinition* def = tllCont.getDefinition(logic->getID(), logic->getProgramID());
1031
assert(def != nullptr);
1032
def->finalChecks();
1033
}
1034
if (logics.size() > 0) {
1035
into.lf();
1036
}
1037
}
1038
1039
1040
void
1041
NWWriter_SUMO::writeTrafficLight(OutputDevice& into, const NBTrafficLightLogic* logic) {
1042
into.openTag(SUMO_TAG_TLLOGIC);
1043
into.writeAttr(SUMO_ATTR_ID, logic->getID());
1044
into.writeAttr(SUMO_ATTR_TYPE, logic->getType());
1045
into.writeAttr(SUMO_ATTR_PROGRAMID, logic->getProgramID());
1046
into.writeAttr(SUMO_ATTR_OFFSET, logic->getOffset() == SUMOTime_MAX ? "begin" : writeSUMOTime(logic->getOffset()));
1047
// write the phases
1048
const bool varPhaseLength = logic->getType() != TrafficLightType::STATIC;
1049
for (const NBTrafficLightLogic::PhaseDefinition& phase : logic->getPhases()) {
1050
into.openTag(SUMO_TAG_PHASE);
1051
into.writeAttr(SUMO_ATTR_DURATION, writeSUMOTime(phase.duration));
1052
if (phase.duration < TIME2STEPS(10)) {
1053
into.writePadding(" ");
1054
}
1055
into.writeAttr(SUMO_ATTR_STATE, phase.state);
1056
if (varPhaseLength) {
1057
if (phase.minDur != NBTrafficLightDefinition::UNSPECIFIED_DURATION) {
1058
into.writeAttr(SUMO_ATTR_MINDURATION, writeSUMOTime(phase.minDur));
1059
}
1060
if (phase.maxDur != NBTrafficLightDefinition::UNSPECIFIED_DURATION) {
1061
into.writeAttr(SUMO_ATTR_MAXDURATION, writeSUMOTime(phase.maxDur));
1062
}
1063
if (phase.earliestEnd != NBTrafficLightDefinition::UNSPECIFIED_DURATION) {
1064
into.writeAttr(SUMO_ATTR_EARLIEST_END, writeSUMOTime(phase.earliestEnd));
1065
}
1066
if (phase.latestEnd != NBTrafficLightDefinition::UNSPECIFIED_DURATION) {
1067
into.writeAttr(SUMO_ATTR_LATEST_END, writeSUMOTime(phase.latestEnd));
1068
}
1069
// NEMA attributes
1070
if (phase.vehExt != NBTrafficLightDefinition::UNSPECIFIED_DURATION) {
1071
into.writeAttr(SUMO_ATTR_VEHICLEEXTENSION, writeSUMOTime(phase.vehExt));
1072
}
1073
if (phase.yellow != NBTrafficLightDefinition::UNSPECIFIED_DURATION) {
1074
into.writeAttr(SUMO_ATTR_YELLOW, writeSUMOTime(phase.yellow));
1075
}
1076
if (phase.red != NBTrafficLightDefinition::UNSPECIFIED_DURATION) {
1077
into.writeAttr(SUMO_ATTR_RED, writeSUMOTime(phase.red));
1078
}
1079
}
1080
if (phase.name != "") {
1081
into.writeAttr(SUMO_ATTR_NAME, StringUtils::escapeXML(phase.name));
1082
}
1083
if (phase.next.size() > 0) {
1084
into.writeAttr(SUMO_ATTR_NEXT, phase.next);
1085
}
1086
into.closeTag();
1087
}
1088
// write params
1089
logic->writeParams(into);
1090
into.closeTag();
1091
}
1092
1093
1094
void
1095
NWWriter_SUMO::writeStopOffsets(OutputDevice& into, const StopOffset& stopOffset) {
1096
if (stopOffset.isDefined()) {
1097
const std::string ss_vclasses = getVehicleClassNames(stopOffset.getPermissions());
1098
if (ss_vclasses.length() == 0) {
1099
// This stopOffset would have no effect...
1100
return;
1101
}
1102
into.openTag(SUMO_TAG_STOPOFFSET);
1103
const std::string ss_exceptions = getVehicleClassNames(~stopOffset.getPermissions());
1104
if (ss_vclasses.length() <= ss_exceptions.length()) {
1105
into.writeAttr(SUMO_ATTR_VCLASSES, ss_vclasses);
1106
} else {
1107
if (ss_exceptions.length() == 0) {
1108
into.writeAttr(SUMO_ATTR_VCLASSES, "all");
1109
} else {
1110
into.writeAttr(SUMO_ATTR_EXCEPTIONS, ss_exceptions);
1111
}
1112
}
1113
into.writeAttr(SUMO_ATTR_VALUE, stopOffset.getOffset());
1114
into.closeTag();
1115
}
1116
}
1117
1118
1119
/****************************************************************************/
1120
1121