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