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 NIImporter_SUMO.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Michael Behrisch
18 : /// @author Leonhard Luecken
19 : /// @date Mon, 14.04.2008
20 : ///
21 : // Importer for networks stored in SUMO format
22 : /****************************************************************************/
23 : #include <config.h>
24 : #include <string>
25 : #include <utils/common/UtilExceptions.h>
26 : #include <utils/common/StringUtils.h>
27 : #include <utils/common/MsgHandler.h>
28 : #include <utils/common/StringTokenizer.h>
29 : #include <utils/common/FileHelpers.h>
30 : #include <utils/common/ToString.h>
31 : #include <utils/common/StringUtils.h>
32 : #include <utils/xml/SUMOXMLDefinitions.h>
33 : #include <utils/xml/SUMOSAXHandler.h>
34 : #include <utils/xml/XMLSubSys.h>
35 : #include <utils/geom/GeoConvHelper.h>
36 : #include <utils/geom/GeomConvHelper.h>
37 : #include <utils/options/OptionsCont.h>
38 : #include <netbuild/NBEdge.h>
39 : #include <netbuild/NBEdgeCont.h>
40 : #include <netbuild/NBNode.h>
41 : #include <netbuild/NBNodeCont.h>
42 : #include <netbuild/NBAlgorithms_Ramps.h>
43 : #include <netbuild/NBNetBuilder.h>
44 : #include "NILoader.h"
45 : #include "NIXMLTypesHandler.h"
46 : #include "NIImporter_SUMO.h"
47 :
48 :
49 : // ===========================================================================
50 : // method definitions
51 : // ===========================================================================
52 : // ---------------------------------------------------------------------------
53 : // static methods (interface in this case)
54 : // ---------------------------------------------------------------------------
55 : void
56 2003 : NIImporter_SUMO::loadNetwork(OptionsCont& oc, NBNetBuilder& nb) {
57 2003 : NIImporter_SUMO importer(nb);
58 2003 : importer._loadNetwork(oc);
59 2003 : }
60 :
61 :
62 : // ---------------------------------------------------------------------------
63 : // loader methods
64 : // ---------------------------------------------------------------------------
65 2003 : NIImporter_SUMO::NIImporter_SUMO(NBNetBuilder& nb)
66 : : SUMOSAXHandler("sumo-network"),
67 2003 : myNetBuilder(nb),
68 2003 : myNodeCont(nb.getNodeCont()),
69 2003 : myTLLCont(nb.getTLLogicCont()),
70 2003 : myTypesHandler(nb.getTypeCont()),
71 2003 : myCurrentEdge(nullptr),
72 2003 : myCurrentLane(nullptr),
73 2003 : myCurrentTL(nullptr),
74 2003 : myLocation(nullptr),
75 : myNetworkVersion(0, 0),
76 2003 : myHaveSeenInternalEdge(false),
77 2003 : myAmLefthand(false),
78 2003 : myChangeLefthand(false),
79 2003 : myCornerDetail(0),
80 2003 : myLinkDetail(-1),
81 2003 : myRectLaneCut(false),
82 2003 : myWalkingAreas(false),
83 2003 : myLimitTurnSpeed(-1),
84 2003 : myCheckLaneFoesAll(false),
85 2003 : myCheckLaneFoesRoundabout(true),
86 2003 : myTlsIgnoreInternalJunctionJam(false),
87 2003 : myDefaultSpreadType(toString(LaneSpreadFunction::RIGHT)),
88 2003 : myGeomAvoidOverlap(false),
89 2003 : myJunctionsHigherSpeed(false),
90 2003 : myInternalJunctionsVehicleWidth(OptionsCont::getOptions().getFloat("internal-junctions.vehicle-width")),
91 2003 : myJunctionsMinimalShape(OptionsCont::getOptions().getBool("junctions.minimal-shape")),
92 6009 : myJunctionsEndpointShape(OptionsCont::getOptions().getBool("junctions.endpoint-shape")) {
93 2003 : }
94 :
95 :
96 2003 : NIImporter_SUMO::~NIImporter_SUMO() {
97 66124 : for (std::map<std::string, EdgeAttrs*>::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
98 64121 : EdgeAttrs* ed = (*i).second;
99 142481 : for (std::vector<LaneAttrs*>::const_iterator j = ed->lanes.begin(); j != ed->lanes.end(); ++j) {
100 78360 : delete *j;
101 : }
102 : delete ed;
103 : }
104 2003 : delete myLocation;
105 6009 : }
106 :
107 :
108 : void
109 2003 : NIImporter_SUMO::_loadNetwork(OptionsCont& oc) {
110 : // check whether the option is set (properly)
111 4006 : if (!oc.isUsableFileList("sumo-net-file")) {
112 1320 : return;
113 : }
114 1366 : const std::vector<std::string> discardableParams = oc.getStringVector("discard-params");
115 : myDiscardableParams.insert(discardableParams.begin(), discardableParams.end());
116 : // parse file(s)
117 1366 : const std::vector<std::string> files = oc.getStringVector("sumo-net-file");
118 1376 : for (std::vector<std::string>::const_iterator file = files.begin(); file != files.end(); ++file) {
119 1386 : if (!FileHelpers::isReadable(*file)) {
120 0 : WRITE_ERRORF(TL("Could not open sumo-net-file '%'."), *file);
121 : return;
122 : }
123 693 : setFileName(*file);
124 2079 : const long before = PROGRESS_BEGIN_TIME_MESSAGE("Parsing sumo-net from '" + *file + "'");
125 693 : XMLSubSys::runParser(*this, *file, true);
126 693 : PROGRESS_TIME_MESSAGE(before);
127 : }
128 : // build edges
129 1366 : const double maxSegmentLength = oc.getFloat("geometry.max-segment-length");
130 64804 : for (std::map<std::string, EdgeAttrs*>::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
131 64121 : EdgeAttrs* ed = (*i).second;
132 : // skip internal edges
133 64121 : if (ed->func == SumoXMLEdgeFunc::INTERNAL || ed->func == SumoXMLEdgeFunc::CROSSING || ed->func == SumoXMLEdgeFunc::WALKINGAREA) {
134 29859 : continue;
135 : }
136 : // get and check the nodes
137 34262 : NBNode* from = myNodeCont.retrieve(ed->fromNode);
138 34262 : NBNode* to = myNodeCont.retrieve(ed->toNode);
139 34262 : if (from == nullptr) {
140 228 : WRITE_ERRORF(TL("Edge's '%' from-node '%' is not known."), ed->id, ed->fromNode);
141 76 : continue;
142 : }
143 34186 : if (to == nullptr) {
144 114 : WRITE_ERRORF(TL("Edge's '%' to-node '%' is not known."), ed->id, ed->toNode);
145 38 : continue;
146 : }
147 34148 : if (from == to) {
148 0 : WRITE_ERRORF(TL("Edge's '%' from-node and to-node '%' are identical."), ed->id, ed->toNode);
149 0 : continue;
150 : }
151 34148 : if (ed->shape.size() == 0 && maxSegmentLength > 0) {
152 13 : ed->shape.push_back(from->getPosition());
153 13 : ed->shape.push_back(to->getPosition());
154 : // shape is already cartesian but we must use a copy because the original will be modified
155 13 : NBNetBuilder::addGeometrySegments(ed->shape, PositionVector(ed->shape), maxSegmentLength);
156 : }
157 : // build and insert the edge
158 34148 : NBEdge* e = new NBEdge(ed->id, from, to,
159 : ed->type, ed->maxSpeed, NBEdge::UNSPECIFIED_FRICTION,
160 : (int) ed->lanes.size(),
161 : ed->priority, NBEdge::UNSPECIFIED_WIDTH, NBEdge::UNSPECIFIED_OFFSET,
162 102444 : ed->shape, ed->lsf, ed->streetName, "", true); // always use tryIgnoreNodePositions to keep original shape
163 34148 : e->setLoadedLength(ed->length);
164 34148 : e->updateParameters(ed->getParametersMap());
165 34148 : e->setDistance(ed->distance);
166 34148 : if (!myNetBuilder.getEdgeCont().insert(e)) {
167 0 : WRITE_ERRORF(TL("Could not insert edge '%'."), ed->id);
168 0 : delete e;
169 0 : continue;
170 : }
171 34148 : ed->builtEdge = myNetBuilder.getEdgeCont().retrieve(ed->id);
172 34148 : if (ed->builtEdge != nullptr) {
173 33978 : ed->builtEdge->setEdgeStopOffset(-1, ed->edgeStopOffset);
174 33978 : ed->builtEdge->setBidi(ed->bidi != "");
175 33978 : ed->builtEdge->setRoutingType(ed->routingType);
176 : }
177 : }
178 : // assign further lane attributes (edges are built)
179 : EdgeVector toRemove;
180 1366 : const bool dismissVclasses = oc.getBool("dismiss-vclasses");
181 64804 : for (std::map<std::string, EdgeAttrs*>::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
182 64121 : EdgeAttrs* ed = (*i).second;
183 64121 : NBEdge* nbe = ed->builtEdge;
184 64121 : if (nbe == nullptr) { // inner edge or removed by explicit list, vclass, ...
185 30143 : continue;
186 : }
187 : const SumoXMLNodeType toType = nbe->getToNode()->getType();
188 78333 : for (int fromLaneIndex = 0; fromLaneIndex < (int) ed->lanes.size(); ++fromLaneIndex) {
189 44355 : LaneAttrs* lane = ed->lanes[fromLaneIndex];
190 : // connections
191 : const std::vector<Connection>& connections = lane->connections;
192 111092 : for (const Connection& c : connections) {
193 66737 : if (myEdges.count(c.toEdgeID) == 0) {
194 66 : WRITE_ERRORF(TL("Unknown edge '%' given in connection."), c.toEdgeID);
195 22 : continue;
196 : }
197 66715 : NBEdge* toEdge = myEdges[c.toEdgeID]->builtEdge;
198 66715 : if (toEdge == nullptr) { // removed by explicit list, vclass, ...
199 420 : continue;
200 : }
201 66295 : if (toEdge->getFromNode() != nbe->getToNode()) { // inconsistency may occur when merging networks
202 84 : WRITE_WARNINGF("Removing invalid connection from edge '%' to edge '%'", nbe->getID(), toEdge->getID());
203 21 : continue;
204 : }
205 : // patch attribute uncontrolled for legacy networks where it is not set explicitly
206 66274 : bool uncontrolled = c.uncontrolled;
207 :
208 125417 : if ((NBNode::isTrafficLight(toType) || toType == SumoXMLNodeType::RAIL_SIGNAL)
209 67197 : && c.tlLinkIndex == NBConnection::InvalidTlIndex) {
210 : uncontrolled = true;
211 : }
212 132548 : nbe->addLane2LaneConnection(
213 66274 : fromLaneIndex, toEdge, c.toLaneIdx, NBEdge::Lane2LaneInfoType::VALIDATED,
214 66274 : true, c.mayDefinitelyPass, c.keepClear ? KEEPCLEAR_TRUE : KEEPCLEAR_FALSE,
215 66274 : c.contPos, c.visibility, c.speed, c.friction, c.customLength, c.customShape, uncontrolled, c.permissions, c.indirectLeft, c.edgeType, c.changeLeft, c.changeRight);
216 66274 : if (c.getParametersMap().size() > 0) {
217 0 : nbe->getConnectionRef(fromLaneIndex, toEdge, c.toLaneIdx).updateParameters(c.getParametersMap());
218 : }
219 : // maybe we have a tls-controlled connection
220 66274 : if (c.tlID != "" && myRailSignals.count(c.tlID) == 0) {
221 7116 : const std::map<std::string, NBTrafficLightDefinition*>& programs = myTLLCont.getPrograms(c.tlID);
222 7116 : if (programs.size() > 0) {
223 : std::map<std::string, NBTrafficLightDefinition*>::const_iterator it;
224 14250 : for (it = programs.begin(); it != programs.end(); it++) {
225 7134 : NBLoadedSUMOTLDef* tlDef = dynamic_cast<NBLoadedSUMOTLDef*>(it->second);
226 7134 : if (tlDef) {
227 7134 : tlDef->addConnection(nbe, toEdge, fromLaneIndex, c.toLaneIdx, c.tlLinkIndex, c.tlLinkIndex2, false);
228 : } else {
229 0 : throw ProcessError("Corrupt traffic light definition '" + c.tlID + "' (program '" + it->first + "')");
230 : }
231 : }
232 : } else {
233 0 : WRITE_ERRORF(TL("The traffic light '%' is not known."), c.tlID);
234 : }
235 : }
236 : }
237 : // allow/disallow XXX preferred
238 44355 : if (!dismissVclasses) {
239 44349 : nbe->setPermissions(parseVehicleClasses(lane->allow, lane->disallow, myNetworkVersion), fromLaneIndex);
240 : }
241 88710 : nbe->setPermittedChanging(fromLaneIndex, parseVehicleClasses(lane->changeLeft, ""), parseVehicleClasses(lane->changeRight, ""));
242 : // width, offset
243 44355 : nbe->setLaneWidth(fromLaneIndex, lane->width);
244 44355 : nbe->setEndOffset(fromLaneIndex, lane->endOffset);
245 44355 : nbe->setSpeed(fromLaneIndex, lane->maxSpeed);
246 44355 : nbe->setFriction(fromLaneIndex, lane->friction);
247 44355 : nbe->setAcceleration(fromLaneIndex, lane->accelRamp);
248 44355 : nbe->getLaneStruct(fromLaneIndex).oppositeID = lane->oppositeID;
249 44355 : nbe->getLaneStruct(fromLaneIndex).type = lane->type;
250 44355 : nbe->getLaneStruct(fromLaneIndex).updateParameters(lane->getParametersMap());
251 44355 : if (lane->customShape) {
252 5 : nbe->setLaneShape(fromLaneIndex, lane->shape);
253 : }
254 : // stop offset for lane
255 : bool stopOffsetSet = false;
256 44355 : if (lane->laneStopOffset.isDefined() || nbe->getEdgeStopOffset().isDefined()) {
257 : // apply lane-specific stopOffset (might be none as well)
258 26 : stopOffsetSet = nbe->setEdgeStopOffset(fromLaneIndex, lane->laneStopOffset);
259 : }
260 26 : if (!stopOffsetSet) {
261 : // apply default stop offset to lane
262 44329 : nbe->setEdgeStopOffset(fromLaneIndex, nbe->getEdgeStopOffset());
263 : }
264 : }
265 33978 : nbe->declareConnectionsAsLoaded();
266 33978 : if (!nbe->hasLaneSpecificWidth() && nbe->getLanes()[0].width != NBEdge::UNSPECIFIED_WIDTH) {
267 404 : nbe->setLaneWidth(-1, nbe->getLaneWidth(0));
268 : }
269 33978 : if (!nbe->hasLaneSpecificEndOffset() && nbe->getEndOffset(0) != NBEdge::UNSPECIFIED_OFFSET) {
270 0 : nbe->setEndOffset(-1, nbe->getEndOffset(0));
271 : }
272 33978 : if (!nbe->hasLaneSpecificStopOffsets() && nbe->getEdgeStopOffset().isDefined()) {
273 4 : nbe->setEdgeStopOffset(-1, nbe->getEdgeStopOffset());
274 : }
275 : // check again after permissions are set
276 33978 : if (myNetBuilder.getEdgeCont().ignoreFilterMatch(nbe)) {
277 12 : myNetBuilder.getEdgeCont().ignore(nbe->getID());
278 12 : toRemove.push_back(nbe);
279 : }
280 : }
281 695 : for (EdgeVector::iterator i = toRemove.begin(); i != toRemove.end(); ++i) {
282 12 : myNetBuilder.getEdgeCont().erase(myNetBuilder.getDistrictCont(), *i);
283 : }
284 : // insert loaded prohibitions
285 703 : for (std::vector<Prohibition>::const_iterator it = myProhibitions.begin(); it != myProhibitions.end(); it++) {
286 20 : NBEdge* prohibitedFrom = myEdges[it->prohibitedFrom]->builtEdge;
287 20 : NBEdge* prohibitedTo = myEdges[it->prohibitedTo]->builtEdge;
288 20 : NBEdge* prohibitorFrom = myEdges[it->prohibitorFrom]->builtEdge;
289 20 : NBEdge* prohibitorTo = myEdges[it->prohibitorTo]->builtEdge;
290 20 : if (prohibitedFrom == nullptr) {
291 0 : WRITE_WARNINGF(TL("Edge '%' in prohibition was not built."), it->prohibitedFrom);
292 20 : } else if (prohibitedTo == nullptr) {
293 0 : WRITE_WARNINGF(TL("Edge '%' in prohibition was not built."), it->prohibitedTo);
294 20 : } else if (prohibitorFrom == nullptr) {
295 9 : WRITE_WARNINGF(TL("Edge '%' in prohibition was not built."), it->prohibitorFrom);
296 17 : } else if (prohibitorTo == nullptr) {
297 0 : WRITE_WARNINGF(TL("Edge '%' in prohibition was not built."), it->prohibitorTo);
298 : } else {
299 : NBNode* n = prohibitedFrom->getToNode();
300 17 : n->addSortedLinkFoes(
301 34 : NBConnection(prohibitorFrom, prohibitorTo),
302 34 : NBConnection(prohibitedFrom, prohibitedTo));
303 : }
304 : }
305 1102 : if (!myHaveSeenInternalEdge && oc.isWriteable("no-internal-links")) {
306 764 : oc.set("no-internal-links", "true");
307 : }
308 1366 : if (oc.isWriteable("lefthand")) {
309 1360 : oc.set("lefthand", toString(myAmLefthand));
310 : }
311 1366 : if (oc.isWriteable("junctions.corner-detail")) {
312 1290 : oc.set("junctions.corner-detail", toString(myCornerDetail));
313 : }
314 1366 : if (oc.isWriteable("junctions.internal-link-detail") && myLinkDetail > 0) {
315 0 : oc.set("junctions.internal-link-detail", toString(myLinkDetail));
316 : }
317 1366 : if (oc.isWriteable("rectangular-lane-cut")) {
318 1334 : oc.set("rectangular-lane-cut", toString(myRectLaneCut));
319 : }
320 1366 : if (oc.isWriteable("walkingareas")) {
321 1366 : oc.set("walkingareas", toString(myWalkingAreas));
322 : }
323 1366 : if (oc.isWriteable("junctions.limit-turn-speed")) {
324 1364 : oc.set("junctions.limit-turn-speed", toString(myLimitTurnSpeed));
325 : }
326 2048 : if (oc.isWriteable("check-lane-foes.all") && oc.getBool("check-lane-foes.all") != myCheckLaneFoesAll) {
327 2 : oc.set("check-lane-foes.all", toString(myCheckLaneFoesAll));
328 : }
329 2049 : if (oc.isWriteable("check-lane-foes.roundabout") && oc.getBool("check-lane-foes.roundabout") != myCheckLaneFoesRoundabout) {
330 0 : oc.set("check-lane-foes.roundabout", toString(myCheckLaneFoesRoundabout));
331 : }
332 2048 : if (oc.isWriteable("tls.ignore-internal-junction-jam") && oc.getBool("tls.ignore-internal-junction-jam") != myTlsIgnoreInternalJunctionJam) {
333 2 : oc.set("tls.ignore-internal-junction-jam", toString(myTlsIgnoreInternalJunctionJam));
334 : }
335 2648 : if (oc.isWriteable("default.spreadtype") && oc.getString("default.spreadtype") != myDefaultSpreadType) {
336 0 : oc.set("default.spreadtype", myDefaultSpreadType);
337 : }
338 683 : if (oc.isWriteable("geometry.avoid-overlap") && oc.getBool("geometry.avoid-overlap") != myGeomAvoidOverlap) {
339 0 : oc.set("geometry.avoid-overlap", toString(myGeomAvoidOverlap));
340 : }
341 2049 : if (oc.isWriteable("junctions.higher-speed") && oc.getBool("junctions.higher-speed") != myJunctionsHigherSpeed) {
342 0 : oc.set("junctions.higher-speed", toString(myJunctionsHigherSpeed));
343 : }
344 2048 : if (oc.isWriteable("internal-junctions.vehicle-width") && oc.getFloat("internal-junctions.vehicle-width") != myInternalJunctionsVehicleWidth) {
345 2 : oc.set("internal-junctions.vehicle-width", toString(myInternalJunctionsVehicleWidth));
346 : }
347 2048 : if (oc.isWriteable("junctions.minimal-shape") && oc.getBool("junctions.minimal-shape") != myJunctionsMinimalShape) {
348 2 : oc.set("junctions.minimal-shape", toString(myJunctionsMinimalShape));
349 : }
350 2048 : if (oc.isWriteable("junctions.endpoint-shape") && oc.getBool("junctions.endpoint-shape") != myJunctionsEndpointShape) {
351 2 : oc.set("junctions.endpoint-shape", toString(myJunctionsEndpointShape));
352 : }
353 683 : if (!deprecatedVehicleClassesSeen.empty()) {
354 2 : WRITE_WARNINGF(TL("Deprecated vehicle class(es) '%' in input network."), toString(deprecatedVehicleClassesSeen));
355 : deprecatedVehicleClassesSeen.clear();
356 : }
357 1366 : if (!oc.getBool("no-internal-links")) {
358 : // add loaded crossings
359 311 : for (const auto& crossIt : myPedestrianCrossings) {
360 59 : NBNode* const node = myNodeCont.retrieve(crossIt.first);
361 204 : for (const Crossing& crossing : crossIt.second) {
362 : EdgeVector edges;
363 372 : for (const std::string& edgeID : crossing.crossingEdges) {
364 227 : NBEdge* edge = myNetBuilder.getEdgeCont().retrieve(edgeID);
365 : // edge might have been removed due to options
366 227 : if (edge != nullptr) {
367 225 : edges.push_back(edge);
368 : }
369 : }
370 145 : if (!edges.empty()) {
371 144 : node->addCrossing(edges, crossing.width, crossing.priority,
372 144 : crossing.customTLIndex, crossing.customTLIndex2, crossing.customShape, true, &crossing);
373 : }
374 145 : }
375 : }
376 252 : myNetBuilder.setHaveNetworkCrossings(myPedestrianCrossings.size() > 0);
377 : // add walking area custom shapes
378 255 : for (const auto& item : myWACustomShapes) {
379 3 : std::string nodeID = SUMOXMLDefinitions::getJunctionIDFromInternalEdge(item.first);
380 3 : NBNode* node = myNodeCont.retrieve(nodeID);
381 : std::vector<std::string> edgeIDs;
382 3 : if (item.second.fromEdges.size() + item.second.toEdges.size() == 0) {
383 : // must be a split crossing
384 : assert(item.second.fromCrossed.size() > 0);
385 : assert(item.second.toCrossed.size() > 0);
386 2 : edgeIDs = item.second.fromCrossed;
387 2 : edgeIDs.insert(edgeIDs.end(), item.second.toCrossed.begin(), item.second.toCrossed.end());
388 1 : } else if (item.second.fromEdges.size() > 0) {
389 1 : edgeIDs = item.second.fromEdges;
390 : } else {
391 0 : edgeIDs = item.second.toEdges;
392 : }
393 : EdgeVector edges;
394 8 : for (const std::string& edgeID : edgeIDs) {
395 5 : NBEdge* edge = myNetBuilder.getEdgeCont().retrieve(edgeID);
396 : // edge might have been removed due to options
397 5 : if (edge != nullptr) {
398 5 : edges.push_back(edge);
399 : }
400 : }
401 3 : if (edges.size() > 0) {
402 3 : node->addWalkingAreaShape(edges, item.second.shape, item.second.width);
403 : }
404 3 : }
405 : }
406 : // add roundabouts
407 743 : for (const std::vector<std::string>& ra : myRoundabouts) {
408 : EdgeSet roundabout;
409 337 : for (const std::string& edgeID : ra) {
410 277 : NBEdge* edge = myNetBuilder.getEdgeCont().retrieve(edgeID);
411 277 : if (edge == nullptr) {
412 18 : if (!myNetBuilder.getEdgeCont().wasIgnored(edgeID)) {
413 0 : WRITE_ERRORF(TL("Unknown edge '%' in roundabout"), (edgeID));
414 : }
415 : } else {
416 : roundabout.insert(edge);
417 : }
418 : }
419 60 : myNetBuilder.getEdgeCont().addRoundabout(roundabout);
420 : }
421 683 : }
422 :
423 :
424 : void
425 361456 : NIImporter_SUMO::myStartElement(int element,
426 : const SUMOSAXAttributes& attrs) {
427 : /* our goal is to reproduce the input net faithfully
428 : * there are different types of objects in the netfile:
429 : * 1) those which must be loaded into NBNetBuilder-Containers for processing
430 : * 2) those which can be ignored because they are recomputed based on group 1
431 : * 3) those which are of no concern to NBNetBuilder but should be exposed to
432 : * netedit. We will probably have to patch NBNetBuilder to contain them
433 : * and hand them over to netedit
434 : * alternative idea: those shouldn't really be contained within the
435 : * network but rather in separate files. teach netedit how to open those
436 : * (POI?)
437 : * 4) those which are of concern neither to NBNetBuilder nor netedit and
438 : * must be copied over - need to patch NBNetBuilder for this.
439 : * copy unknown by default
440 : */
441 361456 : switch (element) {
442 693 : case SUMO_TAG_NET: {
443 : bool ok;
444 693 : myNetworkVersion = StringUtils::toVersion(attrs.get<std::string>(SUMO_ATTR_VERSION, nullptr, ok, false));
445 693 : myAmLefthand = attrs.getOpt<bool>(SUMO_ATTR_LEFTHAND, nullptr, ok, false);
446 693 : myCornerDetail = attrs.getOpt<int>(SUMO_ATTR_CORNERDETAIL, nullptr, ok, 0);
447 693 : myLinkDetail = attrs.getOpt<int>(SUMO_ATTR_LINKDETAIL, nullptr, ok, -1);
448 693 : myRectLaneCut = attrs.getOpt<bool>(SUMO_ATTR_RECTANGULAR_LANE_CUT, nullptr, ok, false);
449 693 : myWalkingAreas = attrs.getOpt<bool>(SUMO_ATTR_WALKINGAREAS, nullptr, ok, false);
450 693 : myLimitTurnSpeed = attrs.getOpt<double>(SUMO_ATTR_LIMIT_TURN_SPEED, nullptr, ok, -1);
451 693 : myCheckLaneFoesAll = attrs.getOpt<bool>(SUMO_ATTR_CHECKLANEFOES_ALL, nullptr, ok, false);
452 693 : myCheckLaneFoesRoundabout = attrs.getOpt<bool>(SUMO_ATTR_CHECKLANEFOES_ROUNDABOUT, nullptr, ok, true);
453 693 : myTlsIgnoreInternalJunctionJam = attrs.getOpt<bool>(SUMO_ATTR_TLS_IGNORE_INTERNAL_JUNCTION_JAM, nullptr, ok, false);
454 693 : myDefaultSpreadType = attrs.getOpt<std::string>(SUMO_ATTR_SPREADTYPE, nullptr, ok, myDefaultSpreadType);
455 693 : myGeomAvoidOverlap = attrs.getOpt<bool>(SUMO_ATTR_AVOID_OVERLAP, nullptr, ok, myGeomAvoidOverlap);
456 693 : myJunctionsHigherSpeed = attrs.getOpt<bool>(SUMO_ATTR_HIGHER_SPEED, nullptr, ok, myJunctionsHigherSpeed);
457 693 : myInternalJunctionsVehicleWidth = attrs.getOpt<double>(SUMO_ATTR_INTERNAL_JUNCTIONS_VEHICLE_WIDTH, nullptr, ok, myInternalJunctionsVehicleWidth);
458 693 : myJunctionsMinimalShape = attrs.getOpt<bool>(SUMO_ATTR_JUNCTIONS_MINIMAL_SHAPE, nullptr, ok, myJunctionsMinimalShape);
459 693 : myJunctionsEndpointShape = attrs.getOpt<bool>(SUMO_ATTR_JUNCTIONS_ENDPOINT_SHAPE, nullptr, ok, myJunctionsEndpointShape);
460 : // derived
461 693 : const OptionsCont& oc = OptionsCont::getOptions();
462 696 : myChangeLefthand = !oc.isDefault("lefthand") && (oc.getBool("lefthand") != myAmLefthand);
463 :
464 : break;
465 : }
466 64232 : case SUMO_TAG_EDGE:
467 64232 : addEdge(attrs);
468 64232 : break;
469 78467 : case SUMO_TAG_LANE:
470 78467 : addLane(attrs);
471 78467 : break;
472 26 : case SUMO_TAG_STOPOFFSET: {
473 26 : bool ok = true;
474 26 : addStopOffsets(attrs, ok);
475 : }
476 26 : break;
477 87 : case SUMO_TAG_NEIGH:
478 87 : myCurrentLane->oppositeID = attrs.getString(SUMO_ATTR_LANE);
479 87 : break;
480 22285 : case SUMO_TAG_JUNCTION:
481 22285 : addJunction(attrs);
482 22284 : break;
483 67015 : case SUMO_TAG_REQUEST:
484 67015 : addRequest(attrs);
485 67015 : break;
486 100978 : case SUMO_TAG_CONNECTION:
487 100978 : addConnection(attrs);
488 100978 : break;
489 745 : case SUMO_TAG_TLLOGIC:
490 745 : myCurrentTL = initTrafficLightLogic(attrs, myCurrentTL);
491 745 : if (myCurrentTL) {
492 745 : myLastParameterised.push_back(myCurrentTL);
493 : }
494 : break;
495 4513 : case SUMO_TAG_PHASE:
496 4513 : addPhase(attrs, myCurrentTL);
497 4513 : break;
498 693 : case SUMO_TAG_LOCATION:
499 693 : delete myLocation;
500 693 : myLocation = loadLocation(attrs);
501 693 : break;
502 20 : case SUMO_TAG_PROHIBITION:
503 20 : addProhibition(attrs);
504 20 : break;
505 60 : case SUMO_TAG_ROUNDABOUT:
506 60 : addRoundabout(attrs);
507 60 : break;
508 19681 : case SUMO_TAG_PARAM:
509 19681 : if (myLastParameterised.size() != 0) {
510 19681 : bool ok = true;
511 19681 : const std::string key = attrs.get<std::string>(SUMO_ATTR_KEY, nullptr, ok);
512 : if (myDiscardableParams.count(key) == 0) {
513 : // circumventing empty string test
514 19384 : const std::string val = attrs.hasAttribute(SUMO_ATTR_VALUE) ? attrs.getString(SUMO_ATTR_VALUE) : "";
515 19384 : myLastParameterised.back()->setParameter(key, val);
516 : }
517 : }
518 : break;
519 1961 : default:
520 1961 : myTypesHandler.myStartElement(element, attrs);
521 1961 : break;
522 : }
523 361455 : }
524 :
525 :
526 : void
527 361309 : NIImporter_SUMO::myEndElement(int element) {
528 361309 : switch (element) {
529 64172 : case SUMO_TAG_EDGE:
530 64172 : if (myCurrentEdge != nullptr) {
531 64168 : if (myEdges.find(myCurrentEdge->id) != myEdges.end()) {
532 94 : WRITE_WARNINGF(TL("Edge '%' occurred at least twice in the input."), myCurrentEdge->id);
533 108 : for (LaneAttrs* const lane : myCurrentEdge->lanes) {
534 61 : delete lane;
535 : }
536 47 : delete myCurrentEdge;
537 : } else {
538 64121 : myEdges[myCurrentEdge->id] = myCurrentEdge;
539 : }
540 64168 : myCurrentEdge = nullptr;
541 : myLastParameterised.pop_back();
542 : }
543 : break;
544 78467 : case SUMO_TAG_LANE:
545 78467 : if (myCurrentEdge != nullptr && myCurrentLane != nullptr) {
546 78451 : myCurrentEdge->maxSpeed = MAX2(myCurrentEdge->maxSpeed, myCurrentLane->maxSpeed);
547 78451 : myCurrentEdge->lanes.push_back(myCurrentLane);
548 : myLastParameterised.pop_back();
549 : }
550 78467 : myCurrentLane = nullptr;
551 78467 : break;
552 745 : case SUMO_TAG_TLLOGIC:
553 745 : if (myCurrentTL == nullptr) {
554 0 : WRITE_ERROR(TL("Unmatched closing tag for tl-logic."));
555 : } else {
556 745 : if (!myTLLCont.insert(myCurrentTL)) {
557 6 : WRITE_WARNINGF(TL("Could not add program '%' for traffic light '%'"), myCurrentTL->getProgramID(), myCurrentTL->getID());
558 2 : delete myCurrentTL;
559 : }
560 745 : myCurrentTL = nullptr;
561 : myLastParameterised.pop_back();
562 : }
563 : break;
564 22284 : case SUMO_TAG_JUNCTION:
565 22284 : if (myCurrentJunction.node != nullptr) {
566 : myLastParameterised.pop_back();
567 : }
568 : break;
569 100978 : case SUMO_TAG_CONNECTION:
570 : // !!! this just avoids a crash but is not a real check that it was a connection
571 100978 : if (!myLastParameterised.empty()) {
572 : myLastParameterised.pop_back();
573 : }
574 : break;
575 : default:
576 : break;
577 : }
578 361309 : }
579 :
580 :
581 : void
582 64232 : NIImporter_SUMO::addEdge(const SUMOSAXAttributes& attrs) {
583 : // get the id, report an error if not given or empty...
584 64232 : bool ok = true;
585 64232 : const std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
586 64232 : if (!ok) {
587 : return;
588 : }
589 64228 : myCurrentEdge = new EdgeAttrs();
590 64228 : myLastParameterised.push_back(myCurrentEdge);
591 64228 : myCurrentEdge->builtEdge = nullptr;
592 64228 : myCurrentEdge->id = id;
593 : // get the function
594 64228 : myCurrentEdge->func = attrs.getOpt<SumoXMLEdgeFunc>(SUMO_ATTR_FUNCTION, id.c_str(), ok, SumoXMLEdgeFunc::NORMAL);
595 64228 : if (myCurrentEdge->func == SumoXMLEdgeFunc::CROSSING) {
596 : // add the crossing but don't do anything else
597 149 : Crossing c(id);
598 149 : c.crossingEdges = attrs.get<std::vector<std::string> >(SUMO_ATTR_CROSSING_EDGES, nullptr, ok);
599 298 : myPedestrianCrossings[SUMOXMLDefinitions::getJunctionIDFromInternalEdge(id)].push_back(c);
600 : return;
601 64228 : } else if (myCurrentEdge->func == SumoXMLEdgeFunc::INTERNAL || myCurrentEdge->func == SumoXMLEdgeFunc::WALKINGAREA) {
602 29725 : myHaveSeenInternalEdge = true;
603 29725 : return; // skip internal edges
604 : }
605 : // get the origin and the destination node
606 34354 : myCurrentEdge->fromNode = attrs.getOpt<std::string>(SUMO_ATTR_FROM, id.c_str(), ok, "");
607 68708 : myCurrentEdge->toNode = attrs.getOpt<std::string>(SUMO_ATTR_TO, id.c_str(), ok, "");
608 34354 : myCurrentEdge->priority = attrs.getOpt<int>(SUMO_ATTR_PRIORITY, id.c_str(), ok, -1);
609 34354 : myCurrentEdge->type = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, "");
610 34354 : myCurrentEdge->routingType = attrs.getOpt<std::string>(SUMO_ATTR_ROUTINGTYPE, id.c_str(), ok, "");
611 34354 : myCurrentEdge->shape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, id.c_str(), ok, PositionVector());
612 34354 : NBNetBuilder::transformCoordinates(myCurrentEdge->shape, true, myLocation);
613 34354 : myCurrentEdge->length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, id.c_str(), ok, NBEdge::UNSPECIFIED_LOADED_LENGTH);
614 34354 : myCurrentEdge->maxSpeed = 0;
615 68708 : myCurrentEdge->streetName = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
616 34354 : myCurrentEdge->distance = attrs.getOpt<double>(SUMO_ATTR_DISTANCE, id.c_str(), ok, 0);
617 34354 : myCurrentEdge->bidi = attrs.getOpt<std::string>(SUMO_ATTR_BIDI, id.c_str(), ok, "");
618 52985 : if (myCurrentEdge->streetName != "" && OptionsCont::getOptions().isDefault("output.street-names")) {
619 154 : OptionsCont::getOptions().set("output.street-names", "true");
620 : }
621 :
622 68708 : std::string lsfS = attrs.getOpt<std::string>(SUMO_ATTR_SPREADTYPE, id.c_str(), ok, myDefaultSpreadType);
623 : if (SUMOXMLDefinitions::LaneSpreadFunctions.hasString(lsfS)) {
624 34354 : myCurrentEdge->lsf = SUMOXMLDefinitions::LaneSpreadFunctions.get(lsfS);
625 : } else {
626 0 : WRITE_ERRORF(TL("Unknown spreadType '%' for edge '%'."), lsfS, id);
627 : }
628 : }
629 :
630 :
631 : void
632 78467 : NIImporter_SUMO::addLane(const SUMOSAXAttributes& attrs) {
633 78467 : bool ok = true;
634 78467 : std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
635 78467 : if (!ok) {
636 : return;
637 : }
638 78459 : if (myCurrentEdge == nullptr) {
639 24 : WRITE_ERRORF(TL("Found lane '%' not within edge element."), id);
640 8 : return;
641 : }
642 156902 : const std::string expectedID = myCurrentEdge->id + "_" + toString(myCurrentEdge->lanes.size());
643 78451 : if (id != expectedID) {
644 108 : WRITE_WARNINGF(TL("Renaming lane '%' to '%'."), id, expectedID);
645 : }
646 78451 : myCurrentLane = new LaneAttrs();
647 78451 : myLastParameterised.push_back(myCurrentLane);
648 78451 : myCurrentLane->customShape = attrs.getOpt<bool>(SUMO_ATTR_CUSTOMSHAPE, nullptr, ok, false);
649 78451 : myCurrentLane->shape = attrs.get<PositionVector>(SUMO_ATTR_SHAPE, id.c_str(), ok);
650 78451 : myCurrentLane->width = attrs.getOpt<double>(SUMO_ATTR_WIDTH, id.c_str(), ok, (double) NBEdge::UNSPECIFIED_WIDTH);
651 78451 : myCurrentLane->type = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, "");
652 78451 : if (myCurrentEdge->func == SumoXMLEdgeFunc::CROSSING) {
653 : // save the width and the lane id of the crossing but don't do anything else
654 447 : std::vector<Crossing>& crossings = myPedestrianCrossings[SUMOXMLDefinitions::getJunctionIDFromInternalEdge(myCurrentEdge->id)];
655 : assert(crossings.size() > 0);
656 149 : crossings.back().width = attrs.get<double>(SUMO_ATTR_WIDTH, id.c_str(), ok);
657 : myLastParameterised.pop_back();
658 149 : myLastParameterised.push_back(&crossings.back());
659 149 : if (myCurrentLane->customShape) {
660 : crossings.back().customShape = myCurrentLane->shape;
661 11 : NBNetBuilder::transformCoordinates(crossings.back().customShape, true, myLocation);
662 : }
663 78302 : } else if (myCurrentEdge->func == SumoXMLEdgeFunc::WALKINGAREA) {
664 : // save custom shape if needed but don't do anything else
665 344 : if (myCurrentLane->customShape) {
666 : WalkingAreaParsedCustomShape wacs;
667 3 : wacs.shape = myCurrentLane->shape;
668 3 : wacs.width = myCurrentLane->width;
669 3 : NBNetBuilder::transformCoordinates(wacs.shape, true, myLocation);
670 3 : myWACustomShapes[myCurrentEdge->id] = wacs;
671 3 : }
672 344 : return;
673 77958 : } else if (myCurrentEdge->func == SumoXMLEdgeFunc::INTERNAL) {
674 : return; // skip internal edges
675 : }
676 89988 : if (attrs.hasAttribute("maxspeed")) {
677 : // !!! deprecated
678 0 : myCurrentLane->maxSpeed = attrs.getFloat("maxspeed");
679 : } else {
680 44994 : myCurrentLane->maxSpeed = attrs.get<double>(SUMO_ATTR_SPEED, id.c_str(), ok);
681 : }
682 44994 : myCurrentLane->friction = attrs.getOpt<double>(SUMO_ATTR_FRICTION, id.c_str(), ok, NBEdge::UNSPECIFIED_FRICTION, false); //sets 1 on empty
683 : try {
684 89988 : myCurrentLane->allow = attrs.getOpt<std::string>(SUMO_ATTR_ALLOW, id.c_str(), ok, "", false);
685 0 : } catch (EmptyData&) {
686 : // !!! deprecated
687 0 : myCurrentLane->allow = "";
688 0 : }
689 44994 : myCurrentLane->disallow = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, id.c_str(), ok, "");
690 44994 : myCurrentLane->endOffset = attrs.getOpt<double>(SUMO_ATTR_ENDOFFSET, id.c_str(), ok, (double) NBEdge::UNSPECIFIED_OFFSET);
691 44994 : myCurrentLane->accelRamp = attrs.getOpt<bool>(SUMO_ATTR_ACCELERATION, id.c_str(), ok, false);
692 44994 : myCurrentLane->changeLeft = attrs.getOpt<std::string>(SUMO_ATTR_CHANGE_LEFT, id.c_str(), ok, "");
693 44994 : myCurrentLane->changeRight = attrs.getOpt<std::string>(SUMO_ATTR_CHANGE_RIGHT, id.c_str(), ok, "");
694 44994 : if (myChangeLefthand) {
695 10 : std::swap(myCurrentLane->changeLeft, myCurrentLane->changeRight);
696 : }
697 :
698 : // lane coordinates are derived (via lane spread) do not include them in convex boundary
699 44994 : NBNetBuilder::transformCoordinates(myCurrentLane->shape, false, myLocation);
700 : }
701 :
702 :
703 : void
704 26 : NIImporter_SUMO::addStopOffsets(const SUMOSAXAttributes& attrs, bool& ok) {
705 26 : const StopOffset offset(attrs, ok);
706 26 : if (!ok) {
707 1 : return;
708 : }
709 : // Admissibility of value will be checked in _loadNetwork(), when lengths are known
710 25 : if (myCurrentLane == nullptr) {
711 12 : if (myCurrentEdge->edgeStopOffset.isDefined()) {
712 6 : WRITE_WARNINGF(TL("Duplicate definition of stopOffset for edge %.\nIgnoring duplicate specification."), myCurrentEdge->id);
713 : } else {
714 9 : myCurrentEdge->edgeStopOffset = offset;
715 : }
716 : } else {
717 13 : if (myCurrentLane->laneStopOffset.isDefined()) {
718 4 : WRITE_WARNINGF(TL("Duplicate definition of lane's stopOffset on edge %.\nIgnoring duplicate specifications."), myCurrentEdge->id);
719 : } else {
720 11 : myCurrentLane->laneStopOffset = offset;
721 : }
722 : }
723 : }
724 :
725 :
726 : void
727 22285 : NIImporter_SUMO::addJunction(const SUMOSAXAttributes& attrs) {
728 : // get the id, report an error if not given or empty...
729 22285 : myCurrentJunction.node = nullptr;
730 : myCurrentJunction.response.clear();
731 22285 : bool ok = true;
732 22285 : std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
733 22285 : if (!ok) {
734 : return;
735 : }
736 22279 : if (id[0] == ':') { // internal node
737 : return;
738 : }
739 19641 : SumoXMLNodeType type = attrs.getOpt<SumoXMLNodeType>(SUMO_ATTR_TYPE, id.c_str(), ok, SumoXMLNodeType::UNKNOWN);
740 19641 : if (ok) {
741 19635 : if (type == SumoXMLNodeType::DEAD_END_DEPRECATED || type == SumoXMLNodeType::DEAD_END) {
742 : // dead end is a computed status. Reset this to unknown so it will
743 : // be corrected if additional connections are loaded
744 : type = SumoXMLNodeType::UNKNOWN;
745 17256 : } else if (type == SumoXMLNodeType::INTERNAL) {
746 3 : WRITE_WARNINGF("Invalid node type '%' for junction '%' in input network", toString(SumoXMLNodeType::INTERNAL), id);
747 : type = SumoXMLNodeType::UNKNOWN;
748 : }
749 : }
750 19641 : Position pos = readPosition(attrs, id, ok);
751 19641 : NBNetBuilder::transformCoordinate(pos, true, myLocation);
752 19641 : NBNode* node = new NBNode(id, pos, type);
753 19640 : if (!myNodeCont.insert(node)) {
754 66 : WRITE_WARNINGF(TL("Junction '%' occurred at least twice in the input."), id);
755 22 : delete node;
756 22 : myLastParameterised.push_back(myNodeCont.retrieve(id));
757 22 : return;
758 : } else {
759 19618 : myLastParameterised.push_back(node);
760 : }
761 19618 : myCurrentJunction.node = node;
762 : // set optional radius
763 19618 : if (attrs.hasAttribute(SUMO_ATTR_RADIUS)) {
764 14 : node->setRadius(attrs.get<double>(SUMO_ATTR_RADIUS, id.c_str(), ok));
765 : }
766 : // handle custom shape
767 19618 : if (attrs.getOpt<bool>(SUMO_ATTR_CUSTOMSHAPE, id.c_str(), ok, false)) {
768 17 : PositionVector shape = attrs.get<PositionVector>(SUMO_ATTR_SHAPE, id.c_str(), ok);
769 17 : NBNetBuilder::transformCoordinates(shape, true, myLocation);
770 17 : node->setCustomShape(shape);
771 17 : }
772 19618 : if (type == SumoXMLNodeType::RAIL_SIGNAL || type == SumoXMLNodeType::RAIL_CROSSING) {
773 : // both types of nodes come without a tlLogic
774 : myRailSignals.insert(id);
775 : }
776 19618 : node->setRightOfWay(attrs.getOpt<RightOfWay>(SUMO_ATTR_RIGHT_OF_WAY, id.c_str(), ok, node->getRightOfWay()));
777 19618 : node->setFringeType(attrs.getOpt<FringeType>(SUMO_ATTR_FRINGE, id.c_str(), ok, node->getFringeType()));
778 19618 : node->setRoundaboutType(attrs.getOpt<RoundaboutType>(SUMO_ATTR_ROUNDABOUT, id.c_str(), ok, node->getRoundaboutType()));
779 19618 : if (attrs.hasAttribute(SUMO_ATTR_NAME)) {
780 5 : node->setName(attrs.get<std::string>(SUMO_ATTR_NAME, id.c_str(), ok));
781 : }
782 : }
783 :
784 :
785 : void
786 67015 : NIImporter_SUMO::addRequest(const SUMOSAXAttributes& attrs) {
787 67015 : if (myCurrentJunction.node != nullptr) {
788 66945 : bool ok = true;
789 133890 : myCurrentJunction.response.push_back(attrs.get<std::string>(SUMO_ATTR_RESPONSE, nullptr, ok));
790 : }
791 67015 : }
792 :
793 :
794 : void
795 100978 : NIImporter_SUMO::addConnection(const SUMOSAXAttributes& attrs) {
796 100978 : bool ok = true;
797 100978 : std::string fromID = attrs.get<std::string>(SUMO_ATTR_FROM, nullptr, ok);
798 : if (myEdges.count(fromID) == 0) {
799 66 : WRITE_ERRORF(TL("Unknown edge '%' given in connection."), fromID);
800 22 : return;
801 : }
802 100956 : EdgeAttrs* from = myEdges[fromID];
803 100956 : if (from->func == SumoXMLEdgeFunc::INTERNAL) {
804 : // internal junction connection
805 : return;
806 : }
807 :
808 67875 : Connection conn;
809 67875 : conn.toEdgeID = attrs.get<std::string>(SUMO_ATTR_TO, nullptr, ok);
810 67875 : int fromLaneIdx = attrs.get<int>(SUMO_ATTR_FROM_LANE, nullptr, ok);
811 67875 : conn.toLaneIdx = attrs.get<int>(SUMO_ATTR_TO_LANE, nullptr, ok);
812 67875 : conn.tlID = attrs.getOpt<std::string>(SUMO_ATTR_TLID, nullptr, ok, "");
813 67875 : conn.mayDefinitelyPass = attrs.getOpt<bool>(SUMO_ATTR_PASS, nullptr, ok, false);
814 67875 : conn.keepClear = attrs.getOpt<bool>(SUMO_ATTR_KEEP_CLEAR, nullptr, ok, true);
815 67875 : conn.indirectLeft = attrs.getOpt<bool>(SUMO_ATTR_INDIRECT, nullptr, ok, false);
816 67875 : conn.edgeType = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, nullptr, ok, "");
817 67875 : double contPos = NBEdge::UNSPECIFIED_CONTPOS;
818 135750 : if (OptionsCont::getOptions().isSet("default.connection.cont-pos")) {
819 135750 : contPos = OptionsCont::getOptions().getFloat("default.connection.cont-pos");
820 : }
821 67875 : conn.contPos = attrs.getOpt<double>(SUMO_ATTR_CONTPOS, nullptr, ok, contPos);
822 67875 : conn.visibility = attrs.getOpt<double>(SUMO_ATTR_VISIBILITY_DISTANCE, nullptr, ok, NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE);
823 67875 : std::string allow = attrs.getOpt<std::string>(SUMO_ATTR_ALLOW, nullptr, ok, "", false);
824 67875 : std::string disallow = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, nullptr, ok, "", false);
825 67875 : if (allow == "" && disallow == "") {
826 67873 : conn.permissions = SVC_UNSPECIFIED;
827 : } else {
828 2 : conn.permissions = parseVehicleClasses(allow, disallow, myNetworkVersion);
829 : }
830 67875 : if (attrs.hasAttribute(SUMO_ATTR_CHANGE_LEFT)) {
831 1 : conn.changeLeft = parseVehicleClasses(attrs.get<std::string>(SUMO_ATTR_CHANGE_LEFT, nullptr, ok), "");
832 : } else {
833 67874 : conn.changeLeft = SVC_UNSPECIFIED;
834 : }
835 67875 : if (attrs.hasAttribute(SUMO_ATTR_CHANGE_RIGHT)) {
836 0 : conn.changeRight = parseVehicleClasses(attrs.get<std::string>(SUMO_ATTR_CHANGE_RIGHT, nullptr, ok), "");
837 : } else {
838 67875 : conn.changeRight = SVC_UNSPECIFIED;
839 : }
840 67875 : if (myChangeLefthand) {
841 : std::swap(conn.changeLeft, conn.changeRight);
842 : }
843 67875 : conn.speed = attrs.getOpt<double>(SUMO_ATTR_SPEED, nullptr, ok, NBEdge::UNSPECIFIED_SPEED);
844 67875 : conn.friction = attrs.getOpt<double>(SUMO_ATTR_FRICTION, nullptr, ok, NBEdge::UNSPECIFIED_FRICTION);
845 67875 : conn.customLength = attrs.getOpt<double>(SUMO_ATTR_LENGTH, nullptr, ok, NBEdge::UNSPECIFIED_LOADED_LENGTH);
846 135750 : conn.customShape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, nullptr, ok, PositionVector::EMPTY);
847 67875 : NBNetBuilder::transformCoordinates(conn.customShape, false, myLocation);
848 67875 : conn.uncontrolled = attrs.getOpt<bool>(SUMO_ATTR_UNCONTROLLED, nullptr, ok, NBEdge::UNSPECIFIED_CONNECTION_UNCONTROLLED, false);
849 67875 : if (conn.tlID != "") {
850 8149 : conn.tlLinkIndex = attrs.get<int>(SUMO_ATTR_TLLINKINDEX, nullptr, ok);
851 8149 : conn.tlLinkIndex2 = attrs.getOpt<int>(SUMO_ATTR_TLLINKINDEX2, nullptr, ok, -1);
852 : } else {
853 59726 : conn.tlLinkIndex = NBConnection::InvalidTlIndex;
854 : }
855 67875 : if ((int)from->lanes.size() <= fromLaneIdx) {
856 24 : WRITE_ERRORF(TL("Invalid lane index '%' for connection from '%'."), toString(fromLaneIdx), fromID);
857 : return;
858 : }
859 67867 : from->lanes[fromLaneIdx]->connections.push_back(conn);
860 67867 : myLastParameterised.push_back(&from->lanes[fromLaneIdx]->connections.back());
861 :
862 : // determine crossing priority and tlIndex
863 67867 : if (myPedestrianCrossings.size() > 0) {
864 1670 : if (from->func == SumoXMLEdgeFunc::WALKINGAREA && myEdges[conn.toEdgeID]->func == SumoXMLEdgeFunc::CROSSING) {
865 : // connection from walkingArea to crossing
866 450 : std::vector<Crossing>& crossings = myPedestrianCrossings[SUMOXMLDefinitions::getJunctionIDFromInternalEdge(fromID)];
867 730 : for (std::vector<Crossing>::iterator it = crossings.begin(); it != crossings.end(); ++it) {
868 580 : if (conn.toEdgeID == (*it).edgeID) {
869 150 : if (conn.tlID != "") {
870 104 : (*it).priority = true;
871 104 : (*it).customTLIndex = conn.tlLinkIndex;
872 : } else {
873 46 : LinkState state = SUMOXMLDefinitions::LinkStates.get(attrs.get<std::string>(SUMO_ATTR_STATE, nullptr, ok));
874 46 : (*it).priority = state == LINKSTATE_MAJOR;
875 : }
876 : }
877 : }
878 1520 : } else if (from->func == SumoXMLEdgeFunc::CROSSING && myEdges[conn.toEdgeID]->func == SumoXMLEdgeFunc::WALKINGAREA) {
879 : // connection from crossing to walkingArea (set optional linkIndex2)
880 1022 : for (Crossing& c : myPedestrianCrossings[SUMOXMLDefinitions::getJunctionIDFromInternalEdge(fromID)]) {
881 575 : if (fromID == c.edgeID) {
882 149 : c.customTLIndex2 = attrs.getOpt<int>(SUMO_ATTR_TLLINKINDEX, nullptr, ok, -1);
883 : }
884 : }
885 : }
886 : }
887 : // determine walking area reference edges
888 67867 : if (myWACustomShapes.size() > 0) {
889 88 : EdgeAttrs* to = myEdges[conn.toEdgeID];
890 88 : if (from->func == SumoXMLEdgeFunc::WALKINGAREA) {
891 : std::map<std::string, WalkingAreaParsedCustomShape>::iterator it = myWACustomShapes.find(fromID);
892 28 : if (it != myWACustomShapes.end()) {
893 4 : if (to->func == SumoXMLEdgeFunc::NORMAL) {
894 : // add target sidewalk as reference
895 1 : it->second.toEdges.push_back(conn.toEdgeID);
896 3 : } else if (to->func == SumoXMLEdgeFunc::CROSSING) {
897 : // add target crossing edges as reference
898 27 : for (Crossing crossing : myPedestrianCrossings[SUMOXMLDefinitions::getJunctionIDFromInternalEdge(fromID)]) {
899 18 : if (conn.toEdgeID == crossing.edgeID) {
900 3 : it->second.toCrossed.insert(it->second.toCrossed.end(), crossing.crossingEdges.begin(), crossing.crossingEdges.end());
901 : }
902 18 : }
903 : }
904 : }
905 60 : } else if (to->func == SumoXMLEdgeFunc::WALKINGAREA) {
906 : std::map<std::string, WalkingAreaParsedCustomShape>::iterator it = myWACustomShapes.find(conn.toEdgeID);
907 28 : if (it != myWACustomShapes.end()) {
908 4 : if (from->func == SumoXMLEdgeFunc::NORMAL) {
909 : // add origin sidewalk as reference
910 1 : it->second.fromEdges.push_back(fromID);
911 3 : } else if (from->func == SumoXMLEdgeFunc::CROSSING) {
912 : // add origin crossing edges as reference
913 27 : for (Crossing crossing : myPedestrianCrossings[SUMOXMLDefinitions::getJunctionIDFromInternalEdge(fromID)]) {
914 18 : if (fromID == crossing.edgeID) {
915 3 : it->second.fromCrossed.insert(it->second.fromCrossed.end(), crossing.crossingEdges.begin(), crossing.crossingEdges.end());
916 : }
917 18 : }
918 : }
919 : }
920 : }
921 : }
922 67875 : }
923 :
924 :
925 : void
926 20 : NIImporter_SUMO::addProhibition(const SUMOSAXAttributes& attrs) {
927 20 : bool ok = true;
928 20 : std::string prohibitor = attrs.getOpt<std::string>(SUMO_ATTR_PROHIBITOR, nullptr, ok, "");
929 20 : std::string prohibited = attrs.getOpt<std::string>(SUMO_ATTR_PROHIBITED, nullptr, ok, "");
930 20 : if (!ok) {
931 : return;
932 : }
933 : Prohibition p;
934 20 : parseProhibitionConnection(prohibitor, p.prohibitorFrom, p.prohibitorTo, ok);
935 20 : parseProhibitionConnection(prohibited, p.prohibitedFrom, p.prohibitedTo, ok);
936 20 : if (!ok) {
937 : return;
938 : }
939 20 : myProhibitions.push_back(p);
940 20 : }
941 :
942 :
943 : NBLoadedSUMOTLDef*
944 745 : NIImporter_SUMO::initTrafficLightLogic(const SUMOSAXAttributes& attrs, NBLoadedSUMOTLDef* currentTL) {
945 745 : if (currentTL) {
946 0 : WRITE_ERRORF(TL("Definition of tl-logic '%' was not finished."), currentTL->getID());
947 0 : return nullptr;
948 : }
949 745 : bool ok = true;
950 745 : std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
951 745 : SUMOTime offset = attrs.getOptOffsetReporting(SUMO_ATTR_OFFSET, id.c_str(), ok, 0);
952 745 : std::string programID = attrs.getOpt<std::string>(SUMO_ATTR_PROGRAMID, id.c_str(), ok, "<unknown>");
953 745 : std::string typeS = attrs.get<std::string>(SUMO_ATTR_TYPE, nullptr, ok);
954 : TrafficLightType type;
955 : if (SUMOXMLDefinitions::TrafficLightTypes.hasString(typeS)) {
956 745 : type = SUMOXMLDefinitions::TrafficLightTypes.get(typeS);
957 : } else {
958 0 : WRITE_ERRORF(TL("Unknown traffic light type '%' for tlLogic '%'."), typeS, id);
959 0 : return nullptr;
960 : }
961 745 : if (ok) {
962 745 : return new NBLoadedSUMOTLDef(id, programID, offset, type);
963 : } else {
964 : return nullptr;
965 : }
966 : }
967 :
968 :
969 : void
970 4757 : NIImporter_SUMO::addPhase(const SUMOSAXAttributes& attrs, NBLoadedSUMOTLDef* currentTL) {
971 4757 : if (!currentTL) {
972 0 : WRITE_ERROR(TL("found phase without tl-logic"));
973 0 : return;
974 : }
975 : const std::string& id = currentTL->getID();
976 4757 : bool ok = true;
977 4757 : std::string state = attrs.get<std::string>(SUMO_ATTR_STATE, id.c_str(), ok);
978 4757 : SUMOTime duration = TIME2STEPS(attrs.get<double>(SUMO_ATTR_DURATION, id.c_str(), ok));
979 4757 : if (duration < 0) {
980 0 : WRITE_ERRORF(TL("Phase duration for tl-logic '%/%' must be positive."), id, currentTL->getProgramID());
981 : return;
982 : }
983 : // if the traffic light is an actuated traffic light, try to get the minimum and maximum durations and ends
984 4758 : std::vector<int> nextPhases = attrs.getOpt<std::vector<int> >(SUMO_ATTR_NEXT, id.c_str(), ok);
985 4757 : const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, nullptr, ok);
986 : // Specific from actuated
987 4757 : const SUMOTime minDuration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_MINDURATION, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
988 4757 : const SUMOTime maxDuration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_MAXDURATION, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
989 4757 : const SUMOTime earliestEnd = attrs.getOptSUMOTimeReporting(SUMO_ATTR_EARLIEST_END, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
990 4757 : const SUMOTime latestEnd = attrs.getOptSUMOTimeReporting(SUMO_ATTR_LATEST_END, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
991 : // specific von NEMA
992 4757 : const SUMOTime vehExt = attrs.getOptSUMOTimeReporting(SUMO_ATTR_VEHICLEEXTENSION, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
993 4757 : const SUMOTime yellow = attrs.getOptSUMOTimeReporting(SUMO_ATTR_YELLOW, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
994 4757 : const SUMOTime red = attrs.getOptSUMOTimeReporting(SUMO_ATTR_RED, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
995 4757 : if (ok) {
996 4757 : currentTL->addPhase(duration, state, minDuration, maxDuration, earliestEnd, latestEnd, vehExt, yellow, red, nextPhases, name);
997 : }
998 4757 : }
999 :
1000 :
1001 : GeoConvHelper*
1002 1282 : NIImporter_SUMO::loadLocation(const SUMOSAXAttributes& attrs, bool setLoaded) {
1003 : // @todo refactor parsing of location since its duplicated in NLHandler and PCNetProjectionLoader
1004 1282 : bool ok = true;
1005 : GeoConvHelper* result = nullptr;
1006 1282 : const Position offset = attrs.get<Position>(SUMO_ATTR_NET_OFFSET, nullptr, ok);
1007 1282 : const Boundary convBoundary = attrs.get<Boundary>(SUMO_ATTR_CONV_BOUNDARY, nullptr, ok);
1008 1282 : const Boundary origBoundary = attrs.get<Boundary>(SUMO_ATTR_ORIG_BOUNDARY, nullptr, ok);
1009 1282 : const std::string proj = attrs.get<std::string>(SUMO_ATTR_ORIG_PROJ, nullptr, ok);
1010 1282 : if (ok) {
1011 1282 : result = new GeoConvHelper(proj, offset, origBoundary, convBoundary);
1012 1282 : result->resolveAbstractProjection();
1013 1282 : if (setLoaded) {
1014 1256 : GeoConvHelper::setLoaded(*result);
1015 : }
1016 : }
1017 1282 : return result;
1018 : }
1019 :
1020 :
1021 : Position
1022 19641 : NIImporter_SUMO::readPosition(const SUMOSAXAttributes& attrs, const std::string& id, bool& ok) {
1023 19641 : const double x = attrs.get<double>(SUMO_ATTR_X, id.c_str(), ok);
1024 19641 : const double y = attrs.get<double>(SUMO_ATTR_Y, id.c_str(), ok);
1025 19641 : const double z = attrs.getOpt<double>(SUMO_ATTR_Z, id.c_str(), ok, 0.);
1026 19641 : return Position(x, y, z);
1027 : }
1028 :
1029 :
1030 : void
1031 40 : NIImporter_SUMO::parseProhibitionConnection(const std::string& attr, std::string& from, std::string& to, bool& ok) {
1032 : // split from/to
1033 : const std::string::size_type div = attr.find("->");
1034 40 : if (div == std::string::npos) {
1035 0 : WRITE_ERRORF(TL("Missing connection divider in prohibition attribute '%'"), attr);
1036 0 : ok = false;
1037 : }
1038 40 : from = attr.substr(0, div);
1039 80 : to = attr.substr(div + 2);
1040 : // check whether the edges are known
1041 : if (myEdges.count(from) == 0) {
1042 0 : WRITE_ERRORF(TL("Unknown edge prohibition '%'"), from);
1043 0 : ok = false;
1044 : }
1045 : if (myEdges.count(to) == 0) {
1046 0 : WRITE_ERRORF(TL("Unknown edge prohibition '%'"), to);
1047 0 : ok = false;
1048 : }
1049 40 : }
1050 :
1051 :
1052 : void
1053 60 : NIImporter_SUMO::addRoundabout(const SUMOSAXAttributes& attrs) {
1054 60 : bool ok = true;
1055 60 : const std::vector<std::string>& edgeIDs = attrs.get<std::vector<std::string> >(SUMO_ATTR_EDGES, nullptr, ok);
1056 60 : if (ok) {
1057 60 : myRoundabouts.push_back(edgeIDs);
1058 : }
1059 60 : }
1060 :
1061 :
1062 : /****************************************************************************/
|