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: 2025-11-13 15:38:19 Functions: 100.0 % 20 20

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

Generated by: LCOV version 2.0-1