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