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