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