LCOV - code coverage report
Current view: top level - src/netwrite - NWWriter_SUMO.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 98.5 % 611 602
Test Date: 2026-10-12 15:53:19 Functions: 100.0 % 21 21

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

Generated by: LCOV version 2.0-1