LCOV - code coverage report
Current view: top level - src/netwrite - NWWriter_SUMO.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 98.4 % 564 555
Test Date: 2024-11-22 15:46:21 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-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         1692 : NWWriter_SUMO::writeNetwork(const OptionsCont& oc, NBNetBuilder& nb) {
      56              :     // check whether a sumo net-file shall be generated
      57         3384 :     if (!oc.isSet("output-file")) {
      58           74 :         return;
      59              :     }
      60         3233 :     OutputDevice& device = OutputDevice::getDevice(oc.getString("output-file"));
      61              :     std::map<SumoXMLAttr, std::string> attrs;
      62         1615 :     attrs[SUMO_ATTR_VERSION] = toString(NETWORK_VERSION);
      63         3230 :     if (oc.getBool("lefthand") != oc.getBool("flip-y-axis")) {
      64           34 :         attrs[SUMO_ATTR_LEFTHAND] = "true";
      65         3196 :     } 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         1615 :     const int cornerDetail = oc.getInt("junctions.corner-detail");
      71         1615 :     if (cornerDetail > 0) {
      72         1415 :         attrs[SUMO_ATTR_CORNERDETAIL] = toString(cornerDetail);
      73              :     }
      74         3230 :     if (!oc.isDefault("junctions.internal-link-detail")) {
      75            2 :         attrs[SUMO_ATTR_LINKDETAIL] = toString(oc.getInt("junctions.internal-link-detail"));
      76              :     }
      77         3230 :     if (oc.getBool("rectangular-lane-cut")) {
      78           52 :         attrs[SUMO_ATTR_RECTANGULAR_LANE_CUT] = "true";
      79              :     }
      80         3182 :     if (oc.getBool("crossings.guess") || oc.getBool("walkingareas")) {
      81          112 :         attrs[SUMO_ATTR_WALKINGAREAS] = "true";
      82              :     }
      83         3230 :     if (oc.getFloat("junctions.limit-turn-speed") > 0) {
      84         2780 :         attrs[SUMO_ATTR_LIMIT_TURN_SPEED] = toString(oc.getFloat("junctions.limit-turn-speed"));
      85              :     }
      86         3230 :     if (!oc.isDefault("check-lane-foes.all")) {
      87           10 :         attrs[SUMO_ATTR_CHECKLANEFOES_ALL] = toString(oc.getBool("check-lane-foes.all"));
      88              :     }
      89         3230 :     if (!oc.isDefault("check-lane-foes.roundabout")) {
      90            0 :         attrs[SUMO_ATTR_CHECKLANEFOES_ROUNDABOUT] = toString(oc.getBool("check-lane-foes.roundabout"));
      91              :     }
      92         3230 :     if (!oc.isDefault("tls.ignore-internal-junction-jam")) {
      93            8 :         attrs[SUMO_ATTR_TLS_IGNORE_INTERNAL_JUNCTION_JAM] = toString(oc.getBool("tls.ignore-internal-junction-jam"));
      94              :     }
      95         3230 :     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         4660 :     if (oc.exists("geometry.avoid-overlap") && !oc.getBool("geometry.avoid-overlap")) {
     102           30 :         attrs[SUMO_ATTR_AVOID_OVERLAP] = toString(oc.getBool("geometry.avoid-overlap"));
     103              :     }
     104         3230 :     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         4843 :     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         3230 :     if (!oc.isDefault("junctions.minimal-shape")) {
     111            4 :         attrs[SUMO_ATTR_JUNCTIONS_MINIMAL_SHAPE] = toString(oc.getBool("junctions.minimal-shape"));
     112              :     }
     113         3230 :     if (!oc.isDefault("junctions.endpoint-shape")) {
     114            4 :         attrs[SUMO_ATTR_JUNCTIONS_ENDPOINT_SHAPE] = toString(oc.getBool("junctions.endpoint-shape"));
     115              :     }
     116         3230 :     device.writeXMLHeader("net", "net_file.xsd", attrs); // street names may contain non-ascii chars
     117         1615 :     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         1615 :     GeoConvHelper::writeLocation(device);
     125              : 
     126              :     // write edge types and restrictions
     127         1615 :     std::set<std::string> usedTypes = ec.getUsedTypes();
     128         1615 :     nb.getTypeCont().writeEdgeTypes(device, usedTypes);
     129              : 
     130              :     // write inner lanes
     131         3230 :     if (!oc.getBool("no-internal-links")) {
     132              :         bool hadAny = false;
     133        27685 :         for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
     134        26605 :             hadAny |= writeInternalEdges(device, ec, *(*i).second);
     135              :         }
     136         1080 :         if (hadAny) {
     137         1028 :             device.lf();
     138              :         }
     139              :     }
     140              : 
     141              :     // write edges with lanes and connected edges
     142         1615 :     bool noNames = !oc.getBool("output.street-names");
     143        89890 :     for (std::map<std::string, NBEdge*>::const_iterator i = ec.begin(); i != ec.end(); ++i) {
     144        88275 :         writeEdge(device, *(*i).second, noNames);
     145              :     }
     146         1615 :     device.lf();
     147              : 
     148              :     // write tls logics
     149         1615 :     writeTrafficLights(device, nb.getTLLogicCont());
     150              : 
     151              :     // write the nodes (junctions)
     152        51060 :     for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
     153        49445 :         writeJunction(device, *(*i).second);
     154              :     }
     155         1615 :     device.lf();
     156         1615 :     const bool includeInternal = !oc.getBool("no-internal-links");
     157         1615 :     if (includeInternal) {
     158              :         // ... internal nodes if not unwanted
     159              :         bool hadAny = false;
     160        27685 :         for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
     161        26605 :             hadAny |= writeInternalNodes(device, *(*i).second);
     162              :         }
     163         1080 :         if (hadAny) {
     164          679 :             device.lf();
     165              :         }
     166              :     }
     167              : 
     168              :     // write the successors of lanes
     169              :     int numConnections = 0;
     170        89890 :     for (std::map<std::string, NBEdge*>::const_iterator it_edge = ec.begin(); it_edge != ec.end(); it_edge++) {
     171        88275 :         NBEdge* from = it_edge->second;
     172              :         const std::vector<NBEdge::Connection>& connections = from->getConnections();
     173        88275 :         numConnections += (int)connections.size();
     174       263605 :         for (const NBEdge::Connection& con : connections) {
     175       175330 :             writeConnection(device, *from, con, includeInternal);
     176              :         }
     177              :     }
     178         1615 :     if (numConnections > 0) {
     179         1533 :         device.lf();
     180              :     }
     181         1615 :     if (includeInternal) {
     182              :         // ... internal successors if not unwanted
     183              :         bool hadAny = false;
     184        27685 :         for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
     185        26605 :             hadAny |= writeInternalConnections(device, *(*i).second);
     186              :         }
     187         1080 :         if (hadAny) {
     188         1028 :             device.lf();
     189              :         }
     190              :     }
     191        51060 :     for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
     192        49445 :         NBNode* node = (*i).second;
     193              :         // write connections from pedestrian crossings
     194        49445 :         std::vector<NBNode::Crossing*> crossings = node->getCrossings();
     195        50979 :         for (auto c : crossings) {
     196         3068 :             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        53654 :         for (const NBNode::WalkingArea& wa : node->getWalkingAreas()) {
     200         5743 :             for (const std::string& cID : wa.nextCrossings) {
     201         1534 :                 const NBNode::Crossing& nextCrossing = *node->getCrossing(cID);
     202              :                 // connection to next crossing (may be tls-controlled)
     203         1534 :                 device.openTag(SUMO_TAG_CONNECTION);
     204         1534 :                 device.writeAttr(SUMO_ATTR_FROM, wa.id);
     205              :                 device.writeAttr(SUMO_ATTR_TO, cID);
     206         1534 :                 device.writeAttr(SUMO_ATTR_FROM_LANE, 0);
     207         1534 :                 device.writeAttr(SUMO_ATTR_TO_LANE, 0);
     208         1534 :                 if (nextCrossing.tlID != "") {
     209              :                     device.writeAttr(SUMO_ATTR_TLID, nextCrossing.tlID);
     210              :                     assert(nextCrossing.tlLinkIndex >= 0);
     211          605 :                     device.writeAttr(SUMO_ATTR_TLLINKINDEX, nextCrossing.tlLinkIndex);
     212              :                 }
     213         1534 :                 device.writeAttr(SUMO_ATTR_DIR, LinkDirection::STRAIGHT);
     214         2473 :                 device.writeAttr(SUMO_ATTR_STATE, nextCrossing.priority ? LINKSTATE_MAJOR : LINKSTATE_MINOR);
     215         3068 :                 device.closeTag();
     216              :             }
     217              :             // optional connections from/to sidewalk
     218              :             std::string edgeID;
     219              :             int laneIndex;
     220         9382 :             for (const std::string& sw : wa.nextSidewalks) {
     221         5173 :                 NBHelpers::interpretLaneID(sw, edgeID, laneIndex);
     222        10346 :                 NWWriter_SUMO::writeInternalConnection(device, wa.id, edgeID, 0, laneIndex, "");
     223              :             }
     224         9332 :             for (const std::string& sw : wa.prevSidewalks) {
     225         5123 :                 NBHelpers::interpretLaneID(sw, edgeID, laneIndex);
     226        10246 :                 NWWriter_SUMO::writeInternalConnection(device, edgeID, wa.id, laneIndex, 0, "");
     227              :             }
     228              :         }
     229        49445 :     }
     230              : 
     231              :     // write loaded prohibitions
     232        51060 :     for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
     233        49445 :         writeProhibitions(device, i->second->getProhibitions());
     234              :     }
     235              : 
     236              :     // write roundabout information
     237         1615 :     writeRoundabouts(device, ec.getRoundabouts(), ec);
     238              : 
     239              :     // write the districts
     240         1617 :     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         1615 :     device.close();
     248              : }
     249              : 
     250              : 
     251              : std::string
     252        91271 : NWWriter_SUMO::getOppositeInternalID(const NBEdgeCont& ec, const NBEdge* from, const NBEdge::Connection& con, double& oppositeLength) {
     253        91271 :     const NBEdge::Lane& succ = con.toEdge->getLanes()[con.toLane];
     254        91271 :     const NBEdge::Lane& pred = from->getLanes()[con.fromLane];
     255        91271 :     const bool lefthand = OptionsCont::getOptions().getBool("lefthand");
     256        91271 :     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        91217 :         return "";
     300              :     }
     301              : }
     302              : 
     303              : 
     304              : bool
     305        26605 : 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        72999 :     for (NBEdge* e : incoming) {
     312       137665 :         for (const NBEdge::Connection& c : e->getConnections()) {
     313        91271 :             double oppositeLength = 0;
     314        91271 :             const std::string op = getOppositeInternalID(ec, e, c, oppositeLength);
     315       182542 :             oppositeLaneID[c.getInternalLaneID()] = op;
     316        91271 :             if (op != "") {
     317           10 :                 oppositeLengths[c.id] = oppositeLength;
     318              :             }
     319              :         }
     320              :     }
     321        26605 :     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        72999 :     for (NBEdge* e : incoming) {
     332              :         const std::vector<NBEdge::Connection>& elv = e->getConnections();
     333        46394 :         if (elv.size() > 0) {
     334              :             bool haveVia = false;
     335        34503 :             std::string edgeID = "";
     336        34503 :             double bidiLength = -1;
     337              :             // second pass: write non-via edges
     338       125774 :             for (const NBEdge::Connection& k : elv) {
     339        91271 :                 if (k.toEdge == nullptr) {
     340              :                     assert(false); // should never happen. tell me when it does
     341            0 :                     continue;
     342              :                 }
     343        91271 :                 if (edgeID != k.id) {
     344        82153 :                     if (edgeID != "") {
     345              :                         // close the previous edge
     346        95300 :                         into.closeTag();
     347              :                     }
     348              :                     edgeID = k.id;
     349        82153 :                     into.openTag(SUMO_TAG_EDGE);
     350              :                     into.writeAttr(SUMO_ATTR_ID, edgeID);
     351        82153 :                     into.writeAttr(SUMO_ATTR_FUNCTION, SumoXMLEdgeFunc::INTERNAL);
     352        82153 :                     if (k.edgeType != "") {
     353              :                         into.writeAttr(SUMO_ATTR_TYPE, k.edgeType);
     354              :                     }
     355        82153 :                     bidiLength = -1;
     356        83336 :                     if (e->getBidiEdge() && k.toEdge->getBidiEdge() &&
     357         1183 :                             e != k.toEdge->getTurnDestination(true)) {
     358         1008 :                         const std::string bidiEdge = getInternalBidi(e, k, bidiLength);
     359         1008 :                         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        91271 :                 const NBEdge::Lane& successor = k.toEdge->getLanes()[k.toLane];
     368       182390 :                 SVCPermissions permissions = (k.permissions != SVC_UNSPECIFIED) ? k.permissions : (
     369        91119 :                                                  successor.permissions & e->getPermissions(k.fromLane));
     370        91271 :                 SVCPermissions changeLeft = k.changeLeft != SVC_UNSPECIFIED ? k.changeLeft : SVCAll;
     371        91271 :                 SVCPermissions changeRight = k.changeRight != SVC_UNSPECIFIED ? k.changeRight : SVCAll;
     372        91271 :                 const double width = e->getInternalLaneWidth(n, k, successor, false);
     373        91271 :                 const double length = bidiLength > 0 ? bidiLength : k.length;
     374       365084 :                 writeLane(into, k.getInternalLaneID(), k.vmax, k.friction,
     375        91271 :                           permissions, successor.preferred,
     376              :                           changeLeft, changeRight,
     377              :                           NBEdge::UNSPECIFIED_OFFSET, NBEdge::UNSPECIFIED_OFFSET,
     378       182542 :                           StopOffset(), width, k.shape, &k,
     379       182542 :                           length, k.internalLaneIndex, oppositeLaneID[k.getInternalLaneID()], "");
     380        91271 :                 haveVia = haveVia || k.haveVia;
     381              :             }
     382              :             ret = true;
     383        34503 :             into.closeTag(); // close the last edge
     384              :             // third pass: write via edges
     385        34503 :             if (haveVia) {
     386        10310 :                 std::string viaEdgeID = "";
     387        47816 :                 for (const NBEdge::Connection& k : elv) {
     388        37506 :                     if (!k.haveVia) {
     389        20327 :                         continue;
     390              :                     }
     391        17179 :                     if (k.toEdge == nullptr) {
     392              :                         assert(false); // should never happen. tell me when it does
     393            0 :                         continue;
     394              :                     }
     395        17179 :                     if (viaEdgeID != k.viaID) {
     396        16991 :                         if (viaEdgeID != "") {
     397              :                             // close the previous edge
     398        13362 :                             into.closeTag();
     399              :                         }
     400              :                         viaEdgeID = k.viaID;
     401              :                         // open a new edge
     402        16991 :                         into.openTag(SUMO_TAG_EDGE);
     403              :                         into.writeAttr(SUMO_ATTR_ID, viaEdgeID);
     404        16991 :                         into.writeAttr(SUMO_ATTR_FUNCTION, SumoXMLEdgeFunc::INTERNAL);
     405        16991 :                         if (k.edgeType != "") {
     406              :                             into.writeAttr(SUMO_ATTR_TYPE, k.edgeType);
     407              :                         }
     408              :                     }
     409        17179 :                     const NBEdge::Lane& successor = k.toEdge->getLanes()[k.toLane];
     410        34268 :                     SVCPermissions permissions = (k.permissions != SVC_UNSPECIFIED) ? k.permissions : (
     411        17089 :                                                      successor.permissions & e->getPermissions(k.fromLane));
     412        17179 :                     const double width = e->getInternalLaneWidth(n, k, successor, true);
     413        68716 :                     writeLane(into, k.getInternalViaLaneID(), k.vmax, k.friction, permissions, successor.preferred,
     414              :                               SVCAll, SVCAll, // #XXX todo
     415              :                               NBEdge::UNSPECIFIED_OFFSET, NBEdge::UNSPECIFIED_OFFSET,
     416        34358 :                               StopOffset(), width, k.viaShape, &k,
     417        17179 :                               MAX2(k.viaLength, POSITION_EPS), // microsim needs positive length
     418              :                               0, "", "");
     419              :                 }
     420        20620 :                 into.closeTag();
     421              :             }
     422              :         }
     423              :     }
     424              :     // write pedestrian crossings
     425        26605 :     const double crossingSpeed = OptionsCont::getOptions().getFloat("default.crossing-speed");
     426        28139 :     for (auto c : n.getCrossings()) {
     427         1534 :         into.openTag(SUMO_TAG_EDGE);
     428         1534 :         into.writeAttr(SUMO_ATTR_ID, c->id);
     429         1534 :         into.writeAttr(SUMO_ATTR_FUNCTION, SumoXMLEdgeFunc::CROSSING);
     430         1534 :         into.writeAttr(SUMO_ATTR_CROSSING_EDGES, c->edges);
     431         4602 :         writeLane(into, c->id + "_0", crossingSpeed, NBEdge::UNSPECIFIED_FRICTION, SVC_PEDESTRIAN, 0, SVCAll, SVCAll,
     432              :                   NBEdge::UNSPECIFIED_OFFSET, NBEdge::UNSPECIFIED_OFFSET,
     433         3068 :                   StopOffset(), c->width, c->shape, c,
     434         1534 :                   MAX2(c->shape.length(), POSITION_EPS), 0, "", "", false, c->customShape.size() != 0, c->outlineShape);
     435         3068 :         into.closeTag();
     436        26605 :     }
     437              :     // write pedestrian walking areas
     438        53210 :     const double walkingareaSpeed = OptionsCont::getOptions().getFloat("default.walkingarea-speed");
     439              :     const std::vector<NBNode::WalkingArea>& WalkingAreas = n.getWalkingAreas();
     440        30814 :     for (std::vector<NBNode::WalkingArea>::const_iterator it = WalkingAreas.begin(); it != WalkingAreas.end(); it++) {
     441              :         const NBNode::WalkingArea& wa = *it;
     442         4209 :         into.openTag(SUMO_TAG_EDGE);
     443         4209 :         into.writeAttr(SUMO_ATTR_ID, wa.id);
     444         4209 :         into.writeAttr(SUMO_ATTR_FUNCTION, SumoXMLEdgeFunc::WALKINGAREA);
     445        16836 :         writeLane(into, wa.id + "_0", walkingareaSpeed, NBEdge::UNSPECIFIED_FRICTION, SVC_PEDESTRIAN, 0, SVCAll, SVCAll,
     446              :                   NBEdge::UNSPECIFIED_OFFSET, NBEdge::UNSPECIFIED_OFFSET,
     447        12627 :                   StopOffset(), wa.width, wa.shape, nullptr, wa.length, 0, "", "", false, wa.hasCustomShape);
     448         8418 :         into.closeTag();
     449              :     }
     450        26605 :     return ret;
     451              : }
     452              : 
     453              : 
     454              : std::string
     455         1008 : NWWriter_SUMO::getInternalBidi(const NBEdge* e, const NBEdge::Connection& k, double& length) {
     456         1008 :     const NBEdge* fromBidi = e->getTurnDestination(true);
     457         1008 :     const NBEdge* toBidi = k.toEdge->getTurnDestination(true);
     458         1008 :     const std::vector<NBEdge::Connection> cons = toBidi->getConnectionsFromLane(-1, fromBidi, -1);
     459         1008 :     if (cons.size() > 0) {
     460         1006 :         if (e->getNumLanes() == 1 && k.toEdge->getNumLanes() == 1 && fromBidi->getNumLanes() == 1 && toBidi->getNumLanes() == 1) {
     461          990 :             length = (k.length + cons.back().length) / 2;
     462              :             return cons.back().id;
     463              :         }
     464              :         // do a more careful check in case there are parallel internal edges
     465              :         // note: k is the first connection with the new id
     466           43 :         for (const NBEdge::Connection& c : e->getConnections()) {
     467           41 :             if (c.id == k.id) {
     468           26 :                 PositionVector rShape = c.shape.reverse();
     469           62 :                 for (const NBEdge::Connection& k2 : cons) {
     470           50 :                     if (k2.shape.almostSame(rShape, POSITION_EPS)) {
     471           14 :                         length = (c.length + k2.length) / 2;
     472              :                         return k2.id;
     473              :                     }
     474              :                 }
     475           26 :             }
     476              :         }
     477              :     } else {
     478            6 :         WRITE_WARNINGF(TL("Could not find bidi-connection for edge '%'"), k.id)
     479              :     }
     480            4 :     return "";
     481         1008 : }
     482              : 
     483              : void
     484        88275 : NWWriter_SUMO::writeEdge(OutputDevice& into, const NBEdge& e, bool noNames) {
     485              :     // write the edge's begin
     486       176550 :     into.openTag(SUMO_TAG_EDGE).writeAttr(SUMO_ATTR_ID, e.getID());
     487              :     into.writeAttr(SUMO_ATTR_FROM, e.getFromNode()->getID());
     488              :     into.writeAttr(SUMO_ATTR_TO, e.getToNode()->getID());
     489        88275 :     if (!noNames && e.getStreetName() != "") {
     490        38440 :         into.writeAttr(SUMO_ATTR_NAME, StringUtils::escapeXML(e.getStreetName()));
     491              :     }
     492       176550 :     into.writeAttr(SUMO_ATTR_PRIORITY, e.getPriority());
     493        88275 :     if (e.getTypeID() != "") {
     494              :         into.writeAttr(SUMO_ATTR_TYPE, e.getTypeID());
     495              :     }
     496        88275 :     if (e.isMacroscopicConnector()) {
     497            0 :         into.writeAttr(SUMO_ATTR_FUNCTION, SumoXMLEdgeFunc::CONNECTOR);
     498              :     }
     499              :     // write the spread type if not default ("right")
     500        88275 :     if (e.getLaneSpreadFunction() != LaneSpreadFunction::RIGHT) {
     501        71582 :         into.writeAttr(SUMO_ATTR_SPREADTYPE, e.getLaneSpreadFunction());
     502              :     }
     503        88275 :     if (e.hasLoadedLength()) {
     504         3696 :         into.writeAttr(SUMO_ATTR_LENGTH, e.getLoadedLength());
     505              :     }
     506        88275 :     if (!e.hasDefaultGeometry()) {
     507              :         into.writeAttr(SUMO_ATTR_SHAPE, e.getGeometry());
     508              :     }
     509        88275 :     if (e.getEdgeStopOffset().isDefined()) {
     510           12 :         writeStopOffsets(into, e.getEdgeStopOffset());
     511              :     }
     512        88275 :     if (e.getBidiEdge()) {
     513         4084 :         into.writeAttr(SUMO_ATTR_BIDI, e.getBidiEdge()->getID());
     514              :     }
     515        88275 :     if (e.getDistance() != 0) {
     516         2030 :         into.writeAttr(SUMO_ATTR_DISTANCE, e.getDistance());
     517              :     }
     518              : 
     519              :     // write the lanes
     520              :     const std::vector<NBEdge::Lane>& lanes = e.getLanes();
     521              : 
     522        88275 :     double length = e.getFinalLength();
     523        88275 :     if (e.getBidiEdge() != nullptr) {
     524         4084 :         length = (length + e.getBidiEdge()->getFinalLength()) / 2;
     525              :     }
     526        88275 :     double startOffset = e.isBidiRail() ? e.getTurnDestination(true)->getEndOffset() : 0;
     527       203128 :     for (int i = 0; i < (int) lanes.size(); i++) {
     528       114853 :         const NBEdge::Lane& l = lanes[i];
     529       114853 :         StopOffset stopOffset;
     530       114853 :         if (l.laneStopOffset != e.getEdgeStopOffset()) {
     531           27 :             stopOffset = l.laneStopOffset;
     532              :         }
     533       344559 :         writeLane(into, e.getLaneID(i), l.speed, l.friction,
     534       114853 :                   l.permissions, l.preferred,
     535       114853 :                   l.changeLeft, l.changeRight,
     536       114853 :                   startOffset, l.endOffset,
     537       114853 :                   stopOffset, l.width, l.shape, &l,
     538       114853 :                   length, i, l.oppositeID, l.type, l.accelRamp, l.customShape.size() > 0);
     539              :     }
     540              :     // close the edge
     541        88275 :     e.writeParams(into);
     542        88275 :     into.closeTag();
     543        88275 : }
     544              : 
     545              : 
     546              : void
     547       229046 : NWWriter_SUMO::writeLane(OutputDevice& into, const std::string& lID,
     548              :                          double speed, double friction,
     549              :                          SVCPermissions permissions, SVCPermissions preferred,
     550              :                          SVCPermissions changeLeft, SVCPermissions changeRight,
     551              :                          double startOffset, double endOffset,
     552              :                          const StopOffset& stopOffset, double width, PositionVector shape,
     553              :                          const Parameterised* params, double length, int index,
     554              :                          const std::string& oppositeID,
     555              :                          const std::string& type,
     556              :                          bool accelRamp, bool customShape,
     557              :                          const PositionVector& outlineShape) {
     558              :     // output the lane's attributes
     559       458092 :     into.openTag(SUMO_TAG_LANE).writeAttr(SUMO_ATTR_ID, lID);
     560              :     // the first lane of an edge will be the depart lane
     561              :     into.writeAttr(SUMO_ATTR_INDEX, index);
     562              :     // write the list of allowed/disallowed vehicle classes
     563       229046 :     if (permissions != SVC_UNSPECIFIED) {
     564       229046 :         writePermissions(into, permissions);
     565              :     }
     566       229046 :     writePreferences(into, preferred);
     567              :     // some further information
     568       229046 :     into.writeAttr(SUMO_ATTR_SPEED, MAX2(0.0, speed));
     569       229046 :     if (friction != NBEdge::UNSPECIFIED_FRICTION) {
     570              :         into.writeAttr(SUMO_ATTR_FRICTION, friction);
     571              :     }
     572              :     into.writeAttr(SUMO_ATTR_LENGTH, length);
     573       229046 :     if (endOffset != NBEdge::UNSPECIFIED_OFFSET) {
     574              :         into.writeAttr(SUMO_ATTR_ENDOFFSET, endOffset);
     575              :     }
     576       229046 :     if (width != NBEdge::UNSPECIFIED_WIDTH) {
     577              :         into.writeAttr(SUMO_ATTR_WIDTH, width);
     578              :     }
     579       229046 :     if (accelRamp) {
     580              :         into.writeAttr<bool>(SUMO_ATTR_ACCELERATION, accelRamp);
     581              :     }
     582       229046 :     if (customShape) {
     583           86 :         into.writeAttr(SUMO_ATTR_CUSTOMSHAPE, true);
     584              :     }
     585       229046 :     if (endOffset > 0 || startOffset > 0) {
     586           41 :         startOffset = MIN2(startOffset, shape.length() - POSITION_EPS);
     587           41 :         endOffset = MIN2(endOffset, shape.length() - startOffset - POSITION_EPS);
     588              :         assert(startOffset + endOffset < shape.length());
     589           82 :         shape = shape.getSubpart(startOffset, shape.length() - endOffset);
     590              :     }
     591              :     into.writeAttr(SUMO_ATTR_SHAPE, shape);
     592       229046 :     if (type != "") {
     593              :         into.writeAttr(SUMO_ATTR_TYPE, type);
     594              :     }
     595       229046 :     if (changeLeft != SVC_UNSPECIFIED && changeLeft != SVCAll && changeLeft != SVC_IGNORING) {
     596          183 :         into.writeAttr(SUMO_ATTR_CHANGE_LEFT, getVehicleClassNames(changeLeft));
     597              :     }
     598       229046 :     if (changeRight != SVC_UNSPECIFIED && changeRight != SVCAll && changeRight != SVC_IGNORING) {
     599           71 :         into.writeAttr(SUMO_ATTR_CHANGE_RIGHT, getVehicleClassNames(changeRight));
     600              :     }
     601       229046 :     if (stopOffset.isDefined()) {
     602           13 :         writeStopOffsets(into, stopOffset);
     603              :     }
     604       229046 :     if (outlineShape.size() != 0) {
     605              :         into.writeAttr(SUMO_ATTR_OUTLINESHAPE, outlineShape);
     606              :     }
     607              : 
     608       229046 :     if (oppositeID != "" && oppositeID != "-") {
     609           60 :         into.openTag(SUMO_TAG_NEIGH);
     610              :         into.writeAttr(SUMO_ATTR_LANE, oppositeID);
     611          120 :         into.closeTag();
     612              :     }
     613              : 
     614       229046 :     if (params != nullptr) {
     615       224837 :         params->writeParams(into);
     616              :     }
     617              : 
     618       229046 :     into.closeTag();
     619       229046 : }
     620              : 
     621              : 
     622              : void
     623        49445 : NWWriter_SUMO::writeJunction(OutputDevice& into, const NBNode& n) {
     624              :     // write the attributes
     625        98890 :     into.openTag(SUMO_TAG_JUNCTION).writeAttr(SUMO_ATTR_ID, n.getID());
     626        98890 :     into.writeAttr(SUMO_ATTR_TYPE, n.getType());
     627        49445 :     NWFrame::writePositionLong(n.getPosition(), into);
     628              :     // write the incoming lanes
     629              :     std::vector<std::string> incLanes;
     630              :     const std::vector<NBEdge*>& incoming = n.getIncomingEdges();
     631       137720 :     for (std::vector<NBEdge*>::const_iterator i = incoming.begin(); i != incoming.end(); ++i) {
     632        88275 :         int noLanes = (*i)->getNumLanes();
     633       203128 :         for (int j = 0; j < noLanes; j++) {
     634       229706 :             incLanes.push_back((*i)->getLaneID(j));
     635              :         }
     636              :     }
     637        49445 :     std::vector<NBNode::Crossing*> crossings = n.getCrossings();
     638              :     std::set<std::string> prevWAs;
     639              :     // avoid duplicates
     640        50979 :     for (auto c : crossings) {
     641         1534 :         if (prevWAs.count(c->prevWalkingArea) == 0) {
     642         3048 :             incLanes.push_back(c->prevWalkingArea + "_0");
     643              :             prevWAs.insert(c->prevWalkingArea);
     644              :         }
     645              :     }
     646              :     into.writeAttr(SUMO_ATTR_INCLANES, incLanes);
     647              :     // write the internal lanes
     648              :     std::vector<std::string> intLanes;
     649        98890 :     if (!OptionsCont::getOptions().getBool("no-internal-links")) {
     650        72999 :         for (EdgeVector::const_iterator i = incoming.begin(); i != incoming.end(); i++) {
     651        46394 :             const std::vector<NBEdge::Connection>& elv = (*i)->getConnections();
     652       137665 :             for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
     653        91271 :                 if ((*k).toEdge == nullptr) {
     654            0 :                     continue;
     655              :                 }
     656        91271 :                 if (!(*k).haveVia) {
     657       148184 :                     intLanes.push_back((*k).getInternalLaneID());
     658              :                 } else {
     659        34358 :                     intLanes.push_back((*k).getInternalViaLaneID());
     660              :                 }
     661              :             }
     662              :         }
     663              :     }
     664        49445 :     if (n.getType() != SumoXMLNodeType::DEAD_END && n.getType() != SumoXMLNodeType::NOJUNCTION) {
     665        37159 :         for (auto c : crossings) {
     666         3054 :             intLanes.push_back(c->id + "_0");
     667              :         }
     668              :     }
     669              :     into.writeAttr(SUMO_ATTR_INTLANES, intLanes);
     670              :     // close writing
     671        98890 :     into.writeAttr(SUMO_ATTR_SHAPE, n.getShape().simplified());
     672              :     // write optional radius
     673        49445 :     if (n.getRadius() != NBNode::UNSPECIFIED_RADIUS) {
     674          112 :         into.writeAttr(SUMO_ATTR_RADIUS, n.getRadius());
     675              :     }
     676              :     // specify whether a custom shape was used
     677        49445 :     if (n.hasCustomShape()) {
     678           42 :         into.writeAttr(SUMO_ATTR_CUSTOMSHAPE, true);
     679              :     }
     680        49445 :     if (n.getRightOfWay() != RightOfWay::DEFAULT) {
     681           64 :         into.writeAttr<std::string>(SUMO_ATTR_RIGHT_OF_WAY, toString(n.getRightOfWay()));
     682              :     }
     683        49445 :     if (n.getFringeType() != FringeType::DEFAULT) {
     684          636 :         into.writeAttr<std::string>(SUMO_ATTR_FRINGE, toString(n.getFringeType()));
     685              :     }
     686        49445 :     if (n.getName() != "") {
     687            8 :         into.writeAttr<std::string>(SUMO_ATTR_NAME, StringUtils::escapeXML(n.getName()));
     688              :     }
     689        49445 :     if (n.getType() != SumoXMLNodeType::DEAD_END) {
     690              :         // write right-of-way logics
     691        35707 :         n.writeLogic(into);
     692              :     }
     693        49445 :     n.writeParams(into);
     694        49445 :     into.closeTag();
     695        98890 : }
     696              : 
     697              : 
     698              : bool
     699        26605 : NWWriter_SUMO::writeInternalNodes(OutputDevice& into, const NBNode& n) {
     700              :     bool ret = false;
     701              :     const std::vector<NBEdge*>& incoming = n.getIncomingEdges();
     702              :     // build the list of internal lane ids
     703              :     std::vector<std::string> internalLaneIDs;
     704              :     std::map<std::string, std::string> viaIDs;
     705        72999 :     for (EdgeVector::const_iterator i = incoming.begin(); i != incoming.end(); i++) {
     706        46394 :         const std::vector<NBEdge::Connection>& elv = (*i)->getConnections();
     707       137665 :         for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
     708        91271 :             if ((*k).toEdge != nullptr) {
     709        91271 :                 internalLaneIDs.push_back((*k).getInternalLaneID());
     710        91271 :                 if ((*k).viaID != "") {
     711        34358 :                     viaIDs[(*k).getInternalLaneID()] = ((*k).getInternalViaLaneID());
     712              :                 }
     713              :             }
     714              :         }
     715              :     }
     716        28139 :     for (auto c : n.getCrossings()) {
     717         3068 :         internalLaneIDs.push_back(c->id + "_0");
     718        26605 :     }
     719              :     // write the internal nodes
     720        72999 :     for (std::vector<NBEdge*>::const_iterator i = incoming.begin(); i != incoming.end(); i++) {
     721        46394 :         const std::vector<NBEdge::Connection>& elv = (*i)->getConnections();
     722       137665 :         for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
     723        91271 :             if ((*k).toEdge == nullptr || !(*k).haveVia) {
     724        74092 :                 continue;
     725              :             }
     726        17179 :             Position pos = (*k).shape[-1];
     727        17179 :             into.openTag(SUMO_TAG_JUNCTION).writeAttr(SUMO_ATTR_ID, (*k).getInternalViaLaneID());
     728        17179 :             into.writeAttr(SUMO_ATTR_TYPE, SumoXMLNodeType::INTERNAL);
     729        17179 :             NWFrame::writePositionLong(pos, into);
     730        17179 :             std::string incLanes = (*k).getInternalLaneID();
     731              :             std::vector<std::string> foeIDs;
     732        52166 :             for (std::string incLane : (*k).foeIncomingLanes) {
     733        34987 :                 if (incLane[0] == ':') {
     734              :                     // intersecting left turns
     735          167 :                     const int index = StringUtils::toInt(incLane.substr(1));
     736          167 :                     incLane = internalLaneIDs[index];
     737          167 :                     if (viaIDs[incLane] != "") {
     738          167 :                         foeIDs.push_back(viaIDs[incLane]);
     739              :                     }
     740              :                 }
     741        69974 :                 incLanes += " " + incLane;
     742              :             }
     743              :             into.writeAttr(SUMO_ATTR_INCLANES, incLanes);
     744              :             const std::vector<int>& foes = (*k).foeInternalLinks;
     745       102244 :             for (std::vector<int>::const_iterator it = foes.begin(); it != foes.end(); ++it) {
     746        85065 :                 foeIDs.push_back(internalLaneIDs[*it]);
     747              :             }
     748        17179 :             into.writeAttr(SUMO_ATTR_INTLANES, joinToString(foeIDs, " "));
     749        17179 :             into.closeTag();
     750              :             ret = true;
     751        17179 :         }
     752              :     }
     753        26605 :     return ret;
     754        26605 : }
     755              : 
     756              : 
     757              : void
     758       180048 : NWWriter_SUMO::writeConnection(OutputDevice& into, const NBEdge& from, const NBEdge::Connection& c,
     759              :                                bool includeInternal, ConnectionStyle style, bool geoAccuracy) {
     760              :     assert(c.toEdge != 0);
     761       180048 :     into.openTag(SUMO_TAG_CONNECTION);
     762       180048 :     into.writeAttr(SUMO_ATTR_FROM, from.getID());
     763       180048 :     into.writeAttr(SUMO_ATTR_TO, c.toEdge->getID());
     764       180048 :     into.writeAttr(SUMO_ATTR_FROM_LANE, c.fromLane);
     765       180048 :     into.writeAttr(SUMO_ATTR_TO_LANE, c.toLane);
     766       180048 :     if (style != TLL) {
     767       178983 :         if (c.mayDefinitelyPass) {
     768           25 :             into.writeAttr(SUMO_ATTR_PASS, c.mayDefinitelyPass);
     769              :         }
     770       178983 :         if (c.keepClear == KEEPCLEAR_FALSE) {
     771          878 :             into.writeAttr<bool>(SUMO_ATTR_KEEP_CLEAR, false);
     772              :         }
     773       178983 :         if (c.contPos != NBEdge::UNSPECIFIED_CONTPOS) {
     774           50 :             into.writeAttr(SUMO_ATTR_CONTPOS, c.contPos);
     775              :         }
     776       178983 :         if (c.permissions != SVC_UNSPECIFIED) {
     777          203 :             writePermissions(into, c.permissions);
     778              :         }
     779       178983 :         if (c.changeLeft != SVC_UNSPECIFIED && c.changeLeft != SVCAll && c.changeLeft != SVC_IGNORING) {
     780            6 :             into.writeAttr(SUMO_ATTR_CHANGE_LEFT, getVehicleClassNames(c.changeLeft));
     781              :         }
     782       178983 :         if (c.changeRight != SVC_UNSPECIFIED && c.changeRight != SVCAll && c.changeRight != SVC_IGNORING) {
     783            4 :             into.writeAttr(SUMO_ATTR_CHANGE_RIGHT, getVehicleClassNames(c.changeRight));
     784              :         }
     785       178983 :         if (c.speed != NBEdge::UNSPECIFIED_SPEED) {
     786          229 :             into.writeAttr(SUMO_ATTR_SPEED, c.speed);
     787              :         }
     788       178983 :         if (c.customLength != NBEdge::UNSPECIFIED_LOADED_LENGTH) {
     789           55 :             into.writeAttr(SUMO_ATTR_LENGTH, c.customLength);
     790              :         }
     791       178983 :         if (c.customShape.size() != 0) {
     792           48 :             if (geoAccuracy) {
     793            1 :                 into.setPrecision(gPrecisionGeo);
     794              :             }
     795           48 :             into.writeAttr(SUMO_ATTR_SHAPE, c.customShape);
     796           48 :             if (geoAccuracy) {
     797            1 :                 into.setPrecision();
     798              :             }
     799              :         }
     800       178983 :         if (c.uncontrolled != false) {
     801           35 :             into.writeAttr(SUMO_ATTR_UNCONTROLLED, c.uncontrolled);
     802              :         }
     803       178983 :         if (c.indirectLeft != false) {
     804           21 :             into.writeAttr(SUMO_ATTR_INDIRECT, c.indirectLeft);
     805              :         }
     806       178983 :         if (c.edgeType != "") {
     807              :             into.writeAttr(SUMO_ATTR_TYPE, c.edgeType);
     808              :         }
     809              :     }
     810       178983 :     if (style != PLAIN) {
     811       176395 :         if (includeInternal) {
     812       182542 :             into.writeAttr(SUMO_ATTR_VIA, c.getInternalLaneID());
     813              :         }
     814              :         // set information about the controlling tl if any
     815       176395 :         if (c.tlID != "") {
     816              :             into.writeAttr(SUMO_ATTR_TLID, c.tlID);
     817        22614 :             into.writeAttr(SUMO_ATTR_TLLINKINDEX, c.tlLinkIndex);
     818        22614 :             if (c.tlLinkIndex2 >= 0) {
     819           13 :                 into.writeAttr(SUMO_ATTR_TLLINKINDEX2, c.tlLinkIndex2);
     820              :             }
     821              :         }
     822              :     }
     823       176395 :     if (style != TLL) {
     824       178983 :         if (style == SUMONET) {
     825              :             // write the direction information
     826       175330 :             LinkDirection dir = from.getToNode()->getDirection(&from, c.toEdge, OptionsCont::getOptions().getBool("lefthand"));
     827              :             assert(dir != LinkDirection::NODIR);
     828       350660 :             into.writeAttr(SUMO_ATTR_DIR, toString(dir));
     829              :             // write the state information
     830       175330 :             const LinkState linkState = from.getToNode()->getLinkState(
     831       175330 :                                             &from, c.toEdge, c.fromLane, c.toLane, c.mayDefinitelyPass, c.tlID);
     832              :             into.writeAttr(SUMO_ATTR_STATE, linkState);
     833              :             if (linkState == LINKSTATE_MINOR
     834        64399 :                     && c.visibility == NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE
     835       239700 :                     && c.toEdge->getJunctionPriority(c.toEdge->getToNode()) == NBEdge::JunctionPriority::ROUNDABOUT) {
     836          178 :                 const double visibilityDistance = OptionsCont::getOptions().getFloat("roundabouts.visibility-distance");
     837          178 :                 if (visibilityDistance != NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE) {
     838              :                     into.writeAttr(SUMO_ATTR_VISIBILITY_DISTANCE, visibilityDistance);
     839              :                 }
     840              :             }
     841              :         }
     842       178983 :         if (c.visibility != NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE) {
     843           33 :             into.writeAttr(SUMO_ATTR_VISIBILITY_DISTANCE, c.visibility);
     844              :         }
     845              :     }
     846       180048 :     c.writeParams(into);
     847       180048 :     into.closeTag();
     848       180048 : }
     849              : 
     850              : 
     851              : bool
     852        26605 : NWWriter_SUMO::writeInternalConnections(OutputDevice& into, const NBNode& n) {
     853              :     bool ret = false;
     854        53210 :     const bool lefthand = OptionsCont::getOptions().getBool("lefthand");
     855        72999 :     for (const NBEdge* const from : n.getIncomingEdges()) {
     856       137665 :         for (const NBEdge::Connection& c : from->getConnections()) {
     857        91271 :             LinkDirection dir = n.getDirection(from, c.toEdge, lefthand);
     858              :             assert(c.toEdge != 0);
     859        91271 :             if (c.haveVia) {
     860              :                 // internal split with optional signal
     861        17179 :                 std::string tlID = "";
     862        17179 :                 int linkIndex2 = NBConnection::InvalidTlIndex;
     863        17179 :                 if (c.tlLinkIndex2 != NBConnection::InvalidTlIndex) {
     864              :                     linkIndex2 = c.tlLinkIndex2;
     865            9 :                     tlID = c.tlID;
     866              :                 }
     867        17179 :                 writeInternalConnection(into, c.id, c.toEdge->getID(), c.internalLaneIndex, c.toLane, c.getInternalViaLaneID(), dir, tlID, linkIndex2, false, c.visibility);
     868        34358 :                 writeInternalConnection(into, c.viaID, c.toEdge->getID(), c.internalViaLaneIndex, c.toLane, "", dir, "", NBConnection::InvalidTlIndex, n.brakeForCrossingOnExit(c.toEdge));
     869              :             } else {
     870              :                 // no internal split
     871       148184 :                 writeInternalConnection(into, c.id, c.toEdge->getID(), c.internalLaneIndex, c.toLane, "", dir);
     872              :             }
     873              :             ret = true;
     874              :         }
     875              :     }
     876        26605 :     return ret;
     877              : }
     878              : 
     879              : 
     880              : void
     881       120280 : NWWriter_SUMO::writeInternalConnection(OutputDevice& into,
     882              :                                        const std::string& from, const std::string& to,
     883              :                                        int fromLane, int toLane, const std::string& via,
     884              :                                        LinkDirection dir,
     885              :                                        const std::string& tlID, int linkIndex,
     886              :                                        bool minor,
     887              :                                        double visibility) {
     888       120280 :     into.openTag(SUMO_TAG_CONNECTION);
     889              :     into.writeAttr(SUMO_ATTR_FROM, from);
     890              :     into.writeAttr(SUMO_ATTR_TO, to);
     891              :     into.writeAttr(SUMO_ATTR_FROM_LANE, fromLane);
     892              :     into.writeAttr(SUMO_ATTR_TO_LANE, toLane);
     893       120280 :     if (via != "") {
     894              :         into.writeAttr(SUMO_ATTR_VIA, via);
     895              :     }
     896       120280 :     if (tlID != "" && linkIndex != NBConnection::InvalidTlIndex) {
     897              :         // used for the reverse direction of pedestrian crossings
     898              :         into.writeAttr(SUMO_ATTR_TLID, tlID);
     899              :         into.writeAttr(SUMO_ATTR_TLLINKINDEX, linkIndex);
     900              :     }
     901              :     into.writeAttr(SUMO_ATTR_DIR, dir);
     902       120280 :     into.writeAttr(SUMO_ATTR_STATE, ((via != "" || minor) ? "m" : "M"));
     903       120280 :     if (visibility != NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE) {
     904              :         into.writeAttr(SUMO_ATTR_VISIBILITY_DISTANCE, visibility);
     905              :     }
     906       120280 :     into.closeTag();
     907       120280 : }
     908              : 
     909              : 
     910              : void
     911         1619 : NWWriter_SUMO::writeRoundabouts(OutputDevice& into, const std::set<EdgeSet>& roundabouts,
     912              :                                 const NBEdgeCont& ec) {
     913              :     //  make output deterministic
     914              :     std::vector<std::vector<std::string> > edgeIDs;
     915         1690 :     for (std::set<EdgeSet>::const_iterator i = roundabouts.begin(); i != roundabouts.end(); ++i) {
     916              :         std::vector<std::string> tEdgeIDs;
     917          413 :         for (EdgeSet::const_iterator j = (*i).begin(); j != (*i).end(); ++j) {
     918              :             // the edges may have been erased from NBEdgeCont but their pointers are still valid
     919              :             // we verify their existance in writeRoundabout()
     920          342 :             tEdgeIDs.push_back((*j)->getID());
     921              :         }
     922           71 :         std::sort(tEdgeIDs.begin(), tEdgeIDs.end());
     923           71 :         edgeIDs.push_back(tEdgeIDs);
     924           71 :     }
     925         1619 :     std::sort(edgeIDs.begin(), edgeIDs.end());
     926              :     //  write
     927         1690 :     for (std::vector<std::vector<std::string> >::const_iterator i = edgeIDs.begin(); i != edgeIDs.end(); ++i) {
     928           71 :         writeRoundabout(into, *i, ec);
     929              :     }
     930         1619 :     if (roundabouts.size() != 0) {
     931           58 :         into.lf();
     932              :     }
     933         1619 : }
     934              : 
     935              : 
     936              : void
     937           71 : NWWriter_SUMO::writeRoundabout(OutputDevice& into, const std::vector<std::string>& edgeIDs,
     938              :                                const NBEdgeCont& ec) {
     939              :     std::vector<std::string> validEdgeIDs;
     940              :     std::vector<std::string> invalidEdgeIDs;
     941              :     std::vector<std::string> nodeIDs;
     942          413 :     for (std::vector<std::string>::const_iterator i = edgeIDs.begin(); i != edgeIDs.end(); ++i) {
     943          342 :         const NBEdge* edge = ec.retrieve(*i);
     944          342 :         if (edge != nullptr) {
     945          339 :             nodeIDs.push_back(edge->getToNode()->getID());
     946          339 :             validEdgeIDs.push_back(edge->getID());
     947              :         } else {
     948            3 :             invalidEdgeIDs.push_back(*i);
     949              :         }
     950              :     }
     951           71 :     std::sort(nodeIDs.begin(), nodeIDs.end());
     952           71 :     if (validEdgeIDs.size() > 0) {
     953           71 :         into.openTag(SUMO_TAG_ROUNDABOUT);
     954           71 :         into.writeAttr(SUMO_ATTR_NODES, joinToString(nodeIDs, " "));
     955           71 :         into.writeAttr(SUMO_ATTR_EDGES, joinToString(validEdgeIDs, " "));
     956          142 :         into.closeTag();
     957           71 :         if (invalidEdgeIDs.size() > 0) {
     958            6 :             WRITE_WARNING("Writing incomplete roundabout. Edges: '"
     959              :                           + joinToString(invalidEdgeIDs, " ") + "' no longer exist'");
     960              :         }
     961              :     }
     962           71 : }
     963              : 
     964              : 
     965              : void
     966           50 : NWWriter_SUMO::writeDistrict(OutputDevice& into, const NBDistrict& d) {
     967           50 :     std::vector<double> sourceW = d.getSourceWeights();
     968           50 :     VectorHelper<double>::normaliseSum(sourceW, 1.0);
     969           50 :     std::vector<double> sinkW = d.getSinkWeights();
     970           50 :     VectorHelper<double>::normaliseSum(sinkW, 1.0);
     971              :     // write the head and the id of the district
     972          100 :     into.openTag(SUMO_TAG_TAZ).writeAttr(SUMO_ATTR_ID, d.getID());
     973           50 :     if (d.getShape().size() > 0) {
     974              :         into.writeAttr(SUMO_ATTR_SHAPE, d.getShape());
     975              :     }
     976              :     // write all sources
     977              :     const std::vector<NBEdge*>& sources = d.getSourceEdges();
     978           50 :     for (int i = 0; i < (int)sources.size(); i++) {
     979              :         // write the head and the id of the source
     980            0 :         into.openTag(SUMO_TAG_TAZSOURCE).writeAttr(SUMO_ATTR_ID, sources[i]->getID()).writeAttr(SUMO_ATTR_WEIGHT, sourceW[i]);
     981            0 :         into.closeTag();
     982              :     }
     983              :     // write all sinks
     984              :     const std::vector<NBEdge*>& sinks = d.getSinkEdges();
     985           50 :     for (int i = 0; i < (int)sinks.size(); i++) {
     986              :         // write the head and the id of the sink
     987            0 :         into.openTag(SUMO_TAG_TAZSINK).writeAttr(SUMO_ATTR_ID, sinks[i]->getID()).writeAttr(SUMO_ATTR_WEIGHT, sinkW[i]);
     988            0 :         into.closeTag();
     989              :     }
     990              :     // write the tail
     991           50 :     into.closeTag();
     992           50 : }
     993              : 
     994              : 
     995              : std::string
     996        15093 : NWWriter_SUMO::writeSUMOTime(SUMOTime steps) {
     997        15093 :     double time = STEPS2TIME(steps);
     998        15093 :     if (time == std::floor(time)) {
     999        15075 :         return toString(int(time));
    1000              :     } else {
    1001           18 :         return toString(time);
    1002              :     }
    1003              : }
    1004              : 
    1005              : void
    1006        50615 : NWWriter_SUMO::writeProhibitions(OutputDevice& into, const NBConnectionProhibits& prohibitions) {
    1007        50717 :     for (NBConnectionProhibits::const_iterator j = prohibitions.begin(); j != prohibitions.end(); j++) {
    1008          102 :         NBConnection prohibited = (*j).first;
    1009              :         const NBConnectionVector& prohibiting = (*j).second;
    1010          301 :         for (NBConnectionVector::const_iterator k = prohibiting.begin(); k != prohibiting.end(); k++) {
    1011          199 :             NBConnection prohibitor = *k;
    1012          199 :             into.openTag(SUMO_TAG_PROHIBITION);
    1013          199 :             into.writeAttr(SUMO_ATTR_PROHIBITOR, prohibitionConnection(prohibitor));
    1014          199 :             into.writeAttr(SUMO_ATTR_PROHIBITED, prohibitionConnection(prohibited));
    1015          199 :             into.closeTag();
    1016          199 :         }
    1017          102 :     }
    1018        50615 : }
    1019              : 
    1020              : 
    1021              : std::string
    1022          398 : NWWriter_SUMO::prohibitionConnection(const NBConnection& c) {
    1023          796 :     return c.getFrom()->getID() + "->" + c.getTo()->getID();
    1024              : }
    1025              : 
    1026              : 
    1027              : void
    1028         1777 : NWWriter_SUMO::writeTrafficLights(OutputDevice& into, const NBTrafficLightLogicCont& tllCont) {
    1029         1777 :     std::vector<NBTrafficLightLogic*> logics = tllCont.getComputed();
    1030         3868 :     for (NBTrafficLightLogic* logic : logics) {
    1031         2091 :         writeTrafficLight(into, logic);
    1032              :         // only raise warnings on write instead of on compute (to avoid cluttering netedit)
    1033         2091 :         NBTrafficLightDefinition* def = tllCont.getDefinition(logic->getID(), logic->getProgramID());
    1034              :         assert(def != nullptr);
    1035         2091 :         def->finalChecks();
    1036              :     }
    1037         1777 :     if (logics.size() > 0) {
    1038          662 :         into.lf();
    1039              :     }
    1040         1777 : }
    1041              : 
    1042              : 
    1043              : void
    1044         2091 : NWWriter_SUMO::writeTrafficLight(OutputDevice& into, const NBTrafficLightLogic* logic) {
    1045         2091 :     into.openTag(SUMO_TAG_TLLOGIC);
    1046              :     into.writeAttr(SUMO_ATTR_ID, logic->getID());
    1047         4182 :     into.writeAttr(SUMO_ATTR_TYPE, logic->getType());
    1048              :     into.writeAttr(SUMO_ATTR_PROGRAMID, logic->getProgramID());
    1049         4182 :     into.writeAttr(SUMO_ATTR_OFFSET, logic->getOffset() == SUMOTime_MAX ? "begin" : writeSUMOTime(logic->getOffset()));
    1050              :     // write the phases
    1051              :     const bool varPhaseLength = logic->getType() != TrafficLightType::STATIC;
    1052        12498 :     for (const NBTrafficLightLogic::PhaseDefinition& phase : logic->getPhases()) {
    1053        10407 :         into.openTag(SUMO_TAG_PHASE);
    1054        10407 :         into.writeAttr(SUMO_ATTR_DURATION, writeSUMOTime(phase.duration));
    1055        10407 :         if (phase.duration < TIME2STEPS(10)) {
    1056        13088 :             into.writePadding(" ");
    1057              :         }
    1058        10407 :         into.writeAttr(SUMO_ATTR_STATE, phase.state);
    1059        10407 :         if (varPhaseLength) {
    1060         2546 :             if (phase.minDur != NBTrafficLightDefinition::UNSPECIFIED_DURATION) {
    1061         2352 :                 into.writeAttr(SUMO_ATTR_MINDURATION, writeSUMOTime(phase.minDur));
    1062              :             }
    1063         2546 :             if (phase.maxDur != NBTrafficLightDefinition::UNSPECIFIED_DURATION) {
    1064         2352 :                 into.writeAttr(SUMO_ATTR_MAXDURATION, writeSUMOTime(phase.maxDur));
    1065              :             }
    1066         2546 :             if (phase.earliestEnd != NBTrafficLightDefinition::UNSPECIFIED_DURATION) {
    1067            2 :                 into.writeAttr(SUMO_ATTR_EARLIEST_END, writeSUMOTime(phase.earliestEnd));
    1068              :             }
    1069         2546 :             if (phase.latestEnd != NBTrafficLightDefinition::UNSPECIFIED_DURATION) {
    1070            2 :                 into.writeAttr(SUMO_ATTR_LATEST_END, writeSUMOTime(phase.latestEnd));
    1071              :             }
    1072              :             // NEMA attributes
    1073         2546 :             if (phase.vehExt != NBTrafficLightDefinition::UNSPECIFIED_DURATION) {
    1074          162 :                 into.writeAttr(SUMO_ATTR_VEHICLEEXTENSION, writeSUMOTime(phase.vehExt));
    1075              :             }
    1076         2546 :             if (phase.yellow != NBTrafficLightDefinition::UNSPECIFIED_DURATION) {
    1077          162 :                 into.writeAttr(SUMO_ATTR_YELLOW, writeSUMOTime(phase.yellow));
    1078              :             }
    1079         2546 :             if (phase.red != NBTrafficLightDefinition::UNSPECIFIED_DURATION) {
    1080          162 :                 into.writeAttr(SUMO_ATTR_RED, writeSUMOTime(phase.red));
    1081              :             }
    1082              :         }
    1083        10407 :         if (phase.name != "") {
    1084          222 :             into.writeAttr(SUMO_ATTR_NAME, StringUtils::escapeXML(phase.name));
    1085              :         }
    1086        10407 :         if (phase.next.size() > 0) {
    1087           28 :             into.writeAttr(SUMO_ATTR_NEXT, phase.next);
    1088              :         }
    1089        20814 :         into.closeTag();
    1090              :     }
    1091              :     // write params
    1092         2091 :     logic->writeParams(into);
    1093         2091 :     into.closeTag();
    1094         2091 : }
    1095              : 
    1096              : 
    1097              : void
    1098         1093 : NWWriter_SUMO::writeStopOffsets(OutputDevice& into, const StopOffset& stopOffset) {
    1099         1093 :     if (stopOffset.isDefined()) {
    1100           25 :         const std::string ss_vclasses = getVehicleClassNames(stopOffset.getPermissions());
    1101           25 :         if (ss_vclasses.length() == 0) {
    1102              :             // This stopOffset would have no effect...
    1103              :             return;
    1104              :         }
    1105           22 :         into.openTag(SUMO_TAG_STOPOFFSET);
    1106           22 :         const std::string ss_exceptions = getVehicleClassNames(~stopOffset.getPermissions());
    1107           22 :         if (ss_vclasses.length() <= ss_exceptions.length()) {
    1108              :             into.writeAttr(SUMO_ATTR_VCLASSES, ss_vclasses);
    1109              :         } else {
    1110           14 :             if (ss_exceptions.length() == 0) {
    1111              :                 into.writeAttr(SUMO_ATTR_VCLASSES, "all");
    1112              :             } else {
    1113              :                 into.writeAttr(SUMO_ATTR_EXCEPTIONS, ss_exceptions);
    1114              :             }
    1115              :         }
    1116           22 :         into.writeAttr(SUMO_ATTR_VALUE, stopOffset.getOffset());
    1117           44 :         into.closeTag();
    1118              :     }
    1119              : }
    1120              : 
    1121              : 
    1122              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1