Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netwrite/NWWriter_SUMO.cpp
193716 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2001-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 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(), ec);
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.getRoutingType() != "") {
495
into.writeAttr(SUMO_ATTR_ROUTINGTYPE, e.getRoutingType());
496
}
497
if (e.isMacroscopicConnector()) {
498
into.writeAttr(SUMO_ATTR_FUNCTION, SumoXMLEdgeFunc::CONNECTOR);
499
}
500
// write the spread type if not default ("right")
501
if (e.getLaneSpreadFunction() != defaultSpread) {
502
into.writeAttr(SUMO_ATTR_SPREADTYPE, e.getLaneSpreadFunction());
503
}
504
if (e.hasLoadedLength()) {
505
into.writeAttr(SUMO_ATTR_LENGTH, e.getLoadedLength());
506
}
507
if (!e.hasDefaultGeometry()) {
508
into.writeAttr(SUMO_ATTR_SHAPE, e.getGeometry());
509
}
510
if (e.getEdgeStopOffset().isDefined()) {
511
writeStopOffsets(into, e.getEdgeStopOffset());
512
}
513
if (e.getBidiEdge()) {
514
into.writeAttr(SUMO_ATTR_BIDI, e.getBidiEdge()->getID());
515
}
516
if (e.getDistance() != 0) {
517
into.writeAttr(SUMO_ATTR_DISTANCE, e.getDistance());
518
}
519
520
// write the lanes
521
const std::vector<NBEdge::Lane>& lanes = e.getLanes();
522
523
double length = e.getFinalLength();
524
if (e.getBidiEdge() != nullptr) {
525
length = (length + e.getBidiEdge()->getFinalLength()) / 2;
526
}
527
double startOffset = e.isBidiRail() ? e.getTurnDestination(true)->getEndOffset() : 0;
528
for (int i = 0; i < (int) lanes.size(); i++) {
529
const NBEdge::Lane& l = lanes[i];
530
StopOffset stopOffset;
531
if (l.laneStopOffset != e.getEdgeStopOffset()) {
532
stopOffset = l.laneStopOffset;
533
}
534
writeLane(into, e.getLaneID(i), l.speed, l.friction,
535
l.permissions, l.preferred,
536
l.changeLeft, l.changeRight,
537
startOffset, l.endOffset,
538
stopOffset, l.width, l.shape, &l,
539
length, i, l.oppositeID, l.type, l.accelRamp, l.customShape.size() > 0);
540
}
541
// close the edge
542
e.writeParams(into);
543
into.closeTag();
544
}
545
546
547
void
548
NWWriter_SUMO::writeLane(OutputDevice& into, const std::string& lID,
549
double speed, double friction,
550
SVCPermissions permissions, SVCPermissions preferred,
551
SVCPermissions changeLeft, SVCPermissions changeRight,
552
double startOffset, double endOffset,
553
const StopOffset& stopOffset, double width, PositionVector shape,
554
const Parameterised* params, double length, int index,
555
const std::string& oppositeID,
556
const std::string& type,
557
bool accelRamp, bool customShape,
558
const PositionVector& outlineShape) {
559
// output the lane's attributes
560
into.openTag(SUMO_TAG_LANE).writeAttr(SUMO_ATTR_ID, lID);
561
// the first lane of an edge will be the depart lane
562
into.writeAttr(SUMO_ATTR_INDEX, index);
563
// write the list of allowed/disallowed vehicle classes
564
if (permissions != SVC_UNSPECIFIED) {
565
writePermissions(into, permissions);
566
}
567
writePreferences(into, preferred);
568
// some further information
569
into.writeAttr(SUMO_ATTR_SPEED, MAX2(0.0, speed));
570
if (friction != NBEdge::UNSPECIFIED_FRICTION) {
571
into.writeAttr(SUMO_ATTR_FRICTION, friction);
572
}
573
into.writeAttr(SUMO_ATTR_LENGTH, length);
574
if (endOffset != NBEdge::UNSPECIFIED_OFFSET) {
575
into.writeAttr(SUMO_ATTR_ENDOFFSET, endOffset);
576
}
577
if (width != NBEdge::UNSPECIFIED_WIDTH) {
578
into.writeAttr(SUMO_ATTR_WIDTH, width);
579
}
580
if (accelRamp) {
581
into.writeAttr<bool>(SUMO_ATTR_ACCELERATION, accelRamp);
582
}
583
if (customShape) {
584
into.writeAttr(SUMO_ATTR_CUSTOMSHAPE, true);
585
}
586
if (endOffset > 0 || startOffset > 0) {
587
startOffset = MIN2(startOffset, shape.length() - POSITION_EPS);
588
endOffset = MIN2(endOffset, shape.length() - startOffset - POSITION_EPS);
589
assert(startOffset + endOffset < shape.length());
590
shape = shape.getSubpart(startOffset, shape.length() - endOffset);
591
}
592
into.writeAttr(SUMO_ATTR_SHAPE, shape);
593
if (type != "") {
594
into.writeAttr(SUMO_ATTR_TYPE, type);
595
}
596
if (changeLeft != SVC_UNSPECIFIED && changeLeft != SVCAll && changeLeft != SVC_IGNORING) {
597
into.writeAttr(SUMO_ATTR_CHANGE_LEFT, getVehicleClassNames(changeLeft));
598
}
599
if (changeRight != SVC_UNSPECIFIED && changeRight != SVCAll && changeRight != SVC_IGNORING) {
600
into.writeAttr(SUMO_ATTR_CHANGE_RIGHT, getVehicleClassNames(changeRight));
601
}
602
if (stopOffset.isDefined()) {
603
writeStopOffsets(into, stopOffset);
604
}
605
if (outlineShape.size() != 0) {
606
into.writeAttr(SUMO_ATTR_OUTLINESHAPE, outlineShape);
607
}
608
609
if (oppositeID != "" && oppositeID != "-") {
610
into.openTag(SUMO_TAG_NEIGH);
611
into.writeAttr(SUMO_ATTR_LANE, oppositeID);
612
into.closeTag();
613
}
614
615
if (params != nullptr) {
616
params->writeParams(into);
617
}
618
619
into.closeTag();
620
}
621
622
623
void
624
NWWriter_SUMO::writeJunction(OutputDevice& into, const NBNode& n) {
625
// write the attributes
626
into.openTag(SUMO_TAG_JUNCTION).writeAttr(SUMO_ATTR_ID, n.getID());
627
into.writeAttr(SUMO_ATTR_TYPE, n.getType());
628
NWFrame::writePositionLong(n.getPosition(), into);
629
// write the incoming lanes
630
std::vector<std::string> incLanes;
631
const std::vector<NBEdge*>& incoming = n.getIncomingEdges();
632
for (std::vector<NBEdge*>::const_iterator i = incoming.begin(); i != incoming.end(); ++i) {
633
int noLanes = (*i)->getNumLanes();
634
for (int j = 0; j < noLanes; j++) {
635
incLanes.push_back((*i)->getLaneID(j));
636
}
637
}
638
std::vector<NBNode::Crossing*> crossings = n.getCrossings();
639
std::set<std::string> prevWAs;
640
// avoid duplicates
641
for (auto c : crossings) {
642
if (prevWAs.count(c->prevWalkingArea) == 0) {
643
incLanes.push_back(c->prevWalkingArea + "_0");
644
prevWAs.insert(c->prevWalkingArea);
645
}
646
}
647
into.writeAttr(SUMO_ATTR_INCLANES, incLanes);
648
// write the internal lanes
649
std::vector<std::string> intLanes;
650
if (!OptionsCont::getOptions().getBool("no-internal-links")) {
651
for (EdgeVector::const_iterator i = incoming.begin(); i != incoming.end(); i++) {
652
const std::vector<NBEdge::Connection>& elv = (*i)->getConnections();
653
for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
654
if ((*k).toEdge == nullptr) {
655
continue;
656
}
657
if (!(*k).haveVia) {
658
intLanes.push_back((*k).getInternalLaneID());
659
} else {
660
intLanes.push_back((*k).getInternalViaLaneID());
661
}
662
}
663
}
664
}
665
if (n.getType() != SumoXMLNodeType::DEAD_END && n.getType() != SumoXMLNodeType::NOJUNCTION) {
666
for (auto c : crossings) {
667
intLanes.push_back(c->id + "_0");
668
}
669
}
670
into.writeAttr(SUMO_ATTR_INTLANES, intLanes);
671
// close writing
672
into.writeAttr(SUMO_ATTR_SHAPE, n.getShape().simplified());
673
// write optional radius
674
if (n.getRadius() != NBNode::UNSPECIFIED_RADIUS) {
675
into.writeAttr(SUMO_ATTR_RADIUS, n.getRadius());
676
}
677
// specify whether a custom shape was used
678
if (n.hasCustomShape()) {
679
into.writeAttr(SUMO_ATTR_CUSTOMSHAPE, true);
680
}
681
if (n.getRightOfWay() != RightOfWay::DEFAULT) {
682
into.writeAttr<std::string>(SUMO_ATTR_RIGHT_OF_WAY, toString(n.getRightOfWay()));
683
}
684
if (n.getFringeType() != FringeType::DEFAULT) {
685
into.writeAttr<std::string>(SUMO_ATTR_FRINGE, toString(n.getFringeType()));
686
}
687
if (n.getRoundaboutType() != RoundaboutType::DEFAULT) {
688
into.writeAttr<std::string>(SUMO_ATTR_ROUNDABOUT, toString(n.getRoundaboutType()));
689
}
690
if (n.getName() != "") {
691
into.writeAttr<std::string>(SUMO_ATTR_NAME, StringUtils::escapeXML(n.getName()));
692
}
693
if (n.getType() != SumoXMLNodeType::DEAD_END) {
694
// write right-of-way logics
695
n.writeLogic(into);
696
}
697
n.writeParams(into);
698
into.closeTag();
699
}
700
701
702
bool
703
NWWriter_SUMO::writeInternalNodes(OutputDevice& into, const NBNode& n) {
704
bool ret = false;
705
const std::vector<NBEdge*>& incoming = n.getIncomingEdges();
706
// build the list of internal lane ids
707
std::vector<std::string> internalLaneIDs;
708
std::map<std::string, std::string> viaIDs;
709
for (const NBEdge* in : incoming) {
710
for (const auto& con : in->getConnections()) {
711
if (con.toEdge != nullptr) {
712
internalLaneIDs.push_back(con.getInternalLaneID());
713
if (con.viaID != "") {
714
viaIDs[con.getInternalLaneID()] = (con.getInternalViaLaneID());
715
}
716
}
717
}
718
}
719
for (auto c : n.getCrossings()) {
720
internalLaneIDs.push_back(c->id + "_0");
721
}
722
// write the internal nodes
723
for (const NBEdge* in : incoming) {
724
for (const auto& con : in->getConnections()) {
725
if (con.toEdge == nullptr || !con.haveVia) {
726
continue;
727
}
728
Position pos = con.shape[-1];
729
into.openTag(SUMO_TAG_JUNCTION).writeAttr(SUMO_ATTR_ID, con.getInternalViaLaneID());
730
into.writeAttr(SUMO_ATTR_TYPE, SumoXMLNodeType::INTERNAL);
731
NWFrame::writePositionLong(pos, into);
732
std::string incLanes = con.getInternalLaneID();
733
std::vector<std::string> foeIDs;
734
for (std::string incLane : con.foeIncomingLanes) {
735
if (incLane[0] == ':') {
736
// intersecting left turns
737
const int index = StringUtils::toInt(incLane.substr(1));
738
incLane = internalLaneIDs[index];
739
if (viaIDs[incLane] != "") {
740
foeIDs.push_back(viaIDs[incLane]);
741
}
742
}
743
incLanes += " " + incLane;
744
}
745
into.writeAttr(SUMO_ATTR_INCLANES, incLanes);
746
const std::vector<int>& foes = con.foeInternalLinks;
747
for (int foe : foes) {
748
foeIDs.push_back(internalLaneIDs[foe]);
749
}
750
into.writeAttr(SUMO_ATTR_INTLANES, joinToString(foeIDs, " "));
751
into.closeTag();
752
ret = true;
753
}
754
}
755
return ret;
756
}
757
758
759
void
760
NWWriter_SUMO::writeConnection(OutputDevice& into, const NBEdge& from, const NBEdge::Connection& c,
761
bool includeInternal, ConnectionStyle style, bool geoAccuracy) {
762
assert(c.toEdge != 0);
763
into.openTag(SUMO_TAG_CONNECTION);
764
into.writeAttr(SUMO_ATTR_FROM, from.getID());
765
into.writeAttr(SUMO_ATTR_TO, c.toEdge->getID());
766
into.writeAttr(SUMO_ATTR_FROM_LANE, c.fromLane);
767
into.writeAttr(SUMO_ATTR_TO_LANE, c.toLane);
768
if (style != TLL) {
769
if (c.mayDefinitelyPass) {
770
into.writeAttr(SUMO_ATTR_PASS, c.mayDefinitelyPass);
771
}
772
if (c.keepClear == KEEPCLEAR_FALSE) {
773
into.writeAttr<bool>(SUMO_ATTR_KEEP_CLEAR, false);
774
}
775
if (c.contPos != NBEdge::UNSPECIFIED_CONTPOS) {
776
into.writeAttr(SUMO_ATTR_CONTPOS, c.contPos);
777
}
778
if (c.permissions != SVC_UNSPECIFIED) {
779
writePermissions(into, c.permissions);
780
}
781
if (c.changeLeft != SVC_UNSPECIFIED && c.changeLeft != SVCAll && c.changeLeft != SVC_IGNORING) {
782
into.writeAttr(SUMO_ATTR_CHANGE_LEFT, getVehicleClassNames(c.changeLeft));
783
}
784
if (c.changeRight != SVC_UNSPECIFIED && c.changeRight != SVCAll && c.changeRight != SVC_IGNORING) {
785
into.writeAttr(SUMO_ATTR_CHANGE_RIGHT, getVehicleClassNames(c.changeRight));
786
}
787
if (c.speed != NBEdge::UNSPECIFIED_SPEED) {
788
into.writeAttr(SUMO_ATTR_SPEED, c.speed);
789
}
790
if (c.customLength != NBEdge::UNSPECIFIED_LOADED_LENGTH) {
791
into.writeAttr(SUMO_ATTR_LENGTH, c.customLength);
792
}
793
if (c.customShape.size() != 0) {
794
if (geoAccuracy) {
795
into.setPrecision(gPrecisionGeo);
796
}
797
into.writeAttr(SUMO_ATTR_SHAPE, c.customShape);
798
if (geoAccuracy) {
799
into.setPrecision();
800
}
801
}
802
if (c.uncontrolled != false) {
803
into.writeAttr(SUMO_ATTR_UNCONTROLLED, c.uncontrolled);
804
}
805
if (c.indirectLeft != false) {
806
into.writeAttr(SUMO_ATTR_INDIRECT, c.indirectLeft);
807
}
808
if (c.edgeType != "") {
809
into.writeAttr(SUMO_ATTR_TYPE, c.edgeType);
810
}
811
}
812
if (style != PLAIN) {
813
if (includeInternal) {
814
into.writeAttr(SUMO_ATTR_VIA, c.getInternalLaneID());
815
}
816
// set information about the controlling tl if any
817
if (c.tlID != "") {
818
into.writeAttr(SUMO_ATTR_TLID, c.tlID);
819
into.writeAttr(SUMO_ATTR_TLLINKINDEX, c.tlLinkIndex);
820
if (c.tlLinkIndex2 >= 0) {
821
into.writeAttr(SUMO_ATTR_TLLINKINDEX2, c.tlLinkIndex2);
822
}
823
}
824
}
825
if (style != TLL) {
826
if (style == SUMONET) {
827
// write the direction information
828
LinkDirection dir = from.getToNode()->getDirection(&from, c.toEdge, OptionsCont::getOptions().getBool("lefthand"));
829
assert(dir != LinkDirection::NODIR);
830
into.writeAttr(SUMO_ATTR_DIR, toString(dir));
831
// write the state information
832
const LinkState linkState = from.getToNode()->getLinkState(
833
&from, c.toEdge, c.fromLane, c.toLane, c.mayDefinitelyPass, c.tlID);
834
into.writeAttr(SUMO_ATTR_STATE, linkState);
835
if (linkState == LINKSTATE_MINOR
836
&& c.visibility == NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE
837
&& c.toEdge->getJunctionPriority(c.toEdge->getToNode()) == NBEdge::JunctionPriority::ROUNDABOUT) {
838
const double visibilityDistance = OptionsCont::getOptions().getFloat("roundabouts.visibility-distance");
839
if (visibilityDistance != NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE) {
840
into.writeAttr(SUMO_ATTR_VISIBILITY_DISTANCE, visibilityDistance);
841
}
842
}
843
}
844
if (c.visibility != NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE) {
845
into.writeAttr(SUMO_ATTR_VISIBILITY_DISTANCE, c.visibility);
846
}
847
}
848
c.writeParams(into);
849
into.closeTag();
850
}
851
852
853
bool
854
NWWriter_SUMO::writeInternalConnections(OutputDevice& into, const NBNode& n) {
855
bool ret = false;
856
const bool lefthand = OptionsCont::getOptions().getBool("lefthand");
857
for (const NBEdge* const from : n.getIncomingEdges()) {
858
for (const NBEdge::Connection& c : from->getConnections()) {
859
LinkDirection dir = n.getDirection(from, c.toEdge, lefthand);
860
assert(c.toEdge != 0);
861
if (c.haveVia) {
862
// internal split with optional signal
863
std::string tlID = "";
864
int linkIndex2 = NBConnection::InvalidTlIndex;
865
if (c.tlLinkIndex2 != NBConnection::InvalidTlIndex) {
866
linkIndex2 = c.tlLinkIndex2;
867
tlID = c.tlID;
868
}
869
writeInternalConnection(into, c.id, c.toEdge->getID(), c.internalLaneIndex, c.toLane, c.getInternalViaLaneID(), dir, tlID, linkIndex2, false, c.visibility);
870
writeInternalConnection(into, c.viaID, c.toEdge->getID(), c.internalViaLaneIndex, c.toLane, "", dir, "", NBConnection::InvalidTlIndex,
871
n.brakeForCrossingOnExit(c.toEdge, dir, c.indirectLeft));
872
} else {
873
// no internal split
874
writeInternalConnection(into, c.id, c.toEdge->getID(), c.internalLaneIndex, c.toLane, "", dir);
875
}
876
ret = true;
877
}
878
}
879
return ret;
880
}
881
882
883
void
884
NWWriter_SUMO::writeInternalConnection(OutputDevice& into,
885
const std::string& from, const std::string& to,
886
int fromLane, int toLane, const std::string& via,
887
LinkDirection dir,
888
const std::string& tlID, int linkIndex,
889
bool minor,
890
double visibility) {
891
into.openTag(SUMO_TAG_CONNECTION);
892
into.writeAttr(SUMO_ATTR_FROM, from);
893
into.writeAttr(SUMO_ATTR_TO, to);
894
into.writeAttr(SUMO_ATTR_FROM_LANE, fromLane);
895
into.writeAttr(SUMO_ATTR_TO_LANE, toLane);
896
if (via != "") {
897
into.writeAttr(SUMO_ATTR_VIA, via);
898
}
899
if (tlID != "" && linkIndex != NBConnection::InvalidTlIndex) {
900
// used for the reverse direction of pedestrian crossings
901
into.writeAttr(SUMO_ATTR_TLID, tlID);
902
into.writeAttr(SUMO_ATTR_TLLINKINDEX, linkIndex);
903
}
904
into.writeAttr(SUMO_ATTR_DIR, dir);
905
into.writeAttr(SUMO_ATTR_STATE, ((via != "" || minor) ? "m" : "M"));
906
if (visibility != NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE) {
907
into.writeAttr(SUMO_ATTR_VISIBILITY_DISTANCE, visibility);
908
}
909
into.closeTag();
910
}
911
912
913
void
914
NWWriter_SUMO::writeRoundabouts(OutputDevice& into, const std::set<EdgeSet>& roundabouts,
915
const NBEdgeCont& ec) {
916
// make output deterministic
917
std::vector<std::vector<std::string> > edgeIDs;
918
for (std::set<EdgeSet>::const_iterator i = roundabouts.begin(); i != roundabouts.end(); ++i) {
919
std::vector<std::string> tEdgeIDs;
920
for (EdgeSet::const_iterator j = (*i).begin(); j != (*i).end(); ++j) {
921
// the edges may have been erased from NBEdgeCont but their pointers are still valid
922
// we verify their existance in writeRoundabout()
923
tEdgeIDs.push_back((*j)->getID());
924
}
925
std::sort(tEdgeIDs.begin(), tEdgeIDs.end());
926
edgeIDs.push_back(tEdgeIDs);
927
}
928
std::sort(edgeIDs.begin(), edgeIDs.end());
929
// write
930
for (std::vector<std::vector<std::string> >::const_iterator i = edgeIDs.begin(); i != edgeIDs.end(); ++i) {
931
writeRoundabout(into, *i, ec);
932
}
933
if (roundabouts.size() != 0) {
934
into.lf();
935
}
936
}
937
938
939
void
940
NWWriter_SUMO::writeRoundabout(OutputDevice& into, const std::vector<std::string>& edgeIDs,
941
const NBEdgeCont& ec) {
942
std::vector<std::string> validEdgeIDs;
943
std::vector<std::string> invalidEdgeIDs;
944
std::vector<std::string> nodeIDs;
945
for (std::vector<std::string>::const_iterator i = edgeIDs.begin(); i != edgeIDs.end(); ++i) {
946
const NBEdge* edge = ec.retrieve(*i);
947
if (edge != nullptr) {
948
nodeIDs.push_back(edge->getToNode()->getID());
949
validEdgeIDs.push_back(edge->getID());
950
} else {
951
invalidEdgeIDs.push_back(*i);
952
}
953
}
954
std::sort(nodeIDs.begin(), nodeIDs.end());
955
if (validEdgeIDs.size() > 0) {
956
into.openTag(SUMO_TAG_ROUNDABOUT);
957
into.writeAttr(SUMO_ATTR_NODES, joinToString(nodeIDs, " "));
958
into.writeAttr(SUMO_ATTR_EDGES, joinToString(validEdgeIDs, " "));
959
into.closeTag();
960
if (invalidEdgeIDs.size() > 0) {
961
WRITE_WARNING("Writing incomplete roundabout. Edges: '"
962
+ joinToString(invalidEdgeIDs, " ") + "' no longer exist'");
963
}
964
}
965
}
966
967
968
void
969
NWWriter_SUMO::writeDistrict(OutputDevice& into, const NBDistrict& d) {
970
std::vector<double> sourceW = d.getSourceWeights();
971
VectorHelper<double>::normaliseSum(sourceW, 1.0);
972
std::vector<double> sinkW = d.getSinkWeights();
973
VectorHelper<double>::normaliseSum(sinkW, 1.0);
974
// write the head and the id of the district
975
into.openTag(SUMO_TAG_TAZ).writeAttr(SUMO_ATTR_ID, d.getID());
976
if (d.getShape().size() > 0) {
977
into.writeAttr(SUMO_ATTR_SHAPE, d.getShape());
978
}
979
// write all sources
980
const std::vector<NBEdge*>& sources = d.getSourceEdges();
981
for (int i = 0; i < (int)sources.size(); i++) {
982
// write the head and the id of the source
983
into.openTag(SUMO_TAG_TAZSOURCE).writeAttr(SUMO_ATTR_ID, sources[i]->getID()).writeAttr(SUMO_ATTR_WEIGHT, sourceW[i]);
984
into.closeTag();
985
}
986
// write all sinks
987
const std::vector<NBEdge*>& sinks = d.getSinkEdges();
988
for (int i = 0; i < (int)sinks.size(); i++) {
989
// write the head and the id of the sink
990
into.openTag(SUMO_TAG_TAZSINK).writeAttr(SUMO_ATTR_ID, sinks[i]->getID()).writeAttr(SUMO_ATTR_WEIGHT, sinkW[i]);
991
into.closeTag();
992
}
993
// write the tail
994
into.closeTag();
995
}
996
997
998
std::string
999
NWWriter_SUMO::writeSUMOTime(SUMOTime steps) {
1000
double time = STEPS2TIME(steps);
1001
if (time == std::floor(time)) {
1002
return toString(int(time));
1003
} else {
1004
return toString(time);
1005
}
1006
}
1007
1008
void
1009
NWWriter_SUMO::writeProhibitions(OutputDevice& into, const NBConnectionProhibits& prohibitions, const NBEdgeCont& ec) {
1010
// the edges may have been erased from NBEdgeCont but their pointers are still valid so we need to check
1011
for (NBConnectionProhibits::const_iterator j = prohibitions.begin(); j != prohibitions.end(); j++) {
1012
NBConnection prohibited = (*j).first;
1013
if (ec.retrieve(prohibited.getFrom()->getID()) == nullptr
1014
|| ec.retrieve(prohibited.getTo()->getID()) == nullptr) {
1015
continue;
1016
}
1017
const NBConnectionVector& prohibiting = (*j).second;
1018
for (NBConnectionVector::const_iterator k = prohibiting.begin(); k != prohibiting.end(); k++) {
1019
NBConnection prohibitor = *k;
1020
if (ec.retrieve(prohibitor.getFrom()->getID()) == nullptr
1021
|| ec.retrieve(prohibitor.getTo()->getID()) == nullptr) {
1022
continue;
1023
}
1024
into.openTag(SUMO_TAG_PROHIBITION);
1025
into.writeAttr(SUMO_ATTR_PROHIBITOR, prohibitionConnection(prohibitor));
1026
into.writeAttr(SUMO_ATTR_PROHIBITED, prohibitionConnection(prohibited));
1027
into.closeTag();
1028
}
1029
}
1030
}
1031
1032
1033
std::string
1034
NWWriter_SUMO::prohibitionConnection(const NBConnection& c) {
1035
return c.getFrom()->getID() + "->" + c.getTo()->getID();
1036
}
1037
1038
1039
void
1040
NWWriter_SUMO::writeTrafficLights(OutputDevice& into, const NBTrafficLightLogicCont& tllCont) {
1041
std::vector<NBTrafficLightLogic*> logics = tllCont.getComputed();
1042
for (NBTrafficLightLogic* logic : logics) {
1043
writeTrafficLight(into, logic);
1044
// only raise warnings on write instead of on compute (to avoid cluttering netedit)
1045
NBTrafficLightDefinition* def = tllCont.getDefinition(logic->getID(), logic->getProgramID());
1046
assert(def != nullptr);
1047
def->finalChecks();
1048
}
1049
if (logics.size() > 0) {
1050
into.lf();
1051
}
1052
}
1053
1054
1055
void
1056
NWWriter_SUMO::writeTrafficLight(OutputDevice& into, const NBTrafficLightLogic* logic) {
1057
into.openTag(SUMO_TAG_TLLOGIC);
1058
into.writeAttr(SUMO_ATTR_ID, logic->getID());
1059
into.writeAttr(SUMO_ATTR_TYPE, logic->getType());
1060
into.writeAttr(SUMO_ATTR_PROGRAMID, logic->getProgramID());
1061
into.writeAttr(SUMO_ATTR_OFFSET, logic->getOffset() == SUMOTime_MAX ? "begin" : writeSUMOTime(logic->getOffset()));
1062
// write the phases
1063
const bool varPhaseLength = logic->getType() != TrafficLightType::STATIC;
1064
for (const NBTrafficLightLogic::PhaseDefinition& phase : logic->getPhases()) {
1065
into.openTag(SUMO_TAG_PHASE);
1066
into.writeAttr(SUMO_ATTR_DURATION, writeSUMOTime(phase.duration));
1067
if (phase.duration < TIME2STEPS(10)) {
1068
into.writePadding(" ");
1069
}
1070
into.writeAttr(SUMO_ATTR_STATE, phase.state);
1071
if (varPhaseLength) {
1072
if (phase.minDur != NBTrafficLightDefinition::UNSPECIFIED_DURATION) {
1073
into.writeAttr(SUMO_ATTR_MINDURATION, writeSUMOTime(phase.minDur));
1074
}
1075
if (phase.maxDur != NBTrafficLightDefinition::UNSPECIFIED_DURATION) {
1076
into.writeAttr(SUMO_ATTR_MAXDURATION, writeSUMOTime(phase.maxDur));
1077
}
1078
if (phase.earliestEnd != NBTrafficLightDefinition::UNSPECIFIED_DURATION) {
1079
into.writeAttr(SUMO_ATTR_EARLIEST_END, writeSUMOTime(phase.earliestEnd));
1080
}
1081
if (phase.latestEnd != NBTrafficLightDefinition::UNSPECIFIED_DURATION) {
1082
into.writeAttr(SUMO_ATTR_LATEST_END, writeSUMOTime(phase.latestEnd));
1083
}
1084
// NEMA attributes
1085
if (phase.vehExt != NBTrafficLightDefinition::UNSPECIFIED_DURATION) {
1086
into.writeAttr(SUMO_ATTR_VEHICLEEXTENSION, writeSUMOTime(phase.vehExt));
1087
}
1088
if (phase.yellow != NBTrafficLightDefinition::UNSPECIFIED_DURATION) {
1089
into.writeAttr(SUMO_ATTR_YELLOW, writeSUMOTime(phase.yellow));
1090
}
1091
if (phase.red != NBTrafficLightDefinition::UNSPECIFIED_DURATION) {
1092
into.writeAttr(SUMO_ATTR_RED, writeSUMOTime(phase.red));
1093
}
1094
}
1095
if (phase.name != "") {
1096
into.writeAttr(SUMO_ATTR_NAME, StringUtils::escapeXML(phase.name));
1097
}
1098
if (phase.next.size() > 0) {
1099
into.writeAttr(SUMO_ATTR_NEXT, phase.next);
1100
}
1101
into.closeTag();
1102
}
1103
// write params
1104
logic->writeParams(into);
1105
into.closeTag();
1106
}
1107
1108
1109
void
1110
NWWriter_SUMO::writeStopOffsets(OutputDevice& into, const StopOffset& stopOffset) {
1111
if (stopOffset.isDefined()) {
1112
const std::string ss_vclasses = getVehicleClassNames(stopOffset.getPermissions());
1113
if (ss_vclasses.length() == 0) {
1114
// This stopOffset would have no effect...
1115
return;
1116
}
1117
into.openTag(SUMO_TAG_STOPOFFSET);
1118
const std::string ss_exceptions = getVehicleClassNames(~stopOffset.getPermissions());
1119
if (ss_vclasses.length() <= ss_exceptions.length()) {
1120
into.writeAttr(SUMO_ATTR_VCLASSES, ss_vclasses);
1121
} else {
1122
if (ss_exceptions.length() == 0) {
1123
into.writeAttr(SUMO_ATTR_VCLASSES, "all");
1124
} else {
1125
into.writeAttr(SUMO_ATTR_EXCEPTIONS, ss_exceptions);
1126
}
1127
}
1128
into.writeAttr(SUMO_ATTR_VALUE, stopOffset.getOffset());
1129
into.closeTag();
1130
}
1131
}
1132
1133
1134
/****************************************************************************/
1135
1136