Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netimport/NIXMLEdgesHandler.cpp
169665 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2001-2025 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
// preserve bidi edge (only as bool, the actual edge will be recomputed)
372
const std::string bidi = attrs.getOpt<std::string>(SUMO_ATTR_BIDI, myCurrentID.c_str(), ok, "");
373
myCurrentEdge->setBidi(myCurrentEdge->getBidiEdge() != nullptr || myCurrentEdge->isBidi() || bidi != "");
374
375
myLastParameterised.push_back(myCurrentEdge);
376
}
377
378
379
void
380
NIXMLEdgesHandler::addLane(const SUMOSAXAttributes& attrs) {
381
if (myCurrentEdge == nullptr) {
382
if (!OptionsCont::getOptions().isInStringVector("remove-edges.explicit", myCurrentID)) {
383
WRITE_ERRORF("Additional lane information could not be set - the edge with id '%s' is not known.", myCurrentID);
384
}
385
return;
386
}
387
bool ok = true;
388
int lane;
389
if (attrs.hasAttribute(SUMO_ATTR_ID)) {
390
lane = attrs.get<int>(SUMO_ATTR_ID, myCurrentID.c_str(), ok);
391
if (!myHaveWarnedAboutDeprecatedLaneId) {
392
myHaveWarnedAboutDeprecatedLaneId = true;
393
WRITE_WARNINGF(TL("'%' is deprecated, please use '%' instead."), toString(SUMO_ATTR_ID), toString(SUMO_ATTR_INDEX));
394
}
395
} else {
396
lane = attrs.get<int>(SUMO_ATTR_INDEX, myCurrentID.c_str(), ok);
397
}
398
if (!ok) {
399
return;
400
}
401
// check whether this lane exists
402
if (lane >= myCurrentEdge->getNumLanes()) {
403
WRITE_ERRORF(TL("Lane index is larger than number of lanes (edge '%')."), myCurrentID);
404
return;
405
}
406
myCurrentLaneIndex = lane;
407
// set information about allowed / disallowed vehicle classes (if specified)
408
if (attrs.hasAttribute(SUMO_ATTR_ALLOW) || attrs.hasAttribute(SUMO_ATTR_DISALLOW)) {
409
const std::string allowed = attrs.getOpt<std::string>(SUMO_ATTR_ALLOW, nullptr, ok, "");
410
const std::string disallowed = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, nullptr, ok, "");
411
myCurrentEdge->setPermissions(parseVehicleClasses(allowed, disallowed), lane);
412
}
413
if (attrs.hasAttribute(SUMO_ATTR_PREFER)) {
414
const std::string preferred = attrs.get<std::string>(SUMO_ATTR_PREFER, nullptr, ok);
415
myCurrentEdge->setPreferredVehicleClass(parseVehicleClasses(preferred), lane);
416
}
417
if (attrs.hasAttribute(SUMO_ATTR_CHANGE_LEFT) || attrs.hasAttribute(SUMO_ATTR_CHANGE_RIGHT)) {
418
const std::string changeLeft = attrs.getOpt<std::string>(SUMO_ATTR_CHANGE_LEFT, nullptr, ok, "");
419
const std::string changeRight = attrs.getOpt<std::string>(SUMO_ATTR_CHANGE_RIGHT, nullptr, ok, "");
420
myCurrentEdge->setPermittedChanging(lane, parseVehicleClasses(changeLeft, ""), parseVehicleClasses(changeRight, ""));
421
}
422
// try to get the width
423
if (attrs.hasAttribute(SUMO_ATTR_WIDTH)) {
424
myCurrentEdge->setLaneWidth(lane, attrs.get<double>(SUMO_ATTR_WIDTH, myCurrentID.c_str(), ok));
425
}
426
// try to get the end-offset (lane shortened due to pedestrian crossing etc..)
427
if (attrs.hasAttribute(SUMO_ATTR_ENDOFFSET)) {
428
myCurrentEdge->setEndOffset(lane, attrs.get<double>(SUMO_ATTR_ENDOFFSET, myCurrentID.c_str(), ok));
429
}
430
// try to get lane specific speed (should not occur for german networks)
431
if (attrs.hasAttribute(SUMO_ATTR_SPEED)) {
432
myCurrentEdge->setSpeed(lane, attrs.get<double>(SUMO_ATTR_SPEED, myCurrentID.c_str(), ok));
433
}
434
// try to get lane specific friction
435
if (attrs.hasAttribute(SUMO_ATTR_FRICTION)) {
436
myCurrentEdge->setFriction(lane, attrs.get<double>(SUMO_ATTR_FRICTION, myCurrentID.c_str(), ok));
437
}
438
// check whether this is an acceleration lane
439
if (attrs.hasAttribute(SUMO_ATTR_ACCELERATION)) {
440
myCurrentEdge->setAcceleration(lane, attrs.get<bool>(SUMO_ATTR_ACCELERATION, myCurrentID.c_str(), ok));
441
}
442
// check whether this lane has a custom shape
443
if (attrs.hasAttribute(SUMO_ATTR_SHAPE)) {
444
PositionVector shape = attrs.get<PositionVector>(SUMO_ATTR_SHAPE, myCurrentID.c_str(), ok);
445
if (!NBNetBuilder::transformCoordinates(shape, true, myLocation)) {
446
const std::string laneID = myCurrentID + "_" + toString(lane);
447
WRITE_ERRORF(TL("Unable to project coordinates for lane '%'."), laneID);
448
}
449
if (shape.size() == 1) {
450
// lane shape of length 1 is not permitted
451
shape.push_front(myCurrentEdge->getFromNode()->getPosition());
452
shape.push_back(myCurrentEdge->getToNode()->getPosition());
453
}
454
shape.removeDoublePoints();
455
if (shape.size() < 2) {
456
// ignore lane shape for very short lanes
457
shape.clear();
458
}
459
myCurrentEdge->setLaneShape(lane, shape);
460
}
461
// set custom lane type
462
if (attrs.hasAttribute(SUMO_ATTR_TYPE)) {
463
myCurrentEdge->setLaneType(lane, attrs.get<std::string>(SUMO_ATTR_TYPE, myCurrentID.c_str(), ok));
464
}
465
myLastParameterised.push_back(&myCurrentEdge->getLaneStruct(lane));
466
}
467
468
469
void NIXMLEdgesHandler::addSplit(const SUMOSAXAttributes& attrs) {
470
if (myCurrentEdge == nullptr) {
471
if (!OptionsCont::getOptions().isInStringVector("remove-edges.explicit", myCurrentID)) {
472
WRITE_WARNING(TL("Ignoring 'split' because it cannot be assigned to an edge"));
473
}
474
return;
475
}
476
bool ok = true;
477
NBEdgeCont::Split e;
478
e.pos = attrs.get<double>(SUMO_ATTR_POSITION, nullptr, ok);
479
if (ok) {
480
if (fabs(e.pos) > myCurrentEdge->getLoadedLength()) {
481
WRITE_ERRORF(TL("Edge '%' has a split at invalid position %."), myCurrentID, toString(e.pos));
482
return;
483
}
484
std::vector<NBEdgeCont::Split>::iterator i = find_if(mySplits.begin(), mySplits.end(), split_by_pos_finder(e.pos));
485
if (i != mySplits.end()) {
486
WRITE_ERRORF(TL("Edge '%' has already a split at position %."), myCurrentID, toString(e.pos));
487
return;
488
}
489
// XXX rounding to int may duplicate the id of another split
490
e.nameID = myCurrentID + "." + toString((int)e.pos);
491
if (e.pos < 0) {
492
e.pos += myCurrentEdge->getGeometry().length();
493
}
494
for (const std::string& id : attrs.getOpt<std::vector<std::string> >(SUMO_ATTR_LANES, myCurrentID.c_str(), ok)) {
495
try {
496
int lane = StringUtils::toInt(id);
497
e.lanes.push_back(lane);
498
} catch (NumberFormatException&) {
499
WRITE_ERRORF(TL("Error on parsing a split (edge '%')."), myCurrentID);
500
} catch (EmptyData&) {
501
WRITE_ERRORF(TL("Error on parsing a split (edge '%')."), myCurrentID);
502
}
503
}
504
if (e.lanes.empty()) {
505
for (int l = 0; l < myCurrentEdge->getNumLanes(); ++l) {
506
e.lanes.push_back(l);
507
}
508
}
509
e.speed = attrs.getOpt(SUMO_ATTR_SPEED, nullptr, ok, myCurrentEdge->getSpeed());
510
if (attrs.hasAttribute(SUMO_ATTR_SPEED) && myOptions.getBool("speed-in-kmh")) {
511
e.speed /= 3.6;
512
}
513
e.idBefore = attrs.getOpt(SUMO_ATTR_ID_BEFORE, nullptr, ok, std::string(""));
514
e.idAfter = attrs.getOpt(SUMO_ATTR_ID_AFTER, nullptr, ok, std::string(""));
515
if (!ok) {
516
return;
517
}
518
const std::string nodeID = attrs.getOpt(SUMO_ATTR_ID, nullptr, ok, e.nameID);
519
if (nodeID == myCurrentEdge->getFromNode()->getID() || nodeID == myCurrentEdge->getToNode()->getID()) {
520
WRITE_ERRORF(TL("Invalid split node id for edge '%' (from- and to-node are forbidden)"), myCurrentEdge->getID());
521
return;
522
}
523
e.node = myNodeCont.retrieve(nodeID);
524
e.offsetFactor = OptionsCont::getOptions().getBool("lefthand") ? -1 : 1;
525
if (e.node == nullptr) {
526
double geomPos = e.pos;
527
if (myCurrentEdge->hasLoadedLength()) {
528
geomPos *= myCurrentEdge->getGeometry().length() / myCurrentEdge->getLoadedLength();
529
}
530
e.node = new NBNode(nodeID, myCurrentEdge->getGeometry().positionAtOffset(geomPos));
531
myNodeCont.insert(e.node);
532
}
533
NIXMLNodesHandler::processNodeType(attrs, e.node, e.node->getID(), e.node->getPosition(), false,
534
myNodeCont, myEdgeCont, myTLLogicCont, myLocation);
535
mySplits.push_back(e);
536
}
537
}
538
539
540
bool
541
NIXMLEdgesHandler::setNodes(const SUMOSAXAttributes& attrs) {
542
// the names and the coordinates of the beginning and the end node
543
// may be found, try
544
bool ok = true;
545
if (myIsUpdate) {
546
myFromNode = myCurrentEdge->getFromNode();
547
myToNode = myCurrentEdge->getToNode();
548
}
549
if (attrs.hasAttribute(SUMO_ATTR_FROM)) {
550
const std::string begNodeID = attrs.get<std::string>(SUMO_ATTR_FROM, nullptr, ok);
551
if (begNodeID != "") {
552
myFromNode = myNodeCont.retrieve(begNodeID);
553
if (myFromNode == nullptr) {
554
WRITE_ERRORF(TL("Edge's '%' from-node '%' is not known."), myCurrentID, begNodeID);
555
}
556
}
557
} else if (!myIsUpdate) {
558
WRITE_ERRORF(TL("The from-node is not given for edge '%'."), myCurrentID);
559
ok = false;
560
}
561
if (attrs.hasAttribute(SUMO_ATTR_TO)) {
562
const std::string endNodeID = attrs.get<std::string>(SUMO_ATTR_TO, nullptr, ok);
563
if (endNodeID != "") {
564
myToNode = myNodeCont.retrieve(endNodeID);
565
if (myToNode == nullptr) {
566
WRITE_ERRORF(TL("Edge's '%' to-node '%' is not known."), myCurrentID, endNodeID);
567
}
568
}
569
} else if (!myIsUpdate) {
570
WRITE_ERRORF(TL("The to-node is not given for edge '%'."), myCurrentID);
571
ok = false;
572
}
573
return ok && myFromNode != nullptr && myToNode != nullptr;
574
}
575
576
577
PositionVector
578
NIXMLEdgesHandler::tryGetShape(const SUMOSAXAttributes& attrs) {
579
if (!attrs.hasAttribute(SUMO_ATTR_SHAPE) && myShape.size() > 0) {
580
return myShape;
581
}
582
// try to build shape
583
bool ok = true;
584
if (!attrs.hasAttribute(SUMO_ATTR_SHAPE)) {
585
const double maxSegmentLength = OptionsCont::getOptions().getFloat("geometry.max-segment-length");
586
if (maxSegmentLength > 0) {
587
PositionVector shape;
588
shape.push_back(myFromNode->getPosition());
589
shape.push_back(myToNode->getPosition());
590
// shape is already cartesian but we must use a copy because the original will be modified
591
NBNetBuilder::addGeometrySegments(shape, PositionVector(shape), maxSegmentLength);
592
return shape;
593
} else {
594
myReinitKeepEdgeShape = false;
595
return PositionVector();
596
}
597
}
598
PositionVector shape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, nullptr, ok, PositionVector());
599
if (!NBNetBuilder::transformCoordinates(shape, true, myLocation)) {
600
WRITE_ERRORF(TL("Unable to project coordinates for edge '%'."), myCurrentID);
601
}
602
myReinitKeepEdgeShape = myKeepEdgeShape;
603
return shape;
604
}
605
606
607
LaneSpreadFunction
608
NIXMLEdgesHandler::tryGetLaneSpread(const SUMOSAXAttributes& attrs) {
609
bool ok = true;
610
LaneSpreadFunction result = myLanesSpread;
611
std::string lsfS = toString(result);
612
lsfS = attrs.getOpt<std::string>(SUMO_ATTR_SPREADTYPE, myCurrentID.c_str(), ok, lsfS);
613
if (SUMOXMLDefinitions::LaneSpreadFunctions.hasString(lsfS)) {
614
result = SUMOXMLDefinitions::LaneSpreadFunctions.get(lsfS);
615
} else {
616
WRITE_WARNINGF(TL("Ignoring unknown spreadType '%' for edge '%'."), lsfS, myCurrentID);
617
}
618
return result;
619
}
620
621
622
void
623
NIXMLEdgesHandler::deleteEdge(const SUMOSAXAttributes& attrs) {
624
bool ok = true;
625
myCurrentID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
626
if (!ok) {
627
return;
628
}
629
NBEdge* edge = myEdgeCont.retrieve(myCurrentID);
630
if (edge == nullptr) {
631
WRITE_WARNING("Ignoring tag '" + toString(SUMO_TAG_DEL) + "' for unknown edge '" +
632
myCurrentID + "'");
633
return;
634
}
635
const int lane = attrs.getOpt<int>(SUMO_ATTR_INDEX, myCurrentID.c_str(), ok, -1);
636
if (lane < 0) {
637
myEdgeCont.extract(myDistrictCont, edge, true);
638
} else {
639
edge->deleteLane(lane, false, true);
640
}
641
}
642
643
644
void
645
NIXMLEdgesHandler::myEndElement(int element) {
646
if (element == SUMO_TAG_VIEWSETTINGS_EDGES) {
647
delete myLocation;
648
myLocation = nullptr;
649
return;
650
}
651
if (myCurrentEdge == nullptr) {
652
return;
653
}
654
if (element == SUMO_TAG_EDGE) {
655
myLastParameterised.pop_back();
656
// add bike lane, wait until lanes are loaded to avoid building if it already exists
657
if (myBikeLaneWidth != NBEdge::UNSPECIFIED_WIDTH) {
658
myCurrentEdge->addBikeLane(myBikeLaneWidth);
659
}
660
// add sidewalk, wait until lanes are loaded to avoid building if it already exists
661
if (mySidewalkWidth != NBEdge::UNSPECIFIED_WIDTH) {
662
myCurrentEdge->addSidewalk(mySidewalkWidth);
663
}
664
// apply default stopOffsets of edge to all lanes without specified stopOffset.
665
const StopOffset stopOffsets = myCurrentEdge->getEdgeStopOffset();
666
if (stopOffsets.isDefined()) {
667
for (int i = 0; i < (int)myCurrentEdge->getLanes().size(); i++) {
668
myCurrentEdge->setEdgeStopOffset(i, stopOffsets, false);
669
}
670
}
671
if (!myIsUpdate) {
672
try {
673
if (!myEdgeCont.insert(myCurrentEdge)) {
674
WRITE_ERRORF(TL("Duplicate edge '%' occurred."), myCurrentID);
675
delete myCurrentEdge;
676
myCurrentEdge = nullptr;
677
return;
678
}
679
} catch (InvalidArgument& e) {
680
WRITE_ERROR(e.what());
681
throw;
682
} catch (...) {
683
WRITE_ERRORF(TL("An important information is missing in edge '%'."), myCurrentID);
684
}
685
}
686
myEdgeCont.processSplits(myCurrentEdge, mySplits, myNodeCont, myDistrictCont, myTLLogicCont);
687
myCurrentEdge = nullptr;
688
} else if (element == SUMO_TAG_LANE && myCurrentLaneIndex != -1) {
689
myLastParameterised.pop_back();
690
myCurrentLaneIndex = -1;
691
}
692
}
693
694
695
void
696
NIXMLEdgesHandler::addRoundabout(const SUMOSAXAttributes& attrs) {
697
bool ok = true;
698
const std::vector<std::string>& edgeIDs = attrs.get<std::vector<std::string> >(SUMO_ATTR_EDGES, nullptr, ok);
699
if (ok) {
700
EdgeSet roundabout;
701
for (const std::string& eID : edgeIDs) {
702
NBEdge* edge = myEdgeCont.retrieve(eID);
703
if (edge == nullptr) {
704
if (!myEdgeCont.wasIgnored(eID)) {
705
WRITE_ERRORF(TL("Unknown edge '%' in roundabout."), eID);
706
}
707
} else {
708
roundabout.insert(edge);
709
}
710
}
711
myEdgeCont.addRoundabout(roundabout);
712
}
713
}
714
715
716
/****************************************************************************/
717
718