1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file NIXMLNodesHandler.cpp
11 /// @author Daniel Krajzewicz
12 /// @author Jakob Erdmann
13 /// @author Sascha Krieg
14 /// @author Michael Behrisch
15 /// @date Tue, 20 Nov 2001
16 /// @version $Id$
17 ///
18 // Importer for network nodes stored in XML
19 /****************************************************************************/
20
21
22 // ===========================================================================
23 // included modules
24 // ===========================================================================
25 #include <config.h>
26
27 #include <string>
28 #include <iostream>
29 #include <xercesc/sax/HandlerBase.hpp>
30 #include <xercesc/sax/AttributeList.hpp>
31 #include <xercesc/sax/SAXParseException.hpp>
32 #include <xercesc/sax/SAXException.hpp>
33 #include <utils/xml/SUMOSAXHandler.h>
34 #include <utils/xml/SUMOXMLDefinitions.h>
35 #include <utils/common/MsgHandler.h>
36 #include <utils/common/StringUtils.h>
37 #include <utils/common/ToString.h>
38 #include <utils/common/StringTokenizer.h>
39 #include <utils/options/OptionsCont.h>
40 #include <utils/geom/GeoConvHelper.h>
41 #include <netbuild/NBNodeCont.h>
42 #include <netbuild/NBTrafficLightLogicCont.h>
43 #include <netbuild/NBOwnTLDef.h>
44 #include <netbuild/NBNetBuilder.h>
45 #include "NIXMLNodesHandler.h"
46 #include "NIImporter_SUMO.h"
47
48
49 // ===========================================================================
50 // method definitions
51 // ===========================================================================
NIXMLNodesHandler(NBNodeCont & nc,NBEdgeCont & ec,NBTrafficLightLogicCont & tlc,OptionsCont & options)52 NIXMLNodesHandler::NIXMLNodesHandler(NBNodeCont& nc, NBEdgeCont& ec,
53 NBTrafficLightLogicCont& tlc,
54 OptionsCont& options) :
55 SUMOSAXHandler("xml-nodes - file"),
56 myOptions(options),
57 myNodeCont(nc),
58 myEdgeCont(ec),
59 myTLLogicCont(tlc),
60 myLocation(nullptr),
61 myLastParameterised(nullptr) {
62 }
63
64
~NIXMLNodesHandler()65 NIXMLNodesHandler::~NIXMLNodesHandler() {
66 delete myLocation;
67 }
68
69
70 void
myStartElement(int element,const SUMOSAXAttributes & attrs)71 NIXMLNodesHandler::myStartElement(int element,
72 const SUMOSAXAttributes& attrs) {
73 switch (element) {
74 case SUMO_TAG_LOCATION:
75 myLocation = NIImporter_SUMO::loadLocation(attrs);
76 break;
77 case SUMO_TAG_NODE:
78 addNode(attrs);
79 break;
80 case SUMO_TAG_JOIN:
81 addJoinCluster(attrs);
82 break;
83 case SUMO_TAG_JOINEXCLUDE:
84 addJoinExclusion(attrs);
85 break;
86 case SUMO_TAG_DELETE:
87 deleteNode(attrs);
88 break;
89 case SUMO_TAG_PARAM:
90 if (myLastParameterised != nullptr) {
91 bool ok = true;
92 const std::string key = attrs.get<std::string>(SUMO_ATTR_KEY, nullptr, ok);
93 // circumventing empty string test
94 const std::string val = attrs.hasAttribute(SUMO_ATTR_VALUE) ? attrs.getString(SUMO_ATTR_VALUE) : "";
95 myLastParameterised->setParameter(key, val);
96 }
97 default:
98 break;
99 }
100 }
101
102
103 void
myEndElement(int element)104 NIXMLNodesHandler::myEndElement(int element) {
105 switch (element) {
106 case SUMO_TAG_NODE:
107 myLastParameterised = nullptr;
108 break;
109 default:
110 break;
111 }
112 }
113
114
115 void
addNode(const SUMOSAXAttributes & attrs)116 NIXMLNodesHandler::addNode(const SUMOSAXAttributes& attrs) {
117 bool ok = true;
118 // get the id, report a warning if not given or empty...
119 myID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
120 if (!ok) {
121 return;
122 }
123 NBNode* node = myNodeCont.retrieve(myID);
124 // retrieve the position of the node
125 bool xOk = false;
126 bool yOk = false;
127 bool needConversion = true;
128 if (node != nullptr) {
129 myPosition = node->getPosition();
130 xOk = yOk = true;
131 needConversion = false;
132 } else {
133 myPosition.set(0, 0, 0); // better to reset than to reuse the previous (z)-value
134 }
135 if (attrs.hasAttribute(SUMO_ATTR_X)) {
136 myPosition.set(attrs.get<double>(SUMO_ATTR_X, myID.c_str(), ok), myPosition.y());
137 xOk = true;
138 needConversion = true;
139 }
140 if (attrs.hasAttribute(SUMO_ATTR_Y)) {
141 myPosition.set(myPosition.x(), attrs.get<double>(SUMO_ATTR_Y, myID.c_str(), ok));
142 yOk = true;
143 needConversion = true;
144 }
145 if (attrs.hasAttribute(SUMO_ATTR_Z)) {
146 myPosition.set(myPosition.x(), myPosition.y(), attrs.get<double>(SUMO_ATTR_Z, myID.c_str(), ok));
147 }
148 if (xOk && yOk) {
149 if (needConversion && !NBNetBuilder::transformCoordinate(myPosition, true, myLocation)) {
150 WRITE_ERROR("Unable to project coordinates for node '" + myID + "'.");
151 }
152 } else {
153 WRITE_ERROR("Missing position (at node ID='" + myID + "').");
154 }
155 bool updateEdgeGeometries = node != nullptr && myPosition != node->getPosition();
156 // check whether the y-axis shall be flipped
157 if (myOptions.getBool("flip-y-axis")) {
158 myPosition.mul(1.0, -1.0);
159 }
160 node = processNodeType(attrs, node, myID, myPosition, updateEdgeGeometries, myNodeCont, myEdgeCont, myTLLogicCont);
161 myLastParameterised = node;
162 }
163
164
165 NBNode*
processNodeType(const SUMOSAXAttributes & attrs,NBNode * node,const std::string & nodeID,const Position & position,bool updateEdgeGeometries,NBNodeCont & nc,NBEdgeCont & ec,NBTrafficLightLogicCont & tlc)166 NIXMLNodesHandler::processNodeType(const SUMOSAXAttributes& attrs, NBNode* node, const std::string& nodeID, const Position& position,
167 bool updateEdgeGeometries,
168 NBNodeCont& nc, NBEdgeCont& ec, NBTrafficLightLogicCont& tlc) {
169 bool ok = true;
170 // get the type
171 SumoXMLNodeType type = NODETYPE_UNKNOWN;
172 if (node != nullptr) {
173 type = node->getType();
174 }
175 std::string typeS = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, nodeID.c_str(), ok, "");
176 if (SUMOXMLDefinitions::NodeTypes.hasString(typeS)) {
177 type = SUMOXMLDefinitions::NodeTypes.get(typeS);
178 if (type == NODETYPE_DEAD_END_DEPRECATED || type == NODETYPE_DEAD_END) {
179 // dead end is a computed status. Reset this to unknown so it will
180 // be corrected if additional connections are loaded
181 type = NODETYPE_UNKNOWN;
182 }
183 }
184 std::set<NBTrafficLightDefinition*> oldTLS;
185 // check whether a prior node shall be modified
186 if (node == nullptr) {
187 node = new NBNode(nodeID, position, type);
188 if (!nc.insert(node)) {
189 throw ProcessError("Could not insert node though checked this before (id='" + nodeID + "').");
190 }
191 } else {
192 // patch information
193 oldTLS = node->getControllingTLS();
194 if (node->getType() == NODETYPE_PRIORITY && type == NODETYPE_RIGHT_BEFORE_LEFT) {
195 ec.removeRoundabout(node);
196 }
197 node->reinit(position, type, updateEdgeGeometries);
198 }
199 // process traffic light definition
200 if (NBNode::isTrafficLight(type)) {
201 processTrafficLightDefinitions(attrs, node, tlc);
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)) {
215 WRITE_ERROR("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
231 // set optional right-of-way hint
232 if (attrs.hasAttribute(SUMO_ATTR_RIGHT_OF_WAY)) {
233 node->setRightOfWay(attrs.getRightOfWay(ok));
234 }
235
236 // set optional fringe type
237 if (attrs.hasAttribute(SUMO_ATTR_FRINGE)) {
238 node->setFringeType(attrs.getFringeType(ok));
239 }
240 return node;
241 }
242
243
244 void
deleteNode(const SUMOSAXAttributes & attrs)245 NIXMLNodesHandler::deleteNode(const SUMOSAXAttributes& attrs) {
246 bool ok = true;
247 // get the id, report a warning if not given or empty...
248 myID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
249 if (!ok) {
250 return;
251 }
252 NBNode* node = myNodeCont.retrieve(myID);
253 if (node == nullptr) {
254 WRITE_WARNING("Ignoring tag '" + toString(SUMO_TAG_DELETE) + "' for unknown node '" +
255 myID + "'");
256 return;
257 } else {
258 myNodeCont.extract(node, true);
259 }
260 }
261
262
263 void
addJoinCluster(const SUMOSAXAttributes & attrs)264 NIXMLNodesHandler::addJoinCluster(const SUMOSAXAttributes& attrs) {
265 bool ok = true;
266 const std::string clusterString = attrs.get<std::string>(SUMO_ATTR_NODES, nullptr, ok);
267 std::vector<std::string> ids = StringTokenizer(clusterString).getVector();
268 std::sort(ids.begin(), ids.end());
269
270 myID = attrs.getOpt<std::string>(SUMO_ATTR_ID, nullptr, ok, "cluster_" + joinToString(ids, "_"));
271
272 Position myPosition = Position::INVALID;
273 if (attrs.hasAttribute(SUMO_ATTR_X)) {
274 myPosition.setx(attrs.get<double>(SUMO_ATTR_X, myID.c_str(), ok));
275 }
276 if (attrs.hasAttribute(SUMO_ATTR_Y)) {
277 myPosition.sety(attrs.get<double>(SUMO_ATTR_Y, myID.c_str(), ok));
278 }
279 if (attrs.hasAttribute(SUMO_ATTR_Z)) {
280 myPosition.setz(attrs.get<double>(SUMO_ATTR_Z, myID.c_str(), ok));
281 }
282
283 NBNode* node = processNodeType(attrs, nullptr, myID, myPosition, false, myNodeCont, myEdgeCont, myTLLogicCont);
284 if (ok) {
285 myNodeCont.addCluster2Join(std::set<std::string>(ids.begin(), ids.end()), node);
286 }
287 }
288
289
290 void
addJoinExclusion(const SUMOSAXAttributes & attrs)291 NIXMLNodesHandler::addJoinExclusion(const SUMOSAXAttributes& attrs) {
292 bool ok = true;
293 const std::vector<std::string> ids = StringTokenizer(
294 attrs.get<std::string>(SUMO_ATTR_NODES, nullptr, ok)).getVector();
295 if (ok) {
296 myNodeCont.addJoinExclusion(ids);
297 }
298 }
299
300
301 void
processTrafficLightDefinitions(const SUMOSAXAttributes & attrs,NBNode * currentNode,NBTrafficLightLogicCont & tlc)302 NIXMLNodesHandler::processTrafficLightDefinitions(const SUMOSAXAttributes& attrs,
303 NBNode* currentNode, NBTrafficLightLogicCont& tlc) {
304 // try to get the tl-id
305 // if a tl-id is given, we will look whether this tl already exists
306 // if so, we will add the node to it (and to all programs with this id), otherwise allocate a new one with this id
307 // if no tl-id exists, we will build a tl with the node's id
308 std::set<NBTrafficLightDefinition*> tlDefs;
309 bool ok = true;
310
311 std::string oldTlID = "";
312 std::string oldTypeS = OptionsCont::getOptions().getString("tls.default-type");
313
314 if (currentNode->isTLControlled()) {
315 NBTrafficLightDefinition* oldDef = *(currentNode->getControllingTLS().begin());
316 oldTlID = oldDef->getID();
317 oldTypeS = toString(oldDef->getType());
318 }
319 std::string tlID = attrs.getOpt<std::string>(SUMO_ATTR_TLID, nullptr, ok, oldTlID);
320 std::string typeS = attrs.getOpt<std::string>(SUMO_ATTR_TLTYPE, nullptr, ok, oldTypeS);
321 if (tlID != oldTlID || typeS != oldTypeS) {
322 currentNode->removeTrafficLights();
323 }
324 TrafficLightType type;
325 if (SUMOXMLDefinitions::TrafficLightTypes.hasString(typeS)) {
326 type = SUMOXMLDefinitions::TrafficLightTypes.get(typeS);
327 } else {
328 WRITE_ERROR("Unknown traffic light type '" + typeS + "' for node '" + currentNode->getID() + "'.");
329 return;
330 }
331 if (tlID != "" && tlc.getPrograms(tlID).size() > 0) {
332 // we already have definitions for this tlID
333 const std::map<std::string, NBTrafficLightDefinition*>& programs = tlc.getPrograms(tlID);
334 std::map<std::string, NBTrafficLightDefinition*>::const_iterator it;
335 for (it = programs.begin(); it != programs.end(); it++) {
336 if (it->second->getType() != type) {
337 WRITE_ERROR("Mismatched traffic light type '" + typeS + "' for tl '" + tlID + "'.");
338 ok = false;
339 } else {
340 tlDefs.insert(it->second);
341 it->second->addNode(currentNode);
342 }
343 }
344 } else {
345 // we need to add a new defition
346 tlID = (tlID == "" ? currentNode->getID() : tlID);
347 NBTrafficLightDefinition* tlDef = new NBOwnTLDef(tlID, currentNode, 0, type);
348 if (!tlc.insert(tlDef)) {
349 // actually, nothing should fail here
350 delete tlDef;
351 throw ProcessError("Could not allocate tls '" + currentNode->getID() + "'.");
352 }
353 tlDefs.insert(tlDef);
354 }
355 // process inner edges which shall be controlled
356 const std::vector<std::string>& controlledInner = attrs.getOptStringVector(SUMO_ATTR_CONTROLLED_INNER, nullptr, ok);
357 if (controlledInner.size() != 0) {
358 for (std::set<NBTrafficLightDefinition*>::iterator it = tlDefs.begin(); it != tlDefs.end(); it++) {
359 (*it)->addControlledInnerEdges(controlledInner);
360 }
361 }
362 }
363
364
365
366 /****************************************************************************/
367
368