Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2026 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 2031 : NIImporter_SUMO::loadNetwork(OptionsCont& oc, NBNetBuilder& nb) {
57 2031 : NIImporter_SUMO importer(nb);
58 2031 : importer._loadNetwork(oc);
59 2031 : }
60 :
61 :
62 : // ---------------------------------------------------------------------------
63 : // loader methods
64 : // ---------------------------------------------------------------------------
65 2031 : NIImporter_SUMO::NIImporter_SUMO(NBNetBuilder& nb)
66 : : SUMOSAXHandler("sumo-network"),
67 2031 : myNetBuilder(nb),
68 2031 : myNodeCont(nb.getNodeCont()),
69 2031 : myTLLCont(nb.getTLLogicCont()),
70 2031 : myTypesHandler(nb.getTypeCont()),
71 2031 : myCurrentEdge(nullptr),
72 2031 : myCurrentLane(nullptr),
73 2031 : myCurrentTL(nullptr),
74 2031 : myLocation(nullptr),
75 : myNetworkVersion(0, 0),
76 2031 : myHaveSeenInternalEdge(false),
77 2031 : myAmLefthand(false),
78 2031 : myChangeLefthand(false),
79 2031 : myCornerDetail(0),
80 2031 : myLinkDetail(-1),
81 2031 : myRectLaneCut(false),
82 2031 : myWalkingAreas(false),
83 2031 : myLimitTurnSpeed(-1),
84 2031 : myCheckLaneFoesAll(false),
85 2031 : myCheckLaneFoesRoundabout(true),
86 2031 : myTlsIgnoreInternalJunctionJam(false),
87 2031 : myDefaultSpreadType(toString(LaneSpreadFunction::RIGHT)),
88 2031 : myGeomAvoidOverlap(false),
89 2031 : myJunctionsHigherSpeed(false),
90 2031 : myInternalJunctionsVehicleWidth(OptionsCont::getOptions().getFloat("internal-junctions.vehicle-width")),
91 2031 : myJunctionsMinimalShape(OptionsCont::getOptions().getBool("junctions.minimal-shape")),
92 6093 : myJunctionsEndpointShape(OptionsCont::getOptions().getBool("junctions.endpoint-shape")) {
93 2031 : }
94 :
95 :
96 2031 : NIImporter_SUMO::~NIImporter_SUMO() {
97 67375 : for (std::map<std::string, EdgeAttrs*>::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
98 65344 : EdgeAttrs* ed = (*i).second;
99 144932 : for (std::vector<LaneAttrs*>::const_iterator j = ed->lanes.begin(); j != ed->lanes.end(); ++j) {
100 79588 : delete *j;
101 : }
102 : delete ed;
103 : }
104 2031 : delete myLocation;
105 6093 : }
106 :
107 :
108 : void
109 2031 : NIImporter_SUMO::_loadNetwork(OptionsCont& oc) {
110 : // check whether the option is set (properly)
111 4062 : if (!oc.isUsableFileList("sumo-net-file")) {
112 1333 : return;
113 : }
114 1396 : const std::vector<std::string> discardableParams = oc.getStringVector("discard-params");
115 : myDiscardableParams.insert(discardableParams.begin(), discardableParams.end());
116 : // parse file(s)
117 1396 : const std::vector<std::string> files = oc.getStringVector("sumo-net-file");
118 1406 : for (std::vector<std::string>::const_iterator file = files.begin(); file != files.end(); ++file) {
119 1416 : if (!FileHelpers::isReadable(*file)) {
120 0 : WRITE_ERRORF(TL("Could not open sumo-net-file '%'."), *file);
121 : return;
122 : }
123 708 : setFileName(*file);
124 2124 : const long before = PROGRESS_BEGIN_TIME_MESSAGE("Parsing sumo-net from '" + *file + "'");
125 708 : XMLSubSys::runParser(*this, *file, true);
126 708 : PROGRESS_TIME_MESSAGE(before);
127 : }
128 : // build edges
129 1396 : const double maxSegmentLength = oc.getFloat("geometry.max-segment-length");
130 66042 : for (std::map<std::string, EdgeAttrs*>::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
131 65344 : EdgeAttrs* ed = (*i).second;
132 : // skip internal edges
133 65344 : if (ed->func == SumoXMLEdgeFunc::INTERNAL || ed->func == SumoXMLEdgeFunc::CROSSING || ed->func == SumoXMLEdgeFunc::WALKINGAREA) {
134 30342 : continue;
135 : }
136 : // get and check the nodes
137 35002 : NBNode* from = myNodeCont.retrieve(ed->fromNode);
138 35002 : NBNode* to = myNodeCont.retrieve(ed->toNode);
139 35002 : if (from == nullptr) {
140 228 : WRITE_ERRORF(TL("Edge's '%' from-node '%' is not known."), ed->id, ed->fromNode);
141 76 : continue;
142 : }
143 34926 : if (to == nullptr) {
144 114 : WRITE_ERRORF(TL("Edge's '%' to-node '%' is not known."), ed->id, ed->toNode);
145 38 : continue;
146 : }
147 34888 : 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 34888 : 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 34888 : 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 104664 : ed->shape, ed->lsf, ed->streetName, "", true); // always use tryIgnoreNodePositions to keep original shape
163 34888 : e->setLoadedLength(ed->length);
164 34888 : e->updateParameters(ed->getParametersMap());
165 34888 : e->setDistance(ed->distance);
166 34888 : 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 34888 : ed->builtEdge = myNetBuilder.getEdgeCont().retrieve(ed->id);
172 34888 : if (ed->builtEdge != nullptr) {
173 34718 : ed->builtEdge->setEdgeStopOffset(-1, ed->edgeStopOffset);
174 34718 : ed->builtEdge->setBidi(ed->bidi != "");
175 34718 : ed->builtEdge->setRoutingType(ed->routingType);
176 : }
177 : }
178 : // assign further lane attributes (edges are built)
179 : EdgeVector toRemove;
180 1396 : const bool dismissVclasses = oc.getBool("dismiss-vclasses");
181 66042 : for (std::map<std::string, EdgeAttrs*>::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
182 65344 : EdgeAttrs* ed = (*i).second;
183 65344 : NBEdge* nbe = ed->builtEdge;
184 65344 : if (nbe == nullptr) { // inner edge or removed by explicit list, vclass, ...
185 30626 : continue;
186 : }
187 : const SumoXMLNodeType toType = nbe->getToNode()->getType();
188 79818 : for (int fromLaneIndex = 0; fromLaneIndex < (int) ed->lanes.size(); ++fromLaneIndex) {
189 45100 : LaneAttrs* lane = ed->lanes[fromLaneIndex];
190 : // connections
191 : const std::vector<Connection>& connections = lane->connections;
192 112689 : for (const Connection& c : connections) {
193 67589 : if (myEdges.count(c.toEdgeID) == 0) {
194 66 : WRITE_ERRORF(TL("Unknown edge '%' given in connection."), c.toEdgeID);
195 22 : continue;
196 : }
197 67567 : NBEdge* toEdge = myEdges[c.toEdgeID]->builtEdge;
198 67567 : if (toEdge == nullptr) { // removed by explicit list, vclass, ...
199 424 : continue;
200 : }
201 67143 : 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 67122 : bool uncontrolled = c.uncontrolled;
207 :
208 127081 : if ((NBNode::isTrafficLight(toType) || toType == SumoXMLNodeType::RAIL_SIGNAL)
209 68045 : && c.tlLinkIndex == NBConnection::InvalidTlIndex) {
210 : uncontrolled = true;
211 : }
212 134244 : nbe->addLane2LaneConnection(
213 67122 : fromLaneIndex, toEdge, c.toLaneIdx, NBEdge::Lane2LaneInfoType::VALIDATED,
214 67122 : true, c.mayDefinitelyPass, c.keepClear ? KEEPCLEAR_TRUE : KEEPCLEAR_FALSE,
215 67122 : c.contPos, c.visibility, c.speed, c.friction, c.customLength, c.customShape, uncontrolled, c.permissions, c.indirectLeft, c.edgeType, c.changeLeft, c.changeRight);
216 67122 : 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 67122 : if (c.tlID != "" && myRailSignals.count(c.tlID) == 0) {
221 7148 : const std::map<std::string, NBTrafficLightDefinition*>& programs = myTLLCont.getPrograms(c.tlID);
222 7148 : if (programs.size() > 0) {
223 : std::map<std::string, NBTrafficLightDefinition*>::const_iterator it;
224 14314 : for (it = programs.begin(); it != programs.end(); it++) {
225 7166 : NBLoadedSUMOTLDef* tlDef = dynamic_cast<NBLoadedSUMOTLDef*>(it->second);
226 7166 : if (tlDef) {
227 7166 : 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 45100 : if (!dismissVclasses) {
239 45094 : nbe->setPermissions(parseVehicleClasses(lane->allow, lane->disallow, myNetworkVersion), fromLaneIndex);
240 : }
241 90200 : nbe->setPermittedChanging(fromLaneIndex, parseVehicleClasses(lane->changeLeft, ""), parseVehicleClasses(lane->changeRight, ""));
242 : // width, offset
243 45100 : nbe->setLaneWidth(fromLaneIndex, lane->width);
244 45100 : nbe->setEndOffset(fromLaneIndex, lane->endOffset);
245 45100 : nbe->setSpeed(fromLaneIndex, lane->maxSpeed);
246 45100 : nbe->setFriction(fromLaneIndex, lane->friction);
247 45100 : nbe->setAcceleration(fromLaneIndex, lane->accelRamp);
248 45100 : nbe->getLaneStruct(fromLaneIndex).oppositeID = lane->oppositeID;
249 45100 : nbe->getLaneStruct(fromLaneIndex).type = lane->type;
250 45100 : nbe->getLaneStruct(fromLaneIndex).updateParameters(lane->getParametersMap());
251 45100 : if (lane->customShape) {
252 5 : nbe->setLaneShape(fromLaneIndex, lane->shape);
253 : }
254 : // stop offset for lane
255 : bool stopOffsetSet = false;
256 45100 : 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 45074 : nbe->setEdgeStopOffset(fromLaneIndex, nbe->getEdgeStopOffset());
263 : }
264 : }
265 34718 : nbe->declareConnectionsAsLoaded();
266 34718 : if (!nbe->hasLaneSpecificWidth() && nbe->getLanes()[0].width != NBEdge::UNSPECIFIED_WIDTH) {
267 410 : nbe->setLaneWidth(-1, nbe->getLaneWidth(0));
268 : }
269 34718 : if (!nbe->hasLaneSpecificEndOffset() && nbe->getEndOffset(0) != NBEdge::UNSPECIFIED_OFFSET) {
270 0 : nbe->setEndOffset(-1, nbe->getEndOffset(0));
271 : }
272 34718 : if (!nbe->hasLaneSpecificStopOffsets() && nbe->getEdgeStopOffset().isDefined()) {
273 4 : nbe->setEdgeStopOffset(-1, nbe->getEdgeStopOffset());
274 : }
275 : // check again after permissions are set
276 34718 : if (myNetBuilder.getEdgeCont().ignoreFilterMatch(nbe)) {
277 12 : myNetBuilder.getEdgeCont().ignore(nbe->getID());
278 12 : toRemove.push_back(nbe);
279 : }
280 : }
281 710 : for (EdgeVector::iterator i = toRemove.begin(); i != toRemove.end(); ++i) {
282 12 : myNetBuilder.getEdgeCont().erase(myNetBuilder.getDistrictCont(), *i);
283 : }
284 : // insert loaded prohibitions
285 718 : 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 1121 : if (!myHaveSeenInternalEdge && oc.isWriteable("no-internal-links")) {
306 766 : oc.set("no-internal-links", "true");
307 : }
308 1396 : if (oc.isWriteable("lefthand")) {
309 1390 : oc.set("lefthand", toString(myAmLefthand));
310 : }
311 1396 : if (oc.isWriteable("junctions.corner-detail")) {
312 1308 : oc.set("junctions.corner-detail", toString(myCornerDetail));
313 : }
314 1396 : if (oc.isWriteable("junctions.internal-link-detail") && myLinkDetail > 0) {
315 0 : oc.set("junctions.internal-link-detail", toString(myLinkDetail));
316 : }
317 1396 : if (oc.isWriteable("rectangular-lane-cut")) {
318 1364 : oc.set("rectangular-lane-cut", toString(myRectLaneCut));
319 : }
320 1396 : if (oc.isWriteable("walkingareas")) {
321 1396 : oc.set("walkingareas", toString(myWalkingAreas));
322 : }
323 1396 : if (oc.isWriteable("junctions.limit-turn-speed")) {
324 1394 : oc.set("junctions.limit-turn-speed", toString(myLimitTurnSpeed));
325 : }
326 2093 : 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 2094 : 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 2093 : 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 2708 : if (oc.isWriteable("default.spreadtype") && oc.getString("default.spreadtype") != myDefaultSpreadType) {
336 0 : oc.set("default.spreadtype", myDefaultSpreadType);
337 : }
338 698 : if (oc.isWriteable("geometry.avoid-overlap") && oc.getBool("geometry.avoid-overlap") != myGeomAvoidOverlap) {
339 0 : oc.set("geometry.avoid-overlap", toString(myGeomAvoidOverlap));
340 : }
341 2094 : if (oc.isWriteable("junctions.higher-speed") && oc.getBool("junctions.higher-speed") != myJunctionsHigherSpeed) {
342 0 : oc.set("junctions.higher-speed", toString(myJunctionsHigherSpeed));
343 : }
344 2093 : 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 2093 : if (oc.isWriteable("junctions.minimal-shape") && oc.getBool("junctions.minimal-shape") != myJunctionsMinimalShape) {
348 2 : oc.set("junctions.minimal-shape", toString(myJunctionsMinimalShape));
349 : }
350 2093 : if (oc.isWriteable("junctions.endpoint-shape") && oc.getBool("junctions.endpoint-shape") != myJunctionsEndpointShape) {
351 2 : oc.set("junctions.endpoint-shape", toString(myJunctionsEndpointShape));
352 : }
353 698 : if (!deprecatedVehicleClassesSeen.empty()) {
354 2 : WRITE_WARNINGF(TL("Deprecated vehicle class(es) '%' in input network."), toString(deprecatedVehicleClassesSeen));
355 : deprecatedVehicleClassesSeen.clear();
356 : }
357 1396 : if (!oc.getBool("no-internal-links")) {
358 : // add loaded crossings
359 320 : for (const auto& crossIt : myPedestrianCrossings) {
360 60 : NBNode* const node = myNodeCont.retrieve(crossIt.first);
361 206 : for (const Crossing& crossing : crossIt.second) {
362 : EdgeVector edges;
363 375 : for (const std::string& edgeID : crossing.crossingEdges) {
364 229 : NBEdge* edge = myNetBuilder.getEdgeCont().retrieve(edgeID);
365 : // edge might have been removed due to options
366 229 : if (edge != nullptr) {
367 227 : edges.push_back(edge);
368 : }
369 : }
370 146 : if (!edges.empty()) {
371 145 : node->addCrossing(edges, crossing.width, crossing.priority,
372 145 : crossing.customTLIndex, crossing.customTLIndex2, crossing.customShape, true, &crossing);
373 : }
374 146 : }
375 : }
376 260 : myNetBuilder.setHaveNetworkCrossings(myPedestrianCrossings.size() > 0);
377 : // add walking area custom shapes
378 263 : 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 758 : 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 698 : }
422 :
423 :
424 : void
425 367230 : 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 367230 : switch (element) {
442 708 : case SUMO_TAG_NET: {
443 : bool ok;
444 708 : myNetworkVersion = StringUtils::toVersion(attrs.get<std::string>(SUMO_ATTR_VERSION, nullptr, ok, false));
445 708 : myAmLefthand = attrs.getOpt<bool>(SUMO_ATTR_LEFTHAND, nullptr, ok, false);
446 708 : myCornerDetail = attrs.getOpt<int>(SUMO_ATTR_CORNERDETAIL, nullptr, ok, 0);
447 708 : myLinkDetail = attrs.getOpt<int>(SUMO_ATTR_LINKDETAIL, nullptr, ok, -1);
448 708 : myRectLaneCut = attrs.getOpt<bool>(SUMO_ATTR_RECTANGULAR_LANE_CUT, nullptr, ok, false);
449 708 : myWalkingAreas = attrs.getOpt<bool>(SUMO_ATTR_WALKINGAREAS, nullptr, ok, false);
450 708 : myLimitTurnSpeed = attrs.getOpt<double>(SUMO_ATTR_LIMIT_TURN_SPEED, nullptr, ok, -1);
451 708 : myCheckLaneFoesAll = attrs.getOpt<bool>(SUMO_ATTR_CHECKLANEFOES_ALL, nullptr, ok, false);
452 708 : myCheckLaneFoesRoundabout = attrs.getOpt<bool>(SUMO_ATTR_CHECKLANEFOES_ROUNDABOUT, nullptr, ok, true);
453 708 : myTlsIgnoreInternalJunctionJam = attrs.getOpt<bool>(SUMO_ATTR_TLS_IGNORE_INTERNAL_JUNCTION_JAM, nullptr, ok, false);
454 708 : myDefaultSpreadType = attrs.getOpt<std::string>(SUMO_ATTR_SPREADTYPE, nullptr, ok, myDefaultSpreadType);
455 708 : myGeomAvoidOverlap = attrs.getOpt<bool>(SUMO_ATTR_AVOID_OVERLAP, nullptr, ok, myGeomAvoidOverlap);
456 708 : myJunctionsHigherSpeed = attrs.getOpt<bool>(SUMO_ATTR_HIGHER_SPEED, nullptr, ok, myJunctionsHigherSpeed);
457 708 : myInternalJunctionsVehicleWidth = attrs.getOpt<double>(SUMO_ATTR_INTERNAL_JUNCTIONS_VEHICLE_WIDTH, nullptr, ok, myInternalJunctionsVehicleWidth);
458 708 : myJunctionsMinimalShape = attrs.getOpt<bool>(SUMO_ATTR_JUNCTIONS_MINIMAL_SHAPE, nullptr, ok, myJunctionsMinimalShape);
459 708 : myJunctionsEndpointShape = attrs.getOpt<bool>(SUMO_ATTR_JUNCTIONS_ENDPOINT_SHAPE, nullptr, ok, myJunctionsEndpointShape);
460 : // derived
461 708 : const OptionsCont& oc = OptionsCont::getOptions();
462 711 : myChangeLefthand = !oc.isDefault("lefthand") && (oc.getBool("lefthand") != myAmLefthand);
463 :
464 : break;
465 : }
466 65455 : case SUMO_TAG_EDGE:
467 65455 : addEdge(attrs);
468 65455 : break;
469 79695 : case SUMO_TAG_LANE:
470 79695 : addLane(attrs);
471 79695 : 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 22703 : case SUMO_TAG_JUNCTION:
481 22703 : addJunction(attrs);
482 22702 : break;
483 67864 : case SUMO_TAG_REQUEST:
484 67864 : addRequest(attrs);
485 67864 : break;
486 102314 : case SUMO_TAG_CONNECTION:
487 102314 : addConnection(attrs);
488 102314 : break;
489 749 : case SUMO_TAG_TLLOGIC:
490 749 : myCurrentTL = initTrafficLightLogic(attrs, myCurrentTL);
491 749 : if (myCurrentTL) {
492 749 : myLastParameterised.push_back(myCurrentTL);
493 : }
494 : break;
495 4529 : case SUMO_TAG_PHASE:
496 4529 : addPhase(attrs, myCurrentTL);
497 4529 : break;
498 708 : case SUMO_TAG_LOCATION:
499 708 : delete myLocation;
500 708 : myLocation = loadLocation(attrs);
501 708 : 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 20344 : case SUMO_TAG_PARAM:
509 20344 : if (myLastParameterised.size() != 0) {
510 20344 : bool ok = true;
511 20344 : 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 19723 : const std::string val = attrs.hasAttribute(SUMO_ATTR_VALUE) ? attrs.getString(SUMO_ATTR_VALUE) : "";
515 19723 : myLastParameterised.back()->setParameter(key, val);
516 : }
517 : }
518 : break;
519 1968 : default:
520 1968 : myTypesHandler.myStartElement(element, attrs);
521 1968 : break;
522 : }
523 367229 : }
524 :
525 :
526 : void
527 367083 : NIImporter_SUMO::myEndElement(int element) {
528 367083 : switch (element) {
529 65395 : case SUMO_TAG_EDGE:
530 65395 : if (myCurrentEdge != nullptr) {
531 65391 : 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 65344 : myEdges[myCurrentEdge->id] = myCurrentEdge;
539 : }
540 65391 : myCurrentEdge = nullptr;
541 : myLastParameterised.pop_back();
542 : }
543 : break;
544 79695 : case SUMO_TAG_LANE:
545 79695 : if (myCurrentEdge != nullptr && myCurrentLane != nullptr) {
546 79679 : myCurrentEdge->maxSpeed = MAX2(myCurrentEdge->maxSpeed, myCurrentLane->maxSpeed);
547 79679 : myCurrentEdge->lanes.push_back(myCurrentLane);
548 : myLastParameterised.pop_back();
549 : }
550 79695 : myCurrentLane = nullptr;
551 79695 : break;
552 749 : case SUMO_TAG_TLLOGIC:
553 749 : if (myCurrentTL == nullptr) {
554 0 : WRITE_ERROR(TL("Unmatched closing tag for tl-logic."));
555 : } else {
556 749 : 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 749 : myCurrentTL = nullptr;
561 : myLastParameterised.pop_back();
562 : }
563 : break;
564 22702 : case SUMO_TAG_JUNCTION:
565 22702 : if (myCurrentJunction.node != nullptr) {
566 : myLastParameterised.pop_back();
567 : }
568 : break;
569 102314 : case SUMO_TAG_CONNECTION:
570 : // !!! this just avoids a crash but is not a real check that it was a connection
571 102314 : if (!myLastParameterised.empty()) {
572 : myLastParameterised.pop_back();
573 : }
574 : break;
575 : default:
576 : break;
577 : }
578 367083 : }
579 :
580 :
581 : void
582 65455 : NIImporter_SUMO::addEdge(const SUMOSAXAttributes& attrs) {
583 : // get the id, report an error if not given or empty...
584 65455 : bool ok = true;
585 65455 : const std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
586 65455 : if (!ok) {
587 : return;
588 : }
589 65451 : myCurrentEdge = new EdgeAttrs();
590 65451 : myLastParameterised.push_back(myCurrentEdge);
591 65451 : myCurrentEdge->builtEdge = nullptr;
592 65451 : myCurrentEdge->id = id;
593 : // get the function
594 65451 : myCurrentEdge->func = attrs.getOpt<SumoXMLEdgeFunc>(SUMO_ATTR_FUNCTION, id.c_str(), ok, SumoXMLEdgeFunc::NORMAL);
595 65451 : if (myCurrentEdge->func == SumoXMLEdgeFunc::CROSSING) {
596 : // add the crossing but don't do anything else
597 150 : Crossing c(id);
598 150 : c.crossingEdges = attrs.get<std::vector<std::string> >(SUMO_ATTR_CROSSING_EDGES, nullptr, ok);
599 300 : myPedestrianCrossings[SUMOXMLDefinitions::getJunctionIDFromInternalEdge(id)].push_back(c);
600 : return;
601 65451 : } else if (myCurrentEdge->func == SumoXMLEdgeFunc::INTERNAL || myCurrentEdge->func == SumoXMLEdgeFunc::WALKINGAREA) {
602 30207 : myHaveSeenInternalEdge = true;
603 30207 : return; // skip internal edges
604 : }
605 : // get the origin and the destination node
606 35094 : myCurrentEdge->fromNode = attrs.getOpt<std::string>(SUMO_ATTR_FROM, id.c_str(), ok, "");
607 70188 : myCurrentEdge->toNode = attrs.getOpt<std::string>(SUMO_ATTR_TO, id.c_str(), ok, "");
608 35094 : myCurrentEdge->priority = attrs.getOpt<int>(SUMO_ATTR_PRIORITY, id.c_str(), ok, -1);
609 35094 : myCurrentEdge->type = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, "");
610 35094 : myCurrentEdge->routingType = attrs.getOpt<std::string>(SUMO_ATTR_ROUTINGTYPE, id.c_str(), ok, "");
611 35094 : myCurrentEdge->shape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, id.c_str(), ok, PositionVector());
612 35094 : NBNetBuilder::transformCoordinates(myCurrentEdge->shape, true, myLocation);
613 35094 : myCurrentEdge->length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, id.c_str(), ok, NBEdge::UNSPECIFIED_LOADED_LENGTH);
614 35094 : myCurrentEdge->maxSpeed = 0;
615 70188 : myCurrentEdge->streetName = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
616 35094 : myCurrentEdge->distance = attrs.getOpt<double>(SUMO_ATTR_DISTANCE, id.c_str(), ok, 0);
617 35094 : myCurrentEdge->bidi = attrs.getOpt<std::string>(SUMO_ATTR_BIDI, id.c_str(), ok, "");
618 53881 : if (myCurrentEdge->streetName != "" && OptionsCont::getOptions().isDefault("output.street-names")) {
619 166 : OptionsCont::getOptions().set("output.street-names", "true");
620 : }
621 :
622 70188 : std::string lsfS = attrs.getOpt<std::string>(SUMO_ATTR_SPREADTYPE, id.c_str(), ok, myDefaultSpreadType);
623 : if (SUMOXMLDefinitions::LaneSpreadFunctions.hasString(lsfS)) {
624 35094 : 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 79695 : NIImporter_SUMO::addLane(const SUMOSAXAttributes& attrs) {
633 79695 : bool ok = true;
634 79695 : std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
635 79695 : if (!ok) {
636 : return;
637 : }
638 79687 : if (myCurrentEdge == nullptr) {
639 24 : WRITE_ERRORF(TL("Found lane '%' not within edge element."), id);
640 8 : return;
641 : }
642 159358 : const std::string expectedID = myCurrentEdge->id + "_" + toString(myCurrentEdge->lanes.size());
643 79679 : if (id != expectedID) {
644 108 : WRITE_WARNINGF(TL("Renaming lane '%' to '%'."), id, expectedID);
645 : }
646 79679 : myCurrentLane = new LaneAttrs();
647 79679 : myLastParameterised.push_back(myCurrentLane);
648 79679 : myCurrentLane->customShape = attrs.getOpt<bool>(SUMO_ATTR_CUSTOMSHAPE, nullptr, ok, false);
649 79679 : myCurrentLane->shape = attrs.get<PositionVector>(SUMO_ATTR_SHAPE, id.c_str(), ok);
650 79679 : myCurrentLane->width = attrs.getOpt<double>(SUMO_ATTR_WIDTH, id.c_str(), ok, (double) NBEdge::UNSPECIFIED_WIDTH);
651 79679 : myCurrentLane->type = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, "");
652 79679 : if (myCurrentEdge->func == SumoXMLEdgeFunc::CROSSING) {
653 : // save the width and the lane id of the crossing but don't do anything else
654 450 : std::vector<Crossing>& crossings = myPedestrianCrossings[SUMOXMLDefinitions::getJunctionIDFromInternalEdge(myCurrentEdge->id)];
655 : assert(crossings.size() > 0);
656 150 : crossings.back().width = attrs.get<double>(SUMO_ATTR_WIDTH, id.c_str(), ok);
657 : myLastParameterised.pop_back();
658 150 : myLastParameterised.push_back(&crossings.back());
659 150 : if (myCurrentLane->customShape) {
660 : crossings.back().customShape = myCurrentLane->shape;
661 11 : NBNetBuilder::transformCoordinates(crossings.back().customShape, true, myLocation);
662 : }
663 79529 : } else if (myCurrentEdge->func == SumoXMLEdgeFunc::WALKINGAREA) {
664 : // save custom shape if needed but don't do anything else
665 348 : 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 348 : return;
673 79181 : } else if (myCurrentEdge->func == SumoXMLEdgeFunc::INTERNAL) {
674 : return; // skip internal edges
675 : }
676 91480 : if (attrs.hasAttribute("maxspeed")) {
677 : // !!! deprecated
678 0 : myCurrentLane->maxSpeed = attrs.getFloat("maxspeed");
679 : } else {
680 45740 : myCurrentLane->maxSpeed = attrs.get<double>(SUMO_ATTR_SPEED, id.c_str(), ok);
681 : }
682 45740 : myCurrentLane->friction = attrs.getOpt<double>(SUMO_ATTR_FRICTION, id.c_str(), ok, NBEdge::UNSPECIFIED_FRICTION, false); //sets 1 on empty
683 : try {
684 91480 : 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 45740 : myCurrentLane->disallow = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, id.c_str(), ok, "");
690 45740 : myCurrentLane->endOffset = attrs.getOpt<double>(SUMO_ATTR_ENDOFFSET, id.c_str(), ok, (double) NBEdge::UNSPECIFIED_OFFSET);
691 45740 : myCurrentLane->accelRamp = attrs.getOpt<bool>(SUMO_ATTR_ACCELERATION, id.c_str(), ok, false);
692 45740 : myCurrentLane->changeLeft = attrs.getOpt<std::string>(SUMO_ATTR_CHANGE_LEFT, id.c_str(), ok, "");
693 45740 : myCurrentLane->changeRight = attrs.getOpt<std::string>(SUMO_ATTR_CHANGE_RIGHT, id.c_str(), ok, "");
694 45740 : 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 45740 : 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 22703 : NIImporter_SUMO::addJunction(const SUMOSAXAttributes& attrs) {
728 : // get the id, report an error if not given or empty...
729 22703 : myCurrentJunction.node = nullptr;
730 : myCurrentJunction.response.clear();
731 22703 : bool ok = true;
732 22703 : std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
733 22703 : if (!ok) {
734 : return;
735 : }
736 22697 : if (id[0] == ':') { // internal node
737 : return;
738 : }
739 20050 : SumoXMLNodeType type = attrs.getOpt<SumoXMLNodeType>(SUMO_ATTR_TYPE, id.c_str(), ok, SumoXMLNodeType::UNKNOWN);
740 20050 : if (ok) {
741 20044 : 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 17554 : } 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 20050 : Position pos = readPosition(attrs, id, ok);
751 20050 : NBNetBuilder::transformCoordinate(pos, true, myLocation);
752 20050 : NBNode* node = new NBNode(id, pos, type);
753 20049 : 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 20027 : myLastParameterised.push_back(node);
760 : }
761 20027 : myCurrentJunction.node = node;
762 : // set optional radius
763 20027 : 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 20027 : 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 20027 : if (type == SumoXMLNodeType::RAIL_SIGNAL || type == SumoXMLNodeType::RAIL_CROSSING) {
773 : // both types of nodes come without a tlLogic
774 : myRailSignals.insert(id);
775 : }
776 20027 : node->setRightOfWay(attrs.getOpt<RightOfWay>(SUMO_ATTR_RIGHT_OF_WAY, id.c_str(), ok, node->getRightOfWay()));
777 20027 : node->setFringeType(attrs.getOpt<FringeType>(SUMO_ATTR_FRINGE, id.c_str(), ok, node->getFringeType()));
778 20027 : node->setRoundaboutType(attrs.getOpt<RoundaboutType>(SUMO_ATTR_ROUNDABOUT, id.c_str(), ok, node->getRoundaboutType()));
779 20027 : 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 67864 : NIImporter_SUMO::addRequest(const SUMOSAXAttributes& attrs) {
787 67864 : if (myCurrentJunction.node != nullptr) {
788 67794 : bool ok = true;
789 135588 : myCurrentJunction.response.push_back(attrs.get<std::string>(SUMO_ATTR_RESPONSE, nullptr, ok));
790 : }
791 67864 : }
792 :
793 :
794 : void
795 102314 : NIImporter_SUMO::addConnection(const SUMOSAXAttributes& attrs) {
796 102314 : bool ok = true;
797 102314 : 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 102292 : EdgeAttrs* from = myEdges[fromID];
803 102292 : if (from->func == SumoXMLEdgeFunc::INTERNAL) {
804 : // internal junction connection
805 : return;
806 : }
807 :
808 68733 : Connection conn;
809 68733 : conn.toEdgeID = attrs.get<std::string>(SUMO_ATTR_TO, nullptr, ok);
810 68733 : int fromLaneIdx = attrs.get<int>(SUMO_ATTR_FROM_LANE, nullptr, ok);
811 68733 : conn.toLaneIdx = attrs.get<int>(SUMO_ATTR_TO_LANE, nullptr, ok);
812 68733 : conn.tlID = attrs.getOpt<std::string>(SUMO_ATTR_TLID, nullptr, ok, "");
813 68733 : conn.mayDefinitelyPass = attrs.getOpt<bool>(SUMO_ATTR_PASS, nullptr, ok, false);
814 68733 : conn.keepClear = attrs.getOpt<bool>(SUMO_ATTR_KEEP_CLEAR, nullptr, ok, true);
815 68733 : conn.indirectLeft = attrs.getOpt<bool>(SUMO_ATTR_INDIRECT, nullptr, ok, false);
816 68733 : conn.edgeType = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, nullptr, ok, "");
817 68733 : double contPos = NBEdge::UNSPECIFIED_CONTPOS;
818 137466 : if (OptionsCont::getOptions().isSet("default.connection.cont-pos")) {
819 137466 : contPos = OptionsCont::getOptions().getFloat("default.connection.cont-pos");
820 : }
821 68733 : conn.contPos = attrs.getOpt<double>(SUMO_ATTR_CONTPOS, nullptr, ok, contPos);
822 68733 : conn.visibility = attrs.getOpt<double>(SUMO_ATTR_VISIBILITY_DISTANCE, nullptr, ok, NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE);
823 68733 : std::string allow = attrs.getOpt<std::string>(SUMO_ATTR_ALLOW, nullptr, ok, "", false);
824 68733 : std::string disallow = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, nullptr, ok, "", false);
825 68733 : if (allow == "" && disallow == "") {
826 68731 : conn.permissions = SVC_UNSPECIFIED;
827 : } else {
828 2 : conn.permissions = parseVehicleClasses(allow, disallow, myNetworkVersion);
829 : }
830 68733 : 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 68732 : conn.changeLeft = SVC_UNSPECIFIED;
834 : }
835 68733 : 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 68733 : conn.changeRight = SVC_UNSPECIFIED;
839 : }
840 68733 : if (myChangeLefthand) {
841 : std::swap(conn.changeLeft, conn.changeRight);
842 : }
843 68733 : conn.speed = attrs.getOpt<double>(SUMO_ATTR_SPEED, nullptr, ok, NBEdge::UNSPECIFIED_SPEED);
844 68733 : conn.friction = attrs.getOpt<double>(SUMO_ATTR_FRICTION, nullptr, ok, NBEdge::UNSPECIFIED_FRICTION);
845 68733 : conn.customLength = attrs.getOpt<double>(SUMO_ATTR_LENGTH, nullptr, ok, NBEdge::UNSPECIFIED_LOADED_LENGTH);
846 137466 : conn.customShape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, nullptr, ok, PositionVector::EMPTY);
847 68733 : NBNetBuilder::transformCoordinates(conn.customShape, false, myLocation);
848 68733 : conn.uncontrolled = attrs.getOpt<bool>(SUMO_ATTR_UNCONTROLLED, nullptr, ok, NBEdge::UNSPECIFIED_CONNECTION_UNCONTROLLED, false);
849 68733 : if (conn.tlID != "") {
850 8182 : conn.tlLinkIndex = attrs.get<int>(SUMO_ATTR_TLLINKINDEX, nullptr, ok);
851 8182 : conn.tlLinkIndex2 = attrs.getOpt<int>(SUMO_ATTR_TLLINKINDEX2, nullptr, ok, -1);
852 : } else {
853 60551 : conn.tlLinkIndex = NBConnection::InvalidTlIndex;
854 : }
855 68733 : if ((int)from->lanes.size() <= fromLaneIdx) {
856 24 : WRITE_ERRORF(TL("Invalid lane index '%' for connection from '%'."), toString(fromLaneIdx), fromID);
857 : return;
858 : }
859 68725 : from->lanes[fromLaneIdx]->connections.push_back(conn);
860 68725 : myLastParameterised.push_back(&from->lanes[fromLaneIdx]->connections.back());
861 :
862 : // determine crossing priority and tlIndex
863 68725 : if (myPedestrianCrossings.size() > 0) {
864 1682 : if (from->func == SumoXMLEdgeFunc::WALKINGAREA && myEdges[conn.toEdgeID]->func == SumoXMLEdgeFunc::CROSSING) {
865 : // connection from walkingArea to crossing
866 453 : std::vector<Crossing>& crossings = myPedestrianCrossings[SUMOXMLDefinitions::getJunctionIDFromInternalEdge(fromID)];
867 732 : for (std::vector<Crossing>::iterator it = crossings.begin(); it != crossings.end(); ++it) {
868 581 : if (conn.toEdgeID == (*it).edgeID) {
869 151 : if (conn.tlID != "") {
870 105 : (*it).priority = true;
871 105 : (*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 1531 : } else if (from->func == SumoXMLEdgeFunc::CROSSING && myEdges[conn.toEdgeID]->func == SumoXMLEdgeFunc::WALKINGAREA) {
879 : // connection from crossing to walkingArea (set optional linkIndex2)
880 1026 : for (Crossing& c : myPedestrianCrossings[SUMOXMLDefinitions::getJunctionIDFromInternalEdge(fromID)]) {
881 576 : if (fromID == c.edgeID) {
882 150 : c.customTLIndex2 = attrs.getOpt<int>(SUMO_ATTR_TLLINKINDEX, nullptr, ok, -1);
883 : }
884 : }
885 : }
886 : }
887 : // determine walking area reference edges
888 68725 : 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 68733 : }
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 749 : NIImporter_SUMO::initTrafficLightLogic(const SUMOSAXAttributes& attrs, NBLoadedSUMOTLDef* currentTL) {
945 749 : if (currentTL) {
946 0 : WRITE_ERRORF(TL("Definition of tl-logic '%' was not finished."), currentTL->getID());
947 0 : return nullptr;
948 : }
949 749 : bool ok = true;
950 749 : std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
951 749 : SUMOTime offset = attrs.getOptOffsetReporting(SUMO_ATTR_OFFSET, id.c_str(), ok, 0);
952 749 : std::string programID = attrs.getOpt<std::string>(SUMO_ATTR_PROGRAMID, id.c_str(), ok, "<unknown>");
953 749 : std::string typeS = attrs.get<std::string>(SUMO_ATTR_TYPE, nullptr, ok);
954 : TrafficLightType type;
955 : if (SUMOXMLDefinitions::TrafficLightTypes.hasString(typeS)) {
956 749 : 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 749 : if (ok) {
962 749 : return new NBLoadedSUMOTLDef(id, programID, offset, type);
963 : } else {
964 : return nullptr;
965 : }
966 : }
967 :
968 :
969 : void
970 4773 : NIImporter_SUMO::addPhase(const SUMOSAXAttributes& attrs, NBLoadedSUMOTLDef* currentTL) {
971 4773 : if (!currentTL) {
972 0 : WRITE_ERROR(TL("found phase without tl-logic"));
973 0 : return;
974 : }
975 : const std::string& id = currentTL->getID();
976 4773 : bool ok = true;
977 4773 : std::string state = attrs.get<std::string>(SUMO_ATTR_STATE, id.c_str(), ok);
978 4773 : SUMOTime duration = TIME2STEPS(attrs.get<double>(SUMO_ATTR_DURATION, id.c_str(), ok));
979 4773 : 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 4774 : std::vector<int> nextPhases = attrs.getOpt<std::vector<int> >(SUMO_ATTR_NEXT, id.c_str(), ok);
985 4773 : const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, nullptr, ok);
986 : // Specific from actuated
987 4773 : const SUMOTime minDuration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_MINDURATION, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
988 4773 : const SUMOTime maxDuration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_MAXDURATION, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
989 4773 : const SUMOTime earliestEnd = attrs.getOptSUMOTimeReporting(SUMO_ATTR_EARLIEST_END, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
990 4773 : const SUMOTime latestEnd = attrs.getOptSUMOTimeReporting(SUMO_ATTR_LATEST_END, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
991 : // specific von NEMA
992 4773 : const SUMOTime vehExt = attrs.getOptSUMOTimeReporting(SUMO_ATTR_VEHICLEEXTENSION, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
993 4773 : const SUMOTime yellow = attrs.getOptSUMOTimeReporting(SUMO_ATTR_YELLOW, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
994 4773 : const SUMOTime red = attrs.getOptSUMOTimeReporting(SUMO_ATTR_RED, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
995 4773 : if (ok) {
996 4773 : currentTL->addPhase(duration, state, minDuration, maxDuration, earliestEnd, latestEnd, vehExt, yellow, red, nextPhases, name);
997 : }
998 4773 : }
999 :
1000 :
1001 : GeoConvHelper*
1002 1300 : NIImporter_SUMO::loadLocation(const SUMOSAXAttributes& attrs, bool setLoaded) {
1003 : // @todo refactor parsing of location since its duplicated in NLHandler and PCNetProjectionLoader
1004 1300 : bool ok = true;
1005 : GeoConvHelper* result = nullptr;
1006 1300 : const Position offset = attrs.get<Position>(SUMO_ATTR_NET_OFFSET, nullptr, ok);
1007 1300 : const Boundary convBoundary = attrs.get<Boundary>(SUMO_ATTR_CONV_BOUNDARY, nullptr, ok);
1008 1300 : const Boundary origBoundary = attrs.get<Boundary>(SUMO_ATTR_ORIG_BOUNDARY, nullptr, ok);
1009 1300 : const std::string proj = attrs.get<std::string>(SUMO_ATTR_ORIG_PROJ, nullptr, ok);
1010 1300 : if (ok) {
1011 1300 : result = new GeoConvHelper(proj, offset, origBoundary, convBoundary);
1012 1300 : result->resolveAbstractProjection();
1013 1300 : if (setLoaded) {
1014 1274 : GeoConvHelper::setLoaded(*result);
1015 : }
1016 : }
1017 1300 : return result;
1018 : }
1019 :
1020 :
1021 : Position
1022 20050 : NIImporter_SUMO::readPosition(const SUMOSAXAttributes& attrs, const std::string& id, bool& ok) {
1023 20050 : const double x = attrs.get<double>(SUMO_ATTR_X, id.c_str(), ok);
1024 20050 : const double y = attrs.get<double>(SUMO_ATTR_Y, id.c_str(), ok);
1025 20050 : const double z = attrs.getOpt<double>(SUMO_ATTR_Z, id.c_str(), ok, 0.);
1026 20050 : 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 : /****************************************************************************/
|