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