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 : /****************************************************************************/
|