1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file    NWWriter_XML.cpp
11 /// @author  Daniel Krajzewicz
12 /// @author  Jakob Erdmann
13 /// @author  Michael Behrisch
14 /// @author  Leonhard Luecken
15 /// @date    Tue, 11.05.2011
16 /// @version $Id$
17 ///
18 // Exporter writing networks using XML (native input) format
19 /****************************************************************************/
20 
21 
22 // ===========================================================================
23 // included modules
24 // ===========================================================================
25 #include <config.h>
26 #include <algorithm>
27 #include <utils/common/MsgHandler.h>
28 #include <netbuild/NBEdge.h>
29 #include <netbuild/NBEdgeCont.h>
30 #include <netbuild/NBNode.h>
31 #include <netbuild/NBNodeCont.h>
32 #include <netbuild/NBNetBuilder.h>
33 #include <netbuild/NBPTLineCont.h>
34 #include <netbuild/NBParking.h>
35 #include <utils/common/ToString.h>
36 #include <utils/common/StringUtils.h>
37 #include <utils/options/OptionsCont.h>
38 #include <utils/iodevices/OutputDevice.h>
39 #include <utils/geom/GeoConvHelper.h>
40 #include "NWFrame.h"
41 #include "NWWriter_SUMO.h"
42 #include "NWWriter_XML.h"
43 
44 
45 
46 // ===========================================================================
47 // method definitions
48 // ===========================================================================
49 // ---------------------------------------------------------------------------
50 // static methods
51 // ---------------------------------------------------------------------------
52 void
writeNetwork(const OptionsCont & oc,NBNetBuilder & nb)53 NWWriter_XML::writeNetwork(const OptionsCont& oc, NBNetBuilder& nb) {
54     // check whether plain-output files shall be generated
55     if (oc.isSet("plain-output-prefix")) {
56         writeNodes(oc, nb.getNodeCont());
57         if (nb.getTypeCont().size() > 0) {
58             writeTypes(oc, nb.getTypeCont());
59         }
60         writeEdgesAndConnections(oc, nb.getNodeCont(), nb.getEdgeCont());
61         writeTrafficLights(oc, nb.getTLLogicCont(), nb.getEdgeCont());
62     }
63     if (oc.isSet("junctions.join-output")) {
64         writeJoinedJunctions(oc, nb.getNodeCont());
65     }
66     if (oc.isSet("street-sign-output")) {
67         writeStreetSigns(oc, nb.getEdgeCont());
68     }
69     if (oc.exists("ptstop-output") && oc.isSet("ptstop-output")) {
70         writePTStops(oc, nb.getPTStopCont());
71     }
72     if (oc.exists("ptline-output") && oc.isSet("ptline-output")) {
73         writePTLines(oc, nb.getPTLineCont(), nb.getEdgeCont());
74     }
75 
76     if (oc.exists("parking-output") && oc.isSet("parking-output")) {
77         writeParkingAreas(oc, nb.getParkingCont(), nb.getEdgeCont());
78     }
79 }
80 
81 
82 void
writeNodes(const OptionsCont & oc,NBNodeCont & nc)83 NWWriter_XML::writeNodes(const OptionsCont& oc, NBNodeCont& nc) {
84     const GeoConvHelper& gch = GeoConvHelper::getFinal();
85     bool useGeo = oc.exists("proj.plain-geo") && oc.getBool("proj.plain-geo");
86     if (useGeo && !gch.usingGeoProjection()) {
87         WRITE_WARNING("Ignoring option \"proj.plain-geo\" because no geo-conversion has been defined");
88         useGeo = false;
89     }
90     const bool geoAccuracy = useGeo || gch.usingInverseGeoProjection();
91 
92     OutputDevice& device = OutputDevice::getDevice(oc.getString("plain-output-prefix") + ".nod.xml");
93     std::map<SumoXMLAttr, std::string> attrs;
94     attrs[SUMO_ATTR_VERSION] = NWFrame::MAJOR_VERSION;
95     device.writeXMLHeader("nodes", "nodes_file.xsd", attrs);
96 
97     // write network offsets and projection to allow reconstruction of original coordinates
98     if (!useGeo) {
99         GeoConvHelper::writeLocation(device);
100     }
101 
102     // write nodes
103     for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
104         NBNode* n = (*i).second;
105         device.openTag(SUMO_TAG_NODE);
106         device.writeAttr(SUMO_ATTR_ID, n->getID());
107         // write position
108         Position pos = n->getPosition();
109         if (useGeo) {
110             gch.cartesian2geo(pos);
111         }
112         if (geoAccuracy) {
113             device.setPrecision(gPrecisionGeo);
114         }
115         NWFrame::writePositionLong(pos, device);
116         if (geoAccuracy) {
117             device.setPrecision();
118         }
119 
120         device.writeAttr(SUMO_ATTR_TYPE, toString(n->getType()));
121         if (n->isTLControlled()) {
122             const std::set<NBTrafficLightDefinition*>& tlss = n->getControllingTLS();
123             // set may contain multiple programs for the same id.
124             // make sure ids are unique and sorted
125             std::set<std::string> tlsIDs;
126             std::set<std::string> controlledInnerEdges;
127             for (std::set<NBTrafficLightDefinition*>::const_iterator it_tl = tlss.begin(); it_tl != tlss.end(); it_tl++) {
128                 tlsIDs.insert((*it_tl)->getID());
129                 std::vector<std::string> cie = (*it_tl)->getControlledInnerEdges();
130                 controlledInnerEdges.insert(cie.begin(), cie.end());
131             }
132             std::vector<std::string> sortedIDs(tlsIDs.begin(), tlsIDs.end());
133             sort(sortedIDs.begin(), sortedIDs.end());
134             device.writeAttr(SUMO_ATTR_TLID, sortedIDs);
135             if (controlledInnerEdges.size() > 0) {
136                 std::vector<std::string> sortedCIEs(controlledInnerEdges.begin(), controlledInnerEdges.end());
137                 sort(sortedCIEs.begin(), sortedCIEs.end());
138                 device.writeAttr(SUMO_ATTR_CONTROLLED_INNER, joinToString(sortedCIEs, " "));
139             }
140         }
141         if (n->hasCustomShape()) {
142             device.writeAttr(SUMO_ATTR_SHAPE, n->getShape());
143         }
144         if (n->getRadius() != NBNode::UNSPECIFIED_RADIUS) {
145             device.writeAttr(SUMO_ATTR_RADIUS, n->getRadius());
146         }
147         if (n->getKeepClear() == false) {
148             device.writeAttr<bool>(SUMO_ATTR_KEEP_CLEAR, n->getKeepClear());
149         }
150         if (n->getRightOfWay() != RIGHT_OF_WAY_DEFAULT) {
151             device.writeAttr<std::string>(SUMO_ATTR_RIGHT_OF_WAY, toString(n->getRightOfWay()));
152         }
153         if (n->getFringeType() != FRINGE_TYPE_DEFAULT) {
154             device.writeAttr<std::string>(SUMO_ATTR_FRINGE, toString(n->getFringeType()));
155         }
156         n->writeParams(device);
157         device.closeTag();
158     }
159     device.close();
160 }
161 
162 
163 void
writeTypes(const OptionsCont & oc,NBTypeCont & tc)164 NWWriter_XML::writeTypes(const OptionsCont& oc, NBTypeCont& tc) {
165     OutputDevice& device = OutputDevice::getDevice(oc.getString("plain-output-prefix") + ".typ.xml");
166     std::map<SumoXMLAttr, std::string> attrs;
167     attrs[SUMO_ATTR_VERSION] = NWFrame::MAJOR_VERSION;
168     device.writeXMLHeader("types", "types_file.xsd", attrs);
169     tc.writeTypes(device);
170     device.close();
171 }
172 
173 
174 void
writeEdgesAndConnections(const OptionsCont & oc,NBNodeCont & nc,NBEdgeCont & ec)175 NWWriter_XML::writeEdgesAndConnections(const OptionsCont& oc, NBNodeCont& nc, NBEdgeCont& ec) {
176     const GeoConvHelper& gch = GeoConvHelper::getFinal();
177     bool useGeo = oc.exists("proj.plain-geo") && oc.getBool("proj.plain-geo");
178     const bool geoAccuracy = useGeo || gch.usingInverseGeoProjection();
179 
180     std::map<SumoXMLAttr, std::string> attrs;
181     attrs[SUMO_ATTR_VERSION] = NWFrame::MAJOR_VERSION;
182     OutputDevice& edevice = OutputDevice::getDevice(oc.getString("plain-output-prefix") + ".edg.xml");
183     edevice.writeXMLHeader("edges", "edges_file.xsd", attrs);
184     OutputDevice& cdevice = OutputDevice::getDevice(oc.getString("plain-output-prefix") + ".con.xml");
185     cdevice.writeXMLHeader("connections", "connections_file.xsd", attrs);
186     const bool writeNames = oc.getBool("output.street-names");
187     for (std::map<std::string, NBEdge*>::const_iterator i = ec.begin(); i != ec.end(); ++i) {
188         // write the edge itself to the edges-files
189         NBEdge* e = (*i).second;
190         edevice.openTag(SUMO_TAG_EDGE);
191         edevice.writeAttr(SUMO_ATTR_ID, e->getID());
192         edevice.writeAttr(SUMO_ATTR_FROM, e->getFromNode()->getID());
193         edevice.writeAttr(SUMO_ATTR_TO, e->getToNode()->getID());
194         if (writeNames && e->getStreetName() != "") {
195             edevice.writeAttr(SUMO_ATTR_NAME, StringUtils::escapeXML(e->getStreetName()));
196         }
197         edevice.writeAttr(SUMO_ATTR_PRIORITY, e->getPriority());
198         // write the type if given
199         if (e->getTypeID() != "") {
200             edevice.writeAttr(SUMO_ATTR_TYPE, e->getTypeID());
201         }
202         edevice.writeAttr(SUMO_ATTR_NUMLANES, e->getNumLanes());
203         if (!e->hasLaneSpecificSpeed()) {
204             edevice.writeAttr(SUMO_ATTR_SPEED, e->getSpeed());
205         }
206         // write non-default geometry
207         if (!e->hasDefaultGeometry()) {
208             PositionVector geom = e->getGeometry();
209             if (useGeo) {
210                 for (int i = 0; i < (int) geom.size(); i++) {
211                     gch.cartesian2geo(geom[i]);
212                 }
213             }
214             if (geoAccuracy) {
215                 edevice.setPrecision(gPrecisionGeo);
216             }
217             edevice.writeAttr(SUMO_ATTR_SHAPE, geom);
218             if (geoAccuracy) {
219                 edevice.setPrecision();
220             }
221         }
222         // write the spread type if not default ("right")
223         if (e->getLaneSpreadFunction() != LANESPREAD_RIGHT) {
224             edevice.writeAttr(SUMO_ATTR_SPREADTYPE, toString(e->getLaneSpreadFunction()));
225         }
226         // write the length if it was specified
227         if (e->hasLoadedLength()) {
228             edevice.writeAttr(SUMO_ATTR_LENGTH, e->getLoadedLength());
229         }
230         // some attributes can be set by edge default or per lane. Write as default if possible (efficiency)
231         if (e->getLaneWidth() != NBEdge::UNSPECIFIED_WIDTH && !e->hasLaneSpecificWidth()) {
232             edevice.writeAttr(SUMO_ATTR_WIDTH, e->getLaneWidth());
233         }
234         if (e->getEndOffset() != NBEdge::UNSPECIFIED_OFFSET && !e->hasLaneSpecificEndOffset()) {
235             edevice.writeAttr(SUMO_ATTR_ENDOFFSET, e->getEndOffset());
236         }
237         if (!e->hasLaneSpecificPermissions()) {
238             writePermissions(edevice, e->getPermissions(0));
239         }
240         if (!e->hasLaneSpecificStopOffsets() && e->getStopOffsets().size() != 0) {
241             NWWriter_SUMO::writeStopOffsets(edevice, e->getStopOffsets());
242         }
243         if (e->needsLaneSpecificOutput()) {
244             for (int i = 0; i < (int)e->getLanes().size(); ++i) {
245                 const NBEdge::Lane& lane = e->getLanes()[i];
246                 edevice.openTag(SUMO_TAG_LANE);
247                 edevice.writeAttr(SUMO_ATTR_INDEX, i);
248                 // write allowed lanes
249                 if (e->hasLaneSpecificPermissions()) {
250                     writePermissions(edevice, lane.permissions);
251                 }
252                 writePreferences(edevice, lane.preferred);
253                 // write other attributes
254                 if (lane.width != NBEdge::UNSPECIFIED_WIDTH && e->hasLaneSpecificWidth()) {
255                     edevice.writeAttr(SUMO_ATTR_WIDTH, lane.width);
256                 }
257                 if (lane.endOffset != NBEdge::UNSPECIFIED_OFFSET && e->hasLaneSpecificEndOffset()) {
258                     edevice.writeAttr(SUMO_ATTR_ENDOFFSET, lane.endOffset);
259                 }
260                 if (e->hasLaneSpecificSpeed()) {
261                     edevice.writeAttr(SUMO_ATTR_SPEED, lane.speed);
262                 }
263                 if (lane.accelRamp) {
264                     edevice.writeAttr(SUMO_ATTR_ACCELERATION, lane.accelRamp);
265                 }
266                 if (lane.customShape.size() > 0) {
267                     edevice.writeAttr(SUMO_ATTR_SHAPE, lane.customShape);
268                 }
269                 if (lane.oppositeID != "") {
270                     edevice.openTag(SUMO_TAG_NEIGH);
271                     edevice.writeAttr(SUMO_ATTR_LANE, lane.oppositeID);
272                     edevice.closeTag();
273                 }
274                 lane.writeParams(edevice);
275                 NWWriter_SUMO::writeStopOffsets(edevice, lane.stopOffsets);
276                 edevice.closeTag();
277             }
278         }
279         e->writeParams(edevice);
280         edevice.closeTag();
281         // write this edge's connections to the connections-files
282         const std::vector<NBEdge::Connection> connections = e->getConnections();
283         if (connections.empty()) {
284             // if there are no connections and this appears to be customized, preserve the information
285             const int numOutgoing = (int)e->getToNode()->getOutgoingEdges().size();
286             if (numOutgoing > 0) {
287                 const SVCPermissions inPerm = e->getPermissions();
288                 SVCPermissions outPerm = 0;
289                 for (auto out : e->getToNode()->getOutgoingEdges()) {
290                     outPerm |= out->getPermissions();
291                 }
292                 if ((inPerm & outPerm) != 0 && (inPerm & outPerm) != SVC_PEDESTRIAN) {
293                     cdevice.openTag(SUMO_TAG_CONNECTION);
294                     cdevice.writeAttr(SUMO_ATTR_FROM, e->getID());
295                     cdevice.closeTag();
296                     cdevice << "\n";
297                 }
298             }
299         } else {
300             for (std::vector<NBEdge::Connection>::const_iterator c = connections.begin(); c != connections.end(); ++c) {
301                 NWWriter_SUMO::writeConnection(cdevice, *e, *c, false, NWWriter_SUMO::PLAIN);
302             }
303             cdevice << "\n";
304         }
305     }
306     // write roundabout information to the edges-files
307     if (ec.getRoundabouts().size() > 0) {
308         edevice.lf();
309         NWWriter_SUMO::writeRoundabouts(edevice, ec.getRoundabouts(), ec);
310     }
311 
312     // write loaded prohibitions to the connections-file
313     for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
314         NWWriter_SUMO::writeProhibitions(cdevice, i->second->getProhibitions());
315     }
316     // write pedestrian crossings to the connections-file
317     for (std::map<std::string, NBNode*>::const_iterator it_node = nc.begin(); it_node != nc.end(); ++it_node) {
318         const std::vector<NBNode::Crossing*>& crossings = (*it_node).second->getCrossings();
319         for (auto c : crossings) {
320             cdevice.openTag(SUMO_TAG_CROSSING);
321             cdevice.writeAttr(SUMO_ATTR_NODE, (*it_node).second->getID());
322             cdevice.writeAttr(SUMO_ATTR_EDGES, c->edges);
323             cdevice.writeAttr(SUMO_ATTR_PRIORITY, c->priority);
324             if (c->customWidth != NBEdge::UNSPECIFIED_WIDTH) {
325                 cdevice.writeAttr(SUMO_ATTR_WIDTH, c->customWidth);
326             }
327             if (c->customTLIndex != -1) {
328                 cdevice.writeAttr(SUMO_ATTR_TLLINKINDEX, c->customTLIndex);
329             }
330             if (c->customTLIndex2 != -1) {
331                 cdevice.writeAttr(SUMO_ATTR_TLLINKINDEX2, c->customTLIndex2);
332             }
333             if (c->customShape.size() != 0) {
334                 cdevice.writeAttr(SUMO_ATTR_SHAPE, c->customShape);
335             }
336             cdevice.closeTag();
337         }
338     }
339     // write custom walkingarea shapes to the connections file
340     for (std::map<std::string, NBNode*>::const_iterator it_node = nc.begin(); it_node != nc.end(); ++it_node) {
341         for (const auto& wacs : it_node->second->getWalkingAreaCustomShapes()) {
342             cdevice.openTag(SUMO_TAG_WALKINGAREA);
343             cdevice.writeAttr(SUMO_ATTR_NODE, it_node->first);
344             cdevice.writeAttr(SUMO_ATTR_EDGES, joinNamedToString(wacs.edges, " "));
345             cdevice.writeAttr(SUMO_ATTR_SHAPE, wacs.shape);
346             cdevice.closeTag();
347         }
348     }
349 
350     edevice.close();
351     cdevice.close();
352 }
353 
354 
355 void
writeTrafficLights(const OptionsCont & oc,NBTrafficLightLogicCont & tc,NBEdgeCont & ec)356 NWWriter_XML::writeTrafficLights(const OptionsCont& oc, NBTrafficLightLogicCont& tc, NBEdgeCont& ec) {
357     std::map<SumoXMLAttr, std::string> attrs;
358     attrs[SUMO_ATTR_VERSION] = NWFrame::MAJOR_VERSION;
359     OutputDevice& device = OutputDevice::getDevice(oc.getString("plain-output-prefix") + ".tll.xml");
360     device.writeXMLHeader("tlLogics", "tllogic_file.xsd", attrs);
361     NWWriter_SUMO::writeTrafficLights(device, tc);
362     // we also need to remember the associations between tlLogics and connections
363     // since the information in con.xml is insufficient
364     for (std::map<std::string, NBEdge*>::const_iterator i = ec.begin(); i != ec.end(); ++i) {
365         NBEdge* e = (*i).second;
366         // write this edge's tl-controlled connections
367         const std::vector<NBEdge::Connection> connections = e->getConnections();
368         for (std::vector<NBEdge::Connection>::const_iterator c = connections.begin(); c != connections.end(); ++c) {
369             if (c->tlID != "") {
370                 NWWriter_SUMO::writeConnection(device, *e, *c, false, NWWriter_SUMO::TLL);
371             }
372         }
373     }
374     device.close();
375 }
376 
377 
378 void
writeJoinedJunctions(const OptionsCont & oc,NBNodeCont & nc)379 NWWriter_XML::writeJoinedJunctions(const OptionsCont& oc, NBNodeCont& nc) {
380     std::map<SumoXMLAttr, std::string> attrs;
381     attrs[SUMO_ATTR_VERSION] = NWFrame::MAJOR_VERSION;
382     OutputDevice& device = OutputDevice::getDevice(oc.getString("junctions.join-output"));
383     device.writeXMLHeader("nodes", "nodes_file.xsd", attrs);
384     const std::vector<std::set<std::string> >& clusters = nc.getJoinedClusters();
385     for (std::vector<std::set<std::string> >::const_iterator it = clusters.begin(); it != clusters.end(); it++) {
386         assert((*it).size() > 0);
387         device.openTag(SUMO_TAG_JOIN);
388         // prepare string
389         std::ostringstream oss;
390         for (std::set<std::string>::const_iterator it_id = it->begin(); it_id != it->end(); it_id++) {
391             oss << *it_id << " ";
392         }
393         // remove final space
394         std::string ids = oss.str();
395         device.writeAttr(SUMO_ATTR_NODES, ids.substr(0, ids.size() - 1));
396         device.closeTag();
397     }
398     device.close();
399 }
400 
401 
402 void
writeStreetSigns(const OptionsCont & oc,NBEdgeCont & ec)403 NWWriter_XML::writeStreetSigns(const OptionsCont& oc, NBEdgeCont& ec) {
404     OutputDevice& device = OutputDevice::getDevice(oc.getString("street-sign-output"));
405     device.writeXMLHeader("additional", "additional_file.xsd");
406     for (std::map<std::string, NBEdge*>::const_iterator i = ec.begin(); i != ec.end(); ++i) {
407         NBEdge* e = (*i).second;
408         const std::vector<NBSign>& signs =  e->getSigns();
409         for (std::vector<NBSign>::const_iterator it = signs.begin(); it != signs.end(); ++it) {
410             it->writeAsPOI(device, e);
411         }
412     }
413     device.close();
414 }
415 void
writePTStops(const OptionsCont & oc,NBPTStopCont & sc)416 NWWriter_XML::writePTStops(const OptionsCont& oc, NBPTStopCont& sc) {
417     OutputDevice& device = OutputDevice::getDevice(oc.getString("ptstop-output"));
418     device.writeXMLHeader("additional", "additional_file.xsd");
419     for (std::map<std::string, NBPTStop*>::const_iterator i = sc.begin(); i != sc.end(); ++i) {
420         i->second->write(device);
421     }
422     device.close();
423 }
writePTLines(const OptionsCont & oc,NBPTLineCont & lc,NBEdgeCont & ec)424 void NWWriter_XML::writePTLines(const OptionsCont& oc, NBPTLineCont& lc, NBEdgeCont& ec) {
425     OutputDevice& device = OutputDevice::getDevice(oc.getString("ptline-output"));
426     device.writeXMLHeader("additional", "additional_file.xsd");
427     for (const auto& item : lc.getLines()) {
428         item.second->write(device, ec);
429     }
430     device.close();
431 }
432 
writeParkingAreas(const OptionsCont & oc,NBParkingCont & pc,NBEdgeCont & ec)433 void NWWriter_XML::writeParkingAreas(const OptionsCont& oc, NBParkingCont& pc, NBEdgeCont& ec) {
434     OutputDevice& device = OutputDevice::getDevice(oc.getString("parking-output"));
435     device.writeXMLHeader("additional", "additional_file.xsd");
436     for (NBParking& p : pc) {
437         p.write(device, ec);
438     }
439     device.close();
440 }
441 
442 
443 /****************************************************************************/
444 
445 
446 /****************************************************************************/
447