1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file    NWWriter_SUMO.cpp
11 /// @author  Daniel Krajzewicz
12 /// @author  Jakob Erdmann
13 /// @author  Michael Behrisch
14 /// @author  Leonhard Luecken
15 /// @date    Tue, 04.05.2011
16 /// @version $Id$
17 ///
18 // Exporter writing networks using the SUMO format
19 /****************************************************************************/
20 
21 
22 // ===========================================================================
23 // included modules
24 // ===========================================================================
25 #include <config.h>
26 #include <cmath>
27 #include <algorithm>
28 #include <utils/options/OptionsCont.h>
29 #include <utils/iodevices/OutputDevice.h>
30 #include <utils/geom/GeoConvHelper.h>
31 #include <utils/common/ToString.h>
32 #include <utils/common/MsgHandler.h>
33 #include <utils/common/StringUtils.h>
34 #include <utils/common/SUMOVehicleClass.h>
35 #include <utils/geom/GeomConvHelper.h>
36 #include <netbuild/NBEdge.h>
37 #include <netbuild/NBEdgeCont.h>
38 #include <netbuild/NBNode.h>
39 #include <netbuild/NBNodeCont.h>
40 #include <netbuild/NBNetBuilder.h>
41 #include <netbuild/NBTrafficLightLogic.h>
42 #include <netbuild/NBDistrict.h>
43 #include <netbuild/NBHelpers.h>
44 #include "NWFrame.h"
45 #include "NWWriter_SUMO.h"
46 
47 
48 //#define DEBUG_OPPOSITE_INTERNAL
49 
50 // ===========================================================================
51 // method definitions
52 // ===========================================================================
53 // ---------------------------------------------------------------------------
54 // static methods
55 // ---------------------------------------------------------------------------
56 void
writeNetwork(const OptionsCont & oc,NBNetBuilder & nb)57 NWWriter_SUMO::writeNetwork(const OptionsCont& oc, NBNetBuilder& nb) {
58     // check whether a sumo net-file shall be generated
59     if (!oc.isSet("output-file")) {
60         return;
61     }
62     OutputDevice& device = OutputDevice::getDevice(oc.getString("output-file"));
63     std::map<SumoXMLAttr, std::string> attrs;
64     attrs[SUMO_ATTR_VERSION] = NWFrame::MAJOR_VERSION;
65     if (oc.getBool("lefthand")) {
66         attrs[SUMO_ATTR_LEFTHAND] = "true";
67     }
68     const int cornerDetail = oc.getInt("junctions.corner-detail");
69     if (cornerDetail > 0) {
70         attrs[SUMO_ATTR_CORNERDETAIL] = toString(cornerDetail);
71     }
72     if (!oc.isDefault("junctions.internal-link-detail")) {
73         attrs[SUMO_ATTR_LINKDETAIL] = toString(oc.getInt("junctions.internal-link-detail"));
74     }
75     if (oc.getBool("rectangular-lane-cut")) {
76         attrs[SUMO_ATTR_RECTANGULAR_LANE_CUT] = "true";
77     }
78     if (oc.getBool("crossings.guess") || oc.getBool("walkingareas")) {
79         attrs[SUMO_ATTR_WALKINGAREAS] = "true";
80     }
81     if (oc.getFloat("junctions.limit-turn-speed") > 0) {
82         attrs[SUMO_ATTR_LIMIT_TURN_SPEED] = toString(oc.getFloat("junctions.limit-turn-speed"));
83     }
84     if (!oc.isDefault("check-lane-foes.all")) {
85         attrs[SUMO_ATTR_CHECKLANEFOES_ALL] = toString(oc.getBool("check-lane-foes.all"));
86     }
87     if (!oc.isDefault("check-lane-foes.roundabout")) {
88         attrs[SUMO_ATTR_CHECKLANEFOES_ROUNDABOUT] = toString(oc.getBool("check-lane-foes.roundabout"));
89     }
90     device.writeXMLHeader("net", "net_file.xsd", attrs); // street names may contain non-ascii chars
91     device.lf();
92     // get involved container
93     const NBNodeCont& nc = nb.getNodeCont();
94     const NBEdgeCont& ec = nb.getEdgeCont();
95     const NBDistrictCont& dc = nb.getDistrictCont();
96 
97     // write network offsets and projection
98     GeoConvHelper::writeLocation(device);
99 
100     // write edge types and restrictions
101     nb.getTypeCont().writeTypes(device);
102 
103     // write inner lanes
104     if (!oc.getBool("no-internal-links")) {
105         bool hadAny = false;
106         for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
107             hadAny |= writeInternalEdges(device, ec, *(*i).second);
108         }
109         if (hadAny) {
110             device.lf();
111         }
112     }
113 
114     // write edges with lanes and connected edges
115     bool noNames = !oc.getBool("output.street-names");
116     for (std::map<std::string, NBEdge*>::const_iterator i = ec.begin(); i != ec.end(); ++i) {
117         writeEdge(device, *(*i).second, noNames);
118     }
119     device.lf();
120 
121     // write tls logics
122     writeTrafficLights(device, nb.getTLLogicCont());
123 
124     // write the nodes (junctions)
125     for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
126         writeJunction(device, *(*i).second);
127     }
128     device.lf();
129     const bool includeInternal = !oc.getBool("no-internal-links");
130     if (includeInternal) {
131         // ... internal nodes if not unwanted
132         bool hadAny = false;
133         for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
134             hadAny |= writeInternalNodes(device, *(*i).second);
135         }
136         if (hadAny) {
137             device.lf();
138         }
139     }
140 
141     // write the successors of lanes
142     int numConnections = 0;
143     for (std::map<std::string, NBEdge*>::const_iterator it_edge = ec.begin(); it_edge != ec.end(); it_edge++) {
144         NBEdge* from = it_edge->second;
145         const std::vector<NBEdge::Connection> connections = from->getConnections();
146         numConnections += (int)connections.size();
147         for (std::vector<NBEdge::Connection>::const_iterator it_c = connections.begin(); it_c != connections.end(); it_c++) {
148             writeConnection(device, *from, *it_c, includeInternal);
149         }
150     }
151     if (numConnections > 0) {
152         device.lf();
153     }
154     if (includeInternal) {
155         // ... internal successors if not unwanted
156         bool hadAny = false;
157         for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
158             hadAny |= writeInternalConnections(device, *(*i).second);
159         }
160         if (hadAny) {
161             device.lf();
162         }
163     }
164     for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
165         NBNode* node = (*i).second;
166         // write connections from pedestrian crossings
167         std::vector<NBNode::Crossing*> crossings = node->getCrossings();
168         for (auto c : crossings) {
169             NWWriter_SUMO::writeInternalConnection(device, c->id, c->nextWalkingArea, 0, 0, "", LINKDIR_STRAIGHT, c->tlID, c->tlLinkIndex2);
170         }
171         // write connections from pedestrian walking areas
172         for (const NBNode::WalkingArea& wa : node->getWalkingAreas()) {
173             for (const std::string& cID : wa.nextCrossings) {
174                 const NBNode::Crossing& nextCrossing = *node->getCrossing(cID);
175                 // connection to next crossing (may be tls-controlled)
176                 device.openTag(SUMO_TAG_CONNECTION);
177                 device.writeAttr(SUMO_ATTR_FROM, wa.id);
178                 device.writeAttr(SUMO_ATTR_TO, cID);
179                 device.writeAttr(SUMO_ATTR_FROM_LANE, 0);
180                 device.writeAttr(SUMO_ATTR_TO_LANE, 0);
181                 if (nextCrossing.tlID != "") {
182                     device.writeAttr(SUMO_ATTR_TLID, nextCrossing.tlID);
183                     assert(nextCrossing.tlLinkIndex >= 0);
184                     device.writeAttr(SUMO_ATTR_TLLINKINDEX, nextCrossing.tlLinkIndex);
185                 }
186                 device.writeAttr(SUMO_ATTR_DIR, LINKDIR_STRAIGHT);
187                 device.writeAttr(SUMO_ATTR_STATE, nextCrossing.priority ? LINKSTATE_MAJOR : LINKSTATE_MINOR);
188                 device.closeTag();
189             }
190             // optional connections from/to sidewalk
191             std::string edgeID;
192             int laneIndex;
193             for (const std::string& sw : wa.nextSidewalks) {
194                 NBHelpers::interpretLaneID(sw, edgeID, laneIndex);
195                 NWWriter_SUMO::writeInternalConnection(device, wa.id, edgeID, 0, laneIndex, "");
196             }
197             for (const std::string& sw : wa.prevSidewalks) {
198                 NBHelpers::interpretLaneID(sw, edgeID, laneIndex);
199                 NWWriter_SUMO::writeInternalConnection(device, edgeID, wa.id, laneIndex, 0, "");
200             }
201         }
202     }
203 
204     // write loaded prohibitions
205     for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
206         writeProhibitions(device, i->second->getProhibitions());
207     }
208 
209     // write roundabout information
210     writeRoundabouts(device, ec.getRoundabouts(), ec);
211 
212     // write the districts
213     for (std::map<std::string, NBDistrict*>::const_iterator i = dc.begin(); i != dc.end(); i++) {
214         writeDistrict(device, *(*i).second);
215     }
216     if (dc.size() != 0) {
217         device.lf();
218     }
219     device.close();
220 }
221 
222 
223 std::string
getOppositeInternalID(const NBEdgeCont & ec,const NBEdge * from,const NBEdge::Connection & con,double & oppositeLength)224 NWWriter_SUMO::getOppositeInternalID(const NBEdgeCont& ec, const NBEdge* from, const NBEdge::Connection& con, double& oppositeLength) {
225     const NBEdge::Lane& succ = con.toEdge->getLanes()[con.toLane];
226     const NBEdge::Lane& pred = from->getLanes()[con.fromLane];
227     const bool lefthand = OptionsCont::getOptions().getBool("lefthand");
228     if (succ.oppositeID != "" && succ.oppositeID != "-" && pred.oppositeID != "" && pred.oppositeID != "-") {
229 #ifdef DEBUG_OPPOSITE_INTERNAL
230         std::cout << "getOppositeInternalID con=" << con.getDescription(from) << " (" << con.getInternalLaneID() << ")\n";
231 #endif
232         // find the connection that connects succ.oppositeID to pred.oppositeID
233         const NBEdge* succOpp = ec.retrieve(succ.oppositeID.substr(0, succ.oppositeID.rfind("_")));
234         const NBEdge* predOpp = ec.retrieve(pred.oppositeID.substr(0, pred.oppositeID.rfind("_")));
235         assert(succOpp != 0);
236         assert(predOpp != 0);
237         const std::vector<NBEdge::Connection>& connections = succOpp->getConnections();
238         for (std::vector<NBEdge::Connection>::const_iterator it_c = connections.begin(); it_c != connections.end(); it_c++) {
239             const NBEdge::Connection& conOpp = *it_c;
240             if (succOpp != from // turnaround
241                     && predOpp == conOpp.toEdge
242                     && succOpp->getLaneID(conOpp.fromLane) == succ.oppositeID
243                     && predOpp->getLaneID(conOpp.toLane) == pred.oppositeID
244                     && from->getToNode()->getDirection(from, con.toEdge, lefthand) == LINKDIR_STRAIGHT
245                     && from->getToNode()->getDirection(succOpp, predOpp, lefthand) == LINKDIR_STRAIGHT
246                ) {
247 #ifdef DEBUG_OPPOSITE_INTERNAL
248                 std::cout << "  found " << conOpp.getInternalLaneID() << "\n";
249 #endif
250                 oppositeLength = conOpp.length;
251                 return conOpp.getInternalLaneID();
252             } else {
253                 /*
254                 #ifdef DEBUG_OPPOSITE_INTERNAL
255                 std::cout << "  rejected " << conOpp.getInternalLaneID()
256                     << "\n     succ.oppositeID=" << succ.oppositeID
257                     << "\n         succOppLane=" << succOpp->getLaneID(conOpp.fromLane)
258                     << "\n     pred.oppositeID=" << pred.oppositeID
259                     << "\n         predOppLane=" << predOpp->getLaneID(conOpp.toLane)
260                     << "\n      predOpp=" << predOpp->getID()
261                     << "\n     conOppTo=" << conOpp.toEdge->getID()
262                     << "\n     len1=" << con.shape.length()
263                     << "\n     len2=" << conOpp.shape.length()
264                     << "\n";
265                 #endif
266                 */
267             }
268         }
269         return "";
270     } else {
271         return "";
272     }
273 }
274 
275 
276 bool
writeInternalEdges(OutputDevice & into,const NBEdgeCont & ec,const NBNode & n)277 NWWriter_SUMO::writeInternalEdges(OutputDevice& into, const NBEdgeCont& ec, const NBNode& n) {
278     bool ret = false;
279     const EdgeVector& incoming = n.getIncomingEdges();
280     // first pass: determine opposite internal edges and average their length
281     std::map<std::string, std::string> oppositeLaneID;
282     std::map<std::string, double> oppositeLengths;
283     for (NBEdge* e : incoming) {
284         for (const NBEdge::Connection& c : e->getConnections()) {
285             double oppositeLength = 0;
286             const std::string op = getOppositeInternalID(ec, e, c, oppositeLength);
287             oppositeLaneID[c.getInternalLaneID()] = op;
288             if (op != "") {
289                 oppositeLengths[c.id] = oppositeLength;
290             }
291         }
292     }
293     if (oppositeLengths.size() > 0) {
294         for (NBEdge* e : incoming) {
295             for (NBEdge::Connection& c : e->getConnections()) {
296                 if (oppositeLengths.count(c.id) > 0) {
297                     c.length = (c.length + oppositeLengths[c.id]) / 2;
298                 }
299             }
300         }
301     }
302 
303     for (EdgeVector::const_iterator i = incoming.begin(); i != incoming.end(); i++) {
304         const std::vector<NBEdge::Connection>& elv = (*i)->getConnections();
305         if (elv.size() > 0) {
306             bool haveVia = false;
307             std::string edgeID = "";
308             // second pass: write non-via edges
309             for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
310                 if ((*k).toEdge == nullptr) {
311                     assert(false); // should never happen. tell me when it does
312                     continue;
313                 }
314                 if (edgeID != (*k).id) {
315                     if (edgeID != "") {
316                         // close the previous edge
317                         into.closeTag();
318                     }
319                     edgeID = (*k).id;
320                     into.openTag(SUMO_TAG_EDGE);
321                     into.writeAttr(SUMO_ATTR_ID, edgeID);
322                     into.writeAttr(SUMO_ATTR_FUNCTION, EDGEFUNC_INTERNAL);
323                     if ((*i)->isBidiRail() && (*k).toEdge->isBidiRail() &&
324                             (*i) != (*k).toEdge->getTurnDestination(true)) {
325                         try {
326                             NBEdge::Connection bidiCon = (*k).toEdge->getTurnDestination(true)->getConnection(
327                                                              0, (*i)->getTurnDestination(true), 0);
328                             into.writeAttr(SUMO_ATTR_BIDI, bidiCon.id);
329                         } catch (ProcessError&) {
330                             std::cout << " could not find bidi-connection\n";
331                         }
332                     }
333                     // open a new edge
334                 }
335                 // to avoid changing to an internal lane which has a successor
336                 // with the wrong permissions we need to inherit them from the successor
337                 const NBEdge::Lane& successor = (*k).toEdge->getLanes()[(*k).toLane];
338                 const double width = n.isConstantWidthTransition() && (*i)->getNumLanes() > (*k).toEdge->getNumLanes() ? (*i)->getLaneWidth((*k).fromLane) : successor.width;
339                 writeLane(into, (*k).getInternalLaneID(), (*k).vmax,
340                           successor.permissions, successor.preferred,
341                           NBEdge::UNSPECIFIED_OFFSET, NBEdge::UNSPECIFIED_OFFSET,
342                           std::map<int, double>(), width, (*k).shape, &(*k),
343                           (*k).length, (*k).internalLaneIndex, oppositeLaneID[(*k).getInternalLaneID()]);
344                 haveVia = haveVia || (*k).haveVia;
345             }
346             ret = true;
347             into.closeTag(); // close the last edge
348             // third pass: write via edges
349             if (haveVia) {
350                 for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
351                     if (!(*k).haveVia) {
352                         continue;
353                     }
354                     if ((*k).toEdge == nullptr) {
355                         assert(false); // should never happen. tell me when it does
356                         continue;
357                     }
358                     const NBEdge::Lane& successor = (*k).toEdge->getLanes()[(*k).toLane];
359                     into.openTag(SUMO_TAG_EDGE);
360                     into.writeAttr(SUMO_ATTR_ID, (*k).viaID);
361                     into.writeAttr(SUMO_ATTR_FUNCTION, EDGEFUNC_INTERNAL);
362                     writeLane(into, (*k).viaID + "_0", (*k).vmax, successor.permissions, successor.preferred,
363                               NBEdge::UNSPECIFIED_OFFSET, NBEdge::UNSPECIFIED_OFFSET,
364                               std::map<int, double>(), successor.width, (*k).viaShape, &(*k),
365                               MAX2((*k).viaShape.length(), POSITION_EPS), // microsim needs positive length
366                               0, "");
367                     into.closeTag();
368                 }
369             }
370         }
371     }
372     // write pedestrian crossings
373     for (auto c : n.getCrossings()) {
374         into.openTag(SUMO_TAG_EDGE);
375         into.writeAttr(SUMO_ATTR_ID, c->id);
376         into.writeAttr(SUMO_ATTR_FUNCTION, EDGEFUNC_CROSSING);
377         into.writeAttr(SUMO_ATTR_CROSSING_EDGES, c->edges);
378         writeLane(into, c->id + "_0", 1, SVC_PEDESTRIAN, 0,
379                   NBEdge::UNSPECIFIED_OFFSET, NBEdge::UNSPECIFIED_OFFSET,
380                   std::map<int, double>(), c->width, c->shape, nullptr,
381                   MAX2(c->shape.length(), POSITION_EPS), 0, "", false, c->customShape.size() != 0);
382         into.closeTag();
383     }
384     // write pedestrian walking areas
385     const std::vector<NBNode::WalkingArea>& WalkingAreas = n.getWalkingAreas();
386     for (std::vector<NBNode::WalkingArea>::const_iterator it = WalkingAreas.begin(); it != WalkingAreas.end(); it++) {
387         const NBNode::WalkingArea& wa = *it;
388         into.openTag(SUMO_TAG_EDGE);
389         into.writeAttr(SUMO_ATTR_ID, wa.id);
390         into.writeAttr(SUMO_ATTR_FUNCTION, EDGEFUNC_WALKINGAREA);
391         writeLane(into, wa.id + "_0", 1, SVC_PEDESTRIAN, 0,
392                   NBEdge::UNSPECIFIED_OFFSET, NBEdge::UNSPECIFIED_OFFSET,
393                   std::map<int, double>(), wa.width, wa.shape, nullptr, wa.length, 0, "", false, wa.hasCustomShape);
394         into.closeTag();
395     }
396     return ret;
397 }
398 
399 
400 void
writeEdge(OutputDevice & into,const NBEdge & e,bool noNames)401 NWWriter_SUMO::writeEdge(OutputDevice& into, const NBEdge& e, bool noNames) {
402     // write the edge's begin
403     into.openTag(SUMO_TAG_EDGE).writeAttr(SUMO_ATTR_ID, e.getID());
404     into.writeAttr(SUMO_ATTR_FROM, e.getFromNode()->getID());
405     into.writeAttr(SUMO_ATTR_TO, e.getToNode()->getID());
406     if (!noNames && e.getStreetName() != "") {
407         into.writeAttr(SUMO_ATTR_NAME, StringUtils::escapeXML(e.getStreetName()));
408     }
409     into.writeAttr(SUMO_ATTR_PRIORITY, e.getPriority());
410     if (e.getTypeID() != "") {
411         into.writeAttr(SUMO_ATTR_TYPE, e.getTypeID());
412     }
413     if (e.isMacroscopicConnector()) {
414         into.writeAttr(SUMO_ATTR_FUNCTION, EDGEFUNC_CONNECTOR);
415     }
416     // write the spread type if not default ("right")
417     if (e.getLaneSpreadFunction() != LANESPREAD_RIGHT) {
418         into.writeAttr(SUMO_ATTR_SPREADTYPE, e.getLaneSpreadFunction());
419     }
420     if (e.hasLoadedLength()) {
421         into.writeAttr(SUMO_ATTR_LENGTH, e.getLoadedLength());
422     }
423     if (!e.hasDefaultGeometry()) {
424         into.writeAttr(SUMO_ATTR_SHAPE, e.getGeometry());
425     }
426     if (e.getStopOffsets().size() != 0) {
427         writeStopOffsets(into, e.getStopOffsets());
428     }
429     if (e.isBidiRail()) {
430         into.writeAttr(SUMO_ATTR_BIDI, e.getTurnDestination(true)->getID());
431     }
432 
433     // write the lanes
434     const std::vector<NBEdge::Lane>& lanes = e.getLanes();
435 
436     const double length = e.getFinalLength();
437     double startOffset = e.isBidiRail() ? e.getTurnDestination(true)->getEndOffset() : 0;
438     for (int i = 0; i < (int) lanes.size(); i++) {
439         const NBEdge::Lane& l = lanes[i];
440         std::map<int, double> stopOffsets;
441         if (l.stopOffsets != e.getStopOffsets()) {
442             stopOffsets = l.stopOffsets;
443         }
444         writeLane(into, e.getLaneID(i), l.speed,
445                   l.permissions, l.preferred,
446                   startOffset, l.endOffset,
447                   stopOffsets, l.width, l.shape, &l,
448                   length, i, l.oppositeID, l.accelRamp, l.customShape.size() > 0);
449     }
450     // close the edge
451     e.writeParams(into);
452     into.closeTag();
453 }
454 
455 
456 void
writeLane(OutputDevice & into,const std::string & lID,double speed,SVCPermissions permissions,SVCPermissions preferred,double startOffset,double endOffset,std::map<SVCPermissions,double> stopOffsets,double width,PositionVector shape,const Parameterised * params,double length,int index,const std::string & oppositeID,bool accelRamp,bool customShape)457 NWWriter_SUMO::writeLane(OutputDevice& into, const std::string& lID,
458                          double speed, SVCPermissions permissions, SVCPermissions preferred,
459                          double startOffset, double endOffset,
460                          std::map<SVCPermissions, double> stopOffsets, double width, PositionVector shape,
461                          const Parameterised* params, double length, int index,
462                          const std::string& oppositeID, bool accelRamp, bool customShape) {
463     // output the lane's attributes
464     into.openTag(SUMO_TAG_LANE).writeAttr(SUMO_ATTR_ID, lID);
465     // the first lane of an edge will be the depart lane
466     into.writeAttr(SUMO_ATTR_INDEX, index);
467     // write the list of allowed/disallowed vehicle classes
468     if (permissions != SVC_UNSPECIFIED) {
469         writePermissions(into, permissions);
470     }
471     writePreferences(into, preferred);
472     // some further information
473     if (speed == 0) {
474         WRITE_WARNING("Lane '" + lID + "' has a maximum allowed speed of 0.");
475     } else if (speed < 0) {
476         throw ProcessError("Negative allowed speed (" + toString(speed) + ") on lane '" + lID + "', use --speed.minimum to prevent this.");
477     }
478     into.writeAttr(SUMO_ATTR_SPEED, speed);
479     into.writeAttr(SUMO_ATTR_LENGTH, length);
480     if (endOffset != NBEdge::UNSPECIFIED_OFFSET) {
481         into.writeAttr(SUMO_ATTR_ENDOFFSET, endOffset);
482     }
483     if (width != NBEdge::UNSPECIFIED_WIDTH) {
484         into.writeAttr(SUMO_ATTR_WIDTH, width);
485     }
486     if (accelRamp) {
487         into.writeAttr<bool>(SUMO_ATTR_ACCELERATION, accelRamp);
488     }
489     if (customShape) {
490         into.writeAttr(SUMO_ATTR_CUSTOMSHAPE, true);
491     }
492     if (endOffset > 0 || startOffset > 0) {
493         if (startOffset + endOffset < shape.length()) {
494             shape = shape.getSubpart(startOffset, shape.length() - endOffset);
495         } else {
496             WRITE_ERROR("Invalid endOffset " + toString(endOffset) + " at lane '" + lID
497                         + "' with length " + toString(shape.length()) + " (startOffset " + toString(startOffset) + ")");
498             if (!OptionsCont::getOptions().getBool("ignore-errors")) {
499                 throw ProcessError();
500             }
501         }
502     }
503     into.writeAttr(SUMO_ATTR_SHAPE, shape);
504 
505     if (stopOffsets.size() != 0) {
506         writeStopOffsets(into, stopOffsets);
507     }
508 
509     if (oppositeID != "" && oppositeID != "-") {
510         into.openTag(SUMO_TAG_NEIGH);
511         into.writeAttr(SUMO_ATTR_LANE, oppositeID);
512         into.closeTag();
513     }
514 
515     if (params != nullptr) {
516         params->writeParams(into);
517     }
518 
519     into.closeTag();
520 }
521 
522 
523 void
writeJunction(OutputDevice & into,const NBNode & n)524 NWWriter_SUMO::writeJunction(OutputDevice& into, const NBNode& n) {
525     // write the attributes
526     into.openTag(SUMO_TAG_JUNCTION).writeAttr(SUMO_ATTR_ID, n.getID());
527     into.writeAttr(SUMO_ATTR_TYPE, n.getType());
528     NWFrame::writePositionLong(n.getPosition(), into);
529     // write the incoming lanes
530     std::string incLanes;
531     const std::vector<NBEdge*>& incoming = n.getIncomingEdges();
532     for (std::vector<NBEdge*>::const_iterator i = incoming.begin(); i != incoming.end(); ++i) {
533         int noLanes = (*i)->getNumLanes();
534         for (int j = 0; j < noLanes; j++) {
535             incLanes += (*i)->getLaneID(j);
536             if (i != incoming.end() - 1 || j < noLanes - 1) {
537                 incLanes += ' ';
538             }
539         }
540     }
541     std::vector<NBNode::Crossing*> crossings = n.getCrossings();
542     std::set<std::string> prevWAs;
543     // avoid duplicates
544     for (auto c : crossings) {
545         if (prevWAs.count(c->prevWalkingArea) == 0) {
546             incLanes += ' ' + c->prevWalkingArea + "_0";
547             prevWAs.insert(c->prevWalkingArea);
548         }
549     }
550     into.writeAttr(SUMO_ATTR_INCLANES, incLanes);
551     // write the internal lanes
552     std::string intLanes;
553     if (!OptionsCont::getOptions().getBool("no-internal-links")) {
554         int l = 0;
555         for (EdgeVector::const_iterator i = incoming.begin(); i != incoming.end(); i++) {
556             const std::vector<NBEdge::Connection>& elv = (*i)->getConnections();
557             for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
558                 if ((*k).toEdge == nullptr) {
559                     continue;
560                 }
561                 if (l != 0) {
562                     intLanes += ' ';
563                 }
564                 if (!(*k).haveVia) {
565                     intLanes += (*k).getInternalLaneID();
566                 } else {
567                     intLanes += (*k).viaID + "_0";
568                 }
569                 l++;
570             }
571         }
572     }
573     if (n.getType() != NODETYPE_DEAD_END && n.getType() != NODETYPE_NOJUNCTION) {
574         for (auto c : crossings) {
575             intLanes += ' ' + c->id + "_0";
576         }
577     }
578     into.writeAttr(SUMO_ATTR_INTLANES, intLanes);
579     // close writing
580     into.writeAttr(SUMO_ATTR_SHAPE, n.getShape().simplified());
581     // write optional radius
582     if (n.getRadius() != NBNode::UNSPECIFIED_RADIUS) {
583         into.writeAttr(SUMO_ATTR_RADIUS, n.getRadius());
584     }
585     // specify whether a custom shape was used
586     if (n.hasCustomShape()) {
587         into.writeAttr(SUMO_ATTR_CUSTOMSHAPE, true);
588     }
589     if (n.getRightOfWay() != RIGHT_OF_WAY_DEFAULT) {
590         into.writeAttr<std::string>(SUMO_ATTR_RIGHT_OF_WAY, toString(n.getRightOfWay()));
591     }
592     if (n.getFringeType() != FRINGE_TYPE_DEFAULT) {
593         into.writeAttr<std::string>(SUMO_ATTR_FRINGE, toString(n.getFringeType()));
594     }
595     if (n.getType() != NODETYPE_DEAD_END) {
596         // write right-of-way logics
597         n.writeLogic(into);
598     }
599     n.writeParams(into);
600     into.closeTag();
601 }
602 
603 
604 bool
writeInternalNodes(OutputDevice & into,const NBNode & n)605 NWWriter_SUMO::writeInternalNodes(OutputDevice& into, const NBNode& n) {
606     bool ret = false;
607     const std::vector<NBEdge*>& incoming = n.getIncomingEdges();
608     // build the list of internal lane ids
609     std::vector<std::string> internalLaneIDs;
610     std::map<std::string, std::string> viaIDs;
611     for (EdgeVector::const_iterator i = incoming.begin(); i != incoming.end(); i++) {
612         const std::vector<NBEdge::Connection>& elv = (*i)->getConnections();
613         for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
614             if ((*k).toEdge != nullptr) {
615                 internalLaneIDs.push_back((*k).getInternalLaneID());
616                 viaIDs[(*k).getInternalLaneID()] = ((*k).viaID);
617             }
618         }
619     }
620     for (auto c : n.getCrossings()) {
621         internalLaneIDs.push_back(c->id + "_0");
622     }
623     // write the internal nodes
624     for (std::vector<NBEdge*>::const_iterator i = incoming.begin(); i != incoming.end(); i++) {
625         const std::vector<NBEdge::Connection>& elv = (*i)->getConnections();
626         for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
627             if ((*k).toEdge == nullptr || !(*k).haveVia) {
628                 continue;
629             }
630             Position pos = (*k).shape[-1];
631             into.openTag(SUMO_TAG_JUNCTION).writeAttr(SUMO_ATTR_ID, (*k).viaID + "_0");
632             into.writeAttr(SUMO_ATTR_TYPE, NODETYPE_INTERNAL);
633             NWFrame::writePositionLong(pos, into);
634             std::string incLanes = (*k).getInternalLaneID();
635             std::vector<std::string> foeIDs;
636             for (std::string incLane : (*k).foeIncomingLanes) {
637                 incLanes += " " + incLane;
638                 if (incLane[0] == ':' && viaIDs[incLane] != "") {
639                     // intersecting left turns
640                     foeIDs.push_back(viaIDs[incLane] + "_0");
641                 }
642             }
643             into.writeAttr(SUMO_ATTR_INCLANES, incLanes);
644             const std::vector<int>& foes = (*k).foeInternalLinks;
645             for (std::vector<int>::const_iterator it = foes.begin(); it != foes.end(); ++it) {
646                 foeIDs.push_back(internalLaneIDs[*it]);
647             }
648             into.writeAttr(SUMO_ATTR_INTLANES, joinToString(foeIDs, " "));
649             into.closeTag();
650             ret = true;
651         }
652     }
653     return ret;
654 }
655 
656 
657 void
writeConnection(OutputDevice & into,const NBEdge & from,const NBEdge::Connection & c,bool includeInternal,ConnectionStyle style)658 NWWriter_SUMO::writeConnection(OutputDevice& into, const NBEdge& from, const NBEdge::Connection& c,
659                                bool includeInternal, ConnectionStyle style) {
660     assert(c.toEdge != 0);
661     into.openTag(SUMO_TAG_CONNECTION);
662     into.writeAttr(SUMO_ATTR_FROM, from.getID());
663     into.writeAttr(SUMO_ATTR_TO, c.toEdge->getID());
664     into.writeAttr(SUMO_ATTR_FROM_LANE, c.fromLane);
665     into.writeAttr(SUMO_ATTR_TO_LANE, c.toLane);
666     if (c.mayDefinitelyPass && style != TLL) {
667         into.writeAttr(SUMO_ATTR_PASS, c.mayDefinitelyPass);
668     }
669     if ((from.getToNode()->getKeepClear() == false || c.keepClear == false) && style != TLL) {
670         into.writeAttr<bool>(SUMO_ATTR_KEEP_CLEAR, false);
671     }
672     if (c.contPos != NBEdge::UNSPECIFIED_CONTPOS && style != TLL) {
673         into.writeAttr(SUMO_ATTR_CONTPOS, c.contPos);
674     }
675     if (c.visibility != NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE && style != TLL) {
676         into.writeAttr(SUMO_ATTR_VISIBILITY_DISTANCE, c.visibility);
677     }
678     if (c.speed != NBEdge::UNSPECIFIED_SPEED && style != TLL) {
679         into.writeAttr(SUMO_ATTR_SPEED, c.speed);
680     }
681     if (c.customShape.size() != 0 && style != TLL) {
682         into.writeAttr(SUMO_ATTR_SHAPE, c.customShape);
683     }
684     if (c.uncontrolled != false && style != TLL) {
685         into.writeAttr(SUMO_ATTR_UNCONTROLLED, c.uncontrolled);
686     }
687     if (style != PLAIN) {
688         if (includeInternal) {
689             into.writeAttr(SUMO_ATTR_VIA, c.getInternalLaneID());
690         }
691         // set information about the controlling tl if any
692         if (c.tlID != "") {
693             into.writeAttr(SUMO_ATTR_TLID, c.tlID);
694             into.writeAttr(SUMO_ATTR_TLLINKINDEX, c.tlLinkIndex);
695         }
696         if (style == SUMONET) {
697             // write the direction information
698             LinkDirection dir = from.getToNode()->getDirection(&from, c.toEdge, OptionsCont::getOptions().getBool("lefthand"));
699             assert(dir != LINKDIR_NODIR);
700             into.writeAttr(SUMO_ATTR_DIR, toString(dir));
701             // write the state information
702             const LinkState linkState = from.getToNode()->getLinkState(
703                                             &from, c.toEdge, c.fromLane, c.toLane, c.mayDefinitelyPass, c.tlID);
704             into.writeAttr(SUMO_ATTR_STATE, linkState);
705         }
706     }
707     into.closeTag();
708 }
709 
710 
711 bool
writeInternalConnections(OutputDevice & into,const NBNode & n)712 NWWriter_SUMO::writeInternalConnections(OutputDevice& into, const NBNode& n) {
713     bool ret = false;
714     const bool lefthand = OptionsCont::getOptions().getBool("lefthand");
715     const std::vector<NBEdge*>& incoming = n.getIncomingEdges();
716     for (std::vector<NBEdge*>::const_iterator i = incoming.begin(); i != incoming.end(); ++i) {
717         NBEdge* from = *i;
718         const std::vector<NBEdge::Connection>& connections = from->getConnections();
719         for (std::vector<NBEdge::Connection>::const_iterator j = connections.begin(); j != connections.end(); ++j) {
720             const NBEdge::Connection& c = *j;
721             LinkDirection dir = n.getDirection(from, c.toEdge, lefthand);
722             assert(c.toEdge != 0);
723             if (c.haveVia) {
724                 // internal split
725                 writeInternalConnection(into, c.id, c.toEdge->getID(), c.internalLaneIndex, c.toLane, c.viaID + "_0", dir);
726                 writeInternalConnection(into, c.viaID, c.toEdge->getID(), 0, c.toLane, "", dir);
727             } else {
728                 // no internal split
729                 writeInternalConnection(into, c.id, c.toEdge->getID(), c.internalLaneIndex, c.toLane, "", dir);
730             }
731             ret = true;
732         }
733     }
734     return ret;
735 }
736 
737 
738 void
writeInternalConnection(OutputDevice & into,const std::string & from,const std::string & to,int fromLane,int toLane,const std::string & via,LinkDirection dir,const std::string & tlID,int linkIndex)739 NWWriter_SUMO::writeInternalConnection(OutputDevice& into,
740                                        const std::string& from, const std::string& to,
741                                        int fromLane, int toLane, const std::string& via,
742                                        LinkDirection dir, const std::string& tlID, int linkIndex) {
743     into.openTag(SUMO_TAG_CONNECTION);
744     into.writeAttr(SUMO_ATTR_FROM, from);
745     into.writeAttr(SUMO_ATTR_TO, to);
746     into.writeAttr(SUMO_ATTR_FROM_LANE, fromLane);
747     into.writeAttr(SUMO_ATTR_TO_LANE, toLane);
748     if (via != "") {
749         into.writeAttr(SUMO_ATTR_VIA, via);
750     }
751     if (tlID != "" && linkIndex != NBConnection::InvalidTlIndex) {
752         // used for the reverse direction of pedestrian crossings
753         into.writeAttr(SUMO_ATTR_TLID, tlID);
754         into.writeAttr(SUMO_ATTR_TLLINKINDEX, linkIndex);
755     }
756     into.writeAttr(SUMO_ATTR_DIR, dir);
757     into.writeAttr(SUMO_ATTR_STATE, (via != "" ? "m" : "M"));
758     into.closeTag();
759 }
760 
761 
762 void
writeRoundabouts(OutputDevice & into,const std::set<EdgeSet> & roundabouts,const NBEdgeCont & ec)763 NWWriter_SUMO::writeRoundabouts(OutputDevice& into, const std::set<EdgeSet>& roundabouts,
764                                 const NBEdgeCont& ec) {
765     //  make output deterministic
766     std::vector<std::vector<std::string> > edgeIDs;
767     for (std::set<EdgeSet>::const_iterator i = roundabouts.begin(); i != roundabouts.end(); ++i) {
768         std::vector<std::string> tEdgeIDs;
769         for (EdgeSet::const_iterator j = (*i).begin(); j != (*i).end(); ++j) {
770             // the edges may have been erased from NBEdgeCont but their pointers are still valid
771             // we verify their existance in writeRoundabout()
772             tEdgeIDs.push_back((*j)->getID());
773         }
774         std::sort(tEdgeIDs.begin(), tEdgeIDs.end());
775         edgeIDs.push_back(tEdgeIDs);
776     }
777     std::sort(edgeIDs.begin(), edgeIDs.end());
778     //  write
779     for (std::vector<std::vector<std::string> >::const_iterator i = edgeIDs.begin(); i != edgeIDs.end(); ++i) {
780         writeRoundabout(into, *i, ec);
781     }
782     if (roundabouts.size() != 0) {
783         into.lf();
784     }
785 }
786 
787 
788 void
writeRoundabout(OutputDevice & into,const std::vector<std::string> & edgeIDs,const NBEdgeCont & ec)789 NWWriter_SUMO::writeRoundabout(OutputDevice& into, const std::vector<std::string>& edgeIDs,
790                                const NBEdgeCont& ec) {
791     std::vector<std::string> validEdgeIDs;
792     std::vector<std::string> invalidEdgeIDs;
793     std::vector<std::string> nodeIDs;
794     for (std::vector<std::string>::const_iterator i = edgeIDs.begin(); i != edgeIDs.end(); ++i) {
795         const NBEdge* edge = ec.retrieve(*i);
796         if (edge != nullptr) {
797             nodeIDs.push_back(edge->getToNode()->getID());
798             validEdgeIDs.push_back(edge->getID());
799         } else {
800             invalidEdgeIDs.push_back(*i);
801         }
802     }
803     std::sort(nodeIDs.begin(), nodeIDs.end());
804     if (validEdgeIDs.size() > 0) {
805         into.openTag(SUMO_TAG_ROUNDABOUT);
806         into.writeAttr(SUMO_ATTR_NODES, joinToString(nodeIDs, " "));
807         into.writeAttr(SUMO_ATTR_EDGES, joinToString(validEdgeIDs, " "));
808         into.closeTag();
809         if (invalidEdgeIDs.size() > 0) {
810             WRITE_WARNING("Writing incomplete roundabout. Edges: '"
811                           + joinToString(invalidEdgeIDs, " ") + "' no longer exist'");
812         }
813     }
814 }
815 
816 
817 void
writeDistrict(OutputDevice & into,const NBDistrict & d)818 NWWriter_SUMO::writeDistrict(OutputDevice& into, const NBDistrict& d) {
819     std::vector<double> sourceW = d.getSourceWeights();
820     VectorHelper<double>::normaliseSum(sourceW, 1.0);
821     std::vector<double> sinkW = d.getSinkWeights();
822     VectorHelper<double>::normaliseSum(sinkW, 1.0);
823     // write the head and the id of the district
824     into.openTag(SUMO_TAG_TAZ).writeAttr(SUMO_ATTR_ID, d.getID());
825     if (d.getShape().size() > 0) {
826         into.writeAttr(SUMO_ATTR_SHAPE, d.getShape());
827     }
828     // write all sources
829     const std::vector<NBEdge*>& sources = d.getSourceEdges();
830     for (int i = 0; i < (int)sources.size(); i++) {
831         // write the head and the id of the source
832         into.openTag(SUMO_TAG_TAZSOURCE).writeAttr(SUMO_ATTR_ID, sources[i]->getID()).writeAttr(SUMO_ATTR_WEIGHT, sourceW[i]);
833         into.closeTag();
834     }
835     // write all sinks
836     const std::vector<NBEdge*>& sinks = d.getSinkEdges();
837     for (int i = 0; i < (int)sinks.size(); i++) {
838         // write the head and the id of the sink
839         into.openTag(SUMO_TAG_TAZSINK).writeAttr(SUMO_ATTR_ID, sinks[i]->getID()).writeAttr(SUMO_ATTR_WEIGHT, sinkW[i]);
840         into.closeTag();
841     }
842     // write the tail
843     into.closeTag();
844 }
845 
846 
847 std::string
writeSUMOTime(SUMOTime steps)848 NWWriter_SUMO::writeSUMOTime(SUMOTime steps) {
849     double time = STEPS2TIME(steps);
850     if (time == std::floor(time)) {
851         return toString(int(time));
852     } else {
853         return toString(time);
854     }
855 }
856 
857 
858 void
writeProhibitions(OutputDevice & into,const NBConnectionProhibits & prohibitions)859 NWWriter_SUMO::writeProhibitions(OutputDevice& into, const NBConnectionProhibits& prohibitions) {
860     for (NBConnectionProhibits::const_iterator j = prohibitions.begin(); j != prohibitions.end(); j++) {
861         NBConnection prohibited = (*j).first;
862         const NBConnectionVector& prohibiting = (*j).second;
863         for (NBConnectionVector::const_iterator k = prohibiting.begin(); k != prohibiting.end(); k++) {
864             NBConnection prohibitor = *k;
865             into.openTag(SUMO_TAG_PROHIBITION);
866             into.writeAttr(SUMO_ATTR_PROHIBITOR, prohibitionConnection(prohibitor));
867             into.writeAttr(SUMO_ATTR_PROHIBITED, prohibitionConnection(prohibited));
868             into.closeTag();
869         }
870     }
871 }
872 
873 
874 std::string
prohibitionConnection(const NBConnection & c)875 NWWriter_SUMO::prohibitionConnection(const NBConnection& c) {
876     return c.getFrom()->getID() + "->" + c.getTo()->getID();
877 }
878 
879 
880 void
writeTrafficLights(OutputDevice & into,const NBTrafficLightLogicCont & tllCont)881 NWWriter_SUMO::writeTrafficLights(OutputDevice& into, const NBTrafficLightLogicCont& tllCont) {
882     std::vector<NBTrafficLightLogic*> logics = tllCont.getComputed();
883     for (std::vector<NBTrafficLightLogic*>::iterator it = logics.begin(); it != logics.end(); it++) {
884         into.openTag(SUMO_TAG_TLLOGIC);
885         into.writeAttr(SUMO_ATTR_ID, (*it)->getID());
886         into.writeAttr(SUMO_ATTR_TYPE, (*it)->getType());
887         into.writeAttr(SUMO_ATTR_PROGRAMID, (*it)->getProgramID());
888         into.writeAttr(SUMO_ATTR_OFFSET, writeSUMOTime((*it)->getOffset()));
889         // write the phases
890         const bool varPhaseLength = (*it)->getType() != TLTYPE_STATIC;
891         const std::vector<NBTrafficLightLogic::PhaseDefinition>& phases = (*it)->getPhases();
892         for (std::vector<NBTrafficLightLogic::PhaseDefinition>::const_iterator j = phases.begin(); j != phases.end(); ++j) {
893             into.openTag(SUMO_TAG_PHASE);
894             into.writeAttr(SUMO_ATTR_DURATION, writeSUMOTime(j->duration));
895             if (j->duration < TIME2STEPS(10)) {
896                 into.writePadding(" ");
897             }
898             into.writeAttr(SUMO_ATTR_STATE, j->state);
899             if (varPhaseLength) {
900                 if (j->minDur != NBTrafficLightDefinition::UNSPECIFIED_DURATION) {
901                     into.writeAttr(SUMO_ATTR_MINDURATION, writeSUMOTime(j->minDur));
902                 }
903                 if (j->maxDur != NBTrafficLightDefinition::UNSPECIFIED_DURATION) {
904                     into.writeAttr(SUMO_ATTR_MAXDURATION, writeSUMOTime(j->maxDur));
905                 }
906             }
907             if (j->name != "") {
908                 into.writeAttr(SUMO_ATTR_NAME, j->name);
909             }
910             if (j->next.size() > 0) {
911                 into.writeAttr(SUMO_ATTR_NEXT, j->next);
912             }
913             into.closeTag();
914         }
915         // write params
916         (*it)->writeParams(into);
917         into.closeTag();
918     }
919     if (logics.size() > 0) {
920         into.lf();
921     }
922 }
923 
924 
925 void
writeStopOffsets(OutputDevice & into,const std::map<SVCPermissions,double> & stopOffsets)926 NWWriter_SUMO::writeStopOffsets(OutputDevice& into, const std::map<SVCPermissions, double>& stopOffsets) {
927     if (stopOffsets.size() == 0) {
928         return;
929     }
930     assert(stopOffsets.size() == 1);
931     std::pair<int, double> offset = *stopOffsets.begin();
932     std::string ss_vclasses = getVehicleClassNames(offset.first);
933     if (ss_vclasses.length() == 0) {
934         // This stopOffset would have no effect...
935         return;
936     }
937     into.openTag(SUMO_TAG_STOPOFFSET);
938     std::string ss_exceptions = getVehicleClassNames(~offset.first);
939     if (ss_vclasses.length() <= ss_exceptions.length()) {
940         into.writeAttr(SUMO_ATTR_VCLASSES, ss_vclasses);
941     } else {
942         if (ss_exceptions.length() == 0) {
943             into.writeAttr(SUMO_ATTR_VCLASSES, "all");
944         } else {
945             into.writeAttr(SUMO_ATTR_EXCEPTIONS, ss_exceptions);
946         }
947     }
948     into.writeAttr(SUMO_ATTR_VALUE, offset.second);
949     into.closeTag();
950 }
951 
952 /****************************************************************************/
953 
954