Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netimport/NIXMLEdgesHandler.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 NIXMLEdgesHandler.cpp
15
/// @author Daniel Krajzewicz
16
/// @author Jakob Erdmann
17
/// @author Michael Behrisch
18
/// @author Walter Bamberger
19
/// @author Laura Bieker
20
/// @author Leonhard Luecken
21
/// @date Tue, 20 Nov 2001
22
///
23
// Importer for network edges stored in XML
24
/****************************************************************************/
25
#include <config.h>
26
27
#include <string>
28
#include <iostream>
29
#include <map>
30
#include <cmath>
31
#include <utils/xml/SUMOSAXHandler.h>
32
#include <netbuild/NBNodeCont.h>
33
#include <netbuild/NBTypeCont.h>
34
#include <netbuild/NBNetBuilder.h>
35
#include <utils/xml/SUMOXMLDefinitions.h>
36
#include <utils/common/MsgHandler.h>
37
#include <utils/common/StringUtils.h>
38
#include <utils/common/StringTokenizer.h>
39
#include <utils/geom/GeomConvHelper.h>
40
#include <utils/common/ToString.h>
41
#include <utils/options/OptionsCont.h>
42
#include <utils/geom/GeoConvHelper.h>
43
#include "NIXMLNodesHandler.h"
44
#include "NIXMLEdgesHandler.h"
45
#include "NIImporter_SUMO.h"
46
47
48
// ===========================================================================
49
// method definitions
50
// ===========================================================================
51
NIXMLEdgesHandler::NIXMLEdgesHandler(NBNodeCont& nc,
52
NBEdgeCont& ec,
53
NBTypeCont& tc,
54
NBDistrictCont& dc,
55
NBTrafficLightLogicCont& tlc,
56
OptionsCont& options) :
57
SUMOSAXHandler("xml-edges - file"),
58
myOptions(options),
59
myNodeCont(nc),
60
myEdgeCont(ec),
61
myTypeCont(tc),
62
myDistrictCont(dc),
63
myTLLogicCont(tlc),
64
myCurrentEdge(nullptr),
65
myCurrentLaneIndex(-1),
66
myHaveReportedAboutOverwriting(false),
67
myHaveReportedAboutTypeOverride(false),
68
myHaveWarnedAboutDeprecatedLaneId(false),
69
myKeepEdgeShape(!options.getBool("plain.extend-edge-shape")) {
70
}
71
72
73
NIXMLEdgesHandler::~NIXMLEdgesHandler() {
74
delete myLocation;
75
}
76
77
78
void
79
NIXMLEdgesHandler::myStartElement(int element,
80
const SUMOSAXAttributes& attrs) {
81
switch (element) {
82
case SUMO_TAG_VIEWSETTINGS_EDGES:
83
// infer location for legacy networks that don't have location information
84
myLocation = GeoConvHelper::getLoadedPlain(getFileName());
85
break;
86
case SUMO_TAG_LOCATION:
87
delete myLocation;
88
myLocation = NIImporter_SUMO::loadLocation(attrs, false);
89
break;
90
case SUMO_TAG_EDGE:
91
addEdge(attrs);
92
break;
93
case SUMO_TAG_LANE:
94
addLane(attrs);
95
break;
96
case SUMO_TAG_NEIGH:
97
myCurrentEdge->getLaneStruct((int)myCurrentEdge->getNumLanes() - 1).oppositeID = attrs.getString(SUMO_ATTR_LANE);
98
break;
99
case SUMO_TAG_SPLIT:
100
addSplit(attrs);
101
break;
102
case SUMO_TAG_DEL:
103
deleteEdge(attrs);
104
break;
105
case SUMO_TAG_ROUNDABOUT:
106
addRoundabout(attrs);
107
break;
108
case SUMO_TAG_PARAM:
109
if (myLastParameterised.size() != 0 && myCurrentEdge != nullptr) {
110
bool ok = true;
111
const std::string key = attrs.get<std::string>(SUMO_ATTR_KEY, nullptr, ok);
112
// circumventing empty string test
113
const std::string val = attrs.hasAttribute(SUMO_ATTR_VALUE) ? attrs.getString(SUMO_ATTR_VALUE) : "";
114
myLastParameterised.back()->setParameter(key, val);
115
}
116
break;
117
case SUMO_TAG_STOPOFFSET: {
118
bool ok = true;
119
const StopOffset stopOffset(attrs, ok);
120
if (!ok) {
121
std::stringstream ss;
122
ss << "(Error encountered at lane " << myCurrentLaneIndex << " of edge '" << myCurrentID << "' while parsing stopOffsets.)";
123
WRITE_ERROR(ss.str());
124
} else {
125
if (myCurrentEdge->getLaneStopOffset(myCurrentLaneIndex).isDefined()) {
126
std::stringstream ss;
127
ss << "Duplicate definition of stopOffset for ";
128
if (myCurrentLaneIndex != -1) {
129
ss << "lane " << myCurrentLaneIndex << " on ";
130
}
131
ss << "edge " << myCurrentEdge->getID() << ". Ignoring duplicate specification.";
132
WRITE_WARNING(ss.str());
133
} else if ((stopOffset.getOffset() > myCurrentEdge->getLength()) || (stopOffset.getOffset() < 0)) {
134
std::stringstream ss;
135
ss << "Ignoring invalid stopOffset for ";
136
if (myCurrentLaneIndex != -1) {
137
ss << "lane " << myCurrentLaneIndex << " on ";
138
}
139
ss << "edge " << myCurrentEdge->getID();
140
if (stopOffset.getOffset() > myCurrentEdge->getLength()) {
141
ss << " (offset larger than the edge length).";
142
} else {
143
ss << " (negative offset).";
144
}
145
WRITE_WARNING(ss.str());
146
} else {
147
myCurrentEdge->setEdgeStopOffset(myCurrentLaneIndex, stopOffset);
148
}
149
}
150
}
151
break;
152
default:
153
break;
154
}
155
}
156
157
158
void
159
NIXMLEdgesHandler::addEdge(const SUMOSAXAttributes& attrs) {
160
myIsUpdate = false;
161
bool ok = true;
162
// initialise the edge
163
myCurrentEdge = nullptr;
164
mySplits.clear();
165
// get the id, report an error if not given or empty...
166
myCurrentID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
167
if (!ok) {
168
return;
169
}
170
myCurrentEdge = myEdgeCont.retrieve(myCurrentID);
171
// check deprecated (unused) attributes
172
// use default values, first
173
myCurrentType = myOptions.getString("default.type");
174
myCurrentPriority = myTypeCont.getEdgeTypePriority(myCurrentType);
175
myCurrentLaneNo = myTypeCont.getEdgeTypeNumLanes(myCurrentType);
176
myCurrentEndOffset = NBEdge::UNSPECIFIED_OFFSET;
177
if (myCurrentEdge != nullptr) {
178
// update existing edge. only update lane-specific settings when explicitly requested
179
myIsUpdate = true;
180
myCurrentSpeed = NBEdge::UNSPECIFIED_SPEED;
181
myCurrentFriction = NBEdge::UNSPECIFIED_FRICTION;
182
myPermissions = SVC_UNSPECIFIED;
183
myCurrentWidth = NBEdge::UNSPECIFIED_WIDTH;
184
myCurrentType = myCurrentEdge->getTypeID();
185
myLanesSpread = SUMOXMLDefinitions::LaneSpreadFunctions.get(myOptions.getString("default.spreadtype"));
186
} else {
187
// this is a completely new edge. get the type specific defaults
188
myCurrentSpeed = myTypeCont.getEdgeTypeSpeed(myCurrentType);
189
myCurrentFriction = myTypeCont.getEdgeTypeFriction(myCurrentType);
190
myPermissions = myTypeCont.getEdgeTypePermissions(myCurrentType);
191
myCurrentWidth = myTypeCont.getEdgeTypeWidth(myCurrentType);
192
myLanesSpread = myTypeCont.getEdgeTypeSpreadType(myCurrentType);
193
}
194
myShape = PositionVector();
195
myLength = NBEdge::UNSPECIFIED_LOADED_LENGTH;
196
myCurrentStreetName = "";
197
myReinitKeepEdgeShape = false;
198
mySidewalkWidth = NBEdge::UNSPECIFIED_WIDTH;
199
myBikeLaneWidth = NBEdge::UNSPECIFIED_WIDTH;
200
// check whether a type's values shall be used
201
if (attrs.hasAttribute(SUMO_ATTR_TYPE)) {
202
myCurrentType = attrs.get<std::string>(SUMO_ATTR_TYPE, myCurrentID.c_str(), ok);
203
if (!ok) {
204
return;
205
}
206
if (!myTypeCont.knows(myCurrentType) && !myOptions.getBool("ignore-errors.edge-type")) {
207
WRITE_ERRORF("Type '%' used by edge '%' was not defined (ignore with option --ignore-errors.edge-type).", myCurrentType, myCurrentID);
208
return;
209
}
210
myCurrentSpeed = myTypeCont.getEdgeTypeSpeed(myCurrentType);
211
myCurrentPriority = myTypeCont.getEdgeTypePriority(myCurrentType);
212
myCurrentLaneNo = myTypeCont.getEdgeTypeNumLanes(myCurrentType);
213
myPermissions = myTypeCont.getEdgeTypePermissions(myCurrentType);
214
myCurrentWidth = myTypeCont.getEdgeTypeWidth(myCurrentType);
215
myLanesSpread = myTypeCont.getEdgeTypeSpreadType(myCurrentType);
216
mySidewalkWidth = myTypeCont.getEdgeTypeSidewalkWidth(myCurrentType);
217
myBikeLaneWidth = myTypeCont.getEdgeTypeBikeLaneWidth(myCurrentType);
218
}
219
// use values from the edge to overwrite if existing, then
220
if (myIsUpdate) {
221
if (!myHaveReportedAboutOverwriting) {
222
WRITE_MESSAGEF(TL("Duplicate edge id occurred ('%'); assuming overwriting is wished."), myCurrentID);
223
myHaveReportedAboutOverwriting = true;
224
}
225
if (attrs.hasAttribute(SUMO_ATTR_TYPE) && myCurrentType != myCurrentEdge->getTypeID()) {
226
if (!myHaveReportedAboutTypeOverride) {
227
WRITE_MESSAGEF(TL("Edge '%' changed its type; assuming type override is wished."), myCurrentID);
228
myHaveReportedAboutTypeOverride = true;
229
}
230
}
231
if (attrs.getOpt<bool>(SUMO_ATTR_REMOVE, myCurrentID.c_str(), ok, false)) {
232
myEdgeCont.erase(myDistrictCont, myCurrentEdge);
233
myCurrentEdge = nullptr;
234
return;
235
}
236
myCurrentPriority = myCurrentEdge->getPriority();
237
myCurrentLaneNo = myCurrentEdge->getNumLanes();
238
if (!myCurrentEdge->hasDefaultGeometry()) {
239
myShape = myCurrentEdge->getGeometry();
240
myReinitKeepEdgeShape = true;
241
}
242
myLanesSpread = myCurrentEdge->getLaneSpreadFunction();
243
if (myCurrentEdge->hasLoadedLength()) {
244
myLength = myCurrentEdge->getLoadedLength();
245
}
246
myCurrentStreetName = myCurrentEdge->getStreetName();
247
}
248
// speed, priority and the number of lanes have now default values;
249
// try to read the real values from the file
250
if (attrs.hasAttribute(SUMO_ATTR_SPEED)) {
251
myCurrentSpeed = attrs.get<double>(SUMO_ATTR_SPEED, myCurrentID.c_str(), ok);
252
}
253
if (myOptions.getBool("speed-in-kmh") && myCurrentSpeed != NBEdge::UNSPECIFIED_SPEED) {
254
myCurrentSpeed = myCurrentSpeed / (double) 3.6;
255
}
256
// try to read the friction value from file
257
if (attrs.hasAttribute(SUMO_ATTR_FRICTION)) {
258
myCurrentFriction = attrs.get<double>(SUMO_ATTR_FRICTION, myCurrentID.c_str(), ok);
259
}
260
// try to get the number of lanes
261
if (attrs.hasAttribute(SUMO_ATTR_NUMLANES)) {
262
myCurrentLaneNo = attrs.get<int>(SUMO_ATTR_NUMLANES, myCurrentID.c_str(), ok);
263
}
264
// try to get the priority
265
if (attrs.hasAttribute(SUMO_ATTR_PRIORITY)) {
266
myCurrentPriority = attrs.get<int>(SUMO_ATTR_PRIORITY, myCurrentID.c_str(), ok);
267
}
268
// try to get the width
269
if (attrs.hasAttribute(SUMO_ATTR_WIDTH)) {
270
myCurrentWidth = attrs.get<double>(SUMO_ATTR_WIDTH, myCurrentID.c_str(), ok);
271
}
272
// try to get the offset of the stop line from the intersection
273
if (attrs.hasAttribute(SUMO_ATTR_ENDOFFSET)) {
274
myCurrentEndOffset = attrs.get<double>(SUMO_ATTR_ENDOFFSET, myCurrentID.c_str(), ok);
275
}
276
// try to get the street name
277
if (attrs.hasAttribute(SUMO_ATTR_NAME)) {
278
myCurrentStreetName = attrs.get<std::string>(SUMO_ATTR_NAME, myCurrentID.c_str(), ok);
279
if (myCurrentStreetName != "" && myOptions.isDefault("output.street-names")) {
280
myOptions.set("output.street-names", "true");
281
}
282
}
283
284
// try to get the allowed/disallowed classes
285
if (attrs.hasAttribute(SUMO_ATTR_ALLOW) || attrs.hasAttribute(SUMO_ATTR_DISALLOW)) {
286
std::string allowS = attrs.hasAttribute(SUMO_ATTR_ALLOW) ? attrs.getStringSecure(SUMO_ATTR_ALLOW, "") : "";
287
std::string disallowS = attrs.hasAttribute(SUMO_ATTR_DISALLOW) ? attrs.getStringSecure(SUMO_ATTR_DISALLOW, "") : "";
288
// XXX matter of interpretation: should updated permissions replace or extend previously set permissions?
289
myPermissions = parseVehicleClasses(allowS, disallowS);
290
}
291
// try to set the nodes
292
if (!setNodes(attrs)) {
293
// return if this failed
294
myCurrentEdge = nullptr;
295
return;
296
}
297
// try to get the shape
298
myShape = tryGetShape(attrs);
299
// try to get the spread type
300
myLanesSpread = tryGetLaneSpread(attrs);
301
// try to get the length
302
myLength = attrs.getOpt<double>(SUMO_ATTR_LENGTH, myCurrentID.c_str(), ok, myLength);
303
// try to get the sidewalkWidth
304
mySidewalkWidth = attrs.getOpt<double>(SUMO_ATTR_SIDEWALKWIDTH, myCurrentID.c_str(), ok, mySidewalkWidth);
305
// try to get the bikeLaneWidth
306
myBikeLaneWidth = attrs.getOpt<double>(SUMO_ATTR_BIKELANEWIDTH, myCurrentID.c_str(), ok, myBikeLaneWidth);
307
// insert the parsed edge into the edges map
308
if (!ok) {
309
myCurrentEdge = nullptr;
310
return;
311
}
312
if (myFromNode == myToNode) {
313
// this might as well be an error. We make this a warning mostly for
314
// backward compatibility
315
WRITE_WARNINGF(TL("Ignoring self-looped edge '%' at junction '%'"), myCurrentID, myFromNode->getID());
316
myCurrentEdge = nullptr;
317
return;
318
}
319
// check whether a previously defined edge shall be overwritten
320
const bool applyLaneType = myCurrentEdge == nullptr;
321
if (myCurrentEdge != nullptr) {
322
myCurrentEdge->reinit(myFromNode, myToNode, myCurrentType, myCurrentSpeed, myCurrentFriction,
323
myCurrentLaneNo, myCurrentPriority, myShape,
324
myCurrentWidth, myCurrentEndOffset,
325
myCurrentStreetName, myLanesSpread,
326
myReinitKeepEdgeShape);
327
} else {
328
// the edge must be allocated in dependence to whether a shape is given
329
if (myShape.size() == 0) {
330
myCurrentEdge = new NBEdge(myCurrentID, myFromNode, myToNode, myCurrentType, myCurrentSpeed, myCurrentFriction,
331
myCurrentLaneNo, myCurrentPriority, myCurrentWidth, myCurrentEndOffset,
332
myLanesSpread, myCurrentStreetName);
333
} else {
334
myCurrentEdge = new NBEdge(myCurrentID, myFromNode, myToNode, myCurrentType, myCurrentSpeed, myCurrentFriction,
335
myCurrentLaneNo, myCurrentPriority, myCurrentWidth, myCurrentEndOffset,
336
myShape, myLanesSpread, myCurrentStreetName, "",
337
myKeepEdgeShape);
338
}
339
}
340
myCurrentEdge->setLoadedLength(myLength);
341
if (myPermissions != SVC_UNSPECIFIED) {
342
myCurrentEdge->setPermissions(myPermissions);
343
}
344
// apply laneType if given
345
if (applyLaneType && myCurrentType != "" && myTypeCont.knows(myCurrentType)) {
346
const NBTypeCont::EdgeTypeDefinition* eType = myTypeCont.getEdgeType(myCurrentType);
347
if (eType->needsLaneType()) {
348
int lane = 0;
349
for (const NBTypeCont::LaneTypeDefinition& laneType : eType->laneTypeDefinitions) {
350
if (lane >= myCurrentLaneNo) {
351
break;
352
}
353
if (laneType.attrs.count(SUMO_ATTR_SPEED) > 0) {
354
myCurrentEdge->setSpeed(lane, laneType.speed);
355
}
356
if (laneType.attrs.count(SUMO_ATTR_FRICTION) > 0) {
357
myCurrentEdge->setFriction(lane, laneType.friction);
358
}
359
if (laneType.attrs.count(SUMO_ATTR_DISALLOW) > 0 || laneType.attrs.count(SUMO_ATTR_ALLOW) > 0) {
360
myCurrentEdge->setPermissions(laneType.permissions, lane);
361
}
362
if (laneType.attrs.count(SUMO_ATTR_WIDTH) > 0) {
363
myCurrentEdge->setLaneWidth(lane, laneType.width);
364
}
365
lane++;
366
}
367
}
368
}
369
// try to get the kilometrage/mileage
370
myCurrentEdge->setDistance(attrs.getOpt<double>(SUMO_ATTR_DISTANCE, myCurrentID.c_str(), ok, myCurrentEdge->getDistance()));
371
myCurrentEdge->setRoutingType(attrs.getOpt<std::string>(SUMO_ATTR_ROUTINGTYPE, myCurrentID.c_str(), ok, myCurrentEdge->getRoutingType()));
372
// preserve bidi edge (only as bool, the actual edge will be recomputed)
373
const std::string bidi = attrs.getOpt<std::string>(SUMO_ATTR_BIDI, myCurrentID.c_str(), ok, "");
374
myCurrentEdge->setBidi(myCurrentEdge->getBidiEdge() != nullptr || myCurrentEdge->isBidi() || bidi != "");
375
376
myLastParameterised.push_back(myCurrentEdge);
377
}
378
379
380
void
381
NIXMLEdgesHandler::addLane(const SUMOSAXAttributes& attrs) {
382
if (myCurrentEdge == nullptr) {
383
if (!OptionsCont::getOptions().isInStringVector("remove-edges.explicit", myCurrentID)) {
384
WRITE_ERRORF("Additional lane information could not be set - the edge with id '%s' is not known.", myCurrentID);
385
}
386
return;
387
}
388
bool ok = true;
389
int lane;
390
if (attrs.hasAttribute(SUMO_ATTR_ID)) {
391
lane = attrs.get<int>(SUMO_ATTR_ID, myCurrentID.c_str(), ok);
392
if (!myHaveWarnedAboutDeprecatedLaneId) {
393
myHaveWarnedAboutDeprecatedLaneId = true;
394
WRITE_WARNINGF(TL("'%' is deprecated, please use '%' instead."), toString(SUMO_ATTR_ID), toString(SUMO_ATTR_INDEX));
395
}
396
} else {
397
lane = attrs.get<int>(SUMO_ATTR_INDEX, myCurrentID.c_str(), ok);
398
}
399
if (!ok) {
400
return;
401
}
402
// check whether this lane exists
403
if (lane >= myCurrentEdge->getNumLanes()) {
404
WRITE_ERRORF(TL("Lane index is larger than number of lanes (edge '%')."), myCurrentID);
405
return;
406
}
407
myCurrentLaneIndex = lane;
408
// set information about allowed / disallowed vehicle classes (if specified)
409
if (attrs.hasAttribute(SUMO_ATTR_ALLOW) || attrs.hasAttribute(SUMO_ATTR_DISALLOW)) {
410
const std::string allowed = attrs.getOpt<std::string>(SUMO_ATTR_ALLOW, nullptr, ok, "");
411
const std::string disallowed = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, nullptr, ok, "");
412
myCurrentEdge->setPermissions(parseVehicleClasses(allowed, disallowed), lane);
413
}
414
if (attrs.hasAttribute(SUMO_ATTR_PREFER)) {
415
const std::string preferred = attrs.get<std::string>(SUMO_ATTR_PREFER, nullptr, ok);
416
myCurrentEdge->setPreferredVehicleClass(parseVehicleClasses(preferred), lane);
417
}
418
if (attrs.hasAttribute(SUMO_ATTR_CHANGE_LEFT) || attrs.hasAttribute(SUMO_ATTR_CHANGE_RIGHT)) {
419
const std::string changeLeft = attrs.getOpt<std::string>(SUMO_ATTR_CHANGE_LEFT, nullptr, ok, "");
420
const std::string changeRight = attrs.getOpt<std::string>(SUMO_ATTR_CHANGE_RIGHT, nullptr, ok, "");
421
myCurrentEdge->setPermittedChanging(lane, parseVehicleClasses(changeLeft, ""), parseVehicleClasses(changeRight, ""));
422
}
423
// try to get the width
424
if (attrs.hasAttribute(SUMO_ATTR_WIDTH)) {
425
myCurrentEdge->setLaneWidth(lane, attrs.get<double>(SUMO_ATTR_WIDTH, myCurrentID.c_str(), ok));
426
}
427
// try to get the end-offset (lane shortened due to pedestrian crossing etc..)
428
if (attrs.hasAttribute(SUMO_ATTR_ENDOFFSET)) {
429
myCurrentEdge->setEndOffset(lane, attrs.get<double>(SUMO_ATTR_ENDOFFSET, myCurrentID.c_str(), ok));
430
}
431
// try to get lane specific speed (should not occur for german networks)
432
if (attrs.hasAttribute(SUMO_ATTR_SPEED)) {
433
myCurrentEdge->setSpeed(lane, attrs.get<double>(SUMO_ATTR_SPEED, myCurrentID.c_str(), ok));
434
}
435
// try to get lane specific friction
436
if (attrs.hasAttribute(SUMO_ATTR_FRICTION)) {
437
myCurrentEdge->setFriction(lane, attrs.get<double>(SUMO_ATTR_FRICTION, myCurrentID.c_str(), ok));
438
}
439
// check whether this is an acceleration lane
440
if (attrs.hasAttribute(SUMO_ATTR_ACCELERATION)) {
441
myCurrentEdge->setAcceleration(lane, attrs.get<bool>(SUMO_ATTR_ACCELERATION, myCurrentID.c_str(), ok));
442
}
443
// check whether this lane has a custom shape
444
if (attrs.hasAttribute(SUMO_ATTR_SHAPE)) {
445
PositionVector shape = attrs.get<PositionVector>(SUMO_ATTR_SHAPE, myCurrentID.c_str(), ok);
446
if (!NBNetBuilder::transformCoordinates(shape, true, myLocation)) {
447
const std::string laneID = myCurrentID + "_" + toString(lane);
448
WRITE_ERRORF(TL("Unable to project coordinates for lane '%'."), laneID);
449
}
450
if (shape.size() == 1) {
451
// lane shape of length 1 is not permitted
452
shape.push_front(myCurrentEdge->getFromNode()->getPosition());
453
shape.push_back(myCurrentEdge->getToNode()->getPosition());
454
}
455
shape.removeDoublePoints();
456
if (shape.size() < 2) {
457
// ignore lane shape for very short lanes
458
shape.clear();
459
}
460
myCurrentEdge->setLaneShape(lane, shape);
461
}
462
// set custom lane type
463
if (attrs.hasAttribute(SUMO_ATTR_TYPE)) {
464
myCurrentEdge->setLaneType(lane, attrs.get<std::string>(SUMO_ATTR_TYPE, myCurrentID.c_str(), ok));
465
}
466
myLastParameterised.push_back(&myCurrentEdge->getLaneStruct(lane));
467
}
468
469
470
void NIXMLEdgesHandler::addSplit(const SUMOSAXAttributes& attrs) {
471
if (myCurrentEdge == nullptr) {
472
if (!OptionsCont::getOptions().isInStringVector("remove-edges.explicit", myCurrentID)) {
473
WRITE_WARNING(TL("Ignoring 'split' because it cannot be assigned to an edge"));
474
}
475
return;
476
}
477
bool ok = true;
478
NBEdgeCont::Split e;
479
e.pos = attrs.get<double>(SUMO_ATTR_POSITION, nullptr, ok);
480
if (ok) {
481
if (fabs(e.pos) > myCurrentEdge->getLoadedLength()) {
482
WRITE_ERRORF(TL("Edge '%' has a split at invalid position %."), myCurrentID, toString(e.pos));
483
return;
484
}
485
std::vector<NBEdgeCont::Split>::iterator i = find_if(mySplits.begin(), mySplits.end(), split_by_pos_finder(e.pos));
486
if (i != mySplits.end()) {
487
WRITE_ERRORF(TL("Edge '%' has already a split at position %."), myCurrentID, toString(e.pos));
488
return;
489
}
490
// XXX rounding to int may duplicate the id of another split
491
e.nameID = myCurrentID + "." + toString((int)e.pos);
492
if (e.pos < 0) {
493
e.pos += myCurrentEdge->getLoadedLength();
494
}
495
for (const std::string& id : attrs.getOpt<std::vector<std::string> >(SUMO_ATTR_LANES, myCurrentID.c_str(), ok)) {
496
try {
497
int lane = StringUtils::toInt(id);
498
e.lanes.push_back(lane);
499
} catch (NumberFormatException&) {
500
WRITE_ERRORF(TL("Error on parsing a split (edge '%')."), myCurrentID);
501
} catch (EmptyData&) {
502
WRITE_ERRORF(TL("Error on parsing a split (edge '%')."), myCurrentID);
503
}
504
}
505
if (e.lanes.empty()) {
506
for (int l = 0; l < myCurrentEdge->getNumLanes(); ++l) {
507
e.lanes.push_back(l);
508
}
509
}
510
e.speed = attrs.getOpt(SUMO_ATTR_SPEED, nullptr, ok, myCurrentEdge->getSpeed());
511
if (attrs.hasAttribute(SUMO_ATTR_SPEED) && myOptions.getBool("speed-in-kmh")) {
512
e.speed /= 3.6;
513
}
514
e.idBefore = attrs.getOpt(SUMO_ATTR_ID_BEFORE, nullptr, ok, std::string(""));
515
e.idAfter = attrs.getOpt(SUMO_ATTR_ID_AFTER, nullptr, ok, std::string(""));
516
if (!ok) {
517
return;
518
}
519
const std::string nodeID = attrs.getOpt(SUMO_ATTR_ID, nullptr, ok, e.nameID);
520
if (nodeID == myCurrentEdge->getFromNode()->getID() || nodeID == myCurrentEdge->getToNode()->getID()) {
521
WRITE_ERRORF(TL("Invalid split node id for edge '%' (from- and to-node are forbidden)"), myCurrentEdge->getID());
522
return;
523
}
524
e.node = e.pos != 0 ? myNodeCont.retrieve(nodeID) : myCurrentEdge->getFromNode();
525
e.offset = attrs.getOpt(SUMO_ATTR_OFFSET, nullptr, ok, 0.0);
526
e.offsetFactor = OptionsCont::getOptions().getBool("lefthand") ? -1 : 1;
527
if (e.node == nullptr) {
528
double geomPos = e.pos;
529
if (myCurrentEdge->hasLoadedLength()) {
530
geomPos *= myCurrentEdge->getGeometry().length() / myCurrentEdge->getLoadedLength();
531
}
532
e.node = new NBNode(nodeID, myCurrentEdge->getGeometry().positionAtOffset(geomPos));
533
myNodeCont.insert(e.node);
534
}
535
NIXMLNodesHandler::processNodeType(attrs, e.node, e.node->getID(), e.node->getPosition(), false,
536
myNodeCont, myEdgeCont, myTLLogicCont, myLocation);
537
mySplits.push_back(e);
538
}
539
}
540
541
542
bool
543
NIXMLEdgesHandler::setNodes(const SUMOSAXAttributes& attrs) {
544
// the names and the coordinates of the beginning and the end node
545
// may be found, try
546
bool ok = true;
547
if (myIsUpdate) {
548
myFromNode = myCurrentEdge->getFromNode();
549
myToNode = myCurrentEdge->getToNode();
550
}
551
if (attrs.hasAttribute(SUMO_ATTR_FROM)) {
552
const std::string begNodeID = attrs.get<std::string>(SUMO_ATTR_FROM, nullptr, ok);
553
if (begNodeID != "") {
554
myFromNode = myNodeCont.retrieve(begNodeID);
555
if (myFromNode == nullptr) {
556
WRITE_ERRORF(TL("Edge's '%' from-node '%' is not known."), myCurrentID, begNodeID);
557
}
558
}
559
} else if (!myIsUpdate) {
560
WRITE_ERRORF(TL("The from-node is not given for edge '%'."), myCurrentID);
561
ok = false;
562
}
563
if (attrs.hasAttribute(SUMO_ATTR_TO)) {
564
const std::string endNodeID = attrs.get<std::string>(SUMO_ATTR_TO, nullptr, ok);
565
if (endNodeID != "") {
566
myToNode = myNodeCont.retrieve(endNodeID);
567
if (myToNode == nullptr) {
568
WRITE_ERRORF(TL("Edge's '%' to-node '%' is not known."), myCurrentID, endNodeID);
569
}
570
}
571
} else if (!myIsUpdate) {
572
WRITE_ERRORF(TL("The to-node is not given for edge '%'."), myCurrentID);
573
ok = false;
574
}
575
return ok && myFromNode != nullptr && myToNode != nullptr;
576
}
577
578
579
PositionVector
580
NIXMLEdgesHandler::tryGetShape(const SUMOSAXAttributes& attrs) {
581
if (!attrs.hasAttribute(SUMO_ATTR_SHAPE) && myShape.size() > 0) {
582
return myShape;
583
}
584
// try to build shape
585
bool ok = true;
586
if (!attrs.hasAttribute(SUMO_ATTR_SHAPE)) {
587
const double maxSegmentLength = OptionsCont::getOptions().getFloat("geometry.max-segment-length");
588
if (maxSegmentLength > 0) {
589
PositionVector shape;
590
shape.push_back(myFromNode->getPosition());
591
shape.push_back(myToNode->getPosition());
592
// shape is already cartesian but we must use a copy because the original will be modified
593
NBNetBuilder::addGeometrySegments(shape, PositionVector(shape), maxSegmentLength);
594
return shape;
595
} else {
596
myReinitKeepEdgeShape = false;
597
return PositionVector();
598
}
599
}
600
PositionVector shape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, nullptr, ok, PositionVector());
601
if (!NBNetBuilder::transformCoordinates(shape, true, myLocation)) {
602
WRITE_ERRORF(TL("Unable to project coordinates for edge '%'."), myCurrentID);
603
}
604
myReinitKeepEdgeShape = myKeepEdgeShape;
605
return shape;
606
}
607
608
609
LaneSpreadFunction
610
NIXMLEdgesHandler::tryGetLaneSpread(const SUMOSAXAttributes& attrs) {
611
bool ok = true;
612
LaneSpreadFunction result = myLanesSpread;
613
std::string lsfS = toString(result);
614
lsfS = attrs.getOpt<std::string>(SUMO_ATTR_SPREADTYPE, myCurrentID.c_str(), ok, lsfS);
615
if (SUMOXMLDefinitions::LaneSpreadFunctions.hasString(lsfS)) {
616
result = SUMOXMLDefinitions::LaneSpreadFunctions.get(lsfS);
617
} else {
618
WRITE_WARNINGF(TL("Ignoring unknown spreadType '%' for edge '%'."), lsfS, myCurrentID);
619
}
620
return result;
621
}
622
623
624
void
625
NIXMLEdgesHandler::deleteEdge(const SUMOSAXAttributes& attrs) {
626
bool ok = true;
627
myCurrentID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
628
if (!ok) {
629
return;
630
}
631
NBEdge* edge = myEdgeCont.retrieve(myCurrentID);
632
if (edge == nullptr) {
633
WRITE_WARNING("Ignoring tag '" + toString(SUMO_TAG_DEL) + "' for unknown edge '" +
634
myCurrentID + "'");
635
return;
636
}
637
const int lane = attrs.getOpt<int>(SUMO_ATTR_INDEX, myCurrentID.c_str(), ok, -1);
638
if (lane < 0) {
639
myEdgeCont.extract(myDistrictCont, edge, true);
640
} else {
641
edge->deleteLane(lane, false, true);
642
}
643
}
644
645
646
void
647
NIXMLEdgesHandler::myEndElement(int element) {
648
if (element == SUMO_TAG_VIEWSETTINGS_EDGES) {
649
delete myLocation;
650
myLocation = nullptr;
651
return;
652
}
653
if (myCurrentEdge == nullptr) {
654
return;
655
}
656
if (element == SUMO_TAG_EDGE) {
657
myLastParameterised.pop_back();
658
// add bike lane, wait until lanes are loaded to avoid building if it already exists
659
if (myBikeLaneWidth != NBEdge::UNSPECIFIED_WIDTH) {
660
myCurrentEdge->addBikeLane(myBikeLaneWidth);
661
}
662
// add sidewalk, wait until lanes are loaded to avoid building if it already exists
663
if (mySidewalkWidth != NBEdge::UNSPECIFIED_WIDTH) {
664
myCurrentEdge->addSidewalk(mySidewalkWidth);
665
}
666
// apply default stopOffsets of edge to all lanes without specified stopOffset.
667
const StopOffset stopOffsets = myCurrentEdge->getEdgeStopOffset();
668
if (stopOffsets.isDefined()) {
669
for (int i = 0; i < (int)myCurrentEdge->getLanes().size(); i++) {
670
myCurrentEdge->setEdgeStopOffset(i, stopOffsets, false);
671
}
672
}
673
if (!myIsUpdate) {
674
try {
675
if (!myEdgeCont.insert(myCurrentEdge)) {
676
WRITE_ERRORF(TL("Duplicate edge '%' occurred."), myCurrentID);
677
delete myCurrentEdge;
678
myCurrentEdge = nullptr;
679
return;
680
}
681
} catch (InvalidArgument& e) {
682
WRITE_ERROR(e.what());
683
throw;
684
} catch (...) {
685
WRITE_ERRORF(TL("An important information is missing in edge '%'."), myCurrentID);
686
}
687
}
688
myEdgeCont.processSplits(myCurrentEdge, mySplits, myNodeCont, myDistrictCont, myTLLogicCont);
689
myCurrentEdge = nullptr;
690
} else if (element == SUMO_TAG_LANE && myCurrentLaneIndex != -1) {
691
myLastParameterised.pop_back();
692
myCurrentLaneIndex = -1;
693
}
694
}
695
696
697
void
698
NIXMLEdgesHandler::addRoundabout(const SUMOSAXAttributes& attrs) {
699
bool ok = true;
700
const std::vector<std::string>& edgeIDs = attrs.get<std::vector<std::string> >(SUMO_ATTR_EDGES, nullptr, ok);
701
if (ok) {
702
EdgeSet roundabout;
703
for (const std::string& eID : edgeIDs) {
704
NBEdge* edge = myEdgeCont.retrieve(eID);
705
if (edge == nullptr) {
706
if (!myEdgeCont.wasIgnored(eID)) {
707
WRITE_ERRORF(TL("Unknown edge '%' in roundabout."), eID);
708
}
709
} else {
710
roundabout.insert(edge);
711
}
712
}
713
myEdgeCont.addRoundabout(roundabout);
714
}
715
}
716
717
718
/****************************************************************************/
719
720