Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netload/NLHandler.cpp
193674 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.
4
// This program and the accompanying materials are made available under the
5
// terms of the Eclipse Public License 2.0 which is available at
6
// https://www.eclipse.org/legal/epl-2.0/
7
// This Source Code may also be made available under the following Secondary
8
// Licenses when the conditions for such availability set forth in the Eclipse
9
// Public License 2.0 are satisfied: GNU General Public License, version 2
10
// or later which is available at
11
// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13
/****************************************************************************/
14
/// @file NLHandler.cpp
15
/// @author Daniel Krajzewicz
16
/// @author Jakob Erdmann
17
/// @author Clemens Honomichl
18
/// @author Sascha Krieg
19
/// @author Michael Behrisch
20
/// @author Felix Brack
21
/// @date Mon, 9 Jul 2001
22
///
23
// The XML-Handler for network loading
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/MSStoppingPlace.h>
46
#include <microsim/traffic_lights/MSTrafficLightLogic.h>
47
#include <microsim/traffic_lights/MSRailSignal.h>
48
#include <microsim/traffic_lights/MSRailSignalControl.h>
49
#include <microsim/traffic_lights/MSRailSignalConstraint.h>
50
#include <mesosim/MESegment.h>
51
#include <utils/iodevices/OutputDevice.h>
52
#include <utils/common/UtilExceptions.h>
53
#include <utils/geom/GeoConvHelper.h>
54
#include <utils/shapes/ShapeContainer.h>
55
#include <utils/shapes/Shape.h>
56
57
58
// ===========================================================================
59
// method definitions
60
// ===========================================================================
61
NLHandler::NLHandler(const std::string& file, MSNet& net,
62
NLDetectorBuilder& detBuilder,
63
NLTriggerBuilder& triggerBuilder,
64
NLEdgeControlBuilder& edgeBuilder,
65
NLJunctionControlBuilder& junctionBuilder) :
66
MSRouteHandler(file, true),
67
myNet(net), myActionBuilder(net),
68
myCurrentIsInternalToSkip(false),
69
myDetectorBuilder(detBuilder), myTriggerBuilder(triggerBuilder),
70
myEdgeControlBuilder(edgeBuilder), myJunctionControlBuilder(junctionBuilder),
71
myAmParsingTLLogicOrJunction(false), myCurrentIsBroken(false),
72
myHaveWarnedAboutInvalidTLType(false),
73
myHaveSeenInternalEdge(false),
74
myHaveJunctionHigherSpeeds(false),
75
myHaveSeenDefaultLength(false),
76
myHaveSeenNeighs(false),
77
myHaveSeenAdditionalSpeedRestrictions(false),
78
myHaveSeenTLSParams(false),
79
myNetworkVersion(0, 0),
80
myNetIsLoaded(false) {
81
}
82
83
84
NLHandler::~NLHandler() {}
85
86
87
void
88
NLHandler::myStartElement(int element,
89
const SUMOSAXAttributes& attrs) {
90
try {
91
switch (element) {
92
case SUMO_TAG_NET: {
93
bool ok;
94
MSGlobals::gLefthand = attrs.getOpt<bool>(SUMO_ATTR_LEFTHAND, nullptr, ok, false);
95
myHaveJunctionHigherSpeeds = attrs.getOpt<bool>(SUMO_ATTR_HIGHER_SPEED, nullptr, ok, false);
96
myNetworkVersion = StringUtils::toVersion(attrs.get<std::string>(SUMO_ATTR_VERSION, nullptr, ok, false));
97
break;
98
}
99
case SUMO_TAG_EDGE:
100
beginEdgeParsing(attrs);
101
break;
102
case SUMO_TAG_LANE:
103
addLane(attrs);
104
break;
105
case SUMO_TAG_NEIGH:
106
if (!myCurrentIsInternalToSkip) {
107
myEdgeControlBuilder.addNeigh(attrs.getString(SUMO_ATTR_LANE));
108
}
109
myHaveSeenNeighs = true;
110
break;
111
case SUMO_TAG_JUNCTION:
112
openJunction(attrs);
113
initJunctionLogic(attrs);
114
break;
115
case SUMO_TAG_PHASE:
116
addPhase(attrs);
117
break;
118
case SUMO_TAG_CONDITION:
119
addCondition(attrs);
120
break;
121
case SUMO_TAG_ASSIGNMENT:
122
addAssignment(attrs);
123
break;
124
case SUMO_TAG_FUNCTION:
125
addFunction(attrs);
126
break;
127
case SUMO_TAG_CONNECTION:
128
addConnection(attrs);
129
break;
130
case SUMO_TAG_CONFLICT:
131
addConflict(attrs);
132
break;
133
case SUMO_TAG_TLLOGIC:
134
initTrafficLightLogic(attrs);
135
break;
136
case SUMO_TAG_REQUEST:
137
addRequest(attrs);
138
break;
139
case SUMO_TAG_WAUT:
140
openWAUT(attrs);
141
break;
142
case SUMO_TAG_WAUT_SWITCH:
143
addWAUTSwitch(attrs);
144
break;
145
case SUMO_TAG_WAUT_JUNCTION:
146
addWAUTJunction(attrs);
147
break;
148
case SUMO_TAG_E1DETECTOR:
149
case SUMO_TAG_INDUCTION_LOOP:
150
addE1Detector(attrs);
151
break;
152
case SUMO_TAG_E2DETECTOR:
153
case SUMO_TAG_LANE_AREA_DETECTOR:
154
addE2Detector(attrs);
155
break;
156
case SUMO_TAG_E3DETECTOR:
157
case SUMO_TAG_ENTRY_EXIT_DETECTOR:
158
beginE3Detector(attrs);
159
break;
160
case SUMO_TAG_DET_ENTRY:
161
addE3Entry(attrs);
162
break;
163
case SUMO_TAG_DET_EXIT:
164
addE3Exit(attrs);
165
break;
166
case SUMO_TAG_INSTANT_INDUCTION_LOOP:
167
addInstantE1Detector(attrs);
168
break;
169
case SUMO_TAG_VSS:
170
myTriggerBuilder.parseAndBuildLaneSpeedTrigger(myNet, attrs, getFileName());
171
break;
172
case SUMO_TAG_CALIBRATOR:
173
myTriggerBuilder.parseAndBuildCalibrator(myNet, attrs, getFileName());
174
break;
175
case SUMO_TAG_REROUTER:
176
myTriggerBuilder.parseAndBuildRerouter(myNet, attrs);
177
break;
178
case SUMO_TAG_BUS_STOP:
179
case SUMO_TAG_TRAIN_STOP:
180
case SUMO_TAG_CONTAINER_STOP:
181
myTriggerBuilder.parseAndBuildStoppingPlace(myNet, attrs, (SumoXMLTag)element);
182
myLastParameterised.push_back(myTriggerBuilder.getCurrentStop());
183
break;
184
case SUMO_TAG_PARKING_SPACE:
185
myTriggerBuilder.parseAndAddLotEntry(attrs);
186
break;
187
case SUMO_TAG_PARKING_AREA:
188
myTriggerBuilder.parseAndBeginParkingArea(myNet, attrs);
189
myLastParameterised.push_back(myTriggerBuilder.getCurrentStop());
190
break;
191
case SUMO_TAG_ACCESS:
192
myTriggerBuilder.addAccess(myNet, attrs);
193
break;
194
case SUMO_TAG_CHARGING_STATION:
195
myTriggerBuilder.parseAndBuildChargingStation(myNet, attrs);
196
myLastParameterised.push_back(myTriggerBuilder.getCurrentStop());
197
break;
198
case SUMO_TAG_OVERHEAD_WIRE_SEGMENT:
199
myTriggerBuilder.parseAndBuildOverheadWireSegment(myNet, attrs);
200
break;
201
case SUMO_TAG_OVERHEAD_WIRE_SECTION:
202
myTriggerBuilder.parseAndBuildOverheadWireSection(myNet, attrs);
203
break;
204
case SUMO_TAG_TRACTION_SUBSTATION:
205
myTriggerBuilder.parseAndBuildTractionSubstation(myNet, attrs);
206
break;
207
case SUMO_TAG_OVERHEAD_WIRE_CLAMP:
208
myTriggerBuilder.parseAndBuildOverheadWireClamp(myNet, attrs);
209
break;
210
case SUMO_TAG_VTYPEPROBE:
211
addVTypeProbeDetector(attrs);
212
break;
213
case SUMO_TAG_ROUTEPROBE:
214
addRouteProbeDetector(attrs);
215
break;
216
case SUMO_TAG_MEANDATA_EDGE:
217
addEdgeLaneMeanData(attrs, SUMO_TAG_MEANDATA_EDGE);
218
break;
219
case SUMO_TAG_MEANDATA_LANE:
220
addEdgeLaneMeanData(attrs, SUMO_TAG_MEANDATA_LANE);
221
break;
222
case SUMO_TAG_TIMEDEVENT:
223
myActionBuilder.addAction(attrs, getFileName());
224
break;
225
case SUMO_TAG_VAPORIZER:
226
myTriggerBuilder.buildVaporizer(attrs);
227
break;
228
case SUMO_TAG_LOCATION:
229
setLocation(attrs);
230
break;
231
case SUMO_TAG_TAZ:
232
addDistrict(attrs);
233
break;
234
case SUMO_TAG_TAZSOURCE:
235
addDistrictEdge(attrs, true);
236
break;
237
case SUMO_TAG_TAZSINK:
238
addDistrictEdge(attrs, false);
239
break;
240
case SUMO_TAG_ROUNDABOUT:
241
addRoundabout(attrs);
242
break;
243
case SUMO_TAG_TYPE: {
244
bool ok = true;
245
myCurrentTypeID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
246
break;
247
}
248
case SUMO_TAG_RESTRICTION: {
249
bool ok = true;
250
const SUMOVehicleClass svc = getVehicleClassID(attrs.get<std::string>(SUMO_ATTR_VCLASS, myCurrentTypeID.c_str(), ok));
251
const double speed = attrs.get<double>(SUMO_ATTR_SPEED, myCurrentTypeID.c_str(), ok);
252
if (ok) {
253
myNet.addRestriction(myCurrentTypeID, svc, speed);
254
}
255
if (myNetIsLoaded) {
256
myHaveSeenAdditionalSpeedRestrictions = true;
257
}
258
break;
259
}
260
case SUMO_TAG_PREFERENCE: {
261
bool ok = true;
262
const std::string routingType = attrs.get<std::string>(SUMO_ATTR_ROUTINGTYPE, nullptr, ok);
263
const double prio = attrs.get<double>(SUMO_ATTR_PRIORITY, routingType.c_str(), ok);
264
if (prio <= 0) {
265
throw InvalidArgument("In preference for routingType '" + routingType + "', priority must be positve");
266
}
267
if (attrs.hasAttribute(SUMO_ATTR_VCLASSES)) {
268
StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_VCLASSES, routingType.c_str(), ok));
269
for (std::string className : st.getVector()) {
270
myNet.addPreference(routingType, getVehicleClassID(className), prio);
271
}
272
} else if (!attrs.hasAttribute(SUMO_ATTR_VTYPES)) {
273
// general preferenze applying to all types and vClasses
274
myNet.addPreference(routingType, "", prio);
275
}
276
if (attrs.hasAttribute(SUMO_ATTR_VTYPES)) {
277
StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_VTYPES, routingType.c_str(), ok));
278
for (std::string typeName : st.getVector()) {
279
myNet.addPreference(routingType, typeName, prio);
280
}
281
}
282
break;
283
}
284
case SUMO_TAG_STOPOFFSET: {
285
bool ok = true;
286
const StopOffset stopOffset(attrs, ok);
287
if (!ok) {
288
WRITE_ERROR(myEdgeControlBuilder.reportCurrentEdgeOrLane());
289
} else {
290
myEdgeControlBuilder.addStopOffsets(stopOffset);
291
}
292
break;
293
}
294
case SUMO_TAG_RAILSIGNAL_CONSTRAINTS: {
295
bool ok = true;
296
const std::string signalID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
297
if (!MSNet::getInstance()->getTLSControl().knows(signalID)) {
298
throw InvalidArgument("Rail signal '" + signalID + "' in railSignalConstraints is not known");
299
}
300
myConstrainedSignal = dynamic_cast<MSRailSignal*>(MSNet::getInstance()->getTLSControl().get(signalID).getDefault());
301
if (myConstrainedSignal == nullptr) {
302
throw InvalidArgument("Traffic light '" + signalID + "' is not a rail signal");
303
}
304
break;
305
}
306
case SUMO_TAG_PREDECESSOR: // intended fall-through
307
case SUMO_TAG_FOE_INSERTION: // intended fall-through
308
case SUMO_TAG_INSERTION_PREDECESSOR: // intended fall-through
309
case SUMO_TAG_INSERTION_ORDER: // intended fall-through
310
case SUMO_TAG_BIDI_PREDECESSOR:
311
myLastParameterised.push_back(addPredecessorConstraint(element, attrs, myConstrainedSignal));
312
break;
313
case SUMO_TAG_DEADLOCK:
314
addDeadlock(attrs);
315
break;
316
default:
317
break;
318
}
319
} catch (InvalidArgument& e) {
320
myCurrentIsBroken = true;
321
WRITE_ERROR(e.what());
322
}
323
MSRouteHandler::myStartElement(element, attrs);
324
if (element == SUMO_TAG_PARAM && !myCurrentIsBroken) {
325
addParam(attrs);
326
}
327
}
328
329
330
void
331
NLHandler::myEndElement(int element) {
332
switch (element) {
333
case SUMO_TAG_EDGE:
334
closeEdge();
335
break;
336
case SUMO_TAG_LANE:
337
myEdgeControlBuilder.closeLane();
338
if (!myCurrentIsInternalToSkip && !myCurrentIsBroken) {
339
myLastParameterised.pop_back();
340
}
341
break;
342
case SUMO_TAG_JUNCTION:
343
if (!myCurrentIsBroken) {
344
try {
345
myJunctionControlBuilder.closeJunction(getFileName());
346
} catch (InvalidArgument& e) {
347
WRITE_ERROR(e.what());
348
}
349
}
350
myAmParsingTLLogicOrJunction = false;
351
break;
352
case SUMO_TAG_TLLOGIC:
353
if (!myCurrentIsBroken) {
354
try {
355
myJunctionControlBuilder.closeTrafficLightLogic(getFileName());
356
} catch (InvalidArgument& e) {
357
for (MSPhaseDefinition* const phase : myJunctionControlBuilder.getLoadedPhases()) {
358
delete phase;
359
}
360
WRITE_ERROR(e.what());
361
}
362
}
363
myAmParsingTLLogicOrJunction = false;
364
break;
365
case SUMO_TAG_FUNCTION:
366
closeFunction();
367
break;
368
case SUMO_TAG_WAUT:
369
closeWAUT();
370
break;
371
case SUMO_TAG_RAILSIGNAL_CONSTRAINTS:
372
myConstrainedSignal = nullptr;
373
break;
374
case SUMO_TAG_E1DETECTOR:
375
case SUMO_TAG_INDUCTION_LOOP:
376
case SUMO_TAG_INSTANT_INDUCTION_LOOP:
377
case SUMO_TAG_E2DETECTOR:
378
case SUMO_TAG_LANE_AREA_DETECTOR:
379
if (!myCurrentIsBroken) {
380
myLastParameterised.pop_back();
381
}
382
break;
383
case SUMO_TAG_E3DETECTOR:
384
case SUMO_TAG_ENTRY_EXIT_DETECTOR:
385
endE3Detector();
386
if (!myCurrentIsBroken) {
387
myLastParameterised.pop_back();
388
}
389
break;
390
case SUMO_TAG_PARKING_AREA:
391
myTriggerBuilder.updateParkingAreaDefaultCapacity();
392
myTriggerBuilder.endParkingArea();
393
myLastParameterised.pop_back();
394
break;
395
case SUMO_TAG_BUS_STOP:
396
case SUMO_TAG_TRAIN_STOP:
397
case SUMO_TAG_CONTAINER_STOP:
398
case SUMO_TAG_CHARGING_STATION:
399
myTriggerBuilder.endStoppingPlace();
400
myLastParameterised.pop_back();
401
break;
402
case SUMO_TAG_PREDECESSOR: // intended fall-through
403
case SUMO_TAG_FOE_INSERTION: // intended fall-through
404
case SUMO_TAG_INSERTION_PREDECESSOR: // intended fall-through
405
case SUMO_TAG_INSERTION_ORDER: // intended fall-through
406
case SUMO_TAG_BIDI_PREDECESSOR:
407
myLastParameterised.pop_back();
408
break;
409
case SUMO_TAG_NET:
410
// build junction graph
411
for (JunctionGraph::iterator it = myJunctionGraph.begin(); it != myJunctionGraph.end(); ++it) {
412
MSEdge* edge = MSEdge::dictionary(it->first);
413
MSJunction* from = myJunctionControlBuilder.retrieve(it->second.first);
414
MSJunction* to = myJunctionControlBuilder.retrieve(it->second.second);
415
if (from == nullptr) {
416
WRITE_ERRORF(TL("Unknown from-node '%' for edge '%'."), it->second.first, it->first);
417
return;
418
}
419
if (to == nullptr) {
420
WRITE_ERRORF(TL("Unknown to-node '%' for edge '%'."), it->second.second, it->first);
421
return;
422
}
423
if (edge != nullptr) {
424
edge->setJunctions(from, to);
425
from->addOutgoing(edge);
426
to->addIncoming(edge);
427
}
428
}
429
myNetIsLoaded = true;
430
break;
431
default:
432
break;
433
}
434
MSRouteHandler::myEndElement(element);
435
}
436
437
438
439
// ---- the root/edge - element
440
void
441
NLHandler::beginEdgeParsing(const SUMOSAXAttributes& attrs) {
442
bool ok = true;
443
myCurrentIsBroken = false;
444
// get the id, report an error if not given or empty...
445
std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
446
if (!ok) {
447
myCurrentIsBroken = true;
448
return;
449
}
450
// parse the function
451
const SumoXMLEdgeFunc func = attrs.getOpt<SumoXMLEdgeFunc>(SUMO_ATTR_FUNCTION, id.c_str(), ok, SumoXMLEdgeFunc::NORMAL);
452
if (!ok) {
453
myCurrentIsBroken = true;
454
return;
455
}
456
// omit internal edges if not wished
457
if (id[0] == ':') {
458
myHaveSeenInternalEdge = true;
459
if (!MSGlobals::gUsingInternalLanes && (func == SumoXMLEdgeFunc::CROSSING || func == SumoXMLEdgeFunc::WALKINGAREA)) {
460
myCurrentIsInternalToSkip = true;
461
return;
462
}
463
std::string junctionID = SUMOXMLDefinitions::getJunctionIDFromInternalEdge(id);
464
myJunctionGraph[id] = std::make_pair(junctionID, junctionID);
465
} else {
466
myHaveSeenDefaultLength |= !attrs.hasAttribute(SUMO_ATTR_LENGTH);
467
myJunctionGraph[id] = std::make_pair(
468
attrs.get<std::string>(SUMO_ATTR_FROM, id.c_str(), ok),
469
attrs.get<std::string>(SUMO_ATTR_TO, id.c_str(), ok));
470
}
471
if (!ok) {
472
myCurrentIsBroken = true;
473
return;
474
}
475
myCurrentIsInternalToSkip = false;
476
// get the street name
477
const std::string streetName = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
478
// get the edge type
479
const std::string edgeType = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, "");
480
const std::string routingType = attrs.getOpt<std::string>(SUMO_ATTR_ROUTINGTYPE, id.c_str(), ok, "");
481
// get the edge priority (only for visualization)
482
const int priority = attrs.getOpt<int>(SUMO_ATTR_PRIORITY, id.c_str(), ok, -1); // default taken from netbuild/NBFrame option 'default.priority'
483
// get the bidi-edge
484
const std::string bidi = attrs.getOpt<std::string>(SUMO_ATTR_BIDI, id.c_str(), ok, "");
485
// get the kilometrage/mileage (for visualization and output)
486
const double distance = attrs.getOpt<double>(SUMO_ATTR_DISTANCE, id.c_str(), ok, 0);
487
488
if (!ok) {
489
myCurrentIsBroken = true;
490
return;
491
}
492
//
493
try {
494
myEdgeControlBuilder.beginEdgeParsing(id, func, streetName, edgeType, routingType, priority, bidi, distance);
495
} catch (InvalidArgument& e) {
496
WRITE_ERROR(e.what());
497
myCurrentIsBroken = true;
498
}
499
500
if (func == SumoXMLEdgeFunc::CROSSING) {
501
//get the crossingEdges attribute (to implement the other side of the road pushbutton)
502
const std::string crossingEdges = attrs.getOpt<std::string>(SUMO_ATTR_CROSSING_EDGES, id.c_str(), ok, "");
503
if (!crossingEdges.empty()) {
504
std::vector<std::string> crossingEdgesVector;
505
StringTokenizer edges(crossingEdges);
506
while (edges.hasNext()) {
507
crossingEdgesVector.push_back(edges.next());
508
}
509
myEdgeControlBuilder.addCrossingEdges(crossingEdgesVector);
510
}
511
}
512
myLastEdgeParameters.clearParameter();
513
myLastParameterised.push_back(&myLastEdgeParameters);
514
}
515
516
517
void
518
NLHandler::closeEdge() {
519
myLastParameterised.clear();
520
// omit internal edges if not wished and broken edges
521
if (myCurrentIsInternalToSkip || myCurrentIsBroken) {
522
return;
523
}
524
try {
525
MSEdge* e = myEdgeControlBuilder.closeEdge();
526
MSEdge::dictionary(e->getID(), e);
527
e->updateParameters(myLastEdgeParameters.getParametersMap());
528
} catch (InvalidArgument& e) {
529
WRITE_ERROR(e.what());
530
}
531
}
532
533
534
// ---- the root/edge/lanes/lane - element
535
void
536
NLHandler::addLane(const SUMOSAXAttributes& attrs) {
537
// omit internal edges if not wished and broken edges
538
if (myCurrentIsInternalToSkip || myCurrentIsBroken) {
539
return;
540
}
541
bool ok = true;
542
// get the id, report an error if not given or empty...
543
std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
544
if (!ok) {
545
myCurrentIsBroken = true;
546
return;
547
}
548
const double maxSpeed = attrs.get<double>(SUMO_ATTR_SPEED, id.c_str(), ok);
549
const double friction = attrs.getOpt<double>(SUMO_ATTR_FRICTION, id.c_str(), ok, (double)(1.), false);
550
const double length = attrs.get<double>(SUMO_ATTR_LENGTH, id.c_str(), ok);
551
const std::string allow = attrs.getOpt<std::string>(SUMO_ATTR_ALLOW, id.c_str(), ok, "", false);
552
const std::string disallow = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, id.c_str(), ok, "");
553
const std::string changeLeftS = attrs.getOpt<std::string>(SUMO_ATTR_CHANGE_LEFT, id.c_str(), ok, "");
554
const std::string changeRightS = attrs.getOpt<std::string>(SUMO_ATTR_CHANGE_RIGHT, id.c_str(), ok, "");
555
const double width = attrs.getOpt<double>(SUMO_ATTR_WIDTH, id.c_str(), ok, SUMO_const_laneWidth);
556
const PositionVector shape = attrs.get<PositionVector>(SUMO_ATTR_SHAPE, id.c_str(), ok);
557
const PositionVector outlineShape = attrs.getOpt<PositionVector>(SUMO_ATTR_OUTLINESHAPE, id.c_str(), ok, PositionVector());
558
const int index = attrs.get<int>(SUMO_ATTR_INDEX, id.c_str(), ok);
559
const bool isRampAccel = attrs.getOpt<bool>(SUMO_ATTR_ACCELERATION, id.c_str(), ok, false);
560
const std::string type = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, "");
561
if (shape.size() < 2) {
562
WRITE_ERRORF(TL("Shape of lane '%' is broken.\n Can not build according edge."), id);
563
myCurrentIsBroken = true;
564
return;
565
}
566
const SVCPermissions permissions = parseVehicleClasses(allow, disallow, myNetworkVersion);
567
SVCPermissions changeLeft = parseVehicleClasses(changeLeftS, "", myNetworkVersion);
568
SVCPermissions changeRight = parseVehicleClasses(changeRightS, "", myNetworkVersion);
569
if (MSGlobals::gLefthand) {
570
// internally, changeLeft always checks for the higher lane index
571
// even though the higher lane index is to the right in a left-hand network
572
std::swap(changeLeft, changeRight);
573
}
574
if (permissions != SVCAll || changeLeft != SVCAll || changeRight != SVCAll) {
575
myNet.setPermissionsFound();
576
}
577
myCurrentIsBroken |= !ok;
578
if (!myCurrentIsBroken) {
579
try {
580
MSLane* lane = myEdgeControlBuilder.addLane(id, maxSpeed, friction, length, shape, width, permissions, changeLeft, changeRight, index, isRampAccel, type, outlineShape);
581
// insert the lane into the lane-dictionary, checking
582
if (!MSLane::dictionary(id, lane)) {
583
delete lane;
584
WRITE_ERRORF(TL("Another lane with the id '%' exists."), id);
585
myCurrentIsBroken = true;
586
myLastParameterised.push_back(nullptr);
587
} else {
588
myLastParameterised.push_back(lane);
589
}
590
} catch (InvalidArgument& e) {
591
WRITE_ERROR(e.what());
592
}
593
}
594
}
595
596
597
// ---- the root/junction - element
598
void
599
NLHandler::openJunction(const SUMOSAXAttributes& attrs) {
600
myCurrentIsBroken = false;
601
bool ok = true;
602
// get the id, report an error if not given or empty...
603
std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
604
if (!ok) {
605
myCurrentIsBroken = true;
606
return;
607
}
608
PositionVector shape;
609
if (attrs.hasAttribute(SUMO_ATTR_SHAPE)) {
610
// inner junctions have no shape
611
shape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, id.c_str(), ok, PositionVector());
612
if (shape.size() > 2) {
613
shape.closePolygon();
614
}
615
}
616
double x = attrs.get<double>(SUMO_ATTR_X, id.c_str(), ok);
617
double y = attrs.get<double>(SUMO_ATTR_Y, id.c_str(), ok);
618
double z = attrs.getOpt<double>(SUMO_ATTR_Z, id.c_str(), ok, 0);
619
const SumoXMLNodeType type = attrs.get<SumoXMLNodeType>(SUMO_ATTR_TYPE, id.c_str(), ok);
620
std::string key = attrs.getOpt<std::string>(SUMO_ATTR_KEY, id.c_str(), ok, "");
621
std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
622
// incoming lanes
623
std::vector<MSLane*> incomingLanes;
624
parseLanes(id, attrs.getStringSecure(SUMO_ATTR_INCLANES, ""), incomingLanes, ok);
625
// internal lanes
626
std::vector<MSLane*> internalLanes;
627
if (MSGlobals::gUsingInternalLanes) {
628
parseLanes(id, attrs.getStringSecure(SUMO_ATTR_INTLANES, ""), internalLanes, ok);
629
}
630
if (!ok) {
631
myCurrentIsBroken = true;
632
} else {
633
try {
634
myJunctionControlBuilder.openJunction(id, key, type, Position(x, y, z), shape, incomingLanes, internalLanes, name);
635
} catch (InvalidArgument& e) {
636
WRITE_ERROR(e.what() + std::string("\n Can not build according junction."));
637
myCurrentIsBroken = true;
638
}
639
}
640
}
641
642
643
void
644
NLHandler::parseLanes(const std::string& junctionID,
645
const std::string& def, std::vector<MSLane*>& into, bool& ok) {
646
StringTokenizer st(def, " ");
647
while (ok && st.hasNext()) {
648
std::string laneID = st.next();
649
MSLane* lane = MSLane::dictionary(laneID);
650
if (!MSGlobals::gUsingInternalLanes && laneID[0] == ':') {
651
continue;
652
}
653
if (lane == nullptr) {
654
WRITE_ERRORF(TL("An unknown lane ('%') was tried to be set as incoming to junction '%'."), laneID, junctionID);
655
ok = false;
656
continue;
657
}
658
into.push_back(lane);
659
}
660
}
661
// ----
662
663
void
664
NLHandler::addParam(const SUMOSAXAttributes& attrs) {
665
bool ok = true;
666
const std::string key = attrs.get<std::string>(SUMO_ATTR_KEY, nullptr, ok);
667
// circumventing empty string test
668
const std::string val = attrs.hasAttribute(SUMO_ATTR_VALUE) ? attrs.getString(SUMO_ATTR_VALUE) : "";
669
if (myLastParameterised.size() > 0 && myLastParameterised.back() != nullptr) {
670
myLastParameterised.back()->setParameter(key, val);
671
}
672
// set
673
if (ok && myAmParsingTLLogicOrJunction) {
674
assert(key != "");
675
myJunctionControlBuilder.addParam(key, val);
676
if (myNetIsLoaded) {
677
myHaveSeenTLSParams = true;
678
}
679
}
680
}
681
682
683
void
684
NLHandler::openWAUT(const SUMOSAXAttributes& attrs) {
685
myCurrentIsBroken = false;
686
bool ok = true;
687
// get the id, report an error if not given or empty...
688
std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
689
if (!ok) {
690
myCurrentIsBroken = true;
691
return;
692
}
693
SUMOTime refTime = attrs.getOptSUMOTimeReporting(SUMO_ATTR_REF_TIME, id.c_str(), ok, 0);
694
SUMOTime period = attrs.getOptSUMOTimeReporting(SUMO_ATTR_PERIOD, id.c_str(), ok, 0);
695
std::string startProg = attrs.get<std::string>(SUMO_ATTR_START_PROG, id.c_str(), ok);
696
if (!ok) {
697
myCurrentIsBroken = true;
698
}
699
if (!myCurrentIsBroken) {
700
myCurrentWAUTID = id;
701
try {
702
myJunctionControlBuilder.getTLLogicControlToUse().addWAUT(refTime, id, startProg, period);
703
} catch (InvalidArgument& e) {
704
WRITE_ERROR(e.what());
705
myCurrentIsBroken = true;
706
}
707
}
708
}
709
710
711
void
712
NLHandler::addWAUTSwitch(const SUMOSAXAttributes& attrs) {
713
bool ok = true;
714
SUMOTime t = attrs.getSUMOTimeReporting(SUMO_ATTR_TIME, myCurrentWAUTID.c_str(), ok);
715
std::string to = attrs.get<std::string>(SUMO_ATTR_TO, myCurrentWAUTID.c_str(), ok);
716
if (!ok) {
717
myCurrentIsBroken = true;
718
}
719
if (!myCurrentIsBroken) {
720
try {
721
myJunctionControlBuilder.getTLLogicControlToUse().addWAUTSwitch(myCurrentWAUTID, t, to);
722
} catch (InvalidArgument& e) {
723
WRITE_ERROR(e.what());
724
myCurrentIsBroken = true;
725
}
726
}
727
}
728
729
730
void
731
NLHandler::addWAUTJunction(const SUMOSAXAttributes& attrs) {
732
bool ok = true;
733
std::string wautID = attrs.get<std::string>(SUMO_ATTR_WAUT_ID, nullptr, ok);
734
std::string junctionID = attrs.get<std::string>(SUMO_ATTR_JUNCTION_ID, nullptr, ok);
735
std::string procedure = attrs.getOpt<std::string>(SUMO_ATTR_PROCEDURE, nullptr, ok, "");
736
bool synchron = attrs.getOpt<bool>(SUMO_ATTR_SYNCHRON, nullptr, ok, false);
737
if (!ok) {
738
myCurrentIsBroken = true;
739
}
740
try {
741
if (!myCurrentIsBroken) {
742
myJunctionControlBuilder.getTLLogicControlToUse().addWAUTJunction(wautID, junctionID, procedure, synchron);
743
}
744
} catch (InvalidArgument& e) {
745
WRITE_ERROR(e.what());
746
myCurrentIsBroken = true;
747
}
748
}
749
750
751
void
752
NLHandler::addRequest(const SUMOSAXAttributes& attrs) {
753
if (myCurrentIsBroken) {
754
return;
755
}
756
bool ok = true;
757
int request = attrs.get<int>(SUMO_ATTR_INDEX, nullptr, ok);
758
bool cont = false;
759
cont = attrs.getOpt<bool>(SUMO_ATTR_CONT, nullptr, ok, false);
760
std::string response = attrs.get<std::string>(SUMO_ATTR_RESPONSE, nullptr, ok);
761
std::string foes = attrs.get<std::string>(SUMO_ATTR_FOES, nullptr, ok);
762
if (!ok) {
763
return;
764
}
765
// store received information
766
if (request >= 0 && response.length() > 0) {
767
try {
768
myJunctionControlBuilder.addLogicItem(request, response, foes, cont);
769
} catch (InvalidArgument& e) {
770
WRITE_ERROR(e.what());
771
}
772
}
773
}
774
775
776
void
777
NLHandler::initJunctionLogic(const SUMOSAXAttributes& attrs) {
778
if (myCurrentIsBroken) {
779
return;
780
}
781
myAmParsingTLLogicOrJunction = true;
782
bool ok = true;
783
// we either a have a junction or a legacy network with ROWLogic
784
std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
785
if (ok) {
786
myJunctionControlBuilder.initJunctionLogic(id);
787
}
788
}
789
790
791
void
792
NLHandler::initTrafficLightLogic(const SUMOSAXAttributes& attrs) {
793
myCurrentIsBroken = false;
794
myAmParsingTLLogicOrJunction = true;
795
bool ok = true;
796
std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
797
std::string programID = attrs.getOpt<std::string>(SUMO_ATTR_PROGRAMID, id.c_str(), ok, "<unknown>");
798
TrafficLightType type = TrafficLightType::STATIC;
799
std::string typeS;
800
if (myJunctionControlBuilder.getTLLogicControlToUse().get(id, programID) == nullptr) {
801
// SUMO_ATTR_TYPE is not needed when only modifying the offset of an
802
// existing program
803
typeS = attrs.get<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok);
804
if (!ok) {
805
myCurrentIsBroken = true;
806
return;
807
}
808
if (SUMOXMLDefinitions::TrafficLightTypes.hasString(typeS)) {
809
type = SUMOXMLDefinitions::TrafficLightTypes.get(typeS);
810
} else {
811
WRITE_ERRORF(TL("Traffic light '%' has unknown type '%'."), id, typeS);
812
}
813
if (MSGlobals::gUseMesoSim && (type == TrafficLightType::ACTUATED || type == TrafficLightType::NEMA)) {
814
if (!myHaveWarnedAboutInvalidTLType) {
815
WRITE_WARNINGF(TL("Traffic light type '%' cannot be used in mesoscopic simulation. Using '%' as fallback."), toString(type), toString(TrafficLightType::STATIC));
816
myHaveWarnedAboutInvalidTLType = true;
817
}
818
type = TrafficLightType::STATIC;
819
}
820
}
821
SUMOTime offset = attrs.getOptOffsetReporting(SUMO_ATTR_OFFSET, id.c_str(), ok, 0);
822
if (offset == SUMOTime_MAX) {
823
offset = string2time(OptionsCont::getOptions().getString("begin"));
824
}
825
if (!ok) {
826
myCurrentIsBroken = true;
827
return;
828
}
829
myJunctionControlBuilder.initTrafficLightLogic(id, programID, type, offset);
830
}
831
832
833
void
834
NLHandler::addPhase(const SUMOSAXAttributes& attrs) {
835
// try to get the phase definition
836
bool ok = true;
837
const std::string& id = myJunctionControlBuilder.getActiveKey();
838
const SUMOTime tDefault = MSPhaseDefinition::UNSPECIFIED_DURATION;
839
840
const SUMOTime duration = attrs.getSUMOTimeReporting(SUMO_ATTR_DURATION, myJunctionControlBuilder.getActiveKey().c_str(), ok);
841
const std::string state = attrs.get<std::string>(SUMO_ATTR_STATE, nullptr, ok);
842
if (duration == 0) {
843
WRITE_ERROR("Duration of phase " + toString(myJunctionControlBuilder.getLoadedPhases().size())
844
+ " for tlLogic '" + myJunctionControlBuilder.getActiveKey()
845
+ "' program '" + myJunctionControlBuilder.getActiveSubKey() + "' is zero.");
846
return;
847
}
848
if (!ok) {
849
return;
850
}
851
MSPhaseDefinition* phase = new MSPhaseDefinition(duration, state);
852
853
// if the traffic light is an actuated traffic light, try to get
854
// the minimum and maximum durations
855
phase->minDuration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_MINDURATION, id.c_str(), ok, duration);
856
// if minDur is set but not maxDur, assume high maxDur (avoid using the absolute max in case we do some arithmetic later)
857
SUMOTime defaultMaxDur = attrs.hasAttribute(SUMO_ATTR_MINDURATION) ? std::numeric_limits<int>::max() : duration;
858
phase->maxDuration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_MAXDURATION, id.c_str(), ok, defaultMaxDur);
859
phase->earliestEnd = attrs.getOptSUMOTimeReporting(SUMO_ATTR_EARLIEST_END, id.c_str(), ok, tDefault);
860
phase->latestEnd = attrs.getOptSUMOTimeReporting(SUMO_ATTR_LATEST_END, id.c_str(), ok, tDefault);
861
phase->nextPhases = attrs.getOpt<std::vector<int> >(SUMO_ATTR_NEXT, id.c_str(), ok);
862
phase->earlyTarget = attrs.getOpt<std::string>(SUMO_ATTR_EARLY_TARGET, id.c_str(), ok);
863
phase->finalTarget = attrs.getOpt<std::string>(SUMO_ATTR_FINAL_TARGET, id.c_str(), ok);
864
phase->name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok);
865
866
phase->vehext = attrs.getOptSUMOTimeReporting(SUMO_ATTR_VEHICLEEXTENSION, id.c_str(), ok, tDefault);
867
phase->yellow = attrs.getOptSUMOTimeReporting(SUMO_ATTR_YELLOW, id.c_str(), ok, tDefault);
868
phase->red = attrs.getOptSUMOTimeReporting(SUMO_ATTR_RED, id.c_str(), ok, tDefault);
869
870
if (attrs.hasAttribute(SUMO_ATTR_TYPE)) {
871
//SOTL attributes
872
//If the type attribute is not present, the parsed phase is of type "undefined" (MSPhaseDefinition constructor),
873
//in this way SOTL traffic light logic can recognize the phase as unsuitable or decides other
874
//behaviors. See SOTL traffic light logic implementations.
875
std::string phaseTypeString;
876
try {
877
phaseTypeString = attrs.get<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, false);
878
} catch (EmptyData&) {
879
MsgHandler::getWarningInstance()->inform("Empty type definition. Assuming phase type as SUMOSOTL_TagAttrDefinitions::SOTL_ATTL_TYPE_TRANSIENT");
880
phase->myTransientNotDecisional = false;
881
}
882
if (phaseTypeString.find("decisional") != std::string::npos) {
883
phase->myTransientNotDecisional = false;
884
} else if (phaseTypeString.find("transient") != std::string::npos) {
885
phase->myTransientNotDecisional = true;
886
} else {
887
MsgHandler::getWarningInstance()->inform("SOTL_ATTL_TYPE_DECISIONAL nor SOTL_ATTL_TYPE_TRANSIENT. Assuming phase type as SUMOSOTL_TagAttrDefinitions::SOTL_ATTL_TYPE_TRANSIENT");
888
phase->myTransientNotDecisional = false;
889
}
890
phase->myCommit = (phaseTypeString.find("commit") != std::string::npos);
891
892
if (phaseTypeString.find("target") != std::string::npos) {
893
std::string delimiter(" ,;");
894
//Phase declared as target, getting targetLanes attribute
895
try {
896
phase->myTargetLaneSet = StringTokenizer(attrs.getStringSecure(SUMO_ATTR_TARGETLANE, ""), " ,;", true).getVector();
897
} catch (EmptyData&) {
898
MsgHandler::getErrorInstance()->inform("Missing targetLane definition for the target phase.");
899
delete phase;
900
return;
901
}
902
}
903
}
904
905
if (phase->maxDuration < phase->minDuration) {
906
WRITE_WARNINGF(TL("maxDur % should not be smaller than minDir % in phase of tlLogic %"), phase->maxDuration, phase->minDuration, id);
907
phase->maxDuration = phase->duration;
908
}
909
910
phase->myLastSwitch = string2time(OptionsCont::getOptions().getString("begin")) - 1; // SUMOTime-option
911
myJunctionControlBuilder.addPhase(phase);
912
}
913
914
915
void
916
NLHandler::addCondition(const SUMOSAXAttributes& attrs) {
917
bool ok = true;
918
const std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
919
const std::string value = attrs.get<std::string>(SUMO_ATTR_VALUE, id.c_str(), ok);
920
if (!myJunctionControlBuilder.addCondition(id, value)) {
921
WRITE_ERRORF(TL("Duplicate condition '%' in tlLogic '%'"), id, myJunctionControlBuilder.getActiveKey());
922
}
923
}
924
925
926
void
927
NLHandler::addAssignment(const SUMOSAXAttributes& attrs) {
928
bool ok = true;
929
const std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
930
const std::string check = attrs.get<std::string>(SUMO_ATTR_CHECK, nullptr, ok);
931
const std::string value = attrs.get<std::string>(SUMO_ATTR_VALUE, id.c_str(), ok);
932
myJunctionControlBuilder.addAssignment(id, check, value);
933
}
934
935
936
void
937
NLHandler::addFunction(const SUMOSAXAttributes& attrs) {
938
bool ok = true;
939
const std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
940
const int nArgs = attrs.get<int>(SUMO_ATTR_NARGS, nullptr, ok);
941
myJunctionControlBuilder.addFunction(id, nArgs);
942
}
943
944
void
945
NLHandler::closeFunction() {
946
myJunctionControlBuilder.closeFunction();
947
}
948
949
void
950
NLHandler::addE1Detector(const SUMOSAXAttributes& attrs) {
951
myCurrentIsBroken = false;
952
bool ok = true;
953
// get the id, report an error if not given or empty...
954
const std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
955
if (!ok) {
956
myCurrentIsBroken = true;
957
return;
958
}
959
const SUMOTime period = attrs.getOptPeriod(id.c_str(), ok, SUMOTime_MAX_PERIOD);
960
const double position = attrs.get<double>(SUMO_ATTR_POSITION, id.c_str(), ok);
961
const double length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, id.c_str(), ok, 0);
962
const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, id.c_str(), ok, false);
963
const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
964
const std::string vTypes = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id.c_str(), ok, "");
965
const std::string nextEdges = attrs.getOpt<std::string>(SUMO_ATTR_NEXT_EDGES, id.c_str(), ok, "");
966
const std::string lane = attrs.get<std::string>(SUMO_ATTR_LANE, id.c_str(), ok);
967
const std::string file = attrs.get<std::string>(SUMO_ATTR_FILE, id.c_str(), ok);
968
const std::string detectPersonsString = attrs.getOpt<std::string>(SUMO_ATTR_DETECT_PERSONS, id.c_str(), ok, "");
969
const int detectPersons = parseDetectPersons(detectPersonsString, id, ok);
970
if (!ok) {
971
myCurrentIsBroken = true;
972
return;
973
}
974
try {
975
Parameterised* det = myDetectorBuilder.buildInductLoop(id, lane, position, length, period,
976
FileHelpers::checkForRelativity(file, getFileName()),
977
friendlyPos, name, vTypes, nextEdges, detectPersons);
978
myLastParameterised.push_back(det);
979
} catch (InvalidArgument& e) {
980
myCurrentIsBroken = true;
981
WRITE_ERROR(e.what());
982
} catch (IOError& e) {
983
myCurrentIsBroken = true;
984
WRITE_ERROR(e.what());
985
}
986
}
987
988
989
void
990
NLHandler::addInstantE1Detector(const SUMOSAXAttributes& attrs) {
991
myCurrentIsBroken = false;
992
bool ok = true;
993
// get the id, report an error if not given or empty...
994
std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
995
if (!ok) {
996
myCurrentIsBroken = true;
997
return;
998
}
999
const double position = attrs.get<double>(SUMO_ATTR_POSITION, id.c_str(), ok);
1000
const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, id.c_str(), ok, false);
1001
const std::string lane = attrs.get<std::string>(SUMO_ATTR_LANE, id.c_str(), ok);
1002
const std::string file = attrs.get<std::string>(SUMO_ATTR_FILE, id.c_str(), ok);
1003
const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
1004
const std::string vTypes = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id.c_str(), ok, "");
1005
const std::string nextEdges = attrs.getOpt<std::string>(SUMO_ATTR_NEXT_EDGES, id.c_str(), ok, "");
1006
const std::string detectPersonsString = attrs.getOpt<std::string>(SUMO_ATTR_DETECT_PERSONS, id.c_str(), ok, "");
1007
const int detectPersons = parseDetectPersons(detectPersonsString, id, ok);
1008
if (!ok) {
1009
myCurrentIsBroken = true;
1010
return;
1011
}
1012
try {
1013
Parameterised* det = myDetectorBuilder.buildInstantInductLoop(id, lane, position, FileHelpers::checkForRelativity(file, getFileName()), friendlyPos, name, vTypes, nextEdges, detectPersons);
1014
myLastParameterised.push_back(det);
1015
} catch (InvalidArgument& e) {
1016
WRITE_ERROR(e.what());
1017
} catch (IOError& e) {
1018
WRITE_ERROR(e.what());
1019
}
1020
myCurrentIsBroken = true;
1021
}
1022
1023
1024
void
1025
NLHandler::addVTypeProbeDetector(const SUMOSAXAttributes& attrs) {
1026
WRITE_WARNING(TL("VTypeProbes are deprecated. Use fcd-output devices (assigned to the vType) instead."));
1027
bool ok = true;
1028
std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
1029
SUMOTime period = attrs.getOptPeriod(id.c_str(), ok, SUMOTime_MAX_PERIOD);
1030
std::string type = attrs.getStringSecure(SUMO_ATTR_TYPE, "");
1031
std::string file = attrs.get<std::string>(SUMO_ATTR_FILE, id.c_str(), ok);
1032
if (!ok) {
1033
return;
1034
}
1035
try {
1036
myDetectorBuilder.buildVTypeProbe(id, type, period, FileHelpers::checkForRelativity(file, getFileName()));
1037
} catch (InvalidArgument& e) {
1038
WRITE_ERROR(e.what());
1039
} catch (IOError& e) {
1040
WRITE_ERROR(e.what());
1041
}
1042
}
1043
1044
1045
void
1046
NLHandler::addRouteProbeDetector(const SUMOSAXAttributes& attrs) {
1047
bool ok = true;
1048
std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
1049
SUMOTime period = attrs.getOptPeriod(id.c_str(), ok, SUMOTime_MAX_PERIOD);
1050
SUMOTime begin = attrs.getOptSUMOTimeReporting(SUMO_ATTR_BEGIN, id.c_str(), ok, -1);
1051
std::string edge = attrs.get<std::string>(SUMO_ATTR_EDGE, id.c_str(), ok);
1052
std::string file = attrs.get<std::string>(SUMO_ATTR_FILE, id.c_str(), ok);
1053
const std::string vTypes = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id.c_str(), ok, "");
1054
if (!ok) {
1055
return;
1056
}
1057
try {
1058
myDetectorBuilder.buildRouteProbe(id, edge, period, begin,
1059
FileHelpers::checkForRelativity(file, getFileName()), vTypes);
1060
} catch (InvalidArgument& e) {
1061
WRITE_ERROR(e.what());
1062
} catch (IOError& e) {
1063
WRITE_ERROR(e.what());
1064
}
1065
}
1066
1067
1068
1069
void
1070
NLHandler::addE2Detector(const SUMOSAXAttributes& attrs) {
1071
myCurrentIsBroken = false;
1072
// check whether this is a detector connected to a tls and optionally to a link
1073
bool ok = true;
1074
const std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
1075
const std::string lsaid = attrs.getOpt<std::string>(SUMO_ATTR_TLID, id.c_str(), ok, "");
1076
const std::string toLane = attrs.getOpt<std::string>(SUMO_ATTR_TO, id.c_str(), ok, "");
1077
const SUMOTime haltingTimeThreshold = attrs.getOptSUMOTimeReporting(SUMO_ATTR_HALTING_TIME_THRESHOLD, id.c_str(), ok, TIME2STEPS(1));
1078
const double haltingSpeedThreshold = attrs.getOpt<double>(SUMO_ATTR_HALTING_SPEED_THRESHOLD, id.c_str(), ok, 5.0f / 3.6f);
1079
const double jamDistThreshold = attrs.getOpt<double>(SUMO_ATTR_JAM_DIST_THRESHOLD, id.c_str(), ok, 10.0f);
1080
double position = attrs.getOpt<double>(SUMO_ATTR_POSITION, id.c_str(), ok, std::numeric_limits<double>::max());
1081
const double length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, id.c_str(), ok, std::numeric_limits<double>::max());
1082
const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, id.c_str(), ok, false);
1083
const bool showDetector = attrs.getOpt<bool>(SUMO_ATTR_SHOW_DETECTOR, id.c_str(), ok, true);
1084
const std::string contStr = attrs.getOpt<std::string>(SUMO_ATTR_CONT, id.c_str(), ok, "");
1085
if (contStr != "") {
1086
WRITE_WARNINGF(TL("Ignoring deprecated argument 'cont' for E2 detector '%'"), id);
1087
}
1088
std::string lane = attrs.getOpt<std::string>(SUMO_ATTR_LANE, id.c_str(), ok, "");
1089
const std::string file = attrs.get<std::string>(SUMO_ATTR_FILE, id.c_str(), ok);
1090
const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
1091
const std::string vTypes = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id.c_str(), ok, "");
1092
const std::string nextEdges = attrs.getOpt<std::string>(SUMO_ATTR_NEXT_EDGES, id.c_str(), ok, "");
1093
1094
double endPosition = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, id.c_str(), ok, std::numeric_limits<double>::max());
1095
const std::string lanes = attrs.getOpt<std::string>(SUMO_ATTR_LANES, id.c_str(), ok, ""); // lanes has priority to lane
1096
const std::string detectPersonsString = attrs.getOpt<std::string>(SUMO_ATTR_DETECT_PERSONS, id.c_str(), ok, "");
1097
const int detectPersons = parseDetectPersons(detectPersonsString, id, ok);
1098
if (!ok) {
1099
myCurrentIsBroken = true;
1100
return;
1101
}
1102
1103
bool lanesGiven = lanes != "";
1104
bool laneGiven = lane != "";
1105
if (!(lanesGiven || laneGiven)) {
1106
// in absence of any lane-specification assume specification by id
1107
WRITE_WARNING(TL("Trying to specify detector's lane by the given id since the argument 'lane' is missing."))
1108
lane = id;
1109
laneGiven = true;
1110
}
1111
bool lengthGiven = length != std::numeric_limits<double>::max();
1112
bool posGiven = position != std::numeric_limits<double>::max();
1113
bool endPosGiven = endPosition != std::numeric_limits<double>::max();
1114
bool lsaGiven = lsaid != "";
1115
bool toLaneGiven = toLane != "";
1116
1117
MSLane* clane = nullptr;
1118
std::vector<MSLane*> clanes;
1119
if (lanesGiven) {
1120
// If lanes is given, endPos and startPos are required. lane, and length are ignored
1121
std::string seps = " ,\t\n";
1122
StringTokenizer st = StringTokenizer(lanes, seps, true);
1123
// std::cout << "Parsing lanes..." << std::endl;
1124
while (st.hasNext()) {
1125
std::string nextLaneID = st.next();
1126
// std::cout << "Next: " << nextLaneID << std::endl;
1127
if (nextLaneID.find_first_of(seps) != nextLaneID.npos) {
1128
continue;
1129
}
1130
clane = myDetectorBuilder.getLaneChecking(nextLaneID, SUMO_TAG_E2DETECTOR, id);
1131
clanes.push_back(clane);
1132
}
1133
if (clanes.size() == 0) {
1134
throw InvalidArgument("Malformed argument 'lanes' for E2Detector '" + id + "'.\nSpecify 'lanes' as a sequence of lane-IDs separated by whitespace or comma (',')");
1135
}
1136
if (laneGiven) {
1137
WRITE_WARNING("Ignoring argument 'lane' for E2Detector '" + id + "' since argument 'lanes' was given.\n"
1138
"Usage combinations for positional specification: [lane, pos, length], [lane, endPos, length], or [lanes, pos, endPos]");
1139
}
1140
if (lengthGiven) {
1141
WRITE_WARNING("Ignoring argument 'length' for E2Detector '" + id + "' since argument 'lanes' was given.\n"
1142
"Usage combinations for positional specification: [lane, pos, length], [lane, endPos, length], or [lanes, pos, endPos]");
1143
}
1144
if (!posGiven) {
1145
// assuming start pos == lane start
1146
position = 0;
1147
WRITE_WARNINGF(TL("Missing argument 'pos' for E2Detector '%'. Assuming detector start == lane start of lane '%'."), id, clanes[0]->getID());
1148
}
1149
if (!endPosGiven) {
1150
// assuming end pos == lane end
1151
endPosition = clanes[clanes.size() - 1]->getLength();
1152
WRITE_WARNINGF(TL("Missing argument 'endPos' for E2Detector '%'. Assuming detector end == lane end of lane '%'."), id, clanes[clanes.size() - 1]->getID());
1153
}
1154
1155
} else {
1156
if (!laneGiven) {
1157
std::stringstream ss;
1158
ss << "Missing argument 'lane' for E2Detector '" << id << "'."
1159
<< "\nUsage combinations for positional specification: [lane, pos, length], [lane, endPos, length], or [lanes, pos, endPos]";
1160
throw InvalidArgument(ss.str());
1161
}
1162
clane = myDetectorBuilder.getLaneChecking(lane, SUMO_TAG_E2DETECTOR, id);
1163
1164
if (posGiven) {
1165
// start pos is given
1166
if (endPosGiven && lengthGiven) {
1167
std::stringstream ss;
1168
ss << "Ignoring argument 'endPos' for E2Detector '" << id << "' since argument 'pos' was given."
1169
<< "\nUsage combinations for positional specification: [lane, pos, length], [lane, endPos, length], or [lanes, pos, endPos]";
1170
WRITE_WARNING(ss.str());
1171
endPosition = std::numeric_limits<double>::max();
1172
}
1173
if (!lengthGiven && !endPosGiven) {
1174
std::stringstream ss;
1175
ss << "Missing arguments 'length'/'endPos' for E2Detector '" << id << "'. Assuming detector end == lane end of lane '" << lane << "'.";
1176
WRITE_WARNING(ss.str());
1177
endPosition = clane->getLength();
1178
}
1179
} else if (endPosGiven) {
1180
// endPos is given, pos is not given
1181
if (!lengthGiven) {
1182
std::stringstream ss;
1183
ss << "Missing arguments 'length'/'pos' for E2Detector '" << id << "'. Assuming detector start == lane start of lane '" << lane << "'.";
1184
WRITE_WARNING(ss.str());
1185
}
1186
} else {
1187
std::stringstream ss;
1188
if (lengthGiven && fabs(length - clane->getLength()) > NUMERICAL_EPS) {
1189
ss << "Incomplete positional specification for E2Detector '" << id << "'."
1190
<< "\nUsage combinations for positional specification: [lane, pos, length], [lane, endPos, length], or [lanes, pos, endPos]";
1191
throw InvalidArgument(ss.str());
1192
}
1193
endPosition = clane->getLength();
1194
position = 0;
1195
ss << "Missing arguments 'pos'/'endPos' for E2Detector '" << id << "'. Assuming that the detector covers the whole lane '" << lane << "'.";
1196
WRITE_WARNING(ss.str());
1197
}
1198
}
1199
1200
// Period
1201
1202
SUMOTime period;
1203
if (!lsaGiven) {
1204
period = attrs.getOptPeriod(id.c_str(), ok, SUMOTime_MAX_PERIOD);
1205
if (!ok) {
1206
myCurrentIsBroken = true;
1207
return;
1208
}
1209
} else {
1210
period = attrs.getPeriod(id.c_str(), ok, false);
1211
}
1212
1213
// TLS
1214
MSTLLogicControl::TLSLogicVariants* tlls = nullptr;
1215
if (lsaGiven) {
1216
tlls = &myJunctionControlBuilder.getTLLogic(lsaid);
1217
if (tlls->getActive() == nullptr) {
1218
throw InvalidArgument("The detector '" + id + "' refers to an unknown lsa '" + lsaid + "'.");
1219
}
1220
if (period != -1) {
1221
WRITE_WARNINGF(TL("Ignoring argument 'period' for E2Detector '%' since argument 'tl' was given."), id);
1222
period = -1;
1223
}
1224
}
1225
1226
// Link
1227
MSLane* cToLane = nullptr;
1228
if (toLaneGiven) {
1229
cToLane = myDetectorBuilder.getLaneChecking(toLane, SUMO_TAG_E2DETECTOR, id);
1230
}
1231
1232
// File
1233
std::string filename;
1234
try {
1235
filename = FileHelpers::checkForRelativity(file, getFileName());
1236
} catch (IOError& e) {
1237
WRITE_ERROR(e.what());
1238
}
1239
1240
Parameterised* det;
1241
// Build detector
1242
if (lanesGiven) {
1243
// specification by a lane sequence
1244
det = myDetectorBuilder.buildE2Detector(id, clanes, position, endPosition, filename, period,
1245
haltingTimeThreshold, haltingSpeedThreshold, jamDistThreshold,
1246
name, vTypes, nextEdges, detectPersons, friendlyPos, showDetector,
1247
tlls, cToLane);
1248
} else {
1249
// specification by start or end lane
1250
det = myDetectorBuilder.buildE2Detector(id, clane, position, endPosition, length, filename, period,
1251
haltingTimeThreshold, haltingSpeedThreshold, jamDistThreshold,
1252
name, vTypes, nextEdges, detectPersons, friendlyPos, showDetector,
1253
tlls, cToLane);
1254
}
1255
myLastParameterised.push_back(det);
1256
}
1257
1258
1259
void
1260
NLHandler::beginE3Detector(const SUMOSAXAttributes& attrs) {
1261
myCurrentIsBroken = false;
1262
bool ok = true;
1263
std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
1264
const SUMOTime period = attrs.getOptPeriod(id.c_str(), ok, SUMOTime_MAX_PERIOD);
1265
const SUMOTime haltingTimeThreshold = attrs.getOptSUMOTimeReporting(SUMO_ATTR_HALTING_TIME_THRESHOLD, id.c_str(), ok, TIME2STEPS(1));
1266
const double haltingSpeedThreshold = attrs.getOpt<double>(SUMO_ATTR_HALTING_SPEED_THRESHOLD, id.c_str(), ok, 5.0f / 3.6f);
1267
const std::string file = attrs.get<std::string>(SUMO_ATTR_FILE, id.c_str(), ok);
1268
const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
1269
const std::string vTypes = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id.c_str(), ok, "");
1270
const std::string nextEdges = attrs.getOpt<std::string>(SUMO_ATTR_NEXT_EDGES, id.c_str(), ok, "");
1271
const bool openEntry = attrs.getOpt<bool>(SUMO_ATTR_OPEN_ENTRY, id.c_str(), ok, false);
1272
const bool expectArrival = attrs.getOpt<bool>(SUMO_ATTR_EXPECT_ARRIVAL, id.c_str(), ok, false);
1273
const std::string detectPersonsString = attrs.getOpt<std::string>(SUMO_ATTR_DETECT_PERSONS, id.c_str(), ok, "");
1274
const int detectPersons = parseDetectPersons(detectPersonsString, id, ok);
1275
if (!ok) {
1276
myCurrentIsBroken = true;
1277
return;
1278
}
1279
try {
1280
Parameterised* det = myDetectorBuilder.beginE3Detector(id,
1281
FileHelpers::checkForRelativity(file, getFileName()),
1282
period, haltingSpeedThreshold, haltingTimeThreshold, name, vTypes, nextEdges, detectPersons, openEntry, expectArrival);
1283
myLastParameterised.push_back(det);
1284
} catch (InvalidArgument& e) {
1285
myCurrentIsBroken = true;
1286
WRITE_ERROR(e.what());
1287
} catch (IOError& e) {
1288
myCurrentIsBroken = true;
1289
WRITE_ERROR(e.what());
1290
}
1291
}
1292
1293
1294
void
1295
NLHandler::addE3Entry(const SUMOSAXAttributes& attrs) {
1296
bool ok = true;
1297
const double position = attrs.get<double>(SUMO_ATTR_POSITION, myDetectorBuilder.getCurrentE3ID().c_str(), ok);
1298
const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, myDetectorBuilder.getCurrentE3ID().c_str(), ok, false);
1299
const std::string lane = attrs.get<std::string>(SUMO_ATTR_LANE, myDetectorBuilder.getCurrentE3ID().c_str(), ok);
1300
if (!ok) {
1301
return;
1302
}
1303
myDetectorBuilder.addE3Entry(lane, position, friendlyPos);
1304
}
1305
1306
1307
void
1308
NLHandler::addE3Exit(const SUMOSAXAttributes& attrs) {
1309
bool ok = true;
1310
const double position = attrs.get<double>(SUMO_ATTR_POSITION, myDetectorBuilder.getCurrentE3ID().c_str(), ok);
1311
const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, myDetectorBuilder.getCurrentE3ID().c_str(), ok, false);
1312
const std::string lane = attrs.get<std::string>(SUMO_ATTR_LANE, myDetectorBuilder.getCurrentE3ID().c_str(), ok);
1313
if (!ok) {
1314
return;
1315
}
1316
myDetectorBuilder.addE3Exit(lane, position, friendlyPos);
1317
}
1318
1319
1320
void
1321
NLHandler::addEdgeLaneMeanData(const SUMOSAXAttributes& attrs, int objecttype) {
1322
bool ok = true;
1323
std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
1324
const double maxTravelTime = attrs.getOpt<double>(SUMO_ATTR_MAX_TRAVELTIME, id.c_str(), ok, 100000);
1325
const double minSamples = attrs.getOpt<double>(SUMO_ATTR_MIN_SAMPLES, id.c_str(), ok, 0);
1326
const double haltingSpeedThreshold = attrs.getOpt<double>(SUMO_ATTR_HALTING_SPEED_THRESHOLD, id.c_str(), ok, POSITION_EPS);
1327
const std::string excludeEmpty = attrs.getOpt<std::string>(SUMO_ATTR_EXCLUDE_EMPTY, id.c_str(), ok, "false");
1328
const bool withInternal = attrs.getOpt<bool>(SUMO_ATTR_WITH_INTERNAL, id.c_str(), ok, false);
1329
const bool trackVehicles = attrs.getOpt<bool>(SUMO_ATTR_TRACK_VEHICLES, id.c_str(), ok, false);
1330
const std::string detectPersonsString = attrs.getOpt<std::string>(SUMO_ATTR_DETECT_PERSONS, id.c_str(), ok, "");
1331
const std::string file = attrs.get<std::string>(SUMO_ATTR_FILE, id.c_str(), ok);
1332
const std::string type = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, "performance");
1333
std::string vtypes = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id.c_str(), ok, "");
1334
const std::string writeAttributes = attrs.getOpt<std::string>(SUMO_ATTR_WRITE_ATTRIBUTES, id.c_str(), ok, "");
1335
const SUMOTime period = attrs.getOptPeriod(id.c_str(), ok, -1);
1336
const SUMOTime begin = attrs.getOptSUMOTimeReporting(SUMO_ATTR_BEGIN, id.c_str(), ok, string2time(OptionsCont::getOptions().getString("begin")));
1337
const SUMOTime end = attrs.getOptSUMOTimeReporting(SUMO_ATTR_END, id.c_str(), ok, string2time(OptionsCont::getOptions().getString("end")));
1338
std::vector<std::string> edgeIDs = attrs.getOpt<std::vector<std::string> >(SUMO_ATTR_EDGES, id.c_str(), ok);
1339
const std::string edgesFile = attrs.getOpt<std::string>(SUMO_ATTR_EDGESFILE, id.c_str(), ok, "");
1340
const std::string aggregateStr = attrs.getOpt<std::string>(SUMO_ATTR_AGGREGATE, id.c_str(), ok, "false");
1341
AggregateType aggregate = AggregateType::NO;
1342
if (aggregateStr == "taz") {
1343
aggregate = AggregateType::TAZ;
1344
} else if (StringUtils::toBool(aggregateStr)) {
1345
aggregate = AggregateType::YES;
1346
}
1347
if (!ok) {
1348
return;
1349
}
1350
int detectPersons = 0;
1351
for (std::string mode : StringTokenizer(detectPersonsString).getVector()) {
1352
if (SUMOXMLDefinitions::PersonModeValues.hasString(mode)) {
1353
detectPersons |= (int)SUMOXMLDefinitions::PersonModeValues.get(mode);
1354
} else {
1355
WRITE_ERRORF(TL("Invalid person mode '%' in edgeData definition '%'"), mode, id);
1356
return;
1357
}
1358
}
1359
if (edgesFile != "") {
1360
std::ifstream strm(edgesFile.c_str());
1361
if (!strm.good()) {
1362
throw ProcessError("Could not load names of edges for edgeData definition '" + id + "' from '" + edgesFile + "'.");
1363
}
1364
while (strm.good()) {
1365
std::string name;
1366
strm >> name;
1367
// maybe we're loading an edge-selection
1368
if (StringUtils::startsWith(name, "edge:")) {
1369
edgeIDs.push_back(name.substr(5));
1370
} else if (name != "") {
1371
edgeIDs.push_back(name);
1372
}
1373
}
1374
}
1375
std::vector<MSEdge*> edges;
1376
for (const std::string& edgeID : edgeIDs) {
1377
MSEdge* edge = MSEdge::dictionary(edgeID);
1378
if (edge == nullptr) {
1379
WRITE_ERRORF(TL("Unknown edge '%' in edgeData definition '%'"), edgeID, id);
1380
return;
1381
}
1382
edges.push_back(edge);
1383
}
1384
bool useLanes = objecttype == SUMO_TAG_MEANDATA_LANE;
1385
if (useLanes && MSGlobals::gUseMesoSim && !OptionsCont::getOptions().getBool("meso-lane-queue")) {
1386
WRITE_WARNINGF(TL("LaneData '%' requested for mesoscopic simulation but --meso-lane-queue is not active. Falling back to edgeData."), id);
1387
useLanes = false;
1388
}
1389
try {
1390
myDetectorBuilder.createEdgeLaneMeanData(id, period, begin, end,
1391
type, useLanes,
1392
// equivalent to TplConvert::_2bool used in SUMOSAXAttributes::getBool
1393
excludeEmpty[0] != 't' && excludeEmpty[0] != 'T' && excludeEmpty[0] != '1' && excludeEmpty[0] != 'x',
1394
excludeEmpty == "defaults", withInternal, trackVehicles, detectPersons,
1395
maxTravelTime, minSamples, haltingSpeedThreshold, vtypes, writeAttributes, edges, aggregate,
1396
FileHelpers::checkForRelativity(file, getFileName()));
1397
} catch (InvalidArgument& e) {
1398
WRITE_ERROR(e.what());
1399
} catch (IOError& e) {
1400
WRITE_ERROR(e.what());
1401
}
1402
}
1403
1404
1405
void
1406
NLHandler::addConnection(const SUMOSAXAttributes& attrs) {
1407
bool ok = true;
1408
const std::string fromID = attrs.get<std::string>(SUMO_ATTR_FROM, nullptr, ok);
1409
const std::string toID = attrs.get<std::string>(SUMO_ATTR_TO, nullptr, ok);
1410
if (!MSGlobals::gUsingInternalLanes && (fromID[0] == ':' || toID[0] == ':')) {
1411
std::string tlID = attrs.getOpt<std::string>(SUMO_ATTR_TLID, nullptr, ok, "");
1412
if (tlID != "") {
1413
int tlLinkIdx = attrs.get<int>(SUMO_ATTR_TLLINKINDEX, nullptr, ok);
1414
myJunctionControlBuilder.getTLLogic(tlID).ignoreLinkIndex(tlLinkIdx);
1415
}
1416
return;
1417
}
1418
1419
myCurrentLink = nullptr;
1420
try {
1421
const int fromLaneIdx = attrs.get<int>(SUMO_ATTR_FROM_LANE, nullptr, ok);
1422
const int toLaneIdx = attrs.get<int>(SUMO_ATTR_TO_LANE, nullptr, ok);
1423
LinkDirection dir = parseLinkDir(attrs.get<std::string>(SUMO_ATTR_DIR, nullptr, ok));
1424
LinkState state = parseLinkState(attrs.get<std::string>(SUMO_ATTR_STATE, nullptr, ok));
1425
const double foeVisibilityDistance = attrs.getOpt<double>(SUMO_ATTR_VISIBILITY_DISTANCE, nullptr, ok, state == LINKSTATE_ZIPPER ? 100 : 4.5);
1426
const bool keepClear = attrs.getOpt<bool>(SUMO_ATTR_KEEP_CLEAR, nullptr, ok, true);
1427
const bool indirect = attrs.getOpt<bool>(SUMO_ATTR_INDIRECT, nullptr, ok, false);
1428
std::string tlID = attrs.getOpt<std::string>(SUMO_ATTR_TLID, nullptr, ok, "");
1429
std::string viaID = attrs.getOpt<std::string>(SUMO_ATTR_VIA, nullptr, ok, "");
1430
1431
MSEdge* from = MSEdge::dictionaryHint(fromID, myPreviousEdgeIdx);
1432
if (from == nullptr) {
1433
WRITE_ERRORF(TL("Unknown from-edge '%' in connection."), fromID);
1434
return;
1435
}
1436
myPreviousEdgeIdx = from->getNumericalID();
1437
MSEdge* to = MSEdge::dictionary(toID);
1438
if (to == nullptr) {
1439
WRITE_ERRORF(TL("Unknown to-edge '%' in connection."), toID);
1440
return;
1441
}
1442
if (fromLaneIdx < 0 || fromLaneIdx >= (int)from->getLanes().size() ||
1443
toLaneIdx < 0 || toLaneIdx >= (int)to->getLanes().size()) {
1444
WRITE_ERRORF(TL("Invalid lane index in connection from '%' to '%'."), from->getID(), to->getID());
1445
return;
1446
}
1447
MSLane* fromLane = from->getLanes()[fromLaneIdx];
1448
MSLane* toLane = to->getLanes()[toLaneIdx];
1449
assert(fromLane);
1450
assert(toLane);
1451
1452
MSTrafficLightLogic* logic = nullptr;
1453
int tlLinkIdx = -1;
1454
if (tlID != "") {
1455
tlLinkIdx = attrs.get<int>(SUMO_ATTR_TLLINKINDEX, nullptr, ok);
1456
// make sure that the index is in range
1457
logic = myJunctionControlBuilder.getTLLogic(tlID).getActive();
1458
if ((tlLinkIdx < 0 || tlLinkIdx >= (int)logic->getCurrentPhaseDef().getState().size())
1459
&& logic->getLogicType() != TrafficLightType::RAIL_SIGNAL
1460
&& logic->getLogicType() != TrafficLightType::RAIL_CROSSING) {
1461
WRITE_ERROR("Invalid " + toString(SUMO_ATTR_TLLINKINDEX) + " '" + toString(tlLinkIdx) +
1462
"' in connection controlled by '" + tlID + "'");
1463
return;
1464
}
1465
if (!ok) {
1466
return;
1467
}
1468
}
1469
double length;
1470
// build the link
1471
MSLane* via = nullptr;
1472
if (viaID != "" && MSGlobals::gUsingInternalLanes) {
1473
via = MSLane::dictionary(viaID);
1474
if (via == nullptr) {
1475
WRITE_ERROR("An unknown lane ('" + viaID +
1476
"') should be set as a via-lane for lane '" + toLane->getID() + "'.");
1477
return;
1478
}
1479
length = via->getLength();
1480
} else if (toLane->isCrossing()) {
1481
length = toLane->getLength();
1482
} else {
1483
length = fromLane->getShape()[-1].distanceTo(toLane->getShape()[0]);
1484
}
1485
myCurrentLink = new MSLink(fromLane, toLane, via, dir, state, length, foeVisibilityDistance, keepClear, logic, tlLinkIdx, indirect);
1486
if (via != nullptr) {
1487
via->addIncomingLane(fromLane, myCurrentLink);
1488
} else {
1489
toLane->addIncomingLane(fromLane, myCurrentLink);
1490
}
1491
toLane->addApproachingLane(fromLane, myNetworkVersion < MMVersion(0, 25));
1492
1493
// if a traffic light is responsible for it, inform the traffic light
1494
// check whether this link is controlled by a traffic light
1495
// we can not reuse logic here because it might be an inactive one
1496
if (tlID != "") {
1497
myJunctionControlBuilder.getTLLogic(tlID).addLink(myCurrentLink, fromLane, tlLinkIdx);
1498
}
1499
// add the link
1500
fromLane->addLink(myCurrentLink);
1501
1502
} catch (InvalidArgument& e) {
1503
WRITE_ERROR(e.what());
1504
}
1505
}
1506
1507
1508
void
1509
NLHandler::addConflict(const SUMOSAXAttributes& attrs) {
1510
if (myCurrentLink == nullptr) {
1511
throw InvalidArgument(toString(SUMO_TAG_CONFLICT) + " must occur within a " + toString(SUMO_TAG_CONNECTION) + " element");
1512
}
1513
if (!MSGlobals::gUsingInternalLanes) {
1514
return;
1515
}
1516
bool ok = true;
1517
const std::string fromID = attrs.get<std::string>(SUMO_ATTR_FROM, nullptr, ok);
1518
const std::string toID = attrs.get<std::string>(SUMO_ATTR_TO, nullptr, ok);
1519
const int fromLaneIdx = attrs.get<int>(SUMO_ATTR_FROM_LANE, nullptr, ok);
1520
const int toLaneIdx = attrs.get<int>(SUMO_ATTR_TO_LANE, nullptr, ok);
1521
double startPos = attrs.get<double>(SUMO_ATTR_STARTPOS, nullptr, ok);
1522
double endPos = attrs.get<double>(SUMO_ATTR_ENDPOS, nullptr, ok);
1523
MSEdge* from = MSEdge::dictionary(fromID);
1524
if (from == nullptr) {
1525
WRITE_ERRORF(TL("Unknown from-edge '%' in conflict."), fromID);
1526
return;
1527
}
1528
MSEdge* to = MSEdge::dictionary(toID);
1529
if (to == nullptr) {
1530
WRITE_ERRORF(TL("Unknown to-edge '%' in conflict."), toID);
1531
return;
1532
}
1533
if (fromLaneIdx < 0 || fromLaneIdx >= (int)from->getLanes().size() ||
1534
toLaneIdx < 0 || toLaneIdx >= (int)to->getLanes().size()) {
1535
WRITE_ERRORF(TL("Invalid lane index in conflict with '%' to '%'."), from->getID(), to->getID());
1536
return;
1537
}
1538
MSLane* fromLane = from->getLanes()[fromLaneIdx];
1539
MSLane* toLane = to->getLanes()[toLaneIdx];
1540
assert(fromLane);
1541
assert(toLane);
1542
myCurrentLink->addCustomConflict(fromLane, toLane, startPos, endPos);
1543
}
1544
1545
1546
LinkDirection
1547
NLHandler::parseLinkDir(const std::string& dir) {
1548
if (SUMOXMLDefinitions::LinkDirections.hasString(dir)) {
1549
return SUMOXMLDefinitions::LinkDirections.get(dir);
1550
} else {
1551
throw InvalidArgument("Unrecognised link direction '" + dir + "'.");
1552
}
1553
}
1554
1555
1556
LinkState
1557
NLHandler::parseLinkState(const std::string& state) {
1558
if (SUMOXMLDefinitions::LinkStates.hasString(state)) {
1559
return SUMOXMLDefinitions::LinkStates.get(state);
1560
} else {
1561
if (state == "t") { // legacy networks
1562
// WRITE_WARNING(TL("Obsolete link state 't'. Use 'o' instead"));
1563
return LINKSTATE_TL_OFF_BLINKING;
1564
} else {
1565
throw InvalidArgument("Unrecognised link state '" + state + "'.");
1566
}
1567
}
1568
}
1569
1570
1571
// ----------------------------------
1572
void
1573
NLHandler::setLocation(const SUMOSAXAttributes& attrs) {
1574
if (myNetIsLoaded) {
1575
//WRITE_WARNING(TL("POIs and Polygons should be loaded using option --po-files"))
1576
return;
1577
}
1578
bool ok = true;
1579
PositionVector s = attrs.get<PositionVector>(SUMO_ATTR_NET_OFFSET, nullptr, ok);
1580
Boundary convBoundary = attrs.get<Boundary>(SUMO_ATTR_CONV_BOUNDARY, nullptr, ok);
1581
Boundary origBoundary = attrs.get<Boundary>(SUMO_ATTR_ORIG_BOUNDARY, nullptr, ok);
1582
std::string proj = attrs.get<std::string>(SUMO_ATTR_ORIG_PROJ, nullptr, ok);
1583
if (ok) {
1584
Position networkOffset = s[0];
1585
GeoConvHelper::init(proj, networkOffset, origBoundary, convBoundary);
1586
if (OptionsCont::getOptions().getBool("fcd-output.geo") && !GeoConvHelper::getFinal().usingGeoProjection()) {
1587
WRITE_WARNING(TL("no valid geo projection loaded from network. fcd-output.geo will not work"));
1588
}
1589
}
1590
}
1591
1592
1593
void
1594
NLHandler::addDistrict(const SUMOSAXAttributes& attrs) {
1595
bool ok = true;
1596
myCurrentIsBroken = false;
1597
// get the id, report an error if not given or empty...
1598
myCurrentDistrictID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
1599
if (!ok) {
1600
myCurrentIsBroken = true;
1601
return;
1602
}
1603
try {
1604
const std::string sinkID = myCurrentDistrictID + "-sink";
1605
const std::string sourceID = myCurrentDistrictID + "-source";
1606
1607
MSEdge* sink = MSEdge::dictionary(sinkID);
1608
if (sink == nullptr) {
1609
sink = myEdgeControlBuilder.buildEdge(sinkID, SumoXMLEdgeFunc::CONNECTOR, "", "", "", -1, 0);
1610
MSEdge::dictionary(sinkID, sink);
1611
sink->initialize(new std::vector<MSLane*>());
1612
} else {
1613
if (OptionsCont::getOptions().getBool("junction-taz")
1614
&& myNet.getJunctionControl().get(myCurrentDistrictID) != nullptr) {
1615
// overwrite junction taz
1616
sink->resetTAZ(myNet.getJunctionControl().get(myCurrentDistrictID));
1617
WRITE_WARNINGF(TL("Replacing junction-taz '%' with loaded TAZ."), myCurrentDistrictID);
1618
} else {
1619
throw InvalidArgument("Another edge with the id '" + sinkID + "' exists.");
1620
}
1621
}
1622
MSEdge* source = MSEdge::dictionary(sourceID);
1623
if (source == nullptr) {
1624
source = myEdgeControlBuilder.buildEdge(sourceID, SumoXMLEdgeFunc::CONNECTOR, "", "", "", -1, 0);
1625
MSEdge::dictionary(sourceID, source);
1626
source->initialize(new std::vector<MSLane*>());
1627
} else {
1628
if (OptionsCont::getOptions().getBool("junction-taz")
1629
&& myNet.getJunctionControl().get(myCurrentDistrictID) != nullptr) {
1630
// overwrite junction taz
1631
source->resetTAZ(myNet.getJunctionControl().get(myCurrentDistrictID));
1632
} else {
1633
throw InvalidArgument("Another edge with the id '" + sourceID + "' exists.");
1634
}
1635
}
1636
sink->setOtherTazConnector(source);
1637
source->setOtherTazConnector(sink);
1638
const std::vector<std::string>& desc = attrs.getOpt<std::vector<std::string> >(SUMO_ATTR_EDGES, myCurrentDistrictID.c_str(), ok);
1639
for (const std::string& eID : desc) {
1640
MSEdge* edge = MSEdge::dictionary(eID);
1641
// check whether the edge exists
1642
if (edge == nullptr) {
1643
throw InvalidArgument("The edge '" + eID + "' within district '" + myCurrentDistrictID + "' is not known.");
1644
}
1645
source->addSuccessor(edge);
1646
edge->addSuccessor(sink);
1647
}
1648
source->setParameter("taz", myCurrentDistrictID);
1649
sink->setParameter("taz", myCurrentDistrictID);
1650
RGBColor color = attrs.getOpt<RGBColor>(SUMO_ATTR_COLOR, myCurrentDistrictID.c_str(), ok, RGBColor::parseColor("1.0,.33,.33"));
1651
const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, myCurrentDistrictID.c_str(), ok, "");
1652
source->setParameter("tazColor", toString(color));
1653
sink->setParameter("tazColor", toString(color));
1654
1655
if (attrs.hasAttribute(SUMO_ATTR_SHAPE)) {
1656
PositionVector shape = attrs.get<PositionVector>(SUMO_ATTR_SHAPE, myCurrentDistrictID.c_str(), ok);
1657
const bool fill = attrs.getOpt<bool>(SUMO_ATTR_FILL, myCurrentDistrictID.c_str(), ok, false);
1658
if (shape.size() != 0) {
1659
if (!myNet.getShapeContainer().addPolygon(myCurrentDistrictID, "taz", color, 0, 0, "", shape, false, fill, 1.0, false, name)) {
1660
WRITE_WARNINGF(TL("Skipping visualization of taz '%', polygon already exists."), myCurrentDistrictID);
1661
} else {
1662
myLastParameterised.push_back(myNet.getShapeContainer().getPolygons().get(myCurrentDistrictID));
1663
myCurrentIsBroken = false;
1664
}
1665
}
1666
}
1667
} catch (InvalidArgument& e) {
1668
WRITE_ERROR(e.what());
1669
myCurrentIsBroken = true;
1670
}
1671
}
1672
1673
1674
void
1675
NLHandler::addDistrictEdge(const SUMOSAXAttributes& attrs, bool isSource) {
1676
if (myCurrentIsBroken) {
1677
// earlier error
1678
return;
1679
}
1680
bool ok = true;
1681
std::string id = attrs.get<std::string>(SUMO_ATTR_ID, myCurrentDistrictID.c_str(), ok);
1682
MSEdge* succ = MSEdge::dictionary(id);
1683
if (succ != nullptr) {
1684
// connect edge
1685
if (isSource) {
1686
MSEdge::dictionary(myCurrentDistrictID + "-source")->addSuccessor(succ);
1687
} else {
1688
succ->addSuccessor(MSEdge::dictionary(myCurrentDistrictID + "-sink"));
1689
}
1690
} else {
1691
WRITE_ERRORF(TL("At district '%': succeeding edge '%' does not exist."), myCurrentDistrictID, id);
1692
}
1693
}
1694
1695
1696
void
1697
NLHandler::addRoundabout(const SUMOSAXAttributes& attrs) {
1698
bool ok = true;
1699
const std::vector<std::string>& edgeIDs = attrs.get<std::vector<std::string> >(SUMO_ATTR_EDGES, nullptr, ok);
1700
if (ok) {
1701
for (const std::string& eID : edgeIDs) {
1702
MSEdge* edge = MSEdge::dictionary(eID);
1703
if (edge == nullptr) {
1704
WRITE_ERRORF(TL("Unknown edge '%' in roundabout"), eID);
1705
} else {
1706
edge->markAsRoundabout();
1707
}
1708
}
1709
}
1710
}
1711
1712
1713
void
1714
NLHandler::addDeadlock(const SUMOSAXAttributes& attrs) {
1715
bool ok = true;
1716
std::vector<std::string> signalIDs = attrs.get<std::vector<std::string>>(SUMO_ATTR_SIGNALS, nullptr, ok);
1717
std::vector<const MSRailSignal*> signals;
1718
for (const std::string& id : signalIDs) {
1719
const MSTrafficLightLogic* tll = myJunctionControlBuilder.getTLLogicControlToUse().getActive(id);
1720
const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(tll);
1721
if (rs != nullptr) {
1722
signals.push_back(rs);
1723
} else {
1724
throw InvalidArgument("Rail signal '" + toString(id) + "' in " + toString(SUMO_TAG_DEADLOCK) + " is not known");
1725
}
1726
}
1727
MSRailSignalControl::getInstance().addDeadlockCheck(signals);
1728
}
1729
1730
// ----------------------------------
1731
void
1732
NLHandler::endE3Detector() {
1733
try {
1734
myDetectorBuilder.endE3Detector();
1735
} catch (InvalidArgument& e) {
1736
WRITE_ERROR(e.what());
1737
}
1738
}
1739
1740
1741
void
1742
NLHandler::closeWAUT() {
1743
if (!myCurrentIsBroken) {
1744
try {
1745
myJunctionControlBuilder.getTLLogicControlToUse().closeWAUT(myCurrentWAUTID);
1746
} catch (InvalidArgument& e) {
1747
WRITE_ERROR(e.what());
1748
myCurrentIsBroken = true;
1749
}
1750
}
1751
myCurrentWAUTID = "";
1752
}
1753
1754
1755
Position
1756
NLShapeHandler::getLanePos(const std::string& poiID, const std::string& laneID, double lanePos, bool friendlyPos, double lanePosLat) {
1757
MSLane* lane = MSLane::dictionary(laneID);
1758
if (lane == nullptr) {
1759
WRITE_ERRORF(TL("Lane '%' to place poi '%' on is not known."), laneID, poiID);
1760
return Position::INVALID;
1761
}
1762
if (lanePos < 0) {
1763
lanePos = lane->getLength() + lanePos;
1764
}
1765
if ((lanePos < 0) && friendlyPos) {
1766
lanePos = 0;
1767
}
1768
if ((lanePos > lane->getLength()) && friendlyPos) {
1769
lanePos = lane->getLength();
1770
}
1771
if (lanePos < 0 || lanePos > lane->getLength()) {
1772
WRITE_WARNINGF(TL("lane position % for poi '%' is not valid."), toString(lanePos), poiID);
1773
}
1774
return lane->geometryPositionAtOffset(lanePos, -lanePosLat);
1775
}
1776
1777
1778
Parameterised*
1779
NLHandler::addPredecessorConstraint(int element, const SUMOSAXAttributes& attrs, MSRailSignal* rs) {
1780
if (rs == nullptr) {
1781
throw InvalidArgument("Rail signal '" + toString((SumoXMLTag)element) + "' constraint must occur within a railSignalConstraints element");
1782
}
1783
bool ok = true;
1784
const std::string tripId = attrs.get<std::string>(SUMO_ATTR_TRIP_ID, nullptr, ok);
1785
const std::string signalID = attrs.get<std::string>(SUMO_ATTR_TLID, nullptr, ok);
1786
const std::string foesString = attrs.get<std::string>(SUMO_ATTR_FOES, nullptr, ok);
1787
const std::vector<std::string> foes = StringTokenizer(foesString).getVector();
1788
const int limit = attrs.getOpt<int>(SUMO_ATTR_LIMIT, nullptr, ok, (int)foes.size());
1789
const bool active = attrs.getOpt<bool>(SUMO_ATTR_ACTIVE, nullptr, ok, true);
1790
1791
if (!MSNet::getInstance()->getTLSControl().knows(signalID)) {
1792
throw InvalidArgument("Rail signal '" + signalID + "' in railSignalConstraints is not known");
1793
}
1794
MSRailSignal* signal = dynamic_cast<MSRailSignal*>(MSNet::getInstance()->getTLSControl().get(signalID).getDefault());
1795
if (signal == nullptr) {
1796
throw InvalidArgument("Traffic light '" + signalID + "' is not a rail signal");
1797
}
1798
MSRailSignalConstraint::ConstraintType type;
1799
switch (element) {
1800
case SUMO_TAG_PREDECESSOR:
1801
type = MSRailSignalConstraint::ConstraintType::PREDECESSOR;
1802
break;
1803
case SUMO_TAG_INSERTION_PREDECESSOR:
1804
type = MSRailSignalConstraint::ConstraintType::INSERTION_PREDECESSOR;
1805
break;
1806
case SUMO_TAG_FOE_INSERTION:
1807
type = MSRailSignalConstraint::ConstraintType::FOE_INSERTION;
1808
break;
1809
case SUMO_TAG_INSERTION_ORDER:
1810
type = MSRailSignalConstraint::ConstraintType::INSERTION_ORDER;
1811
break;
1812
case SUMO_TAG_BIDI_PREDECESSOR:
1813
type = MSRailSignalConstraint::ConstraintType::BIDI_PREDECESSOR;
1814
break;
1815
default:
1816
throw InvalidArgument("Unsupported rail signal constraint '" + toString((SumoXMLTag)element) + "'");
1817
}
1818
Parameterised* result = nullptr;
1819
if (ok) {
1820
for (const std::string& foe : foes) {
1821
MSRailSignalConstraint* c = new MSRailSignalConstraint_Predecessor(type, signal, foe, limit, active);
1822
rs->addConstraint(tripId, c);
1823
// XXX if there are multiple foes, only one constraint will receive the parameters
1824
result = c;
1825
}
1826
}
1827
return result;
1828
}
1829
1830
1831
int
1832
NLHandler::parseDetectPersons(const std::string& detectPersonsString, const std::string& id, bool& ok) {
1833
int detectPersons = 0;
1834
for (std::string mode : StringTokenizer(detectPersonsString).getVector()) {
1835
if (SUMOXMLDefinitions::PersonModeValues.hasString(mode)) {
1836
detectPersons |= (int)SUMOXMLDefinitions::PersonModeValues.get(mode);
1837
} else {
1838
WRITE_ERRORF(TL("Invalid person mode '%' in E1 detector definition '%'"), mode, id);
1839
ok = false;
1840
return 0;
1841
}
1842
}
1843
return detectPersons;
1844
}
1845
1846
1847
/****************************************************************************/
1848
1849