LCOV - code coverage report
Current view: top level - src/netwrite - NWWriter_SUMO.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 545 555 98.2 %
Date: 2024-04-30 15:40:33 Functions: 20 20 100.0 %

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

Generated by: LCOV version 1.14