Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netimport/NIXMLNodesHandler.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 NIXMLNodesHandler.cpp
15
/// @author Daniel Krajzewicz
16
/// @author Jakob Erdmann
17
/// @author Sascha Krieg
18
/// @author Michael Behrisch
19
/// @date Tue, 20 Nov 2001
20
///
21
// Importer for network nodes stored in XML
22
/****************************************************************************/
23
#include <config.h>
24
25
#include <string>
26
#include <iostream>
27
#include <utils/xml/SUMOSAXHandler.h>
28
#include <utils/xml/SUMOXMLDefinitions.h>
29
#include <utils/common/MsgHandler.h>
30
#include <utils/common/StringUtils.h>
31
#include <utils/common/ToString.h>
32
#include <utils/common/StringTokenizer.h>
33
#include <utils/options/OptionsCont.h>
34
#include <utils/geom/GeoConvHelper.h>
35
#include <netbuild/NBNodeCont.h>
36
#include <netbuild/NBTrafficLightLogicCont.h>
37
#include <netbuild/NBOwnTLDef.h>
38
#include <netbuild/NBNetBuilder.h>
39
#include "NIXMLNodesHandler.h"
40
#include "NIImporter_SUMO.h"
41
42
43
// ===========================================================================
44
// method definitions
45
// ===========================================================================
46
NIXMLNodesHandler::NIXMLNodesHandler(NBNodeCont& nc, NBEdgeCont& ec,
47
NBTrafficLightLogicCont& tlc,
48
OptionsCont& options) :
49
SUMOSAXHandler("xml-nodes - file"),
50
myOptions(options),
51
myNodeCont(nc),
52
myEdgeCont(ec),
53
myTLLogicCont(tlc),
54
myLocation(nullptr),
55
myLastParameterised(nullptr) {
56
}
57
58
59
NIXMLNodesHandler::~NIXMLNodesHandler() {
60
delete myLocation;
61
}
62
63
64
void
65
NIXMLNodesHandler::myStartElement(int element,
66
const SUMOSAXAttributes& attrs) {
67
switch (element) {
68
case SUMO_TAG_LOCATION:
69
delete myLocation;
70
myLocation = NIImporter_SUMO::loadLocation(attrs);
71
if (myLocation) {
72
GeoConvHelper::setLoadedPlain(getFileName(), *myLocation);
73
}
74
break;
75
case SUMO_TAG_NODE:
76
addNode(attrs);
77
break;
78
case SUMO_TAG_JOIN:
79
addJoinCluster(attrs);
80
break;
81
case SUMO_TAG_JOINEXCLUDE:
82
addJoinExclusion(attrs);
83
break;
84
case SUMO_TAG_DEL:
85
deleteNode(attrs);
86
break;
87
case SUMO_TAG_PARAM:
88
if (myLastParameterised != nullptr) {
89
bool ok = true;
90
const std::string key = attrs.get<std::string>(SUMO_ATTR_KEY, nullptr, ok);
91
// circumventing empty string test
92
const std::string val = attrs.hasAttribute(SUMO_ATTR_VALUE) ? attrs.getString(SUMO_ATTR_VALUE) : "";
93
myLastParameterised->setParameter(key, val);
94
}
95
break;
96
default:
97
break;
98
}
99
}
100
101
102
void
103
NIXMLNodesHandler::myEndElement(int element) {
104
switch (element) {
105
case SUMO_TAG_NODE:
106
myLastParameterised = nullptr;
107
break;
108
default:
109
break;
110
}
111
}
112
113
114
void
115
NIXMLNodesHandler::addNode(const SUMOSAXAttributes& attrs) {
116
bool ok = true;
117
// get the id, report a warning if not given or empty...
118
myID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
119
if (!ok) {
120
return;
121
}
122
NBNode* node = myNodeCont.retrieve(myID);
123
// retrieve the position of the node
124
bool xOk = false;
125
bool yOk = false;
126
bool needConversion = true;
127
if (node != nullptr) {
128
myPosition = node->getPosition();
129
xOk = yOk = true;
130
needConversion = false;
131
} else {
132
myPosition.set(0, 0, 0); // better to reset than to reuse the previous (z)-value
133
}
134
if (attrs.hasAttribute(SUMO_ATTR_X)) {
135
myPosition.set(attrs.get<double>(SUMO_ATTR_X, myID.c_str(), ok), myPosition.y());
136
xOk = true;
137
needConversion = true;
138
}
139
if (attrs.hasAttribute(SUMO_ATTR_Y)) {
140
myPosition.set(myPosition.x(), attrs.get<double>(SUMO_ATTR_Y, myID.c_str(), ok));
141
yOk = true;
142
needConversion = true;
143
}
144
if (attrs.hasAttribute(SUMO_ATTR_Z)) {
145
myPosition.set(myPosition.x(), myPosition.y(), attrs.get<double>(SUMO_ATTR_Z, myID.c_str(), ok));
146
}
147
if (xOk && yOk) {
148
if (needConversion && !NBNetBuilder::transformCoordinate(myPosition, true, myLocation)) {
149
WRITE_ERRORF(TL("Unable to project coordinates for node '%'."), myID);
150
}
151
} else {
152
WRITE_ERRORF(TL("Missing position (at node ID='%')."), myID);
153
}
154
bool updateEdgeGeometries = node != nullptr && myPosition != node->getPosition();
155
node = processNodeType(attrs, node, myID, myPosition, updateEdgeGeometries, myNodeCont, myEdgeCont, myTLLogicCont, myLocation);
156
myLastParameterised = node;
157
}
158
159
160
NBNode*
161
NIXMLNodesHandler::processNodeType(const SUMOSAXAttributes& attrs, NBNode* node, const std::string& nodeID, const Position& position,
162
bool updateEdgeGeometries,
163
NBNodeCont& nc, NBEdgeCont& ec, NBTrafficLightLogicCont& tlc,
164
GeoConvHelper* from_srs) {
165
bool ok = true;
166
// get the type
167
SumoXMLNodeType type = SumoXMLNodeType::UNKNOWN;
168
if (node != nullptr) {
169
type = node->getType();
170
}
171
std::string typeS = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, nodeID.c_str(), ok, "");
172
if (SUMOXMLDefinitions::NodeTypes.hasString(typeS)) {
173
type = SUMOXMLDefinitions::NodeTypes.get(typeS);
174
if (type == SumoXMLNodeType::DEAD_END_DEPRECATED || type == SumoXMLNodeType::DEAD_END) {
175
// dead end is a computed status. Reset this to unknown so it will
176
// be corrected if additional connections are loaded
177
type = SumoXMLNodeType::UNKNOWN;
178
}
179
}
180
std::set<NBTrafficLightDefinition*> oldTLS;
181
// check whether a prior node shall be modified
182
const bool isPatch = node != nullptr;
183
if (node == nullptr) {
184
node = new NBNode(nodeID, position, type);
185
if (!nc.insert(node)) {
186
throw ProcessError(TLF("Could not insert node though checked this before (id='%').", nodeID));
187
}
188
} else {
189
// patch information
190
oldTLS = node->getControllingTLS();
191
if (node->getType() == SumoXMLNodeType::PRIORITY
192
&& (type == SumoXMLNodeType::RIGHT_BEFORE_LEFT || type == SumoXMLNodeType::LEFT_BEFORE_RIGHT)) {
193
ec.removeRoundabout(node);
194
}
195
node->reinit(position, type, updateEdgeGeometries);
196
}
197
// process traffic light definition
198
if (NBNode::isTrafficLight(type)) {
199
processTrafficLightDefinitions(attrs, node, tlc);
200
} else if (isPatch && typeS != "") {
201
nc.markAsNotTLS(node);
202
}
203
// remove previously set tls if this node is not controlled by them
204
for (std::set<NBTrafficLightDefinition*>::iterator i = oldTLS.begin(); i != oldTLS.end(); ++i) {
205
if ((*i)->getNodes().size() == 0) {
206
tlc.removeFully((*i)->getID());
207
}
208
}
209
210
// set optional shape
211
PositionVector shape;
212
if (attrs.hasAttribute(SUMO_ATTR_SHAPE)) {
213
shape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, nodeID.c_str(), ok, PositionVector());
214
if (!NBNetBuilder::transformCoordinates(shape, true, from_srs)) {
215
WRITE_ERRORF(TL("Unable to project node shape at node '%'."), node->getID());
216
}
217
if (shape.size() > 2) {
218
shape.closePolygon();
219
}
220
node->setCustomShape(shape);
221
}
222
// set optional radius
223
if (attrs.hasAttribute(SUMO_ATTR_RADIUS)) {
224
node->setRadius(attrs.get<double>(SUMO_ATTR_RADIUS, nodeID.c_str(), ok));
225
}
226
// set optional keepClear flag
227
if (attrs.hasAttribute(SUMO_ATTR_KEEP_CLEAR)) {
228
node->setKeepClear(attrs.get<bool>(SUMO_ATTR_KEEP_CLEAR, nodeID.c_str(), ok));
229
}
230
node->setRightOfWay(attrs.getOpt<RightOfWay>(SUMO_ATTR_RIGHT_OF_WAY, nodeID.c_str(), ok, node->getRightOfWay()));
231
node->setFringeType(attrs.getOpt<FringeType>(SUMO_ATTR_FRINGE, nodeID.c_str(), ok, node->getFringeType()));
232
// set optional name
233
if (attrs.hasAttribute(SUMO_ATTR_NAME)) {
234
node->setName(attrs.get<std::string>(SUMO_ATTR_NAME, nodeID.c_str(), ok));
235
}
236
return node;
237
}
238
239
240
void
241
NIXMLNodesHandler::deleteNode(const SUMOSAXAttributes& attrs) {
242
bool ok = true;
243
// get the id, report a warning if not given or empty...
244
myID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
245
if (!ok) {
246
return;
247
}
248
NBNode* node = myNodeCont.retrieve(myID);
249
if (node == nullptr) {
250
WRITE_WARNING("Ignoring tag '" + toString(SUMO_TAG_DEL) + "' for unknown node '" +
251
myID + "'");
252
return;
253
} else {
254
myNodeCont.extract(node, true);
255
}
256
}
257
258
259
void
260
NIXMLNodesHandler::addJoinCluster(const SUMOSAXAttributes& attrs) {
261
bool ok = true;
262
const std::string clusterString = attrs.get<std::string>(SUMO_ATTR_NODES, nullptr, ok);
263
const std::set<std::string>& cluster = StringTokenizer(clusterString).getSet();
264
265
myID = attrs.getOpt<std::string>(SUMO_ATTR_ID, nullptr, ok, myNodeCont.createClusterId(cluster));
266
267
Position pos = Position::INVALID;
268
if (attrs.hasAttribute(SUMO_ATTR_X)) {
269
pos.setx(attrs.get<double>(SUMO_ATTR_X, myID.c_str(), ok));
270
}
271
if (attrs.hasAttribute(SUMO_ATTR_Y)) {
272
pos.sety(attrs.get<double>(SUMO_ATTR_Y, myID.c_str(), ok));
273
}
274
if (attrs.hasAttribute(SUMO_ATTR_Z)) {
275
pos.setz(attrs.get<double>(SUMO_ATTR_Z, myID.c_str(), ok));
276
}
277
278
NBNode* node = processNodeType(attrs, nullptr, myID, pos, false, myNodeCont, myEdgeCont, myTLLogicCont, myLocation);
279
if (ok) {
280
myNodeCont.addCluster2Join(cluster, node);
281
}
282
}
283
284
285
void
286
NIXMLNodesHandler::addJoinExclusion(const SUMOSAXAttributes& attrs) {
287
bool ok = true;
288
const std::vector<std::string> ids = StringTokenizer(
289
attrs.get<std::string>(SUMO_ATTR_NODES, nullptr, ok)).getVector();
290
if (ok) {
291
myNodeCont.addJoinExclusion(ids);
292
}
293
}
294
295
296
void
297
NIXMLNodesHandler::processTrafficLightDefinitions(const SUMOSAXAttributes& attrs,
298
NBNode* currentNode, NBTrafficLightLogicCont& tlc) {
299
// try to get the tl-id
300
// if a tl-id is given, we will look whether this tl already exists
301
// if so, we will add the node to it (and to all programs with this id), otherwise allocate a new one with this id
302
// if no tl-id exists, we will build a tl with the node's id
303
std::set<NBTrafficLightDefinition*> tlDefs;
304
bool ok = true;
305
306
std::string oldTlID = "";
307
std::string oldTypeS = OptionsCont::getOptions().getString("tls.default-type");
308
309
if (currentNode->isTLControlled()) {
310
NBTrafficLightDefinition* oldDef = *(currentNode->getControllingTLS().begin());
311
oldTlID = oldDef->getID();
312
oldTypeS = toString(oldDef->getType());
313
}
314
std::string tlID = attrs.getOpt<std::string>(SUMO_ATTR_TLID, nullptr, ok, oldTlID);
315
std::string typeS = attrs.getOpt<std::string>(SUMO_ATTR_TLTYPE, nullptr, ok, oldTypeS);
316
if (tlID != oldTlID || typeS != oldTypeS) {
317
currentNode->removeTrafficLights();
318
}
319
TrafficLightType type;
320
if (SUMOXMLDefinitions::TrafficLightTypes.hasString(typeS)) {
321
type = SUMOXMLDefinitions::TrafficLightTypes.get(typeS);
322
} else {
323
WRITE_ERRORF(TL("Unknown traffic light type '%' for node '%'."), typeS, currentNode->getID());
324
return;
325
}
326
TrafficLightLayout layout = TrafficLightLayout::DEFAULT;
327
if (attrs.hasAttribute(SUMO_ATTR_TLLAYOUT)) {
328
std::string layoutS = attrs.get<std::string>(SUMO_ATTR_TLLAYOUT, nullptr, ok);
329
if (SUMOXMLDefinitions::TrafficLightLayouts.hasString(layoutS)) {
330
layout = SUMOXMLDefinitions::TrafficLightLayouts.get(layoutS);
331
} else {
332
WRITE_ERRORF(TL("Unknown traffic light layout '%' for node '%'."), typeS, currentNode->getID());
333
return;
334
}
335
}
336
if (tlID != "" && tlc.getPrograms(tlID).size() > 0) {
337
// we already have definitions for this tlID
338
for (auto item : tlc.getPrograms(tlID)) {
339
NBTrafficLightDefinition* def = item.second;
340
tlDefs.insert(def);
341
def->addNode(currentNode);
342
if (def->getType() != type && attrs.hasAttribute(SUMO_ATTR_TLTYPE)) {
343
WRITE_WARNINGF(TL("Changing traffic light type '%' to '%' for tl '%'."), toString(def->getType()), typeS, tlID);
344
def->setType(type);
345
if (type != TrafficLightType::STATIC && dynamic_cast<NBLoadedSUMOTLDef*>(def) != nullptr) {
346
dynamic_cast<NBLoadedSUMOTLDef*>(def)->guessMinMaxDuration();
347
}
348
}
349
if (layout != TrafficLightLayout::DEFAULT && dynamic_cast<NBOwnTLDef*>(def) != nullptr) {
350
dynamic_cast<NBOwnTLDef*>(def)->setLayout(layout);
351
}
352
}
353
} else {
354
// we need to add a new defition
355
tlID = (tlID == "" ? currentNode->getID() : tlID);
356
NBOwnTLDef* tlDef = new NBOwnTLDef(tlID, currentNode, 0, type);
357
if (!tlc.insert(tlDef)) {
358
// actually, nothing should fail here
359
delete tlDef;
360
throw ProcessError(TLF("Could not allocate tls '%'.", currentNode->getID()));
361
}
362
tlDef->setLayout(layout);
363
tlDefs.insert(tlDef);
364
}
365
// process inner edges which shall be controlled
366
const std::vector<std::string>& controlledInner = attrs.getOpt<std::vector<std::string> >(SUMO_ATTR_CONTROLLED_INNER, nullptr, ok);
367
if (controlledInner.size() != 0) {
368
for (std::set<NBTrafficLightDefinition*>::iterator it = tlDefs.begin(); it != tlDefs.end(); it++) {
369
(*it)->addControlledInnerEdges(controlledInner);
370
}
371
}
372
}
373
374
375
/****************************************************************************/
376
377