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.sourceforge.net/
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  tc.insert("railway.rail", 1, (SUMOReal)(300. / 3.6), 15, WIDTH, SVC_RAIL_FAST, true);
173  tc.insert("railway.tram", 1, (SUMOReal)(100. / 3.6), 15, WIDTH, SVC_CITYRAIL, true);
174  tc.insert("railway.light_rail", 1, (SUMOReal)(100. / 3.6), 15, WIDTH, SVC_LIGHTRAIL, true);
175  tc.insert("railway.subway", 1, (SUMOReal)(100. / 3.6), 15, WIDTH, SVC_CITYRAIL, true);
176  tc.insert("railway.preserved", 1, (SUMOReal)(100. / 3.6), 15, WIDTH, SVC_LIGHTRAIL, true);
177  tc.insert("railway.monorail", 1, (SUMOReal)(300. / 3.6), 15, WIDTH, SVC_LIGHTRAIL, true); // rail stuff has to be discussed
178 
179 
180  /* Parse file(s)
181  * Each file is parsed twice: first for nodes, second for edges. */
182  std::vector<std::string> files = oc.getStringVector("osm-files");
183  // load nodes, first
184  NodesHandler nodesHandler(myOSMNodes, myUniqueNodes);
185  for (std::vector<std::string>::const_iterator file = files.begin(); file != files.end(); ++file) {
186  // nodes
187  if (!FileHelpers::exists(*file)) {
188  WRITE_ERROR("Could not open osm-file '" + *file + "'.");
189  return;
190  }
191  nodesHandler.setFileName(*file);
192  PROGRESS_BEGIN_MESSAGE("Parsing nodes from osm-file '" + *file + "'");
193  if (!XMLSubSys::runParser(nodesHandler, *file)) {
194  return;
195  }
197  }
198  // load edges, then
199  EdgesHandler edgesHandler(myOSMNodes, myEdges);
200  for (std::vector<std::string>::const_iterator file = files.begin(); file != files.end(); ++file) {
201  // edges
202  edgesHandler.setFileName(*file);
203  PROGRESS_BEGIN_MESSAGE("Parsing edges from osm-file '" + *file + "'");
204  XMLSubSys::runParser(edgesHandler, *file);
206  }
207 
208  /* Remove duplicate edges with the same shape and attributes */
209  if (!OptionsCont::getOptions().getBool("osm.skip-duplicates-check")) {
210  PROGRESS_BEGIN_MESSAGE("Removing duplicate edges");
211  if (myEdges.size() > 1) {
212  std::set<const Edge*, CompareEdges> dupsFinder;
213  for (std::map<SUMOLong, Edge*>::iterator it = myEdges.begin(); it != myEdges.end();) {
214  if (dupsFinder.count(it->second) > 0) {
215  WRITE_MESSAGE("Found duplicate edges. Removing " + toString(it->first));
216  delete it->second;
217  myEdges.erase(it++);
218  } else {
219  dupsFinder.insert(it->second);
220  it++;
221  }
222  }
223  }
225  }
226 
227  /* Mark which nodes are used (by edges or traffic lights).
228  * This is necessary to detect which OpenStreetMap nodes are for
229  * geometry only */
230  std::map<SUMOLong, int> nodeUsage;
231  // Mark which nodes are used by edges (begin and end)
232  for (std::map<SUMOLong, Edge*>::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
233  Edge* e = (*i).second;
234  assert(e->myCurrentIsRoad);
235  for (std::vector<SUMOLong>::const_iterator j = e->myCurrentNodes.begin(); j != e->myCurrentNodes.end(); ++j) {
236  if (nodeUsage.find(*j) == nodeUsage.end()) {
237  nodeUsage[*j] = 0;
238  }
239  nodeUsage[*j] = nodeUsage[*j] + 1;
240  }
241  }
242  // Mark which nodes are used by traffic lights
243  for (std::map<SUMOLong, NIOSMNode*>::const_iterator nodesIt = myOSMNodes.begin(); nodesIt != myOSMNodes.end(); ++nodesIt) {
244  if (nodesIt->second->tlsControlled) {
245  // If the key is not found in the map, the value is automatically
246  // initialized with 0.
247  nodeUsage[nodesIt->first] += 1;
248  }
249  }
250  /* Instantiate edges
251  * Only those nodes in the middle of an edge which are used by more than
252  * one edge are instantiated. Other nodes are considered as geometry nodes. */
253  NBNodeCont& nc = nb.getNodeCont();
255  for (std::map<SUMOLong, Edge*>::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
256  Edge* e = (*i).second;
257  assert(e->myCurrentIsRoad);
258  if (e->myCurrentNodes.size() < 2) {
259  WRITE_WARNING("Discarding way '" + toString(e->id) + "' because it has only " +
260  toString(e->myCurrentNodes.size()) + " node(s)");
261  continue;
262  }
263  // build nodes;
264  // - the from- and to-nodes must be built in any case
265  // - the in-between nodes are only built if more than one edge references them
266  NBNode* currentFrom = insertNodeChecking(*e->myCurrentNodes.begin(), nc, tlsc);
267  NBNode* last = insertNodeChecking(*(e->myCurrentNodes.end() - 1), nc, tlsc);
268  int running = 0;
269  std::vector<SUMOLong> passed;
270  for (std::vector<SUMOLong>::iterator j = e->myCurrentNodes.begin(); j != e->myCurrentNodes.end(); ++j) {
271  passed.push_back(*j);
272  if (nodeUsage[*j] > 1 && j != e->myCurrentNodes.end() - 1 && j != e->myCurrentNodes.begin()) {
273  NBNode* currentTo = insertNodeChecking(*j, nc, tlsc);
274  running = insertEdge(e, running, currentFrom, currentTo, passed, nb);
275  currentFrom = currentTo;
276  passed.clear();
277  }
278  }
279  if (running == 0) {
280  running = -1;
281  }
282  insertEdge(e, running, currentFrom, last, passed, nb);
283  }
284  // load relations (after edges are built since we want to apply
285  // turn-restrictions directly to NBEdges)
286  RelationHandler relationHandler(myOSMNodes, myEdges);
287  for (std::vector<std::string>::const_iterator file = files.begin(); file != files.end(); ++file) {
288  // relations
289  relationHandler.setFileName(*file);
290  PROGRESS_BEGIN_MESSAGE("Parsing relations from osm-file '" + *file + "'");
291  XMLSubSys::runParser(relationHandler, *file);
293  }
294 }
295 
296 
297 NBNode*
299  NBNode* node = nc.retrieve(toString(id));
300  if (node == 0) {
301  NIOSMNode* n = myOSMNodes.find(id)->second;
302  Position pos(n->lon, n->lat);
303  if (!NILoader::transformCoordinates(pos, true)) {
304  WRITE_ERROR("Unable to project coordinates for node " + toString(id) + ".");
305  return 0;
306  }
307  node = new NBNode(toString(id), pos);
308  if (!nc.insert(node)) {
309  WRITE_ERROR("Could not insert node '" + toString(id) + "').");
310  delete node;
311  return 0;
312  }
313  n->node = node;
314  if (n->tlsControlled) {
315  // ok, this node is a traffic light node where no other nodes
316  // participate
317  // @note: The OSM-community has not settled on a schema for differentiating between fixed and actuated lights
319  NBOwnTLDef* tlDef = new NBOwnTLDef(toString(id), node, 0, type);
320  if (!tlsc.insert(tlDef)) {
321  // actually, nothing should fail here
322  delete tlDef;
323  throw ProcessError("Could not allocate tls '" + toString(id) + "'.");
324  }
325  }
326  }
327  return node;
328 }
329 
330 
331 int
333  const std::vector<SUMOLong>& passed, NBNetBuilder& nb) {
334  NBNodeCont& nc = nb.getNodeCont();
335  NBEdgeCont& ec = nb.getEdgeCont();
336  NBTypeCont& tc = nb.getTypeCont();
338  // patch the id
339  std::string id = toString(e->id);
340  if (from == 0 || to == 0) {
341  WRITE_ERROR("Discarding edge " + id + " because the nodes could not be built.");
342  return index;
343  }
344  if (index >= 0) {
345  id = id + "#" + toString(index);
346  } else {
347  index = 0;
348  }
349  if (from == to) {
350  // in the special case of a looped way split again using passed
351  assert(passed.size() >= 2);
352  std::vector<SUMOLong> geom(passed);
353  geom.pop_back(); // remove to-node
354  NBNode* intermediate = insertNodeChecking(geom.back(), nc, tlsc);
355  index = insertEdge(e, index, from, intermediate, geom, nb);
356  geom.clear();
357  return insertEdge(e, index, intermediate, to, geom, nb);
358  }
359  const int newIndex = index + 1;
360 
361  // convert the shape
362  PositionVector shape;
363  for (std::vector<SUMOLong>::const_iterator i = passed.begin(); i != passed.end(); ++i) {
364  NIOSMNode* n = myOSMNodes.find(*i)->second;
365  Position pos(n->lon, n->lat);
366  if (!NILoader::transformCoordinates(pos, true)) {
367  WRITE_ERROR("Unable to project coordinates for edge " + id + ".");
368  }
369  shape.push_back_noDoublePos(pos);
370  }
371 
372  std::string type = e->myHighWayType;
373  if (!tc.knows(type)) {
374  if (type.find(compoundTypeSeparator) != std::string::npos) {
375  // this edge has a combination type which does not yet exist in the TypeContainer
377  std::set<std::string> types;
378  while (tok.hasNext()) {
379  std::string t = tok.next();
380  if (tc.knows(t)) {
381  types.insert(t);
382  } else {
383  WRITE_WARNING("Discarding unknown compound \"" + t + "\" for edge " + id + " with type \"" + type + "\".");
384  }
385  }
386  switch (types.size()) {
387  case 0:
388  WRITE_WARNING("Discarding edge " + id + " with type unknown compound type \"" + type + "\".");
389  return newIndex;
390  break;
391  case 1: {
392  type = *(types.begin());
393  break;
394  }
395  default:
396  // build a new type by merging all values
397  int numLanes = 0;
398  SUMOReal maxSpeed = 0;
399  int prio = 0;
401  bool defaultIsOneWay = false;
402  for (std::set<std::string>::iterator it = types.begin(); it != types.end(); it++) {
403  numLanes = MAX2(numLanes, tc.getNumLanes(*it));
404  maxSpeed = MAX2(maxSpeed, tc.getSpeed(*it));
405  prio = MAX2(prio, tc.getPriority(*it));
406  defaultIsOneWay &= tc.getIsOneWay(*it);
407  }
408  WRITE_MESSAGE("Adding new compound type \"" + type + "\" for edge " + id + ".");
409  // @todo use the propper bitsets instead of SVC_UNKNOWN (see #675)
410  tc.insert(type, numLanes, maxSpeed, prio, width, SVC_UNKNOWN, defaultIsOneWay);
411  }
412  } else {
413  // we do not know the type -> something else, ignore
414  //WRITE_WARNING("Discarding edge " + id + " with unknown type \"" + type + "\".");
415  return newIndex;
416  }
417  }
418 
419  // otherwise it is not an edge and will be ignored
420  bool ok = true;
421  int numLanesForward = tc.getNumLanes(type);
422  int numLanesBackward = tc.getNumLanes(type);
423  SUMOReal speed = tc.getSpeed(type);
424  bool defaultsToOneWay = tc.getIsOneWay(type);
425  SVCPermissions permissions = tc.getPermissions(type);
426  // check directions
427  bool addForward = true;
428  bool addBackward = true;
429  if (e->myIsOneWay == "true" || e->myIsOneWay == "yes" || e->myIsOneWay == "1" || (defaultsToOneWay && e->myIsOneWay != "no" && e->myIsOneWay != "false" && e->myIsOneWay != "0")) {
430  addBackward = false;
431  }
432  if (e->myIsOneWay == "-1" || e->myIsOneWay == "reverse") {
433  // one-way in reversed direction of way
434  addForward = false;
435  addBackward = true;
436  }
437  if (e->myIsOneWay != "" && e->myIsOneWay != "false" && e->myIsOneWay != "no" && e->myIsOneWay != "true" && e->myIsOneWay != "yes" && e->myIsOneWay != "-1" && e->myIsOneWay != "1" && e->myIsOneWay != "reverse") {
438  WRITE_WARNING("New value for oneway found: " + e->myIsOneWay);
439  }
440  // if we had been able to extract the number of lanes, override the highway type default
441  if (e->myNoLanes > 0) {
442  if (addForward && !addBackward) {
443  numLanesForward = e->myNoLanes;
444  } else if (!addForward && addBackward) {
445  numLanesBackward = e->myNoLanes;
446  } else {
447  if (e->myNoLanesForward > 0) {
448  numLanesForward = e->myNoLanesForward;
449  } else if (e->myNoLanesForward < 0) {
450  numLanesForward = e->myNoLanes + e->myNoLanesForward;
451  } else {
452  numLanesForward = (int)std::ceil(e->myNoLanes / 2.0);
453  }
454  numLanesBackward = e->myNoLanes - numLanesForward;
455  // sometimes ways are tagged according to their physical width of a single
456  // lane but they are intended for traffic in both directions
457  numLanesForward = MAX2(1, numLanesForward);
458  numLanesBackward = MAX2(1, numLanesBackward);
459  }
460  } else if (e->myNoLanes == 0) {
461  WRITE_WARNING("Skipping edge '" + id + "' because it has zero lanes.");
462  ok = false;
463  }
464  // if we had been able to extract the maximum speed, override the type's default
465  if (e->myMaxSpeed != MAXSPEED_UNGIVEN) {
466  speed = (SUMOReal)(e->myMaxSpeed / 3.6);
467  }
468  if (speed <= 0) {
469  WRITE_WARNING("Skipping edge '" + id + "' because it has speed " + toString(speed));
470  ok = false;
471  }
472  if (ok) {
474  if (addForward) {
475  assert(numLanesForward > 0);
476  NBEdge* nbe = new NBEdge(StringUtils::escapeXML(id), from, to, type, speed, numLanesForward, tc.getPriority(type),
478  nbe->setPermissions(permissions);
479  if (!ec.insert(nbe)) {
480  delete nbe;
481  throw ProcessError("Could not add edge '" + id + "'.");
482  }
483  id = "-" + id;
484  }
485  if (addBackward) {
486  assert(numLanesBackward > 0);
487  NBEdge* nbe = new NBEdge(StringUtils::escapeXML(id), to, from, type, speed, numLanesBackward, tc.getPriority(type),
489  nbe->setPermissions(permissions);
490  if (!ec.insert(nbe)) {
491  delete nbe;
492  throw ProcessError("Could not add edge " + id + "'.");
493  }
494  }
495  }
496  return newIndex;
497 }
498 
499 
500 // ---------------------------------------------------------------------------
501 // definitions of NIImporter_OpenStreetMap::NodesHandler-methods
502 // ---------------------------------------------------------------------------
504  std::map<SUMOLong, NIOSMNode*>& toFill,
505  std::set<NIOSMNode*, CompareNodes>& uniqueNodes) :
506  SUMOSAXHandler("osm - file"),
507  myToFill(toFill),
508  myLastNodeID(-1),
509  myIsInValidNodeTag(false),
510  myHierarchyLevel(0),
511  myUniqueNodes(uniqueNodes) {
512 }
513 
514 
516 
517 
518 void
520  ++myHierarchyLevel;
521  if (element == SUMO_TAG_NODE) {
522  bool ok = true;
523  if (myHierarchyLevel != 2) {
524  WRITE_ERROR("Node element on wrong XML hierarchy level (id='" + toString(attrs.get<SUMOLong>(SUMO_ATTR_ID, 0, ok)) + "', level='" + toString(myHierarchyLevel) + "').");
525  return;
526  }
527  SUMOLong id = attrs.get<SUMOLong>(SUMO_ATTR_ID, 0, ok);
528  std::string action = attrs.hasAttribute("action") ? attrs.getStringSecure("action", "") : "";
529  if (action == "delete") {
530  return;
531  }
532  if (!ok) {
533  return;
534  }
535  myLastNodeID = -1;
536  if (myToFill.find(id) == myToFill.end()) {
537  myLastNodeID = id;
538  // assume we are loading multiple files...
539  // ... so we won't report duplicate nodes
540  bool ok = true;
541  double tlat, tlon;
542  std::istringstream lon(attrs.get<std::string>(SUMO_ATTR_LON, toString(id).c_str(), ok));
543  if (!ok) {
544  return;
545  }
546  lon >> tlon;
547  if (lon.fail()) {
548  WRITE_ERROR("Node's '" + toString(id) + "' lon information is not numeric.");
549  return;
550  }
551  std::istringstream lat(attrs.get<std::string>(SUMO_ATTR_LAT, toString(id).c_str(), ok));
552  if (!ok) {
553  return;
554  }
555  lat >> tlat;
556  if (lat.fail()) {
557  WRITE_ERROR("Node's '" + toString(id) + "' lat information is not numeric.");
558  return;
559  }
560  NIOSMNode* toAdd = new NIOSMNode(id, tlon, tlat);
561  myIsInValidNodeTag = true;
562 
563  std::set<NIOSMNode*, CompareNodes>::iterator similarNode = myUniqueNodes.find(toAdd);
564  if (similarNode == myUniqueNodes.end()) {
565  myUniqueNodes.insert(toAdd);
566  } else {
567  delete toAdd;
568  toAdd = *similarNode;
569  WRITE_MESSAGE("Found duplicate nodes. Substituting " + toString(id) + " with " + toString(toAdd->id));
570  }
571  myToFill[id] = toAdd;
572  }
573  }
574  if (element == SUMO_TAG_TAG && myIsInValidNodeTag) {
575  if (myHierarchyLevel != 3) {
576  WRITE_ERROR("Tag element on wrong XML hierarchy level.");
577  return;
578  }
579  bool ok = true;
580  std::string key = attrs.get<std::string>(SUMO_ATTR_K, toString(myLastNodeID).c_str(), ok);
581  std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myLastNodeID).c_str(), ok, false);
582  if (!ok) {
583  return;
584  }
585  if (key == "highway" && value.find("traffic_signal") != std::string::npos) {
586  myToFill[myLastNodeID]->tlsControlled = true;
587  }
588  }
589 }
590 
591 
592 void
594  if (element == SUMO_TAG_NODE && myHierarchyLevel == 2) {
595  myLastNodeID = -1;
596  myIsInValidNodeTag = false;
597  }
598  --myHierarchyLevel;
599 }
600 
601 
602 // ---------------------------------------------------------------------------
603 // definitions of NIImporter_OpenStreetMap::EdgesHandler-methods
604 // ---------------------------------------------------------------------------
606  const std::map<SUMOLong, NIOSMNode*>& osmNodes,
607  std::map<SUMOLong, Edge*>& toFill) :
608  SUMOSAXHandler("osm - file"),
609  myOSMNodes(osmNodes),
610  myEdgeMap(toFill) {
611  mySpeedMap["signals"] = MAXSPEED_UNGIVEN;
612  mySpeedMap["none"] = 300.;
613  mySpeedMap["no"] = 300.;
614  mySpeedMap["walk"] = 5.;
615  mySpeedMap["DE:rural"] = 100.;
616  mySpeedMap["DE:urban"] = 50.;
617  mySpeedMap["DE:living_street"] = 10.;
618 
619 }
620 
621 
623 }
624 
625 
626 void
628  const SUMOSAXAttributes& attrs) {
629  myParentElements.push_back(element);
630  // parse "way" elements
631  if (element == SUMO_TAG_WAY) {
632  bool ok = true;
633  SUMOLong id = attrs.get<SUMOLong>(SUMO_ATTR_ID, 0, ok);
634  std::string action = attrs.hasAttribute("action") ? attrs.getStringSecure("action", "") : "";
635  if (action == "delete") {
636  myCurrentEdge = 0;
637  return;
638  }
639  if (!ok) {
640  myCurrentEdge = 0;
641  return;
642  }
643  myCurrentEdge = new Edge(id);
644  }
645  // parse "nd" (node) elements
646  if (element == SUMO_TAG_ND) {
647  bool ok = true;
648  SUMOLong ref = attrs.get<SUMOLong>(SUMO_ATTR_REF, 0, ok);
649  if (ok) {
650  std::map<SUMOLong, NIOSMNode*>::const_iterator node = myOSMNodes.find(ref);
651  if (node == myOSMNodes.end()) {
652  WRITE_WARNING("The referenced geometry information (ref='" + toString(ref) + "') is not known");
653  return;
654  } else {
655  ref = node->second->id; // node may have been substituted
656  if (myCurrentEdge->myCurrentNodes.size() == 0 ||
657  myCurrentEdge->myCurrentNodes.back() != ref) { // avoid consecutive duplicates
658  myCurrentEdge->myCurrentNodes.push_back(ref);
659  }
660  }
661  }
662  }
663  // parse values
664  if (element == SUMO_TAG_TAG && myParentElements.size() > 2 && myParentElements[myParentElements.size() - 2] == SUMO_TAG_WAY) {
665  if (myCurrentEdge == 0) {
666  return;
667  }
668  bool ok = true;
669  std::string key = attrs.get<std::string>(SUMO_ATTR_K, toString(myCurrentEdge->id).c_str(), ok);
670  std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentEdge->id).c_str(), ok, false);
671  if (!ok) {
672  return;
673  }
674  if (key == "highway" || key == "railway") {
675  if (myCurrentEdge->myHighWayType != "") {
676  // osm-ways may be used by more than one mode (eg railway.tram + highway.residential. this is relevant for multimodal traffic)
677  // we create a new type for this kind of situation which must then be resolved in insertEdge()
678  myCurrentEdge->myHighWayType = myCurrentEdge->myHighWayType + compoundTypeSeparator + key + "." + value;
679  } else {
680  myCurrentEdge->myHighWayType = key + "." + value;
681  }
682  myCurrentEdge->myCurrentIsRoad = true;
683  } else if (key == "lanes") {
684  try {
685  myCurrentEdge->myNoLanes = TplConvert::_2int(value.c_str());
686  } catch (NumberFormatException&) {
687  // might be a list of values
688  StringTokenizer st(value, ";", true);
689  std::vector<std::string> list = st.getVector();
690  if (list.size() >= 2) {
691  int minLanes = std::numeric_limits<int>::max();
692  try {
693  for (std::vector<std::string>::iterator i = list.begin(); i != list.end(); ++i) {
694  int numLanes = TplConvert::_2int(StringUtils::prune(*i).c_str());
695  minLanes = MIN2(minLanes, numLanes);
696  }
697  myCurrentEdge->myNoLanes = minLanes;
698  WRITE_WARNING("Using minimum lane number from list (" + value + ") for edge '" + toString(myCurrentEdge->id) + "'.");
699  } catch (NumberFormatException&) {
700  WRITE_WARNING("Value of key '" + key + "' is not numeric ('" + value + "') in edge '" +
701  toString(myCurrentEdge->id) + "'.");
702  }
703  }
704  }
705  } else if (key == "lanes:forward") {
706  try {
707  myCurrentEdge->myNoLanesForward = TplConvert::_2int(value.c_str());
708  } catch (NumberFormatException&) {
709  WRITE_WARNING("Value of key '" + key + "' is not numeric ('" + value + "') in edge '" +
710  toString(myCurrentEdge->id) + "'.");
711  }
712  } else if (key == "lanes:backward") {
713  try {
714  // denote backwards count with a negative sign
715  myCurrentEdge->myNoLanesForward = -TplConvert::_2int(value.c_str());
716  } catch (NumberFormatException&) {
717  WRITE_WARNING("Value of key '" + key + "' is not numeric ('" + value + "') in edge '" +
718  toString(myCurrentEdge->id) + "'.");
719  }
720  } else if (key == "maxspeed") {
721  if (mySpeedMap.find(value) != mySpeedMap.end()) {
722  myCurrentEdge->myMaxSpeed = mySpeedMap[value];
723  } else {
724  SUMOReal conversion = 1; // OSM default is km/h
725  if (StringUtils::to_lower_case(value).find("km/h") != std::string::npos) {
726  value = StringUtils::prune(value.substr(0, value.find_first_not_of("0123456789")));
727  } else if (StringUtils::to_lower_case(value).find("mph") != std::string::npos) {
728  value = StringUtils::prune(value.substr(0, value.find_first_not_of("0123456789")));
729  conversion = 1.609344; // kilometers per mile
730  }
731  try {
732  myCurrentEdge->myMaxSpeed = TplConvert::_2SUMOReal(value.c_str()) * conversion;
733  } catch (NumberFormatException&) {
734  WRITE_WARNING("Value of key '" + key + "' is not numeric ('" + value + "') in edge '" +
735  toString(myCurrentEdge->id) + "'.");
736  }
737  }
738  } else if (key == "junction") {
739  if ((value == "roundabout") && (myCurrentEdge->myIsOneWay == "")) {
740  myCurrentEdge->myIsOneWay = "yes";
741  }
742  } else if (key == "oneway") {
743  myCurrentEdge->myIsOneWay = value;
744  } else if (key == "name") {
745  myCurrentEdge->streetName = value;
746  }
747  }
748 }
749 
750 
751 void
753  myParentElements.pop_back();
754  if (element == SUMO_TAG_WAY) {
755  if (myCurrentEdge != 0 && myCurrentEdge->myCurrentIsRoad) {
756  myEdgeMap[myCurrentEdge->id] = myCurrentEdge;
757  } else {
758  delete myCurrentEdge;
759  }
760  myCurrentEdge = 0;
761  }
762 }
763 
764 
765 // ---------------------------------------------------------------------------
766 // definitions of NIImporter_OpenStreetMap::RelationHandler-methods
767 // ---------------------------------------------------------------------------
769  const std::map<SUMOLong, NIOSMNode*>& osmNodes,
770  const std::map<SUMOLong, Edge*>& osmEdges) :
771  SUMOSAXHandler("osm - file"),
772  myOSMNodes(osmNodes),
773  myOSMEdges(osmEdges) {
774  resetValues();
775 }
776 
777 
779 }
780 
781 void
783  myCurrentRelation = INVALID_ID;
784  myIsRestriction = false;
785  myFromWay = INVALID_ID;
786  myToWay = INVALID_ID;
787  myViaNode = INVALID_ID;
788  myViaWay = INVALID_ID;
789  myRestrictionType = RESTRICTION_UNKNOWN;
790 }
791 
792 void
794  const SUMOSAXAttributes& attrs) {
795  myParentElements.push_back(element);
796  // parse "way" elements
797  if (element == SUMO_TAG_RELATION) {
798  bool ok = true;
799  myCurrentRelation = attrs.get<SUMOLong>(SUMO_ATTR_ID, 0, ok);
800  std::string action = attrs.hasAttribute("action") ? attrs.getStringSecure("action", "") : "";
801  if (action == "delete" || !ok) {
802  myCurrentRelation = INVALID_ID;
803  }
804  return;
805  } else if (myCurrentRelation == INVALID_ID) {
806  return;
807  }
808  // parse member elements
809  if (element == SUMO_TAG_MEMBER) {
810  bool ok = true;
811  std::string role = attrs.hasAttribute("role") ? attrs.getStringSecure("role", "") : "";
812  SUMOLong ref = attrs.get<SUMOLong>(SUMO_ATTR_REF, 0, ok);
813  if (role == "via") {
814  // u-turns for divided ways may be given with 2 via-nodes or 1 via-way
815  std::string memberType = attrs.get<std::string>(SUMO_ATTR_TYPE, 0, ok);
816  if (memberType == "way" && checkEdgeRef(ref)) {
817  myViaWay = ref;
818  } else if (memberType == "node") {
819  if (myOSMNodes.find(ref) != myOSMNodes.end()) {
820  myViaNode = ref;
821  } else {
822  WRITE_WARNING("No node found for reference '" + toString(ref) + "' in relation '" + toString(myCurrentRelation) + "'");
823  }
824  }
825  } else if (role == "from" && checkEdgeRef(ref)) {
826  myFromWay = ref;
827  } else if (role == "to" && checkEdgeRef(ref)) {
828  myToWay = ref;
829  }
830  return;
831  }
832  // parse values
833  if (element == SUMO_TAG_TAG) {
834  bool ok = true;
835  std::string key = attrs.get<std::string>(SUMO_ATTR_K, toString(myCurrentRelation).c_str(), ok);
836  std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);
837  if (!ok) {
838  return;
839  }
840  if (key == "type" && value == "restriction") {
841  myIsRestriction = true;
842  return;
843  }
844  if (key == "restriction") {
845  if (value.substr(0, 5) == "only_") {
846  myRestrictionType = RESTRICTION_ONLY;
847  } else if (value.substr(0, 3) == "no_") {
848  myRestrictionType = RESTRICTION_NO;
849  } else {
850  WRITE_WARNING("Found unknown restriction type '" + value + "' in relation '" + toString(myCurrentRelation) + "'");
851  }
852  return;
853  }
854  }
855 }
856 
857 
858 bool
860  if (myOSMEdges.find(ref) != myOSMEdges.end()) {
861  return true;
862  } else {
863  WRITE_WARNING("No way found for reference '" + toString(ref) + "' in relation '" + toString(myCurrentRelation) + "'");
864  return false;
865  }
866 }
867 
868 
869 void
871  myParentElements.pop_back();
872  if (element == SUMO_TAG_RELATION) {
873  if (myIsRestriction) {
874  assert(myCurrentRelation != INVALID_ID);
875  bool ok = true;
876  if (myRestrictionType == RESTRICTION_UNKNOWN) {
877  WRITE_WARNING("Ignoring restriction relation '" + toString(myCurrentRelation) + "' with unknown type.");
878  ok = false;
879  }
880  if (myFromWay == INVALID_ID) {
881  WRITE_WARNING("Ignoring restriction relation '" + toString(myCurrentRelation) + "' with unknown from-way.");
882  ok = false;
883  }
884  if (myToWay == INVALID_ID) {
885  WRITE_WARNING("Ignoring restriction relation '" + toString(myCurrentRelation) + "' with unknown to-way.");
886  ok = false;
887  }
888  if (myViaNode == INVALID_ID && myViaWay == INVALID_ID) {
889  WRITE_WARNING("Ignoring restriction relation '" + toString(myCurrentRelation) + "' with unknown via.");
890  ok = false;
891  }
892  if (ok && !applyRestriction()) {
893  WRITE_WARNING("Ignoring restriction relation '" + toString(myCurrentRelation) + "'.");
894  }
895  }
896  // other relations might use similar subelements so reset in any case
897  resetValues();
898  }
899 }
900 
901 
902 bool
904  // since OSM ways are bidirectional we need the via to figure out which direction was meant
905  if (myViaNode != INVALID_ID) {
906  NBNode* viaNode = myOSMNodes.find(myViaNode)->second->node;
907  if (viaNode == 0) {
908  WRITE_WARNING("Via-node '" + toString(myViaNode) + "' was not instantiated");
909  return false;
910  }
911  NBEdge* from = findEdgeRef(myFromWay, viaNode->getIncomingEdges());
912  NBEdge* to = findEdgeRef(myToWay, viaNode->getOutgoingEdges());
913  if (from == 0) {
914  WRITE_WARNING("from-edge of restriction relation could not be determined");
915  return false;
916  }
917  if (to == 0) {
918  WRITE_WARNING("to-edge of restriction relation could not be determined");
919  return false;
920  }
921  if (myRestrictionType == RESTRICTION_ONLY) {
922  from->addEdge2EdgeConnection(to);
923  } else {
924  from->removeFromConnections(to, -1, -1, true);
925  }
926  } else {
927  // XXX interpreting via-ways or via-node lists not yet implemented
928  WRITE_WARNING("direction of restriction relation could not be determined");
929  return false;
930  }
931  return true;
932 }
933 
934 
935 NBEdge*
936 NIImporter_OpenStreetMap::RelationHandler::findEdgeRef(SUMOLong wayRef, const std::vector<NBEdge*>& candidates) const {
937  const std::string prefix = toString(wayRef);
938  const std::string backPrefix = "-" + prefix;
939  NBEdge* result = 0;
940  int found = 0;
941  for (EdgeVector::const_iterator it = candidates.begin(); it != candidates.end(); ++it) {
942  if (((*it)->getID().substr(0, prefix.size()) == prefix) ||
943  ((*it)->getID().substr(0, backPrefix.size()) == backPrefix)) {
944  result = *it;
945  found++;
946  }
947  }
948  if (found > 1) {
949  WRITE_WARNING("Ambigous way reference '" + prefix + "' in restriction relation");
950  result = 0;
951  }
952  return result;
953 };
954 
955 
956 /****************************************************************************/
957