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