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    NIXMLEdgesHandler.cpp
11 /// @author  Daniel Krajzewicz
12 /// @author  Jakob Erdmann
13 /// @author  Michael Behrisch
14 /// @author  Walter Bamberger
15 /// @author  Laura Bieker
16 /// @author  Leonhard Luecken
17 /// @date    Tue, 20 Nov 2001
18 /// @version $Id$
19 ///
20 // Importer for network edges stored in XML
21 /****************************************************************************/
22 
23 
24 // ===========================================================================
25 // included modules
26 // ===========================================================================
27 #include <config.h>
28 
29 #include <string>
30 #include <iostream>
31 #include <map>
32 #include <cmath>
33 #include <xercesc/sax/HandlerBase.hpp>
34 #include <xercesc/sax/AttributeList.hpp>
35 #include <xercesc/sax/SAXParseException.hpp>
36 #include <xercesc/sax/SAXException.hpp>
37 #include <utils/xml/SUMOSAXHandler.h>
38 #include <netbuild/NBNodeCont.h>
39 #include <netbuild/NBTypeCont.h>
40 #include <netbuild/NBNetBuilder.h>
41 #include <utils/xml/SUMOXMLDefinitions.h>
42 #include <utils/common/MsgHandler.h>
43 #include <utils/common/StringUtils.h>
44 #include <utils/common/StringTokenizer.h>
45 #include <utils/geom/GeomConvHelper.h>
46 #include <utils/common/ToString.h>
47 #include <utils/options/OptionsCont.h>
48 #include <utils/geom/GeoConvHelper.h>
49 #include "NIXMLNodesHandler.h"
50 #include "NIXMLEdgesHandler.h"
51 
52 
53 // ===========================================================================
54 // method definitions
55 // ===========================================================================
NIXMLEdgesHandler(NBNodeCont & nc,NBEdgeCont & ec,NBTypeCont & tc,NBDistrictCont & dc,NBTrafficLightLogicCont & tlc,OptionsCont & options)56 NIXMLEdgesHandler::NIXMLEdgesHandler(NBNodeCont& nc,
57                                      NBEdgeCont& ec,
58                                      NBTypeCont& tc,
59                                      NBDistrictCont& dc,
60                                      NBTrafficLightLogicCont& tlc,
61                                      OptionsCont& options) :
62     SUMOSAXHandler("xml-edges - file"),
63     myOptions(options),
64     myNodeCont(nc),
65     myEdgeCont(ec),
66     myTypeCont(tc),
67     myDistrictCont(dc),
68     myTLLogicCont(tlc),
69     myCurrentEdge(nullptr),
70     myCurrentLaneIndex(-1),
71     myHaveReportedAboutOverwriting(false),
72     myHaveReportedAboutTypeOverride(false),
73     myHaveWarnedAboutDeprecatedLaneId(false),
74     myKeepEdgeShape(!options.getBool("plain.extend-edge-shape")) {
75 }
76 
77 
~NIXMLEdgesHandler()78 NIXMLEdgesHandler::~NIXMLEdgesHandler() {}
79 
80 
81 void
myStartElement(int element,const SUMOSAXAttributes & attrs)82 NIXMLEdgesHandler::myStartElement(int element,
83                                   const SUMOSAXAttributes& attrs) {
84     switch (element) {
85         case SUMO_TAG_EDGE:
86             addEdge(attrs);
87             break;
88         case SUMO_TAG_LANE:
89             addLane(attrs);
90             break;
91         case SUMO_TAG_NEIGH:
92             myCurrentEdge->getLaneStruct((int)myCurrentEdge->getNumLanes() - 1).oppositeID = attrs.getString(SUMO_ATTR_LANE);
93             break;
94         case SUMO_TAG_SPLIT:
95             addSplit(attrs);
96             break;
97         case SUMO_TAG_DELETE:
98             deleteEdge(attrs);
99             break;
100         case SUMO_TAG_ROUNDABOUT:
101             addRoundabout(attrs);
102             break;
103         case SUMO_TAG_PARAM:
104             if (myLastParameterised.size() != 0 && myCurrentEdge != nullptr) {
105                 bool ok = true;
106                 const std::string key = attrs.get<std::string>(SUMO_ATTR_KEY, nullptr, ok);
107                 // circumventing empty string test
108                 const std::string val = attrs.hasAttribute(SUMO_ATTR_VALUE) ? attrs.getString(SUMO_ATTR_VALUE) : "";
109                 myLastParameterised.back()->setParameter(key, val);
110             }
111             break;
112         case SUMO_TAG_STOPOFFSET: {
113             bool ok = true;
114             std::map<SVCPermissions, double> stopOffsets = parseStopOffsets(attrs, ok);
115             assert(stopOffsets.size() == 1);
116             if (!ok) {
117                 std::stringstream ss;
118                 ss << "(Error encountered at lane " << myCurrentLaneIndex << " of edge '" << myCurrentID << "' while parsing stopOffsets.)";
119                 WRITE_ERROR(ss.str());
120             } else {
121                 if (myCurrentEdge->getStopOffsets(myCurrentLaneIndex).size() != 0) {
122                     std::stringstream ss;
123                     ss << "Duplicate definition of stopOffset for ";
124                     if (myCurrentLaneIndex != -1) {
125                         ss << "lane " << myCurrentLaneIndex << " on ";
126                     }
127                     ss << "edge " << myCurrentEdge->getID() << ". Ignoring duplicate specification.";
128                     WRITE_WARNING(ss.str());
129                     return;
130                 } else if (stopOffsets.begin()->second > myCurrentEdge->getLength() || stopOffsets.begin()->second < 0) {
131                     std::stringstream ss;
132                     ss << "Ignoring invalid stopOffset for ";
133                     if (myCurrentLaneIndex != -1) {
134                         ss << "lane " << myCurrentLaneIndex << " on ";
135                     }
136                     ss << "edge " << myCurrentEdge->getID();
137                     if (stopOffsets.begin()->second > myCurrentEdge->getLength()) {
138                         ss << " (offset larger than the edge length).";
139                     } else {
140                         ss << " (negative offset).";
141                     }
142                     WRITE_WARNING(ss.str());
143                 } else {
144                     myCurrentEdge->setStopOffsets(myCurrentLaneIndex, stopOffsets);
145                 }
146             }
147         }
148         break;
149         default:
150             break;
151     }
152 }
153 
154 
155 void
addEdge(const SUMOSAXAttributes & attrs)156 NIXMLEdgesHandler::addEdge(const SUMOSAXAttributes& attrs) {
157     myIsUpdate = false;
158     bool ok = true;
159     // initialise the edge
160     myCurrentEdge = nullptr;
161     mySplits.clear();
162     // get the id, report an error if not given or empty...
163     myCurrentID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
164     if (!ok) {
165         return;
166     }
167     myCurrentEdge = myEdgeCont.retrieve(myCurrentID);
168     // check deprecated (unused) attributes
169     // use default values, first
170     myCurrentPriority = myTypeCont.getPriority("");
171     myCurrentLaneNo = myTypeCont.getNumLanes("");
172     myCurrentEndOffset = NBEdge::UNSPECIFIED_OFFSET;
173     myCurrentType = "";
174     if (myCurrentEdge != nullptr) {
175         // update existing edge. only update lane-specific settings when explicitly requested
176         myIsUpdate = true;
177         myCurrentSpeed = NBEdge::UNSPECIFIED_SPEED;
178         myPermissions = SVC_UNSPECIFIED;
179         myCurrentWidth = NBEdge::UNSPECIFIED_WIDTH;
180         myCurrentType = myCurrentEdge->getTypeID();
181     } else {
182         // this is a completely new edge. get the type specific defaults
183         myCurrentSpeed = myTypeCont.getSpeed("");
184         myPermissions = myTypeCont.getPermissions("");
185         myCurrentWidth = myTypeCont.getWidth("");
186     }
187     myShape = PositionVector();
188     myLanesSpread = LANESPREAD_RIGHT;
189     myLength = NBEdge::UNSPECIFIED_LOADED_LENGTH;
190     myCurrentStreetName = "";
191     myReinitKeepEdgeShape = false;
192     mySidewalkWidth = NBEdge::UNSPECIFIED_WIDTH;
193     myBikeLaneWidth = NBEdge::UNSPECIFIED_WIDTH;
194     // check whether a type's values shall be used
195     if (attrs.hasAttribute(SUMO_ATTR_TYPE)) {
196         myCurrentType = attrs.get<std::string>(SUMO_ATTR_TYPE, myCurrentID.c_str(), ok);
197         if (!ok) {
198             return;
199         }
200         if (!myTypeCont.knows(myCurrentType) && !myOptions.getBool("ignore-errors.edge-type")) {
201             WRITE_ERROR("Type '" + myCurrentType + "' used by edge '" + myCurrentID + "' was not defined (ignore with option --ignore-errors.edge-type).");
202             return;
203         }
204         myCurrentSpeed = myTypeCont.getSpeed(myCurrentType);
205         myCurrentPriority = myTypeCont.getPriority(myCurrentType);
206         myCurrentLaneNo = myTypeCont.getNumLanes(myCurrentType);
207         myPermissions = myTypeCont.getPermissions(myCurrentType);
208         myCurrentWidth = myTypeCont.getWidth(myCurrentType);
209         mySidewalkWidth = myTypeCont.getSidewalkWidth(myCurrentType);
210         myBikeLaneWidth = myTypeCont.getBikeLaneWidth(myCurrentType);
211     }
212     // use values from the edge to overwrite if existing, then
213     if (myIsUpdate) {
214         if (!myHaveReportedAboutOverwriting) {
215             WRITE_MESSAGE("Duplicate edge id occurred ('" + myCurrentID + "'); assuming overwriting is wished.");
216             myHaveReportedAboutOverwriting = true;
217         }
218         if (attrs.hasAttribute(SUMO_ATTR_TYPE) && myCurrentType != myCurrentEdge->getTypeID()) {
219             if (!myHaveReportedAboutTypeOverride) {
220                 WRITE_MESSAGE("Edge '" + myCurrentID + "' changed it's type; assuming type override is wished.");
221                 myHaveReportedAboutTypeOverride = true;
222             }
223         }
224         if (attrs.getOpt<bool>(SUMO_ATTR_REMOVE, myCurrentID.c_str(), ok, false)) {
225             myEdgeCont.erase(myDistrictCont, myCurrentEdge);
226             myCurrentEdge = nullptr;
227             return;
228         }
229         myCurrentPriority = myCurrentEdge->getPriority();
230         myCurrentLaneNo = myCurrentEdge->getNumLanes();
231         if (!myCurrentEdge->hasDefaultGeometry()) {
232             myShape = myCurrentEdge->getGeometry();
233             myReinitKeepEdgeShape = true;
234         }
235         myLanesSpread = myCurrentEdge->getLaneSpreadFunction();
236         if (myCurrentEdge->hasLoadedLength()) {
237             myLength = myCurrentEdge->getLoadedLength();
238         }
239         myCurrentStreetName = myCurrentEdge->getStreetName();
240     }
241     // speed, priority and the number of lanes have now default values;
242     // try to read the real values from the file
243     if (attrs.hasAttribute(SUMO_ATTR_SPEED)) {
244         myCurrentSpeed = attrs.get<double>(SUMO_ATTR_SPEED, myCurrentID.c_str(), ok);
245     }
246     if (myOptions.getBool("speed-in-kmh") && myCurrentSpeed != NBEdge::UNSPECIFIED_SPEED) {
247         myCurrentSpeed = myCurrentSpeed / (double) 3.6;
248     }
249     // try to get the number of lanes
250     if (attrs.hasAttribute(SUMO_ATTR_NUMLANES)) {
251         myCurrentLaneNo = attrs.get<int>(SUMO_ATTR_NUMLANES, myCurrentID.c_str(), ok);
252     }
253     // try to get the priority
254     if (attrs.hasAttribute(SUMO_ATTR_PRIORITY)) {
255         myCurrentPriority = attrs.get<int>(SUMO_ATTR_PRIORITY, myCurrentID.c_str(), ok);
256     }
257     // try to get the width
258     if (attrs.hasAttribute(SUMO_ATTR_WIDTH)) {
259         myCurrentWidth = attrs.get<double>(SUMO_ATTR_WIDTH, myCurrentID.c_str(), ok);
260     }
261     // try to get the offset of the stop line from the intersection
262     if (attrs.hasAttribute(SUMO_ATTR_ENDOFFSET)) {
263         myCurrentEndOffset = attrs.get<double>(SUMO_ATTR_ENDOFFSET, myCurrentID.c_str(), ok);
264     }
265     // try to get the street name
266     if (attrs.hasAttribute(SUMO_ATTR_NAME)) {
267         myCurrentStreetName = attrs.get<std::string>(SUMO_ATTR_NAME, myCurrentID.c_str(), ok);
268         if (myCurrentStreetName != "" && myOptions.isDefault("output.street-names")) {
269             myOptions.set("output.street-names", "true");
270         }
271     }
272 
273     // try to get the allowed/disallowed classes
274     if (attrs.hasAttribute(SUMO_ATTR_ALLOW) || attrs.hasAttribute(SUMO_ATTR_DISALLOW)) {
275         std::string allowS = attrs.hasAttribute(SUMO_ATTR_ALLOW) ? attrs.getStringSecure(SUMO_ATTR_ALLOW, "") : "";
276         std::string disallowS = attrs.hasAttribute(SUMO_ATTR_DISALLOW) ? attrs.getStringSecure(SUMO_ATTR_DISALLOW, "") : "";
277         // XXX matter of interpretation: should updated permissions replace or extend previously set permissions?
278         myPermissions = parseVehicleClasses(allowS, disallowS);
279     }
280     // try to set the nodes
281     if (!setNodes(attrs)) {
282         // return if this failed
283         return;
284     }
285     // try to get the shape
286     myShape = tryGetShape(attrs);
287     // try to get the spread type
288     myLanesSpread = tryGetLaneSpread(attrs);
289     // try to get the length
290     myLength = attrs.getOpt<double>(SUMO_ATTR_LENGTH, myCurrentID.c_str(), ok, myLength);
291     // try to get the sidewalkWidth
292     mySidewalkWidth = attrs.getOpt<double>(SUMO_ATTR_SIDEWALKWIDTH, myCurrentID.c_str(), ok, mySidewalkWidth);
293     // try to get the bikeLaneWidth
294     myBikeLaneWidth = attrs.getOpt<double>(SUMO_ATTR_BIKELANEWIDTH, myCurrentID.c_str(), ok, myBikeLaneWidth);
295     // insert the parsed edge into the edges map
296     if (!ok) {
297         return;
298     }
299     // check whether a previously defined edge shall be overwritten
300     if (myCurrentEdge != nullptr) {
301         myCurrentEdge->reinit(myFromNode, myToNode, myCurrentType, myCurrentSpeed,
302                               myCurrentLaneNo, myCurrentPriority, myShape,
303                               myCurrentWidth, myCurrentEndOffset,
304                               myCurrentStreetName, myLanesSpread,
305                               myReinitKeepEdgeShape);
306     } else {
307         // the edge must be allocated in dependence to whether a shape is given
308         if (myShape.size() == 0) {
309             myCurrentEdge = new NBEdge(myCurrentID, myFromNode, myToNode, myCurrentType, myCurrentSpeed,
310                                        myCurrentLaneNo, myCurrentPriority, myCurrentWidth, myCurrentEndOffset,
311                                        myCurrentStreetName, myLanesSpread);
312         } else {
313             myCurrentEdge = new NBEdge(myCurrentID, myFromNode, myToNode, myCurrentType, myCurrentSpeed,
314                                        myCurrentLaneNo, myCurrentPriority, myCurrentWidth, myCurrentEndOffset,
315                                        myShape, myCurrentStreetName, "", myLanesSpread,
316                                        myKeepEdgeShape);
317         }
318     }
319     myCurrentEdge->setLoadedLength(myLength);
320     if (myPermissions != SVC_UNSPECIFIED) {
321         myCurrentEdge->setPermissions(myPermissions);
322     }
323     myLastParameterised.push_back(myCurrentEdge);
324 }
325 
326 
327 void
addLane(const SUMOSAXAttributes & attrs)328 NIXMLEdgesHandler::addLane(const SUMOSAXAttributes& attrs) {
329     if (myCurrentEdge == nullptr) {
330         if (!OptionsCont::getOptions().isInStringVector("remove-edges.explicit", myCurrentID)) {
331             WRITE_ERROR("Additional lane information could not be set - the edge with id '" + myCurrentID + "' is not known.");
332         }
333         return;
334     }
335     bool ok = true;
336     int lane;
337     if (attrs.hasAttribute(SUMO_ATTR_ID)) {
338         lane = attrs.get<int>(SUMO_ATTR_ID, myCurrentID.c_str(), ok);
339         if (!myHaveWarnedAboutDeprecatedLaneId) {
340             myHaveWarnedAboutDeprecatedLaneId = true;
341             WRITE_WARNING("'" + toString(SUMO_ATTR_ID) + "' is deprecated, please use '" + toString(SUMO_ATTR_INDEX) + "' instead.");
342         }
343     } else {
344         lane = attrs.get<int>(SUMO_ATTR_INDEX, myCurrentID.c_str(), ok);
345     }
346     if (!ok) {
347         return;
348     }
349     // check whether this lane exists
350     if (lane >= myCurrentEdge->getNumLanes()) {
351         WRITE_ERROR("Lane index is larger than number of lanes (edge '" + myCurrentID + "').");
352         return;
353     }
354     myCurrentLaneIndex = lane;
355     // set information about allowed / disallowed vehicle classes (if specified)
356     if (attrs.hasAttribute(SUMO_ATTR_ALLOW) || attrs.hasAttribute(SUMO_ATTR_DISALLOW)) {
357         const std::string allowed = attrs.getOpt<std::string>(SUMO_ATTR_ALLOW, nullptr, ok, "");
358         const std::string disallowed = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, nullptr, ok, "");
359         myCurrentEdge->setPermissions(parseVehicleClasses(allowed, disallowed), lane);
360     }
361     if (attrs.hasAttribute(SUMO_ATTR_PREFER)) {
362         const std::string preferred  = attrs.get<std::string>(SUMO_ATTR_PREFER, nullptr, ok);
363         myCurrentEdge->setPreferredVehicleClass(parseVehicleClasses(preferred), lane);
364     }
365     // try to get the width
366     if (attrs.hasAttribute(SUMO_ATTR_WIDTH)) {
367         myCurrentEdge->setLaneWidth(lane, attrs.get<double>(SUMO_ATTR_WIDTH, myCurrentID.c_str(), ok));
368     }
369     // try to get the end-offset (lane shortened due to pedestrian crossing etc..)
370     if (attrs.hasAttribute(SUMO_ATTR_ENDOFFSET)) {
371         myCurrentEdge->setEndOffset(lane, attrs.get<double>(SUMO_ATTR_ENDOFFSET, myCurrentID.c_str(), ok));
372     }
373     // try to get lane specific speed (should not occur for german networks)
374     if (attrs.hasAttribute(SUMO_ATTR_SPEED)) {
375         myCurrentEdge->setSpeed(lane, attrs.get<double>(SUMO_ATTR_SPEED, myCurrentID.c_str(), ok));
376     }
377     // check whether this is an acceleration lane
378     if (attrs.hasAttribute(SUMO_ATTR_ACCELERATION)) {
379         myCurrentEdge->setAcceleration(lane, attrs.get<bool>(SUMO_ATTR_ACCELERATION, myCurrentID.c_str(), ok));
380     }
381 
382     // check whether this is an acceleration lane
383     if (attrs.hasAttribute(SUMO_ATTR_SHAPE)) {
384         PositionVector shape = attrs.get<PositionVector>(SUMO_ATTR_SHAPE, myCurrentID.c_str(), ok);
385         if (!NBNetBuilder::transformCoordinates(shape)) {
386             const std::string laneID = myCurrentID + "_" + toString(lane);
387             WRITE_ERROR("Unable to project coordinates for lane '" + laneID + "'.");
388         }
389         myCurrentEdge->setLaneShape(lane, shape);
390     }
391     myLastParameterised.push_back(&myCurrentEdge->getLaneStruct(lane));
392 }
393 
394 
addSplit(const SUMOSAXAttributes & attrs)395 void NIXMLEdgesHandler::addSplit(const SUMOSAXAttributes& attrs) {
396     if (myCurrentEdge == nullptr) {
397         if (!OptionsCont::getOptions().isInStringVector("remove-edges.explicit", myCurrentID)) {
398             WRITE_WARNING("Ignoring 'split' because it cannot be assigned to an edge");
399         }
400         return;
401     }
402     bool ok = true;
403     NBEdgeCont::Split e;
404     e.pos = attrs.get<double>(SUMO_ATTR_POSITION, nullptr, ok);
405     if (ok) {
406         if (fabs(e.pos) > myCurrentEdge->getGeometry().length()) {
407             WRITE_ERROR("Edge '" + myCurrentID + "' has a split at invalid position " + toString(e.pos) + ".");
408             return;
409         }
410         std::vector<NBEdgeCont::Split>::iterator i = find_if(mySplits.begin(), mySplits.end(), split_by_pos_finder(e.pos));
411         if (i != mySplits.end()) {
412             WRITE_ERROR("Edge '" + myCurrentID + "' has already a split at position " + toString(e.pos) + ".");
413             return;
414         }
415         e.nameID = myCurrentID + "." + toString((int)e.pos);
416         if (e.pos < 0) {
417             e.pos += myCurrentEdge->getGeometry().length();
418         }
419         for (const std::string& id : attrs.getOptStringVector(SUMO_ATTR_LANES, myCurrentID.c_str(), ok)) {
420             try {
421                 int lane = StringUtils::toInt(id);
422                 e.lanes.push_back(lane);
423             } catch (NumberFormatException&) {
424                 WRITE_ERROR("Error on parsing a split (edge '" + myCurrentID + "').");
425             } catch (EmptyData&) {
426                 WRITE_ERROR("Error on parsing a split (edge '" + myCurrentID + "').");
427             }
428         }
429         if (e.lanes.empty()) {
430             for (int l = 0; l < myCurrentEdge->getNumLanes(); ++l) {
431                 e.lanes.push_back(l);
432             }
433         }
434         e.speed = attrs.getOpt(SUMO_ATTR_SPEED, nullptr, ok, myCurrentEdge->getSpeed());
435         if (attrs.hasAttribute(SUMO_ATTR_SPEED) && myOptions.getBool("speed-in-kmh")) {
436             e.speed /= 3.6;
437         }
438         e.idBefore = attrs.getOpt(SUMO_ATTR_ID_BEFORE, nullptr, ok, std::string(""));
439         e.idAfter = attrs.getOpt(SUMO_ATTR_ID_AFTER, nullptr, ok, std::string(""));
440         if (!ok) {
441             return;
442         }
443         const std::string nodeID = attrs.getOpt(SUMO_ATTR_ID, nullptr, ok, e.nameID);
444         if (nodeID == myCurrentEdge->getFromNode()->getID() || nodeID == myCurrentEdge->getToNode()->getID()) {
445             WRITE_ERROR("Invalid split node id for edge '" + myCurrentEdge->getID() + "' (from- and to-node are forbidden)");
446             return;
447         }
448         e.node = myNodeCont.retrieve(nodeID);
449         if (e.node == nullptr) {
450             e.node = new NBNode(nodeID, myCurrentEdge->getGeometry().positionAtOffset(e.pos));
451         }
452         NIXMLNodesHandler::processNodeType(attrs, e.node, e.node->getID(), e.node->getPosition(), false,
453                                            myNodeCont, myEdgeCont, myTLLogicCont);
454         mySplits.push_back(e);
455     }
456 }
457 
458 
459 bool
setNodes(const SUMOSAXAttributes & attrs)460 NIXMLEdgesHandler::setNodes(const SUMOSAXAttributes& attrs) {
461     // the names and the coordinates of the beginning and the end node
462     // may be found, try
463     bool ok = true;
464     std::string begNodeID = myIsUpdate ? myCurrentEdge->getFromNode()->getID() : "";
465     std::string endNodeID = myIsUpdate ? myCurrentEdge->getToNode()->getID() : "";
466     std::string oldBegID = begNodeID;
467     std::string oldEndID = endNodeID;
468     if (attrs.hasAttribute(SUMO_ATTR_FROM)) {
469         begNodeID = attrs.get<std::string>(SUMO_ATTR_FROM, nullptr, ok);
470     } else if (!myIsUpdate) {
471         WRITE_ERROR("The from-node is not given for edge '" + myCurrentID + "'.");
472         ok = false;
473     }
474     if (attrs.hasAttribute(SUMO_ATTR_TO)) {
475         endNodeID = attrs.get<std::string>(SUMO_ATTR_TO, nullptr, ok);
476     } else if (!myIsUpdate) {
477         WRITE_ERROR("The to-node is not given for edge '" + myCurrentID + "'.");
478         ok = false;
479     }
480     if (!ok) {
481         return false;
482     }
483     myFromNode = myNodeCont.retrieve(begNodeID);
484     myToNode = myNodeCont.retrieve(endNodeID);
485     if (myFromNode == nullptr) {
486         WRITE_ERROR("Edge's '" + myCurrentID + "' from-node '" + begNodeID + "' is not known.");
487     }
488     if (myToNode == nullptr) {
489         WRITE_ERROR("Edge's '" + myCurrentID + "' to-node '" + endNodeID + "' is not known.");
490     }
491     return myFromNode != nullptr && myToNode != nullptr;
492 }
493 
494 
495 PositionVector
tryGetShape(const SUMOSAXAttributes & attrs)496 NIXMLEdgesHandler::tryGetShape(const SUMOSAXAttributes& attrs) {
497     if (!attrs.hasAttribute(SUMO_ATTR_SHAPE)) {
498         return myShape;
499     }
500     // try to build shape
501     bool ok = true;
502     if (!attrs.hasAttribute(SUMO_ATTR_SHAPE)) {
503         myReinitKeepEdgeShape = false;
504         return PositionVector();
505     }
506     PositionVector shape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, nullptr, ok, PositionVector());
507     if (!NBNetBuilder::transformCoordinates(shape)) {
508         WRITE_ERROR("Unable to project coordinates for edge '" + myCurrentID + "'.");
509     }
510     myReinitKeepEdgeShape = myKeepEdgeShape;
511     return shape;
512 }
513 
514 
515 LaneSpreadFunction
tryGetLaneSpread(const SUMOSAXAttributes & attrs)516 NIXMLEdgesHandler::tryGetLaneSpread(const SUMOSAXAttributes& attrs) {
517     bool ok = true;
518     LaneSpreadFunction result = myLanesSpread;
519     std::string lsfS = toString(result);
520     lsfS = attrs.getOpt<std::string>(SUMO_ATTR_SPREADTYPE, myCurrentID.c_str(), ok, lsfS);
521     if (SUMOXMLDefinitions::LaneSpreadFunctions.hasString(lsfS)) {
522         result = SUMOXMLDefinitions::LaneSpreadFunctions.get(lsfS);
523     } else {
524         WRITE_WARNING("Ignoring unknown spreadType '" + lsfS + "' for edge '" + myCurrentID + "'.");
525     }
526     return result;
527 }
528 
529 
530 void
deleteEdge(const SUMOSAXAttributes & attrs)531 NIXMLEdgesHandler::deleteEdge(const SUMOSAXAttributes& attrs) {
532     bool ok = true;
533     myCurrentID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
534     if (!ok) {
535         return;
536     }
537     NBEdge* edge = myEdgeCont.retrieve(myCurrentID);
538     if (edge == nullptr) {
539         WRITE_WARNING("Ignoring tag '" + toString(SUMO_TAG_DELETE) + "' for unknown edge '" +
540                       myCurrentID + "'");
541         return;
542     }
543     const int lane = attrs.getOpt<int>(SUMO_ATTR_INDEX, myCurrentID.c_str(), ok, -1);
544     if (lane < 0) {
545         myEdgeCont.extract(myDistrictCont, edge, true);
546     } else {
547         edge->deleteLane(lane, false, true);
548     }
549 }
550 
551 
552 void
myEndElement(int element)553 NIXMLEdgesHandler::myEndElement(int element) {
554     if (myCurrentEdge == nullptr) {
555         return;
556     }
557     if (element == SUMO_TAG_EDGE) {
558         myLastParameterised.pop_back();
559         // add bike lane, wait until lanes are loaded to avoid building if it already exists
560         if (myBikeLaneWidth != NBEdge::UNSPECIFIED_WIDTH) {
561             myCurrentEdge->addBikeLane(myBikeLaneWidth);
562         }
563         // add sidewalk, wait until lanes are loaded to avoid building if it already exists
564         if (mySidewalkWidth != NBEdge::UNSPECIFIED_WIDTH) {
565             myCurrentEdge->addSidewalk(mySidewalkWidth);
566         }
567         // apply default stopOffsets of edge to all lanes without specified stopOffset.
568         std::map<SVCPermissions, double> stopOffsets = myCurrentEdge->getStopOffsets(-1);
569         if (stopOffsets.size() != 0) {
570             for (int i = 0; i < (int)myCurrentEdge->getLanes().size(); i++) {
571                 myCurrentEdge->setStopOffsets(i, stopOffsets, false);
572             }
573         }
574         if (!myIsUpdate) {
575             try {
576                 if (!myEdgeCont.insert(myCurrentEdge)) {
577                     WRITE_ERROR("Duplicate edge occurred. ID='" + myCurrentID + "'");
578                     delete myCurrentEdge;
579                 }
580             } catch (InvalidArgument& e) {
581                 WRITE_ERROR(e.what());
582                 throw;
583             } catch (...) {
584                 WRITE_ERROR("An important information is missing in edge '" + myCurrentID + "'.");
585             }
586         }
587         myEdgeCont.processSplits(myCurrentEdge, mySplits, myNodeCont, myDistrictCont, myTLLogicCont);
588         myCurrentEdge = nullptr;
589     } else if (element == SUMO_TAG_LANE) {
590         myLastParameterised.pop_back();
591         myCurrentLaneIndex = -1;
592     }
593 }
594 
595 
596 void
addRoundabout(const SUMOSAXAttributes & attrs)597 NIXMLEdgesHandler::addRoundabout(const SUMOSAXAttributes& attrs) {
598     if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
599         std::vector<std::string> edgeIDs = attrs.getStringVector(SUMO_ATTR_EDGES);
600         EdgeSet roundabout;
601         for (std::vector<std::string>::iterator it = edgeIDs.begin(); it != edgeIDs.end(); ++it) {
602             NBEdge* edge = myEdgeCont.retrieve(*it);
603             if (edge == nullptr) {
604                 if (!myEdgeCont.wasIgnored(*it)) {
605                     WRITE_ERROR("Unknown edge '" + (*it) + "' in roundabout");
606                 }
607             } else {
608                 roundabout.insert(edge);
609             }
610         }
611         myEdgeCont.addRoundabout(roundabout);
612     } else {
613         WRITE_ERROR("Empty edges in roundabout.");
614     }
615 }
616 
617 
618 /****************************************************************************/
619 
620