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