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