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