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