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 2373 : NIImporter_SUMO::loadNetwork(OptionsCont& oc, NBNetBuilder& nb) {
57 2373 : NIImporter_SUMO importer(nb);
58 2373 : importer._loadNetwork(oc);
59 2373 : }
60 :
61 :
62 : // ---------------------------------------------------------------------------
63 : // loader methods
64 : // ---------------------------------------------------------------------------
65 2373 : NIImporter_SUMO::NIImporter_SUMO(NBNetBuilder& nb)
66 : : SUMOSAXHandler("sumo-network"),
67 2373 : myNetBuilder(nb),
68 2373 : myNodeCont(nb.getNodeCont()),
69 2373 : myTLLCont(nb.getTLLogicCont()),
70 2373 : myTypesHandler(nb.getTypeCont()),
71 2373 : myCurrentEdge(nullptr),
72 2373 : myCurrentLane(nullptr),
73 2373 : myCurrentTL(nullptr),
74 2373 : myLocation(nullptr),
75 : myNetworkVersion(0, 0),
76 2373 : myHaveSeenInternalEdge(false),
77 2373 : myAmLefthand(false),
78 2373 : myChangeLefthand(false),
79 2373 : myCornerDetail(0),
80 2373 : myLinkDetail(-1),
81 2373 : myRectLaneCut(false),
82 2373 : myWalkingAreas(false),
83 2373 : myLimitTurnSpeed(-1),
84 2373 : myCheckLaneFoesAll(false),
85 2373 : myCheckLaneFoesRoundabout(true),
86 2373 : myTlsIgnoreInternalJunctionJam(false),
87 2373 : myDefaultSpreadType(toString(LaneSpreadFunction::RIGHT)),
88 2373 : myGeomAvoidOverlap(false),
89 2373 : myJunctionsHigherSpeed(false),
90 2373 : myInternalJunctionsVehicleWidth(OptionsCont::getOptions().getFloat("internal-junctions.vehicle-width")),
91 2373 : myJunctionsMinimalShape(OptionsCont::getOptions().getBool("junctions.minimal-shape")),
92 7119 : myJunctionsEndpointShape(OptionsCont::getOptions().getBool("junctions.endpoint-shape")) {
93 2373 : }
94 :
95 :
96 2373 : NIImporter_SUMO::~NIImporter_SUMO() {
97 72682 : for (std::map<std::string, EdgeAttrs*>::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
98 70309 : EdgeAttrs* ed = (*i).second;
99 155869 : for (std::vector<LaneAttrs*>::const_iterator j = ed->lanes.begin(); j != ed->lanes.end(); ++j) {
100 85560 : delete *j;
101 : }
102 : delete ed;
103 : }
104 2373 : delete myLocation;
105 2373 : if (myCurrentEdge != nullptr) {
106 90 : for (LaneAttrs* const lane : myCurrentEdge->lanes) {
107 30 : delete lane;
108 : }
109 60 : delete myCurrentEdge;
110 : }
111 7119 : }
112 :
113 :
114 : void
115 2373 : NIImporter_SUMO::_loadNetwork(OptionsCont& oc) {
116 : // check whether the option is set (properly)
117 4746 : if (!oc.isUsableFileList("sumo-net-file")) {
118 1660 : return;
119 : }
120 1426 : const std::vector<std::string> discardableParams = oc.getStringVector("discard-params");
121 : myDiscardableParams.insert(discardableParams.begin(), discardableParams.end());
122 : // parse file(s)
123 1426 : const std::vector<std::string> files = oc.getStringVector("sumo-net-file");
124 1436 : for (std::vector<std::string>::const_iterator file = files.begin(); file != files.end(); ++file) {
125 1446 : if (!FileHelpers::isReadable(*file)) {
126 0 : WRITE_ERRORF(TL("Could not open sumo-net-file '%'."), *file);
127 : return;
128 : }
129 723 : setFileName(*file);
130 2169 : const long before = PROGRESS_BEGIN_TIME_MESSAGE("Parsing sumo-net from '" + *file + "'");
131 723 : XMLSubSys::runParser(*this, *file, true);
132 723 : PROGRESS_TIME_MESSAGE(before);
133 : }
134 : // build edges
135 1426 : const double maxSegmentLength = oc.getFloat("geometry.max-segment-length");
136 71022 : for (std::map<std::string, EdgeAttrs*>::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
137 70309 : EdgeAttrs* ed = (*i).second;
138 : // skip internal edges
139 70309 : if (ed->func == SumoXMLEdgeFunc::INTERNAL || ed->func == SumoXMLEdgeFunc::CROSSING || ed->func == SumoXMLEdgeFunc::WALKINGAREA) {
140 32671 : continue;
141 : }
142 : // get and check the nodes
143 37638 : NBNode* from = myNodeCont.retrieve(ed->fromNode);
144 37638 : NBNode* to = myNodeCont.retrieve(ed->toNode);
145 37638 : if (from == nullptr) {
146 228 : WRITE_ERRORF(TL("Edge's '%' from-node '%' is not known."), ed->id, ed->fromNode);
147 76 : continue;
148 : }
149 37562 : if (to == nullptr) {
150 114 : WRITE_ERRORF(TL("Edge's '%' to-node '%' is not known."), ed->id, ed->toNode);
151 38 : continue;
152 : }
153 37524 : 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 37524 : 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 37524 : 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 112572 : ed->shape, ed->lsf, ed->streetName, "", true); // always use tryIgnoreNodePositions to keep original shape
169 37524 : e->setLoadedLength(ed->length);
170 37524 : e->updateParameters(ed->getParametersMap());
171 37524 : e->setDistance(ed->distance);
172 37524 : 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 37524 : ed->builtEdge = myNetBuilder.getEdgeCont().retrieve(ed->id);
178 37524 : if (ed->builtEdge != nullptr) {
179 37354 : ed->builtEdge->setEdgeStopOffset(-1, ed->edgeStopOffset);
180 37354 : ed->builtEdge->setBidi(ed->bidi != "");
181 37354 : ed->builtEdge->setRoutingType(ed->routingType);
182 : }
183 : }
184 : // assign further lane attributes (edges are built)
185 : EdgeVector toRemove;
186 1426 : const bool dismissVclasses = oc.getBool("dismiss-vclasses");
187 71022 : for (std::map<std::string, EdgeAttrs*>::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
188 70309 : EdgeAttrs* ed = (*i).second;
189 70309 : NBEdge* nbe = ed->builtEdge;
190 70309 : if (nbe == nullptr) { // inner edge or removed by explicit list, vclass, ...
191 32955 : continue;
192 : }
193 : const SumoXMLNodeType toType = nbe->getToNode()->getType();
194 85777 : for (int fromLaneIndex = 0; fromLaneIndex < (int) ed->lanes.size(); ++fromLaneIndex) {
195 48423 : LaneAttrs* lane = ed->lanes[fromLaneIndex];
196 : // connections
197 : const std::vector<Connection>& connections = lane->connections;
198 121060 : for (const Connection& c : connections) {
199 72637 : if (myEdges.count(c.toEdgeID) == 0) {
200 66 : WRITE_ERRORF(TL("Unknown edge '%' given in connection."), c.toEdgeID);
201 22 : continue;
202 : }
203 72615 : NBEdge* toEdge = myEdges[c.toEdgeID]->builtEdge;
204 72615 : if (toEdge == nullptr) { // removed by explicit list, vclass, ...
205 424 : continue;
206 : }
207 72191 : 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 72170 : bool uncontrolled = c.uncontrolled;
213 :
214 136680 : if ((NBNode::isTrafficLight(toType) || toType == SumoXMLNodeType::RAIL_SIGNAL)
215 73093 : && c.tlLinkIndex == NBConnection::InvalidTlIndex) {
216 : uncontrolled = true;
217 : }
218 144340 : nbe->addLane2LaneConnection(
219 72170 : fromLaneIndex, toEdge, c.toLaneIdx, NBEdge::Lane2LaneInfoType::VALIDATED,
220 72170 : true, c.mayDefinitelyPass, c.keepClear ? KEEPCLEAR_TRUE : KEEPCLEAR_FALSE,
221 72170 : c.contPos, c.visibility, c.speed, c.friction, c.customLength, c.customShape, uncontrolled, c.permissions, c.indirectLeft, c.edgeType, c.changeLeft, c.changeRight);
222 72170 : 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 72170 : if (c.tlID != "" && myRailSignals.count(c.tlID) == 0) {
227 7645 : const std::map<std::string, NBTrafficLightDefinition*>& programs = myTLLCont.getPrograms(c.tlID);
228 7645 : if (programs.size() > 0) {
229 : std::map<std::string, NBTrafficLightDefinition*>::const_iterator it;
230 15308 : for (it = programs.begin(); it != programs.end(); it++) {
231 7663 : NBLoadedSUMOTLDef* tlDef = dynamic_cast<NBLoadedSUMOTLDef*>(it->second);
232 7663 : if (tlDef) {
233 7663 : 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 48423 : if (!dismissVclasses) {
245 48417 : nbe->setPermissions(parseVehicleClasses(lane->allow, lane->disallow, myNetworkVersion), fromLaneIndex);
246 : }
247 96846 : nbe->setPermittedChanging(fromLaneIndex, parseVehicleClasses(lane->changeLeft, ""), parseVehicleClasses(lane->changeRight, ""));
248 : // width, offset
249 48423 : nbe->setLaneWidth(fromLaneIndex, lane->width);
250 48423 : nbe->setEndOffset(fromLaneIndex, lane->endOffset);
251 48423 : nbe->setSpeed(fromLaneIndex, lane->maxSpeed);
252 48423 : nbe->setFriction(fromLaneIndex, lane->friction);
253 48423 : nbe->setAcceleration(fromLaneIndex, lane->accelRamp);
254 48423 : nbe->getLaneStruct(fromLaneIndex).oppositeID = lane->oppositeID;
255 48423 : nbe->getLaneStruct(fromLaneIndex).type = lane->type;
256 48423 : nbe->getLaneStruct(fromLaneIndex).updateParameters(lane->getParametersMap());
257 48423 : if (lane->customShape) {
258 5 : nbe->setLaneShape(fromLaneIndex, lane->shape);
259 : }
260 : // stop offset for lane
261 : bool stopOffsetSet = false;
262 48423 : 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 48397 : nbe->setEdgeStopOffset(fromLaneIndex, nbe->getEdgeStopOffset());
269 : }
270 : }
271 37354 : nbe->declareConnectionsAsLoaded();
272 37354 : if (!nbe->hasLaneSpecificWidth() && nbe->getLanes()[0].width != NBEdge::UNSPECIFIED_WIDTH) {
273 416 : nbe->setLaneWidth(-1, nbe->getLaneWidth(0));
274 : }
275 37354 : if (!nbe->hasLaneSpecificEndOffset() && nbe->getEndOffset(0) != NBEdge::UNSPECIFIED_OFFSET) {
276 0 : nbe->setEndOffset(-1, nbe->getEndOffset(0));
277 : }
278 37354 : if (!nbe->hasLaneSpecificStopOffsets() && nbe->getEdgeStopOffset().isDefined()) {
279 4 : nbe->setEdgeStopOffset(-1, nbe->getEdgeStopOffset());
280 : }
281 : // check again after permissions are set
282 37354 : if (myNetBuilder.getEdgeCont().ignoreFilterMatch(nbe)) {
283 12 : myNetBuilder.getEdgeCont().ignore(nbe->getID());
284 12 : toRemove.push_back(nbe);
285 : }
286 : }
287 725 : for (EdgeVector::iterator i = toRemove.begin(); i != toRemove.end(); ++i) {
288 12 : myNetBuilder.getEdgeCont().erase(myNetBuilder.getDistrictCont(), *i);
289 : }
290 : // insert loaded prohibitions
291 733 : 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 1597 : if (oc.isWriteable("no-internal-links") && oc.isDefault("no-internal-links") && !myHaveSeenInternalEdge) {
312 770 : oc.set("no-internal-links", "true");
313 : }
314 1423 : if (oc.isWriteable("lefthand") && oc.isDefault("lefthand") && myAmLefthand) {
315 14 : oc.set("lefthand", toString(myAmLefthand));
316 : }
317 2454 : 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 2852 : 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 2803 : 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 2844 : if (oc.isWriteable("walkingareas") && oc.isDefault("walkingareas") && oc.getBool("walkingareas") != myWalkingAreas) {
327 16 : oc.set("walkingareas", toString(myWalkingAreas));
328 : }
329 2567 : 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 2851 : 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 2852 : 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 2851 : 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 3445 : if (oc.isWriteable("default.spreadtype") && oc.isDefault("default.spreadtype") && oc.getString("default.spreadtype") != myDefaultSpreadType) {
342 0 : oc.set("default.spreadtype", myDefaultSpreadType);
343 : }
344 713 : 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 2852 : 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 2851 : 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 2851 : 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 2851 : 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 713 : if (!deprecatedVehicleClassesSeen.empty()) {
360 2 : WRITE_WARNINGF(TL("Deprecated vehicle class(es) '%' in input network."), toString(deprecatedVehicleClassesSeen));
361 : deprecatedVehicleClassesSeen.clear();
362 : }
363 1426 : 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 777 : for (const std::vector<std::string>& ra : myRoundabouts) {
414 : EdgeSet roundabout;
415 361 : for (const std::string& edgeID : ra) {
416 297 : NBEdge* edge = myNetBuilder.getEdgeCont().retrieve(edgeID);
417 297 : if (edge == nullptr) {
418 18 : 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 64 : myNetBuilder.getEdgeCont().addRoundabout(roundabout);
426 : }
427 713 : }
428 :
429 :
430 : void
431 394275 : 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 394275 : switch (element) {
448 723 : case SUMO_TAG_NET: {
449 : bool ok;
450 723 : myNetworkVersion = StringUtils::toVersion(attrs.get<std::string>(SUMO_ATTR_VERSION, nullptr, ok, false));
451 723 : myAmLefthand = attrs.getOpt<bool>(SUMO_ATTR_LEFTHAND, nullptr, ok, false);
452 723 : myCornerDetail = attrs.getOpt<int>(SUMO_ATTR_CORNERDETAIL, nullptr, ok, 0);
453 723 : myLinkDetail = attrs.getOpt<int>(SUMO_ATTR_LINKDETAIL, nullptr, ok, -1);
454 723 : myRectLaneCut = attrs.getOpt<bool>(SUMO_ATTR_RECTANGULAR_LANE_CUT, nullptr, ok, false);
455 723 : myWalkingAreas = attrs.getOpt<bool>(SUMO_ATTR_WALKINGAREAS, nullptr, ok, false);
456 723 : myLimitTurnSpeed = attrs.getOpt<double>(SUMO_ATTR_LIMIT_TURN_SPEED, nullptr, ok, -1);
457 723 : myCheckLaneFoesAll = attrs.getOpt<bool>(SUMO_ATTR_CHECKLANEFOES_ALL, nullptr, ok, false);
458 723 : myCheckLaneFoesRoundabout = attrs.getOpt<bool>(SUMO_ATTR_CHECKLANEFOES_ROUNDABOUT, nullptr, ok, true);
459 723 : myTlsIgnoreInternalJunctionJam = attrs.getOpt<bool>(SUMO_ATTR_TLS_IGNORE_INTERNAL_JUNCTION_JAM, nullptr, ok, false);
460 723 : myDefaultSpreadType = attrs.getOpt<std::string>(SUMO_ATTR_SPREADTYPE, nullptr, ok, myDefaultSpreadType);
461 723 : myGeomAvoidOverlap = attrs.getOpt<bool>(SUMO_ATTR_AVOID_OVERLAP, nullptr, ok, myGeomAvoidOverlap);
462 723 : myJunctionsHigherSpeed = attrs.getOpt<bool>(SUMO_ATTR_HIGHER_SPEED, nullptr, ok, myJunctionsHigherSpeed);
463 723 : myInternalJunctionsVehicleWidth = attrs.getOpt<double>(SUMO_ATTR_INTERNAL_JUNCTIONS_VEHICLE_WIDTH, nullptr, ok, myInternalJunctionsVehicleWidth);
464 723 : myJunctionsMinimalShape = attrs.getOpt<bool>(SUMO_ATTR_JUNCTIONS_MINIMAL_SHAPE, nullptr, ok, myJunctionsMinimalShape);
465 723 : myJunctionsEndpointShape = attrs.getOpt<bool>(SUMO_ATTR_JUNCTIONS_ENDPOINT_SHAPE, nullptr, ok, myJunctionsEndpointShape);
466 : // derived
467 723 : const OptionsCont& oc = OptionsCont::getOptions();
468 726 : myChangeLefthand = !oc.isDefault("lefthand") && (oc.getBool("lefthand") != myAmLefthand);
469 :
470 : break;
471 : }
472 70420 : case SUMO_TAG_EDGE:
473 70420 : addEdge(attrs);
474 70420 : break;
475 85667 : case SUMO_TAG_LANE:
476 85667 : addLane(attrs);
477 85667 : 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 24272 : case SUMO_TAG_JUNCTION:
487 24272 : addJunction(attrs);
488 24271 : break;
489 72912 : case SUMO_TAG_REQUEST:
490 72912 : addRequest(attrs);
491 72912 : break;
492 110011 : case SUMO_TAG_CONNECTION:
493 110011 : addConnection(attrs);
494 110011 : break;
495 805 : case SUMO_TAG_TLLOGIC:
496 805 : myCurrentTL = initTrafficLightLogic(attrs, myCurrentTL);
497 805 : if (myCurrentTL) {
498 805 : myLastParameterised.push_back(myCurrentTL);
499 : }
500 : break;
501 4799 : case SUMO_TAG_PHASE:
502 4799 : addPhase(attrs, myCurrentTL);
503 4799 : break;
504 723 : case SUMO_TAG_LOCATION:
505 723 : delete myLocation;
506 723 : myLocation = loadLocation(attrs);
507 723 : break;
508 20 : case SUMO_TAG_PROHIBITION:
509 20 : addProhibition(attrs);
510 20 : break;
511 64 : case SUMO_TAG_ROUNDABOUT:
512 64 : addRoundabout(attrs);
513 64 : break;
514 21731 : case SUMO_TAG_PARAM:
515 21731 : if (myLastParameterised.size() != 0) {
516 21731 : bool ok = true;
517 21731 : 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 20976 : const std::string val = attrs.hasAttribute(SUMO_ATTR_VALUE) ? attrs.getString(SUMO_ATTR_VALUE) : "";
521 20976 : myLastParameterised.back()->setParameter(key, val);
522 : }
523 : }
524 : break;
525 2015 : default:
526 2015 : myTypesHandler.myStartElement(element, attrs);
527 2015 : break;
528 : }
529 394274 : }
530 :
531 :
532 : void
533 394128 : NIImporter_SUMO::myEndElement(int element) {
534 394128 : switch (element) {
535 70360 : case SUMO_TAG_EDGE:
536 70360 : if (myCurrentEdge != nullptr) {
537 70356 : 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 70309 : myEdges[myCurrentEdge->id] = myCurrentEdge;
545 : }
546 70356 : myCurrentEdge = nullptr;
547 : myLastParameterised.pop_back();
548 : }
549 : break;
550 85667 : case SUMO_TAG_LANE:
551 85667 : if (myCurrentEdge != nullptr && myCurrentLane != nullptr) {
552 85651 : myCurrentEdge->maxSpeed = MAX2(myCurrentEdge->maxSpeed, myCurrentLane->maxSpeed);
553 85651 : myCurrentEdge->lanes.push_back(myCurrentLane);
554 : myLastParameterised.pop_back();
555 : }
556 85667 : myCurrentLane = nullptr;
557 85667 : break;
558 805 : case SUMO_TAG_TLLOGIC:
559 805 : if (myCurrentTL == nullptr) {
560 0 : WRITE_ERROR(TL("Unmatched closing tag for tl-logic."));
561 : } else {
562 805 : 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 805 : myCurrentTL = nullptr;
567 : myLastParameterised.pop_back();
568 : }
569 : break;
570 24271 : case SUMO_TAG_JUNCTION:
571 24271 : if (myCurrentJunction.node != nullptr) {
572 : myLastParameterised.pop_back();
573 : }
574 : break;
575 110011 : case SUMO_TAG_CONNECTION:
576 : // !!! this just avoids a crash but is not a real check that it was a connection
577 110011 : if (!myLastParameterised.empty()) {
578 : myLastParameterised.pop_back();
579 : }
580 : break;
581 : default:
582 : break;
583 : }
584 394128 : }
585 :
586 :
587 : void
588 70420 : NIImporter_SUMO::addEdge(const SUMOSAXAttributes& attrs) {
589 : // get the id, report an error if not given or empty...
590 70420 : bool ok = true;
591 70420 : const std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
592 70420 : if (!ok) {
593 : return;
594 : }
595 70416 : myCurrentEdge = new EdgeAttrs();
596 70416 : myLastParameterised.push_back(myCurrentEdge);
597 70416 : myCurrentEdge->builtEdge = nullptr;
598 70416 : myCurrentEdge->id = id;
599 : // get the function
600 70416 : myCurrentEdge->func = attrs.getOpt<SumoXMLEdgeFunc>(SUMO_ATTR_FUNCTION, id.c_str(), ok, SumoXMLEdgeFunc::NORMAL);
601 70416 : 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 70416 : } else if (myCurrentEdge->func == SumoXMLEdgeFunc::INTERNAL || myCurrentEdge->func == SumoXMLEdgeFunc::WALKINGAREA) {
608 32536 : myHaveSeenInternalEdge = true;
609 32536 : return; // skip internal edges
610 : }
611 : // get the origin and the destination node
612 37730 : myCurrentEdge->fromNode = attrs.getOpt<std::string>(SUMO_ATTR_FROM, id.c_str(), ok, "");
613 75460 : myCurrentEdge->toNode = attrs.getOpt<std::string>(SUMO_ATTR_TO, id.c_str(), ok, "");
614 37730 : myCurrentEdge->priority = attrs.getOpt<int>(SUMO_ATTR_PRIORITY, id.c_str(), ok, -1);
615 37730 : myCurrentEdge->type = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, "");
616 37730 : myCurrentEdge->routingType = attrs.getOpt<std::string>(SUMO_ATTR_ROUTINGTYPE, id.c_str(), ok, "");
617 37730 : myCurrentEdge->shape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, id.c_str(), ok, PositionVector());
618 37730 : NBNetBuilder::transformCoordinates(myCurrentEdge->shape, true, myLocation);
619 37730 : myCurrentEdge->length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, id.c_str(), ok, NBEdge::UNSPECIFIED_LOADED_LENGTH);
620 37730 : myCurrentEdge->maxSpeed = 0;
621 75460 : myCurrentEdge->streetName = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
622 37730 : myCurrentEdge->distance = attrs.getOpt<double>(SUMO_ATTR_DISTANCE, id.c_str(), ok, 0);
623 37730 : myCurrentEdge->bidi = attrs.getOpt<std::string>(SUMO_ATTR_BIDI, id.c_str(), ok, "");
624 58131 : if (myCurrentEdge->streetName != "" && OptionsCont::getOptions().isDefault("output.street-names")) {
625 178 : OptionsCont::getOptions().set("output.street-names", "true");
626 : }
627 :
628 75460 : std::string lsfS = attrs.getOpt<std::string>(SUMO_ATTR_SPREADTYPE, id.c_str(), ok, myDefaultSpreadType);
629 : if (SUMOXMLDefinitions::LaneSpreadFunctions.hasString(lsfS)) {
630 37730 : 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 85667 : NIImporter_SUMO::addLane(const SUMOSAXAttributes& attrs) {
639 85667 : bool ok = true;
640 85667 : std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
641 85667 : if (!ok) {
642 : return;
643 : }
644 85659 : if (myCurrentEdge == nullptr) {
645 24 : WRITE_ERRORF(TL("Found lane '%' not within edge element."), id);
646 8 : return;
647 : }
648 171302 : const std::string expectedID = myCurrentEdge->id + "_" + toString(myCurrentEdge->lanes.size());
649 85651 : if (id != expectedID) {
650 108 : WRITE_WARNINGF(TL("Renaming lane '%' to '%'."), id, expectedID);
651 : }
652 85651 : myCurrentLane = new LaneAttrs();
653 85651 : myLastParameterised.push_back(myCurrentLane);
654 85651 : myCurrentLane->customShape = attrs.getOpt<bool>(SUMO_ATTR_CUSTOMSHAPE, nullptr, ok, false);
655 85651 : myCurrentLane->shape = attrs.get<PositionVector>(SUMO_ATTR_SHAPE, id.c_str(), ok);
656 85651 : myCurrentLane->width = attrs.getOpt<double>(SUMO_ATTR_WIDTH, id.c_str(), ok, (double) NBEdge::UNSPECIFIED_WIDTH);
657 85651 : myCurrentLane->type = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, "");
658 85651 : 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 85501 : } 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 85153 : } else if (myCurrentEdge->func == SumoXMLEdgeFunc::INTERNAL) {
680 : return; // skip internal edges
681 : }
682 98126 : if (attrs.hasAttribute("maxspeed")) {
683 : // !!! deprecated
684 0 : myCurrentLane->maxSpeed = attrs.getFloat("maxspeed");
685 : } else {
686 49063 : myCurrentLane->maxSpeed = attrs.get<double>(SUMO_ATTR_SPEED, id.c_str(), ok);
687 : }
688 49063 : myCurrentLane->friction = attrs.getOpt<double>(SUMO_ATTR_FRICTION, id.c_str(), ok, NBEdge::UNSPECIFIED_FRICTION, false); //sets 1 on empty
689 : try {
690 98126 : 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 49063 : myCurrentLane->disallow = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, id.c_str(), ok, "");
696 49063 : myCurrentLane->endOffset = attrs.getOpt<double>(SUMO_ATTR_ENDOFFSET, id.c_str(), ok, (double) NBEdge::UNSPECIFIED_OFFSET);
697 49063 : myCurrentLane->accelRamp = attrs.getOpt<bool>(SUMO_ATTR_ACCELERATION, id.c_str(), ok, false);
698 49063 : myCurrentLane->changeLeft = attrs.getOpt<std::string>(SUMO_ATTR_CHANGE_LEFT, id.c_str(), ok, "");
699 49063 : myCurrentLane->changeRight = attrs.getOpt<std::string>(SUMO_ATTR_CHANGE_RIGHT, id.c_str(), ok, "");
700 49063 : 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 49063 : 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 24272 : NIImporter_SUMO::addJunction(const SUMOSAXAttributes& attrs) {
734 : // get the id, report an error if not given or empty...
735 24272 : myCurrentJunction.node = nullptr;
736 : myCurrentJunction.response.clear();
737 24272 : bool ok = true;
738 24272 : std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
739 24272 : if (!ok) {
740 : return;
741 : }
742 24266 : if (id[0] == ':') { // internal node
743 : return;
744 : }
745 21494 : SumoXMLNodeType type = attrs.getOpt<SumoXMLNodeType>(SUMO_ATTR_TYPE, id.c_str(), ok, SumoXMLNodeType::UNKNOWN);
746 21494 : if (ok) {
747 21488 : 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 18883 : } 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 21494 : Position pos = readPosition(attrs, id, ok);
757 21494 : NBNetBuilder::transformCoordinate(pos, true, myLocation);
758 21494 : NBNode* node = new NBNode(id, pos, type);
759 21493 : 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 21471 : myLastParameterised.push_back(node);
766 : }
767 21471 : myCurrentJunction.node = node;
768 : // set optional radius
769 21471 : 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 21471 : 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 21471 : if (type == SumoXMLNodeType::RAIL_SIGNAL || type == SumoXMLNodeType::RAIL_CROSSING) {
779 : // both types of nodes come without a tlLogic
780 : myRailSignals.insert(id);
781 : }
782 21471 : node->setRightOfWay(attrs.getOpt<RightOfWay>(SUMO_ATTR_RIGHT_OF_WAY, id.c_str(), ok, node->getRightOfWay()));
783 21471 : node->setFringeType(attrs.getOpt<FringeType>(SUMO_ATTR_FRINGE, id.c_str(), ok, node->getFringeType()));
784 21471 : node->setRoundaboutType(attrs.getOpt<RoundaboutType>(SUMO_ATTR_ROUNDABOUT, id.c_str(), ok, node->getRoundaboutType()));
785 21471 : 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 72912 : NIImporter_SUMO::addRequest(const SUMOSAXAttributes& attrs) {
793 72912 : if (myCurrentJunction.node != nullptr) {
794 72842 : bool ok = true;
795 145684 : myCurrentJunction.response.push_back(attrs.get<std::string>(SUMO_ATTR_RESPONSE, nullptr, ok));
796 : }
797 72912 : }
798 :
799 :
800 : void
801 110011 : NIImporter_SUMO::addConnection(const SUMOSAXAttributes& attrs) {
802 110011 : bool ok = true;
803 110011 : 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 109989 : EdgeAttrs* from = myEdges[fromID];
809 109989 : if (from->func == SumoXMLEdgeFunc::INTERNAL) {
810 : // internal junction connection
811 : return;
812 : }
813 :
814 73781 : Connection conn;
815 73781 : conn.toEdgeID = attrs.get<std::string>(SUMO_ATTR_TO, nullptr, ok);
816 73781 : int fromLaneIdx = attrs.get<int>(SUMO_ATTR_FROM_LANE, nullptr, ok);
817 73781 : conn.toLaneIdx = attrs.get<int>(SUMO_ATTR_TO_LANE, nullptr, ok);
818 73781 : conn.tlID = attrs.getOpt<std::string>(SUMO_ATTR_TLID, nullptr, ok, "");
819 73781 : conn.mayDefinitelyPass = attrs.getOpt<bool>(SUMO_ATTR_PASS, nullptr, ok, false);
820 73781 : conn.keepClear = attrs.getOpt<bool>(SUMO_ATTR_KEEP_CLEAR, nullptr, ok, true);
821 73781 : conn.indirectLeft = attrs.getOpt<bool>(SUMO_ATTR_INDIRECT, nullptr, ok, false);
822 73781 : conn.edgeType = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, nullptr, ok, "");
823 73781 : double contPos = NBEdge::UNSPECIFIED_CONTPOS;
824 147562 : if (OptionsCont::getOptions().isSet("default.connection.cont-pos")) {
825 147562 : contPos = OptionsCont::getOptions().getFloat("default.connection.cont-pos");
826 : }
827 73781 : conn.contPos = attrs.getOpt<double>(SUMO_ATTR_CONTPOS, nullptr, ok, contPos);
828 73781 : conn.visibility = attrs.getOpt<double>(SUMO_ATTR_VISIBILITY_DISTANCE, nullptr, ok, NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE);
829 73781 : std::string allow = attrs.getOpt<std::string>(SUMO_ATTR_ALLOW, nullptr, ok, "", false);
830 73781 : std::string disallow = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, nullptr, ok, "", false);
831 73781 : if (allow == "" && disallow == "") {
832 73779 : conn.permissions = SVC_UNSPECIFIED;
833 : } else {
834 2 : conn.permissions = parseVehicleClasses(allow, disallow, myNetworkVersion);
835 : }
836 73781 : 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 73780 : conn.changeLeft = SVC_UNSPECIFIED;
840 : }
841 73781 : 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 73781 : conn.changeRight = SVC_UNSPECIFIED;
845 : }
846 73781 : if (myChangeLefthand) {
847 : std::swap(conn.changeLeft, conn.changeRight);
848 : }
849 73781 : conn.speed = attrs.getOpt<double>(SUMO_ATTR_SPEED, nullptr, ok, NBEdge::UNSPECIFIED_SPEED);
850 73781 : conn.friction = attrs.getOpt<double>(SUMO_ATTR_FRICTION, nullptr, ok, NBEdge::UNSPECIFIED_FRICTION);
851 73781 : conn.customLength = attrs.getOpt<double>(SUMO_ATTR_LENGTH, nullptr, ok, NBEdge::UNSPECIFIED_LOADED_LENGTH);
852 147562 : conn.customShape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, nullptr, ok, PositionVector::EMPTY);
853 73781 : NBNetBuilder::transformCoordinates(conn.customShape, false, myLocation);
854 73781 : conn.uncontrolled = attrs.getOpt<bool>(SUMO_ATTR_UNCONTROLLED, nullptr, ok, NBEdge::UNSPECIFIED_CONNECTION_UNCONTROLLED, false);
855 73781 : if (conn.tlID != "") {
856 8679 : conn.tlLinkIndex = attrs.get<int>(SUMO_ATTR_TLLINKINDEX, nullptr, ok);
857 8679 : conn.tlLinkIndex2 = attrs.getOpt<int>(SUMO_ATTR_TLLINKINDEX2, nullptr, ok, -1);
858 : } else {
859 65102 : conn.tlLinkIndex = NBConnection::InvalidTlIndex;
860 : }
861 73781 : if ((int)from->lanes.size() <= fromLaneIdx) {
862 24 : WRITE_ERRORF(TL("Invalid lane index '%' for connection from '%'."), toString(fromLaneIdx), fromID);
863 : return;
864 : }
865 73773 : from->lanes[fromLaneIdx]->connections.push_back(conn);
866 73773 : myLastParameterised.push_back(&from->lanes[fromLaneIdx]->connections.back());
867 :
868 : // determine crossing priority and tlIndex
869 73773 : 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 73773 : 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 73781 : }
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 805 : NIImporter_SUMO::initTrafficLightLogic(const SUMOSAXAttributes& attrs, NBLoadedSUMOTLDef* currentTL) {
951 805 : if (currentTL) {
952 0 : WRITE_ERRORF(TL("Definition of tl-logic '%' was not finished."), currentTL->getID());
953 0 : return nullptr;
954 : }
955 805 : bool ok = true;
956 805 : std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
957 805 : SUMOTime offset = attrs.getOptOffsetReporting(SUMO_ATTR_OFFSET, id.c_str(), ok, 0);
958 805 : std::string programID = attrs.getOpt<std::string>(SUMO_ATTR_PROGRAMID, id.c_str(), ok, "<unknown>");
959 805 : std::string typeS = attrs.get<std::string>(SUMO_ATTR_TYPE, nullptr, ok);
960 : TrafficLightType type;
961 : if (SUMOXMLDefinitions::TrafficLightTypes.hasString(typeS)) {
962 805 : 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 805 : if (ok) {
968 805 : return new NBLoadedSUMOTLDef(id, programID, offset, type);
969 : } else {
970 : return nullptr;
971 : }
972 : }
973 :
974 :
975 : void
976 5043 : NIImporter_SUMO::addPhase(const SUMOSAXAttributes& attrs, NBLoadedSUMOTLDef* currentTL) {
977 5043 : if (!currentTL) {
978 0 : WRITE_ERROR(TL("found phase without tl-logic"));
979 0 : return;
980 : }
981 : const std::string& id = currentTL->getID();
982 5043 : bool ok = true;
983 5043 : std::string state = attrs.get<std::string>(SUMO_ATTR_STATE, id.c_str(), ok);
984 5043 : SUMOTime duration = TIME2STEPS(attrs.get<double>(SUMO_ATTR_DURATION, id.c_str(), ok));
985 5043 : 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 5044 : std::vector<int> nextPhases = attrs.getOpt<std::vector<int> >(SUMO_ATTR_NEXT, id.c_str(), ok);
991 5043 : const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, nullptr, ok);
992 : // Specific from actuated
993 5043 : const SUMOTime minDuration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_MINDURATION, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
994 5043 : const SUMOTime maxDuration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_MAXDURATION, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
995 5043 : const SUMOTime earliestEnd = attrs.getOptSUMOTimeReporting(SUMO_ATTR_EARLIEST_END, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
996 5043 : const SUMOTime latestEnd = attrs.getOptSUMOTimeReporting(SUMO_ATTR_LATEST_END, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
997 : // specific von NEMA
998 5043 : const SUMOTime vehExt = attrs.getOptSUMOTimeReporting(SUMO_ATTR_VEHICLEEXTENSION, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
999 5043 : const SUMOTime yellow = attrs.getOptSUMOTimeReporting(SUMO_ATTR_YELLOW, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
1000 5043 : const SUMOTime red = attrs.getOptSUMOTimeReporting(SUMO_ATTR_RED, id.c_str(), ok, NBTrafficLightDefinition::UNSPECIFIED_DURATION);
1001 5043 : if (ok) {
1002 5043 : currentTL->addPhase(duration, state, minDuration, maxDuration, earliestEnd, latestEnd, vehExt, yellow, red, nextPhases, name);
1003 : }
1004 5043 : }
1005 :
1006 :
1007 : GeoConvHelper*
1008 1319 : NIImporter_SUMO::loadLocation(const SUMOSAXAttributes& attrs, bool setLoaded) {
1009 : // @todo refactor parsing of location since its duplicated in NLHandler and PCNetProjectionLoader
1010 1319 : bool ok = true;
1011 : GeoConvHelper* result = nullptr;
1012 1319 : const Position offset = attrs.get<Position>(SUMO_ATTR_NET_OFFSET, nullptr, ok);
1013 1319 : const Boundary convBoundary = attrs.get<Boundary>(SUMO_ATTR_CONV_BOUNDARY, nullptr, ok);
1014 1319 : const Boundary origBoundary = attrs.get<Boundary>(SUMO_ATTR_ORIG_BOUNDARY, nullptr, ok);
1015 1319 : const std::string proj = attrs.get<std::string>(SUMO_ATTR_ORIG_PROJ, nullptr, ok);
1016 1319 : if (ok) {
1017 1319 : result = new GeoConvHelper(proj, offset, origBoundary, convBoundary);
1018 1319 : result->resolveAbstractProjection();
1019 1319 : if (setLoaded) {
1020 1292 : GeoConvHelper::setLoaded(*result);
1021 : }
1022 : }
1023 1319 : return result;
1024 : }
1025 :
1026 :
1027 : Position
1028 21494 : NIImporter_SUMO::readPosition(const SUMOSAXAttributes& attrs, const std::string& id, bool& ok) {
1029 21494 : const double x = attrs.get<double>(SUMO_ATTR_X, id.c_str(), ok);
1030 21494 : const double y = attrs.get<double>(SUMO_ATTR_Y, id.c_str(), ok);
1031 21494 : const double z = attrs.getOpt<double>(SUMO_ATTR_Z, id.c_str(), ok, 0.);
1032 21494 : 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 64 : NIImporter_SUMO::addRoundabout(const SUMOSAXAttributes& attrs) {
1060 64 : bool ok = true;
1061 64 : const std::vector<std::string>& edgeIDs = attrs.get<std::vector<std::string> >(SUMO_ATTR_EDGES, nullptr, ok);
1062 64 : if (ok) {
1063 64 : myRoundabouts.push_back(edgeIDs);
1064 : }
1065 64 : }
1066 :
1067 :
1068 : /****************************************************************************/
|