Line data Source code
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 2003 : NIXMLNodesHandler::NIXMLNodesHandler(NBNodeCont& nc, NBEdgeCont& ec,
47 : NBTrafficLightLogicCont& tlc,
48 2003 : OptionsCont& options) :
49 : SUMOSAXHandler("xml-nodes - file"),
50 2003 : myOptions(options),
51 2003 : myNodeCont(nc),
52 2003 : myEdgeCont(ec),
53 2003 : myTLLogicCont(tlc),
54 2003 : myLocation(nullptr),
55 4006 : myLastParameterised(nullptr) {
56 2003 : }
57 :
58 :
59 2003 : NIXMLNodesHandler::~NIXMLNodesHandler() {
60 2003 : delete myLocation;
61 2003 : }
62 :
63 :
64 : void
65 13624 : NIXMLNodesHandler::myStartElement(int element,
66 : const SUMOSAXAttributes& attrs) {
67 13624 : switch (element) {
68 563 : case SUMO_TAG_LOCATION:
69 563 : delete myLocation;
70 563 : myLocation = NIImporter_SUMO::loadLocation(attrs);
71 563 : if (myLocation) {
72 563 : GeoConvHelper::setLoadedPlain(getFileName(), *myLocation);
73 : }
74 : break;
75 11809 : case SUMO_TAG_NODE:
76 11809 : addNode(attrs);
77 11808 : break;
78 9 : case SUMO_TAG_JOIN:
79 9 : addJoinCluster(attrs);
80 9 : break;
81 1 : case SUMO_TAG_JOINEXCLUDE:
82 1 : addJoinExclusion(attrs);
83 1 : break;
84 11 : case SUMO_TAG_DEL:
85 11 : deleteNode(attrs);
86 11 : break;
87 149 : case SUMO_TAG_PARAM:
88 149 : if (myLastParameterised != nullptr) {
89 149 : bool ok = true;
90 149 : const std::string key = attrs.get<std::string>(SUMO_ATTR_KEY, nullptr, ok);
91 : // circumventing empty string test
92 149 : const std::string val = attrs.hasAttribute(SUMO_ATTR_VALUE) ? attrs.getString(SUMO_ATTR_VALUE) : "";
93 149 : myLastParameterised->setParameter(key, val);
94 : }
95 : break;
96 : default:
97 : break;
98 : }
99 13623 : }
100 :
101 :
102 : void
103 13622 : NIXMLNodesHandler::myEndElement(int element) {
104 13622 : switch (element) {
105 11808 : case SUMO_TAG_NODE:
106 11808 : myLastParameterised = nullptr;
107 11808 : break;
108 : default:
109 : break;
110 : }
111 13622 : }
112 :
113 :
114 : void
115 11809 : NIXMLNodesHandler::addNode(const SUMOSAXAttributes& attrs) {
116 11809 : bool ok = true;
117 : // get the id, report a warning if not given or empty...
118 11809 : myID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
119 11809 : if (!ok) {
120 0 : return;
121 : }
122 11809 : 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 11809 : if (node != nullptr) {
128 1456 : 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 11809 : if (attrs.hasAttribute(SUMO_ATTR_X)) {
135 11784 : myPosition.set(attrs.get<double>(SUMO_ATTR_X, myID.c_str(), ok), myPosition.y());
136 : xOk = true;
137 : needConversion = true;
138 : }
139 11809 : if (attrs.hasAttribute(SUMO_ATTR_Y)) {
140 11785 : myPosition.set(myPosition.x(), attrs.get<double>(SUMO_ATTR_Y, myID.c_str(), ok));
141 : yOk = true;
142 : needConversion = true;
143 : }
144 11809 : if (attrs.hasAttribute(SUMO_ATTR_Z)) {
145 57 : myPosition.set(myPosition.x(), myPosition.y(), attrs.get<double>(SUMO_ATTR_Z, myID.c_str(), ok));
146 : }
147 11809 : if (xOk && yOk) {
148 11809 : if (needConversion && !NBNetBuilder::transformCoordinate(myPosition, true, myLocation)) {
149 0 : WRITE_ERRORF(TL("Unable to project coordinates for node '%'."), myID);
150 : }
151 : } else {
152 0 : WRITE_ERRORF(TL("Missing position (at node ID='%')."), myID);
153 : }
154 11809 : bool updateEdgeGeometries = node != nullptr && myPosition != node->getPosition();
155 11809 : node = processNodeType(attrs, node, myID, myPosition, updateEdgeGeometries, myNodeCont, myEdgeCont, myTLLogicCont, myLocation);
156 11808 : myLastParameterised = node;
157 : }
158 :
159 :
160 : NBNode*
161 12030 : 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 12030 : bool ok = true;
166 : // get the type
167 : SumoXMLNodeType type = SumoXMLNodeType::UNKNOWN;
168 12030 : if (node != nullptr) {
169 : type = node->getType();
170 : }
171 24060 : std::string typeS = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, nodeID.c_str(), ok, "");
172 : if (SUMOXMLDefinitions::NodeTypes.hasString(typeS)) {
173 6204 : type = SUMOXMLDefinitions::NodeTypes.get(typeS);
174 6204 : 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 12030 : if (node == nullptr) {
184 10362 : node = new NBNode(nodeID, position, type);
185 10361 : if (!nc.insert(node)) {
186 0 : 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 1668 : && (type == SumoXMLNodeType::RIGHT_BEFORE_LEFT || type == SumoXMLNodeType::LEFT_BEFORE_RIGHT)) {
193 4 : ec.removeRoundabout(node);
194 : }
195 1668 : node->reinit(position, type, updateEdgeGeometries);
196 : }
197 : // process traffic light definition
198 12029 : if (NBNode::isTrafficLight(type)) {
199 589 : processTrafficLightDefinitions(attrs, node, tlc);
200 11440 : } else if (isPatch && typeS != "") {
201 22 : nc.markAsNotTLS(node);
202 : }
203 : // remove previously set tls if this node is not controlled by them
204 12048 : for (std::set<NBTrafficLightDefinition*>::iterator i = oldTLS.begin(); i != oldTLS.end(); ++i) {
205 19 : if ((*i)->getNodes().size() == 0) {
206 29 : tlc.removeFully((*i)->getID());
207 : }
208 : }
209 :
210 : // set optional shape
211 12029 : PositionVector shape;
212 12029 : if (attrs.hasAttribute(SUMO_ATTR_SHAPE)) {
213 78 : shape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, nodeID.c_str(), ok, PositionVector());
214 39 : if (!NBNetBuilder::transformCoordinates(shape, true, from_srs)) {
215 0 : WRITE_ERRORF(TL("Unable to project node shape at node '%'."), node->getID());
216 : }
217 39 : if (shape.size() > 2) {
218 38 : shape.closePolygon();
219 : }
220 39 : node->setCustomShape(shape);
221 : }
222 : // set optional radius
223 12029 : if (attrs.hasAttribute(SUMO_ATTR_RADIUS)) {
224 61 : node->setRadius(attrs.get<double>(SUMO_ATTR_RADIUS, nodeID.c_str(), ok));
225 : }
226 : // set optional keepClear flag
227 12029 : if (attrs.hasAttribute(SUMO_ATTR_KEEP_CLEAR)) {
228 1 : node->setKeepClear(attrs.get<bool>(SUMO_ATTR_KEEP_CLEAR, nodeID.c_str(), ok));
229 : }
230 12029 : node->setRightOfWay(attrs.getOpt<RightOfWay>(SUMO_ATTR_RIGHT_OF_WAY, nodeID.c_str(), ok, node->getRightOfWay()));
231 12029 : node->setFringeType(attrs.getOpt<FringeType>(SUMO_ATTR_FRINGE, nodeID.c_str(), ok, node->getFringeType()));
232 12029 : node->setRoundaboutType(attrs.getOpt<RoundaboutType>(SUMO_ATTR_ROUNDABOUT, nodeID.c_str(), ok, node->getRoundaboutType()));
233 : // set optional name
234 12029 : if (attrs.hasAttribute(SUMO_ATTR_NAME)) {
235 4 : node->setName(attrs.get<std::string>(SUMO_ATTR_NAME, nodeID.c_str(), ok));
236 : }
237 12029 : return node;
238 12029 : }
239 :
240 :
241 : void
242 11 : NIXMLNodesHandler::deleteNode(const SUMOSAXAttributes& attrs) {
243 11 : bool ok = true;
244 : // get the id, report a warning if not given or empty...
245 11 : myID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
246 11 : if (!ok) {
247 0 : return;
248 : }
249 11 : NBNode* node = myNodeCont.retrieve(myID);
250 11 : if (node == nullptr) {
251 0 : WRITE_WARNING("Ignoring tag '" + toString(SUMO_TAG_DEL) + "' for unknown node '" +
252 : myID + "'");
253 0 : return;
254 : } else {
255 11 : myNodeCont.extract(node, true);
256 : }
257 : }
258 :
259 :
260 : void
261 9 : NIXMLNodesHandler::addJoinCluster(const SUMOSAXAttributes& attrs) {
262 9 : bool ok = true;
263 9 : const std::string clusterString = attrs.get<std::string>(SUMO_ATTR_NODES, nullptr, ok);
264 9 : const std::set<std::string>& cluster = StringTokenizer(clusterString).getSet();
265 :
266 9 : myID = attrs.getOpt<std::string>(SUMO_ATTR_ID, nullptr, ok, myNodeCont.createClusterId(cluster));
267 :
268 9 : Position pos = Position::INVALID;
269 9 : if (attrs.hasAttribute(SUMO_ATTR_X)) {
270 1 : pos.setx(attrs.get<double>(SUMO_ATTR_X, myID.c_str(), ok));
271 : }
272 9 : if (attrs.hasAttribute(SUMO_ATTR_Y)) {
273 1 : pos.sety(attrs.get<double>(SUMO_ATTR_Y, myID.c_str(), ok));
274 : }
275 9 : if (attrs.hasAttribute(SUMO_ATTR_Z)) {
276 1 : pos.setz(attrs.get<double>(SUMO_ATTR_Z, myID.c_str(), ok));
277 : }
278 :
279 9 : NBNode* node = processNodeType(attrs, nullptr, myID, pos, false, myNodeCont, myEdgeCont, myTLLogicCont, myLocation);
280 9 : if (ok) {
281 9 : myNodeCont.addCluster2Join(cluster, node);
282 : }
283 9 : }
284 :
285 :
286 : void
287 1 : NIXMLNodesHandler::addJoinExclusion(const SUMOSAXAttributes& attrs) {
288 1 : bool ok = true;
289 2 : const std::vector<std::string> ids = StringTokenizer(
290 2 : attrs.get<std::string>(SUMO_ATTR_NODES, nullptr, ok)).getVector();
291 1 : if (ok) {
292 1 : myNodeCont.addJoinExclusion(ids);
293 : }
294 1 : }
295 :
296 :
297 : void
298 589 : NIXMLNodesHandler::processTrafficLightDefinitions(const SUMOSAXAttributes& attrs,
299 : NBNode* currentNode, NBTrafficLightLogicCont& tlc) {
300 : // try to get the tl-id
301 : // if a tl-id is given, we will look whether this tl already exists
302 : // if so, we will add the node to it (and to all programs with this id), otherwise allocate a new one with this id
303 : // if no tl-id exists, we will build a tl with the node's id
304 : std::set<NBTrafficLightDefinition*> tlDefs;
305 589 : bool ok = true;
306 :
307 589 : std::string oldTlID = "";
308 1178 : std::string oldTypeS = OptionsCont::getOptions().getString("tls.default-type");
309 :
310 589 : if (currentNode->isTLControlled()) {
311 17 : NBTrafficLightDefinition* oldDef = *(currentNode->getControllingTLS().begin());
312 : oldTlID = oldDef->getID();
313 34 : oldTypeS = toString(oldDef->getType());
314 : }
315 1178 : std::string tlID = attrs.getOpt<std::string>(SUMO_ATTR_TLID, nullptr, ok, oldTlID);
316 1178 : std::string typeS = attrs.getOpt<std::string>(SUMO_ATTR_TLTYPE, nullptr, ok, oldTypeS);
317 589 : if (tlID != oldTlID || typeS != oldTypeS) {
318 376 : currentNode->removeTrafficLights();
319 : }
320 : TrafficLightType type;
321 : if (SUMOXMLDefinitions::TrafficLightTypes.hasString(typeS)) {
322 589 : type = SUMOXMLDefinitions::TrafficLightTypes.get(typeS);
323 : } else {
324 0 : WRITE_ERRORF(TL("Unknown traffic light type '%' for node '%'."), typeS, currentNode->getID());
325 0 : return;
326 : }
327 : TrafficLightLayout layout = TrafficLightLayout::DEFAULT;
328 589 : if (attrs.hasAttribute(SUMO_ATTR_TLLAYOUT)) {
329 19 : std::string layoutS = attrs.get<std::string>(SUMO_ATTR_TLLAYOUT, nullptr, ok);
330 : if (SUMOXMLDefinitions::TrafficLightLayouts.hasString(layoutS)) {
331 19 : layout = SUMOXMLDefinitions::TrafficLightLayouts.get(layoutS);
332 : } else {
333 0 : WRITE_ERRORF(TL("Unknown traffic light layout '%' for node '%'."), typeS, currentNode->getID());
334 : return;
335 : }
336 : }
337 589 : if (tlID != "" && tlc.getPrograms(tlID).size() > 0) {
338 : // we already have definitions for this tlID
339 110 : for (auto item : tlc.getPrograms(tlID)) {
340 55 : NBTrafficLightDefinition* def = item.second;
341 : tlDefs.insert(def);
342 55 : def->addNode(currentNode);
343 55 : if (def->getType() != type && attrs.hasAttribute(SUMO_ATTR_TLTYPE)) {
344 3 : WRITE_WARNINGF(TL("Changing traffic light type '%' to '%' for tl '%'."), toString(def->getType()), typeS, tlID);
345 1 : def->setType(type);
346 1 : if (type != TrafficLightType::STATIC && dynamic_cast<NBLoadedSUMOTLDef*>(def) != nullptr) {
347 1 : dynamic_cast<NBLoadedSUMOTLDef*>(def)->guessMinMaxDuration();
348 : }
349 : }
350 55 : if (layout != TrafficLightLayout::DEFAULT && dynamic_cast<NBOwnTLDef*>(def) != nullptr) {
351 : dynamic_cast<NBOwnTLDef*>(def)->setLayout(layout);
352 : }
353 : }
354 : } else {
355 : // we need to add a new defition
356 534 : tlID = (tlID == "" ? currentNode->getID() : tlID);
357 534 : NBOwnTLDef* tlDef = new NBOwnTLDef(tlID, currentNode, 0, type);
358 534 : if (!tlc.insert(tlDef)) {
359 : // actually, nothing should fail here
360 0 : delete tlDef;
361 0 : throw ProcessError(TLF("Could not allocate tls '%'.", currentNode->getID()));
362 : }
363 : tlDef->setLayout(layout);
364 534 : tlDefs.insert(tlDef);
365 : }
366 : // process inner edges which shall be controlled
367 589 : const std::vector<std::string>& controlledInner = attrs.getOpt<std::vector<std::string> >(SUMO_ATTR_CONTROLLED_INNER, nullptr, ok);
368 589 : if (controlledInner.size() != 0) {
369 48 : for (std::set<NBTrafficLightDefinition*>::iterator it = tlDefs.begin(); it != tlDefs.end(); it++) {
370 24 : (*it)->addControlledInnerEdges(controlledInner);
371 : }
372 : }
373 589 : }
374 :
375 :
376 : /****************************************************************************/
|