SUMO - Simulation of Urban MObility
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
NIImporter_OpenStreetMap.cpp
Go to the documentation of this file.
1 /****************************************************************************/
10 // Importer for networks stored in OpenStreetMap format
11 /****************************************************************************/
12 // SUMO, Simulation of Urban MObility; see http://sumo-sim.org/
13 // Copyright (C) 2001-2013 DLR (http://www.dlr.de/) and contributors
14 /****************************************************************************/
15 //
16 // This file is part of SUMO.
17 // SUMO is free software: you can redistribute it and/or modify
18 // it under the terms of the GNU General Public License as published by
19 // the Free Software Foundation, either version 3 of the License, or
20 // (at your option) any later version.
21 //
22 /****************************************************************************/
23 
24 
25 // ===========================================================================
26 // included modules
27 // ===========================================================================
28 #ifdef _MSC_VER
29 #include <windows_config.h>
30 #else
31 #include <config.h>
32 #endif
33 #include <algorithm>
34 #include <set>
35 #include <functional>
36 #include <sstream>
37 #include <limits>
41 #include <utils/common/ToString.h>
45 #include <netbuild/NBEdge.h>
46 #include <netbuild/NBEdgeCont.h>
47 #include <netbuild/NBNode.h>
48 #include <netbuild/NBNodeCont.h>
49 #include <netbuild/NBNetBuilder.h>
50 #include <netbuild/NBOwnTLDef.h>
56 #include <utils/xml/XMLSubSys.h>
57 #include "NILoader.h"
59 
60 #ifdef CHECK_MEMORY_LEAKS
61 #include <foreign/nvwa/debug_new.h>
62 #endif // CHECK_MEMORY_LEAKS
63 
64 // ---------------------------------------------------------------------------
65 // static members
66 // ---------------------------------------------------------------------------
68 
70 
71 // ===========================================================================
72 // Private classes
73 // ===========================================================================
74 
78 public:
79  bool operator()(const Edge* e1, const Edge* e2) const {
80  if (e1->myHighWayType != e2->myHighWayType) {
81  return e1->myHighWayType > e2->myHighWayType;
82  }
83  if (e1->myNoLanes != e2->myNoLanes) {
84  return e1->myNoLanes > e2->myNoLanes;
85  }
86  if (e1->myNoLanesForward != e2->myNoLanesForward) {
87  return e1->myNoLanesForward > e2->myNoLanesForward;
88  }
89  if (e1->myMaxSpeed != e2->myMaxSpeed) {
90  return e1->myMaxSpeed > e2->myMaxSpeed;
91  }
92  if (e1->myIsOneWay != e2->myIsOneWay) {
93  return e1->myIsOneWay > e2->myIsOneWay;
94  }
95  return e1->myCurrentNodes > e2->myCurrentNodes;
96  }
97 };
98 
99 // ===========================================================================
100 // method definitions
101 // ===========================================================================
102 // ---------------------------------------------------------------------------
103 // static methods
104 // ---------------------------------------------------------------------------
106 
107 
108 void
110  NIImporter_OpenStreetMap importer;
111  importer.load(oc, nb);
112 }
113 
114 
116 
117 
119  // delete nodes
120  for (std::set<NIOSMNode*, CompareNodes>::iterator i = myUniqueNodes.begin(); i != myUniqueNodes.end(); i++) {
121  delete *i;
122  }
123  // delete edges
124  for (std::map<SUMOLong, Edge*>::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
125  delete(*i).second;
126  }
127 }
128 
129 
130 void
132  // check whether the option is set (properly)
133  if (!oc.isSet("osm-files")) {
134  return;
135  }
136  // preset types
137  // for highways
138  NBTypeCont& tc = nb.getTypeCont();
139  SUMOReal const WIDTH = NBEdge::UNSPECIFIED_WIDTH;
140  tc.insert("highway.motorway", 3, (SUMOReal)(160. / 3.6), 13, WIDTH, SVC_UNKNOWN, true);
141  tc.insert("highway.motorway_link", 1, (SUMOReal)(80. / 3.6), 12, WIDTH, SVC_UNKNOWN, true);
142  tc.insert("highway.trunk", 2, (SUMOReal)(100. / 3.6), 11, WIDTH); // !!! 130km/h?
143  tc.insert("highway.trunk_link", 1, (SUMOReal)(80. / 3.6), 10, WIDTH);
144  tc.insert("highway.primary", 2, (SUMOReal)(100. / 3.6), 9, WIDTH);
145  tc.insert("highway.primary_link", 1, (SUMOReal)(80. / 3.6), 8, WIDTH);
146  tc.insert("highway.secondary", 2, (SUMOReal)(100. / 3.6), 7, WIDTH);
147  tc.insert("highway.secondary_link", 1, (SUMOReal)(80. / 3.6), 6, WIDTH);
148  tc.insert("highway.tertiary", 1, (SUMOReal)(80. / 3.6), 6, WIDTH);
149  tc.insert("highway.tertiary_link", 1, (SUMOReal)(80. / 3.6), 5, WIDTH);
150  tc.insert("highway.unclassified", 1, (SUMOReal)(80. / 3.6), 5, WIDTH);
151  tc.insert("highway.residential", 1, (SUMOReal)(50. / 3.6), 4, WIDTH); // actually, maybe one lane for parking would be nice...
152  tc.insert("highway.living_street", 1, (SUMOReal)(10. / 3.6), 3, WIDTH);
153  tc.insert("highway.service", 1, (SUMOReal)(20. / 3.6), 2, WIDTH, SVC_DELIVERY);
154  tc.insert("highway.track", 1, (SUMOReal)(20. / 3.6), 1, WIDTH);
155  tc.insert("highway.services", 1, (SUMOReal)(30. / 3.6), 1, WIDTH);
156  tc.insert("highway.unsurfaced", 1, (SUMOReal)(30. / 3.6), 1, WIDTH); // unofficial value, used outside germany
157  tc.insert("highway.footway", 1, (SUMOReal)(30. / 3.6), 1, WIDTH, SVC_PEDESTRIAN);
158  tc.insert("highway.pedestrian", 1, (SUMOReal)(30. / 3.6), 1, WIDTH, SVC_PEDESTRIAN);
159 
160  tc.insert("highway.path", 1, (SUMOReal)(10. / 3.6), 1, WIDTH, SVC_PEDESTRIAN);
161  tc.insert("highway.bridleway", 1, (SUMOReal)(10. / 3.6), 1, WIDTH, SVC_BICYCLE); // no horse stuff
162  tc.insert("highway.cycleway", 1, (SUMOReal)(20. / 3.6), 1, WIDTH, SVC_BICYCLE);
163  tc.insert("highway.footway", 1, (SUMOReal)(10. / 3.6), 1, WIDTH, SVC_PEDESTRIAN);
164  tc.insert("highway.step", 1, (SUMOReal)(5. / 3.6), 1, WIDTH, SVC_PEDESTRIAN); // additional
165  tc.insert("highway.steps", 1, (SUMOReal)(5. / 3.6), 1, WIDTH, SVC_PEDESTRIAN); // :-) do not run too fast
166  tc.insert("highway.stairs", 1, (SUMOReal)(5. / 3.6), 1, WIDTH, SVC_PEDESTRIAN); // additional
167  tc.insert("highway.bus_guideway", 1, (SUMOReal)(30. / 3.6), 1, WIDTH, SVC_BUS);
168  tc.insert("highway.raceway", 2, (SUMOReal)(300. / 3.6), 14, WIDTH, SVC_VIP);
169  tc.insert("highway.ford", 1, (SUMOReal)(10. / 3.6), 1, WIDTH, SVC_PUBLIC_ARMY);
170 
171  // for railways
172  const bool oneWay = OptionsCont::getOptions().getBool("osm.railway.oneway-default");
173  tc.insert("railway.rail", 1, (SUMOReal)(300. / 3.6), 15, WIDTH, SVC_RAIL_FAST, oneWay);
174  tc.insert("railway.tram", 1, (SUMOReal)(100. / 3.6), 15, WIDTH, SVC_CITYRAIL, oneWay);
175  tc.insert("railway.light_rail", 1, (SUMOReal)(100. / 3.6), 15, WIDTH, SVC_LIGHTRAIL, oneWay);
176  tc.insert("railway.subway", 1, (SUMOReal)(100. / 3.6), 15, WIDTH, SVC_CITYRAIL, oneWay);
177  tc.insert("railway.preserved", 1, (SUMOReal)(100. / 3.6), 15, WIDTH, SVC_LIGHTRAIL, oneWay);
178  tc.insert("railway.monorail", 1, (SUMOReal)(300. / 3.6), 15, WIDTH, SVC_LIGHTRAIL, oneWay); // rail stuff has to be discussed
179 
180 
181  /* Parse file(s)
182  * Each file is parsed twice: first for nodes, second for edges. */
183  std::vector<std::string> files = oc.getStringVector("osm-files");
184  // load nodes, first
185  NodesHandler nodesHandler(myOSMNodes, myUniqueNodes);
186  for (std::vector<std::string>::const_iterator file = files.begin(); file != files.end(); ++file) {
187  // nodes
188  if (!FileHelpers::exists(*file)) {
189  WRITE_ERROR("Could not open osm-file '" + *file + "'.");
190  return;
191  }
192  nodesHandler.setFileName(*file);
193  PROGRESS_BEGIN_MESSAGE("Parsing nodes from osm-file '" + *file + "'");
194  if (!XMLSubSys::runParser(nodesHandler, *file)) {
195  return;
196  }
198  }
199  // load edges, then
200  EdgesHandler edgesHandler(myOSMNodes, myEdges);
201  for (std::vector<std::string>::const_iterator file = files.begin(); file != files.end(); ++file) {
202  // edges
203  edgesHandler.setFileName(*file);
204  PROGRESS_BEGIN_MESSAGE("Parsing edges from osm-file '" + *file + "'");
205  XMLSubSys::runParser(edgesHandler, *file);
207  }
208 
209  /* Remove duplicate edges with the same shape and attributes */
210  if (!OptionsCont::getOptions().getBool("osm.skip-duplicates-check")) {
211  PROGRESS_BEGIN_MESSAGE("Removing duplicate edges");
212  if (myEdges.size() > 1) {
213  std::set<const Edge*, CompareEdges> dupsFinder;
214  for (std::map<SUMOLong, Edge*>::iterator it = myEdges.begin(); it != myEdges.end();) {
215  if (dupsFinder.count(it->second) > 0) {
216  WRITE_MESSAGE("Found duplicate edges. Removing " + toString(it->first));
217  delete it->second;
218  myEdges.erase(it++);
219  } else {
220  dupsFinder.insert(it->second);
221  it++;
222  }
223  }
224  }
226  }
227 
228  /* Mark which nodes are used (by edges or traffic lights).
229  * This is necessary to detect which OpenStreetMap nodes are for
230  * geometry only */
231  std::map<SUMOLong, int> nodeUsage;
232  // Mark which nodes are used by edges (begin and end)
233  for (std::map<SUMOLong, Edge*>::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
234  Edge* e = (*i).second;
235  assert(e->myCurrentIsRoad);
236  for (std::vector<SUMOLong>::const_iterator j = e->myCurrentNodes.begin(); j != e->myCurrentNodes.end(); ++j) {
237  if (nodeUsage.find(*j) == nodeUsage.end()) {
238  nodeUsage[*j] = 0;
239  }
240  nodeUsage[*j] = nodeUsage[*j] + 1;
241  }
242  }
243  // Mark which nodes are used by traffic lights
244  for (std::map<SUMOLong, NIOSMNode*>::const_iterator nodesIt = myOSMNodes.begin(); nodesIt != myOSMNodes.end(); ++nodesIt) {
245  if (nodesIt->second->tlsControlled) {
246  // If the key is not found in the map, the value is automatically
247  // initialized with 0.
248  nodeUsage[nodesIt->first] += 1;
249  }
250  }
251  /* Instantiate edges
252  * Only those nodes in the middle of an edge which are used by more than
253  * one edge are instantiated. Other nodes are considered as geometry nodes. */
254  NBNodeCont& nc = nb.getNodeCont();
256  for (std::map<SUMOLong, Edge*>::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
257  Edge* e = (*i).second;
258  assert(e->myCurrentIsRoad);
259  if (e->myCurrentNodes.size() < 2) {
260  WRITE_WARNING("Discarding way '" + toString(e->id) + "' because it has only " +
261  toString(e->myCurrentNodes.size()) + " node(s)");
262  continue;
263  }
264  // build nodes;
265  // - the from- and to-nodes must be built in any case
266  // - the in-between nodes are only built if more than one edge references them
267  NBNode* currentFrom = insertNodeChecking(*e->myCurrentNodes.begin(), nc, tlsc);
268  NBNode* last = insertNodeChecking(*(e->myCurrentNodes.end() - 1), nc, tlsc);
269  int running = 0;
270  std::vector<SUMOLong> passed;
271  for (std::vector<SUMOLong>::iterator j = e->myCurrentNodes.begin(); j != e->myCurrentNodes.end(); ++j) {
272  passed.push_back(*j);
273  if (nodeUsage[*j] > 1 && j != e->myCurrentNodes.end() - 1 && j != e->myCurrentNodes.begin()) {
274  NBNode* currentTo = insertNodeChecking(*j, nc, tlsc);
275  running = insertEdge(e, running, currentFrom, currentTo, passed, nb);
276  currentFrom = currentTo;
277  passed.clear();
278  }
279  }
280  if (running == 0) {
281  running = -1;
282  }
283  insertEdge(e, running, currentFrom, last, passed, nb);
284  }
285  // load relations (after edges are built since we want to apply
286  // turn-restrictions directly to NBEdges)
287  RelationHandler relationHandler(myOSMNodes, myEdges);
288  for (std::vector<std::string>::const_iterator file = files.begin(); file != files.end(); ++file) {
289  // relations
290  relationHandler.setFileName(*file);
291  PROGRESS_BEGIN_MESSAGE("Parsing relations from osm-file '" + *file + "'");
292  XMLSubSys::runParser(relationHandler, *file);
294  }
295 }
296 
297 
298 NBNode*
300  NBNode* node = nc.retrieve(toString(id));
301  if (node == 0) {
302  NIOSMNode* n = myOSMNodes.find(id)->second;
303  Position pos(n->lon, n->lat);
304  if (!NBNetBuilder::transformCoordinates(pos, true)) {
305  WRITE_ERROR("Unable to project coordinates for node " + toString(id) + ".");
306  return 0;
307  }
308  node = new NBNode(toString(id), pos);
309  if (!nc.insert(node)) {
310  WRITE_ERROR("Could not insert node '" + toString(id) + "').");
311  delete node;
312  return 0;
313  }
314  n->node = node;
315  if (n->tlsControlled) {
316  // ok, this node is a traffic light node where no other nodes
317  // participate
318  // @note: The OSM-community has not settled on a schema for differentiating between fixed and actuated lights
320  NBOwnTLDef* tlDef = new NBOwnTLDef(toString(id), node, 0, type);
321  if (!tlsc.insert(tlDef)) {
322  // actually, nothing should fail here
323  delete tlDef;
324  throw ProcessError("Could not allocate tls '" + toString(id) + "'.");
325  }
326  }
327  }
328  return node;
329 }
330 
331 
332 int
334  const std::vector<SUMOLong>& passed, NBNetBuilder& nb) {
335  NBNodeCont& nc = nb.getNodeCont();
336  NBEdgeCont& ec = nb.getEdgeCont();
337  NBTypeCont& tc = nb.getTypeCont();
339  // patch the id
340  std::string id = toString(e->id);
341  if (from == 0 || to == 0) {
342  WRITE_ERROR("Discarding edge " + id + " because the nodes could not be built.");
343  return index;
344  }
345  if (index >= 0) {
346  id = id + "#" + toString(index);
347  } else {
348  index = 0;
349  }
350  if (from == to) {
351  // in the special case of a looped way split again using passed
352  assert(passed.size() >= 2);
353  std::vector<SUMOLong> geom(passed);
354  geom.pop_back(); // remove to-node
355  NBNode* intermediate = insertNodeChecking(geom.back(), nc, tlsc);
356  index = insertEdge(e, index, from, intermediate, geom, nb);
357  geom.clear();
358  return insertEdge(e, index, intermediate, to, geom, nb);
359  }
360  const int newIndex = index + 1;
361 
362  // convert the shape
363  PositionVector shape;
364  for (std::vector<SUMOLong>::const_iterator i = passed.begin(); i != passed.end(); ++i) {
365  NIOSMNode* n = myOSMNodes.find(*i)->second;
366  Position pos(n->lon, n->lat);
367  if (!NBNetBuilder::transformCoordinates(pos, true)) {
368  WRITE_ERROR("Unable to project coordinates for edge " + id + ".");
369  }
370  shape.push_back_noDoublePos(pos);
371  }
372 
373  std::string type = e->myHighWayType;
374  if (!tc.knows(type)) {
375  if (type.find(compoundTypeSeparator) != std::string::npos) {
376  // this edge has a combination type which does not yet exist in the TypeContainer
378  std::set<std::string> types;
379  while (tok.hasNext()) {
380  std::string t = tok.next();
381  if (tc.knows(t)) {
382  types.insert(t);
383  } else {
384  WRITE_WARNING("Discarding unknown compound \"" + t + "\" for edge " + id + " with type \"" + type + "\".");
385  }
386  }
387  switch (types.size()) {
388  case 0:
389  WRITE_WARNING("Discarding edge " + id + " with type unknown compound type \"" + type + "\".");
390  return newIndex;
391  break;
392  case 1: {
393  type = *(types.begin());
394  break;
395  }
396  default:
397  // build a new type by merging all values
398  int numLanes = 0;
399  SUMOReal maxSpeed = 0;
400  int prio = 0;
402  bool defaultIsOneWay = false;
403  for (std::set<std::string>::iterator it = types.begin(); it != types.end(); it++) {
404  numLanes = MAX2(numLanes, tc.getNumLanes(*it));
405  maxSpeed = MAX2(maxSpeed, tc.getSpeed(*it));
406  prio = MAX2(prio, tc.getPriority(*it));
407  defaultIsOneWay &= tc.getIsOneWay(*it);
408  }
409  WRITE_MESSAGE("Adding new compound type \"" + type + "\" for edge " + id + ".");
410  // @todo use the propper bitsets instead of SVC_UNKNOWN (see #675)
411  tc.insert(type, numLanes, maxSpeed, prio, width, SVC_UNKNOWN, defaultIsOneWay);
412  }
413  } else {
414  // we do not know the type -> something else, ignore
415  //WRITE_WARNING("Discarding edge " + id + " with unknown type \"" + type + "\".");
416  return newIndex;
417  }
418  }
419 
420  // otherwise it is not an edge and will be ignored
421  bool ok = true;
422  int numLanesForward = tc.getNumLanes(type);
423  int numLanesBackward = tc.getNumLanes(type);
424  SUMOReal speed = tc.getSpeed(type);
425  bool defaultsToOneWay = tc.getIsOneWay(type);
426  SVCPermissions permissions = tc.getPermissions(type);
427  // check directions
428  bool addForward = true;
429  bool addBackward = true;
430  if (e->myIsOneWay == "true" || e->myIsOneWay == "yes" || e->myIsOneWay == "1" || (defaultsToOneWay && e->myIsOneWay != "no" && e->myIsOneWay != "false" && e->myIsOneWay != "0")) {
431  addBackward = false;
432  }
433  if (e->myIsOneWay == "-1" || e->myIsOneWay == "reverse") {
434  // one-way in reversed direction of way
435  addForward = false;
436  addBackward = true;
437  }
438  if (e->myIsOneWay != "" && e->myIsOneWay != "false" && e->myIsOneWay != "no" && e->myIsOneWay != "true" && e->myIsOneWay != "yes" && e->myIsOneWay != "-1" && e->myIsOneWay != "1" && e->myIsOneWay != "reverse") {
439  WRITE_WARNING("New value for oneway found: " + e->myIsOneWay);
440  }
441  // if we had been able to extract the number of lanes, override the highway type default
442  if (e->myNoLanes > 0) {
443  if (addForward && !addBackward) {
444  numLanesForward = e->myNoLanes;
445  } else if (!addForward && addBackward) {
446  numLanesBackward = e->myNoLanes;
447  } else {
448  if (e->myNoLanesForward > 0) {
449  numLanesForward = e->myNoLanesForward;
450  } else if (e->myNoLanesForward < 0) {
451  numLanesForward = e->myNoLanes + e->myNoLanesForward;
452  } else {
453  numLanesForward = (int)std::ceil(e->myNoLanes / 2.0);
454  }
455  numLanesBackward = e->myNoLanes - numLanesForward;
456  // sometimes ways are tagged according to their physical width of a single
457  // lane but they are intended for traffic in both directions
458  numLanesForward = MAX2(1, numLanesForward);
459  numLanesBackward = MAX2(1, numLanesBackward);
460  }
461  } else if (e->myNoLanes == 0) {
462  WRITE_WARNING("Skipping edge '" + id + "' because it has zero lanes.");
463  ok = false;
464  }
465  // if we had been able to extract the maximum speed, override the type's default
466  if (e->myMaxSpeed != MAXSPEED_UNGIVEN) {
467  speed = (SUMOReal)(e->myMaxSpeed / 3.6);
468  }
469  if (speed <= 0) {
470  WRITE_WARNING("Skipping edge '" + id + "' because it has speed " + toString(speed));
471  ok = false;
472  }
473  if (ok) {
475  if (addForward) {
476  assert(numLanesForward > 0);
477  NBEdge* nbe = new NBEdge(StringUtils::escapeXML(id), from, to, type, speed, numLanesForward, tc.getPriority(type),
479  nbe->setPermissions(permissions);
480  if (!ec.insert(nbe)) {
481  delete nbe;
482  throw ProcessError("Could not add edge '" + id + "'.");
483  }
484  id = "-" + id;
485  }
486  if (addBackward) {
487  assert(numLanesBackward > 0);
488  NBEdge* nbe = new NBEdge(StringUtils::escapeXML(id), to, from, type, speed, numLanesBackward, tc.getPriority(type),
490  nbe->setPermissions(permissions);
491  if (!ec.insert(nbe)) {
492  delete nbe;
493  throw ProcessError("Could not add edge " + id + "'.");
494  }
495  }
496  }
497  return newIndex;
498 }
499 
500 
501 // ---------------------------------------------------------------------------
502 // definitions of NIImporter_OpenStreetMap::NodesHandler-methods
503 // ---------------------------------------------------------------------------
505  std::map<SUMOLong, NIOSMNode*>& toFill,
506  std::set<NIOSMNode*, CompareNodes>& uniqueNodes) :
507  SUMOSAXHandler("osm - file"),
508  myToFill(toFill),
509  myLastNodeID(-1),
510  myIsInValidNodeTag(false),
511  myHierarchyLevel(0),
512  myUniqueNodes(uniqueNodes) {
513 }
514 
515 
517 
518 
519 void
521  ++myHierarchyLevel;
522  if (element == SUMO_TAG_NODE) {
523  bool ok = true;
524  if (myHierarchyLevel != 2) {
525  WRITE_ERROR("Node element on wrong XML hierarchy level (id='" + toString(attrs.get<SUMOLong>(SUMO_ATTR_ID, 0, ok)) + "', level='" + toString(myHierarchyLevel) + "').");
526  return;
527  }
528  SUMOLong id = attrs.get<SUMOLong>(SUMO_ATTR_ID, 0, ok);
529  std::string action = attrs.hasAttribute("action") ? attrs.getStringSecure("action", "") : "";
530  if (action == "delete") {
531  return;
532  }
533  if (!ok) {
534  return;
535  }
536  myLastNodeID = -1;
537  if (myToFill.find(id) == myToFill.end()) {
538  myLastNodeID = id;
539  // assume we are loading multiple files...
540  // ... so we won't report duplicate nodes
541  bool ok = true;
542  double tlat, tlon;
543  std::istringstream lon(attrs.get<std::string>(SUMO_ATTR_LON, toString(id).c_str(), ok));
544  if (!ok) {
545  return;
546  }
547  lon >> tlon;
548  if (lon.fail()) {
549  WRITE_ERROR("Node's '" + toString(id) + "' lon information is not numeric.");
550  return;
551  }
552  std::istringstream lat(attrs.get<std::string>(SUMO_ATTR_LAT, toString(id).c_str(), ok));
553  if (!ok) {
554  return;
555  }
556  lat >> tlat;
557  if (lat.fail()) {
558  WRITE_ERROR("Node's '" + toString(id) + "' lat information is not numeric.");
559  return;
560  }
561  NIOSMNode* toAdd = new NIOSMNode(id, tlon, tlat);
562  myIsInValidNodeTag = true;
563 
564  std::set<NIOSMNode*, CompareNodes>::iterator similarNode = myUniqueNodes.find(toAdd);
565  if (similarNode == myUniqueNodes.end()) {
566  myUniqueNodes.insert(toAdd);
567  } else {
568  delete toAdd;
569  toAdd = *similarNode;
570  WRITE_MESSAGE("Found duplicate nodes. Substituting " + toString(id) + " with " + toString(toAdd->id));
571  }
572  myToFill[id] = toAdd;
573  }
574  }
575  if (element == SUMO_TAG_TAG && myIsInValidNodeTag) {
576  if (myHierarchyLevel != 3) {
577  WRITE_ERROR("Tag element on wrong XML hierarchy level.");
578  return;
579  }
580  bool ok = true;
581  std::string key = attrs.get<std::string>(SUMO_ATTR_K, toString(myLastNodeID).c_str(), ok);
582  std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myLastNodeID).c_str(), ok, false);
583  if (!ok) {
584  return;
585  }
586  if (key == "highway" && value.find("traffic_signal") != std::string::npos) {
587  myToFill[myLastNodeID]->tlsControlled = true;
588  }
589  }
590 }
591 
592 
593 void
595  if (element == SUMO_TAG_NODE && myHierarchyLevel == 2) {
596  myLastNodeID = -1;
597  myIsInValidNodeTag = false;
598  }
599  --myHierarchyLevel;
600 }
601 
602 
603 // ---------------------------------------------------------------------------
604 // definitions of NIImporter_OpenStreetMap::EdgesHandler-methods
605 // ---------------------------------------------------------------------------
607  const std::map<SUMOLong, NIOSMNode*>& osmNodes,
608  std::map<SUMOLong, Edge*>& toFill) :
609  SUMOSAXHandler("osm - file"),
610  myOSMNodes(osmNodes),
611  myEdgeMap(toFill) {
612  mySpeedMap["signals"] = MAXSPEED_UNGIVEN;
613  mySpeedMap["none"] = 300.;
614  mySpeedMap["no"] = 300.;
615  mySpeedMap["walk"] = 5.;
616  mySpeedMap["DE:rural"] = 100.;
617  mySpeedMap["DE:urban"] = 50.;
618  mySpeedMap["DE:living_street"] = 10.;
619 
620 }
621 
622 
624 }
625 
626 
627 void
629  const SUMOSAXAttributes& attrs) {
630  myParentElements.push_back(element);
631  // parse "way" elements
632  if (element == SUMO_TAG_WAY) {
633  bool ok = true;
634  SUMOLong id = attrs.get<SUMOLong>(SUMO_ATTR_ID, 0, ok);
635  std::string action = attrs.hasAttribute("action") ? attrs.getStringSecure("action", "") : "";
636  if (action == "delete") {
637  myCurrentEdge = 0;
638  return;
639  }
640  if (!ok) {
641  myCurrentEdge = 0;
642  return;
643  }
644  myCurrentEdge = new Edge(id);
645  }
646  // parse "nd" (node) elements
647  if (element == SUMO_TAG_ND) {
648  bool ok = true;
649  SUMOLong ref = attrs.get<SUMOLong>(SUMO_ATTR_REF, 0, ok);
650  if (ok) {
651  std::map<SUMOLong, NIOSMNode*>::const_iterator node = myOSMNodes.find(ref);
652  if (node == myOSMNodes.end()) {
653  WRITE_WARNING("The referenced geometry information (ref='" + toString(ref) + "') is not known");
654  return;
655  } else {
656  ref = node->second->id; // node may have been substituted
657  if (myCurrentEdge->myCurrentNodes.size() == 0 ||
658  myCurrentEdge->myCurrentNodes.back() != ref) { // avoid consecutive duplicates
659  myCurrentEdge->myCurrentNodes.push_back(ref);
660  }
661  }
662  }
663  }
664  // parse values
665  if (element == SUMO_TAG_TAG && myParentElements.size() > 2 && myParentElements[myParentElements.size() - 2] == SUMO_TAG_WAY) {
666  if (myCurrentEdge == 0) {
667  return;
668  }
669  bool ok = true;
670  std::string key = attrs.get<std::string>(SUMO_ATTR_K, toString(myCurrentEdge->id).c_str(), ok);
671  std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentEdge->id).c_str(), ok, false);
672  if (!ok) {
673  return;
674  }
675  if (key == "highway" || key == "railway") {
676  if (myCurrentEdge->myHighWayType != "") {
677  // osm-ways may be used by more than one mode (eg railway.tram + highway.residential. this is relevant for multimodal traffic)
678  // we create a new type for this kind of situation which must then be resolved in insertEdge()
679  myCurrentEdge->myHighWayType = myCurrentEdge->myHighWayType + compoundTypeSeparator + key + "." + value;
680  } else {
681  myCurrentEdge->myHighWayType = key + "." + value;
682  }
683  myCurrentEdge->myCurrentIsRoad = true;
684  } else if (key == "lanes") {
685  try {
686  myCurrentEdge->myNoLanes = TplConvert::_2int(value.c_str());
687  } catch (NumberFormatException&) {
688  // might be a list of values
689  StringTokenizer st(value, ";", true);
690  std::vector<std::string> list = st.getVector();
691  if (list.size() >= 2) {
692  int minLanes = std::numeric_limits<int>::max();
693  try {
694  for (std::vector<std::string>::iterator i = list.begin(); i != list.end(); ++i) {
695  int numLanes = TplConvert::_2int(StringUtils::prune(*i).c_str());
696  minLanes = MIN2(minLanes, numLanes);
697  }
698  myCurrentEdge->myNoLanes = minLanes;
699  WRITE_WARNING("Using minimum lane number from list (" + value + ") for edge '" + toString(myCurrentEdge->id) + "'.");
700  } catch (NumberFormatException&) {
701  WRITE_WARNING("Value of key '" + key + "' is not numeric ('" + value + "') in edge '" +
702  toString(myCurrentEdge->id) + "'.");
703  }
704  }
705  }
706  } else if (key == "lanes:forward") {
707  try {
708  myCurrentEdge->myNoLanesForward = TplConvert::_2int(value.c_str());
709  } catch (NumberFormatException&) {
710  WRITE_WARNING("Value of key '" + key + "' is not numeric ('" + value + "') in edge '" +
711  toString(myCurrentEdge->id) + "'.");
712  }
713  } else if (key == "lanes:backward") {
714  try {
715  // denote backwards count with a negative sign
716  myCurrentEdge->myNoLanesForward = -TplConvert::_2int(value.c_str());
717  } catch (NumberFormatException&) {
718  WRITE_WARNING("Value of key '" + key + "' is not numeric ('" + value + "') in edge '" +
719  toString(myCurrentEdge->id) + "'.");
720  }
721  } else if (key == "maxspeed") {
722  if (mySpeedMap.find(value) != mySpeedMap.end()) {
723  myCurrentEdge->myMaxSpeed = mySpeedMap[value];
724  } else {
725  SUMOReal conversion = 1; // OSM default is km/h
726  if (StringUtils::to_lower_case(value).find("km/h") != std::string::npos) {
727  value = StringUtils::prune(value.substr(0, value.find_first_not_of("0123456789")));
728  } else if (StringUtils::to_lower_case(value).find("mph") != std::string::npos) {
729  value = StringUtils::prune(value.substr(0, value.find_first_not_of("0123456789")));
730  conversion = 1.609344; // kilometers per mile
731  }
732  try {
733  myCurrentEdge->myMaxSpeed = TplConvert::_2SUMOReal(value.c_str()) * conversion;
734  } catch (NumberFormatException&) {
735  WRITE_WARNING("Value of key '" + key + "' is not numeric ('" + value + "') in edge '" +
736  toString(myCurrentEdge->id) + "'.");
737  }
738  }
739  } else if (key == "junction") {
740  if ((value == "roundabout") && (myCurrentEdge->myIsOneWay == "")) {
741  myCurrentEdge->myIsOneWay = "yes";
742  }
743  } else if (key == "oneway") {
744  myCurrentEdge->myIsOneWay = value;
745  } else if (key == "name") {
746  myCurrentEdge->streetName = value;
747  } else if (key == "tracks") {
748  if (TplConvert::_2int(value.c_str()) > 1) {
749  myCurrentEdge->myIsOneWay = "false";
750  } else {
751  myCurrentEdge->myIsOneWay = "true";
752  }
753  }
754  }
755 }
756 
757 
758 void
760  myParentElements.pop_back();
761  if (element == SUMO_TAG_WAY) {
762  if (myCurrentEdge != 0 && myCurrentEdge->myCurrentIsRoad) {
763  myEdgeMap[myCurrentEdge->id] = myCurrentEdge;
764  } else {
765  delete myCurrentEdge;
766  }
767  myCurrentEdge = 0;
768  }
769 }
770 
771 
772 // ---------------------------------------------------------------------------
773 // definitions of NIImporter_OpenStreetMap::RelationHandler-methods
774 // ---------------------------------------------------------------------------
776  const std::map<SUMOLong, NIOSMNode*>& osmNodes,
777  const std::map<SUMOLong, Edge*>& osmEdges) :
778  SUMOSAXHandler("osm - file"),
779  myOSMNodes(osmNodes),
780  myOSMEdges(osmEdges) {
781  resetValues();
782 }
783 
784 
786 }
787 
788 void
790  myCurrentRelation = INVALID_ID;
791  myIsRestriction = false;
792  myFromWay = INVALID_ID;
793  myToWay = INVALID_ID;
794  myViaNode = INVALID_ID;
795  myViaWay = INVALID_ID;
796  myRestrictionType = RESTRICTION_UNKNOWN;
797 }
798 
799 void
801  const SUMOSAXAttributes& attrs) {
802  myParentElements.push_back(element);
803  // parse "way" elements
804  if (element == SUMO_TAG_RELATION) {
805  bool ok = true;
806  myCurrentRelation = attrs.get<SUMOLong>(SUMO_ATTR_ID, 0, ok);
807  std::string action = attrs.hasAttribute("action") ? attrs.getStringSecure("action", "") : "";
808  if (action == "delete" || !ok) {
809  myCurrentRelation = INVALID_ID;
810  }
811  return;
812  } else if (myCurrentRelation == INVALID_ID) {
813  return;
814  }
815  // parse member elements
816  if (element == SUMO_TAG_MEMBER) {
817  bool ok = true;
818  std::string role = attrs.hasAttribute("role") ? attrs.getStringSecure("role", "") : "";
819  SUMOLong ref = attrs.get<SUMOLong>(SUMO_ATTR_REF, 0, ok);
820  if (role == "via") {
821  // u-turns for divided ways may be given with 2 via-nodes or 1 via-way
822  std::string memberType = attrs.get<std::string>(SUMO_ATTR_TYPE, 0, ok);
823  if (memberType == "way" && checkEdgeRef(ref)) {
824  myViaWay = ref;
825  } else if (memberType == "node") {
826  if (myOSMNodes.find(ref) != myOSMNodes.end()) {
827  myViaNode = ref;
828  } else {
829  WRITE_WARNING("No node found for reference '" + toString(ref) + "' in relation '" + toString(myCurrentRelation) + "'");
830  }
831  }
832  } else if (role == "from" && checkEdgeRef(ref)) {
833  myFromWay = ref;
834  } else if (role == "to" && checkEdgeRef(ref)) {
835  myToWay = ref;
836  }
837  return;
838  }
839  // parse values
840  if (element == SUMO_TAG_TAG) {
841  bool ok = true;
842  std::string key = attrs.get<std::string>(SUMO_ATTR_K, toString(myCurrentRelation).c_str(), ok);
843  std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);
844  if (!ok) {
845  return;
846  }
847  if (key == "type" && value == "restriction") {
848  myIsRestriction = true;
849  return;
850  }
851  if (key == "restriction") {
852  if (value.substr(0, 5) == "only_") {
853  myRestrictionType = RESTRICTION_ONLY;
854  } else if (value.substr(0, 3) == "no_") {
855  myRestrictionType = RESTRICTION_NO;
856  } else {
857  WRITE_WARNING("Found unknown restriction type '" + value + "' in relation '" + toString(myCurrentRelation) + "'");
858  }
859  return;
860  }
861  }
862 }
863 
864 
865 bool
867  if (myOSMEdges.find(ref) != myOSMEdges.end()) {
868  return true;
869  } else {
870  WRITE_WARNING("No way found for reference '" + toString(ref) + "' in relation '" + toString(myCurrentRelation) + "'");
871  return false;
872  }
873 }
874 
875 
876 void
878  myParentElements.pop_back();
879  if (element == SUMO_TAG_RELATION) {
880  if (myIsRestriction) {
881  assert(myCurrentRelation != INVALID_ID);
882  bool ok = true;
883  if (myRestrictionType == RESTRICTION_UNKNOWN) {
884  WRITE_WARNING("Ignoring restriction relation '" + toString(myCurrentRelation) + "' with unknown type.");
885  ok = false;
886  }
887  if (myFromWay == INVALID_ID) {
888  WRITE_WARNING("Ignoring restriction relation '" + toString(myCurrentRelation) + "' with unknown from-way.");
889  ok = false;
890  }
891  if (myToWay == INVALID_ID) {
892  WRITE_WARNING("Ignoring restriction relation '" + toString(myCurrentRelation) + "' with unknown to-way.");
893  ok = false;
894  }
895  if (myViaNode == INVALID_ID && myViaWay == INVALID_ID) {
896  WRITE_WARNING("Ignoring restriction relation '" + toString(myCurrentRelation) + "' with unknown via.");
897  ok = false;
898  }
899  if (ok && !applyRestriction()) {
900  WRITE_WARNING("Ignoring restriction relation '" + toString(myCurrentRelation) + "'.");
901  }
902  }
903  // other relations might use similar subelements so reset in any case
904  resetValues();
905  }
906 }
907 
908 
909 bool
911  // since OSM ways are bidirectional we need the via to figure out which direction was meant
912  if (myViaNode != INVALID_ID) {
913  NBNode* viaNode = myOSMNodes.find(myViaNode)->second->node;
914  if (viaNode == 0) {
915  WRITE_WARNING("Via-node '" + toString(myViaNode) + "' was not instantiated");
916  return false;
917  }
918  NBEdge* from = findEdgeRef(myFromWay, viaNode->getIncomingEdges());
919  NBEdge* to = findEdgeRef(myToWay, viaNode->getOutgoingEdges());
920  if (from == 0) {
921  WRITE_WARNING("from-edge of restriction relation could not be determined");
922  return false;
923  }
924  if (to == 0) {
925  WRITE_WARNING("to-edge of restriction relation could not be determined");
926  return false;
927  }
928  if (myRestrictionType == RESTRICTION_ONLY) {
929  from->addEdge2EdgeConnection(to);
930  } else {
931  from->removeFromConnections(to, -1, -1, true);
932  }
933  } else {
934  // XXX interpreting via-ways or via-node lists not yet implemented
935  WRITE_WARNING("direction of restriction relation could not be determined");
936  return false;
937  }
938  return true;
939 }
940 
941 
942 NBEdge*
943 NIImporter_OpenStreetMap::RelationHandler::findEdgeRef(SUMOLong wayRef, const std::vector<NBEdge*>& candidates) const {
944  const std::string prefix = toString(wayRef);
945  const std::string backPrefix = "-" + prefix;
946  NBEdge* result = 0;
947  int found = 0;
948  for (EdgeVector::const_iterator it = candidates.begin(); it != candidates.end(); ++it) {
949  if (((*it)->getID().substr(0, prefix.size()) == prefix) ||
950  ((*it)->getID().substr(0, backPrefix.size()) == backPrefix)) {
951  result = *it;
952  found++;
953  }
954  }
955  if (found > 1) {
956  WRITE_WARNING("Ambigous way reference '" + prefix + "' in restriction relation");
957  result = 0;
958  }
959  return result;
960 }
961 
962 
963 /****************************************************************************/
964