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    NLHandler.cpp
11 /// @author  Daniel Krajzewicz
12 /// @author  Jakob Erdmann
13 /// @author  Clemens Honomichl
14 /// @author  Sascha Krieg
15 /// @author  Michael Behrisch
16 /// @author  Felix Brack
17 /// @date    Mon, 9 Jul 2001
18 /// @version $Id$
19 ///
20 // The XML-Handler for network loading
21 /****************************************************************************/
22 // ===========================================================================
23 // included modules
24 // ===========================================================================
25 #include <config.h>
26 
27 #include <string>
28 #include "NLHandler.h"
29 #include "NLEdgeControlBuilder.h"
30 #include "NLJunctionControlBuilder.h"
31 #include "NLDetectorBuilder.h"
32 #include "NLTriggerBuilder.h"
33 #include <utils/xml/SUMOXMLDefinitions.h>
34 #include <utils/xml/SUMOSAXHandler.h>
35 #include <utils/common/MsgHandler.h>
36 #include <utils/common/SUMOTime.h>
37 #include <utils/common/StringUtils.h>
38 #include <utils/common/StringTokenizer.h>
39 #include <utils/common/RGBColor.h>
40 #include <utils/geom/GeomConvHelper.h>
41 #include <microsim/MSGlobals.h>
42 #include <microsim/MSLane.h>
43 #include <microsim/MSJunction.h>
44 #include <microsim/MSJunctionLogic.h>
45 #include <microsim/traffic_lights/MSTrafficLightLogic.h>
46 #include <utils/iodevices/OutputDevice.h>
47 #include <utils/common/UtilExceptions.h>
48 #include <utils/geom/GeoConvHelper.h>
49 #include <utils/shapes/ShapeContainer.h>
50 #include <utils/shapes/Shape.h>
51 #include <utils/gui/globjects/GUIGlObject.h>
52 
53 
54 // ===========================================================================
55 // method definitions
56 // ===========================================================================
NLHandler(const std::string & file,MSNet & net,NLDetectorBuilder & detBuilder,NLTriggerBuilder & triggerBuilder,NLEdgeControlBuilder & edgeBuilder,NLJunctionControlBuilder & junctionBuilder)57 NLHandler::NLHandler(const std::string& file, MSNet& net,
58                      NLDetectorBuilder& detBuilder,
59                      NLTriggerBuilder& triggerBuilder,
60                      NLEdgeControlBuilder& edgeBuilder,
61                      NLJunctionControlBuilder& junctionBuilder) :
62     MSRouteHandler(file, true),
63     myNet(net), myActionBuilder(net),
64     myCurrentIsInternalToSkip(false),
65     myDetectorBuilder(detBuilder), myTriggerBuilder(triggerBuilder),
66     myEdgeControlBuilder(edgeBuilder), myJunctionControlBuilder(junctionBuilder),
67     myAmParsingTLLogicOrJunction(false), myCurrentIsBroken(false),
68     myHaveWarnedAboutInvalidTLType(false),
69     myHaveSeenInternalEdge(false),
70     myHaveSeenNeighs(false),
71     myHaveSeenAdditionalSpeedRestrictions(false),
72     myLefthand(false),
73     myNetworkVersion(0),
74     myNetIsLoaded(false) {
75 }
76 
77 
~NLHandler()78 NLHandler::~NLHandler() {}
79 
80 void
myStartElement(int element,const SUMOSAXAttributes & attrs)81 NLHandler::myStartElement(int element,
82                           const SUMOSAXAttributes& attrs) {
83     try {
84         switch (element) {
85             case SUMO_TAG_NET: {
86                 bool ok;
87                 myLefthand = attrs.getOpt<bool>(SUMO_ATTR_LEFTHAND, nullptr, ok, false);
88                 myNetworkVersion = attrs.get<double>(SUMO_ATTR_VERSION, nullptr, ok, false);
89                 break;
90             }
91             case SUMO_TAG_EDGE:
92                 beginEdgeParsing(attrs);
93                 break;
94             case SUMO_TAG_LANE:
95                 addLane(attrs);
96                 break;
97             case SUMO_TAG_NEIGH:
98                 myEdgeControlBuilder.addNeigh(attrs.getString(SUMO_ATTR_LANE));
99                 myHaveSeenNeighs = true;
100                 break;
101             case SUMO_TAG_JUNCTION:
102                 openJunction(attrs);
103                 initJunctionLogic(attrs);
104                 break;
105             case SUMO_TAG_PHASE:
106                 addPhase(attrs);
107                 break;
108             case SUMO_TAG_CONNECTION:
109                 addConnection(attrs);
110                 break;
111             case SUMO_TAG_TLLOGIC:
112                 initTrafficLightLogic(attrs);
113                 break;
114             case SUMO_TAG_REQUEST:
115                 addRequest(attrs);
116                 break;
117             case SUMO_TAG_WAUT:
118                 openWAUT(attrs);
119                 break;
120             case SUMO_TAG_WAUT_SWITCH:
121                 addWAUTSwitch(attrs);
122                 break;
123             case SUMO_TAG_WAUT_JUNCTION:
124                 addWAUTJunction(attrs);
125                 break;
126             case SUMO_TAG_E1DETECTOR:
127             case SUMO_TAG_INDUCTION_LOOP:
128                 addE1Detector(attrs);
129                 break;
130             case SUMO_TAG_E2DETECTOR:
131             case SUMO_TAG_LANE_AREA_DETECTOR:
132                 addE2Detector(attrs);
133                 break;
134             case SUMO_TAG_E3DETECTOR:
135             case SUMO_TAG_ENTRY_EXIT_DETECTOR:
136                 beginE3Detector(attrs);
137                 break;
138             case SUMO_TAG_DET_ENTRY:
139                 addE3Entry(attrs);
140                 break;
141             case SUMO_TAG_DET_EXIT:
142                 addE3Exit(attrs);
143                 break;
144             case SUMO_TAG_INSTANT_INDUCTION_LOOP:
145                 addInstantE1Detector(attrs);
146                 break;
147             case SUMO_TAG_VSS:
148                 myTriggerBuilder.parseAndBuildLaneSpeedTrigger(myNet, attrs, getFileName());
149                 break;
150             case SUMO_TAG_CALIBRATOR:
151                 myTriggerBuilder.parseAndBuildCalibrator(myNet, attrs, getFileName());
152                 break;
153             case SUMO_TAG_REROUTER:
154                 myTriggerBuilder.parseAndBuildRerouter(myNet, attrs, getFileName());
155                 break;
156             case SUMO_TAG_BUS_STOP:
157             case SUMO_TAG_TRAIN_STOP:
158             case SUMO_TAG_CONTAINER_STOP:
159                 myTriggerBuilder.parseAndBuildStoppingPlace(myNet, attrs, (SumoXMLTag)element);
160                 myLastParameterised.push_back(myTriggerBuilder.getCurrentStop());
161                 break;
162             case SUMO_TAG_PARKING_SPACE:
163                 myTriggerBuilder.parseAndAddLotEntry(attrs);
164                 break;
165             case SUMO_TAG_PARKING_AREA:
166                 myTriggerBuilder.parseAndBeginParkingArea(myNet, attrs);
167                 myLastParameterised.push_back(myTriggerBuilder.getCurrentStop());
168                 break;
169             case SUMO_TAG_ACCESS:
170                 myTriggerBuilder.addAccess(myNet, attrs);
171                 break;
172             case SUMO_TAG_CHARGING_STATION:
173                 myTriggerBuilder.parseAndBuildChargingStation(myNet, attrs);
174                 break;
175             case SUMO_TAG_VTYPEPROBE:
176                 addVTypeProbeDetector(attrs);
177                 break;
178             case SUMO_TAG_ROUTEPROBE:
179                 addRouteProbeDetector(attrs);
180                 break;
181             case SUMO_TAG_MEANDATA_EDGE:
182                 addEdgeLaneMeanData(attrs, SUMO_TAG_MEANDATA_EDGE);
183                 break;
184             case SUMO_TAG_MEANDATA_LANE:
185                 addEdgeLaneMeanData(attrs, SUMO_TAG_MEANDATA_LANE);
186                 break;
187             case SUMO_TAG_TIMEDEVENT:
188                 myActionBuilder.addAction(attrs, getFileName());
189                 break;
190             case SUMO_TAG_VAPORIZER:
191                 myTriggerBuilder.buildVaporizer(attrs);
192                 break;
193             case SUMO_TAG_LOCATION:
194                 setLocation(attrs);
195                 break;
196             case SUMO_TAG_TAZ:
197                 addDistrict(attrs);
198                 break;
199             case SUMO_TAG_TAZSOURCE:
200                 addDistrictEdge(attrs, true);
201                 break;
202             case SUMO_TAG_TAZSINK:
203                 addDistrictEdge(attrs, false);
204                 break;
205             case SUMO_TAG_ROUNDABOUT:
206                 addRoundabout(attrs);
207                 break;
208             case SUMO_TAG_TYPE: {
209                 bool ok = true;
210                 myCurrentTypeID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
211                 break;
212             }
213             case SUMO_TAG_RESTRICTION: {
214                 bool ok = true;
215                 const SUMOVehicleClass svc = getVehicleClassID(attrs.get<std::string>(SUMO_ATTR_VCLASS, myCurrentTypeID.c_str(), ok));
216                 const double speed = attrs.get<double>(SUMO_ATTR_SPEED, myCurrentTypeID.c_str(), ok);
217                 if (ok) {
218                     myNet.addRestriction(myCurrentTypeID, svc, speed);
219                 }
220                 if (myNetIsLoaded) {
221                     myHaveSeenAdditionalSpeedRestrictions = true;
222                 }
223                 break;
224             }
225             case SUMO_TAG_STOPOFFSET: {
226                 bool ok = true;
227                 std::map<SVCPermissions, double> stopOffsets = parseStopOffsets(attrs, ok);
228                 if (!ok) {
229                     WRITE_ERROR(myEdgeControlBuilder.reportCurrentEdgeOrLane());
230                 } else {
231                     myEdgeControlBuilder.addStopOffsets(stopOffsets);
232                 }
233                 break;
234             }
235             default:
236                 break;
237         }
238     } catch (InvalidArgument& e) {
239         WRITE_ERROR(e.what());
240     }
241     MSRouteHandler::myStartElement(element, attrs);
242     if (element == SUMO_TAG_PARAM && !myCurrentIsBroken) {
243         addParam(attrs);
244     }
245 }
246 
247 
248 void
myEndElement(int element)249 NLHandler::myEndElement(int element) {
250     switch (element) {
251         case SUMO_TAG_EDGE:
252             closeEdge();
253             break;
254         case SUMO_TAG_LANE:
255             myEdgeControlBuilder.closeLane();
256             if (!myCurrentIsInternalToSkip && !myCurrentIsBroken) {
257                 myLastParameterised.pop_back();
258             }
259             break;
260         case SUMO_TAG_JUNCTION:
261             if (!myCurrentIsBroken) {
262                 try {
263                     myJunctionControlBuilder.closeJunctionLogic();
264                     myJunctionControlBuilder.closeJunction(getFileName());
265                 } catch (InvalidArgument& e) {
266                     WRITE_ERROR(e.what());
267                 }
268             }
269             myAmParsingTLLogicOrJunction = false;
270             break;
271         case SUMO_TAG_TLLOGIC:
272             if (!myCurrentIsBroken) {
273                 try {
274                     myJunctionControlBuilder.closeTrafficLightLogic(getFileName());
275                 } catch (InvalidArgument& e) {
276                     WRITE_ERROR(e.what());
277                 }
278             }
279             myAmParsingTLLogicOrJunction = false;
280             break;
281         case SUMO_TAG_WAUT:
282             closeWAUT();
283             break;
284         case SUMO_TAG_E3DETECTOR:
285         case SUMO_TAG_ENTRY_EXIT_DETECTOR:
286             endE3Detector();
287             break;
288         case SUMO_TAG_PARKING_AREA:
289             myTriggerBuilder.endParkingArea();
290             myLastParameterised.pop_back();
291             break;
292         case SUMO_TAG_BUS_STOP:
293         case SUMO_TAG_TRAIN_STOP:
294         case SUMO_TAG_CONTAINER_STOP:
295             myTriggerBuilder.endStoppingPlace();
296             myLastParameterised.pop_back();
297             break;
298         case SUMO_TAG_NET:
299             // build junction graph
300             for (JunctionGraph::iterator it = myJunctionGraph.begin(); it != myJunctionGraph.end(); ++it) {
301                 MSEdge* edge = MSEdge::dictionary(it->first);
302                 MSJunction* from = myJunctionControlBuilder.retrieve(it->second.first);
303                 MSJunction* to = myJunctionControlBuilder.retrieve(it->second.second);
304                 if (from == nullptr) {
305                     WRITE_ERROR("Unknown from-node '" + it->second.first + "' for edge '" + it->first + "'.");
306                     return;
307                 }
308                 if (to == nullptr) {
309                     WRITE_ERROR("Unknown to-node '" + it->second.second + "' for edge '" + it->first + "'.");
310                     return;
311                 }
312                 if (edge != nullptr) {
313                     edge->setJunctions(from, to);
314                     from->addOutgoing(edge);
315                     to->addIncoming(edge);
316                 }
317             }
318             myNetIsLoaded = true;
319             break;
320         default:
321             break;
322     }
323     MSRouteHandler::myEndElement(element);
324 }
325 
326 
327 
328 // ---- the root/edge - element
329 void
beginEdgeParsing(const SUMOSAXAttributes & attrs)330 NLHandler::beginEdgeParsing(const SUMOSAXAttributes& attrs) {
331     bool ok = true;
332     myCurrentIsBroken = false;
333     // get the id, report an error if not given or empty...
334     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
335     if (!ok) {
336         myCurrentIsBroken = true;
337         return;
338     }
339     // omit internal edges if not wished
340     if (id[0] == ':') {
341         myHaveSeenInternalEdge = true;
342         if (!MSGlobals::gUsingInternalLanes) {
343             myCurrentIsInternalToSkip = true;
344             return;
345         }
346         std::string junctionID = SUMOXMLDefinitions::getJunctionIDFromInternalEdge(id);
347         myJunctionGraph[id] = std::make_pair(junctionID, junctionID);
348     } else {
349         myJunctionGraph[id] = std::make_pair(
350                                   attrs.get<std::string>(SUMO_ATTR_FROM, id.c_str(), ok),
351                                   attrs.get<std::string>(SUMO_ATTR_TO, id.c_str(), ok));
352         if (!ok) {
353             myCurrentIsBroken = true;
354             return;
355         }
356     }
357     myCurrentIsInternalToSkip = false;
358     // parse the function
359     const SumoXMLEdgeFunc func = attrs.getEdgeFunc(ok);
360     if (!ok) {
361         WRITE_ERROR("Edge '" + id + "' has an invalid type.");
362         myCurrentIsBroken = true;
363         return;
364     }
365     // get the street name
366     const std::string streetName = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
367     // get the edge type
368     const std::string edgeType = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, "");
369     // get the edge priority (only for visualization)
370     const int priority = attrs.getOpt<int>(SUMO_ATTR_PRIORITY, id.c_str(), ok, -1); // default taken from netbuild/NBFrame option 'default.priority'
371     // get the bidi-edge
372     const std::string bidi = attrs.getOpt<std::string>(SUMO_ATTR_BIDI, id.c_str(), ok, "");
373     if (!ok) {
374         myCurrentIsBroken = true;
375         return;
376     }
377     //
378     try {
379         myEdgeControlBuilder.beginEdgeParsing(id, func, streetName, edgeType, priority, bidi);
380     } catch (InvalidArgument& e) {
381         WRITE_ERROR(e.what());
382         myCurrentIsBroken = true;
383     }
384 
385     if (func == EDGEFUNC_CROSSING) {
386         //get the crossingEdges attribute (to implement the other side of the road pushbutton)
387         const std::string crossingEdges = attrs.getOpt<std::string>(SUMO_ATTR_CROSSING_EDGES, id.c_str(), ok, "");
388         if (!crossingEdges.empty()) {
389             std::vector<std::string> crossingEdgesVector;
390             StringTokenizer edges(crossingEdges);
391             while (edges.hasNext()) {
392                 crossingEdgesVector.push_back(edges.next());
393             }
394             myEdgeControlBuilder.addCrossingEdges(crossingEdgesVector);
395         }
396     }
397     myLastEdgeParameters.clearParameter();
398     myLastParameterised.push_back(&myLastEdgeParameters);
399 }
400 
401 
402 void
closeEdge()403 NLHandler::closeEdge() {
404     myLastParameterised.clear();
405     // omit internal edges if not wished and broken edges
406     if (myCurrentIsInternalToSkip || myCurrentIsBroken) {
407         return;
408     }
409     try {
410         MSEdge* e = myEdgeControlBuilder.closeEdge();
411         MSEdge::dictionary(e->getID(), e);
412         e->updateParameter(myLastEdgeParameters.getParametersMap());
413     } catch (InvalidArgument& e) {
414         WRITE_ERROR(e.what());
415     }
416 }
417 
418 
419 //             ---- the root/edge/lanes/lane - element
420 void
addLane(const SUMOSAXAttributes & attrs)421 NLHandler::addLane(const SUMOSAXAttributes& attrs) {
422     // omit internal edges if not wished and broken edges
423     if (myCurrentIsInternalToSkip || myCurrentIsBroken) {
424         return;
425     }
426     bool ok = true;
427     // get the id, report an error if not given or empty...
428     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
429     if (!ok) {
430         myCurrentIsBroken = true;
431         return;
432     }
433     const double maxSpeed = attrs.get<double>(SUMO_ATTR_SPEED, id.c_str(), ok);
434     const double length = attrs.get<double>(SUMO_ATTR_LENGTH, id.c_str(), ok);
435     const std::string allow = attrs.getOpt<std::string>(SUMO_ATTR_ALLOW, id.c_str(), ok, "", false);
436     const std::string disallow = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, id.c_str(), ok, "");
437     const double width = attrs.getOpt<double>(SUMO_ATTR_WIDTH, id.c_str(), ok, SUMO_const_laneWidth);
438     const PositionVector shape = attrs.get<PositionVector>(SUMO_ATTR_SHAPE, id.c_str(), ok);
439     const int index = attrs.get<int>(SUMO_ATTR_INDEX, id.c_str(), ok);
440     const bool isRampAccel = attrs.getOpt<bool>(SUMO_ATTR_ACCELERATION, id.c_str(), ok, false);
441     if (shape.size() < 2) {
442         WRITE_ERROR("Shape of lane '" + id + "' is broken.\n Can not build according edge.");
443         myCurrentIsBroken = true;
444         return;
445     }
446     const SVCPermissions permissions = parseVehicleClasses(allow, disallow);
447     if (permissions != SVCAll) {
448         myNet.setPermissionsFound();
449     }
450     myCurrentIsBroken |= !ok;
451     if (!myCurrentIsBroken) {
452         try {
453             MSLane* lane = myEdgeControlBuilder.addLane(id, maxSpeed, length, shape, width, permissions, index, isRampAccel);
454             // insert the lane into the lane-dictionary, checking
455             if (!MSLane::dictionary(id, lane)) {
456                 delete lane;
457                 WRITE_ERROR("Another lane with the id '" + id + "' exists.");
458                 myCurrentIsBroken = true;
459                 myLastParameterised.push_back(nullptr);
460             } else {
461                 myLastParameterised.push_back(lane);
462             }
463         } catch (InvalidArgument& e) {
464             WRITE_ERROR(e.what());
465         }
466     }
467 }
468 
469 
470 // ---- the root/junction - element
471 void
openJunction(const SUMOSAXAttributes & attrs)472 NLHandler::openJunction(const SUMOSAXAttributes& attrs) {
473     myCurrentIsBroken = false;
474     bool ok = true;
475     // get the id, report an error if not given or empty...
476     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
477     if (!ok) {
478         myCurrentIsBroken = true;
479         return;
480     }
481     PositionVector shape;
482     if (attrs.hasAttribute(SUMO_ATTR_SHAPE)) {
483         // inner junctions have no shape
484         shape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, id.c_str(), ok, PositionVector());
485         if (shape.size() > 2) {
486             shape.closePolygon();
487         }
488     }
489     double x = attrs.get<double>(SUMO_ATTR_X, id.c_str(), ok);
490     double y = attrs.get<double>(SUMO_ATTR_Y, id.c_str(), ok);
491     double z = attrs.getOpt<double>(SUMO_ATTR_Z, id.c_str(), ok, 0);
492     bool typeOK = true;
493     SumoXMLNodeType type = attrs.getNodeType(typeOK);
494     if (!typeOK) {
495         WRITE_ERROR("An unknown or invalid junction type occurred in junction '" + id + "'.");
496         ok = false;
497     }
498     std::string key = attrs.getOpt<std::string>(SUMO_ATTR_KEY, id.c_str(), ok, "");
499     // incoming lanes
500     std::vector<MSLane*> incomingLanes;
501     parseLanes(id, attrs.getStringSecure(SUMO_ATTR_INCLANES, ""), incomingLanes, ok);
502     // internal lanes
503     std::vector<MSLane*> internalLanes;
504     if (MSGlobals::gUsingInternalLanes) {
505         parseLanes(id, attrs.getStringSecure(SUMO_ATTR_INTLANES, ""), internalLanes, ok);
506     }
507     if (!ok) {
508         myCurrentIsBroken = true;
509     } else {
510         try {
511             myJunctionControlBuilder.openJunction(id, key, type, Position(x, y, z), shape, incomingLanes, internalLanes);
512         } catch (InvalidArgument& e) {
513             WRITE_ERROR(e.what() + std::string("\n Can not build according junction."));
514             myCurrentIsBroken = true;
515         }
516     }
517 }
518 
519 
520 void
parseLanes(const std::string & junctionID,const std::string & def,std::vector<MSLane * > & into,bool & ok)521 NLHandler::parseLanes(const std::string& junctionID,
522                       const std::string& def, std::vector<MSLane*>& into, bool& ok) {
523     StringTokenizer st(def);
524     while (ok && st.hasNext()) {
525         std::string laneID = st.next();
526         MSLane* lane = MSLane::dictionary(laneID);
527         if (!MSGlobals::gUsingInternalLanes && laneID[0] == ':') {
528             continue;
529         }
530         if (lane == nullptr) {
531             WRITE_ERROR("An unknown lane ('" + laneID + "') was tried to be set as incoming to junction '" + junctionID + "'.");
532             ok = false;
533             continue;
534         }
535         into.push_back(lane);
536     }
537 }
538 // ----
539 
540 void
addParam(const SUMOSAXAttributes & attrs)541 NLHandler::addParam(const SUMOSAXAttributes& attrs) {
542     bool ok = true;
543     const std::string key = attrs.get<std::string>(SUMO_ATTR_KEY, nullptr, ok);
544     // circumventing empty string test
545     const std::string val = attrs.hasAttribute(SUMO_ATTR_VALUE) ? attrs.getString(SUMO_ATTR_VALUE) : "";
546     if (myLastParameterised.size() > 0 && myLastParameterised.back() != nullptr) {
547         myLastParameterised.back()->setParameter(key, val);
548     }
549     // set
550     if (ok && myAmParsingTLLogicOrJunction) {
551         assert(key != "");
552         assert(val != "");
553         myJunctionControlBuilder.addParam(key, val);
554     }
555 }
556 
557 
558 void
openWAUT(const SUMOSAXAttributes & attrs)559 NLHandler::openWAUT(const SUMOSAXAttributes& attrs) {
560     myCurrentIsBroken = false;
561     bool ok = true;
562     // get the id, report an error if not given or empty...
563     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
564     if (!ok) {
565         myCurrentIsBroken = true;
566         return;
567     }
568     SUMOTime t = attrs.getOptSUMOTimeReporting(SUMO_ATTR_REF_TIME, id.c_str(), ok, 0);
569     std::string pro = attrs.get<std::string>(SUMO_ATTR_START_PROG, id.c_str(), ok);
570     if (!ok) {
571         myCurrentIsBroken = true;
572     }
573     if (!myCurrentIsBroken) {
574         myCurrentWAUTID = id;
575         try {
576             myJunctionControlBuilder.getTLLogicControlToUse().addWAUT(t, id, pro);
577         } catch (InvalidArgument& e) {
578             WRITE_ERROR(e.what());
579             myCurrentIsBroken = true;
580         }
581     }
582 }
583 
584 
585 void
addWAUTSwitch(const SUMOSAXAttributes & attrs)586 NLHandler::addWAUTSwitch(const SUMOSAXAttributes& attrs) {
587     bool ok = true;
588     SUMOTime t = attrs.getSUMOTimeReporting(SUMO_ATTR_TIME, myCurrentWAUTID.c_str(), ok);
589     std::string to = attrs.get<std::string>(SUMO_ATTR_TO, myCurrentWAUTID.c_str(), ok);
590     if (!ok) {
591         myCurrentIsBroken = true;
592     }
593     if (!myCurrentIsBroken) {
594         try {
595             myJunctionControlBuilder.getTLLogicControlToUse().addWAUTSwitch(myCurrentWAUTID, t, to);
596         } catch (InvalidArgument& e) {
597             WRITE_ERROR(e.what());
598             myCurrentIsBroken = true;
599         }
600     }
601 }
602 
603 
604 void
addWAUTJunction(const SUMOSAXAttributes & attrs)605 NLHandler::addWAUTJunction(const SUMOSAXAttributes& attrs) {
606     bool ok = true;
607     std::string wautID = attrs.get<std::string>(SUMO_ATTR_WAUT_ID, nullptr, ok);
608     std::string junctionID = attrs.get<std::string>(SUMO_ATTR_JUNCTION_ID, nullptr, ok);
609     std::string procedure = attrs.getOpt<std::string>(SUMO_ATTR_PROCEDURE, nullptr, ok, "");
610     bool synchron = attrs.getOpt<bool>(SUMO_ATTR_SYNCHRON, nullptr, ok, false);
611     if (!ok) {
612         myCurrentIsBroken = true;
613     }
614     try {
615         if (!myCurrentIsBroken) {
616             myJunctionControlBuilder.getTLLogicControlToUse().addWAUTJunction(wautID, junctionID, procedure, synchron);
617         }
618     } catch (InvalidArgument& e) {
619         WRITE_ERROR(e.what());
620         myCurrentIsBroken = true;
621     }
622 }
623 
624 
625 void
addRequest(const SUMOSAXAttributes & attrs)626 NLHandler::addRequest(const SUMOSAXAttributes& attrs) {
627     if (myCurrentIsBroken) {
628         return;
629     }
630     bool ok = true;
631     int request = attrs.get<int>(SUMO_ATTR_INDEX, nullptr, ok);
632     bool cont = false;
633     cont = attrs.getOpt<bool>(SUMO_ATTR_CONT, nullptr, ok, false);
634     std::string response = attrs.get<std::string>(SUMO_ATTR_RESPONSE, nullptr, ok);
635     std::string foes = attrs.get<std::string>(SUMO_ATTR_FOES, nullptr, ok);
636     if (!ok) {
637         return;
638     }
639     // store received information
640     if (request >= 0 && response.length() > 0) {
641         try {
642             myJunctionControlBuilder.addLogicItem(request, response, foes, cont);
643         } catch (InvalidArgument& e) {
644             WRITE_ERROR(e.what());
645         }
646     }
647 }
648 
649 
650 void
initJunctionLogic(const SUMOSAXAttributes & attrs)651 NLHandler::initJunctionLogic(const SUMOSAXAttributes& attrs) {
652     if (myCurrentIsBroken) {
653         return;
654     }
655     myAmParsingTLLogicOrJunction = true;
656     bool ok = true;
657     // we either a have a junction or a legacy network with ROWLogic
658     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
659     if (ok) {
660         myJunctionControlBuilder.initJunctionLogic(id);
661     }
662 }
663 
664 
665 void
initTrafficLightLogic(const SUMOSAXAttributes & attrs)666 NLHandler::initTrafficLightLogic(const SUMOSAXAttributes& attrs) {
667     myCurrentIsBroken = false;
668     myAmParsingTLLogicOrJunction = true;
669     bool ok = true;
670     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
671     std::string programID = attrs.getOpt<std::string>(SUMO_ATTR_PROGRAMID, id.c_str(), ok, "<unknown>");
672     TrafficLightType type = TLTYPE_STATIC;
673     std::string typeS;
674     if (myJunctionControlBuilder.getTLLogicControlToUse().get(id, programID) == nullptr) {
675         // SUMO_ATTR_TYPE is not needed when only modifying the offset of an
676         // existing program
677         typeS = attrs.get<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok);
678         if (!ok) {
679             myCurrentIsBroken = true;
680             return;
681         }
682         if (SUMOXMLDefinitions::TrafficLightTypes.hasString(typeS)) {
683             type = SUMOXMLDefinitions::TrafficLightTypes.get(typeS);
684         } else {
685             WRITE_ERROR("Traffic light '" + id + "' has unknown type '" + typeS + "'.");
686         }
687         if (MSGlobals::gUseMesoSim && type == TLTYPE_ACTUATED) {
688             if (!myHaveWarnedAboutInvalidTLType) {
689                 WRITE_WARNING("Traffic light type '" + toString(type) + "' cannot be used in mesoscopic simulation. Using '" + toString(TLTYPE_STATIC) + "' as fallback");
690                 myHaveWarnedAboutInvalidTLType = true;
691             }
692             type = TLTYPE_STATIC;
693         }
694     }
695     //
696     const SUMOTime offset = attrs.getOptSUMOTimeReporting(SUMO_ATTR_OFFSET, id.c_str(), ok, 0);
697     if (!ok) {
698         myCurrentIsBroken = true;
699         return;
700     }
701     myJunctionControlBuilder.initTrafficLightLogic(id, programID, type, offset);
702 }
703 
704 
705 void
addPhase(const SUMOSAXAttributes & attrs)706 NLHandler::addPhase(const SUMOSAXAttributes& attrs) {
707     // try to get the phase definition
708     bool ok = true;
709     std::string state = attrs.get<std::string>(SUMO_ATTR_STATE, nullptr, ok);
710     if (!ok) {
711         return;
712     }
713     // try to get the phase duration
714     SUMOTime duration = attrs.getSUMOTimeReporting(SUMO_ATTR_DURATION, myJunctionControlBuilder.getActiveKey().c_str(), ok);
715     if (duration == 0) {
716         WRITE_ERROR("Duration of phase " + toString(myJunctionControlBuilder.getNumberOfLoadedPhases())
717                     + " for tlLogic '" + myJunctionControlBuilder.getActiveKey()
718                     + "' program '" + myJunctionControlBuilder.getActiveSubKey() + "' is zero.");
719         return;
720     }
721     // if the traffic light is an actuated traffic light, try to get
722     //  the minimum and maximum durations
723     SUMOTime minDuration = attrs.getOptSUMOTimeReporting(
724                                SUMO_ATTR_MINDURATION, myJunctionControlBuilder.getActiveKey().c_str(), ok, duration);
725     SUMOTime maxDuration = attrs.getOptSUMOTimeReporting(
726                                SUMO_ATTR_MAXDURATION, myJunctionControlBuilder.getActiveKey().c_str(), ok, duration);
727 
728 
729     std::vector<int> nextPhases = attrs.getOptIntVector(SUMO_ATTR_NEXT, nullptr, ok);
730     const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, nullptr, ok, "");
731 
732     //SOTL attributes
733     //If the type attribute is not present, the parsed phase is of type "undefined" (MSPhaseDefinition constructor),
734     //in this way SOTL traffic light logic can recognize the phase as unsuitable or decides other
735     //behaviors. See SOTL traffic light logic implementations.
736     if (attrs.hasAttribute(SUMO_ATTR_TYPE)) {
737         bool ok = true;
738         std::string phaseTypeString;
739         bool transient_notdecisional_bit;
740         bool commit_bit;
741         MSPhaseDefinition::LaneIdVector laneIdVector;
742         try {
743             phaseTypeString = attrs.get<std::string>(SUMO_ATTR_TYPE, "phase", ok, false);
744         } catch (EmptyData&) {
745             MsgHandler::getWarningInstance()->inform("Empty type definition. Assuming phase type as SUMOSOTL_TagAttrDefinitions::SOTL_ATTL_TYPE_TRANSIENT");
746             transient_notdecisional_bit = false;
747         }
748         if (phaseTypeString.find("decisional") != std::string::npos) {
749             transient_notdecisional_bit = false;
750         } else if (phaseTypeString.find("transient") != std::string::npos) {
751             transient_notdecisional_bit = true;
752         } else {
753             MsgHandler::getWarningInstance()->inform("SOTL_ATTL_TYPE_DECISIONAL nor SOTL_ATTL_TYPE_TRANSIENT. Assuming phase type as SUMOSOTL_TagAttrDefinitions::SOTL_ATTL_TYPE_TRANSIENT");
754             transient_notdecisional_bit = false;
755         }
756         commit_bit = (phaseTypeString.find("commit") != std::string::npos);
757 
758         if (phaseTypeString.find("target") != std::string::npos) {
759             std::string delimiter(" ,;");
760             //Phase declared as target, getting targetLanes attribute
761             try {
762                 /// @todo: the following should be moved to StringTok
763                 std::string targetLanesString = attrs.getStringSecure(SUMO_ATTR_TARGETLANE, "");
764                 //TOKENIZING
765                 MSPhaseDefinition::LaneIdVector targetLanesVector;
766                 //Skip delimiters at the beginning
767                 std::string::size_type firstPos = targetLanesString.find_first_not_of(delimiter, 0);
768                 //Find first "non-delimiter".
769                 std::string::size_type pos = targetLanesString.find_first_of(delimiter, firstPos);
770 
771                 while (std::string::npos != pos || std::string::npos != firstPos) {
772                     //Found a token, add it to the vector
773                     targetLanesVector.push_back(targetLanesString.substr(firstPos, pos - firstPos));
774 
775                     //Skip delimiters
776                     firstPos = targetLanesString.find_first_not_of(delimiter, pos);
777 
778                     //Find next "non-delimiter"
779                     pos = targetLanesString.find_first_of(delimiter, firstPos);
780                 }
781                 //Adding the SOTL parsed phase to have a new MSPhaseDefinition that is SOTL compliant for target phases
782                 myJunctionControlBuilder.addPhase(duration, state, nextPhases, minDuration, maxDuration, name, transient_notdecisional_bit, commit_bit, &targetLanesVector);
783             } catch (EmptyData&) {
784                 MsgHandler::getErrorInstance()->inform("Missing targetLane definition for the target phase.");
785                 return;
786             }
787         } else {
788             //Adding the SOTL parsed phase to have a new MSPhaseDefinition that is SOTL compliant for non target phases
789             myJunctionControlBuilder.addPhase(duration, state, nextPhases, minDuration, maxDuration, name, transient_notdecisional_bit, commit_bit);
790         }
791     } else {
792         //Adding the standard parsed phase to have a new MSPhaseDefinition
793 
794         myJunctionControlBuilder.addPhase(duration, state, nextPhases, minDuration, maxDuration, name);
795     }
796 }
797 
798 
799 void
addE1Detector(const SUMOSAXAttributes & attrs)800 NLHandler::addE1Detector(const SUMOSAXAttributes& attrs) {
801     bool ok = true;
802     // get the id, report an error if not given or empty...
803     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
804     if (!ok) {
805         return;
806     }
807     const SUMOTime frequency = attrs.getSUMOTimeReporting(SUMO_ATTR_FREQUENCY, id.c_str(), ok);
808     const double position = attrs.get<double>(SUMO_ATTR_POSITION, id.c_str(), ok);
809     const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, id.c_str(), ok, false);
810     const std::string vTypes = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id.c_str(), ok, "");
811     const std::string lane = attrs.get<std::string>(SUMO_ATTR_LANE, id.c_str(), ok);
812     const std::string file = attrs.get<std::string>(SUMO_ATTR_FILE, id.c_str(), ok);
813     if (!ok) {
814         return;
815     }
816     try {
817         myDetectorBuilder.buildInductLoop(id, lane, position, frequency,
818                                           FileHelpers::checkForRelativity(file, getFileName()),
819                                           friendlyPos, vTypes);
820     } catch (InvalidArgument& e) {
821         WRITE_ERROR(e.what());
822     } catch (IOError& e) {
823         WRITE_ERROR(e.what());
824     }
825 }
826 
827 
828 void
addInstantE1Detector(const SUMOSAXAttributes & attrs)829 NLHandler::addInstantE1Detector(const SUMOSAXAttributes& attrs) {
830     bool ok = true;
831     // get the id, report an error if not given or empty...
832     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
833     if (!ok) {
834         return;
835     }
836     const double position = attrs.get<double>(SUMO_ATTR_POSITION, id.c_str(), ok);
837     const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, id.c_str(), ok, false);
838     const std::string lane = attrs.get<std::string>(SUMO_ATTR_LANE, id.c_str(), ok);
839     const std::string file = attrs.get<std::string>(SUMO_ATTR_FILE, id.c_str(), ok);
840     const std::string vTypes = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id.c_str(), ok, "");
841     if (!ok) {
842         return;
843     }
844     try {
845         myDetectorBuilder.buildInstantInductLoop(id, lane, position, FileHelpers::checkForRelativity(file, getFileName()), friendlyPos, vTypes);
846     } catch (InvalidArgument& e) {
847         WRITE_ERROR(e.what());
848     } catch (IOError& e) {
849         WRITE_ERROR(e.what());
850     }
851 }
852 
853 
854 void
addVTypeProbeDetector(const SUMOSAXAttributes & attrs)855 NLHandler::addVTypeProbeDetector(const SUMOSAXAttributes& attrs) {
856     WRITE_WARNING("VTypeProbes are deprecated. Use fcd-output devices (assigned to the vType) instead.");
857     bool ok = true;
858     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
859     SUMOTime frequency = attrs.getSUMOTimeReporting(SUMO_ATTR_FREQUENCY, id.c_str(), ok);
860     std::string type = attrs.getStringSecure(SUMO_ATTR_TYPE, "");
861     std::string file = attrs.get<std::string>(SUMO_ATTR_FILE, id.c_str(), ok);
862     if (!ok) {
863         return;
864     }
865     try {
866         myDetectorBuilder.buildVTypeProbe(id, type, frequency, FileHelpers::checkForRelativity(file, getFileName()));
867     } catch (InvalidArgument& e) {
868         WRITE_ERROR(e.what());
869     } catch (IOError& e) {
870         WRITE_ERROR(e.what());
871     }
872 }
873 
874 
875 void
addRouteProbeDetector(const SUMOSAXAttributes & attrs)876 NLHandler::addRouteProbeDetector(const SUMOSAXAttributes& attrs) {
877     bool ok = true;
878     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
879     SUMOTime frequency = attrs.getSUMOTimeReporting(SUMO_ATTR_FREQUENCY, id.c_str(), ok);
880     SUMOTime begin = attrs.getOptSUMOTimeReporting(SUMO_ATTR_BEGIN, id.c_str(), ok, -1);
881     std::string edge = attrs.get<std::string>(SUMO_ATTR_EDGE, id.c_str(), ok);
882     std::string file = attrs.get<std::string>(SUMO_ATTR_FILE, id.c_str(), ok);
883     const std::string vTypes = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id.c_str(), ok, "");
884     if (!ok) {
885         return;
886     }
887     try {
888         myDetectorBuilder.buildRouteProbe(id, edge, frequency, begin,
889                                           FileHelpers::checkForRelativity(file, getFileName()), vTypes);
890     } catch (InvalidArgument& e) {
891         WRITE_ERROR(e.what());
892     } catch (IOError& e) {
893         WRITE_ERROR(e.what());
894     }
895 }
896 
897 
898 
899 void
addE2Detector(const SUMOSAXAttributes & attrs)900 NLHandler::addE2Detector(const SUMOSAXAttributes& attrs) {
901 
902     // check whether this is a detector connected to a tls and optionally to a link
903     bool ok = true;
904     const std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
905     const std::string lsaid = attrs.getOpt<std::string>(SUMO_ATTR_TLID, id.c_str(), ok, "");
906     const std::string toLane = attrs.getOpt<std::string>(SUMO_ATTR_TO, id.c_str(), ok, "");
907     const SUMOTime haltingTimeThreshold = attrs.getOptSUMOTimeReporting(SUMO_ATTR_HALTING_TIME_THRESHOLD, id.c_str(), ok, TIME2STEPS(1));
908     const double haltingSpeedThreshold = attrs.getOpt<double>(SUMO_ATTR_HALTING_SPEED_THRESHOLD, id.c_str(), ok, 5.0f / 3.6f);
909     const double jamDistThreshold = attrs.getOpt<double>(SUMO_ATTR_JAM_DIST_THRESHOLD, id.c_str(), ok, 10.0f);
910     double position = attrs.getOpt<double>(SUMO_ATTR_POSITION, id.c_str(), ok, std::numeric_limits<double>::max());
911     const double length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, id.c_str(), ok, std::numeric_limits<double>::max());
912     const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, id.c_str(), ok, false);
913     const bool showDetector = attrs.getOpt<bool>(SUMO_ATTR_SHOW_DETECTOR, id.c_str(), ok, true);
914     const std::string contStr = attrs.getOpt<std::string>(SUMO_ATTR_CONT, id.c_str(), ok, "");
915     if (contStr != "") {
916         WRITE_WARNING("Ignoring deprecated argument 'cont' for E2 detector '" + id + "'");
917     }
918     std::string lane = attrs.getOpt<std::string>(SUMO_ATTR_LANE, id.c_str(), ok, "");
919     const std::string file = attrs.get<std::string>(SUMO_ATTR_FILE, id.c_str(), ok);
920     const std::string vTypes = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id.c_str(), ok, "");
921 
922     double endPosition = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, id.c_str(), ok, std::numeric_limits<double>::max());
923     const std::string lanes = attrs.getOpt<std::string>(SUMO_ATTR_LANES, id.c_str(), ok, ""); // lanes has priority to lane
924     if (!ok) {
925         return;
926     }
927 
928     bool lanesGiven = lanes != "";
929     bool laneGiven = lane != "";
930     if (!(lanesGiven || laneGiven)) {
931         // in absence of any lane-specification assume specification by id
932         WRITE_WARNING("Trying to specify detector's lane by the given id since the argument 'lane' is missing.")
933         lane = id;
934         laneGiven = true;
935     }
936     bool lengthGiven = length != std::numeric_limits<double>::max();
937     bool posGiven = position != std::numeric_limits<double>::max();
938     bool endPosGiven = endPosition != std::numeric_limits<double>::max();
939     bool lsaGiven = lsaid != "";
940     bool toLaneGiven = toLane != "";
941 
942     MSLane* clane = nullptr;
943     std::vector<MSLane*> clanes;
944     if (lanesGiven) {
945         // If lanes is given, endPos and startPos are required. lane, and length are ignored
946         std::string seps = " ,\t\n";
947         StringTokenizer st = StringTokenizer(lanes, seps, true);
948 //        std::cout << "Parsing lanes..." << std::endl;
949         while (st.hasNext()) {
950             std::string nextLaneID = st.next();
951 //            std::cout << "Next: " << nextLaneID << std::endl;
952             if (nextLaneID.find_first_of(seps) != nextLaneID.npos) {
953                 continue;
954             }
955             clane = myDetectorBuilder.getLaneChecking(nextLaneID, SUMO_TAG_E2DETECTOR, id);
956             clanes.push_back(clane);
957         }
958         if (clanes.size() == 0) {
959             throw InvalidArgument("Malformed argument 'lanes' for E2Detector '" + id + "'.\nSpecify 'lanes' as a sequence of lane-IDs seperated by whitespace or comma (',')");
960         }
961         if (laneGiven) {
962             WRITE_WARNING("Ignoring argument 'lane' for E2Detector '" + id + "' since argument 'lanes' was given.\n"
963                           "Usage combinations for positional specification: [lane, pos, length], [lane, endPos, length], or [lanes, pos, endPos]");
964         }
965         if (lengthGiven) {
966             WRITE_WARNING("Ignoring argument 'length' for E2Detector '" + id + "' since argument 'lanes' was given.\n"
967                           "Usage combinations for positional specification: [lane, pos, length], [lane, endPos, length], or [lanes, pos, endPos]");
968         }
969         if (!posGiven) {
970             // assuming start pos == lane start
971             position = 0;
972             WRITE_WARNING("Missing argument 'pos' for E2Detector '" + id + "'. Assuming detector start == lane start of lane '" + clanes[0]->getID() + "'.");
973         }
974         if (!endPosGiven) {
975             // assuming end pos == lane end
976             endPosition = clanes[clanes.size() - 1]->getLength();
977             WRITE_WARNING("Missing argument 'endPos' for E2Detector '" + id + "'. Assuming detector end == lane end of lane '" + clanes[clanes.size() - 1]->getID() + "'.");
978         }
979 
980     } else {
981         if (!laneGiven) {
982             std::stringstream ss;
983             ss << "Missing argument 'lane' for E2Detector '" << id << "'."
984                << "\nUsage combinations for positional specification: [lane, pos, length], [lane, endPos, length], or [lanes, pos, endPos]";
985             throw InvalidArgument(ss.str());
986         }
987         clane = myDetectorBuilder.getLaneChecking(lane, SUMO_TAG_E2DETECTOR, id);
988 
989         if (posGiven) {
990             // start pos is given
991             if (endPosGiven && lengthGiven) {
992                 std::stringstream ss;
993                 ss << "Ignoring argument 'endPos' for E2Detector '" << id << "' since argument 'pos' was given."
994                    << "\nUsage combinations for positional specification: [lane, pos, length], [lane, endPos, length], or [lanes, pos, endPos]";
995                 WRITE_WARNING(ss.str());
996                 endPosition = std::numeric_limits<double>::max();
997             }
998             if (!lengthGiven && !endPosGiven) {
999                 std::stringstream ss;
1000                 ss << "Missing arguments 'length'/'endPos' for E2Detector '" << id << "'. Assuming detector end == lane end of lane '" << lane << "'.";
1001                 WRITE_WARNING(ss.str());
1002                 endPosition = clane->getLength();
1003             }
1004         } else if (endPosGiven) {
1005             // endPos is given, pos is not given
1006             if (!lengthGiven) {
1007                 std::stringstream ss;
1008                 ss << "Missing arguments 'length'/'pos' for E2Detector '" << id << "'. Assuming detector start == lane start of lane '" << lane << "'.";
1009                 WRITE_WARNING(ss.str());
1010             }
1011         } else {
1012             std::stringstream ss;
1013             if (lengthGiven && fabs(length - clane->getLength()) > NUMERICAL_EPS) {
1014                 ss << "Incomplete positional specification for E2Detector '" << id << "'."
1015                    << "\nUsage combinations for positional specification: [lane, pos, length], [lane, endPos, length], or [lanes, pos, endPos]";
1016                 throw InvalidArgument(ss.str());
1017             }
1018             endPosition = clane->getLength();
1019             position = 0;
1020             ss << "Missing arguments 'pos'/'endPos' for E2Detector '" << id << "'. Assuming that the detector covers the whole lane '" << lane << "'.";
1021             WRITE_WARNING(ss.str());
1022         }
1023     }
1024 
1025     // Frequency
1026 
1027     SUMOTime frequency;
1028     if (!lsaGiven) {
1029         frequency = attrs.getSUMOTimeReporting(SUMO_ATTR_FREQUENCY, id.c_str(), ok);
1030         if (!ok) {
1031             return;
1032         }
1033     } else {
1034         frequency = attrs.getSUMOTimeReporting(SUMO_ATTR_FREQUENCY, id.c_str(), ok, false);
1035     }
1036 
1037     // TLS
1038     MSTLLogicControl::TLSLogicVariants* tlls = nullptr;
1039     if (lsaGiven) {
1040         tlls = &myJunctionControlBuilder.getTLLogic(lsaid);
1041         if (tlls->getActive() == nullptr) {
1042             throw InvalidArgument("The detector '" + id + "' refers to an unknown lsa '" + lsaid + "'.");
1043         }
1044         if (frequency != -1) {
1045             WRITE_WARNING("Ignoring argument 'frequency' for E2Detector '" + id + "' since argument 'tl' was given.");
1046             frequency = -1;
1047         }
1048     }
1049 
1050     // Link
1051     MSLane* cToLane = nullptr;
1052     if (toLaneGiven) {
1053         cToLane = myDetectorBuilder.getLaneChecking(toLane, SUMO_TAG_E2DETECTOR, id);
1054     }
1055 
1056     // File
1057     std::string filename;
1058     try {
1059         filename = FileHelpers::checkForRelativity(file, getFileName());
1060     } catch (IOError& e) {
1061         WRITE_ERROR(e.what());
1062     }
1063 
1064     // Build detector
1065     if (lanesGiven) {
1066         // specification by a lane sequence
1067         myDetectorBuilder.buildE2Detector(id, clanes, position, endPosition, filename, frequency,
1068                                           haltingTimeThreshold, haltingSpeedThreshold, jamDistThreshold,
1069                                           vTypes, friendlyPos, showDetector,
1070                                           tlls, cToLane);
1071     } else {
1072         // specification by start or end lane
1073         myDetectorBuilder.buildE2Detector(id, clane, position, endPosition, length, filename, frequency,
1074                                           haltingTimeThreshold, haltingSpeedThreshold, jamDistThreshold,
1075                                           vTypes, friendlyPos, showDetector,
1076                                           tlls, cToLane);
1077     }
1078 
1079 }
1080 
1081 
1082 void
beginE3Detector(const SUMOSAXAttributes & attrs)1083 NLHandler::beginE3Detector(const SUMOSAXAttributes& attrs) {
1084     bool ok = true;
1085     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
1086     const SUMOTime frequency = attrs.getSUMOTimeReporting(SUMO_ATTR_FREQUENCY, id.c_str(), ok);
1087     const SUMOTime haltingTimeThreshold = attrs.getOptSUMOTimeReporting(SUMO_ATTR_HALTING_TIME_THRESHOLD, id.c_str(), ok, TIME2STEPS(1));
1088     const double haltingSpeedThreshold = attrs.getOpt<double>(SUMO_ATTR_HALTING_SPEED_THRESHOLD, id.c_str(), ok, 5.0f / 3.6f);
1089     const std::string file = attrs.get<std::string>(SUMO_ATTR_FILE, id.c_str(), ok);
1090     const std::string vTypes = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id.c_str(), ok, "");
1091     const bool openEntry = attrs.getOpt<bool>(SUMO_ATTR_OPEN_ENTRY, id.c_str(), ok, false);
1092     if (!ok) {
1093         return;
1094     }
1095     try {
1096         myDetectorBuilder.beginE3Detector(id,
1097                                           FileHelpers::checkForRelativity(file, getFileName()),
1098                                           frequency, haltingSpeedThreshold, haltingTimeThreshold, vTypes, openEntry);
1099     } catch (InvalidArgument& e) {
1100         WRITE_ERROR(e.what());
1101     } catch (IOError& e) {
1102         WRITE_ERROR(e.what());
1103     }
1104 }
1105 
1106 
1107 void
addE3Entry(const SUMOSAXAttributes & attrs)1108 NLHandler::addE3Entry(const SUMOSAXAttributes& attrs) {
1109     bool ok = true;
1110     const double position = attrs.get<double>(SUMO_ATTR_POSITION, myDetectorBuilder.getCurrentE3ID().c_str(), ok);
1111     const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, myDetectorBuilder.getCurrentE3ID().c_str(), ok, false);
1112     const std::string lane = attrs.get<std::string>(SUMO_ATTR_LANE, myDetectorBuilder.getCurrentE3ID().c_str(), ok);
1113     if (!ok) {
1114         return;
1115     }
1116     myDetectorBuilder.addE3Entry(lane, position, friendlyPos);
1117 }
1118 
1119 
1120 void
addE3Exit(const SUMOSAXAttributes & attrs)1121 NLHandler::addE3Exit(const SUMOSAXAttributes& attrs) {
1122     bool ok = true;
1123     const double position = attrs.get<double>(SUMO_ATTR_POSITION, myDetectorBuilder.getCurrentE3ID().c_str(), ok);
1124     const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, myDetectorBuilder.getCurrentE3ID().c_str(), ok, false);
1125     const std::string lane = attrs.get<std::string>(SUMO_ATTR_LANE, myDetectorBuilder.getCurrentE3ID().c_str(), ok);
1126     if (!ok) {
1127         return;
1128     }
1129     myDetectorBuilder.addE3Exit(lane, position, friendlyPos);
1130 }
1131 
1132 
1133 void
addEdgeLaneMeanData(const SUMOSAXAttributes & attrs,int objecttype)1134 NLHandler::addEdgeLaneMeanData(const SUMOSAXAttributes& attrs, int objecttype) {
1135     bool ok = true;
1136     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
1137     const double maxTravelTime = attrs.getOpt<double>(SUMO_ATTR_MAX_TRAVELTIME, id.c_str(), ok, 100000);
1138     const double minSamples = attrs.getOpt<double>(SUMO_ATTR_MIN_SAMPLES, id.c_str(), ok, 0);
1139     const double haltingSpeedThreshold = attrs.getOpt<double>(SUMO_ATTR_HALTING_SPEED_THRESHOLD, id.c_str(), ok, POSITION_EPS);
1140     const std::string excludeEmpty = attrs.getOpt<std::string>(SUMO_ATTR_EXCLUDE_EMPTY, id.c_str(), ok, "false");
1141     const bool withInternal = attrs.getOpt<bool>(SUMO_ATTR_WITH_INTERNAL, id.c_str(), ok, false);
1142     const bool trackVehicles = attrs.getOpt<bool>(SUMO_ATTR_TRACK_VEHICLES, id.c_str(), ok, false);
1143     const std::string detectPersonsString = attrs.getOpt<std::string>(SUMO_ATTR_DETECT_PERSONS, id.c_str(), ok, "");
1144     const std::string file = attrs.get<std::string>(SUMO_ATTR_FILE, id.c_str(), ok);
1145     const std::string type = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, "performance");
1146     std::string vtypes = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id.c_str(), ok, "");
1147     const SUMOTime frequency = attrs.getOptSUMOTimeReporting(SUMO_ATTR_FREQUENCY, id.c_str(), ok, -1);
1148     const SUMOTime begin = attrs.getOptSUMOTimeReporting(SUMO_ATTR_BEGIN, id.c_str(), ok, string2time(OptionsCont::getOptions().getString("begin")));
1149     const SUMOTime end = attrs.getOptSUMOTimeReporting(SUMO_ATTR_END, id.c_str(), ok, string2time(OptionsCont::getOptions().getString("end")));
1150     if (!ok) {
1151         return;
1152     }
1153     int detectPersons = 0;
1154     for (std::string mode : StringTokenizer(detectPersonsString).getVector()) {
1155         if (SUMOXMLDefinitions::PersonModeValues.hasString(mode)) {
1156             detectPersons |= SUMOXMLDefinitions::PersonModeValues.get(mode);
1157         } else {
1158             WRITE_ERROR("Invalid person mode '" + mode + "' in edgeData definition '" + id + "'");
1159             return;
1160         }
1161     }
1162     try {
1163         myDetectorBuilder.createEdgeLaneMeanData(id, frequency, begin, end,
1164                 type, objecttype == SUMO_TAG_MEANDATA_LANE,
1165                 // equivalent to TplConvert::_2bool used in SUMOSAXAttributes::getBool
1166                 excludeEmpty[0] != 't' && excludeEmpty[0] != 'T' && excludeEmpty[0] != '1' && excludeEmpty[0] != 'x',
1167                 excludeEmpty == "defaults", withInternal, trackVehicles, detectPersons,
1168                 maxTravelTime, minSamples, haltingSpeedThreshold, vtypes,
1169                 FileHelpers::checkForRelativity(file, getFileName()));
1170     } catch (InvalidArgument& e) {
1171         WRITE_ERROR(e.what());
1172     } catch (IOError& e) {
1173         WRITE_ERROR(e.what());
1174     }
1175 }
1176 
1177 
1178 void
addConnection(const SUMOSAXAttributes & attrs)1179 NLHandler::addConnection(const SUMOSAXAttributes& attrs) {
1180     bool ok = true;
1181     const std::string fromID = attrs.get<std::string>(SUMO_ATTR_FROM, nullptr, ok);
1182     const std::string toID = attrs.get<std::string>(SUMO_ATTR_TO, nullptr, ok);
1183     if (!MSGlobals::gUsingInternalLanes && (fromID[0] == ':' || toID[0] == ':')) {
1184         std::string tlID = attrs.getOpt<std::string>(SUMO_ATTR_TLID, nullptr, ok, "");
1185         if (tlID != "") {
1186             int tlLinkIdx = attrs.get<int>(SUMO_ATTR_TLLINKINDEX, nullptr, ok);
1187             myJunctionControlBuilder.getTLLogic(tlID).ignoreLinkIndex(tlLinkIdx);
1188         }
1189         return;
1190     }
1191 
1192     MSLink* link = nullptr;
1193     try {
1194         bool ok = true;
1195         const int fromLaneIdx = attrs.get<int>(SUMO_ATTR_FROM_LANE, nullptr, ok);
1196         const double foeVisibilityDistance = attrs.getOpt<double>(SUMO_ATTR_VISIBILITY_DISTANCE, nullptr, ok, 4.5);
1197         const int toLaneIdx = attrs.get<int>(SUMO_ATTR_TO_LANE, nullptr, ok);
1198         LinkDirection dir = parseLinkDir(attrs.get<std::string>(SUMO_ATTR_DIR, nullptr, ok));
1199         LinkState state = parseLinkState(attrs.get<std::string>(SUMO_ATTR_STATE, nullptr, ok));
1200         bool keepClear = attrs.getOpt<bool>(SUMO_ATTR_KEEP_CLEAR, nullptr, ok, true);
1201         std::string tlID = attrs.getOpt<std::string>(SUMO_ATTR_TLID, nullptr, ok, "");
1202         std::string viaID = attrs.getOpt<std::string>(SUMO_ATTR_VIA, nullptr, ok, "");
1203 
1204         MSEdge* from = MSEdge::dictionary(fromID);
1205         if (from == nullptr) {
1206             WRITE_ERROR("Unknown from-edge '" + fromID + "' in connection.");
1207             return;
1208         }
1209         MSEdge* to = MSEdge::dictionary(toID);
1210         if (to == nullptr) {
1211             WRITE_ERROR("Unknown to-edge '" + toID + "' in connection.");
1212             return;
1213         }
1214         if (fromLaneIdx < 0 || fromLaneIdx >= (int)from->getLanes().size() ||
1215                 toLaneIdx < 0 || toLaneIdx >= (int)to->getLanes().size()) {
1216             WRITE_ERROR("Invalid lane index in connection from '" + from->getID() + "' to '" + to->getID() + "'.");
1217             return;
1218         }
1219         MSLane* fromLane = from->getLanes()[fromLaneIdx];
1220         MSLane* toLane = to->getLanes()[toLaneIdx];
1221         assert(fromLane);
1222         assert(toLane);
1223 
1224         MSTrafficLightLogic* logic = nullptr;
1225         int tlLinkIdx = -1;
1226         if (tlID != "") {
1227             tlLinkIdx = attrs.get<int>(SUMO_ATTR_TLLINKINDEX, nullptr, ok);
1228             // make sure that the index is in range
1229             logic = myJunctionControlBuilder.getTLLogic(tlID).getActive();
1230             if ((tlLinkIdx < 0 || tlLinkIdx >= (int)logic->getCurrentPhaseDef().getState().size())
1231                     && logic->getLogicType() != TLTYPE_RAIL_SIGNAL
1232                     && logic->getLogicType() != TLTYPE_RAIL_CROSSING) {
1233                 WRITE_ERROR("Invalid " + toString(SUMO_ATTR_TLLINKINDEX) + " '" + toString(tlLinkIdx) +
1234                             "' in connection controlled by '" + tlID + "'");
1235                 return;
1236             }
1237             if (!ok) {
1238                 return;
1239             }
1240         }
1241         double length;
1242         // build the link
1243         MSLane* via = nullptr;
1244         if (viaID != "" && MSGlobals::gUsingInternalLanes) {
1245             via = MSLane::dictionary(viaID);
1246             if (via == nullptr) {
1247                 WRITE_ERROR("An unknown lane ('" + viaID +
1248                             "') should be set as a via-lane for lane '" + toLane->getID() + "'.");
1249                 return;
1250             }
1251             length = via->getLength();
1252         } else {
1253             length = fromLane->getShape()[-1].distanceTo(toLane->getShape()[0]);
1254         }
1255         link = new MSLink(fromLane, toLane, via, dir, state, length, foeVisibilityDistance, keepClear, logic, tlLinkIdx);
1256         if (via != nullptr) {
1257             via->addIncomingLane(fromLane, link);
1258         } else {
1259             toLane->addIncomingLane(fromLane, link);
1260         }
1261         toLane->addApproachingLane(fromLane, myNetworkVersion < 0.25);
1262 
1263         // if a traffic light is responsible for it, inform the traffic light
1264         // check whether this link is controlled by a traffic light
1265         // we can not reuse logic here because it might be an inactive one
1266         if (tlID != "") {
1267             myJunctionControlBuilder.getTLLogic(tlID).addLink(link, fromLane, tlLinkIdx);
1268         }
1269         // add the link
1270         fromLane->addLink(link);
1271 
1272     } catch (InvalidArgument& e) {
1273         WRITE_ERROR(e.what());
1274     }
1275 }
1276 
1277 
1278 LinkDirection
parseLinkDir(const std::string & dir)1279 NLHandler::parseLinkDir(const std::string& dir) {
1280     if (SUMOXMLDefinitions::LinkDirections.hasString(dir)) {
1281         return SUMOXMLDefinitions::LinkDirections.get(dir);
1282     } else {
1283         throw InvalidArgument("Unrecognised link direction '" + dir + "'.");
1284     }
1285 }
1286 
1287 
1288 LinkState
parseLinkState(const std::string & state)1289 NLHandler::parseLinkState(const std::string& state) {
1290     if (SUMOXMLDefinitions::LinkStates.hasString(state)) {
1291         return SUMOXMLDefinitions::LinkStates.get(state);
1292     } else {
1293         if (state == "t") { // legacy networks
1294             // WRITE_WARNING("Obsolete link state 't'. Use 'o' instead");
1295             return LINKSTATE_TL_OFF_BLINKING;
1296         } else {
1297             throw InvalidArgument("Unrecognised link state '" + state + "'.");
1298         }
1299     }
1300 }
1301 
1302 
1303 // ----------------------------------
1304 void
setLocation(const SUMOSAXAttributes & attrs)1305 NLHandler::setLocation(const SUMOSAXAttributes& attrs) {
1306     if (myNetIsLoaded) {
1307         //WRITE_WARNING("POIs and Polygons should be loaded using option --po-files")
1308         return;
1309     }
1310     bool ok = true;
1311     PositionVector s = attrs.get<PositionVector>(SUMO_ATTR_NET_OFFSET, nullptr, ok);
1312     Boundary convBoundary = attrs.get<Boundary>(SUMO_ATTR_CONV_BOUNDARY, nullptr, ok);
1313     Boundary origBoundary = attrs.get<Boundary>(SUMO_ATTR_ORIG_BOUNDARY, nullptr, ok);
1314     std::string proj = attrs.get<std::string>(SUMO_ATTR_ORIG_PROJ, nullptr, ok);
1315     if (ok) {
1316         Position networkOffset = s[0];
1317         GeoConvHelper::init(proj, networkOffset, origBoundary, convBoundary);
1318         if (OptionsCont::getOptions().getBool("fcd-output.geo") && !GeoConvHelper::getFinal().usingGeoProjection()) {
1319             WRITE_WARNING("no valid geo projection loaded from network. fcd-output.geo will not work");
1320         }
1321     }
1322 }
1323 
1324 
1325 void
addDistrict(const SUMOSAXAttributes & attrs)1326 NLHandler::addDistrict(const SUMOSAXAttributes& attrs) {
1327     bool ok = true;
1328     myCurrentIsBroken = false;
1329     // get the id, report an error if not given or empty...
1330     myCurrentDistrictID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
1331     if (!ok) {
1332         myCurrentIsBroken = true;
1333         return;
1334     }
1335     try {
1336         MSEdge* sink = myEdgeControlBuilder.buildEdge(myCurrentDistrictID + "-sink", EDGEFUNC_CONNECTOR, "", "", -1);
1337         if (!MSEdge::dictionary(myCurrentDistrictID + "-sink", sink)) {
1338             delete sink;
1339             throw InvalidArgument("Another edge with the id '" + myCurrentDistrictID + "-sink' exists.");
1340         }
1341         sink->initialize(new std::vector<MSLane*>());
1342         MSEdge* source = myEdgeControlBuilder.buildEdge(myCurrentDistrictID + "-source", EDGEFUNC_CONNECTOR, "", "", -1);
1343         if (!MSEdge::dictionary(myCurrentDistrictID + "-source", source)) {
1344             delete source;
1345             throw InvalidArgument("Another edge with the id '" + myCurrentDistrictID + "-source' exists.");
1346         }
1347         source->initialize(new std::vector<MSLane*>());
1348         if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
1349             std::vector<std::string> desc = attrs.getStringVector(SUMO_ATTR_EDGES);
1350             for (std::vector<std::string>::const_iterator i = desc.begin(); i != desc.end(); ++i) {
1351                 MSEdge* edge = MSEdge::dictionary(*i);
1352                 // check whether the edge exists
1353                 if (edge == nullptr) {
1354                     throw InvalidArgument("The edge '" + *i + "' within district '" + myCurrentDistrictID + "' is not known.");
1355                 }
1356                 source->addSuccessor(edge);
1357                 edge->addSuccessor(sink);
1358             }
1359         }
1360         RGBColor color = attrs.getOpt<RGBColor>(SUMO_ATTR_COLOR, myCurrentDistrictID.c_str(), ok, RGBColor::parseColor("1.0,.33,.33"));
1361         source->setParameter("tazColor", toString(color));
1362         sink->setParameter("tazColor", toString(color));
1363 
1364         if (attrs.hasAttribute(SUMO_ATTR_SHAPE)) {
1365             PositionVector shape = attrs.get<PositionVector>(SUMO_ATTR_SHAPE, myCurrentDistrictID.c_str(), ok);
1366             if (shape.size() != 0) {
1367                 if (!myNet.getShapeContainer().addPolygon(myCurrentDistrictID, "taz", color, 0, 0, "", false, shape, false, false, 1.0)) {
1368                     WRITE_WARNING("Skipping visualization of taz '" + myCurrentDistrictID + "', polygon already exists.");
1369                 }
1370             }
1371         }
1372     } catch (InvalidArgument& e) {
1373         WRITE_ERROR(e.what());
1374         myCurrentIsBroken = true;
1375     }
1376 }
1377 
1378 
1379 void
addDistrictEdge(const SUMOSAXAttributes & attrs,bool isSource)1380 NLHandler::addDistrictEdge(const SUMOSAXAttributes& attrs, bool isSource) {
1381     if (myCurrentIsBroken) {
1382         // earlier error
1383         return;
1384     }
1385     bool ok = true;
1386     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, myCurrentDistrictID.c_str(), ok);
1387     MSEdge* succ = MSEdge::dictionary(id);
1388     if (succ != nullptr) {
1389         // connect edge
1390         if (isSource) {
1391             MSEdge::dictionary(myCurrentDistrictID + "-source")->addSuccessor(succ);
1392         } else {
1393             succ->addSuccessor(MSEdge::dictionary(myCurrentDistrictID + "-sink"));
1394         }
1395     } else {
1396         WRITE_ERROR("At district '" + myCurrentDistrictID + "': succeeding edge '" + id + "' does not exist.");
1397     }
1398 }
1399 
1400 
1401 void
addRoundabout(const SUMOSAXAttributes & attrs)1402 NLHandler::addRoundabout(const SUMOSAXAttributes& attrs) {
1403     if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
1404         std::vector<std::string> edgeIDs = attrs.getStringVector(SUMO_ATTR_EDGES);
1405         for (std::vector<std::string>::iterator it = edgeIDs.begin(); it != edgeIDs.end(); ++it) {
1406             MSEdge* edge = MSEdge::dictionary(*it);
1407             if (edge == nullptr) {
1408                 WRITE_ERROR("Unknown edge '" + (*it) + "' in roundabout");
1409             } else {
1410                 edge->markAsRoundabout();
1411             }
1412         }
1413     } else {
1414         WRITE_ERROR("Empty edges in roundabout.");
1415     }
1416 }
1417 
1418 
1419 // ----------------------------------
1420 void
endE3Detector()1421 NLHandler::endE3Detector() {
1422     try {
1423         myDetectorBuilder.endE3Detector();
1424     } catch (InvalidArgument& e) {
1425         WRITE_ERROR(e.what());
1426     }
1427 }
1428 
1429 
1430 void
closeWAUT()1431 NLHandler::closeWAUT() {
1432     if (!myCurrentIsBroken) {
1433         try {
1434             myJunctionControlBuilder.getTLLogicControlToUse().closeWAUT(myCurrentWAUTID);
1435         } catch (InvalidArgument& e) {
1436             WRITE_ERROR(e.what());
1437             myCurrentIsBroken = true;
1438         }
1439     }
1440     myCurrentWAUTID = "";
1441 }
1442 
1443 
1444 Position
getLanePos(const std::string & poiID,const std::string & laneID,double lanePos,double lanePosLat)1445 NLShapeHandler::getLanePos(const std::string& poiID, const std::string& laneID, double lanePos, double lanePosLat) {
1446     MSLane* lane = MSLane::dictionary(laneID);
1447     if (lane == nullptr) {
1448         WRITE_ERROR("Lane '" + laneID + "' to place poi '" + poiID + "' on is not known.");
1449         return Position::INVALID;
1450     }
1451     if (lanePos < 0) {
1452         lanePos = lane->getLength() + lanePos;
1453     }
1454     if (lanePos < 0 || lanePos > lane->getLength()) {
1455         WRITE_WARNING("lane position " + toString(lanePos) + " for poi '" + poiID + "' is not valid.");
1456     }
1457     return lane->geometryPositionAtOffset(lanePos, -lanePosLat);
1458 }
1459 /****************************************************************************/
1460