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