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 NIXMLConnectionsHandler.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Michael Behrisch
18 : /// @author Laura Bieker
19 : /// @date Thu, 17 Oct 2002
20 : ///
21 : // Importer for edge connections stored in XML
22 : /****************************************************************************/
23 : #include <config.h>
24 :
25 : #include <string>
26 : #include <iostream>
27 : #include "NIXMLConnectionsHandler.h"
28 : #include <netbuild/NBEdge.h>
29 : #include <netbuild/NBEdgeCont.h>
30 : #include <netbuild/NBNodeCont.h>
31 : #include <netbuild/NBTrafficLightLogicCont.h>
32 : #include <netbuild/NBNode.h>
33 : #include <netbuild/NBNetBuilder.h>
34 : #include <utils/common/StringTokenizer.h>
35 : #include <utils/xml/SUMOSAXHandler.h>
36 : #include <utils/xml/SUMOXMLDefinitions.h>
37 : #include <utils/common/ToString.h>
38 : #include <utils/common/StringUtils.h>
39 : #include <utils/common/UtilExceptions.h>
40 : #include <utils/common/MsgHandler.h>
41 : #include <utils/options/OptionsCont.h>
42 :
43 :
44 : // ===========================================================================
45 : // method definitions
46 : // ===========================================================================
47 2168 : NIXMLConnectionsHandler::NIXMLConnectionsHandler(NBEdgeCont& ec, NBNodeCont& nc, NBTrafficLightLogicCont& tlc) :
48 : SUMOSAXHandler("xml-connection-description"),
49 2168 : myEdgeCont(ec),
50 2168 : myNodeCont(nc),
51 2168 : myTLLogicCont(tlc),
52 2168 : myHaveWarnedAboutDeprecatedLanes(false),
53 4336 : myErrorMsgHandler(OptionsCont::getOptions().getBool("ignore-errors.connections") ?
54 8672 : MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance()) {}
55 :
56 :
57 2168 : NIXMLConnectionsHandler::~NIXMLConnectionsHandler() {}
58 :
59 :
60 : void
61 2441 : NIXMLConnectionsHandler::myStartElement(int element,
62 : const SUMOSAXAttributes& attrs) {
63 2441 : if (element == SUMO_TAG_DEL) {
64 69 : bool ok = true;
65 69 : std::string from = attrs.get<std::string>(SUMO_ATTR_FROM, nullptr, ok);
66 69 : std::string to = attrs.get<std::string>(SUMO_ATTR_TO, nullptr, ok);
67 69 : if (!ok) {
68 : return;
69 : }
70 : // these connections were removed when the edge was deleted
71 214 : if (myEdgeCont.wasRemoved(from) || myEdgeCont.wasRemoved(to)) {
72 : return;
73 : }
74 27 : NBEdge* fromEdge = myEdgeCont.retrieve(from);
75 27 : NBEdge* toEdge = myEdgeCont.retrieve(to);
76 27 : if (fromEdge == nullptr) {
77 0 : myErrorMsgHandler->informf("The connection-source edge '%' to reset is not known.", from);
78 0 : return;
79 : }
80 27 : if (toEdge == nullptr) {
81 0 : myErrorMsgHandler->informf("The connection-destination edge '%' to reset is not known.", to);
82 0 : return;
83 : }
84 27 : if (!fromEdge->isConnectedTo(toEdge) && fromEdge->getStep() >= NBEdge::EdgeBuildingStep::EDGE2EDGES) {
85 8 : WRITE_WARNINGF(TL("Target edge '%' is not connected with '%'; the connection cannot be reset."), toEdge->getID(), fromEdge->getID());
86 2 : return;
87 : }
88 25 : int fromLane = -1; // Assume all lanes are to be reset.
89 25 : int toLane = -1;
90 25 : if (attrs.hasAttribute(SUMO_ATTR_LANE)
91 25 : || attrs.hasAttribute(SUMO_ATTR_FROM_LANE)
92 32 : || attrs.hasAttribute(SUMO_ATTR_TO_LANE)) {
93 18 : if (!parseLaneInfo(attrs, fromEdge, toEdge, &fromLane, &toLane)) {
94 : return;
95 : }
96 : // we could be trying to reset a connection loaded from a sumo net and which has become obsolete.
97 : // In this case it's ok to encounter invalid lance indices
98 18 : if (!fromEdge->hasConnectionTo(toEdge, toLane) && fromEdge->getStep() >= NBEdge::EdgeBuildingStep::LANES2EDGES) {
99 0 : WRITE_WARNINGF(TL("Edge '%' has no connection to lane '%'; the connection cannot be reset."), fromEdge->getID(), toEdge->getLaneID(toLane));
100 : }
101 : }
102 25 : fromEdge->removeFromConnections(toEdge, fromLane, toLane, true);
103 : }
104 :
105 2397 : if (element == SUMO_TAG_CONNECTION) {
106 1838 : bool ok = true;
107 1838 : std::string from = attrs.get<std::string>(SUMO_ATTR_FROM, "connection", ok);
108 1838 : std::string to = attrs.getOpt<std::string>(SUMO_ATTR_TO, "connection", ok, "");
109 9191 : if (!ok || myEdgeCont.wasIgnored(from) || myEdgeCont.wasIgnored(to)) {
110 : return;
111 : }
112 : // extract edges
113 1837 : NBEdge* fromEdge = myEdgeCont.retrieve(from);
114 1837 : NBEdge* toEdge = to.length() != 0 ? myEdgeCont.retrieve(to) : nullptr;
115 : // check whether they are valid
116 1837 : if (fromEdge == nullptr) {
117 0 : myErrorMsgHandler->inform("The connection-source edge '" + from + "' is not known.");
118 0 : return;
119 : }
120 1837 : if (toEdge == nullptr && to.length() != 0) {
121 0 : myErrorMsgHandler->inform("The connection-destination edge '" + to + "' is not known.");
122 0 : return;
123 : }
124 : // parse optional lane information
125 1837 : if (attrs.hasAttribute(SUMO_ATTR_LANE) || attrs.hasAttribute(SUMO_ATTR_FROM_LANE) || attrs.hasAttribute(SUMO_ATTR_TO_LANE)) {
126 1589 : parseLaneBound(attrs, fromEdge, toEdge);
127 : } else {
128 248 : fromEdge->addEdge2EdgeConnection(toEdge);
129 248 : fromEdge->getToNode()->invalidateTLS(myTLLogicCont, true, false);
130 248 : if (attrs.hasAttribute(SUMO_ATTR_PASS)
131 247 : || attrs.hasAttribute(SUMO_ATTR_KEEP_CLEAR)
132 247 : || attrs.hasAttribute(SUMO_ATTR_CONTPOS)
133 247 : || attrs.hasAttribute(SUMO_ATTR_VISIBILITY_DISTANCE)
134 247 : || attrs.hasAttribute(SUMO_ATTR_SPEED)
135 246 : || attrs.hasAttribute(SUMO_ATTR_LENGTH)
136 246 : || attrs.hasAttribute(SUMO_ATTR_UNCONTROLLED)
137 246 : || attrs.hasAttribute(SUMO_ATTR_SHAPE)
138 246 : || attrs.hasAttribute(SUMO_ATTR_ALLOW)
139 494 : || attrs.hasAttribute(SUMO_ATTR_DISALLOW)) {
140 4 : WRITE_ERROR("No additional connection attributes are permitted in connection from edge '" + fromEdge->getID() + "' unless '"
141 : + toString(SUMO_ATTR_FROM_LANE) + "' and '" + toString(SUMO_ATTR_TO_LANE) + "' are set.");
142 : }
143 : }
144 : }
145 2396 : if (element == SUMO_TAG_PROHIBITION) {
146 14 : bool ok = true;
147 14 : std::string prohibitor = attrs.getOpt<std::string>(SUMO_ATTR_PROHIBITOR, nullptr, ok, "");
148 14 : std::string prohibited = attrs.getOpt<std::string>(SUMO_ATTR_PROHIBITED, nullptr, ok, "");
149 14 : if (!ok) {
150 : return;
151 : }
152 14 : NBConnection prohibitorC = parseConnection("prohibitor", prohibitor);
153 14 : NBConnection prohibitedC = parseConnection("prohibited", prohibited);
154 14 : if (prohibitorC == NBConnection::InvalidConnection || prohibitedC == NBConnection::InvalidConnection) {
155 : // something failed
156 : return;
157 : }
158 14 : NBNode* n = prohibitorC.getFrom()->getToNode();
159 14 : n->addSortedLinkFoes(prohibitorC, prohibitedC);
160 14 : }
161 2396 : if (element == SUMO_TAG_CROSSING) {
162 276 : addCrossing(attrs);
163 : }
164 2396 : if (element == SUMO_TAG_WALKINGAREA) {
165 8 : addWalkingArea(attrs);
166 : }
167 : }
168 :
169 :
170 : NBConnection
171 28 : NIXMLConnectionsHandler::parseConnection(const std::string& defRole, const std::string& def) {
172 : // split from/to
173 : const std::string::size_type div = def.find("->");
174 28 : if (div == std::string::npos) {
175 0 : myErrorMsgHandler->inform("Missing connection divider in " + defRole + " '" + def + "'");
176 0 : return NBConnection::InvalidConnection;
177 : }
178 28 : std::string fromDef = def.substr(0, div);
179 28 : std::string toDef = def.substr(div + 2);
180 :
181 : // retrieve them now
182 28 : NBEdge* fromE = myEdgeCont.retrieve(fromDef);
183 28 : NBEdge* toE = myEdgeCont.retrieve(toDef);
184 : // check
185 28 : if (fromE == nullptr) {
186 0 : myErrorMsgHandler->inform("Could not find edge '" + fromDef + "' in " + defRole + " '" + def + "'");
187 0 : return NBConnection::InvalidConnection;
188 : }
189 28 : if (toE == nullptr) {
190 0 : myErrorMsgHandler->inform("Could not find edge '" + toDef + "' in " + defRole + " '" + def + "'");
191 0 : return NBConnection::InvalidConnection;
192 : }
193 28 : return NBConnection(fromE, toE);
194 : }
195 :
196 :
197 : void
198 1589 : NIXMLConnectionsHandler::parseLaneBound(const SUMOSAXAttributes& attrs, NBEdge* from, NBEdge* to) {
199 1589 : if (to == nullptr) {
200 : // do nothing if it's a dead end
201 0 : return;
202 : }
203 1589 : bool ok = true;
204 : // get the begin and the end lane
205 : int fromLane;
206 : int toLane;
207 : try {
208 1589 : if (!parseLaneInfo(attrs, from, to, &fromLane, &toLane)) {
209 0 : return;
210 : }
211 1589 : if (fromLane < 0) {
212 0 : myErrorMsgHandler->informf("Invalid value '%' for " + toString(SUMO_ATTR_FROM_LANE) +
213 0 : " in connection from '%' to '%'.", fromLane, from->getID(), to->getID());
214 0 : return;
215 : }
216 1589 : if (toLane < 0) {
217 0 : myErrorMsgHandler->informf("Invalid value '%' for " + toString(SUMO_ATTR_TO_LANE) +
218 0 : " in connection from '%' to '%'.", toLane, from->getID(), to->getID());
219 0 : return;
220 : }
221 :
222 1589 : NBEdge::Connection defaultCon(fromLane, to, toLane);
223 1589 : if (from->getStep() == NBEdge::EdgeBuildingStep::LANES2LANES_USER) {
224 : // maybe we are patching an existing connection
225 1028 : std::vector<NBEdge::Connection> existing = from->getConnectionsFromLane(fromLane, to, toLane);
226 1028 : if (existing.size() > 0) {
227 : assert(existing.size() == 1);
228 10 : defaultCon = existing.front();
229 : // remove the original so we can insert the replacement
230 10 : from->removeFromConnections(defaultCon);
231 : } else {
232 1018 : from->getToNode()->invalidateTLS(myTLLogicCont, true, false);
233 : }
234 1028 : }
235 1589 : const bool mayDefinitelyPass = attrs.getOpt<bool>(SUMO_ATTR_PASS, nullptr, ok, defaultCon.mayDefinitelyPass);
236 1589 : KeepClear keepClear = defaultCon.keepClear;
237 1589 : if (attrs.hasAttribute(SUMO_ATTR_KEEP_CLEAR)) {
238 7 : keepClear = attrs.get<bool>(SUMO_ATTR_KEEP_CLEAR, nullptr, ok) ? KEEPCLEAR_TRUE : KEEPCLEAR_FALSE;
239 : }
240 1589 : const double contPos = attrs.getOpt<double>(SUMO_ATTR_CONTPOS, nullptr, ok, defaultCon.contPos);
241 1589 : const double visibility = attrs.getOpt<double>(SUMO_ATTR_VISIBILITY_DISTANCE, nullptr, ok, defaultCon.visibility);
242 1589 : const double speed = attrs.getOpt<double>(SUMO_ATTR_SPEED, nullptr, ok, defaultCon.speed);
243 1589 : const double friction = attrs.getOpt<double>(SUMO_ATTR_FRICTION, nullptr, ok, defaultCon.friction);
244 1589 : const double length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, nullptr, ok, defaultCon.customLength);
245 1589 : const bool uncontrolled = attrs.getOpt<bool>(SUMO_ATTR_UNCONTROLLED, nullptr, ok, defaultCon.uncontrolled);
246 1589 : const bool indirectLeft = attrs.getOpt<bool>(SUMO_ATTR_INDIRECT, nullptr, ok, false);
247 3178 : const std::string edgeType = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, nullptr, ok, "");
248 1589 : PositionVector customShape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, nullptr, ok, defaultCon.customShape);
249 1589 : std::string allow = attrs.getOpt<std::string>(SUMO_ATTR_ALLOW, nullptr, ok, "");
250 3178 : std::string disallow = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, nullptr, ok, "");
251 : SVCPermissions permissions;
252 3176 : if (allow == "" && disallow == "") {
253 1586 : permissions = SVC_UNSPECIFIED;
254 : } else {
255 6 : permissions = parseVehicleClasses(attrs.getOpt<std::string>(SUMO_ATTR_ALLOW, nullptr, ok, ""), attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, nullptr, ok, ""));
256 : }
257 1589 : SVCPermissions changeLeft = SVC_UNSPECIFIED;
258 : SVCPermissions changeRight = SVC_UNSPECIFIED;
259 1589 : if (attrs.hasAttribute(SUMO_ATTR_CHANGE_LEFT)) {
260 4 : changeLeft = parseVehicleClasses(attrs.get<std::string>(SUMO_ATTR_CHANGE_LEFT, nullptr, ok), "");
261 : }
262 1589 : if (attrs.hasAttribute(SUMO_ATTR_CHANGE_RIGHT)) {
263 4 : changeRight = parseVehicleClasses(attrs.get<std::string>(SUMO_ATTR_CHANGE_RIGHT, nullptr, ok), "");
264 : }
265 :
266 1589 : if (attrs.hasAttribute(SUMO_ATTR_SHAPE) && !NBNetBuilder::transformCoordinates(customShape)) {
267 0 : WRITE_ERRORF(TL("Unable to project shape for connection from edge '%' to edge '%'."), from->getID(), to->getID());
268 : }
269 1589 : if (!ok) {
270 : return;
271 : }
272 1589 : if (!from->addLane2LaneConnection(fromLane, to, toLane, NBEdge::Lane2LaneInfoType::USER, true, mayDefinitelyPass,
273 : keepClear, contPos, visibility, speed, friction, length, customShape, uncontrolled, permissions, indirectLeft, edgeType, changeLeft, changeRight)) {
274 36 : if (OptionsCont::getOptions().getBool("show-errors.connections-first-try")) {
275 6 : WRITE_WARNINGF(TL("Could not set loaded connection from lane '%' to lane '%'."), from->getLaneID(fromLane), to->getLaneID(toLane));
276 : }
277 : // set as to be re-applied after network processing
278 18 : myEdgeCont.addPostProcessConnection(from->getID(), fromLane, to->getID(), toLane, mayDefinitelyPass, keepClear, contPos, visibility,
279 : speed, friction, length, customShape, uncontrolled, false, permissions, indirectLeft, edgeType, changeLeft, changeRight);
280 : }
281 3178 : } catch (NumberFormatException&) {
282 0 : myErrorMsgHandler->inform("At least one of the defined lanes was not numeric");
283 0 : }
284 : }
285 :
286 : bool
287 1607 : NIXMLConnectionsHandler::parseLaneInfo(const SUMOSAXAttributes& attributes, NBEdge* fromEdge, NBEdge* toEdge,
288 : int* fromLane, int* toLane) {
289 1607 : if (attributes.hasAttribute(SUMO_ATTR_LANE)) {
290 0 : return parseDeprecatedLaneDefinition(attributes, fromEdge, toEdge, fromLane, toLane);
291 : } else {
292 1607 : return parseLaneDefinition(attributes, fromLane, toLane);
293 : }
294 : }
295 :
296 :
297 : inline bool
298 0 : NIXMLConnectionsHandler::parseDeprecatedLaneDefinition(const SUMOSAXAttributes& attributes,
299 : NBEdge* from, NBEdge* to,
300 : int* fromLane, int* toLane) {
301 0 : bool ok = true;
302 0 : if (!myHaveWarnedAboutDeprecatedLanes) {
303 0 : myHaveWarnedAboutDeprecatedLanes = true;
304 0 : WRITE_WARNING("'" + toString(SUMO_ATTR_LANE) + "' is deprecated, please use '" +
305 : toString(SUMO_ATTR_FROM_LANE) + "' and '" + toString(SUMO_ATTR_TO_LANE) +
306 : "' instead.");
307 : }
308 :
309 0 : std::string laneConn = attributes.get<std::string>(SUMO_ATTR_LANE, nullptr, ok);
310 0 : StringTokenizer st(laneConn, ':');
311 0 : if (!ok || st.size() != 2) {
312 0 : myErrorMsgHandler->inform("Invalid lane to lane connection from '" +
313 0 : from->getID() + "' to '" + to->getID() + "'.");
314 0 : return false; // There was an error.
315 : }
316 :
317 0 : *fromLane = StringUtils::toIntSecure(st.next(), -1);
318 0 : *toLane = StringUtils::toIntSecure(st.next(), -1);
319 :
320 0 : return true; // We succeeded.
321 0 : }
322 :
323 :
324 : inline bool
325 1607 : NIXMLConnectionsHandler::parseLaneDefinition(const SUMOSAXAttributes& attributes,
326 : int* fromLane,
327 : int* toLane) {
328 1607 : bool ok = true;
329 1607 : *fromLane = attributes.get<int>(SUMO_ATTR_FROM_LANE, nullptr, ok);
330 1607 : *toLane = attributes.get<int>(SUMO_ATTR_TO_LANE, nullptr, ok);
331 1607 : return ok;
332 : }
333 :
334 :
335 : void
336 276 : NIXMLConnectionsHandler::addCrossing(const SUMOSAXAttributes& attrs) {
337 276 : bool ok = true;
338 : EdgeVector edges;
339 276 : const std::string nodeID = attrs.get<std::string>(SUMO_ATTR_NODE, nullptr, ok);
340 276 : double width = attrs.getOpt<double>(SUMO_ATTR_WIDTH, nodeID.c_str(), ok, NBEdge::UNSPECIFIED_WIDTH, true);
341 276 : const bool discard = attrs.getOpt<bool>(SUMO_ATTR_DISCARD, nodeID.c_str(), ok, false, true);
342 276 : int tlIndex = attrs.getOpt<int>(SUMO_ATTR_TLLINKINDEX, nullptr, ok, -1);
343 276 : int tlIndex2 = attrs.getOpt<int>(SUMO_ATTR_TLLINKINDEX2, nullptr, ok, -1);
344 276 : NBNode* node = myNodeCont.retrieve(nodeID);
345 276 : if (node == nullptr) {
346 0 : if (!discard && myNodeCont.wasRemoved(nodeID)) {
347 0 : WRITE_ERRORF(TL("Node '%' in crossing is not known."), nodeID);
348 : }
349 0 : return;
350 : }
351 276 : if (!attrs.hasAttribute(SUMO_ATTR_EDGES)) {
352 1 : if (discard) {
353 1 : node->discardAllCrossings(true);
354 : return;
355 : } else {
356 0 : WRITE_ERRORF(TL("No edges specified for crossing at node '%'."), nodeID);
357 0 : return;
358 : }
359 : }
360 766 : for (const std::string& id : attrs.get<std::vector<std::string> >(SUMO_ATTR_EDGES, nodeID.c_str(), ok)) {
361 491 : NBEdge* edge = myEdgeCont.retrieve(id);
362 491 : if (edge == nullptr) {
363 3 : if (!(discard && myEdgeCont.wasRemoved(id))) {
364 0 : WRITE_ERRORF(TL("Edge '%' for crossing at node '%' is not known."), id, nodeID);
365 0 : return;
366 : } else {
367 1 : edge = myEdgeCont.retrieve(id, true);
368 : }
369 : } else {
370 490 : if (edge->getToNode() != node && edge->getFromNode() != node) {
371 0 : if (!discard) {
372 0 : WRITE_ERRORF(TL("Edge '%' does not touch node '%'."), id, nodeID);
373 0 : return;
374 : }
375 : }
376 : }
377 491 : edges.push_back(edge);
378 275 : }
379 275 : if (!ok) {
380 : return;
381 : }
382 274 : bool priority = attrs.getOpt<bool>(SUMO_ATTR_PRIORITY, nodeID.c_str(), ok, node->isTLControlled(), true);
383 274 : if (node->isTLControlled() && !priority) {
384 : // traffic_light nodes should always have priority crossings
385 6 : WRITE_WARNINGF(TL("Crossing at controlled node '%' must be prioritized"), nodeID);
386 : priority = true;
387 : }
388 274 : PositionVector customShape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, nullptr, ok, PositionVector::EMPTY);
389 274 : if (!NBNetBuilder::transformCoordinates(customShape)) {
390 0 : WRITE_ERRORF(TL("Unable to project shape for crossing at node '%'."), node->getID());
391 : }
392 274 : if (discard) {
393 2 : node->removeCrossing(edges);
394 : } else {
395 544 : if (node->checkCrossingDuplicated(edges)) {
396 : // possibly a diff
397 3 : NBNode::Crossing* existing = node->getCrossing(edges);
398 5 : if (!(
399 6 : (attrs.hasAttribute(SUMO_ATTR_WIDTH) && width != existing->width)
400 2 : || (attrs.hasAttribute(SUMO_ATTR_TLLINKINDEX) && tlIndex != existing->customTLIndex)
401 2 : || (attrs.hasAttribute(SUMO_ATTR_TLLINKINDEX2) && tlIndex2 != existing->customTLIndex2)
402 2 : || (attrs.hasAttribute(SUMO_ATTR_PRIORITY) && priority != existing->priority))) {
403 6 : WRITE_ERRORF(TL("Crossing with edges '%' already exists at node '%'."), toString(edges), node->getID());
404 : return;
405 : } else {
406 : // replace existing, keep old attributes
407 1 : if (!attrs.hasAttribute(SUMO_ATTR_WIDTH)) {
408 0 : width = existing->width;
409 : }
410 1 : if (!attrs.hasAttribute(SUMO_ATTR_TLLINKINDEX)) {
411 1 : tlIndex = existing->customTLIndex;
412 : }
413 1 : if (!attrs.hasAttribute(SUMO_ATTR_TLLINKINDEX2)) {
414 1 : tlIndex2 = existing->customTLIndex2;
415 : }
416 1 : if (!attrs.hasAttribute(SUMO_ATTR_PRIORITY)) {
417 1 : priority = existing->priority;
418 : }
419 1 : node->removeCrossing(edges);
420 : }
421 : }
422 540 : node->addCrossing(edges, width, priority, tlIndex, tlIndex2, customShape);
423 : }
424 274 : }
425 :
426 :
427 : void
428 8 : NIXMLConnectionsHandler::addWalkingArea(const SUMOSAXAttributes& attrs) {
429 8 : bool ok = true;
430 : NBNode* node = nullptr;
431 : EdgeVector edges;
432 8 : const std::string nodeID = attrs.get<std::string>(SUMO_ATTR_NODE, nullptr, ok);
433 : std::vector<std::string> edgeIDs;
434 8 : if (!attrs.hasAttribute(SUMO_ATTR_EDGES)) {
435 0 : WRITE_ERRORF(TL("No edges specified for walkingArea at node '%'."), nodeID);
436 0 : return;
437 : }
438 20 : for (const std::string& id : attrs.get<std::vector<std::string> >(SUMO_ATTR_EDGES, nodeID.c_str(), ok)) {
439 12 : NBEdge* edge = myEdgeCont.retrieve(id);
440 12 : if (edge == nullptr) {
441 0 : WRITE_ERRORF(TL("Edge '%' for walkingArea at node '%' is not known."), id, nodeID);
442 0 : return;
443 : }
444 12 : if (node == nullptr) {
445 8 : if (edge->getToNode()->getID() == nodeID) {
446 : node = edge->getToNode();
447 2 : } else if (edge->getFromNode()->getID() == nodeID) {
448 : node = edge->getFromNode();
449 : } else {
450 0 : WRITE_ERRORF(TL("Edge '%' does not touch node '%'."), id, nodeID);
451 0 : return;
452 : }
453 : } else {
454 4 : if (edge->getToNode() != node && edge->getFromNode() != node) {
455 0 : WRITE_ERRORF(TL("Edge '%' does not touch node '%'."), id, nodeID);
456 0 : return;
457 : }
458 : }
459 12 : edges.push_back(edge);
460 8 : }
461 8 : if (!ok) {
462 : return;
463 : }
464 8 : PositionVector customShape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, nullptr, ok, PositionVector::EMPTY);
465 8 : double customWidth = attrs.getOpt<double>(SUMO_ATTR_WIDTH, nullptr, ok, NBEdge::UNSPECIFIED_WIDTH);
466 8 : if (!NBNetBuilder::transformCoordinates(customShape)) {
467 0 : WRITE_ERRORF(TL("Unable to project shape for walkingArea at node '%'."), node->getID());
468 : }
469 8 : node->addWalkingAreaShape(edges, customShape, customWidth);
470 8 : }
471 :
472 :
473 : /****************************************************************************/
|