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