Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2024 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 1890 : NIXMLEdgesHandler::NIXMLEdgesHandler(NBNodeCont& nc,
52 : NBEdgeCont& ec,
53 : NBTypeCont& tc,
54 : NBDistrictCont& dc,
55 : NBTrafficLightLogicCont& tlc,
56 1890 : OptionsCont& options) :
57 : SUMOSAXHandler("xml-edges - file"),
58 1890 : myOptions(options),
59 1890 : myNodeCont(nc),
60 1890 : myEdgeCont(ec),
61 1890 : myTypeCont(tc),
62 1890 : myDistrictCont(dc),
63 1890 : myTLLogicCont(tlc),
64 1890 : myCurrentEdge(nullptr),
65 1890 : myCurrentLaneIndex(-1),
66 1890 : myHaveReportedAboutOverwriting(false),
67 1890 : myHaveReportedAboutTypeOverride(false),
68 1890 : myHaveWarnedAboutDeprecatedLaneId(false),
69 3780 : myKeepEdgeShape(!options.getBool("plain.extend-edge-shape")) {
70 1890 : }
71 :
72 :
73 1890 : NIXMLEdgesHandler::~NIXMLEdgesHandler() {
74 1890 : delete myLocation;
75 1890 : }
76 :
77 :
78 : void
79 27465 : NIXMLEdgesHandler::myStartElement(int element,
80 : const SUMOSAXAttributes& attrs) {
81 27465 : switch (element) {
82 : case SUMO_TAG_VIEWSETTINGS_EDGES:
83 : // infer location for legacy networks that don't have location information
84 1076 : myLocation = GeoConvHelper::getLoadedPlain(getFileName());
85 1076 : break;
86 20 : case SUMO_TAG_LOCATION:
87 20 : delete myLocation;
88 20 : myLocation = NIImporter_SUMO::loadLocation(attrs, false);
89 20 : break;
90 18438 : case SUMO_TAG_EDGE:
91 18438 : addEdge(attrs);
92 18437 : break;
93 5710 : case SUMO_TAG_LANE:
94 5710 : addLane(attrs);
95 5710 : break;
96 13 : case SUMO_TAG_NEIGH:
97 13 : myCurrentEdge->getLaneStruct((int)myCurrentEdge->getNumLanes() - 1).oppositeID = attrs.getString(SUMO_ATTR_LANE);
98 13 : break;
99 200 : case SUMO_TAG_SPLIT:
100 200 : addSplit(attrs);
101 200 : 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 1941 : case SUMO_TAG_PARAM:
109 1941 : if (myLastParameterised.size() != 0 && myCurrentEdge != nullptr) {
110 1899 : bool ok = true;
111 1899 : const std::string key = attrs.get<std::string>(SUMO_ATTR_KEY, nullptr, ok);
112 : // circumventing empty string test
113 1899 : const std::string val = attrs.hasAttribute(SUMO_ATTR_VALUE) ? attrs.getString(SUMO_ATTR_VALUE) : "";
114 1899 : 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 27464 : }
156 :
157 :
158 : void
159 18438 : NIXMLEdgesHandler::addEdge(const SUMOSAXAttributes& attrs) {
160 18438 : myIsUpdate = false;
161 18438 : bool ok = true;
162 : // initialise the edge
163 18438 : myCurrentEdge = nullptr;
164 : mySplits.clear();
165 : // get the id, report an error if not given or empty...
166 18438 : myCurrentID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
167 18438 : if (!ok) {
168 59 : return;
169 : }
170 18434 : myCurrentEdge = myEdgeCont.retrieve(myCurrentID);
171 : // check deprecated (unused) attributes
172 : // use default values, first
173 18434 : myCurrentType = myOptions.getString("default.type");
174 18434 : myCurrentPriority = myTypeCont.getEdgeTypePriority(myCurrentType);
175 18434 : myCurrentLaneNo = myTypeCont.getEdgeTypeNumLanes(myCurrentType);
176 18434 : myCurrentEndOffset = NBEdge::UNSPECIFIED_OFFSET;
177 18434 : if (myCurrentEdge != nullptr) {
178 : // update existing edge. only update lane-specific settings when explicitly requested
179 1877 : myIsUpdate = true;
180 1877 : myCurrentSpeed = NBEdge::UNSPECIFIED_SPEED;
181 1877 : myCurrentFriction = NBEdge::UNSPECIFIED_FRICTION;
182 1877 : myPermissions = SVC_UNSPECIFIED;
183 1877 : myCurrentWidth = NBEdge::UNSPECIFIED_WIDTH;
184 : myCurrentType = myCurrentEdge->getTypeID();
185 3754 : myLanesSpread = SUMOXMLDefinitions::LaneSpreadFunctions.get(myOptions.getString("default.spreadtype"));
186 : } else {
187 : // this is a completely new edge. get the type specific defaults
188 16557 : myCurrentSpeed = myTypeCont.getEdgeTypeSpeed(myCurrentType);
189 16557 : myCurrentFriction = myTypeCont.getEdgeTypeFriction(myCurrentType);
190 16557 : myPermissions = myTypeCont.getEdgeTypePermissions(myCurrentType);
191 16557 : myCurrentWidth = myTypeCont.getEdgeTypeWidth(myCurrentType);
192 16557 : myLanesSpread = myTypeCont.getEdgeTypeSpreadType(myCurrentType);
193 : }
194 36868 : myShape = PositionVector();
195 18434 : myLength = NBEdge::UNSPECIFIED_LOADED_LENGTH;
196 18434 : myCurrentStreetName = "";
197 18434 : myReinitKeepEdgeShape = false;
198 18434 : mySidewalkWidth = NBEdge::UNSPECIFIED_WIDTH;
199 18434 : myBikeLaneWidth = NBEdge::UNSPECIFIED_WIDTH;
200 : // check whether a type's values shall be used
201 18434 : if (attrs.hasAttribute(SUMO_ATTR_TYPE)) {
202 3118 : myCurrentType = attrs.get<std::string>(SUMO_ATTR_TYPE, myCurrentID.c_str(), ok);
203 3118 : if (!ok) {
204 : return;
205 : }
206 8509 : 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 3089 : myCurrentSpeed = myTypeCont.getEdgeTypeSpeed(myCurrentType);
211 3089 : myCurrentPriority = myTypeCont.getEdgeTypePriority(myCurrentType);
212 3089 : myCurrentLaneNo = myTypeCont.getEdgeTypeNumLanes(myCurrentType);
213 3089 : myPermissions = myTypeCont.getEdgeTypePermissions(myCurrentType);
214 3089 : myCurrentWidth = myTypeCont.getEdgeTypeWidth(myCurrentType);
215 3089 : myLanesSpread = myTypeCont.getEdgeTypeSpreadType(myCurrentType);
216 3089 : mySidewalkWidth = myTypeCont.getEdgeTypeSidewalkWidth(myCurrentType);
217 3089 : myBikeLaneWidth = myTypeCont.getEdgeTypeBikeLaneWidth(myCurrentType);
218 : }
219 : // use values from the edge to overwrite if existing, then
220 18405 : if (myIsUpdate) {
221 1877 : if (!myHaveReportedAboutOverwriting) {
222 246 : WRITE_MESSAGEF(TL("Duplicate edge id occurred ('%'); assuming overwriting is wished."), myCurrentID);
223 82 : myHaveReportedAboutOverwriting = true;
224 : }
225 1877 : 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 1877 : 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 1877 : myCurrentPriority = myCurrentEdge->getPriority();
237 1877 : myCurrentLaneNo = myCurrentEdge->getNumLanes();
238 1877 : if (!myCurrentEdge->hasDefaultGeometry()) {
239 1442 : myShape = myCurrentEdge->getGeometry();
240 1442 : myReinitKeepEdgeShape = true;
241 : }
242 1877 : myLanesSpread = myCurrentEdge->getLaneSpreadFunction();
243 1877 : 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 18405 : if (attrs.hasAttribute(SUMO_ATTR_SPEED)) {
251 12216 : myCurrentSpeed = attrs.get<double>(SUMO_ATTR_SPEED, myCurrentID.c_str(), ok);
252 : }
253 36810 : 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 18405 : 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 18405 : if (attrs.hasAttribute(SUMO_ATTR_NUMLANES)) {
262 12667 : myCurrentLaneNo = attrs.get<int>(SUMO_ATTR_NUMLANES, myCurrentID.c_str(), ok);
263 : }
264 : // try to get the priority
265 18405 : if (attrs.hasAttribute(SUMO_ATTR_PRIORITY)) {
266 10362 : myCurrentPriority = attrs.get<int>(SUMO_ATTR_PRIORITY, myCurrentID.c_str(), ok);
267 : }
268 : // try to get the width
269 18405 : if (attrs.hasAttribute(SUMO_ATTR_WIDTH)) {
270 603 : 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 18405 : 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 18405 : if (attrs.hasAttribute(SUMO_ATTR_NAME)) {
278 1848 : myCurrentStreetName = attrs.get<std::string>(SUMO_ATTR_NAME, myCurrentID.c_str(), ok);
279 1848 : if (myCurrentStreetName != "" && myOptions.isDefault("output.street-names")) {
280 100 : myOptions.set("output.street-names", "true");
281 : }
282 : }
283 :
284 : // try to get the allowed/disallowed classes
285 18405 : if (attrs.hasAttribute(SUMO_ATTR_ALLOW) || attrs.hasAttribute(SUMO_ATTR_DISALLOW)) {
286 4647 : std::string allowS = attrs.hasAttribute(SUMO_ATTR_ALLOW) ? attrs.getStringSecure(SUMO_ATTR_ALLOW, "") : "";
287 4647 : 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 4647 : myPermissions = parseVehicleClasses(allowS, disallowS);
290 : }
291 : // try to set the nodes
292 18405 : if (!setNodes(attrs)) {
293 : // return if this failed
294 13 : myCurrentEdge = nullptr;
295 13 : return;
296 : }
297 : // try to get the shape
298 36784 : myShape = tryGetShape(attrs);
299 : // try to get the spread type
300 18392 : myLanesSpread = tryGetLaneSpread(attrs);
301 : // try to get the length
302 18392 : myLength = attrs.getOpt<double>(SUMO_ATTR_LENGTH, myCurrentID.c_str(), ok, myLength);
303 : // try to get the sidewalkWidth
304 18392 : mySidewalkWidth = attrs.getOpt<double>(SUMO_ATTR_SIDEWALKWIDTH, myCurrentID.c_str(), ok, mySidewalkWidth);
305 : // try to get the bikeLaneWidth
306 18392 : myBikeLaneWidth = attrs.getOpt<double>(SUMO_ATTR_BIKELANEWIDTH, myCurrentID.c_str(), ok, myBikeLaneWidth);
307 : // insert the parsed edge into the edges map
308 18392 : if (!ok) {
309 10 : myCurrentEdge = nullptr;
310 10 : return;
311 : }
312 18382 : 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 18379 : const bool applyLaneType = myCurrentEdge == nullptr;
321 18379 : if (myCurrentEdge != nullptr) {
322 1876 : myCurrentEdge->reinit(myFromNode, myToNode, myCurrentType, myCurrentSpeed, myCurrentFriction,
323 : myCurrentLaneNo, myCurrentPriority, myShape,
324 : myCurrentWidth, myCurrentEndOffset,
325 : myCurrentStreetName, myLanesSpread,
326 1876 : myReinitKeepEdgeShape);
327 : } else {
328 : // the edge must be allocated in dependence to whether a shape is given
329 16503 : if (myShape.size() == 0) {
330 14017 : myCurrentEdge = new NBEdge(myCurrentID, myFromNode, myToNode, myCurrentType, myCurrentSpeed, myCurrentFriction,
331 : myCurrentLaneNo, myCurrentPriority, myCurrentWidth, myCurrentEndOffset,
332 42051 : myLanesSpread, myCurrentStreetName);
333 : } else {
334 2485 : myCurrentEdge = new NBEdge(myCurrentID, myFromNode, myToNode, myCurrentType, myCurrentSpeed, myCurrentFriction,
335 : myCurrentLaneNo, myCurrentPriority, myCurrentWidth, myCurrentEndOffset,
336 : myShape, myLanesSpread, myCurrentStreetName, "",
337 7460 : myKeepEdgeShape);
338 : }
339 : }
340 18378 : myCurrentEdge->setLoadedLength(myLength);
341 18378 : if (myPermissions != SVC_UNSPECIFIED) {
342 16525 : myCurrentEdge->setPermissions(myPermissions);
343 : }
344 : // apply laneType if given
345 18378 : if (applyLaneType && myCurrentType != "" && myTypeCont.knows(myCurrentType)) {
346 422 : const NBTypeCont::EdgeTypeDefinition* eType = myTypeCont.getEdgeType(myCurrentType);
347 422 : 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 18378 : myCurrentEdge->setDistance(attrs.getOpt<double>(SUMO_ATTR_DISTANCE, myCurrentID.c_str(), ok, myCurrentEdge->getDistance()));
371 : // preserve bidi edge (only as boo, the actual edge will be recomputed)
372 18378 : const std::string bidi = attrs.getOpt<std::string>(SUMO_ATTR_BIDI, myCurrentID.c_str(), ok, "");
373 18378 : myCurrentEdge->setBidi(myCurrentEdge->getBidiEdge() != nullptr || bidi != "");
374 :
375 18378 : myLastParameterised.push_back(myCurrentEdge);
376 : }
377 :
378 :
379 : void
380 5710 : NIXMLEdgesHandler::addLane(const SUMOSAXAttributes& attrs) {
381 5710 : if (myCurrentEdge == nullptr) {
382 58 : if (!OptionsCont::getOptions().isInStringVector("remove-edges.explicit", myCurrentID)) {
383 87 : WRITE_ERRORF("Additional lane information could not be set - the edge with id '%s' is not known.", myCurrentID);
384 : }
385 30 : return;
386 : }
387 5681 : bool ok = true;
388 : int lane;
389 5681 : if (attrs.hasAttribute(SUMO_ATTR_ID)) {
390 0 : lane = attrs.get<int>(SUMO_ATTR_ID, myCurrentID.c_str(), ok);
391 0 : if (!myHaveWarnedAboutDeprecatedLaneId) {
392 0 : myHaveWarnedAboutDeprecatedLaneId = true;
393 0 : WRITE_WARNINGF(TL("'%' is deprecated, please use '%' instead."), toString(SUMO_ATTR_ID), toString(SUMO_ATTR_INDEX));
394 : }
395 : } else {
396 5681 : lane = attrs.get<int>(SUMO_ATTR_INDEX, myCurrentID.c_str(), ok);
397 : }
398 5681 : if (!ok) {
399 : return;
400 : }
401 : // check whether this lane exists
402 5681 : if (lane >= myCurrentEdge->getNumLanes()) {
403 3 : WRITE_ERRORF(TL("Lane index is larger than number of lanes (edge '%')."), myCurrentID);
404 1 : return;
405 : }
406 5680 : myCurrentLaneIndex = lane;
407 : // set information about allowed / disallowed vehicle classes (if specified)
408 5680 : if (attrs.hasAttribute(SUMO_ATTR_ALLOW) || attrs.hasAttribute(SUMO_ATTR_DISALLOW)) {
409 4675 : const std::string allowed = attrs.getOpt<std::string>(SUMO_ATTR_ALLOW, nullptr, ok, "");
410 4675 : const std::string disallowed = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, nullptr, ok, "");
411 4675 : myCurrentEdge->setPermissions(parseVehicleClasses(allowed, disallowed), lane);
412 : }
413 5680 : if (attrs.hasAttribute(SUMO_ATTR_PREFER)) {
414 0 : const std::string preferred = attrs.get<std::string>(SUMO_ATTR_PREFER, nullptr, ok);
415 0 : myCurrentEdge->setPreferredVehicleClass(parseVehicleClasses(preferred), lane);
416 : }
417 5680 : if (attrs.hasAttribute(SUMO_ATTR_CHANGE_LEFT) || attrs.hasAttribute(SUMO_ATTR_CHANGE_RIGHT)) {
418 41 : const std::string changeLeft = attrs.getOpt<std::string>(SUMO_ATTR_CHANGE_LEFT, nullptr, ok, "");
419 41 : const std::string changeRight = attrs.getOpt<std::string>(SUMO_ATTR_CHANGE_RIGHT, nullptr, ok, "");
420 82 : myCurrentEdge->setPermittedChanging(lane, parseVehicleClasses(changeLeft, ""), parseVehicleClasses(changeRight, ""));
421 : }
422 : // try to get the width
423 5680 : if (attrs.hasAttribute(SUMO_ATTR_WIDTH)) {
424 1903 : myCurrentEdge->setLaneWidth(lane, attrs.get<double>(SUMO_ATTR_WIDTH, myCurrentID.c_str(), ok));
425 : }
426 : // try to get the end-offset (lane shortened due to pedestrian crossing etc..)
427 5680 : if (attrs.hasAttribute(SUMO_ATTR_ENDOFFSET)) {
428 18 : myCurrentEdge->setEndOffset(lane, attrs.get<double>(SUMO_ATTR_ENDOFFSET, myCurrentID.c_str(), ok));
429 : }
430 : // try to get lane specific speed (should not occur for german networks)
431 5680 : if (attrs.hasAttribute(SUMO_ATTR_SPEED)) {
432 9 : myCurrentEdge->setSpeed(lane, attrs.get<double>(SUMO_ATTR_SPEED, myCurrentID.c_str(), ok));
433 : }
434 : // try to get lane specific friction
435 5680 : if (attrs.hasAttribute(SUMO_ATTR_FRICTION)) {
436 4 : myCurrentEdge->setFriction(lane, attrs.get<double>(SUMO_ATTR_FRICTION, myCurrentID.c_str(), ok));
437 : }
438 : // check whether this is an acceleration lane
439 5680 : if (attrs.hasAttribute(SUMO_ATTR_ACCELERATION)) {
440 2 : myCurrentEdge->setAcceleration(lane, attrs.get<bool>(SUMO_ATTR_ACCELERATION, myCurrentID.c_str(), ok));
441 : }
442 : // check whether this lane has a custom shape
443 5680 : if (attrs.hasAttribute(SUMO_ATTR_SHAPE)) {
444 68 : PositionVector shape = attrs.get<PositionVector>(SUMO_ATTR_SHAPE, myCurrentID.c_str(), ok);
445 68 : if (!NBNetBuilder::transformCoordinates(shape, true, myLocation)) {
446 0 : const std::string laneID = myCurrentID + "_" + toString(lane);
447 0 : WRITE_ERRORF(TL("Unable to project coordinates for lane '%'."), laneID);
448 : }
449 68 : if (shape.size() == 1) {
450 : // lane shape of length 1 is not permitted
451 1 : shape.push_front(myCurrentEdge->getFromNode()->getPosition());
452 1 : shape.push_back(myCurrentEdge->getToNode()->getPosition());
453 : }
454 68 : shape.removeDoublePoints();
455 68 : if (shape.size() < 2) {
456 : // ignore lane shape for very short lanes
457 : shape.clear();
458 : }
459 68 : myCurrentEdge->setLaneShape(lane, shape);
460 68 : }
461 : // set custom lane type
462 5680 : if (attrs.hasAttribute(SUMO_ATTR_TYPE)) {
463 16 : myCurrentEdge->setLaneType(lane, attrs.get<std::string>(SUMO_ATTR_TYPE, myCurrentID.c_str(), ok));
464 : }
465 5680 : myLastParameterised.push_back(&myCurrentEdge->getLaneStruct(lane));
466 : }
467 :
468 :
469 200 : void NIXMLEdgesHandler::addSplit(const SUMOSAXAttributes& attrs) {
470 200 : if (myCurrentEdge == nullptr) {
471 0 : if (!OptionsCont::getOptions().isInStringVector("remove-edges.explicit", myCurrentID)) {
472 0 : WRITE_WARNING(TL("Ignoring 'split' because it cannot be assigned to an edge"));
473 : }
474 4 : return;
475 : }
476 200 : bool ok = true;
477 : NBEdgeCont::Split e;
478 200 : e.pos = attrs.get<double>(SUMO_ATTR_POSITION, nullptr, ok);
479 200 : if (ok) {
480 396 : if (fabs(e.pos) > myCurrentEdge->getLoadedLength()) {
481 3 : WRITE_ERRORF(TL("Edge '%' has a split at invalid position %."), myCurrentID, toString(e.pos));
482 1 : return;
483 : }
484 199 : std::vector<NBEdgeCont::Split>::iterator i = find_if(mySplits.begin(), mySplits.end(), split_by_pos_finder(e.pos));
485 199 : if (i != mySplits.end()) {
486 6 : WRITE_ERRORF(TL("Edge '%' has already a split at position %."), myCurrentID, toString(e.pos));
487 2 : return;
488 : }
489 : // XXX rounding to int may duplicate the id of another split
490 394 : e.nameID = myCurrentID + "." + toString((int)e.pos);
491 197 : if (e.pos < 0) {
492 35 : e.pos += myCurrentEdge->getGeometry().length();
493 : }
494 501 : for (const std::string& id : attrs.getOpt<std::vector<std::string> >(SUMO_ATTR_LANES, myCurrentID.c_str(), ok)) {
495 : try {
496 304 : int lane = StringUtils::toInt(id);
497 302 : e.lanes.push_back(lane);
498 2 : } catch (NumberFormatException&) {
499 6 : WRITE_ERRORF(TL("Error on parsing a split (edge '%')."), myCurrentID);
500 2 : } catch (EmptyData&) {
501 0 : WRITE_ERRORF(TL("Error on parsing a split (edge '%')."), myCurrentID);
502 0 : }
503 197 : }
504 197 : if (e.lanes.empty()) {
505 155 : for (int l = 0; l < myCurrentEdge->getNumLanes(); ++l) {
506 99 : e.lanes.push_back(l);
507 : }
508 : }
509 197 : e.speed = attrs.getOpt(SUMO_ATTR_SPEED, nullptr, ok, myCurrentEdge->getSpeed());
510 201 : if (attrs.hasAttribute(SUMO_ATTR_SPEED) && myOptions.getBool("speed-in-kmh")) {
511 0 : e.speed /= 3.6;
512 : }
513 197 : e.idBefore = attrs.getOpt(SUMO_ATTR_ID_BEFORE, nullptr, ok, std::string(""));
514 197 : e.idAfter = attrs.getOpt(SUMO_ATTR_ID_AFTER, nullptr, ok, std::string(""));
515 197 : if (!ok) {
516 : return;
517 : }
518 197 : const std::string nodeID = attrs.getOpt(SUMO_ATTR_ID, nullptr, ok, e.nameID);
519 197 : if (nodeID == myCurrentEdge->getFromNode()->getID() || nodeID == myCurrentEdge->getToNode()->getID()) {
520 3 : WRITE_ERRORF(TL("Invalid split node id for edge '%' (from- and to-node are forbidden)"), myCurrentEdge->getID());
521 : return;
522 : }
523 196 : e.node = myNodeCont.retrieve(nodeID);
524 391 : e.offsetFactor = OptionsCont::getOptions().getBool("lefthand") ? -1 : 1;
525 196 : if (e.node == nullptr) {
526 189 : double geomPos = e.pos;
527 189 : if (myCurrentEdge->hasLoadedLength()) {
528 4 : geomPos *= myCurrentEdge->getGeometry().length() / myCurrentEdge->getLoadedLength();
529 : }
530 189 : e.node = new NBNode(nodeID, myCurrentEdge->getGeometry().positionAtOffset(geomPos));
531 189 : myNodeCont.insert(e.node);
532 : }
533 196 : NIXMLNodesHandler::processNodeType(attrs, e.node, e.node->getID(), e.node->getPosition(), false,
534 : myNodeCont, myEdgeCont, myTLLogicCont, myLocation);
535 196 : mySplits.push_back(e);
536 : }
537 200 : }
538 :
539 :
540 : bool
541 18405 : NIXMLEdgesHandler::setNodes(const SUMOSAXAttributes& attrs) {
542 : // the names and the coordinates of the beginning and the end node
543 : // may be found, try
544 18405 : bool ok = true;
545 18405 : if (myIsUpdate) {
546 1877 : myFromNode = myCurrentEdge->getFromNode();
547 : myToNode = myCurrentEdge->getToNode();
548 : }
549 18405 : if (attrs.hasAttribute(SUMO_ATTR_FROM)) {
550 16556 : const std::string begNodeID = attrs.get<std::string>(SUMO_ATTR_FROM, nullptr, ok);
551 16556 : if (begNodeID != "") {
552 16554 : myFromNode = myNodeCont.retrieve(begNodeID);
553 16554 : if (myFromNode == nullptr) {
554 12 : WRITE_ERRORF(TL("Edge's '%' from-node '%' is not known."), myCurrentID, begNodeID);
555 : }
556 : }
557 1849 : } else if (!myIsUpdate) {
558 9 : WRITE_ERRORF(TL("The from-node is not given for edge '%'."), myCurrentID);
559 3 : ok = false;
560 : }
561 18405 : if (attrs.hasAttribute(SUMO_ATTR_TO)) {
562 16555 : const std::string endNodeID = attrs.get<std::string>(SUMO_ATTR_TO, nullptr, ok);
563 16555 : if (endNodeID != "") {
564 16553 : myToNode = myNodeCont.retrieve(endNodeID);
565 16553 : if (myToNode == nullptr) {
566 12 : WRITE_ERRORF(TL("Edge's '%' to-node '%' is not known."), myCurrentID, endNodeID);
567 : }
568 : }
569 1850 : } else if (!myIsUpdate) {
570 9 : WRITE_ERRORF(TL("The to-node is not given for edge '%'."), myCurrentID);
571 3 : ok = false;
572 : }
573 18418 : return ok && myFromNode != nullptr && myToNode != nullptr;
574 : }
575 :
576 :
577 : PositionVector
578 18392 : NIXMLEdgesHandler::tryGetShape(const SUMOSAXAttributes& attrs) {
579 18392 : if (!attrs.hasAttribute(SUMO_ATTR_SHAPE) && myShape.size() > 0) {
580 : return myShape;
581 : }
582 : // try to build shape
583 18382 : bool ok = true;
584 18382 : if (!attrs.hasAttribute(SUMO_ATTR_SHAPE)) {
585 14091 : const double maxSegmentLength = OptionsCont::getOptions().getFloat("geometry.max-segment-length");
586 14091 : 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 14090 : myReinitKeepEdgeShape = false;
595 14090 : return PositionVector();
596 : }
597 : }
598 4291 : PositionVector shape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, nullptr, ok, PositionVector());
599 4291 : if (!NBNetBuilder::transformCoordinates(shape, true, myLocation)) {
600 0 : WRITE_ERRORF(TL("Unable to project coordinates for edge '%'."), myCurrentID);
601 : }
602 4291 : myReinitKeepEdgeShape = myKeepEdgeShape;
603 : return shape;
604 4291 : }
605 :
606 :
607 : LaneSpreadFunction
608 18392 : NIXMLEdgesHandler::tryGetLaneSpread(const SUMOSAXAttributes& attrs) {
609 18392 : bool ok = true;
610 18392 : LaneSpreadFunction result = myLanesSpread;
611 18392 : std::string lsfS = toString(result);
612 36784 : lsfS = attrs.getOpt<std::string>(SUMO_ATTR_SPREADTYPE, myCurrentID.c_str(), ok, lsfS);
613 : if (SUMOXMLDefinitions::LaneSpreadFunctions.hasString(lsfS)) {
614 18392 : result = SUMOXMLDefinitions::LaneSpreadFunctions.get(lsfS);
615 : } else {
616 0 : WRITE_WARNINGF(TL("Ignoring unknown spreadType '%' for edge '%'."), lsfS, myCurrentID);
617 : }
618 18392 : 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 27463 : NIXMLEdgesHandler::myEndElement(int element) {
646 27463 : if (element == SUMO_TAG_VIEWSETTINGS_EDGES) {
647 1075 : delete myLocation;
648 1075 : myLocation = nullptr;
649 1075 : return;
650 : }
651 26388 : if (myCurrentEdge == nullptr) {
652 : return;
653 : }
654 26196 : 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 18378 : 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 18378 : if (mySidewalkWidth != NBEdge::UNSPECIFIED_WIDTH) {
662 44 : myCurrentEdge->addSidewalk(mySidewalkWidth);
663 : }
664 : // apply default stopOffsets of edge to all lanes without specified stopOffset.
665 18378 : const StopOffset stopOffsets = myCurrentEdge->getEdgeStopOffset();
666 18378 : 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 18378 : if (!myIsUpdate) {
672 : try {
673 16502 : 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 18378 : myEdgeCont.processSplits(myCurrentEdge, mySplits, myNodeCont, myDistrictCont, myTLLogicCont);
687 18378 : myCurrentEdge = nullptr;
688 7818 : } else if (element == SUMO_TAG_LANE && myCurrentLaneIndex != -1) {
689 : myLastParameterised.pop_back();
690 5680 : 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 : /****************************************************************************/
|