Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
14 : /// @file NIXMLEdgesHandler.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Michael Behrisch
18 : /// @author Walter Bamberger
19 : /// @author Laura Bieker
20 : /// @author Leonhard Luecken
21 : /// @date Tue, 20 Nov 2001
22 : ///
23 : // Importer for network edges stored in XML
24 : /****************************************************************************/
25 : #include <config.h>
26 :
27 : #include <string>
28 : #include <iostream>
29 : #include <map>
30 : #include <cmath>
31 : #include <utils/xml/SUMOSAXHandler.h>
32 : #include <netbuild/NBNodeCont.h>
33 : #include <netbuild/NBTypeCont.h>
34 : #include <netbuild/NBNetBuilder.h>
35 : #include <utils/xml/SUMOXMLDefinitions.h>
36 : #include <utils/common/MsgHandler.h>
37 : #include <utils/common/StringUtils.h>
38 : #include <utils/common/StringTokenizer.h>
39 : #include <utils/geom/GeomConvHelper.h>
40 : #include <utils/common/ToString.h>
41 : #include <utils/options/OptionsCont.h>
42 : #include <utils/geom/GeoConvHelper.h>
43 : #include "NIXMLNodesHandler.h"
44 : #include "NIXMLEdgesHandler.h"
45 : #include "NIImporter_SUMO.h"
46 :
47 :
48 : // ===========================================================================
49 : // method definitions
50 : // ===========================================================================
51 2371 : NIXMLEdgesHandler::NIXMLEdgesHandler(NBNodeCont& nc,
52 : NBEdgeCont& ec,
53 : NBTypeCont& tc,
54 : NBDistrictCont& dc,
55 : NBTrafficLightLogicCont& tlc,
56 2371 : OptionsCont& options) :
57 : SUMOSAXHandler("xml-edges - file"),
58 2371 : myOptions(options),
59 2371 : myNodeCont(nc),
60 2371 : myEdgeCont(ec),
61 2371 : myTypeCont(tc),
62 2371 : myDistrictCont(dc),
63 2371 : myTLLogicCont(tlc),
64 2371 : myCurrentEdge(nullptr),
65 2371 : myCurrentLaneIndex(-1),
66 2371 : myHaveReportedAboutOverwriting(false),
67 2371 : myHaveReportedAboutTypeOverride(false),
68 2371 : myHaveWarnedAboutDeprecatedLaneId(false),
69 2371 : myDefaultNodeType(SUMOXMLDefinitions::NodeTypes.hasString(options.getString("default.junctions.type"))
70 2373 : ? SUMOXMLDefinitions::NodeTypes.get(options.getString("default.junctions.type")) : SumoXMLNodeType::UNKNOWN),
71 7113 : myKeepEdgeShape(!options.getBool("plain.extend-edge-shape")) {
72 2371 : }
73 :
74 :
75 2371 : NIXMLEdgesHandler::~NIXMLEdgesHandler() {
76 2371 : delete myLocation;
77 2371 : }
78 :
79 :
80 : void
81 28530 : NIXMLEdgesHandler::myStartElement(int element,
82 : const SUMOSAXAttributes& attrs) {
83 28530 : switch (element) {
84 : case SUMO_TAG_VIEWSETTINGS_EDGES:
85 : // infer location for legacy networks that don't have location information
86 1154 : myLocation = GeoConvHelper::getLoadedPlain(getFileName());
87 1154 : break;
88 27 : case SUMO_TAG_LOCATION:
89 27 : delete myLocation;
90 27 : myLocation = NIImporter_SUMO::loadLocation(attrs, false);
91 27 : break;
92 19024 : case SUMO_TAG_EDGE:
93 19024 : addEdge(attrs);
94 19023 : break;
95 5888 : case SUMO_TAG_LANE:
96 5888 : addLane(attrs);
97 5888 : break;
98 16 : case SUMO_TAG_NEIGH:
99 16 : myCurrentEdge->getLaneStruct((int)myCurrentEdge->getNumLanes() - 1).oppositeID = attrs.getString(SUMO_ATTR_LANE);
100 16 : break;
101 222 : case SUMO_TAG_SPLIT:
102 222 : addSplit(attrs);
103 222 : break;
104 31 : case SUMO_TAG_DEL:
105 31 : deleteEdge(attrs);
106 31 : break;
107 11 : case SUMO_TAG_ROUNDABOUT:
108 11 : addRoundabout(attrs);
109 11 : break;
110 2132 : case SUMO_TAG_PARAM:
111 2132 : if (myLastParameterised.size() != 0 && myCurrentEdge != nullptr) {
112 2090 : bool ok = true;
113 2090 : const std::string key = attrs.get<std::string>(SUMO_ATTR_KEY, nullptr, ok);
114 : // circumventing empty string test
115 2090 : const std::string val = attrs.hasAttribute(SUMO_ATTR_VALUE) ? attrs.getString(SUMO_ATTR_VALUE) : "";
116 2090 : myLastParameterised.back()->setParameter(key, val);
117 : }
118 : break;
119 25 : case SUMO_TAG_STOPOFFSET: {
120 25 : bool ok = true;
121 25 : const StopOffset stopOffset(attrs, ok);
122 25 : if (!ok) {
123 1 : std::stringstream ss;
124 2 : ss << "(Error encountered at lane " << myCurrentLaneIndex << " of edge '" << myCurrentID << "' while parsing stopOffsets.)";
125 1 : WRITE_ERROR(ss.str());
126 1 : } else {
127 24 : if (myCurrentEdge->getLaneStopOffset(myCurrentLaneIndex).isDefined()) {
128 3 : std::stringstream ss;
129 3 : ss << "Duplicate definition of stopOffset for ";
130 3 : if (myCurrentLaneIndex != -1) {
131 1 : ss << "lane " << myCurrentLaneIndex << " on ";
132 : }
133 6 : ss << "edge " << myCurrentEdge->getID() << ". Ignoring duplicate specification.";
134 3 : WRITE_WARNING(ss.str());
135 24 : } else if ((stopOffset.getOffset() > myCurrentEdge->getLength()) || (stopOffset.getOffset() < 0)) {
136 4 : std::stringstream ss;
137 4 : ss << "Ignoring invalid stopOffset for ";
138 4 : if (myCurrentLaneIndex != -1) {
139 2 : ss << "lane " << myCurrentLaneIndex << " on ";
140 : }
141 4 : ss << "edge " << myCurrentEdge->getID();
142 4 : if (stopOffset.getOffset() > myCurrentEdge->getLength()) {
143 2 : ss << " (offset larger than the edge length).";
144 : } else {
145 2 : ss << " (negative offset).";
146 : }
147 4 : WRITE_WARNING(ss.str());
148 4 : } else {
149 17 : myCurrentEdge->setEdgeStopOffset(myCurrentLaneIndex, stopOffset);
150 : }
151 : }
152 : }
153 25 : break;
154 : default:
155 : break;
156 : }
157 28529 : }
158 :
159 :
160 : void
161 19024 : NIXMLEdgesHandler::addEdge(const SUMOSAXAttributes& attrs) {
162 19024 : myIsUpdate = false;
163 19024 : bool ok = true;
164 : // initialise the edge
165 19024 : myCurrentEdge = nullptr;
166 : mySplits.clear();
167 : // get the id, report an error if not given or empty...
168 19024 : myCurrentID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
169 19024 : if (!ok) {
170 59 : return;
171 : }
172 19020 : myCurrentEdge = myEdgeCont.retrieve(myCurrentID);
173 : // check deprecated (unused) attributes
174 : // use default values, first
175 40168 : myCurrentType = myCurrentEdge == nullptr ? myOptions.getString("default.type") : myCurrentEdge->getTypeID();
176 : // check whether a type's values shall be used
177 19020 : if (attrs.hasAttribute(SUMO_ATTR_TYPE)) {
178 3195 : myCurrentType = attrs.get<std::string>(SUMO_ATTR_TYPE, myCurrentID.c_str(), ok);
179 3195 : if (!ok) {
180 : return;
181 : }
182 8720 : if (!myTypeCont.knows(myCurrentType) && !myOptions.getBool("ignore-errors.edge-type")) {
183 81 : WRITE_ERRORF("Type '%' used by edge '%' was not defined (ignore with option --ignore-errors.edge-type).", myCurrentType, myCurrentID);
184 27 : return;
185 : }
186 : }
187 18991 : myCurrentEndOffset = NBEdge::UNSPECIFIED_OFFSET;
188 18991 : if (myCurrentEdge != nullptr) {
189 : // update existing edge. only update lane-specific settings when explicitly requested
190 2128 : myIsUpdate = true;
191 2128 : myCurrentSpeed = NBEdge::UNSPECIFIED_SPEED;
192 2128 : myCurrentFriction = NBEdge::UNSPECIFIED_FRICTION;
193 2128 : myCurrentPriority = myCurrentEdge->getPriority();
194 2128 : myCurrentLaneNo = myCurrentEdge->getNumLanes();
195 2128 : myPermissions = SVC_UNSPECIFIED;
196 2128 : myCurrentWidth = NBEdge::UNSPECIFIED_WIDTH;
197 2128 : myLanesSpread = myCurrentEdge->getLaneSpreadFunction();
198 2128 : mySidewalkWidth = NBEdge::UNSPECIFIED_WIDTH;
199 2128 : myBikeLaneWidth = NBEdge::UNSPECIFIED_WIDTH;
200 : } else {
201 : // this is a completely new edge. get the type specific defaults
202 16863 : myCurrentSpeed = myTypeCont.getEdgeTypeSpeed(myCurrentType);
203 16863 : myCurrentFriction = myTypeCont.getEdgeTypeFriction(myCurrentType);
204 16863 : myCurrentPriority = myTypeCont.getEdgeTypePriority(myCurrentType);
205 16863 : myCurrentLaneNo = myTypeCont.getEdgeTypeNumLanes(myCurrentType);
206 16863 : myPermissions = myTypeCont.getEdgeTypePermissions(myCurrentType);
207 16863 : myCurrentWidth = myTypeCont.getEdgeTypeWidth(myCurrentType);
208 16863 : myLanesSpread = myTypeCont.getEdgeTypeSpreadType(myCurrentType);
209 16863 : if (myLanesSpread == LaneSpreadFunction::SPREAD_UNKNOWN) {
210 824 : myLanesSpread = SUMOXMLDefinitions::LaneSpreadFunctions.get(myOptions.getString("default.spreadtype"));
211 : }
212 16863 : mySidewalkWidth = myTypeCont.getEdgeTypeSidewalkWidth(myCurrentType);
213 16863 : myBikeLaneWidth = myTypeCont.getEdgeTypeBikeLaneWidth(myCurrentType);
214 : }
215 37982 : myShape = PositionVector();
216 18991 : myLength = NBEdge::UNSPECIFIED_LOADED_LENGTH;
217 18991 : myCurrentStreetName = "";
218 18991 : myReinitKeepEdgeShape = false;
219 :
220 : // use values from the edge to overwrite if existing, then
221 18991 : if (myIsUpdate) {
222 2128 : if (!myHaveReportedAboutOverwriting) {
223 279 : WRITE_MESSAGEF(TL("Duplicate edge id occurred ('%'); assuming overwriting is wished."), myCurrentID);
224 93 : myHaveReportedAboutOverwriting = true;
225 : }
226 2128 : if (attrs.hasAttribute(SUMO_ATTR_TYPE) && myCurrentType != myCurrentEdge->getTypeID()) {
227 3 : if (!myHaveReportedAboutTypeOverride) {
228 6 : WRITE_MESSAGEF(TL("Edge '%' changed its type; assuming type override is wished."), myCurrentID);
229 2 : myHaveReportedAboutTypeOverride = true;
230 : }
231 : }
232 2128 : if (attrs.getOpt<bool>(SUMO_ATTR_REMOVE, myCurrentID.c_str(), ok, false)) {
233 0 : myEdgeCont.erase(myDistrictCont, myCurrentEdge);
234 0 : myCurrentEdge = nullptr;
235 0 : return;
236 : }
237 2128 : if (!myCurrentEdge->hasDefaultGeometry()) {
238 1654 : myShape = myCurrentEdge->getGeometry();
239 1654 : myReinitKeepEdgeShape = true;
240 : }
241 2128 : if (myCurrentEdge->hasLoadedLength()) {
242 1 : myLength = myCurrentEdge->getLoadedLength();
243 : }
244 : myCurrentStreetName = myCurrentEdge->getStreetName();
245 : }
246 : // speed, priority and the number of lanes have now default values;
247 : // try to read the real values from the file
248 18991 : if (attrs.hasAttribute(SUMO_ATTR_SPEED)) {
249 12528 : myCurrentSpeed = attrs.get<double>(SUMO_ATTR_SPEED, myCurrentID.c_str(), ok);
250 : }
251 37982 : if (myOptions.getBool("speed-in-kmh") && myCurrentSpeed != NBEdge::UNSPECIFIED_SPEED) {
252 689 : myCurrentSpeed = myCurrentSpeed / (double) 3.6;
253 : }
254 : // try to read the friction value from file
255 18991 : if (attrs.hasAttribute(SUMO_ATTR_FRICTION)) {
256 3 : myCurrentFriction = attrs.get<double>(SUMO_ATTR_FRICTION, myCurrentID.c_str(), ok);
257 : }
258 : // try to get the number of lanes
259 18991 : if (attrs.hasAttribute(SUMO_ATTR_NUMLANES)) {
260 12977 : myCurrentLaneNo = attrs.get<int>(SUMO_ATTR_NUMLANES, myCurrentID.c_str(), ok);
261 : }
262 : // try to get the priority
263 18991 : if (attrs.hasAttribute(SUMO_ATTR_PRIORITY)) {
264 10658 : myCurrentPriority = attrs.get<int>(SUMO_ATTR_PRIORITY, myCurrentID.c_str(), ok);
265 : }
266 : // try to get the width
267 18991 : if (attrs.hasAttribute(SUMO_ATTR_WIDTH)) {
268 655 : myCurrentWidth = attrs.get<double>(SUMO_ATTR_WIDTH, myCurrentID.c_str(), ok);
269 : }
270 : // try to get the offset of the stop line from the intersection
271 18991 : if (attrs.hasAttribute(SUMO_ATTR_ENDOFFSET)) {
272 11 : myCurrentEndOffset = attrs.get<double>(SUMO_ATTR_ENDOFFSET, myCurrentID.c_str(), ok);
273 : }
274 : // try to get the street name
275 18991 : if (attrs.hasAttribute(SUMO_ATTR_NAME)) {
276 1938 : myCurrentStreetName = attrs.get<std::string>(SUMO_ATTR_NAME, myCurrentID.c_str(), ok);
277 1938 : if (myCurrentStreetName != "" && myOptions.isDefault("output.street-names")) {
278 106 : myOptions.set("output.street-names", "true");
279 : }
280 : }
281 :
282 : // try to get the allowed/disallowed classes
283 18991 : if (attrs.hasAttribute(SUMO_ATTR_ALLOW) || attrs.hasAttribute(SUMO_ATTR_DISALLOW)) {
284 4770 : std::string allowS = attrs.hasAttribute(SUMO_ATTR_ALLOW) ? attrs.getStringSecure(SUMO_ATTR_ALLOW, "") : "";
285 4770 : std::string disallowS = attrs.hasAttribute(SUMO_ATTR_DISALLOW) ? attrs.getStringSecure(SUMO_ATTR_DISALLOW, "") : "";
286 : // XXX matter of interpretation: should updated permissions replace or extend previously set permissions?
287 4770 : myPermissions = parseVehicleClasses(allowS, disallowS);
288 : }
289 : // try to set the nodes
290 18991 : if (!setNodes(attrs)) {
291 : // return if this failed
292 13 : myCurrentEdge = nullptr;
293 13 : return;
294 : }
295 : // try to get the shape
296 37956 : myShape = tryGetShape(attrs);
297 : // try to get the spread type
298 18978 : myLanesSpread = tryGetLaneSpread(attrs);
299 : // try to get the length
300 18978 : myLength = attrs.getOpt<double>(SUMO_ATTR_LENGTH, myCurrentID.c_str(), ok, myLength);
301 : // try to get the sidewalkWidth
302 18978 : mySidewalkWidth = attrs.getOpt<double>(SUMO_ATTR_SIDEWALKWIDTH, myCurrentID.c_str(), ok, mySidewalkWidth);
303 : // try to get the bikeLaneWidth
304 18978 : myBikeLaneWidth = attrs.getOpt<double>(SUMO_ATTR_BIKELANEWIDTH, myCurrentID.c_str(), ok, myBikeLaneWidth);
305 : // insert the parsed edge into the edges map
306 18978 : if (!ok) {
307 10 : myCurrentEdge = nullptr;
308 10 : return;
309 : }
310 18968 : if (myFromNode == myToNode) {
311 : // this might as well be an error. We make this a warning mostly for
312 : // backward compatibility
313 9 : WRITE_WARNINGF(TL("Ignoring self-looped edge '%' at junction '%'"), myCurrentID, myFromNode->getID());
314 3 : myCurrentEdge = nullptr;
315 3 : return;
316 : }
317 : // check whether a previously defined edge shall be overwritten
318 18965 : const bool applyLaneType = myCurrentEdge == nullptr;
319 18965 : if (myCurrentEdge != nullptr) {
320 2127 : myCurrentEdge->reinit(myFromNode, myToNode, myCurrentType, myCurrentSpeed, myCurrentFriction,
321 : myCurrentLaneNo, myCurrentPriority, myShape,
322 : myCurrentWidth, myCurrentEndOffset,
323 : myCurrentStreetName, myLanesSpread,
324 2127 : myReinitKeepEdgeShape);
325 : } else {
326 : // the edge must be allocated in dependence to whether a shape is given
327 16838 : if (myShape.size() == 0) {
328 14254 : myCurrentEdge = new NBEdge(myCurrentID, myFromNode, myToNode, myCurrentType, myCurrentSpeed, myCurrentFriction,
329 : myCurrentLaneNo, myCurrentPriority, myCurrentWidth, myCurrentEndOffset,
330 42762 : myLanesSpread, myCurrentStreetName);
331 : } else {
332 2583 : myCurrentEdge = new NBEdge(myCurrentID, myFromNode, myToNode, myCurrentType, myCurrentSpeed, myCurrentFriction,
333 : myCurrentLaneNo, myCurrentPriority, myCurrentWidth, myCurrentEndOffset,
334 : myShape, myLanesSpread, myCurrentStreetName, "",
335 7754 : myKeepEdgeShape);
336 : }
337 : }
338 18964 : myCurrentEdge->setLoadedLength(myLength);
339 18964 : if (myPermissions != SVC_UNSPECIFIED) {
340 16857 : myCurrentEdge->setPermissions(myPermissions);
341 : }
342 : // apply laneType if given
343 18964 : if (applyLaneType && myCurrentType != "" && myTypeCont.knows(myCurrentType)) {
344 432 : const NBTypeCont::EdgeTypeDefinition* eType = myTypeCont.getEdgeType(myCurrentType);
345 432 : if (eType->needsLaneType()) {
346 : int lane = 0;
347 340 : for (const NBTypeCont::LaneTypeDefinition& laneType : eType->laneTypeDefinitions) {
348 236 : if (lane >= myCurrentLaneNo) {
349 : break;
350 : }
351 : if (laneType.attrs.count(SUMO_ATTR_SPEED) > 0) {
352 139 : myCurrentEdge->setSpeed(lane, laneType.speed);
353 : }
354 : if (laneType.attrs.count(SUMO_ATTR_FRICTION) > 0) {
355 0 : myCurrentEdge->setFriction(lane, laneType.friction);
356 : }
357 : if (laneType.attrs.count(SUMO_ATTR_DISALLOW) > 0 || laneType.attrs.count(SUMO_ATTR_ALLOW) > 0) {
358 54 : myCurrentEdge->setPermissions(laneType.permissions, lane);
359 : }
360 : if (laneType.attrs.count(SUMO_ATTR_WIDTH) > 0) {
361 169 : myCurrentEdge->setLaneWidth(lane, laneType.width);
362 : }
363 236 : lane++;
364 : }
365 : }
366 : }
367 : // try to get the kilometrage/mileage
368 18964 : myCurrentEdge->setDistance(attrs.getOpt<double>(SUMO_ATTR_DISTANCE, myCurrentID.c_str(), ok, myCurrentEdge->getDistance()));
369 18964 : myCurrentEdge->setRoutingType(attrs.getOpt<std::string>(SUMO_ATTR_ROUTINGTYPE, myCurrentID.c_str(), ok, myCurrentEdge->getRoutingType()));
370 : // preserve bidi edge (only as bool, the actual edge will be recomputed)
371 18964 : const std::string bidi = attrs.getOpt<std::string>(SUMO_ATTR_BIDI, myCurrentID.c_str(), ok, "");
372 18964 : myCurrentEdge->setBidi(myCurrentEdge->getBidiEdge() != nullptr || myCurrentEdge->isBidi() || bidi != "");
373 :
374 18964 : myLastParameterised.push_back(myCurrentEdge);
375 : }
376 :
377 :
378 : void
379 5888 : NIXMLEdgesHandler::addLane(const SUMOSAXAttributes& attrs) {
380 5888 : if (myCurrentEdge == nullptr) {
381 58 : if (!OptionsCont::getOptions().isInStringVector("remove-edges.explicit", myCurrentID)) {
382 87 : WRITE_ERRORF("Additional lane information could not be set - the edge with id '%s' is not known.", myCurrentID);
383 : }
384 30 : return;
385 : }
386 5859 : bool ok = true;
387 : int lane;
388 5859 : if (attrs.hasAttribute(SUMO_ATTR_ID)) {
389 0 : lane = attrs.get<int>(SUMO_ATTR_ID, myCurrentID.c_str(), ok);
390 0 : if (!myHaveWarnedAboutDeprecatedLaneId) {
391 0 : myHaveWarnedAboutDeprecatedLaneId = true;
392 0 : WRITE_WARNINGF(TL("'%' is deprecated, please use '%' instead."), toString(SUMO_ATTR_ID), toString(SUMO_ATTR_INDEX));
393 : }
394 : } else {
395 5859 : lane = attrs.get<int>(SUMO_ATTR_INDEX, myCurrentID.c_str(), ok);
396 : }
397 5859 : if (!ok) {
398 : return;
399 : }
400 : // check whether this lane exists
401 5859 : if (lane >= myCurrentEdge->getNumLanes()) {
402 3 : WRITE_ERRORF(TL("Lane index is larger than number of lanes (edge '%')."), myCurrentID);
403 1 : return;
404 : }
405 5858 : myCurrentLaneIndex = lane;
406 : // set information about allowed / disallowed vehicle classes (if specified)
407 5858 : if (attrs.hasAttribute(SUMO_ATTR_ALLOW) || attrs.hasAttribute(SUMO_ATTR_DISALLOW)) {
408 4763 : const std::string allowed = attrs.getOpt<std::string>(SUMO_ATTR_ALLOW, nullptr, ok, "");
409 4763 : const std::string disallowed = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, nullptr, ok, "");
410 4763 : myCurrentEdge->setPermissions(parseVehicleClasses(allowed, disallowed), lane);
411 : }
412 5858 : if (attrs.hasAttribute(SUMO_ATTR_PREFER)) {
413 0 : const std::string preferred = attrs.get<std::string>(SUMO_ATTR_PREFER, nullptr, ok);
414 0 : myCurrentEdge->setPreferredVehicleClass(parseVehicleClasses(preferred), lane);
415 : }
416 5858 : if (attrs.hasAttribute(SUMO_ATTR_CHANGE_LEFT) || attrs.hasAttribute(SUMO_ATTR_CHANGE_RIGHT)) {
417 47 : const std::string changeLeft = attrs.getOpt<std::string>(SUMO_ATTR_CHANGE_LEFT, nullptr, ok, "");
418 47 : const std::string changeRight = attrs.getOpt<std::string>(SUMO_ATTR_CHANGE_RIGHT, nullptr, ok, "");
419 94 : myCurrentEdge->setPermittedChanging(lane, parseVehicleClasses(changeLeft, ""), parseVehicleClasses(changeRight, ""));
420 : }
421 : // try to get the width
422 5858 : if (attrs.hasAttribute(SUMO_ATTR_WIDTH)) {
423 1947 : myCurrentEdge->setLaneWidth(lane, attrs.get<double>(SUMO_ATTR_WIDTH, myCurrentID.c_str(), ok));
424 : }
425 : // try to get the end-offset (lane shortened due to pedestrian crossing etc..)
426 5858 : if (attrs.hasAttribute(SUMO_ATTR_ENDOFFSET)) {
427 18 : myCurrentEdge->setEndOffset(lane, attrs.get<double>(SUMO_ATTR_ENDOFFSET, myCurrentID.c_str(), ok));
428 : }
429 : // try to get lane specific speed (should not occur for german networks)
430 5858 : if (attrs.hasAttribute(SUMO_ATTR_SPEED)) {
431 9 : myCurrentEdge->setSpeed(lane, attrs.get<double>(SUMO_ATTR_SPEED, myCurrentID.c_str(), ok));
432 : }
433 : // try to get lane specific friction
434 5858 : if (attrs.hasAttribute(SUMO_ATTR_FRICTION)) {
435 4 : myCurrentEdge->setFriction(lane, attrs.get<double>(SUMO_ATTR_FRICTION, myCurrentID.c_str(), ok));
436 : }
437 : // check whether this is an acceleration lane
438 5858 : if (attrs.hasAttribute(SUMO_ATTR_ACCELERATION)) {
439 4 : myCurrentEdge->setAcceleration(lane, attrs.get<bool>(SUMO_ATTR_ACCELERATION, myCurrentID.c_str(), ok));
440 : }
441 : // check whether this lane has a custom shape
442 5858 : if (attrs.hasAttribute(SUMO_ATTR_SHAPE)) {
443 72 : PositionVector shape = attrs.get<PositionVector>(SUMO_ATTR_SHAPE, myCurrentID.c_str(), ok);
444 72 : if (!NBNetBuilder::transformCoordinates(shape, true, myLocation)) {
445 0 : const std::string laneID = myCurrentID + "_" + toString(lane);
446 0 : WRITE_ERRORF(TL("Unable to project coordinates for lane '%'."), laneID);
447 : }
448 72 : if (shape.size() == 1) {
449 : // lane shape of length 1 is not permitted
450 1 : shape.push_front(myCurrentEdge->getFromNode()->getPosition());
451 1 : shape.push_back(myCurrentEdge->getToNode()->getPosition());
452 : }
453 72 : shape.removeDoublePoints();
454 72 : if (shape.size() < 2) {
455 : // ignore lane shape for very short lanes
456 : shape.clear();
457 : }
458 72 : myCurrentEdge->setLaneShape(lane, shape);
459 72 : }
460 : // set custom lane type
461 5858 : if (attrs.hasAttribute(SUMO_ATTR_TYPE)) {
462 16 : myCurrentEdge->setLaneType(lane, attrs.get<std::string>(SUMO_ATTR_TYPE, myCurrentID.c_str(), ok));
463 : }
464 5858 : myLastParameterised.push_back(&myCurrentEdge->getLaneStruct(lane));
465 : }
466 :
467 :
468 222 : void NIXMLEdgesHandler::addSplit(const SUMOSAXAttributes& attrs) {
469 222 : if (myCurrentEdge == nullptr) {
470 0 : if (!OptionsCont::getOptions().isInStringVector("remove-edges.explicit", myCurrentID)) {
471 0 : WRITE_WARNING(TL("Ignoring 'split' because it cannot be assigned to an edge"));
472 : }
473 4 : return;
474 : }
475 222 : bool ok = true;
476 : NBEdgeCont::Split e;
477 222 : e.pos = attrs.get<double>(SUMO_ATTR_POSITION, nullptr, ok);
478 222 : if (ok) {
479 437 : if (fabs(e.pos) > myCurrentEdge->getLoadedLength()) {
480 3 : WRITE_ERRORF(TL("Edge '%' has a split at invalid position %."), myCurrentID, toString(e.pos));
481 1 : return;
482 : }
483 221 : std::vector<NBEdgeCont::Split>::iterator i = find_if(mySplits.begin(), mySplits.end(), split_by_pos_finder(e.pos));
484 221 : if (i != mySplits.end()) {
485 6 : WRITE_ERRORF(TL("Edge '%' has already a split at position %."), myCurrentID, toString(e.pos));
486 2 : return;
487 : }
488 : // XXX rounding to int may duplicate the id of another split
489 438 : e.nameID = myCurrentID + "." + toString((int)e.pos);
490 219 : if (e.pos < 0) {
491 75 : e.pos += myCurrentEdge->getLoadedLength();
492 : }
493 535 : for (const std::string& id : attrs.getOpt<std::vector<std::string> >(SUMO_ATTR_LANES, myCurrentID.c_str(), ok)) {
494 : try {
495 316 : int lane = StringUtils::toInt(id);
496 314 : e.lanes.push_back(lane);
497 2 : } catch (NumberFormatException&) {
498 6 : WRITE_ERRORF(TL("Error on parsing a split (edge '%')."), myCurrentID);
499 2 : } catch (EmptyData&) {
500 0 : WRITE_ERRORF(TL("Error on parsing a split (edge '%')."), myCurrentID);
501 0 : }
502 219 : }
503 219 : if (e.lanes.empty()) {
504 195 : for (int l = 0; l < myCurrentEdge->getNumLanes(); ++l) {
505 122 : e.lanes.push_back(l);
506 : }
507 : }
508 219 : e.speed = attrs.getOpt(SUMO_ATTR_SPEED, nullptr, ok, myCurrentEdge->getSpeed());
509 223 : if (attrs.hasAttribute(SUMO_ATTR_SPEED) && myOptions.getBool("speed-in-kmh")) {
510 0 : e.speed /= 3.6;
511 : }
512 219 : e.idBefore = attrs.getOpt(SUMO_ATTR_ID_BEFORE, nullptr, ok, std::string(""));
513 219 : e.idAfter = attrs.getOpt(SUMO_ATTR_ID_AFTER, nullptr, ok, std::string(""));
514 219 : if (!ok) {
515 : return;
516 : }
517 219 : const std::string nodeID = attrs.getOpt(SUMO_ATTR_ID, nullptr, ok, e.nameID);
518 219 : if (nodeID == myCurrentEdge->getFromNode()->getID() || nodeID == myCurrentEdge->getToNode()->getID()) {
519 3 : WRITE_ERRORF(TL("Invalid split node id for edge '%' (from- and to-node are forbidden)"), myCurrentEdge->getID());
520 : return;
521 : }
522 218 : e.node = e.pos != 0 ? myNodeCont.retrieve(nodeID) : myCurrentEdge->getFromNode();
523 218 : e.offset = attrs.getOpt(SUMO_ATTR_OFFSET, nullptr, ok, 0.0);
524 435 : e.offsetFactor = OptionsCont::getOptions().getBool("lefthand") ? -1 : 1;
525 218 : if (e.node == nullptr) {
526 163 : double geomPos = e.pos;
527 163 : if (myCurrentEdge->hasLoadedLength()) {
528 5 : geomPos *= myCurrentEdge->getGeometry().length() / myCurrentEdge->getLoadedLength();
529 : }
530 163 : e.node = new NBNode(nodeID, myCurrentEdge->getGeometry().positionAtOffset(geomPos));
531 163 : myNodeCont.insert(e.node);
532 : }
533 218 : NIXMLNodesHandler::processNodeType(attrs, e.node, e.node->getID(), e.node->getPosition(), false,
534 : myDefaultNodeType, myNodeCont, myEdgeCont, myTLLogicCont, myLocation);
535 218 : mySplits.push_back(e);
536 : }
537 222 : }
538 :
539 :
540 : bool
541 18991 : NIXMLEdgesHandler::setNodes(const SUMOSAXAttributes& attrs) {
542 : // the names and the coordinates of the beginning and the end node
543 : // may be found, try
544 18991 : bool ok = true;
545 18991 : if (myIsUpdate) {
546 2128 : myFromNode = myCurrentEdge->getFromNode();
547 2128 : myToNode = myCurrentEdge->getToNode();
548 : }
549 18991 : if (attrs.hasAttribute(SUMO_ATTR_FROM)) {
550 16891 : const std::string begNodeID = attrs.get<std::string>(SUMO_ATTR_FROM, nullptr, ok);
551 16891 : if (begNodeID != "") {
552 16889 : myFromNode = myNodeCont.retrieve(begNodeID);
553 16889 : if (myFromNode == nullptr) {
554 12 : WRITE_ERRORF(TL("Edge's '%' from-node '%' is not known."), myCurrentID, begNodeID);
555 : }
556 : }
557 2100 : } else if (!myIsUpdate) {
558 9 : WRITE_ERRORF(TL("The from-node is not given for edge '%'."), myCurrentID);
559 3 : ok = false;
560 : }
561 18991 : if (attrs.hasAttribute(SUMO_ATTR_TO)) {
562 16890 : const std::string endNodeID = attrs.get<std::string>(SUMO_ATTR_TO, nullptr, ok);
563 16890 : if (endNodeID != "") {
564 16888 : myToNode = myNodeCont.retrieve(endNodeID);
565 16888 : if (myToNode == nullptr) {
566 12 : WRITE_ERRORF(TL("Edge's '%' to-node '%' is not known."), myCurrentID, endNodeID);
567 : }
568 : }
569 2101 : } else if (!myIsUpdate) {
570 9 : WRITE_ERRORF(TL("The to-node is not given for edge '%'."), myCurrentID);
571 3 : ok = false;
572 : }
573 19004 : return ok && myFromNode != nullptr && myToNode != nullptr;
574 : }
575 :
576 :
577 : PositionVector
578 18978 : NIXMLEdgesHandler::tryGetShape(const SUMOSAXAttributes& attrs) {
579 18978 : if (!attrs.hasAttribute(SUMO_ATTR_SHAPE) && myShape.size() > 0) {
580 : return myShape;
581 : }
582 : // try to build shape
583 18964 : bool ok = true;
584 18964 : if (!attrs.hasAttribute(SUMO_ATTR_SHAPE)) {
585 14333 : const double maxSegmentLength = OptionsCont::getOptions().getFloat("geometry.max-segment-length");
586 14333 : if (maxSegmentLength > 0) {
587 1 : PositionVector shape;
588 1 : shape.push_back(myFromNode->getPosition());
589 1 : shape.push_back(myToNode->getPosition());
590 : // shape is already cartesian but we must use a copy because the original will be modified
591 1 : NBNetBuilder::addGeometrySegments(shape, PositionVector(shape), maxSegmentLength);
592 : return shape;
593 1 : } else {
594 14332 : myReinitKeepEdgeShape = false;
595 14332 : return PositionVector();
596 : }
597 : }
598 4631 : PositionVector shape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, nullptr, ok, PositionVector());
599 4631 : if (!NBNetBuilder::transformCoordinates(shape, true, myLocation)) {
600 0 : WRITE_ERRORF(TL("Unable to project coordinates for edge '%'."), myCurrentID);
601 : }
602 4631 : myReinitKeepEdgeShape = myKeepEdgeShape;
603 : return shape;
604 4631 : }
605 :
606 :
607 : LaneSpreadFunction
608 18978 : NIXMLEdgesHandler::tryGetLaneSpread(const SUMOSAXAttributes& attrs) {
609 18978 : bool ok = true;
610 18978 : LaneSpreadFunction result = myLanesSpread;
611 18978 : std::string lsfS = toString(result);
612 37956 : lsfS = attrs.getOpt<std::string>(SUMO_ATTR_SPREADTYPE, myCurrentID.c_str(), ok, lsfS);
613 : if (SUMOXMLDefinitions::LaneSpreadFunctions.hasString(lsfS)) {
614 18978 : result = SUMOXMLDefinitions::LaneSpreadFunctions.get(lsfS);
615 : } else {
616 0 : WRITE_WARNINGF(TL("Ignoring unknown spreadType '%' for edge '%'."), lsfS, myCurrentID);
617 : }
618 18978 : return result;
619 : }
620 :
621 :
622 : void
623 31 : NIXMLEdgesHandler::deleteEdge(const SUMOSAXAttributes& attrs) {
624 31 : bool ok = true;
625 31 : myCurrentID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
626 31 : if (!ok) {
627 0 : return;
628 : }
629 31 : NBEdge* edge = myEdgeCont.retrieve(myCurrentID);
630 31 : if (edge == nullptr) {
631 0 : WRITE_WARNING("Ignoring tag '" + toString(SUMO_TAG_DEL) + "' for unknown edge '" +
632 : myCurrentID + "'");
633 0 : return;
634 : }
635 31 : const int lane = attrs.getOpt<int>(SUMO_ATTR_INDEX, myCurrentID.c_str(), ok, -1);
636 31 : if (lane < 0) {
637 28 : myEdgeCont.extract(myDistrictCont, edge, true);
638 : } else {
639 3 : edge->deleteLane(lane, false, true);
640 : }
641 : }
642 :
643 :
644 : void
645 28528 : NIXMLEdgesHandler::myEndElement(int element) {
646 28528 : if (element == SUMO_TAG_VIEWSETTINGS_EDGES) {
647 1153 : delete myLocation;
648 1153 : myLocation = nullptr;
649 1153 : return;
650 : }
651 27375 : if (myCurrentEdge == nullptr) {
652 : return;
653 : }
654 27176 : if (element == SUMO_TAG_EDGE) {
655 : myLastParameterised.pop_back();
656 : // add bike lane, wait until lanes are loaded to avoid building if it already exists
657 18964 : if (myBikeLaneWidth != NBEdge::UNSPECIFIED_WIDTH) {
658 7 : myCurrentEdge->addBikeLane(myBikeLaneWidth);
659 : }
660 : // add sidewalk, wait until lanes are loaded to avoid building if it already exists
661 18964 : if (mySidewalkWidth != NBEdge::UNSPECIFIED_WIDTH) {
662 44 : myCurrentEdge->addSidewalk(mySidewalkWidth);
663 : }
664 : // apply default stopOffsets of edge to all lanes without specified stopOffset.
665 18964 : const StopOffset stopOffsets = myCurrentEdge->getEdgeStopOffset();
666 18964 : if (stopOffsets.isDefined()) {
667 32 : for (int i = 0; i < (int)myCurrentEdge->getLanes().size(); i++) {
668 24 : myCurrentEdge->setEdgeStopOffset(i, stopOffsets, false);
669 : }
670 : }
671 18964 : if (!myIsUpdate) {
672 : try {
673 16837 : if (!myEdgeCont.insert(myCurrentEdge)) {
674 0 : WRITE_ERRORF(TL("Duplicate edge '%' occurred."), myCurrentID);
675 0 : delete myCurrentEdge;
676 0 : myCurrentEdge = nullptr;
677 0 : return;
678 : }
679 0 : } catch (InvalidArgument& e) {
680 0 : WRITE_ERROR(e.what());
681 0 : throw;
682 0 : } catch (...) {
683 0 : WRITE_ERRORF(TL("An important information is missing in edge '%'."), myCurrentID);
684 0 : }
685 : }
686 18964 : myEdgeCont.processSplits(myCurrentEdge, mySplits, myNodeCont, myDistrictCont, myTLLogicCont);
687 18964 : myCurrentEdge = nullptr;
688 8212 : } else if (element == SUMO_TAG_LANE && myCurrentLaneIndex != -1) {
689 : myLastParameterised.pop_back();
690 5858 : myCurrentLaneIndex = -1;
691 : }
692 : }
693 :
694 :
695 : void
696 11 : NIXMLEdgesHandler::addRoundabout(const SUMOSAXAttributes& attrs) {
697 11 : bool ok = true;
698 11 : const std::vector<std::string>& edgeIDs = attrs.get<std::vector<std::string> >(SUMO_ATTR_EDGES, nullptr, ok);
699 11 : if (ok) {
700 : EdgeSet roundabout;
701 77 : for (const std::string& eID : edgeIDs) {
702 66 : NBEdge* edge = myEdgeCont.retrieve(eID);
703 66 : if (edge == nullptr) {
704 0 : if (!myEdgeCont.wasIgnored(eID)) {
705 0 : WRITE_ERRORF(TL("Unknown edge '%' in roundabout."), eID);
706 : }
707 : } else {
708 : roundabout.insert(edge);
709 : }
710 : }
711 11 : myEdgeCont.addRoundabout(roundabout);
712 : }
713 11 : }
714 :
715 :
716 : /****************************************************************************/
|