Eclipse SUMO - Simulation of Urban MObility
NIImporter_OpenStreetMap.cpp
Go to the documentation of this file.
1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 // Copyright (C) 2001-2024 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials are made available under the
5 // terms of the Eclipse Public License 2.0 which is available at
6 // https://www.eclipse.org/legal/epl-2.0/
7 // This Source Code may also be made available under the following Secondary
8 // Licenses when the conditions for such availability set forth in the Eclipse
9 // Public License 2.0 are satisfied: GNU General Public License, version 2
10 // or later which is available at
11 // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 /****************************************************************************/
23 // Importer for networks stored in OpenStreetMap format
24 /****************************************************************************/
25 #include <config.h>
26 #include <algorithm>
27 #include <set>
28 #include <functional>
29 #include <sstream>
30 #include <limits>
33 #include <utils/common/ToString.h>
44 #include <utils/xml/XMLSubSys.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>
51 #include <netbuild/NBPTLine.h>
52 #include <netbuild/NBPTLineCont.h>
53 #include <netbuild/NBPTPlatform.h>
54 #include <netbuild/NBPTStop.h>
55 #include "NILoader.h"
57 
58 //#define DEBUG_LAYER_ELEVATION
59 //#define DEBUG_RAIL_DIRECTION
60 
61 // ---------------------------------------------------------------------------
62 // static members
63 // ---------------------------------------------------------------------------
65 
66 const long long int NIImporter_OpenStreetMap::INVALID_ID = std::numeric_limits<long long int>::max();
69 
70 // ===========================================================================
71 // Private classes
72 // ===========================================================================
73 
77 public:
78  bool operator()(const Edge* e1, const Edge* e2) const {
79  if (e1->myHighWayType != e2->myHighWayType) {
80  return e1->myHighWayType > e2->myHighWayType;
81  }
82  if (e1->myNoLanes != e2->myNoLanes) {
83  return e1->myNoLanes > e2->myNoLanes;
84  }
85  if (e1->myNoLanesForward != e2->myNoLanesForward) {
86  return e1->myNoLanesForward > e2->myNoLanesForward;
87  }
88  if (e1->myMaxSpeed != e2->myMaxSpeed) {
89  return e1->myMaxSpeed > e2->myMaxSpeed;
90  }
91  if (e1->myIsOneWay != e2->myIsOneWay) {
92  return e1->myIsOneWay > e2->myIsOneWay;
93  }
94  return e1->myCurrentNodes > e2->myCurrentNodes;
95  }
96 };
97 
98 // ===========================================================================
99 // method definitions
100 // ===========================================================================
101 // ---------------------------------------------------------------------------
102 // static methods
103 // ---------------------------------------------------------------------------
104 const std::string NIImporter_OpenStreetMap::compoundTypeSeparator("|"); //clang-tidy says: "compundTypeSeparator with
105 // static storage duration my throw an exception that cannot be caught
106 
107 void
109  NIImporter_OpenStreetMap importer;
110  importer.load(oc, nb);
111 }
112 
114 
116  // delete nodes
117  for (auto myUniqueNode : myUniqueNodes) {
118  delete myUniqueNode;
119  }
120  // delete edges
121  for (auto& myEdge : myEdges) {
122  delete myEdge.second;
123  }
124  // delete platform shapes
125  for (auto& myPlatformShape : myPlatformShapes) {
126  delete myPlatformShape.second;
127  }
128 }
129 
130 void
132  if (!oc.isSet("osm-files")) {
133  return;
134  }
135  const std::vector<std::string> files = oc.getStringVector("osm-files");
136  std::vector<SUMOSAXReader*> readers;
137 
138  myImportLaneAccess = oc.getBool("osm.lane-access");
139  myImportTurnSigns = oc.getBool("osm.turn-lanes");
140  myImportSidewalks = oc.getBool("osm.sidewalks");
141  myImportBikeAccess = oc.getBool("osm.bike-access");
142  myImportCrossings = oc.getBool("osm.crossings");
143 
144  myAllAttributes = OptionsCont::getOptions().getBool("osm.all-attributes");
145  std::vector<std::string> extra = OptionsCont::getOptions().getStringVector("osm.extra-attributes");
146  myExtraAttributes.insert(extra.begin(), extra.end());
147  if (myExtraAttributes.count("all") != 0) {
148  // import all
149  myExtraAttributes.clear();
150  }
151 
152  // load nodes, first
153  NodesHandler nodesHandler(myOSMNodes, myUniqueNodes, oc);
154  for (const std::string& file : files) {
155  if (!FileHelpers::isReadable(file)) {
156  WRITE_ERRORF(TL("Could not open osm-file '%'."), file);
157  return;
158  }
159  nodesHandler.setFileName(file);
160  nodesHandler.resetHierarchy();
161  const long before = PROGRESS_BEGIN_TIME_MESSAGE("Parsing nodes from osm-file '" + file + "'");
162  readers.push_back(XMLSubSys::getSAXReader(nodesHandler));
163  if (!readers.back()->parseFirst(file) || !readers.back()->parseSection(SUMO_TAG_NODE) ||
165  return;
166  }
167  if (nodesHandler.getDuplicateNodes() > 0) {
168  WRITE_MESSAGEF(TL("Found and substituted % osm nodes."), toString(nodesHandler.getDuplicateNodes()));
169  }
170  PROGRESS_TIME_MESSAGE(before);
171  }
172 
173  // load edges, then
175  int idx = 0;
176  for (const std::string& file : files) {
177  edgesHandler.setFileName(file);
178  readers[idx]->setHandler(edgesHandler);
179  const long before = PROGRESS_BEGIN_TIME_MESSAGE("Parsing edges from osm-file '" + file + "'");
180  if (!readers[idx]->parseSection(SUMO_TAG_WAY)) {
181  // eof already reached, no relations
182  delete readers[idx];
183  readers[idx] = nullptr;
184  }
185  PROGRESS_TIME_MESSAGE(before);
186  idx++;
187  }
188 
189  /* Remove duplicate edges with the same shape and attributes */
190  if (!oc.getBool("osm.skip-duplicates-check")) {
191  int numRemoved = 0;
192  PROGRESS_BEGIN_MESSAGE(TL("Removing duplicate edges"));
193  if (myEdges.size() > 1) {
194  std::set<const Edge*, CompareEdges> dupsFinder;
195  for (auto it = myEdges.begin(); it != myEdges.end();) {
196  if (dupsFinder.count(it->second) > 0) {
197  numRemoved++;
198  delete it->second;
199  myEdges.erase(it++);
200  } else {
201  dupsFinder.insert(it->second);
202  it++;
203  }
204  }
205  }
206  if (numRemoved > 0) {
207  WRITE_MESSAGEF(TL("Removed % duplicate osm edges."), toString(numRemoved));
208  }
210  }
211 
212  /* Mark which nodes are used (by edges or traffic lights).
213  * This is necessary to detect which OpenStreetMap nodes are for
214  * geometry only */
215  std::map<long long int, int> nodeUsage;
216  // Mark which nodes are used by edges (begin and end)
217  for (const auto& edgeIt : myEdges) {
218  assert(edgeIt.second->myCurrentIsRoad);
219  for (const long long int node : edgeIt.second->myCurrentNodes) {
220  nodeUsage[node]++;
221  }
222  }
223  // Mark which nodes are used by traffic lights or are pedestrian crossings
224  for (const auto& nodesIt : myOSMNodes) {
225  if (nodesIt.second->tlsControlled || nodesIt.second->railwaySignal || (nodesIt.second->pedestrianCrossing && myImportCrossings) /* || nodesIt->second->railwayCrossing*/) {
226  // If the key is not found in the map, the value is automatically
227  // initialized with 0.
228  nodeUsage[nodesIt.first]++;
229  }
230  }
231 
232  /* Instantiate edges
233  * Only those nodes in the middle of an edge which are used by more than
234  * one edge are instantiated. Other nodes are considered as geometry nodes. */
235  NBNodeCont& nc = nb.getNodeCont();
237  for (const auto& edgeIt : myEdges) {
238  Edge* const e = edgeIt.second;
239  assert(e->myCurrentIsRoad);
240  if (e->myCurrentNodes.size() < 2) {
241  WRITE_WARNINGF(TL("Discarding way '%' because it has only % node(s)"), e->id, e->myCurrentNodes.size());
242  continue;
243  }
245  // build nodes;
246  // - the from- and to-nodes must be built in any case
247  // - the in-between nodes are only built if more than one edge references them
248  NBNode* first = insertNodeChecking(e->myCurrentNodes.front(), nc, tlsc);
249  NBNode* last = insertNodeChecking(e->myCurrentNodes.back(), nc, tlsc);
250  NBNode* currentFrom = first;
251  int running = 0;
252  std::vector<long long int> passed;
253  for (auto j = e->myCurrentNodes.begin(); j != e->myCurrentNodes.end(); ++j) {
254  passed.push_back(*j);
255  if (nodeUsage[*j] > 1 && j != e->myCurrentNodes.end() - 1 && j != e->myCurrentNodes.begin()) {
256  NBNode* currentTo = insertNodeChecking(*j, nc, tlsc);
257  running = insertEdge(e, running, currentFrom, currentTo, passed, nb, first, last);
258  currentFrom = currentTo;
259  passed.clear();
260  passed.push_back(*j);
261  }
262  }
263  if (running == 0) {
264  running = -1;
265  }
266  insertEdge(e, running, currentFrom, last, passed, nb, first, last);
267  }
268 
269  /* Collect edges which explicitly are part of a roundabout and store the edges of each
270  * detected roundabout */
272 
273  if (myImportCrossings) {
274  /* After edges are instantiated
275  * nodes are parsed again to add pedestrian crossings to them
276  * This is only executed if crossings are imported and not guessed */
277  const double crossingWidth = OptionsCont::getOptions().getFloat("default.crossing-width");
278 
279  for (const auto& nodeIt : nc) {
280  NBNode* const n = nodeIt.second;
281  if (n->hasParameter("computePedestrianCrossing")) {
282  EdgeVector incomingEdges = n->getIncomingEdges();
283  EdgeVector outgoingEdges = n->getOutgoingEdges();
284  size_t incomingEdgesNo = incomingEdges.size();
285  size_t outgoingEdgesNo = outgoingEdges.size();
286 
287  for (size_t i = 0; i < incomingEdgesNo; i++) {
288  /* Check if incoming edge has driving lanes(and sidewalks)
289  * if not, ignore
290  * if yes, check if there is a corresponding outgoing edge for the opposite direction
291  * -> if yes, check if it has driving lanes
292  * --> if yes, do the crossing
293  * --> if no, only do the crossing with the incoming edge (usually one lane roads with two sidewalks)
294  * -> if not, do nothing as we don't have a sidewalk in the opposite direction */
295  auto const iEdge = incomingEdges[i];
296 
297  if (iEdge->getFirstNonPedestrianLaneIndex(NBNode::FORWARD) > -1
298  && iEdge->getSpecialLane(SVC_PEDESTRIAN) > -1) {
299  std::string const& iEdgeId = iEdge->getID();
300  std::size_t const m = iEdgeId.find_first_of("#");
301  std::string const& iWayId = iEdgeId.substr(0, m);
302  for (size_t j = 0; j < outgoingEdgesNo; j++) {
303  auto const oEdge = outgoingEdges[j];
304  // Searching for a corresponding outgoing edge (based on OSM way identifier)
305  // with at least a pedestrian lane, going in the opposite direction
306  if (oEdge->getID().find(iWayId) != std::string::npos
307  && oEdge->getSpecialLane(SVC_PEDESTRIAN) > -1
308  && oEdge->getID().rfind(iWayId, 0) != 0) {
309  EdgeVector edgeVector = EdgeVector{ iEdge };
310  if (oEdge->getFirstNonPedestrianLaneIndex(NBNode::FORWARD) > -1) {
311  edgeVector.push_back(oEdge);
312  }
313 
314  if (!n->checkCrossingDuplicated(edgeVector)) {
315  n->addCrossing(edgeVector, crossingWidth, false);
316  }
317  }
318  }
319  }
320  }
321  for (size_t i = 0; i < outgoingEdgesNo; i++) {
322  // Same checks as above for loop, but for outgoing edges
323  auto const oEdge = outgoingEdges[i];
324 
325  if (oEdge->getFirstNonPedestrianLaneIndex(NBNode::FORWARD) > -1
326  && oEdge->getSpecialLane(SVC_PEDESTRIAN) > -1) {
327  std::string const& oEdgeId = oEdge->getID();
328  std::size_t const m = oEdgeId.find_first_of("#");
329  std::string const& iWayId = oEdgeId.substr(0, m);
330  for (size_t j = 0; j < incomingEdgesNo; j++) {
331  auto const iEdge = incomingEdges[j];
332  if (iEdge->getID().find(iWayId) != std::string::npos
333  && iEdge->getSpecialLane(SVC_PEDESTRIAN) > -1
334  && iEdge->getID().rfind(iWayId, 0) != 0) {
335  EdgeVector edgeVector = EdgeVector{ oEdge };
336  if (iEdge->getFirstNonPedestrianLaneIndex(NBNode::FORWARD) > -1) {
337  edgeVector.push_back(iEdge);
338  }
339 
340  if (!n->checkCrossingDuplicated(edgeVector)) {
341  n->addCrossing(edgeVector, crossingWidth, false);
342  }
343  }
344  }
345  }
346  }
347  n->unsetParameter("computePedestrianCrossing");
348  }
349  }
350  }
351 
352  const double layerElevation = oc.getFloat("osm.layer-elevation");
353  if (layerElevation > 0) {
354  reconstructLayerElevation(layerElevation, nb);
355  }
356 
357  // revise pt stops; remove stops on deleted edges
359 
360  // load relations (after edges are built since we want to apply
361  // turn-restrictions directly to NBEdges)
363  &nb.getPTLineCont(), oc);
364  idx = 0;
365  for (const std::string& file : files) {
366  if (readers[idx] != nullptr) {
367  relationHandler.setFileName(file);
368  readers[idx]->setHandler(relationHandler);
369  const long before = PROGRESS_BEGIN_TIME_MESSAGE("Parsing relations from osm-file '" + file + "'");
370  readers[idx]->parseSection(SUMO_TAG_RELATION);
371  PROGRESS_TIME_MESSAGE(before);
372  delete readers[idx];
373  }
374  idx++;
375  }
376 
377  // declare additional stops that are not anchored to a (road)-way or route relation
378  std::set<std::string> stopNames;
379  for (const auto& item : nb.getPTStopCont().getStops()) {
380  stopNames.insert(item.second->getName());
381  }
382  for (const auto& item : myOSMNodes) {
383  const NIOSMNode* n = item.second;
384  if (n->ptStopPosition && stopNames.count(n->name) == 0) {
385  Position ptPos(n->lon, n->lat, n->ele);
386  if (!NBNetBuilder::transformCoordinate(ptPos)) {
387  WRITE_ERRORF("Unable to project coordinates for node '%'.", n->id);
388  }
389  std::shared_ptr<NBPTStop> ptStop = std::make_shared<NBPTStop>(toString(n->id), ptPos, "", "", n->ptStopLength, n->name, n->permissions);
390  nb.getPTStopCont().insert(ptStop, true);
391  }
392  }
393 }
394 
395 
396 NBNode*
398  NBNode* node = nc.retrieve(toString(id));
399  if (node == nullptr) {
400  NIOSMNode* n = myOSMNodes.find(id)->second;
401  Position pos(n->lon, n->lat, n->ele);
402  if (!NBNetBuilder::transformCoordinate(pos, true)) {
403  WRITE_ERRORF("Unable to project coordinates for junction '%'.", id);
404  return nullptr;
405  }
406  node = new NBNode(toString(id), pos);
407  if (!nc.insert(node)) {
408  WRITE_ERRORF(TL("Could not insert junction '%'."), toString(id));
409  delete node;
410  return nullptr;
411  }
412  n->node = node;
413  if (n->railwayCrossing) {
414  if (n->getParameter("crossing:barrier") != "no") {
416  } else if (n->getParameter("crossing.light") == "yes") {
418  }
419  } else if (n->railwaySignal) {
421  } else if (n->tlsControlled) {
422  // ok, this node is a traffic light node where no other nodes
423  // participate
424  // @note: The OSM-community has not settled on a schema for differentiating between fixed and actuated lights
426  OptionsCont::getOptions().getString("tls.default-type"));
427  NBOwnTLDef* tlDef = new NBOwnTLDef(toString(id), node, 0, type);
428  if (!tlsc.insert(tlDef)) {
429  // actually, nothing should fail here
430  delete tlDef;
431  throw ProcessError(TLF("Could not allocate tls '%'.", toString(id)));
432  }
433  } else if (n->pedestrianCrossing && myImportCrossings) {
434  node->setParameter("computePedestrianCrossing", "true");
435  }
436  if (n->railwayBufferStop) {
437  node->setParameter("buffer_stop", "true");
439  }
440  if (n->railwaySignal) {
441  if (n->myRailDirection == WAY_FORWARD) {
443  } else if (n->myRailDirection == WAY_BACKWARD) {
445  }
446  }
448  }
449  return node;
450 }
451 
452 
453 int
455  const std::vector<long long int>& passed, NBNetBuilder& nb,
456  const NBNode* first, const NBNode* last) {
457  NBNodeCont& nc = nb.getNodeCont();
458  NBEdgeCont& ec = nb.getEdgeCont();
459  NBTypeCont& tc = nb.getTypeCont();
460  NBPTStopCont& sc = nb.getPTStopCont();
461 
463  // patch the id
464  std::string id = toString(e->id);
465  if (from == nullptr || to == nullptr) {
466  WRITE_ERRORF("Discarding edge '%' because the nodes could not be built.", id);
467  return index;
468  }
469  if (index >= 0) {
470  id = id + "#" + toString(index);
471  } else {
472  index = 0;
473  }
474  if (from == to) {
475  assert(passed.size() >= 2);
476  if (passed.size() == 2) {
477  WRITE_WARNINGF(TL("Discarding edge '%' which connects two identical nodes without geometry."), id);
478  return index;
479  }
480  // in the special case of a looped way split again using passed
481  int intermediateIndex = (int) passed.size() / 2;
482  NBNode* intermediate = insertNodeChecking(passed[intermediateIndex], nc, tlsc);
483  std::vector<long long int> part1(passed.begin(), passed.begin() + intermediateIndex + 1);
484  std::vector<long long int> part2(passed.begin() + intermediateIndex, passed.end());
485  index = insertEdge(e, index, from, intermediate, part1, nb, first, last);
486  return insertEdge(e, index, intermediate, to, part2, nb, first, last);
487  }
488  const int newIndex = index + 1;
489  const std::string type = usableType(e->myHighWayType, id, tc);
490  if (type == "") { // we do not want to import it
491  return newIndex;
492  }
493 
494  int numLanesForward = tc.getEdgeTypeNumLanes(type);
495  int numLanesBackward = tc.getEdgeTypeNumLanes(type);
496  double speed = tc.getEdgeTypeSpeed(type);
497  bool defaultsToOneWay = tc.getEdgeTypeIsOneWay(type);
498  const SVCPermissions defaultPermissions = tc.getEdgeTypePermissions(type);
501  // extra permissions are more specific than extra prohibitions except for buses (which come from the less specific psv tag)
502  if ((extraDis & SVC_BUS) && (extra & SVC_BUS)) {
503  extra = extra & ~SVC_BUS;
504  }
505  SVCPermissions permissions = (defaultPermissions & ~extraDis) | extra;
506  if (defaultPermissions == SVC_SHIP) {
507  // extra permission apply to the ships operating on the route rather than the waterway
508  permissions = defaultPermissions;
509  }
510  if (defaultsToOneWay && defaultPermissions == SVC_PEDESTRIAN && (permissions & (~SVC_PEDESTRIAN)) != 0) {
511  defaultsToOneWay = false;
512  }
513  if ((permissions & SVC_RAIL) != 0 && e->myExtraTags.count("electrified") != 0) {
514  permissions |= (SVC_RAIL_ELECTRIC | SVC_RAIL_FAST);
515  }
516 
517  // convert the shape
518  PositionVector shape;
519  double distanceStart = myOSMNodes[passed.front()]->positionMeters;
520  double distanceEnd = myOSMNodes[passed.back()]->positionMeters;
521  const bool useDistance = distanceStart != std::numeric_limits<double>::max() && distanceEnd != std::numeric_limits<double>::max();
522  if (useDistance) {
523  // negative sign denotes counting in the other direction
524  if (distanceStart < distanceEnd) {
525  distanceStart *= -1;
526  } else {
527  distanceEnd *= -1;
528  }
529  } else {
530  distanceStart = 0;
531  distanceEnd = 0;
532  }
533  // get additional direction information
534  int nodeDirection = myOSMNodes.find(StringUtils::toLong(from->getID()))->second->myRailDirection |
535  myOSMNodes.find(StringUtils::toLong(to->getID()))->second->myRailDirection;
536 
537  std::vector<std::shared_ptr<NBPTStop> > ptStops;
538  for (long long i : passed) {
539  NIOSMNode* n = myOSMNodes.find(i)->second;
540  // recheck permissions, maybe they got assigned to a strange edge, see #11656
541  if (n->ptStopPosition && (n->permissions == 0 || (permissions & n->permissions) != 0)) {
542  std::shared_ptr<NBPTStop> existingPtStop = sc.get(toString(n->id));
543  if (existingPtStop != nullptr) {
544  existingPtStop->registerAdditionalEdge(toString(e->id), id);
545  } else {
546  Position ptPos(n->lon, n->lat, n->ele);
547  if (!NBNetBuilder::transformCoordinate(ptPos)) {
548  WRITE_ERRORF("Unable to project coordinates for node '%'.", n->id);
549  }
550  ptStops.push_back(std::make_shared<NBPTStop>(toString(n->id), ptPos, id, toString(e->id), n->ptStopLength, n->name, n->permissions));
551  sc.insert(ptStops.back());
552  }
553  }
554  if (n->railwaySignal) {
555  nodeDirection |= n->myRailDirection;
556  }
557  Position pos(n->lon, n->lat, n->ele);
558  shape.push_back(pos);
559  }
560 #ifdef DEBUG_LAYER_ELEVATION
561  if (e->id == "DEBUGID") {
562  std::cout
563  << " id=" << id << " from=" << from->getID() << " fromRailDirection=" << myOSMNodes.find(StringUtils::toLong(from->getID()))->second->myRailDirection
564  << " to=" << to->getID() << " toRailDirection=" << myOSMNodes.find(StringUtils::toLong(to->getID()))->second->myRailDirection
565  << " origRailDirection=" << e->myRailDirection
566  << " nodeDirection=" << nodeDirection
567  << "\n";
568  }
569 #endif
570  if (e->myRailDirection == WAY_UNKNOWN && nodeDirection != WAY_UNKNOWN && nodeDirection != WAY_FORWARD
571  && nodeDirection != (WAY_FORWARD | WAY_UNKNOWN)) {
572  //std::cout << "way " << e->id << " nodeDirection=" << nodeDirection << " origDirection=" << e->myRailDirection << "\n";
573  // heuristic: assume that the mapped way direction indicates
574  // potential driving direction
576  }
578  WRITE_ERRORF("Unable to project coordinates for edge '%'.", id);
579  }
580 
581  SVCPermissions forwardPermissions = permissions;
582  SVCPermissions backwardPermissions = permissions;
583  const std::string streetName = isRailway(permissions) && e->ref != "" ? e->ref : e->streetName;
584  if (streetName == e->ref) {
585  e->unsetParameter("ref"); // avoid superfluous param for railways
586  }
587  double forwardWidth = tc.getEdgeTypeWidth(type);
588  double backwardWidth = tc.getEdgeTypeWidth(type);
589  double sidewalkWidth = tc.getEdgeTypeSidewalkWidth(type);
590  bool addSidewalk = sidewalkWidth != NBEdge::UNSPECIFIED_WIDTH;
591  if (myImportSidewalks) {
592  if (addSidewalk) {
593  // only use sidewalk width from typemap but don't add sidewalks
594  // unless OSM specifies them
595  addSidewalk = false;
596  } else {
597  sidewalkWidth = OptionsCont::getOptions().getFloat("default.sidewalk-width");
598  }
599  }
600  double bikeLaneWidth = tc.getEdgeTypeBikeLaneWidth(type);
601  const std::string& onewayBike = e->myExtraTags["oneway:bicycle"];
602  if (onewayBike == "false" || onewayBike == "no" || onewayBike == "0") {
604  }
605 
606  const bool addBikeLane = bikeLaneWidth != NBEdge::UNSPECIFIED_WIDTH ||
607  (myImportBikeAccess && (((e->myCyclewayType & WAY_BOTH) != 0 || e->myExtraTags.count("segregated") != 0) &&
608  !(e->myCyclewayType == WAY_BACKWARD && (e->myBuswayType & WAY_BOTH) != 0)));
609  if (addBikeLane && bikeLaneWidth == NBEdge::UNSPECIFIED_WIDTH) {
610  bikeLaneWidth = OptionsCont::getOptions().getFloat("default.bikelane-width");
611  }
612  // check directions
613  bool addForward = true;
614  bool addBackward = true;
615  const bool explicitTwoWay = e->myIsOneWay == "no";
616  if ((e->myIsOneWay == "true" || e->myIsOneWay == "yes" || e->myIsOneWay == "1"
617  || (defaultsToOneWay && e->myIsOneWay != "no" && e->myIsOneWay != "false" && e->myIsOneWay != "0"))
618  && e->myRailDirection != WAY_BOTH) {
619  addBackward = false;
620  }
621  if (e->myIsOneWay == "-1" || e->myIsOneWay == "reverse" || e->myRailDirection == WAY_BACKWARD) {
622  // one-way in reversed direction of way
623  addForward = false;
624  addBackward = true;
625  }
626  if (!e->myIsOneWay.empty() && e->myIsOneWay != "false" && e->myIsOneWay != "no" && e->myIsOneWay != "true"
627  && e->myIsOneWay != "yes" && e->myIsOneWay != "-1" && e->myIsOneWay != "1" && e->myIsOneWay != "reverse") {
628  WRITE_WARNINGF(TL("New value for oneway found: %"), e->myIsOneWay);
629  }
630  if ((permissions == SVC_BICYCLE || permissions == (SVC_BICYCLE | SVC_PEDESTRIAN) || permissions == SVC_PEDESTRIAN)) {
631  if (addBackward && (onewayBike == "true" || onewayBike == "yes" || onewayBike == "1")) {
632  addBackward = false;
633  }
634  if (addForward && (onewayBike == "reverse" || onewayBike == "-1")) {
635  addForward = false;
636  }
637  if (!addBackward && (onewayBike == "false" || onewayBike == "no" || onewayBike == "0")) {
638  addBackward = true;
639  }
640  }
641  bool ok = true;
642  // if we had been able to extract the number of lanes, override the highway type default
643  if (e->myNoLanes > 0) {
644  if (addForward && !addBackward) {
645  numLanesForward = e->myNoLanesForward > 0 ? e->myNoLanesForward : e->myNoLanes;
646  } else if (!addForward && addBackward) {
647  numLanesBackward = e->myNoLanesForward < 0 ? -e->myNoLanesForward : e->myNoLanes;
648  } else {
649  if (e->myNoLanesForward > 0) {
650  numLanesForward = e->myNoLanesForward;
651  } else if (e->myNoLanesForward < 0) {
652  numLanesForward = e->myNoLanes + e->myNoLanesForward;
653  } else {
654  numLanesForward = (int) std::ceil(e->myNoLanes / 2.0);
655  }
656  numLanesBackward = e->myNoLanes - numLanesForward;
657  // sometimes ways are tagged according to their physical width of a single
658  // lane but they are intended for traffic in both directions
659  numLanesForward = MAX2(1, numLanesForward);
660  numLanesBackward = MAX2(1, numLanesBackward);
661  }
662  } else if (e->myNoLanes == 0) {
663  WRITE_WARNINGF(TL("Skipping edge '%' because it has zero lanes."), id);
664  ok = false;
665  } else {
666  // the total number of lanes is not known but at least one direction
667  if (e->myNoLanesForward > 0) {
668  numLanesForward = e->myNoLanesForward;
669  }
670  if (e->myNoLanesForward < 0) {
671  numLanesBackward = -e->myNoLanesForward;
672  }
673  }
674  // deal with busways that run in the opposite direction of a one-way street
675  if (!addForward && (e->myBuswayType & WAY_FORWARD) != 0) {
676  addForward = true;
677  forwardPermissions = SVC_BUS;
678  numLanesForward = 1;
679  }
680  if (!addBackward && (e->myBuswayType & WAY_BACKWARD) != 0) {
681  addBackward = true;
682  backwardPermissions = SVC_BUS;
683  numLanesBackward = 1;
684  }
685  // with is meant for raw lane count before adding sidewalks or cycleways
686  const int taggedLanes = (addForward ? numLanesForward : 0) + (addBackward ? numLanesBackward : 0);
687  if (e->myWidth > 0 && e->myWidthLanesForward.size() == 0 && e->myWidthLanesBackward.size() == 0 && taggedLanes != 0
688  && !OptionsCont::getOptions().getBool("ignore-widths")) {
689  // width is tagged excluding sidewalks and cycleways
690  forwardWidth = e->myWidth / taggedLanes;
691  backwardWidth = forwardWidth;
692  }
693 
694  // if we had been able to extract the maximum speed, override the type's default
695  if (e->myMaxSpeed != MAXSPEED_UNGIVEN) {
696  speed = e->myMaxSpeed;
697  }
698  double speedBackward = speed;
700  speedBackward = e->myMaxSpeedBackward;
701  }
702  if (speed <= 0 || speedBackward <= 0) {
703  WRITE_WARNINGF(TL("Skipping edge '%' because it has speed %."), id, speed);
704  ok = false;
705  }
706  // deal with cycleways that run in the opposite direction of a one-way street
707  WayType cyclewayType = e->myCyclewayType; // make a copy because we do some temporary modifications
708  if (addBikeLane) {
709  if (!addForward && (cyclewayType & WAY_FORWARD) != 0) {
710  addForward = true;
711  forwardPermissions = SVC_BICYCLE;
712  forwardWidth = bikeLaneWidth;
713  numLanesForward = 1;
714  // do not add an additional cycle lane
715  cyclewayType = (WayType)(cyclewayType & ~WAY_FORWARD);
716  }
717  if (!addBackward && (cyclewayType & WAY_BACKWARD) != 0) {
718  addBackward = true;
719  backwardPermissions = SVC_BICYCLE;
720  backwardWidth = bikeLaneWidth;
721  numLanesBackward = 1;
722  // do not add an additional cycle lane
723  cyclewayType = (WayType)(cyclewayType & ~WAY_BACKWARD);
724  }
725  }
726  // deal with sidewalks that run in the opposite direction of a one-way street
727  WayType sidewalkType = e->mySidewalkType; // make a copy because we do some temporary modifications
728  if (sidewalkType == WAY_UNKNOWN && (e->myExtraAllowed & SVC_PEDESTRIAN) != 0 && (permissions & SVC_PASSENGER) != 0) {
729  // do not assume shared space unless sidewalk is actively disabled
730  sidewalkType = WAY_BOTH;
731  }
732  if (addSidewalk || (myImportSidewalks && (permissions & SVC_ROAD_CLASSES) != 0 && defaultPermissions != SVC_PEDESTRIAN)) {
733  if (!addForward && (sidewalkType & WAY_FORWARD) != 0) {
734  addForward = true;
735  forwardPermissions = SVC_PEDESTRIAN;
736  forwardWidth = tc.getEdgeTypeSidewalkWidth(type);
737  numLanesForward = 1;
738  // do not add an additional sidewalk
739  sidewalkType = (WayType)(sidewalkType & ~WAY_FORWARD); //clang tidy thinks "!WAY_FORWARD" is always false
740  } else if (addSidewalk && addForward && (sidewalkType & WAY_BOTH) == 0
741  && numLanesForward == 1 && numLanesBackward <= 1
742  && (e->myExtraDisallowed & SVC_PEDESTRIAN) == 0) {
743  // our typemap says pedestrians should walk here but the data says
744  // there is no sidewalk at all. If the road is small, pedestrians can just walk
745  // on the road
746  forwardPermissions |= SVC_PEDESTRIAN;
747  }
748  if (!addBackward && (sidewalkType & WAY_BACKWARD) != 0) {
749  addBackward = true;
750  backwardPermissions = SVC_PEDESTRIAN;
751  backwardWidth = tc.getEdgeTypeSidewalkWidth(type);
752  numLanesBackward = 1;
753  // do not add an additional cycle lane
754  sidewalkType = (WayType)(sidewalkType & ~WAY_BACKWARD); //clang tidy thinks "!WAY_BACKWARD" is always false
755  } else if (addSidewalk && addBackward && (sidewalkType & WAY_BOTH) == 0
756  && numLanesBackward == 1 && numLanesForward <= 1
757  && (e->myExtraDisallowed & SVC_PEDESTRIAN) == 0) {
758  // our typemap says pedestrians should walk here but the data says
759  // there is no sidewalk at all. If the road is small, pedestrians can just walk
760  // on the road
761  backwardPermissions |= SVC_PEDESTRIAN;
762  }
763  }
764 
765  const std::string origID = OptionsCont::getOptions().getBool("output.original-names") ? toString(e->id) : "";
766  if (ok) {
767  const bool lefthand = OptionsCont::getOptions().getBool("lefthand");
768  const int offsetFactor = lefthand ? -1 : 1;
769  LaneSpreadFunction lsf = (addBackward || OptionsCont::getOptions().getBool("osm.oneway-spread-right")) &&
771  if (addBackward && lsf == LaneSpreadFunction::RIGHT && OptionsCont::getOptions().getString("default.spreadtype") == toString(LaneSpreadFunction::ROADCENTER)) {
773  }
775  // user defined value overrides defaults
776  lsf = tc.getEdgeTypeSpreadType(type);
777  }
778 
779  id = StringUtils::escapeXML(id);
780  const std::string reverseID = "-" + id;
781  const bool markOSMDirection = from->getType() == SumoXMLNodeType::RAIL_SIGNAL || to->getType() == SumoXMLNodeType::RAIL_SIGNAL;
782  if (addForward) {
783  assert(numLanesForward > 0);
784  NBEdge* nbe = new NBEdge(id, from, to, type, speed, NBEdge::UNSPECIFIED_FRICTION, numLanesForward, tc.getEdgeTypePriority(type),
785  forwardWidth, NBEdge::UNSPECIFIED_OFFSET, shape, lsf,
786  StringUtils::escapeXML(streetName), origID, true);
787  if (markOSMDirection) {
789  }
790  nbe->setPermissions(forwardPermissions, -1);
791  if ((e->myBuswayType & WAY_FORWARD) != 0) {
792  nbe->setPermissions(SVC_BUS, 0);
793  }
795  applyLaneUse(nbe, e, true);
797  nbe->setTurnSignTarget(last->getID());
798  if (addBikeLane && (cyclewayType == WAY_UNKNOWN || (cyclewayType & WAY_FORWARD) != 0)) {
799  nbe->addBikeLane(bikeLaneWidth * offsetFactor);
800  } else if (nbe->getPermissions(0) == SVC_BUS) {
801  // bikes drive on buslanes if no separate cycle lane is available
803  }
804  if ((addSidewalk && (sidewalkType == WAY_UNKNOWN || (sidewalkType & WAY_FORWARD) != 0))
805  || (myImportSidewalks && (sidewalkType & WAY_FORWARD) != 0 && defaultPermissions != SVC_PEDESTRIAN)) {
806  nbe->addSidewalk(sidewalkWidth * offsetFactor);
807  }
808  if (!addBackward && (e->myExtraAllowed & SVC_PEDESTRIAN) != 0 && (nbe->getPermissions(0) & SVC_PEDESTRIAN) == 0) {
809  // Pedestrians are explicitly allowed (maybe through foot="yes") but did not get a sidewalk (maybe through sidewalk="no").
810  // Since we do not have a backward edge, we need to make sure they can at least walk somewhere, see #14124
811  nbe->setPermissions(nbe->getPermissions(0) | SVC_PEDESTRIAN, 0);
812  }
814  nbe->setDistance(distanceStart);
815  if (e->myAmInRoundabout) {
816  // ensure roundabout edges have the precedence
817  nbe->setJunctionPriority(to, NBEdge::JunctionPriority::ROUNDABOUT);
818  nbe->setJunctionPriority(from, NBEdge::JunctionPriority::ROUNDABOUT);
819  }
820 
821  // process forward lanes width
822  const int numForwardLanesFromWidthKey = (int)e->myWidthLanesForward.size();
823  if (numForwardLanesFromWidthKey > 0 && !OptionsCont::getOptions().getBool("ignore-widths")) {
824  if ((int)nbe->getLanes().size() != numForwardLanesFromWidthKey) {
825  WRITE_WARNINGF(TL("Forward lanes count for edge '%' ('%') is not matching the number of lanes defined in width:lanes:forward key ('%'). Using default width values."),
826  id, nbe->getLanes().size(), numForwardLanesFromWidthKey);
827  } else {
828  for (int i = 0; i < numForwardLanesFromWidthKey; i++) {
829  const double actualWidth = e->myWidthLanesForward[i] <= 0 ? forwardWidth : e->myWidthLanesForward[i];
830  const int laneIndex = lefthand ? i : numForwardLanesFromWidthKey - i - 1;
831  nbe->setLaneWidth(laneIndex, actualWidth);
832  }
833  }
834  }
835 
836  if (!ec.insert(nbe)) {
837  delete nbe;
838  throw ProcessError(TLF("Could not add edge '%'.", id));
839  }
840  }
841  if (addBackward) {
842  assert(numLanesBackward > 0);
843  NBEdge* nbe = new NBEdge(reverseID, to, from, type, speedBackward, NBEdge::UNSPECIFIED_FRICTION, numLanesBackward, tc.getEdgeTypePriority(type),
844  backwardWidth, NBEdge::UNSPECIFIED_OFFSET, shape.reverse(), lsf,
845  StringUtils::escapeXML(streetName), origID, true);
846  if (markOSMDirection) {
848  }
849  nbe->setPermissions(backwardPermissions);
850  if ((e->myBuswayType & WAY_BACKWARD) != 0) {
851  nbe->setPermissions(SVC_BUS, 0);
852  }
854  applyLaneUse(nbe, e, false);
856  nbe->setTurnSignTarget(first->getID());
857  if (addBikeLane && (cyclewayType == WAY_UNKNOWN || (cyclewayType & WAY_BACKWARD) != 0)) {
858  nbe->addBikeLane(bikeLaneWidth * offsetFactor);
859  } else if (nbe->getPermissions(0) == SVC_BUS) {
860  // bikes drive on buslanes if no separate cycle lane is available
862  }
863  if ((addSidewalk && (sidewalkType == WAY_UNKNOWN || (sidewalkType & WAY_BACKWARD) != 0))
864  || (myImportSidewalks && (sidewalkType & WAY_BACKWARD) != 0 && defaultPermissions != SVC_PEDESTRIAN)) {
865  nbe->addSidewalk(sidewalkWidth * offsetFactor);
866  }
868  nbe->setDistance(distanceEnd);
869  if (e->myAmInRoundabout) {
870  // ensure roundabout edges have the precedence
871  nbe->setJunctionPriority(from, NBEdge::JunctionPriority::ROUNDABOUT);
872  nbe->setJunctionPriority(to, NBEdge::JunctionPriority::ROUNDABOUT);
873  }
874  // process backward lanes width
875  const int numBackwardLanesFromWidthKey = (int)e->myWidthLanesBackward.size();
876  if (numBackwardLanesFromWidthKey > 0 && !OptionsCont::getOptions().getBool("ignore-widths")) {
877  if ((int)nbe->getLanes().size() != numBackwardLanesFromWidthKey) {
878  WRITE_WARNINGF(TL("Backward lanes count for edge '%' ('%') is not matching the number of lanes defined in width:lanes:backward key ('%'). Using default width values."),
879  id, nbe->getLanes().size(), numBackwardLanesFromWidthKey);
880  } else {
881  for (int i = 0; i < numBackwardLanesFromWidthKey; i++) {
882  const double actualWidth = e->myWidthLanesBackward[i] <= 0 ? backwardWidth : e->myWidthLanesBackward[i];
883  const int laneIndex = lefthand ? i : numBackwardLanesFromWidthKey - i - 1;
884  nbe->setLaneWidth(laneIndex, actualWidth);
885  }
886  }
887  }
888 
889  if (!ec.insert(nbe)) {
890  delete nbe;
891  throw ProcessError(TLF("Could not add edge '-%'.", id));
892  }
893  }
894  if ((e->myParkingType & PARKING_BOTH) != 0 && OptionsCont::getOptions().isSet("parking-output")) {
895  if ((e->myParkingType & PARKING_RIGHT) != 0) {
896  if (addForward) {
897  nb.getParkingCont().push_back(NBParking(id, id));
898  } else {
900  if ((e->myParkingType & PARKING_LEFT) == 0 && !addBackward) {
902  nb.getParkingCont().push_back(NBParking(reverseID, reverseID));
903  }
904  }
905  }
906  if ((e->myParkingType & PARKING_LEFT) != 0) {
907  if (addBackward) {
908  nb.getParkingCont().push_back(NBParking(reverseID, reverseID));
909  } else {
911  if ((e->myParkingType & PARKING_RIGHT) == 0 && !addForward) {
913  nb.getParkingCont().push_back(NBParking(id, id));
914  }
915  }
916  }
917  }
918  }
919  return newIndex;
920 }
921 
922 
923 // ---------------------------------------------------------------------------
924 // definitions of NIImporter_OpenStreetMap::NodesHandler-methods
925 // ---------------------------------------------------------------------------
926 NIImporter_OpenStreetMap::NodesHandler::NodesHandler(std::map<long long int, NIOSMNode*>& toFill,
927  std::set<NIOSMNode*, CompareNodes>& uniqueNodes, const OptionsCont& oc) :
928  SUMOSAXHandler("osm - file"),
929  myToFill(toFill),
930  myCurrentNode(nullptr),
931  myHierarchyLevel(0),
932  myUniqueNodes(uniqueNodes),
933  myImportElevation(oc.getBool("osm.elevation")),
934  myDuplicateNodes(0),
935  myOptionsCont(oc) {
936  // init rail signal rules
937  for (std::string kv : oc.getStringVector("osm.railsignals")) {
938  if (kv == "DEFAULT") {
939  myRailSignalRules.push_back("railway:signal:main=");
940  myRailSignalRules.push_back("railway:signal:combined=");
941  } else if (kv == "ALL") {
942  myRailSignalRules.push_back("railway=signal");
943  } else {
944  myRailSignalRules.push_back("railway:signal:" + kv);
945  }
946  }
947 }
948 
949 
951 
952 void
954  ++myHierarchyLevel;
955  if (element == SUMO_TAG_NODE) {
956  bool ok = true;
957  myLastNodeID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
958  if (myHierarchyLevel != 2) {
959  WRITE_ERROR("Node element on wrong XML hierarchy level (id='" + myLastNodeID +
960  "', level='" + toString(myHierarchyLevel) + "').");
961  return;
962  }
963  const std::string& action = attrs.getOpt<std::string>(SUMO_ATTR_ACTION, myLastNodeID.c_str(), ok);
964  if (action == "delete" || !ok) {
965  return;
966  }
967  try {
968  // we do not use attrs.get here to save some time on parsing
969  const long long int id = StringUtils::toLong(myLastNodeID);
970  myCurrentNode = nullptr;
971  const auto insertionIt = myToFill.lower_bound(id);
972  if (insertionIt == myToFill.end() || insertionIt->first != id) {
973  // assume we are loading multiple files, so we won't report duplicate nodes
974  const double tlon = attrs.get<double>(SUMO_ATTR_LON, myLastNodeID.c_str(), ok);
975  const double tlat = attrs.get<double>(SUMO_ATTR_LAT, myLastNodeID.c_str(), ok);
976  if (!ok) {
977  return;
978  }
979  myCurrentNode = new NIOSMNode(id, tlon, tlat);
980  auto similarNode = myUniqueNodes.find(myCurrentNode);
981  if (similarNode == myUniqueNodes.end()) {
982  myUniqueNodes.insert(myCurrentNode);
983  } else {
984  delete myCurrentNode;
985  myCurrentNode = *similarNode;
986  myDuplicateNodes++;
987  }
988  myToFill.emplace_hint(insertionIt, id, myCurrentNode);
989  }
990  } catch (FormatException&) {
991  WRITE_ERROR(TL("Attribute 'id' in the definition of a node is not of type long long int."));
992  return;
993  }
994  }
995  if (element == SUMO_TAG_TAG && myCurrentNode != nullptr) {
996  if (myHierarchyLevel != 3) {
997  WRITE_ERROR(TL("Tag element on wrong XML hierarchy level."));
998  return;
999  }
1000  bool ok = true;
1001  const std::string& key = attrs.get<std::string>(SUMO_ATTR_K, myLastNodeID.c_str(), ok, false);
1002  // we check whether the key is relevant (and we really need to transcode the value) to avoid hitting #1636
1003  if (key == "highway" || key == "ele" || key == "crossing" || key == "railway" || key == "public_transport"
1004  || key == "name" || key == "train" || key == "bus" || key == "tram" || key == "light_rail" || key == "subway" || key == "station" || key == "noexit"
1005  || key == "crossing:barrier"
1006  || key == "crossing:light"
1007  || StringUtils::startsWith(key, "railway:signal")
1008  || StringUtils::startsWith(key, "railway:position")
1009  ) {
1010  const std::string& value = attrs.get<std::string>(SUMO_ATTR_V, myLastNodeID.c_str(), ok, false);
1011  if (key == "highway" && value.find("traffic_signal") != std::string::npos) {
1012  myCurrentNode->tlsControlled = true;
1013  } else if (key == "crossing" && value.find("traffic_signals") != std::string::npos) {
1014  myCurrentNode->tlsControlled = true;
1015  } else if (key == "highway" && value.find("crossing") != std::string::npos) {
1016  myCurrentNode->pedestrianCrossing = true;
1017  } else if ((key == "noexit" && value == "yes")
1018  || (key == "railway" && value == "buffer_stop")) {
1019  myCurrentNode->railwayBufferStop = true;
1020  } else if (key == "railway" && value.find("crossing") != std::string::npos) {
1021  myCurrentNode->railwayCrossing = true;
1022  } else if (key == "crossing:barrier") {
1023  myCurrentNode->setParameter("crossing:barrier", value);
1024  } else if (key == "crossing:light") {
1025  myCurrentNode->setParameter("crossing:light", value);
1026  } else if (key == "railway:signal:direction") {
1027  if (value == "both") {
1028  myCurrentNode->myRailDirection = WAY_BOTH;
1029  } else if (value == "backward") {
1030  myCurrentNode->myRailDirection = WAY_BACKWARD;
1031  } else if (value == "forward") {
1032  myCurrentNode->myRailDirection = WAY_FORWARD;
1033  }
1034  } else if (StringUtils::startsWith(key, "railway:signal") || (key == "railway" && value == "signal")) {
1035  std::string kv = key + "=" + value;
1036  std::string kglob = key + "=";
1037  if ((std::find(myRailSignalRules.begin(), myRailSignalRules.end(), kv) != myRailSignalRules.end())
1038  || (std::find(myRailSignalRules.begin(), myRailSignalRules.end(), kglob) != myRailSignalRules.end())) {
1039  myCurrentNode->railwaySignal = true;
1040  }
1041  } else if (StringUtils::startsWith(key, "railway:position") && value.size() > myCurrentNode->position.size()) {
1042  // use the entry with the highest precision (more digits)
1043  myCurrentNode->position = value;
1044  } else if ((key == "public_transport" && value == "stop_position") ||
1045  (key == "highway" && value == "bus_stop")) {
1046  myCurrentNode->ptStopPosition = true;
1047  if (myCurrentNode->ptStopLength == 0) {
1048  // default length
1049  myCurrentNode->ptStopLength = myOptionsCont.getFloat("osm.stop-output.length");
1050  }
1051  } else if (key == "name") {
1052  myCurrentNode->name = value;
1053  } else if (myImportElevation && key == "ele") {
1054  try {
1055  const double elevation = StringUtils::parseDist(value);
1056  if (std::isnan(elevation)) {
1057  WRITE_WARNINGF(TL("Value of key '%' is invalid ('%') in node '%'."), key, value, myLastNodeID);
1058  } else {
1059  myCurrentNode->ele = elevation;
1060  }
1061  } catch (...) {
1062  WRITE_WARNINGF(TL("Value of key '%' is not numeric ('%') in node '%'."), key, value, myLastNodeID);
1063  }
1064  } else if (key == "station") {
1065  interpretTransportType(value, myCurrentNode);
1066  } else {
1067  // v="yes"
1068  interpretTransportType(key, myCurrentNode);
1069  }
1070  }
1071  if (myAllAttributes && (myExtraAttributes.count(key) != 0 || myExtraAttributes.size() == 0)) {
1072  const std::string info = "node=" + toString(myCurrentNode->id) + ", k=" + key;
1073  myCurrentNode->setParameter(key, attrs.get<std::string>(SUMO_ATTR_V, info.c_str(), ok, false));
1074  }
1075  }
1076 }
1077 
1078 
1079 void
1081  if (element == SUMO_TAG_NODE && myHierarchyLevel == 2) {
1082  myCurrentNode = nullptr;
1083  }
1084  --myHierarchyLevel;
1085 }
1086 
1087 
1088 // ---------------------------------------------------------------------------
1089 // definitions of NIImporter_OpenStreetMap::EdgesHandler-methods
1090 // ---------------------------------------------------------------------------
1092  const std::map<long long int, NIOSMNode*>& osmNodes,
1093  std::map<long long int, Edge*>& toFill, std::map<long long int, Edge*>& platformShapes):
1094  SUMOSAXHandler("osm - file"),
1095  myOSMNodes(osmNodes),
1096  myEdgeMap(toFill),
1097  myPlatformShapesMap(platformShapes) {
1098 
1099  const double unlimitedSpeed = OptionsCont::getOptions().getFloat("osm.speedlimit-none");
1100 
1101  mySpeedMap["nan"] = MAXSPEED_UNGIVEN;
1102  mySpeedMap["sign"] = MAXSPEED_UNGIVEN;
1103  mySpeedMap["signals"] = MAXSPEED_UNGIVEN;
1104  mySpeedMap["none"] = unlimitedSpeed;
1105  mySpeedMap["no"] = unlimitedSpeed;
1106  mySpeedMap["walk"] = 5. / 3.6;
1107  // https://wiki.openstreetmap.org/wiki/Key:source:maxspeed#Commonly_used_values
1108  mySpeedMap["AT:urban"] = 50. / 3.6;
1109  mySpeedMap["AT:rural"] = 100. / 3.6;
1110  mySpeedMap["AT:trunk"] = 100. / 3.6;
1111  mySpeedMap["AT:motorway"] = 130. / 3.6;
1112  mySpeedMap["AU:urban"] = 50. / 3.6;
1113  mySpeedMap["BE:urban"] = 50. / 3.6;
1114  mySpeedMap["BE:zone"] = 30. / 3.6;
1115  mySpeedMap["BE:motorway"] = 120. / 3.6;
1116  mySpeedMap["BE:zone30"] = 30. / 3.6;
1117  mySpeedMap["BE-VLG:rural"] = 70. / 3.6;
1118  mySpeedMap["BE-WAL:rural"] = 90. / 3.6;
1119  mySpeedMap["BE:school"] = 30. / 3.6;
1120  mySpeedMap["CZ:motorway"] = 130. / 3.6;
1121  mySpeedMap["CZ:trunk"] = 110. / 3.6;
1122  mySpeedMap["CZ:rural"] = 90. / 3.6;
1123  mySpeedMap["CZ:urban_motorway"] = 80. / 3.6;
1124  mySpeedMap["CZ:urban_trunk"] = 80. / 3.6;
1125  mySpeedMap["CZ:urban"] = 50. / 3.6;
1126  mySpeedMap["DE:motorway"] = unlimitedSpeed;
1127  mySpeedMap["DE:rural"] = 100. / 3.6;
1128  mySpeedMap["DE:urban"] = 50. / 3.6;
1129  mySpeedMap["DE:bicycle_road"] = 30. / 3.6;
1130  mySpeedMap["DK:motorway"] = 130. / 3.6;
1131  mySpeedMap["DK:rural"] = 80. / 3.6;
1132  mySpeedMap["DK:urban"] = 50. / 3.6;
1133  mySpeedMap["EE:urban"] = 50. / 3.6;
1134  mySpeedMap["EE:rural"] = 90. / 3.6;
1135  mySpeedMap["ES:urban"] = 50. / 3.6;
1136  mySpeedMap["ES:zone30"] = 30. / 3.6;
1137  mySpeedMap["FR:motorway"] = 130. / 3.6; // 110 (raining)
1138  mySpeedMap["FR:rural"] = 80. / 3.6;
1139  mySpeedMap["FR:urban"] = 50. / 3.6;
1140  mySpeedMap["FR:zone30"] = 30. / 3.6;
1141  mySpeedMap["HU:living_street"] = 20. / 3.6;
1142  mySpeedMap["HU:motorway"] = 130. / 3.6;
1143  mySpeedMap["HU:rural"] = 90. / 3.6;
1144  mySpeedMap["HU:trunk"] = 110. / 3.6;
1145  mySpeedMap["HU:urban"] = 50. / 3.6;
1146  mySpeedMap["IT:rural"] = 90. / 3.6;
1147  mySpeedMap["IT:motorway"] = 130. / 3.6;
1148  mySpeedMap["IT:urban"] = 50. / 3.6;
1149  mySpeedMap["JP:nsl"] = 60. / 3.6;
1150  mySpeedMap["JP:express"] = 100. / 3.6;
1151  mySpeedMap["LT:rural"] = 90. / 3.6;
1152  mySpeedMap["LT:urban"] = 50. / 3.6;
1153  mySpeedMap["NO:rural"] = 80. / 3.6;
1154  mySpeedMap["NO:urban"] = 50. / 3.6;
1155  mySpeedMap["ON:urban"] = 50. / 3.6;
1156  mySpeedMap["ON:rural"] = 80. / 3.6;
1157  mySpeedMap["PT:motorway"] = 120. / 3.6;
1158  mySpeedMap["PT:rural"] = 90. / 3.6;
1159  mySpeedMap["PT:trunk"] = 100. / 3.6;
1160  mySpeedMap["PT:urban"] = 50. / 3.6;
1161  mySpeedMap["RO:motorway"] = 130. / 3.6;
1162  mySpeedMap["RO:rural"] = 90. / 3.6;
1163  mySpeedMap["RO:trunk"] = 100. / 3.6;
1164  mySpeedMap["RO:urban"] = 50. / 3.6;
1165  mySpeedMap["RS:living_street"] = 30. / 3.6;
1166  mySpeedMap["RS:motorway"] = 130. / 3.6;
1167  mySpeedMap["RS:rural"] = 80. / 3.6;
1168  mySpeedMap["RS:trunk"] = 100. / 3.6;
1169  mySpeedMap["RS:urban"] = 50. / 3.6;
1170  mySpeedMap["RU:living_street"] = 20. / 3.6;
1171  mySpeedMap["RU:urban"] = 60. / 3.6;
1172  mySpeedMap["RU:rural"] = 90. / 3.6;
1173  mySpeedMap["RU:motorway"] = 110. / 3.6;
1174  const double seventy = StringUtils::parseSpeed("70mph");
1175  const double sixty = StringUtils::parseSpeed("60mph");
1176  mySpeedMap["GB:motorway"] = seventy;
1177  mySpeedMap["GB:nsl_dual"] = seventy;
1178  mySpeedMap["GB:nsl_single"] = sixty;
1179  mySpeedMap["UK:motorway"] = seventy;
1180  mySpeedMap["UK:nsl_dual"] = seventy;
1181  mySpeedMap["UK:nsl_single"] = sixty;
1182  mySpeedMap["UZ:living_street"] = 30. / 3.6;
1183  mySpeedMap["UZ:urban"] = 70. / 3.6;
1184  mySpeedMap["UZ:rural"] = 100. / 3.6;
1185  mySpeedMap["UZ:motorway"] = 110. / 3.6;
1186 }
1187 
1189 
1190 void
1192  if (element == SUMO_TAG_WAY) {
1193  bool ok = true;
1194  const long long int id = attrs.get<long long int>(SUMO_ATTR_ID, nullptr, ok);
1195  const std::string& action = attrs.getOpt<std::string>(SUMO_ATTR_ACTION, nullptr, ok);
1196  if (action == "delete" || !ok) {
1197  myCurrentEdge = nullptr;
1198  return;
1199  }
1200  myCurrentEdge = new Edge(id);
1201  }
1202  // parse "nd" (node) elements
1203  if (element == SUMO_TAG_ND && myCurrentEdge != nullptr) {
1204  bool ok = true;
1205  long long int ref = attrs.get<long long int>(SUMO_ATTR_REF, nullptr, ok);
1206  if (ok) {
1207  auto node = myOSMNodes.find(ref);
1208  if (node == myOSMNodes.end()) {
1209  WRITE_WARNINGF(TL("The referenced geometry information (ref='%') is not known"), toString(ref));
1210  return;
1211  }
1212 
1213  ref = node->second->id; // node may have been substituted
1214  if (myCurrentEdge->myCurrentNodes.empty() ||
1215  myCurrentEdge->myCurrentNodes.back() != ref) { // avoid consecutive duplicates
1216  myCurrentEdge->myCurrentNodes.push_back(ref);
1217  }
1218 
1219  }
1220  }
1221  if (element == SUMO_TAG_TAG && myCurrentEdge != nullptr) {
1222  bool ok = true;
1223  std::string key = attrs.get<std::string>(SUMO_ATTR_K, toString(myCurrentEdge->id).c_str(), ok, false);
1224  if (key.size() > 6 && StringUtils::startsWith(key, "busway:")) {
1225  // handle special busway keys
1226  const std::string buswaySpec = key.substr(7);
1227  key = "busway";
1228  if (buswaySpec == "right") {
1229  myCurrentEdge->myBuswayType = (WayType)(myCurrentEdge->myBuswayType | WAY_FORWARD);
1230  } else if (buswaySpec == "left") {
1231  myCurrentEdge->myBuswayType = (WayType)(myCurrentEdge->myBuswayType | WAY_BACKWARD);
1232  } else if (buswaySpec == "both") {
1233  myCurrentEdge->myBuswayType = (WayType)(myCurrentEdge->myBuswayType | WAY_BOTH);
1234  } else {
1235  key = "ignore";
1236  }
1237  }
1238  if (myAllAttributes && (myExtraAttributes.count(key) != 0 || myExtraAttributes.size() == 0)) {
1239  const std::string info = "way=" + toString(myCurrentEdge->id) + ", k=" + key;
1240  myCurrentEdge->setParameter(key, attrs.get<std::string>(SUMO_ATTR_V, info.c_str(), ok, false));
1241  }
1242  // we check whether the key is relevant (and we really need to transcode the value) to avoid hitting #1636
1243  if (!StringUtils::endsWith(key, "way")
1244  && !StringUtils::startsWith(key, "lanes")
1245  && key != "maxspeed" && key != "maxspeed:type"
1246  && key != "zone:maxspeed"
1247  && key != "maxspeed:forward" && key != "maxspeed:backward"
1248  && key != "junction" && key != "name" && key != "tracks" && key != "layer"
1249  && key != "route"
1250  && !StringUtils::startsWith(key, "cycleway")
1251  && !StringUtils::startsWith(key, "sidewalk")
1252  && key != "ref"
1253  && key != "highspeed"
1254  && !StringUtils::startsWith(key, "parking")
1255  && !StringUtils::startsWith(key, "change")
1256  && !StringUtils::startsWith(key, "vehicle:lanes")
1257  && key != "postal_code"
1258  && key != "railway:preferred_direction"
1259  && key != "railway:bidirectional"
1260  && key != "railway:track_ref"
1261  && key != "usage"
1262  && key != "access"
1263  && key != "emergency"
1264  && key != "service"
1265  && key != "electrified"
1266  && key != "segregated"
1267  && key != "bus"
1268  && key != "psv"
1269  && key != "foot"
1270  && key != "bicycle"
1271  && key != "oneway:bicycle"
1272  && key != "oneway:bus"
1273  && key != "bus:lanes"
1274  && key != "bus:lanes:forward"
1275  && key != "bus:lanes:backward"
1276  && key != "psv:lanes"
1277  && key != "psv:lanes:forward"
1278  && key != "psv:lanes:backward"
1279  && key != "bicycle:lanes"
1280  && key != "bicycle:lanes:forward"
1281  && key != "bicycle:lanes:backward"
1282  && !StringUtils::startsWith(key, "width")
1283  && !(StringUtils::startsWith(key, "turn:") && key.find(":lanes") != std::string::npos)
1284  && key != "public_transport") {
1285  return;
1286  }
1287  const std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentEdge->id).c_str(), ok, false);
1288 
1289  if ((key == "highway" && value != "platform") || key == "railway" || key == "waterway" || StringUtils::startsWith(key, "cycleway")
1290  || key == "busway" || key == "route" || StringUtils::startsWith(key, "sidewalk") || key == "highspeed"
1291  || key == "aeroway" || key == "aerialway" || key == "usage" || key == "service") {
1292  // build type id
1293  myCurrentEdge->myCurrentIsRoad = true;
1294  // special cycleway stuff https://wiki.openstreetmap.org/wiki/Key:cycleway
1295  if (key == "cycleway") {
1296  if (value == "no" || value == "none" || value == "separate") {
1297  myCurrentEdge->myCyclewayType = WAY_NONE;
1298  } else if (value == "both") {
1299  myCurrentEdge->myCyclewayType = WAY_BOTH;
1300  } else if (value == "right") {
1301  myCurrentEdge->myCyclewayType = WAY_FORWARD;
1302  } else if (value == "left") {
1303  myCurrentEdge->myCyclewayType = WAY_BACKWARD;
1304  } else if (value == "opposite_track") {
1305  myCurrentEdge->myCyclewayType = WAY_BACKWARD;
1306  } else if (value == "opposite_lane") {
1307  myCurrentEdge->myCyclewayType = WAY_BACKWARD;
1308  } else if (value == "opposite") {
1309  // according to the wiki ref above, this should rather be a bidi lane, see #13438
1310  myCurrentEdge->myCyclewayType = WAY_BACKWARD;
1311  }
1312  }
1313  if (key == "cycleway:left") {
1314  if (myCurrentEdge->myCyclewayType == WAY_UNKNOWN) {
1315  myCurrentEdge->myCyclewayType = WAY_NONE;
1316  }
1317  if (value == "yes" || value == "lane" || value == "track") {
1318  myCurrentEdge->myCyclewayType = (WayType)(myCurrentEdge->myCyclewayType | WAY_BACKWARD);
1319  }
1320  key = "cycleway"; // for type adaption
1321  }
1322  if (key == "cycleway:right") {
1323  if (myCurrentEdge->myCyclewayType == WAY_UNKNOWN) {
1324  myCurrentEdge->myCyclewayType = WAY_NONE;
1325  }
1326  if (value == "yes" || value == "lane" || value == "track") {
1327  myCurrentEdge->myCyclewayType = (WayType)(myCurrentEdge->myCyclewayType | WAY_FORWARD);
1328  }
1329  key = "cycleway"; // for type adaption
1330  }
1331  if (key == "cycleway:both") {
1332  if (myCurrentEdge->myCyclewayType == WAY_UNKNOWN) {
1333  if (value == "no" || value == "none" || value == "separate") {
1334  myCurrentEdge->myCyclewayType = WAY_NONE;
1335  }
1336  if (value == "yes" || value == "lane" || value == "track") {
1337  myCurrentEdge->myCyclewayType = WAY_BOTH;
1338  }
1339  }
1340  key = "cycleway"; // for type adaption
1341  }
1342  if (key == "cycleway" && value != "lane" && value != "track" && value != "opposite_track" && value != "opposite_lane") {
1343  // typemap covers only the lane and track cases
1344  return;
1345  }
1346  if (StringUtils::startsWith(key, "cycleway:")) {
1347  // no need to extend the type id for other cycleway sub tags
1348  return;
1349  }
1350  // special sidewalk stuff
1351  if (key == "sidewalk") {
1352  if (value == "no" || value == "none" || value == "separate") {
1353  myCurrentEdge->mySidewalkType = WAY_NONE;
1354  } else if (value == "both") {
1355  myCurrentEdge->mySidewalkType = WAY_BOTH;
1356  } else if (value == "right") {
1357  myCurrentEdge->mySidewalkType = WAY_FORWARD;
1358  } else if (value == "left") {
1359  myCurrentEdge->mySidewalkType = WAY_BACKWARD;
1360  }
1361  }
1362  if (key == "sidewalk:left") {
1363  if (myCurrentEdge->mySidewalkType == WAY_UNKNOWN) {
1364  myCurrentEdge->mySidewalkType = WAY_NONE;
1365  }
1366  if (value == "yes") {
1367  myCurrentEdge->mySidewalkType = (WayType)(myCurrentEdge->mySidewalkType | WAY_BACKWARD);
1368  }
1369  }
1370  if (key == "sidewalk:right") {
1371  if (myCurrentEdge->mySidewalkType == WAY_UNKNOWN) {
1372  myCurrentEdge->mySidewalkType = WAY_NONE;
1373  }
1374  if (value == "yes") {
1375  myCurrentEdge->mySidewalkType = (WayType)(myCurrentEdge->mySidewalkType | WAY_FORWARD);
1376  }
1377  }
1378  if (key == "sidewalk:both") {
1379  if (myCurrentEdge->mySidewalkType == WAY_UNKNOWN) {
1380  if (value == "no" || value == "none" || value == "separate") {
1381  myCurrentEdge->mySidewalkType = WAY_NONE;
1382  }
1383  if (value == "yes") {
1384  myCurrentEdge->mySidewalkType = WAY_BOTH;
1385  }
1386  }
1387  }
1388  if (StringUtils::startsWith(key, "sidewalk")) {
1389  // no need to extend the type id
1390  return;
1391  }
1392  // special busway stuff
1393  if (key == "busway") {
1394  if (value == "no") {
1395  return;
1396  }
1397  if (value == "opposite_track") {
1398  myCurrentEdge->myBuswayType = WAY_BACKWARD;
1399  } else if (value == "opposite_lane") {
1400  myCurrentEdge->myBuswayType = WAY_BACKWARD;
1401  }
1402  // no need to extend the type id
1403  return;
1404  }
1405  std::string singleTypeID = key + "." + value;
1406  if (key == "highspeed") {
1407  if (value == "no") {
1408  return;
1409  }
1410  singleTypeID = "railway.highspeed";
1411  }
1412  // special case: never build compound type for highspeed rail
1413  if (!myCurrentEdge->myHighWayType.empty() && singleTypeID != "railway.highspeed") {
1414  if (myCurrentEdge->myHighWayType == "railway.highspeed") {
1415  return;
1416  }
1417  // osm-ways may be used by more than one mode (eg railway.tram + highway.residential. this is relevant for multimodal traffic)
1418  // we create a new type for this kind of situation which must then be resolved in insertEdge()
1419  std::vector<std::string> types = StringTokenizer(myCurrentEdge->myHighWayType,
1421  types.push_back(singleTypeID);
1422  myCurrentEdge->myHighWayType = joinToStringSorting(types, compoundTypeSeparator);
1423  } else {
1424  myCurrentEdge->myHighWayType = singleTypeID;
1425  }
1426  } else if (key == "bus" || key == "psv") {
1427  // 'psv' includes taxi in the UK but not in germany
1428  try {
1429  if (StringUtils::toBool(value)) {
1430  myCurrentEdge->myExtraAllowed |= SVC_BUS;
1431  } else {
1432  myCurrentEdge->myExtraDisallowed |= SVC_BUS;
1433  }
1434  } catch (const BoolFormatException&) {
1435  myCurrentEdge->myExtraAllowed |= SVC_BUS;
1436  }
1437  } else if (key == "emergency") {
1438  try {
1439  if (StringUtils::toBool(value)) {
1440  myCurrentEdge->myExtraAllowed |= SVC_AUTHORITY | SVC_EMERGENCY;
1441  }
1442  } catch (const BoolFormatException&) {
1443  myCurrentEdge->myExtraAllowed |= SVC_AUTHORITY | SVC_EMERGENCY;
1444  }
1445  } else if (key == "access") {
1446  if (value == "no") {
1447  myCurrentEdge->myExtraDisallowed |= ~(SVC_PUBLIC_CLASSES | SVC_EMERGENCY | SVC_AUTHORITY);
1448  }
1449  } else if (StringUtils::startsWith(key, "width:lanes")) {
1450  try {
1451  const std::vector<std::string> values = StringTokenizer(value, "|").getVector();
1452  std::vector<double> widthLanes;
1453  for (std::string width : values) {
1454  const double parsedWidth = width == "" ? -1 : StringUtils::parseDist(width);
1455  widthLanes.push_back(parsedWidth);
1456  }
1457 
1458  if (key == "width:lanes" || key == "width:lanes:forward") {
1459  myCurrentEdge->myWidthLanesForward = widthLanes;
1460  } else if (key == "width:lanes:backward") {
1461  myCurrentEdge->myWidthLanesBackward = widthLanes;
1462  } else {
1463  WRITE_WARNINGF(TL("Using default lane width for edge '%' as key '%' could not be parsed."), toString(myCurrentEdge->id), key);
1464  }
1465  } catch (const NumberFormatException&) {
1466  WRITE_WARNINGF(TL("Using default lane width for edge '%' as value '%' could not be parsed."), toString(myCurrentEdge->id), value);
1467  }
1468  } else if (key == "width") {
1469  try {
1470  myCurrentEdge->myWidth = StringUtils::parseDist(value);
1471  } catch (const NumberFormatException&) {
1472  WRITE_WARNINGF(TL("Using default width for edge '%' as value '%' could not be parsed."), toString(myCurrentEdge->id), value);
1473  }
1474  } else if (key == "foot") {
1475  if (value == "use_sidepath" || value == "no") {
1476  myCurrentEdge->myExtraDisallowed |= SVC_PEDESTRIAN;
1477  } else if (value == "yes" || value == "designated" || value == "permissive") {
1478  myCurrentEdge->myExtraAllowed |= SVC_PEDESTRIAN;
1479  }
1480  } else if (key == "bicycle") {
1481  if (value == "use_sidepath" || value == "no") {
1482  myCurrentEdge->myExtraDisallowed |= SVC_BICYCLE;
1483  } else if (value == "yes" || value == "designated" || value == "permissive") {
1484  myCurrentEdge->myExtraAllowed |= SVC_BICYCLE;
1485  }
1486  } else if (key == "oneway:bicycle") {
1487  myCurrentEdge->myExtraTags["oneway:bicycle"] = value;
1488  } else if (key == "oneway:bus") {
1489  if (value == "no") {
1490  // need to add a bus way in reversed direction of way
1491  myCurrentEdge->myBuswayType = WAY_BACKWARD;
1492  }
1493  } else if (key == "lanes") {
1494  try {
1495  myCurrentEdge->myNoLanes = StringUtils::toInt(value);
1496  } catch (NumberFormatException&) {
1497  // might be a list of values
1498  StringTokenizer st(value, ";", true);
1499  std::vector<std::string> list = st.getVector();
1500  if (list.size() >= 2) {
1501  int minLanes = std::numeric_limits<int>::max();
1502  try {
1503  for (auto& i : list) {
1504  const int numLanes = StringUtils::toInt(StringUtils::prune(i));
1505  minLanes = MIN2(minLanes, numLanes);
1506  }
1507  myCurrentEdge->myNoLanes = minLanes;
1508  WRITE_WARNINGF(TL("Using minimum lane number from list (%) for edge '%'."), value, toString(myCurrentEdge->id));
1509  } catch (NumberFormatException&) {
1510  WRITE_WARNINGF(TL("Value of key '%' is not numeric ('%') in edge '%'."), key, value, myCurrentEdge->id);
1511  }
1512  }
1513  } catch (EmptyData&) {
1514  WRITE_WARNINGF(TL("Value of key '%' is not numeric ('%') in edge '%'."), key, value, myCurrentEdge->id);
1515  }
1516  } else if (key == "lanes:forward") {
1517  try {
1518  const int numLanes = StringUtils::toInt(value);
1519  if (myCurrentEdge->myNoLanesForward < 0 && myCurrentEdge->myNoLanes < 0) {
1520  // fix lane count in case only lanes:forward and lanes:backward are set
1521  myCurrentEdge->myNoLanes = numLanes - myCurrentEdge->myNoLanesForward;
1522  }
1523  myCurrentEdge->myNoLanesForward = numLanes;
1524  } catch (...) {
1525  WRITE_WARNINGF(TL("Value of key '%' is not numeric ('%') in edge '%'."), key, value, myCurrentEdge->id);
1526  }
1527  } else if (key == "lanes:backward") {
1528  try {
1529  const int numLanes = StringUtils::toInt(value);
1530  if (myCurrentEdge->myNoLanesForward > 0 && myCurrentEdge->myNoLanes < 0) {
1531  // fix lane count in case only lanes:forward and lanes:backward are set
1532  myCurrentEdge->myNoLanes = numLanes + myCurrentEdge->myNoLanesForward;
1533  }
1534  // denote backwards count with a negative sign
1535  myCurrentEdge->myNoLanesForward = -numLanes;
1536  } catch (...) {
1537  WRITE_WARNINGF(TL("Value of key '%' is not numeric ('%') in edge '%'."), key, value, myCurrentEdge->id);
1538  }
1539  } else if (myCurrentEdge->myMaxSpeed == MAXSPEED_UNGIVEN &&
1540  (key == "maxspeed" || key == "maxspeed:type" || key == "maxspeed:forward" || key == "zone:maxspeed")) {
1541  // both 'maxspeed' and 'maxspeed:type' may be given so we must take care not to overwrite an already seen value
1542  myCurrentEdge->myMaxSpeed = interpretSpeed(key, value);
1543  } else if (key == "maxspeed:backward" && myCurrentEdge->myMaxSpeedBackward == MAXSPEED_UNGIVEN) {
1544  myCurrentEdge->myMaxSpeedBackward = interpretSpeed(key, value);
1545  } else if (key == "junction") {
1546  if ((value == "roundabout" || value == "circular") && myCurrentEdge->myIsOneWay.empty()) {
1547  myCurrentEdge->myIsOneWay = "yes";
1548  }
1549  if (value == "roundabout") {
1550  myCurrentEdge->myAmInRoundabout = true;
1551  }
1552  } else if (key == "oneway") {
1553  myCurrentEdge->myIsOneWay = value;
1554  } else if (key == "name") {
1555  myCurrentEdge->streetName = value;
1556  } else if (key == "ref") {
1557  myCurrentEdge->ref = value;
1558  myCurrentEdge->setParameter("ref", value);
1559  } else if (key == "layer") {
1560  try {
1561  myCurrentEdge->myLayer = StringUtils::toInt(value);
1562  } catch (...) {
1563  WRITE_WARNINGF(TL("Value of key '%' is not numeric ('%') in edge '%'."), key, value, myCurrentEdge->id);
1564  }
1565  } else if (key == "tracks") {
1566  try {
1567  if (StringUtils::toInt(value) == 1) {
1568  myCurrentEdge->myIsOneWay = "true";
1569  } else {
1570  WRITE_WARNINGF(TL("Ignoring track count % for edge '%'."), value, myCurrentEdge->id);
1571  }
1572  } catch (...) {
1573  WRITE_WARNINGF(TL("Value of key '%' is not numeric ('%') in edge '%'."), key, value, myCurrentEdge->id);
1574  }
1575  } else if (key == "railway:preferred_direction") {
1576  if (value == "both") {
1577  myCurrentEdge->myRailDirection = WAY_BOTH;
1578  } else if (myCurrentEdge->myRailDirection == WAY_UNKNOWN) {
1579  if (value == "backward") {
1580  myCurrentEdge->myRailDirection = WAY_BACKWARD;
1581  } else if (value == "forward") {
1582  myCurrentEdge->myRailDirection = WAY_FORWARD;
1583  }
1584  }
1585  } else if (key == "railway:bidirectional") {
1586  if (value == "regular") {
1587  myCurrentEdge->myRailDirection = WAY_BOTH;
1588  }
1589  } else if (key == "electrified" || key == "segregated") {
1590  if (value != "no") {
1591  myCurrentEdge->myExtraTags[key] = value;
1592  }
1593  } else if (key == "railway:track_ref") {
1594  myCurrentEdge->setParameter(key, value);
1595  } else if (key == "public_transport" && value == "platform") {
1596  myCurrentEdge->myExtraTags["platform"] = "yes";
1597  } else if (key == "parking:lane:both" && !StringUtils::startsWith(value, "no")) {
1598  myCurrentEdge->myParkingType |= PARKING_BOTH;
1599  } else if (key == "parking:lane:left" && !StringUtils::startsWith(value, "no")) {
1600  myCurrentEdge->myParkingType |= PARKING_LEFT;
1601  } else if (key == "parking:lane:right" && !StringUtils::startsWith(value, "no")) {
1602  myCurrentEdge->myParkingType |= PARKING_RIGHT;
1603  } else if (key == "change" || key == "change:lanes") {
1604  myCurrentEdge->myChangeForward = myCurrentEdge->myChangeBackward = interpretChangeType(value);
1605  } else if (key == "change:forward" || key == "change:lanes:forward") {
1606  myCurrentEdge->myChangeForward = interpretChangeType(value);
1607  } else if (key == "change:backward" || key == "change:lanes:backward") {
1608  myCurrentEdge->myChangeBackward = interpretChangeType(value);
1609  } else if (key == "vehicle:lanes" || key == "vehicle:lanes:forward") {
1610  interpretLaneUse(value, SVC_PASSENGER, true);
1611  interpretLaneUse(value, SVC_PRIVATE, true);
1612  } else if (key == "vehicle:lanes:backward") {
1613  interpretLaneUse(value, SVC_PASSENGER, false);
1614  interpretLaneUse(value, SVC_PRIVATE, false);
1615  } else if (key == "bus:lanes" || key == "bus:lanes:forward") {
1616  interpretLaneUse(value, SVC_BUS, true);
1617  } else if (key == "bus:lanes:backward") {
1618  interpretLaneUse(value, SVC_BUS, false);
1619  } else if (key == "psv:lanes" || key == "psv:lanes:forward") {
1620  interpretLaneUse(value, SVC_BUS, true);
1621  interpretLaneUse(value, SVC_TAXI, true);
1622  } else if (key == "psv:lanes:backward") {
1623  interpretLaneUse(value, SVC_BUS, false);
1624  interpretLaneUse(value, SVC_TAXI, false);
1625  } else if (key == "bicycle:lanes" || key == "bicycle:lanes:forward") {
1626  interpretLaneUse(value, SVC_BICYCLE, true);
1627  } else if (key == "bicycle:lanes:backward") {
1628  interpretLaneUse(value, SVC_BICYCLE, false);
1629  } else if (StringUtils::startsWith(key, "turn:") && key.find(":lanes") != std::string::npos) {
1630  int shift = 0;
1631  // use the first 8 bit to encode permitted directions for all classes
1632  // and the successive 8 bit blocks for selected classes
1633  if (StringUtils::startsWith(key, "turn:bus") || StringUtils::startsWith(key, "turn:psv:")) {
1635  } else if (StringUtils::startsWith(key, "turn:taxi")) {
1637  } else if (StringUtils::startsWith(key, "turn:bicycle")) {
1639  }
1640  const std::vector<std::string> values = StringTokenizer(value, "|").getVector();
1641  std::vector<int> turnCodes;
1642  for (std::string codeList : values) {
1643  const std::vector<std::string> codes = StringTokenizer(codeList, ";").getVector();
1644  int turnCode = 0;
1645  if (codes.size() == 0) {
1646  turnCode = (int)LinkDirection::STRAIGHT;
1647  }
1648  for (std::string code : codes) {
1649  if (code == "" || code == "none" || code == "through") {
1650  turnCode |= (int)LinkDirection::STRAIGHT << shift ;
1651  } else if (code == "left" || code == "sharp_left") {
1652  turnCode |= (int)LinkDirection::LEFT << shift;
1653  } else if (code == "right" || code == "sharp_right") {
1654  turnCode |= (int)LinkDirection::RIGHT << shift;
1655  } else if (code == "slight_left") {
1656  turnCode |= (int)LinkDirection::PARTLEFT << shift;
1657  } else if (code == "slight_right") {
1658  turnCode |= (int)LinkDirection::PARTRIGHT << shift;
1659  } else if (code == "reverse") {
1660  turnCode |= (int)LinkDirection::TURN << shift;
1661  } else if (code == "merge_to_left" || code == "merge_to_right") {
1662  turnCode |= (int)LinkDirection::NODIR << shift;
1663  }
1664  }
1665  turnCodes.push_back(turnCode);
1666  }
1667  if (StringUtils::endsWith(key, "lanes") || StringUtils::endsWith(key, "lanes:forward")) {
1668  mergeTurnSigns(myCurrentEdge->myTurnSignsForward, turnCodes);
1669  } else if (StringUtils::endsWith(key, "lanes:backward")) {
1670  mergeTurnSigns(myCurrentEdge->myTurnSignsBackward, turnCodes);
1671  } else if (StringUtils::endsWith(key, "lanes:both_ways")) {
1672  mergeTurnSigns(myCurrentEdge->myTurnSignsForward, turnCodes);
1673  mergeTurnSigns(myCurrentEdge->myTurnSignsBackward, turnCodes);
1674  }
1675  }
1676  }
1677 }
1678 
1679 
1680 double
1681 NIImporter_OpenStreetMap::EdgesHandler::interpretSpeed(const std::string& key, std::string value) {
1682  if (mySpeedMap.find(value) != mySpeedMap.end()) {
1683  return mySpeedMap[value];
1684  } else {
1685  // handle symbolic names of the form DE:30 / DE:zone30
1686  if (value.size() > 3 && value[2] == ':') {
1687  if (value.substr(3, 4) == "zone") {
1688  value = value.substr(7);
1689  } else {
1690  value = value.substr(3);
1691  }
1692  }
1693  try {
1694  return StringUtils::parseSpeed(value);
1695  } catch (...) {
1696  WRITE_WARNING("Value of key '" + key + "' is not numeric ('" + value + "') in edge '" +
1697  toString(myCurrentEdge->id) + "'.");
1698  return MAXSPEED_UNGIVEN;
1699  }
1700  }
1701 }
1702 
1703 
1704 int
1706  int result = 0;
1707  const std::vector<std::string> values = StringTokenizer(value, "|").getVector();
1708  for (const std::string& val : values) {
1709  if (val == "no") {
1710  result += CHANGE_NO;
1711  } else if (val == "not_left") {
1712  result += CHANGE_NO_LEFT;
1713  } else if (val == "not_right") {
1714  result += CHANGE_NO_RIGHT;
1715  }
1716  result = result << 2;
1717  }
1718  // last shift was superfluous
1719  result = result >> 2;
1720 
1721  if (values.size() > 1) {
1722  result += 2 << 29; // mark multi-value input
1723  }
1724  //std::cout << " way=" << myCurrentEdge->id << " value=" << value << " result=" << std::bitset<32>(result) << "\n";
1725  return result;
1726 }
1727 
1728 
1729 void
1730 NIImporter_OpenStreetMap::EdgesHandler::interpretLaneUse(const std::string& value, SUMOVehicleClass svc, const bool forward) const {
1731  const std::vector<std::string> values = StringTokenizer(value, "|").getVector();
1732  std::vector<bool>& designated = forward ? myCurrentEdge->myDesignatedLaneForward : myCurrentEdge->myDesignatedLaneBackward;
1733  std::vector<SVCPermissions>& allowed = forward ? myCurrentEdge->myAllowedLaneForward : myCurrentEdge->myAllowedLaneBackward;
1734  std::vector<SVCPermissions>& disallowed = forward ? myCurrentEdge->myDisallowedLaneForward : myCurrentEdge->myDisallowedLaneBackward;
1735  designated.resize(MAX2(designated.size(), values.size()), false);
1736  allowed.resize(MAX2(allowed.size(), values.size()), SVC_IGNORING);
1737  disallowed.resize(MAX2(disallowed.size(), values.size()), SVC_IGNORING);
1738  int i = 0;
1739  for (const std::string& val : values) {
1740  if (val == "yes" || val == "permissive") {
1741  allowed[i] |= svc;
1742  } else if (val == "lane" || val == "designated") {
1743  allowed[i] |= svc;
1744  designated[i] = true;
1745  } else if (val == "no") {
1746  disallowed[i] |= svc;
1747  } else {
1748  WRITE_WARNINGF(TL("Unknown lane use specifier '%' ignored for way '%'"), val, myCurrentEdge->id);
1749  }
1750  i++;
1751  }
1752 }
1753 
1754 
1755 void
1757  if (element == SUMO_TAG_WAY && myCurrentEdge != nullptr) {
1758  if (myCurrentEdge->myCurrentIsRoad) {
1759  const auto insertionIt = myEdgeMap.lower_bound(myCurrentEdge->id);
1760  if (insertionIt == myEdgeMap.end() || insertionIt->first != myCurrentEdge->id) {
1761  // assume we are loading multiple files, so we won't report duplicate edges
1762  myEdgeMap.emplace_hint(insertionIt, myCurrentEdge->id, myCurrentEdge);
1763  } else {
1764  delete myCurrentEdge;
1765  }
1766  } else if (myCurrentEdge->myExtraTags.count("platform") != 0) {
1767  const auto insertionIt = myPlatformShapesMap.lower_bound(myCurrentEdge->id);
1768  if (insertionIt == myPlatformShapesMap.end() || insertionIt->first != myCurrentEdge->id) {
1769  // assume we are loading multiple files, so we won't report duplicate platforms
1770  myPlatformShapesMap.emplace_hint(insertionIt, myCurrentEdge->id, myCurrentEdge);
1771  } else {
1772  delete myCurrentEdge;
1773  }
1774  } else {
1775  delete myCurrentEdge;
1776  }
1777  myCurrentEdge = nullptr;
1778  }
1779 }
1780 
1781 
1782 // ---------------------------------------------------------------------------
1783 // definitions of NIImporter_OpenStreetMap::RelationHandler-methods
1784 // ---------------------------------------------------------------------------
1786  const std::map<long long int, NIOSMNode*>& osmNodes,
1787  const std::map<long long int, Edge*>& osmEdges, NBPTStopCont* nbptStopCont,
1788  const std::map<long long int, Edge*>& platformShapes,
1789  NBPTLineCont* nbptLineCont,
1790  const OptionsCont& oc) :
1791  SUMOSAXHandler("osm - file"),
1792  myOSMNodes(osmNodes),
1793  myOSMEdges(osmEdges),
1794  myPlatformShapes(platformShapes),
1795  myNBPTStopCont(nbptStopCont),
1796  myNBPTLineCont(nbptLineCont),
1797  myOptionsCont(oc) {
1798  resetValues();
1799 }
1800 
1801 
1803 
1804 
1805 void
1807  myCurrentRelation = INVALID_ID;
1808  myIsRestriction = false;
1809  myRestrictionException = SVC_IGNORING;
1810  myFromWay = INVALID_ID;
1811  myToWay = INVALID_ID;
1812  myViaNode = INVALID_ID;
1813  myViaWay = INVALID_ID;
1814  myRestrictionType = RestrictionType::UNKNOWN;
1815  myPlatforms.clear();
1816  myStops.clear();
1817  myPlatformStops.clear();
1818  myWays.clear();
1819  myIsStopArea = false;
1820  myIsRoute = false;
1821  myPTRouteType = "";
1822  myRouteColor.setValid(false);
1823 }
1824 
1825 
1826 void
1828  if (element == SUMO_TAG_RELATION) {
1829  bool ok = true;
1830  myCurrentRelation = attrs.get<long long int>(SUMO_ATTR_ID, nullptr, ok);
1831  const std::string& action = attrs.getOpt<std::string>(SUMO_ATTR_ACTION, nullptr, ok);
1832  if (action == "delete" || !ok) {
1833  myCurrentRelation = INVALID_ID;
1834  }
1835  myName = "";
1836  myRef = "";
1837  myInterval = -1;
1838  myNightService = "";
1839  return;
1840  }
1841  if (myCurrentRelation == INVALID_ID) {
1842  return;
1843  }
1844  if (element == SUMO_TAG_MEMBER) {
1845  bool ok = true;
1846  std::string role = attrs.hasAttribute("role") ? attrs.getStringSecure("role", "") : "";
1847  const long long int ref = attrs.get<long long int>(SUMO_ATTR_REF, nullptr, ok);
1848  if (role == "via") {
1849  // u-turns for divided ways may be given with 2 via-nodes or 1 via-way
1850  std::string memberType = attrs.get<std::string>(SUMO_ATTR_TYPE, nullptr, ok);
1851  if (memberType == "way" && checkEdgeRef(ref)) {
1852  myViaWay = ref;
1853  } else if (memberType == "node") {
1854  if (myOSMNodes.find(ref) != myOSMNodes.end()) {
1855  myViaNode = ref;
1856  } else {
1857  WRITE_WARNINGF(TL("No node found for reference '%' in relation '%'."), toString(ref), toString(myCurrentRelation));
1858  }
1859  }
1860  } else if (role == "from" && checkEdgeRef(ref)) {
1861  myFromWay = ref;
1862  } else if (role == "to" && checkEdgeRef(ref)) {
1863  myToWay = ref;
1864  } else if (role == "stop") {
1865  myStops.push_back(ref);
1866  } else if (role == "platform") {
1867  std::string memberType = attrs.get<std::string>(SUMO_ATTR_TYPE, nullptr, ok);
1868  if (memberType == "way") {
1869  const std::map<long long int, NIImporter_OpenStreetMap::Edge*>::const_iterator& wayIt = myPlatformShapes.find(ref);
1870  if (wayIt != myPlatformShapes.end()) {
1871  NIIPTPlatform platform;
1872  platform.isWay = true;
1873  platform.ref = ref;
1874  myPlatforms.push_back(platform);
1875  }
1876  } else if (memberType == "node") {
1877  // myIsStopArea may not be set yet
1878  myStops.push_back(ref);
1879  myPlatformStops.insert(ref);
1880  NIIPTPlatform platform;
1881  platform.isWay = false;
1882  platform.ref = ref;
1883  myPlatforms.push_back(platform);
1884  }
1885 
1886  } else if (role.empty()) {
1887  std::string memberType = attrs.get<std::string>(SUMO_ATTR_TYPE, nullptr, ok);
1888  if (memberType == "way") {
1889  myWays.push_back(ref);
1890  } else if (memberType == "node") {
1891  myStops.push_back(ref);
1892  }
1893  }
1894  return;
1895  }
1896  // parse values
1897  if (element == SUMO_TAG_TAG) {
1898  bool ok = true;
1899  std::string key = attrs.get<std::string>(SUMO_ATTR_K, toString(myCurrentRelation).c_str(), ok, false);
1900  // we check whether the key is relevant (and we really need to transcode the value) to avoid hitting #1636
1901  if (key == "type" || key == "restriction") {
1902  std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);
1903  if (key == "type" && value == "restriction") {
1904  myIsRestriction = true;
1905  return;
1906  }
1907  if (key == "type" && value == "route") {
1908  myIsRoute = true;
1909  return;
1910  }
1911  if (key == "restriction") {
1912  // @note: the 'right/left/straight' part is ignored since the information is
1913  // redundantly encoded in the 'from', 'to' and 'via' members
1914  if (value.substr(0, 5) == "only_") {
1915  myRestrictionType = RestrictionType::ONLY;
1916  } else if (value.substr(0, 3) == "no_") {
1917  myRestrictionType = RestrictionType::NO;
1918  } else {
1919  WRITE_WARNINGF(TL("Found unknown restriction type '%' in relation '%'"), value, toString(myCurrentRelation));
1920  }
1921  return;
1922  }
1923  } else if (key == "except") {
1924  std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);
1925  for (const std::string& v : StringTokenizer(value, ";").getVector()) {
1926  if (v == "psv") {
1927  myRestrictionException |= SVC_BUS;
1928  } else if (v == "bicycle") {
1929  myRestrictionException |= SVC_BICYCLE;
1930  } else if (v == "hgv") {
1931  myRestrictionException |= SVC_TRUCK | SVC_TRAILER;
1932  } else if (v == "motorcar") {
1933  myRestrictionException |= SVC_PASSENGER | SVC_TAXI;
1934  } else if (v == "emergency") {
1935  myRestrictionException |= SVC_EMERGENCY;
1936  }
1937  }
1938  } else if (key == "public_transport") {
1939  std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);
1940  if (value == "stop_area") {
1941  myIsStopArea = true;
1942  }
1943  } else if (key == "route") {
1944  std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);
1945  if (value == "train" || value == "subway" || value == "light_rail" || value == "monorail" || value == "tram" || value == "bus"
1946  || value == "trolleybus" || value == "aerialway" || value == "ferry" || value == "share_taxi" || value == "minibus") {
1947  myPTRouteType = value;
1948  }
1949 
1950  } else if (key == "name") {
1951  myName = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);
1952  } else if (key == "colour") {
1953  std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);
1954  try {
1955  myRouteColor = RGBColor::parseColor(value);
1956  } catch (...) {
1957  WRITE_WARNINGF(TL("Invalid color value '%' in relation %"), value, myCurrentRelation);
1958  }
1959  } else if (key == "ref") {
1960  myRef = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);
1961  } else if (key == "interval" || key == "headway") {
1962  myInterval = attrs.get<int>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);
1963  } else if (key == "by_night") {
1964  myNightService = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);
1965  }
1966  }
1967 }
1968 
1969 
1970 bool
1972  if (myOSMEdges.find(ref) != myOSMEdges.end()) {
1973  return true;
1974  }
1975  WRITE_WARNINGF(TL("No way found for reference '%' in relation '%'"), toString(ref), toString(myCurrentRelation));
1976  return false;
1977 }
1978 
1979 
1980 void
1982  if (element == SUMO_TAG_RELATION) {
1983  if (myIsRestriction) {
1984  assert(myCurrentRelation != INVALID_ID);
1985  bool ok = true;
1986  if (myRestrictionType == RestrictionType::UNKNOWN) {
1987  WRITE_WARNINGF(TL("Ignoring restriction relation '%' with unknown type."), toString(myCurrentRelation));
1988  ok = false;
1989  }
1990  if (myFromWay == INVALID_ID) {
1991  WRITE_WARNINGF(TL("Ignoring restriction relation '%' with unknown from-way."), toString(myCurrentRelation));
1992  ok = false;
1993  }
1994  if (myToWay == INVALID_ID) {
1995  WRITE_WARNINGF(TL("Ignoring restriction relation '%' with unknown to-way."), toString(myCurrentRelation));
1996  ok = false;
1997  }
1998  if (myViaNode == INVALID_ID && myViaWay == INVALID_ID) {
1999  WRITE_WARNINGF(TL("Ignoring restriction relation '%' with unknown via."), toString(myCurrentRelation));
2000  ok = false;
2001  }
2002  if (ok && !applyRestriction()) {
2003  WRITE_WARNINGF(TL("Ignoring restriction relation '%'."), toString(myCurrentRelation));
2004  }
2005  } else if (myIsStopArea) {
2006  for (long long ref : myStops) {
2007  myStopAreas[ref] = myCurrentRelation;
2008  if (myOSMNodes.find(ref) == myOSMNodes.end()) {
2009  //WRITE_WARNING(
2010  // "Referenced node: '" + toString(ref) + "' in relation: '" + toString(myCurrentRelation)
2011  // + "' does not exist. Probably OSM file is incomplete.");
2012  continue;
2013  }
2014 
2015  NIOSMNode* n = myOSMNodes.find(ref)->second;
2016  std::shared_ptr<NBPTStop> ptStop = myNBPTStopCont->get(toString(n->id));
2017  if (ptStop == nullptr) {
2018  //WRITE_WARNING(
2019  // "Relation '" + toString(myCurrentRelation) + "' refers to a non existing pt stop at node: '"
2020  // + toString(n->id) + "'. Probably OSM file is incomplete.");
2021  continue;
2022  }
2023  for (NIIPTPlatform& myPlatform : myPlatforms) {
2024  if (myPlatform.isWay) {
2025  assert(myPlatformShapes.find(myPlatform.ref) != myPlatformShapes.end()); //already tested earlier
2026  Edge* edge = (*myPlatformShapes.find(myPlatform.ref)).second;
2027  if (edge->myCurrentNodes.size() > 1 && edge->myCurrentNodes[0] == *(edge->myCurrentNodes.end() - 1)) {
2028  WRITE_WARNINGF(TL("Platform '%' in relation: '%' is given as polygon, which currently is not supported."), myPlatform.ref, myCurrentRelation);
2029  continue;
2030 
2031  }
2032  PositionVector p;
2033  for (auto nodeRef : edge->myCurrentNodes) {
2034  if (myOSMNodes.find(nodeRef) == myOSMNodes.end()) {
2035  //WRITE_WARNING(
2036  // "Referenced node: '" + toString(ref) + "' in relation: '" + toString(myCurrentRelation)
2037  // + "' does not exist. Probably OSM file is incomplete.");
2038  continue;
2039  }
2040  NIOSMNode* pNode = myOSMNodes.find(nodeRef)->second;
2041  Position pNodePos(pNode->lon, pNode->lat, pNode->ele);
2042  if (!NBNetBuilder::transformCoordinate(pNodePos)) {
2043  WRITE_ERRORF("Unable to project coordinates for node '%'.", pNode->id);
2044  continue;
2045  }
2046  p.push_back(pNodePos);
2047  }
2048  if (p.size() == 0) {
2049  WRITE_WARNINGF(TL("Referenced platform: '%' in relation: '%' is corrupt. Probably OSM file is incomplete."),
2050  toString(myPlatform.ref), toString(myCurrentRelation));
2051  continue;
2052  }
2053  NBPTPlatform platform(p[(int)p.size() / 2], p.length());
2054  ptStop->addPlatformCand(platform);
2055  } else {
2056  if (myOSMNodes.find(myPlatform.ref) == myOSMNodes.end()) {
2057  //WRITE_WARNING(
2058  // "Referenced node: '" + toString(ref) + "' in relation: '" + toString(myCurrentRelation)
2059  // + "' does not exist. Probably OSM file is incomplete.");
2060  continue;
2061  }
2062  NIOSMNode* pNode = myOSMNodes.find(myPlatform.ref)->second;
2063  Position platformPos(pNode->lon, pNode->lat, pNode->ele);
2064  if (!NBNetBuilder::transformCoordinate(platformPos)) {
2065  WRITE_ERRORF("Unable to project coordinates for node '%'.", pNode->id);
2066  }
2067  NBPTPlatform platform(platformPos, myOptionsCont.getFloat("osm.stop-output.length"));
2068  ptStop->addPlatformCand(platform);
2069 
2070  }
2071  }
2072  ptStop->setIsMultipleStopPositions(myStops.size() > 1, myCurrentRelation);
2073  }
2074  } else if (myPTRouteType != "" && myIsRoute) {
2075  NBPTLine* ptLine = new NBPTLine(toString(myCurrentRelation), myName, myPTRouteType, myRef, myInterval, myNightService,
2076  interpretTransportType(myPTRouteType), myRouteColor);
2077  bool hadGap = false;
2078  int missingBefore = 0;
2079  int missingAfter = 0;
2080  int stopIndex = 0;
2081  for (long long ref : myStops) {
2082  stopIndex++;
2083  const auto& nodeIt = myOSMNodes.find(ref);
2084  if (nodeIt == myOSMNodes.end()) {
2085  if (ptLine->getStops().empty()) {
2086  missingBefore++;
2087  } else {
2088  missingAfter++;
2089  if (!hadGap) {
2090  hadGap = true;
2091  }
2092  }
2093  continue;
2094  }
2095  if (hadGap) {
2096  WRITE_WARNINGF(TL("PT line '%' in relation % seems to be split, only keeping first part."), myName, myCurrentRelation);
2097  missingAfter = (int)myStops.size() - missingBefore - (int)ptLine->getStops().size();
2098  break;
2099  }
2100 
2101  const NIOSMNode* const n = nodeIt->second;
2102  std::shared_ptr<NBPTStop> ptStop = myNBPTStopCont->get(toString(n->id));
2103  if (ptStop == nullptr) {
2104  // loose stop, which must later be mapped onto a line way
2105  Position ptPos(n->lon, n->lat, n->ele);
2106  if (!NBNetBuilder::transformCoordinate(ptPos)) {
2107  WRITE_ERRORF("Unable to project coordinates for node '%'.", n->id);
2108  }
2109  ptStop = std::make_shared<NBPTStop>(toString(n->id), ptPos, "", "", n->ptStopLength, n->name, n->permissions);
2110  myNBPTStopCont->insert(ptStop);
2111  if (myStopAreas.count(n->id)) {
2112  ptStop->setIsMultipleStopPositions(false, myStopAreas[n->id]);
2113  }
2114  if (myPlatformStops.count(n->id) > 0) {
2115  ptStop->setIsPlatform();
2116  }
2117  }
2118  ptLine->addPTStop(ptStop);
2119  }
2120  for (long long& myWay : myWays) {
2121  auto entr = myOSMEdges.find(myWay);
2122  if (entr != myOSMEdges.end()) {
2123  Edge* edge = entr->second;
2124  for (long long& myCurrentNode : edge->myCurrentNodes) {
2125  ptLine->addWayNode(myWay, myCurrentNode);
2126  }
2127  }
2128  }
2129  ptLine->setNumOfStops((int)myStops.size(), missingBefore, missingAfter);
2130  if (ptLine->getStops().empty()) {
2131  WRITE_WARNINGF(TL("PT line in relation % with no stops ignored. Probably OSM file is incomplete."), myCurrentRelation);
2132  delete ptLine;
2133  resetValues();
2134  return;
2135  }
2136  if (!myNBPTLineCont->insert(ptLine)) {
2137  WRITE_WARNINGF(TL("Ignoring duplicate PT line '%'."), myCurrentRelation);
2138  delete ptLine;
2139  }
2140  }
2141  // other relations might use similar subelements so reset in any case
2142  resetValues();
2143  }
2144 }
2145 
2146 bool
2148  // since OSM ways are bidirectional we need the via to figure out which direction was meant
2149  if (myViaNode != INVALID_ID) {
2150  NBNode* viaNode = myOSMNodes.find(myViaNode)->second->node;
2151  if (viaNode == nullptr) {
2152  WRITE_WARNINGF(TL("Via-node '%' was not instantiated"), toString(myViaNode));
2153  return false;
2154  }
2155  NBEdge* from = findEdgeRef(myFromWay, viaNode->getIncomingEdges());
2156  NBEdge* to = findEdgeRef(myToWay, viaNode->getOutgoingEdges());
2157  if (from == nullptr) {
2158  WRITE_WARNINGF(TL("from-edge '%' of restriction relation could not be determined"), toString(myFromWay));
2159  return false;
2160  }
2161  if (to == nullptr) {
2162  WRITE_WARNINGF(TL("to-edge '%' of restriction relation could not be determined"), toString(myToWay));
2163  return false;
2164  }
2165  if (myRestrictionType == RestrictionType::ONLY) {
2166  from->addEdge2EdgeConnection(to, true);
2167  // make sure that these connections remain disabled even if network
2168  // modifications (ramps.guess) reset existing connections
2169  for (NBEdge* cand : from->getToNode()->getOutgoingEdges()) {
2170  if (!from->isConnectedTo(cand)) {
2171  if (myRestrictionException == SVC_IGNORING) {
2172  from->removeFromConnections(cand, -1, -1, true);
2173  } else {
2174  from->addEdge2EdgeConnection(cand, true, myRestrictionException);
2175  }
2176  }
2177  }
2178  } else {
2179  if (myRestrictionException == SVC_IGNORING) {
2180  from->removeFromConnections(to, -1, -1, true);
2181  } else {
2182  from->addEdge2EdgeConnection(to, true, myRestrictionException);
2183  for (NBEdge* cand : from->getToNode()->getOutgoingEdges()) {
2184  if (!from->isConnectedTo(cand)) {
2185  from->addEdge2EdgeConnection(cand, true);
2186  }
2187  }
2188  }
2189  }
2190  } else {
2191  // XXX interpreting via-ways or via-node lists not yet implemented
2192  WRITE_WARNINGF(TL("direction of restriction relation could not be determined%"), "");
2193  return false;
2194  }
2195  return true;
2196 }
2197 
2198 NBEdge*
2200  const std::vector<NBEdge*>& candidates) const {
2201  const std::string prefix = toString(wayRef);
2202  const std::string backPrefix = "-" + prefix;
2203  NBEdge* result = nullptr;
2204  int found = 0;
2205  for (auto candidate : candidates) {
2206  if ((candidate->getID().substr(0, prefix.size()) == prefix) ||
2207  (candidate->getID().substr(0, backPrefix.size()) == backPrefix)) {
2208  result = candidate;
2209  found++;
2210  }
2211  }
2212  if (found > 1) {
2213  WRITE_WARNINGF(TL("Ambiguous way reference '%' in restriction relation"), prefix);
2214  result = nullptr;
2215  }
2216  return result;
2217 }
2218 
2219 
2220 void
2222  NBNodeCont& nc = nb.getNodeCont();
2223  NBEdgeCont& ec = nb.getEdgeCont();
2224  // reconstruct elevation from layer info
2225  // build a map of raising and lowering forces (attractor and distance)
2226  // for all nodes unknownElevation
2227  std::map<NBNode*, std::vector<std::pair<double, double> > > layerForces;
2228 
2229  // collect all nodes that belong to a way with layer information
2230  std::set<NBNode*> knownElevation;
2231  for (auto& myEdge : myEdges) {
2232  Edge* e = myEdge.second;
2233  if (e->myLayer != 0) {
2234  for (auto j = e->myCurrentNodes.begin(); j != e->myCurrentNodes.end(); ++j) {
2235  NBNode* node = nc.retrieve(toString(*j));
2236  if (node != nullptr) {
2237  knownElevation.insert(node);
2238  layerForces[node].emplace_back(e->myLayer * layerElevation, POSITION_EPS);
2239  }
2240  }
2241  }
2242  }
2243 #ifdef DEBUG_LAYER_ELEVATION
2244  std::cout << "known elevations:\n";
2245  for (std::set<NBNode*>::iterator it = knownElevation.begin(); it != knownElevation.end(); ++it) {
2246  const std::vector<std::pair<double, double> >& primaryLayers = layerForces[*it];
2247  std::cout << " node=" << (*it)->getID() << " ele=";
2248  for (std::vector<std::pair<double, double> >::const_iterator it_ele = primaryLayers.begin(); it_ele != primaryLayers.end(); ++it_ele) {
2249  std::cout << it_ele->first << " ";
2250  }
2251  std::cout << "\n";
2252  }
2253 #endif
2254  // layer data only provides a lower bound on elevation since it is used to
2255  // resolve the relation among overlapping ways.
2256  // Perform a sanity check for steep inclines and raise the knownElevation if necessary
2257  std::map<NBNode*, double> knownEleMax;
2258  for (auto it : knownElevation) {
2259  double eleMax = -std::numeric_limits<double>::max();
2260  const std::vector<std::pair<double, double> >& primaryLayers = layerForces[it];
2261  for (const auto& primaryLayer : primaryLayers) {
2262  eleMax = MAX2(eleMax, primaryLayer.first);
2263  }
2264  knownEleMax[it] = eleMax;
2265  }
2266  const double gradeThreshold = OptionsCont::getOptions().getFloat("osm.layer-elevation.max-grade") / 100;
2267  bool changed = true;
2268  while (changed) {
2269  changed = false;
2270  for (auto it = knownElevation.begin(); it != knownElevation.end(); ++it) {
2271  std::map<NBNode*, std::pair<double, double> > neighbors = getNeighboringNodes(*it,
2272  knownEleMax[*it]
2273  / gradeThreshold * 3,
2274  knownElevation);
2275  for (auto& neighbor : neighbors) {
2276  if (knownElevation.count(neighbor.first) != 0) {
2277  const double grade = fabs(knownEleMax[*it] - knownEleMax[neighbor.first])
2278  / MAX2(POSITION_EPS, neighbor.second.first);
2279 #ifdef DEBUG_LAYER_ELEVATION
2280  std::cout << " grade at node=" << (*it)->getID() << " ele=" << knownEleMax[*it] << " neigh=" << it_neigh->first->getID() << " neighEle=" << knownEleMax[it_neigh->first] << " grade=" << grade << " dist=" << it_neigh->second.first << " speed=" << it_neigh->second.second << "\n";
2281 #endif
2282  if (grade > gradeThreshold * 50 / 3.6 / neighbor.second.second) {
2283  // raise the lower node to the higher level
2284  const double eleMax = MAX2(knownEleMax[*it], knownEleMax[neighbor.first]);
2285  if (knownEleMax[*it] < eleMax) {
2286  knownEleMax[*it] = eleMax;
2287  } else {
2288  knownEleMax[neighbor.first] = eleMax;
2289  }
2290  changed = true;
2291  }
2292  }
2293  }
2294  }
2295  }
2296 
2297  // collect all nodes within a grade-dependent range around knownElevation-nodes and apply knowElevation forces
2298  std::set<NBNode*> unknownElevation;
2299  for (auto it = knownElevation.begin(); it != knownElevation.end(); ++it) {
2300  const double eleMax = knownEleMax[*it];
2301  const double maxDist = fabs(eleMax) * 100 / layerElevation;
2302  std::map<NBNode*, std::pair<double, double> > neighbors = getNeighboringNodes(*it, maxDist, knownElevation);
2303  for (auto& neighbor : neighbors) {
2304  if (knownElevation.count(neighbor.first) == 0) {
2305  unknownElevation.insert(neighbor.first);
2306  layerForces[neighbor.first].emplace_back(eleMax, neighbor.second.first);
2307  }
2308  }
2309  }
2310 
2311  // apply forces to ground-level nodes (neither in knownElevation nor unknownElevation)
2312  for (auto it = unknownElevation.begin(); it != unknownElevation.end(); ++it) {
2313  double eleMax = -std::numeric_limits<double>::max();
2314  const std::vector<std::pair<double, double> >& primaryLayers = layerForces[*it];
2315  for (const auto& primaryLayer : primaryLayers) {
2316  eleMax = MAX2(eleMax, primaryLayer.first);
2317  }
2318  const double maxDist = fabs(eleMax) * 100 / layerElevation;
2319  std::map<NBNode*, std::pair<double, double> > neighbors = getNeighboringNodes(*it, maxDist, knownElevation);
2320  for (auto& neighbor : neighbors) {
2321  if (knownElevation.count(neighbor.first) == 0 && unknownElevation.count(neighbor.first) == 0) {
2322  layerForces[*it].emplace_back(0, neighbor.second.first);
2323  }
2324  }
2325  }
2326  // compute the elevation for each node as the weighted average of all forces
2327 #ifdef DEBUG_LAYER_ELEVATION
2328  std::cout << "summation of forces\n";
2329 #endif
2330  std::map<NBNode*, double> nodeElevation;
2331  for (auto& layerForce : layerForces) {
2332  const std::vector<std::pair<double, double> >& forces = layerForce.second;
2333  if (knownElevation.count(layerForce.first) != 0) {
2334  // use the maximum value
2335  /*
2336  double eleMax = -std::numeric_limits<double>::max();
2337  for (std::vector<std::pair<double, double> >::const_iterator it_force = forces.begin(); it_force != forces.end(); ++it_force) {
2338  eleMax = MAX2(eleMax, it_force->first);
2339  }
2340  */
2341 #ifdef DEBUG_LAYER_ELEVATION
2342  std::cout << " node=" << it->first->getID() << " knownElevation=" << knownEleMax[it->first] << "\n";
2343 #endif
2344  nodeElevation[layerForce.first] = knownEleMax[layerForce.first];
2345  } else if (forces.size() == 1) {
2346  nodeElevation[layerForce.first] = forces.front().first;
2347  } else {
2348  // use the weighted sum
2349  double distSum = 0;
2350  for (const auto& force : forces) {
2351  distSum += force.second;
2352  }
2353  double weightSum = 0;
2354  double elevation = 0;
2355 #ifdef DEBUG_LAYER_ELEVATION
2356  std::cout << " node=" << it->first->getID() << " distSum=" << distSum << "\n";
2357 #endif
2358  for (const auto& force : forces) {
2359  const double weight = (distSum - force.second) / distSum;
2360  weightSum += weight;
2361  elevation += force.first * weight;
2362 
2363 #ifdef DEBUG_LAYER_ELEVATION
2364  std::cout << " force=" << it_force->first << " dist=" << it_force->second << " weight=" << weight << " ele=" << elevation << "\n";
2365 #endif
2366  }
2367  nodeElevation[layerForce.first] = elevation / weightSum;
2368  }
2369  }
2370 #ifdef DEBUG_LAYER_ELEVATION
2371  std::cout << "final elevations:\n";
2372  for (std::map<NBNode*, double>::iterator it = nodeElevation.begin(); it != nodeElevation.end(); ++it) {
2373  std::cout << " node=" << (it->first)->getID() << " ele=" << it->second << "\n";
2374  }
2375 #endif
2376  // apply node elevations
2377  for (auto& it : nodeElevation) {
2378  NBNode* n = it.first;
2379  Position pos = n->getPosition();
2380  n->reinit(n->getPosition() + Position(0, 0, it.second), n->getType());
2381  }
2382 
2383  // apply way elevation to all edges that had layer information
2384  for (const auto& it : ec) {
2385  NBEdge* edge = it.second;
2386  const PositionVector& geom = edge->getGeometry();
2387  const double length = geom.length2D();
2388  const double zFrom = nodeElevation[edge->getFromNode()];
2389  const double zTo = nodeElevation[edge->getToNode()];
2390  // XXX if the from- or to-node was part of multiple ways with
2391  // different layers, reconstruct the layer value from origID
2392  double dist = 0;
2393  PositionVector newGeom;
2394  for (auto it_pos = geom.begin(); it_pos != geom.end(); ++it_pos) {
2395  if (it_pos != geom.begin()) {
2396  dist += (*it_pos).distanceTo2D(*(it_pos - 1));
2397  }
2398  newGeom.push_back((*it_pos) + Position(0, 0, zFrom + (zTo - zFrom) * dist / length));
2399  }
2400  edge->setGeometry(newGeom);
2401  }
2402 }
2403 
2404 std::map<NBNode*, std::pair<double, double> >
2405 NIImporter_OpenStreetMap::getNeighboringNodes(NBNode* node, double maxDist, const std::set<NBNode*>& knownElevation) {
2406  std::map<NBNode*, std::pair<double, double> > result;
2407  std::set<NBNode*> visited;
2408  std::vector<NBNode*> open;
2409  open.push_back(node);
2410  result[node] = std::make_pair(0, 0);
2411  while (!open.empty()) {
2412  NBNode* n = open.back();
2413  open.pop_back();
2414  if (visited.count(n) != 0) {
2415  continue;
2416  }
2417  visited.insert(n);
2418  const EdgeVector& edges = n->getEdges();
2419  for (auto e : edges) {
2420  NBNode* s = nullptr;
2421  if (n->hasIncoming(e)) {
2422  s = e->getFromNode();
2423  } else {
2424  s = e->getToNode();
2425  }
2426  const double dist = result[n].first + e->getGeometry().length2D();
2427  const double speed = MAX2(e->getSpeed(), result[n].second);
2428  if (result.count(s) == 0) {
2429  result[s] = std::make_pair(dist, speed);
2430  } else {
2431  result[s] = std::make_pair(MIN2(dist, result[s].first), MAX2(speed, result[s].second));
2432  }
2433  if (dist < maxDist && knownElevation.count(s) == 0) {
2434  open.push_back(s);
2435  }
2436  }
2437  }
2438  result.erase(node);
2439  return result;
2440 }
2441 
2442 
2443 std::string
2444 NIImporter_OpenStreetMap::usableType(const std::string& type, const std::string& id, NBTypeCont& tc) {
2445  if (tc.knows(type)) {
2446  return type;
2447  }
2448  if (myUnusableTypes.count(type) > 0) {
2449  return "";
2450  }
2451  if (myKnownCompoundTypes.count(type) > 0) {
2452  return myKnownCompoundTypes[type];
2453  }
2454  // this edge has a type which does not yet exist in the TypeContainer
2456  std::vector<std::string> types;
2457  while (tok.hasNext()) {
2458  std::string t = tok.next();
2459  if (tc.knows(t)) {
2460  if (std::find(types.begin(), types.end(), t) == types.end()) {
2461  types.push_back(t);
2462  }
2463  } else if (tok.size() > 1) {
2464  if (!StringUtils::startsWith(t, "service.")) {
2465  WRITE_WARNINGF(TL("Discarding unknown compound '%' in type '%' (first occurrence for edge '%')."), t, type, id);
2466  }
2467  }
2468  }
2469  if (types.empty()) {
2470  if (!StringUtils::startsWith(type, "service.")) {
2471  WRITE_WARNINGF(TL("Discarding unusable type '%' (first occurrence for edge '%')."), type, id);
2472  }
2473  myUnusableTypes.insert(type);
2474  return "";
2475  }
2476  const std::string newType = joinToString(types, "|");
2477  if (tc.knows(newType)) {
2478  myKnownCompoundTypes[type] = newType;
2479  return newType;
2480  } else if (myKnownCompoundTypes.count(newType) > 0) {
2481  return myKnownCompoundTypes[newType];
2482  } else {
2483  // build a new type by merging all values
2484  int numLanes = 0;
2485  double maxSpeed = 0;
2486  int prio = 0;
2487  double width = NBEdge::UNSPECIFIED_WIDTH;
2488  double sidewalkWidth = NBEdge::UNSPECIFIED_WIDTH;
2489  double bikelaneWidth = NBEdge::UNSPECIFIED_WIDTH;
2490  bool defaultIsOneWay = true;
2491  SVCPermissions permissions = 0;
2493  bool discard = true;
2494  bool hadDiscard = false;
2495  for (auto& type2 : types) {
2496  if (!tc.getEdgeTypeShallBeDiscarded(type2)) {
2497  numLanes = MAX2(numLanes, tc.getEdgeTypeNumLanes(type2));
2498  maxSpeed = MAX2(maxSpeed, tc.getEdgeTypeSpeed(type2));
2499  prio = MAX2(prio, tc.getEdgeTypePriority(type2));
2500  defaultIsOneWay &= tc.getEdgeTypeIsOneWay(type2);
2501  //std::cout << "merging component " << type2 << " into type " << newType << " allows=" << getVehicleClassNames(tc.getPermissions(type2)) << " oneway=" << defaultIsOneWay << "\n";
2502  permissions |= tc.getEdgeTypePermissions(type2);
2503  spreadType = tc.getEdgeTypeSpreadType(type2);
2504  width = MAX2(width, tc.getEdgeTypeWidth(type2));
2505  sidewalkWidth = MAX2(sidewalkWidth, tc.getEdgeTypeSidewalkWidth(type2));
2506  bikelaneWidth = MAX2(bikelaneWidth, tc.getEdgeTypeBikeLaneWidth(type2));
2507  discard = false;
2508  } else {
2509  hadDiscard = true;
2510  }
2511  }
2512  if (hadDiscard && permissions == 0) {
2513  discard = true;
2514  }
2515  if (discard) {
2516  WRITE_WARNINGF(TL("Discarding compound type '%' (first occurrence for edge '%')."), newType, id);
2517  myUnusableTypes.insert(newType);
2518  return "";
2519  }
2520  if (width != NBEdge::UNSPECIFIED_WIDTH) {
2521  width = MAX2(width, SUMO_const_laneWidth);
2522  }
2523  // ensure pedestrians don't run into trains
2524  if (sidewalkWidth == NBEdge::UNSPECIFIED_WIDTH
2525  && (permissions & SVC_PEDESTRIAN) != 0
2526  && (permissions & SVC_RAIL_CLASSES) != 0) {
2527  //std::cout << "patching sidewalk for type '" << newType << "' which allows=" << getVehicleClassNames(permissions) << "\n";
2528  sidewalkWidth = OptionsCont::getOptions().getFloat("default.sidewalk-width");
2529  }
2530 
2531  WRITE_MESSAGEF(TL("Adding new type '%' (first occurrence for edge '%')."), type, id);
2532  tc.insertEdgeType(newType, numLanes, maxSpeed, prio, permissions, spreadType, width,
2533  defaultIsOneWay, sidewalkWidth, bikelaneWidth, 0, 0, 0);
2534  for (auto& type3 : types) {
2535  if (!tc.getEdgeTypeShallBeDiscarded(type3)) {
2536  tc.copyEdgeTypeRestrictionsAndAttrs(type3, newType);
2537  }
2538  }
2539  myKnownCompoundTypes[type] = newType;
2540  return newType;
2541  }
2542 }
2543 
2544 void
2546  const std::string id = toString(e->id);
2547  std::string type = usableType(e->myHighWayType, id, tc);
2548  if (type != "" && isRailway(tc.getEdgeTypePermissions(type))) {
2549  std::vector<NIOSMNode*> nodes;
2550  std::vector<double> usablePositions;
2551  std::vector<int> usableIndex;
2552  for (long long int n : e->myCurrentNodes) {
2553  NIOSMNode* node = myOSMNodes[n];
2554  node->positionMeters = interpretDistance(node);
2555  if (node->positionMeters != std::numeric_limits<double>::max()) {
2556  usablePositions.push_back(node->positionMeters);
2557  usableIndex.push_back((int)nodes.size());
2558  }
2559  nodes.push_back(node);
2560  }
2561  if (usablePositions.size() == 0) {
2562  return;
2563  } else {
2564  bool forward = true;
2565  if (usablePositions.size() == 1) {
2566  WRITE_WARNINGF(TL("Ambiguous railway kilometrage direction for way '%' (assuming forward)"), id);
2567  } else {
2568  forward = usablePositions.front() < usablePositions.back();
2569  }
2570  // check for consistency
2571  for (int i = 1; i < (int)usablePositions.size(); i++) {
2572  if ((usablePositions[i - 1] < usablePositions[i]) != forward) {
2573  WRITE_WARNINGF(TL("Inconsistent railway kilometrage direction for way '%': % (skipping)"), id, toString(usablePositions));
2574  return;
2575  }
2576  }
2577  if (nodes.size() > usablePositions.size()) {
2578  // complete missing values
2579  PositionVector shape;
2580  for (NIOSMNode* node : nodes) {
2581  shape.push_back(Position(node->lon, node->lat, 0));
2582  }
2583  if (!NBNetBuilder::transformCoordinates(shape)) {
2584  return; // error will be given later
2585  }
2586  double sign = forward ? 1 : -1;
2587  // extend backward before first usable value
2588  for (int i = usableIndex.front() - 1; i >= 0; i--) {
2589  nodes[i]->positionMeters = nodes[i + 1]->positionMeters - sign * shape[i].distanceTo2D(shape[i + 1]);
2590  }
2591  // extend forward
2592  for (int i = usableIndex.front() + 1; i < (int)nodes.size(); i++) {
2593  if (nodes[i]->positionMeters == std::numeric_limits<double>::max()) {
2594  nodes[i]->positionMeters = nodes[i - 1]->positionMeters + sign * shape[i].distanceTo2D(shape[i - 1]);
2595  }
2596  }
2597  //std::cout << " way=" << id << " usable=" << toString(usablePositions) << "\n indices=" << toString(usableIndex)
2598  // << " final:\n";
2599  //for (auto n : nodes) {
2600  // std::cout << " " << n->id << " " << n->positionMeters << " " << n->position<< "\n";
2601  //}
2602  }
2603  }
2604  }
2605 }
2606 
2607 
2608 double
2610  if (node->position.size() > 0) {
2611  try {
2612  if (StringUtils::startsWith(node->position, "mi:")) {
2613  return StringUtils::toDouble(node->position.substr(3)) * 1609.344; // meters per mile
2614  } else {
2615  return StringUtils::toDouble(node->position) * 1000;
2616  }
2617  } catch (...) {
2618  WRITE_WARNINGF(TL("Value of railway:position is not numeric ('%') in node '%'."), node->position, toString(node->id));
2619  }
2620  }
2621  return std::numeric_limits<double>::max();
2622 }
2623 
2626  SUMOVehicleClass result = SVC_IGNORING;
2627  if (type == "train") {
2628  result = SVC_RAIL;
2629  } else if (type == "subway") {
2630  result = SVC_SUBWAY;
2631  } else if (type == "aerialway") {
2632  result = SVC_CABLE_CAR;
2633  } else if (type == "light_rail" || type == "monorail") {
2634  result = SVC_RAIL_URBAN;
2635  } else if (type == "share_taxi") {
2636  result = SVC_TAXI;
2637  } else if (type == "minibus") {
2638  result = SVC_BUS;
2639  } else if (type == "trolleybus") {
2640  result = SVC_BUS;
2641  } else if (SumoVehicleClassStrings.hasString(type)) {
2642  result = SumoVehicleClassStrings.get(type);
2643  }
2644  std::string stop = "";
2645  if (result == SVC_TRAM) {
2646  stop = ".tram";
2647  } else if (result == SVC_BUS) {
2648  stop = ".bus";
2649  } else if (isRailway(result)) {
2650  stop = ".train";
2651  }
2652  if (toSet != nullptr && result != SVC_IGNORING) {
2653  toSet->permissions |= result;
2654  toSet->ptStopLength = OptionsCont::getOptions().getFloat("osm.stop-output.length" + stop);
2655  }
2656  return result;
2657 }
2658 
2659 
2660 void
2662  bool multiLane = changeProhibition > 3;
2663  //std::cout << "applyChangeProhibition e=" << e->getID() << " changeProhibition=" << std::bitset<32>(changeProhibition) << " val=" << changeProhibition << "\n";
2664  for (int lane = 0; changeProhibition > 0 && lane < e->getNumLanes(); lane++) {
2665  int code = changeProhibition % 4; // only look at the last 2 bits
2666  SVCPermissions changeLeft = (code & CHANGE_NO_LEFT) == 0 ? SVCAll : (SVCPermissions)SVC_AUTHORITY;
2667  SVCPermissions changeRight = (code & CHANGE_NO_RIGHT) == 0 ? SVCAll : (SVCPermissions)SVC_AUTHORITY;
2668  e->setPermittedChanging(lane, changeLeft, changeRight);
2669  if (multiLane) {
2670  changeProhibition = changeProhibition >> 2;
2671  }
2672  }
2673 }
2674 
2675 
2676 void
2678  if (myImportLaneAccess) {
2679  const int numLanes = e->getNumLanes();
2680  const bool lefthand = OptionsCont::getOptions().getBool("lefthand");
2681  const std::vector<bool>& designated = forward ? nie->myDesignatedLaneForward : nie->myDesignatedLaneBackward;
2682  const std::vector<SVCPermissions>& allowed = forward ? nie->myAllowedLaneForward : nie->myAllowedLaneBackward;
2683  const std::vector<SVCPermissions>& disallowed = forward ? nie->myDisallowedLaneForward : nie->myDisallowedLaneBackward;
2684  for (int lane = 0; lane < numLanes; lane++) {
2685  // laneUse stores from left to right
2686  const int i = lefthand ? lane : numLanes - 1 - lane;
2687  // Extra allowed SVCs for this lane or none if no info was present for the lane
2688  const SVCPermissions extraAllowed = i < (int)allowed.size() ? allowed[i] : (SVCPermissions)SVC_IGNORING;
2689  // Extra disallowed SVCs for this lane or none if no info was present for the lane
2690  const SVCPermissions extraDisallowed = i < (int)disallowed.size() ? disallowed[i] : (SVCPermissions)SVC_IGNORING;
2691  if (i < (int)designated.size() && designated[i]) {
2692  // if designated, delete all permissions
2693  e->setPermissions(SVC_IGNORING, lane);
2694  e->preferVehicleClass(lane, extraAllowed);
2695  }
2696  e->setPermissions((e->getPermissions(lane) | extraAllowed) & (~extraDisallowed), lane);
2697  }
2698  }
2699 }
2700 
2701 void
2702 NIImporter_OpenStreetMap::mergeTurnSigns(std::vector<int>& signs, std::vector<int> signs2) {
2703  if (signs.empty()) {
2704  signs.insert(signs.begin(), signs2.begin(), signs2.end());
2705  } else {
2706  for (int i = 0; i < (int)MIN2(signs.size(), signs2.size()); i++) {
2707  signs[i] |= signs2[i];
2708  }
2709  }
2710 }
2711 
2712 
2713 void
2714 NIImporter_OpenStreetMap::applyTurnSigns(NBEdge* e, const std::vector<int>& turnSigns) {
2715  if (myImportTurnSigns && turnSigns.size() > 0) {
2716  // no sidewalks and bike lanes have been added yet
2717  if ((int)turnSigns.size() == e->getNumLanes()) {
2718  //std::cout << "apply turnSigns for " << e->getID() << " turnSigns=" << toString(turnSigns) << "\n";
2719  for (int i = 0; i < (int)turnSigns.size(); i++) {
2720  // laneUse stores from left to right
2721  const int laneIndex = e->getNumLanes() - 1 - i;
2722  NBEdge::Lane& lane = e->getLaneStruct(laneIndex);
2723  lane.turnSigns = turnSigns[i];
2724  }
2725  } else {
2726  WRITE_WARNINGF(TL("Ignoring turn sign information for % lanes on edge % with % driving lanes"), turnSigns.size(), e->getID(), e->getNumLanes());
2727  }
2728  }
2729 }
2730 
2731 
2732 /****************************************************************************/
#define WRITE_WARNINGF(...)
Definition: MsgHandler.h:296
#define WRITE_MESSAGEF(...)
Definition: MsgHandler.h:298
#define WRITE_ERRORF(...)
Definition: MsgHandler.h:305
#define WRITE_ERROR(msg)
Definition: MsgHandler.h:304
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:295
#define PROGRESS_BEGIN_TIME_MESSAGE(msg)
Definition: MsgHandler.h:301
#define TL(string)
Definition: MsgHandler.h:315
#define PROGRESS_TIME_MESSAGE(before)
Definition: MsgHandler.h:302
#define PROGRESS_DONE_MESSAGE()
Definition: MsgHandler.h:300
#define TLF(string,...)
Definition: MsgHandler.h:317
#define PROGRESS_BEGIN_MESSAGE(msg)
Definition: MsgHandler.h:299
std::vector< NBEdge * > EdgeVector
container for (sorted) edges
Definition: NBCont.h:35
SVCPermissions extraDisallowed(SVCPermissions disallowed, const MMVersion &networkVersion)
Interprets disallowed vehicles depending on network version.
const SVCPermissions SVCAll
all VClasses are allowed
bool isRailway(SVCPermissions permissions)
Returns whether an edge with the given permissions is a railway edge.
StringBijection< SUMOVehicleClass > SumoVehicleClassStrings(sumoVehicleClassStringInitializer, SVC_CUSTOM2, false)
long long int SVCPermissions
bitset where each bit declares whether a certain SVC may use this edge/lane
SUMOVehicleClass
Definition of vehicle classes to differ between different lane usage and authority types.
@ SVC_SHIP
is an arbitrary ship
@ SVC_PRIVATE
private vehicles
@ SVC_TRUCK
vehicle is a large transport vehicle
@ SVC_ROAD_CLASSES
classes which drive on roads
@ SVC_IGNORING
vehicles ignoring classes
@ SVC_RAIL
vehicle is a not electrified rail
@ SVC_RAIL_CLASSES
classes which drive on tracks
@ SVC_CABLE_CAR
@ SVC_PASSENGER
vehicle is a passenger car (a "normal" car)
@ SVC_BICYCLE
vehicle is a bicycle
@ SVC_RAIL_FAST
vehicle that is allowed to drive on high-speed rail tracks
@ SVC_TRAILER
vehicle is a large transport vehicle
@ SVC_RAIL_ELECTRIC
rail vehicle that requires electrified tracks
@ SVC_RAIL_URBAN
vehicle is a city rail
@ SVC_EMERGENCY
public emergency vehicles
@ SVC_AUTHORITY
authorities vehicles
@ SVC_TRAM
vehicle is a light rail
@ SVC_PUBLIC_CLASSES
public transport
@ SVC_TAXI
vehicle is a taxi
@ SVC_BUS
vehicle is a bus
@ SVC_SUBWAY
@ SVC_PEDESTRIAN
pedestrian
TrafficLightType
@ SUMO_TAG_MEMBER
@ SUMO_TAG_ND
@ SUMO_TAG_WAY
@ SUMO_TAG_NODE
alternative definition for junction
@ SUMO_TAG_RELATION
@ SUMO_TAG_TAG
LaneSpreadFunction
Numbers representing special SUMO-XML-attribute values Information how the edge's lateral offset shal...
@ PARTLEFT
The link is a partial left direction.
@ RIGHT
The link is a (hard) right direction.
@ TURN
The link is a 180 degree turn.
@ LEFT
The link is a (hard) left direction.
@ STRAIGHT
The link is a straight direction.
@ PARTRIGHT
The link is a partial right direction.
@ NODIR
The link has no direction (is a dead end link)
@ SUMO_ATTR_LON
@ SUMO_ATTR_V
@ SUMO_ATTR_REF
@ SUMO_ATTR_LAT
@ SUMO_ATTR_TYPE
@ SUMO_ATTR_ID
@ SUMO_ATTR_ACTION
@ SUMO_ATTR_K
const double SUMO_const_laneWidth
Definition: StdDefs.h:48
T MIN2(T a, T b)
Definition: StdDefs.h:76
T MAX2(T a, T b)
Definition: StdDefs.h:82
std::string joinToStringSorting(const std::vector< T > &v, const T_BETWEEN &between, std::streamsize accuracy=gPrecision)
Definition: ToString.h:299
std::string joinToString(const std::vector< T > &v, const T_BETWEEN &between, std::streamsize accuracy=gPrecision)
Definition: ToString.h:283
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:46
static bool isReadable(std::string path)
Checks whether the given file is readable.
Definition: FileHelpers.cpp:51
void setFileName(const std::string &name)
Sets the current file name.
bool wasInformed() const
Returns the information whether any messages were added.
Definition: MsgHandler.cpp:416
static MsgHandler * getErrorInstance()
Returns the instance to add errors to.
Definition: MsgHandler.cpp:92
Storage for edges, including some functionality operating on multiple edges.
Definition: NBEdgeCont.h:59
int extractRoundabouts()
Determines which edges have been marked as roundabouts and stores them internally.
bool insert(NBEdge *edge, bool ignorePrunning=false)
Adds an edge to the dictionary.
Definition: NBEdgeCont.cpp:182
The representation of a single edge during network building.
Definition: NBEdge.h:92
static const int TURN_SIGN_SHIFT_BUS
shift values for decoding turn signs
Definition: NBEdge.h:375
static const int TURN_SIGN_SHIFT_BICYCLE
Definition: NBEdge.h:377
void setPermittedChanging(int lane, SVCPermissions changeLeft, SVCPermissions changeRight)
set allowed classes for changing to the left and right from the given lane
Definition: NBEdge.cpp:4359
SVCPermissions getPermissions(int lane=-1) const
get the union of allowed classes over all lanes or for a specific lane
Definition: NBEdge.cpp:4368
void setPermissions(SVCPermissions permissions, int lane=-1)
set allowed/disallowed classes for the given lane or for all lanes if -1 is given
Definition: NBEdge.cpp:4331
void addBikeLane(double width)
add a bicycle lane of the given width and shift existing connctions
Definition: NBEdge.cpp:4574
static const double UNSPECIFIED_FRICTION
unspecified lane friction
Definition: NBEdge.h:351
bool addEdge2EdgeConnection(NBEdge *dest, bool overrideRemoval=false, SVCPermissions permission=SVC_UNSPECIFIED)
Adds a connection to another edge.
Definition: NBEdge.cpp:1091
void setTurnSignTarget(const std::string &target)
Definition: NBEdge.h:1553
void setDistance(double distance)
set kilometrage at start of edge (negative value implies couting down along the edge)
Definition: NBEdge.h:1409
const std::string & getID() const
Definition: NBEdge.h:1524
const std::vector< NBEdge::Lane > & getLanes() const
Returns the lane definitions.
Definition: NBEdge.h:726
NBNode * getToNode() const
Returns the destination node of the edge.
Definition: NBEdge.h:542
void setLaneWidth(int lane, double width)
set lane specific width (negative lane implies set for all lanes)
Definition: NBEdge.cpp:4154
void addSidewalk(double width)
add a pedestrian sidewalk of the given width and shift existing connctions
Definition: NBEdge.cpp:4562
int getNumLanes() const
Returns the number of lanes.
Definition: NBEdge.h:516
const PositionVector & getGeometry() const
Returns the geometry of the edge.
Definition: NBEdge.h:779
void removeFromConnections(NBEdge *toEdge, int fromLane=-1, int toLane=-1, bool tryLater=false, const bool adaptToLaneRemoval=false, const bool keepPossibleTurns=false)
Removes the specified connection(s)
Definition: NBEdge.cpp:1441
bool isConnectedTo(const NBEdge *e, const bool ignoreTurnaround=false) const
Returns the information whethe a connection to the given edge has been added (or computed)
Definition: NBEdge.cpp:1324
static const int TURN_SIGN_SHIFT_TAXI
Definition: NBEdge.h:376
void preferVehicleClass(int lane, SVCPermissions vclasses)
prefer certain vehicle classes for the given lane or for all lanes if -1 is given (ensures also permi...
Definition: NBEdge.cpp:4140
static const double UNSPECIFIED_WIDTH
unspecified lane width
Definition: NBEdge.h:342
static const double UNSPECIFIED_OFFSET
unspecified lane offset
Definition: NBEdge.h:345
Lane & getLaneStruct(int lane)
Definition: NBEdge.h:1424
void setJunctionPriority(const NBNode *const node, int prio)
Sets the junction priority of the edge.
Definition: NBEdge.cpp:2132
void setGeometry(const PositionVector &g, bool inner=false)
(Re)sets the edge's geometry
Definition: NBEdge.cpp:631
NBNode * getFromNode() const
Returns the origin node of the edge.
Definition: NBEdge.h:535
Instance responsible for building networks.
Definition: NBNetBuilder.h:107
static bool transformCoordinates(PositionVector &from, bool includeInBoundary=true, GeoConvHelper *from_srs=nullptr)
NBTypeCont & getTypeCont()
Returns a reference to the type container.
Definition: NBNetBuilder.h:149
NBParkingCont & getParkingCont()
Definition: NBNetBuilder.h:173
NBPTLineCont & getPTLineCont()
Returns a reference to the pt line container.
Definition: NBNetBuilder.h:169
NBEdgeCont & getEdgeCont()
Definition: NBNetBuilder.h:139
NBPTStopCont & getPTStopCont()
Returns a reference to the pt stop container.
Definition: NBNetBuilder.h:164
static bool transformCoordinate(Position &from, bool includeInBoundary=true, GeoConvHelper *from_srs=nullptr)
transforms loaded coordinates handles projections, offsets (using GeoConvHelper) and import of height...
NBNodeCont & getNodeCont()
Returns a reference to the node container.
Definition: NBNetBuilder.h:144
NBTrafficLightLogicCont & getTLLogicCont()
Returns a reference to the traffic light logics container.
Definition: NBNetBuilder.h:154
Container for nodes during the netbuilding process.
Definition: NBNodeCont.h:57
bool insert(const std::string &id, const Position &position, NBDistrict *district=0)
Inserts a node into the map.
Definition: NBNodeCont.cpp:87
NBNode * retrieve(const std::string &id) const
Returns the node with the given name.
Definition: NBNodeCont.cpp:116
Represents a single node (junction) during network building.
Definition: NBNode.h:66
bool hasIncoming(const NBEdge *const e) const
Returns whether the given edge ends at this node.
Definition: NBNode.cpp:1856
NBNode::Crossing * addCrossing(EdgeVector edges, double width, bool priority, int tlIndex=-1, int tlIndex2=-1, const PositionVector &customShape=PositionVector::EMPTY, bool fromSumoNet=false, const Parameterised *params=nullptr)
add a pedestrian crossing to this node
Definition: NBNode.cpp:3837
void reinit(const Position &position, SumoXMLNodeType type, bool updateEdgeGeometries=false)
Resets initial values.
Definition: NBNode.cpp:338
SumoXMLNodeType getType() const
Returns the type of this node.
Definition: NBNode.h:285
const EdgeVector & getOutgoingEdges() const
Returns this node's outgoing edges (The edges which start at this node)
Definition: NBNode.h:273
const EdgeVector & getEdges() const
Returns all edges which participate in this node (Edges that start or end at this node)
Definition: NBNode.h:278
bool checkCrossingDuplicated(EdgeVector edges)
return true if there already exist a crossing with the same edges as the input
Definition: NBNode.cpp:2850
const EdgeVector & getIncomingEdges() const
Returns this node's incoming edges (The edges which yield in this node)
Definition: NBNode.h:268
const Position & getPosition() const
Definition: NBNode.h:260
static const int FORWARD
edge directions (for pedestrian related stuff)
Definition: NBNode.h:216
void setFringeType(FringeType fringeType)
set method for computing right-of-way
Definition: NBNode.h:574
A traffic light logics which must be computed (only nodes/edges are given)
Definition: NBOwnTLDef.h:44
void setNumOfStops(int numStops, int missingBefore, int missingAfter)
Definition: NBPTLine.cpp:166
void addWayNode(long long int way, long long int node)
Definition: NBPTLine.cpp:121
const std::vector< std::shared_ptr< NBPTStop > > & getStops()
Definition: NBPTLine.cpp:68
void addPTStop(std::shared_ptr< NBPTStop > pStop)
Definition: NBPTLine.cpp:54
Container for public transport stops during the net building process.
Definition: NBPTStopCont.h:44
int cleanupDeleted(NBEdgeCont &cont)
remove stops on non existing (removed) edges
const std::map< std::string, std::shared_ptr< NBPTStop > > & getStops() const
Returns an unmodifiable reference to the stored pt stops.
Definition: NBPTStopCont.h:62
std::shared_ptr< NBPTStop > get(std::string id) const
Retrieve a previously inserted pt stop.
bool insert(std::shared_ptr< NBPTStop > ptStop, bool floating=false)
Inserts a node into the map.
The representation of an imported parking area.
Definition: NBParking.h:42
static const std::string OSM_DIRECTION
processing parameter for rail signal edges and nodes
static const std::string OSM_SIGNAL_DIRECTION
A container for traffic light definitions and built programs.
bool insert(NBTrafficLightDefinition *logic, bool forceInsert=false)
Adds a logic definition to the dictionary.
A storage for available edgeTypes of edges.
Definition: NBTypeCont.h:52
bool getEdgeTypeShallBeDiscarded(const std::string &edgeType) const
Returns the information whether edges of this edgeType shall be discarded.
Definition: NBTypeCont.cpp:526
void insertEdgeType(const std::string &id, int numLanes, double maxSpeed, int prio, SVCPermissions permissions, LaneSpreadFunction spreadType, double width, bool oneWayIsDefault, double sidewalkWidth, double bikeLaneWidth, double widthResolution, double maxWidth, double minWidth)
Adds a edgeType into the list.
Definition: NBTypeCont.cpp:204
bool copyEdgeTypeRestrictionsAndAttrs(const std::string &fromId, const std::string &toId)
Copy restrictions to a edgeType.
Definition: NBTypeCont.cpp:342
double getEdgeTypeSpeed(const std::string &edgeType) const
Returns the maximal velocity for the given edgeType [m/s].
Definition: NBTypeCont.cpp:503
int getEdgeTypePriority(const std::string &edgeType) const
Returns the priority for the given edgeType.
Definition: NBTypeCont.cpp:514
int getEdgeTypeNumLanes(const std::string &edgeType) const
Returns the number of lanes for the given edgeType.
Definition: NBTypeCont.cpp:497
double getEdgeTypeWidth(const std::string &edgeType) const
Returns the lane width for the given edgeType [m].
Definition: NBTypeCont.cpp:564
SVCPermissions getEdgeTypePermissions(const std::string &edgeType) const
Returns allowed vehicle classes for the given edgeType.
Definition: NBTypeCont.cpp:552
bool knows(const std::string &edgeType) const
Returns whether the named edgeType is in the container.
Definition: NBTypeCont.cpp:303
double getEdgeTypeSidewalkWidth(const std::string &edgeType) const
Returns the lane width for a sidewalk to be added [m].
Definition: NBTypeCont.cpp:570
LaneSpreadFunction getEdgeTypeSpreadType(const std::string &edgeType) const
Returns spreadType for the given edgeType.
Definition: NBTypeCont.cpp:558
double getEdgeTypeBikeLaneWidth(const std::string &edgeType) const
Returns the lane width for a bike lane to be added [m].
Definition: NBTypeCont.cpp:576
bool getEdgeTypeIsOneWay(const std::string &edgeType) const
Returns whether edges are one-way per default for the given edgeType.
Definition: NBTypeCont.cpp:520
bool operator()(const Edge *e1, const Edge *e2) const
An internal definition of a loaded edge.
std::vector< SVCPermissions > myDisallowedLaneBackward
(optional) information about additional disallowed SVCs on backward lane(s)
std::map< std::string, std::string > myExtraTags
Additionally tagged information.
std::vector< double > myWidthLanesForward
Information on lane width.
WayType mySidewalkType
Information about the kind of sidwalk along this road.
std::vector< SVCPermissions > myDisallowedLaneForward
(optional) information about additional disallowed SVCs on forward lane(s)
bool myCurrentIsRoad
Information whether this is a road.
WayType myCyclewayType
Information about the kind of cycleway along this road.
int myNoLanesForward
number of lanes in forward direction or 0 if unknown, negative if backwards lanes are meant
double myMaxSpeed
maximum speed in km/h, or MAXSPEED_UNGIVEN
std::string ref
The edge's track name.
std::vector< SVCPermissions > myAllowedLaneForward
(optional) information about additional allowed SVCs on forward lane(s)
std::string myHighWayType
The type, stored in "highway" key.
const long long int id
The edge's id.
bool myAmInRoundabout
Information whether this road is part of a roundabout.
int myLayer
Information about the relative z-ordering of ways.
std::vector< bool > myDesignatedLaneBackward
(optional) information about whether the backward lanes are designated to some SVCs
SVCPermissions myExtraDisallowed
Extra permissions prohibited from tags instead of highway type.
std::vector< SVCPermissions > myAllowedLaneBackward
(optional) information about additional allowed SVCs on backward lane(s)
int myNoLanes
number of lanes, or -1 if unknown
std::vector< int > myTurnSignsForward
turning direction (arrows printed on the road)
std::vector< long long int > myCurrentNodes
The list of nodes this edge is made of.
int myParkingType
Information about road-side parking.
double myMaxSpeedBackward
maximum speed in km/h, or MAXSPEED_UNGIVEN
WayType myBuswayType
Information about the kind of busway along this road.
int myChangeForward
Information about change prohibitions (forward direction.
SVCPermissions myExtraAllowed
Extra permissions added from tags instead of highway type.
int myChangeBackward
Information about change prohibitions (backward direction.
std::string streetName
The edge's street name.
WayType myRailDirection
Information about the direction(s) of railway usage.
std::vector< bool > myDesignatedLaneForward
(optional) information about whether the forward lanes are designated to some SVCs
std::string myIsOneWay
Information whether this is an one-way road.
A class which extracts OSM-edges from a parsed OSM-file.
void interpretLaneUse(const std::string &value, SUMOVehicleClass svc, const bool forward) const
EdgesHandler(const std::map< long long int, NIOSMNode * > &osmNodes, std::map< long long int, Edge * > &toFill, std::map< long long int, Edge * > &platformShapes)
Constructor.
int interpretChangeType(const std::string &value) const
void myEndElement(int element) override
Called when a closing tag occurs.
double interpretSpeed(const std::string &key, std::string value)
std::map< std::string, double > mySpeedMap
A map of non-numeric speed descriptions to their numeric values.
void myStartElement(int element, const SUMOSAXAttributes &attrs) override
Called on the opening of a tag;.
A class which extracts OSM-nodes from a parsed OSM-file.
void myStartElement(int element, const SUMOSAXAttributes &attrs) override
Called on the opening of a tag;.
NodesHandler(std::map< long long int, NIOSMNode * > &toFill, std::set< NIOSMNode *, CompareNodes > &uniqueNodes, const OptionsCont &cont)
Constructor.
void myEndElement(int element) override
Called when a closing tag occurs.
StringVector myRailSignalRules
custom requirements for rail signal tagging
A class which extracts relevant relation information from a parsed OSM-file.
void myEndElement(int element) override
Called when a closing tag occurs.
void resetValues()
reset members to their defaults for parsing a new relation
void myStartElement(int element, const SUMOSAXAttributes &attrs) override
Called on the opening of a tag;.
RelationHandler(const std::map< long long int, NIOSMNode * > &osmNodes, const std::map< long long int, Edge * > &osmEdges, NBPTStopCont *nbptStopCont, const std::map< long long int, Edge * > &platfromShapes, NBPTLineCont *nbptLineCont, const OptionsCont &oc)
Constructor.
bool checkEdgeRef(long long int ref) const
check whether a referenced way has a corresponding edge
bool applyRestriction() const
try to apply the parsed restriction and return whether successful
NBEdge * findEdgeRef(long long int wayRef, const std::vector< NBEdge * > &candidates) const
try to find the way segment among candidates
Importer for networks stored in OpenStreetMap format.
int insertEdge(Edge *e, int index, NBNode *from, NBNode *to, const std::vector< long long int > &passed, NBNetBuilder &nb, const NBNode *first, const NBNode *last)
Builds an NBEdge.
std::map< long long int, Edge * > myEdges
the map from OSM way ids to edge objects
bool myImportCrossings
import crossings
std::map< long long int, NIOSMNode * > myOSMNodes
the map from OSM node ids to actual nodes
static void loadNetwork(const OptionsCont &oc, NBNetBuilder &nb)
Loads content of the optionally given OSM file.
static const long long int INVALID_ID
void applyLaneUse(NBEdge *e, NIImporter_OpenStreetMap::Edge *nie, const bool forward)
Applies lane use information from nie to e.
static const double MAXSPEED_UNGIVEN
std::map< long long int, Edge * > myPlatformShapes
the map from OSM way ids to platform shapes
void load(const OptionsCont &oc, NBNetBuilder &nb)
void applyTurnSigns(NBEdge *e, const std::vector< int > &turnSigns)
bool myImportSidewalks
import sidewalks
std::set< NIOSMNode *, CompareNodes > myUniqueNodes
the set of unique nodes used in NodesHandler, used when freeing memory
static bool myAllAttributes
whether additional way and node attributes shall be imported
void reconstructLayerElevation(double layerElevation, NBNetBuilder &nb)
reconstruct elevation from layer info
static SUMOVehicleClass interpretTransportType(const std::string &type, NIOSMNode *toSet=nullptr)
translate osm transport designations into sumo vehicle class
bool myImportLaneAccess
import lane specific access restrictions
bool myImportTurnSigns
import turning signals (turn:lanes) to guide connection building
std::map< std::string, std::string > myKnownCompoundTypes
The compound types that have already been mapped to other known types.
static const std::string compoundTypeSeparator
The separator within newly created compound type names.
std::set< std::string > myUnusableTypes
The compounds types that do not contain known types.
std::map< NBNode *, std::pair< double, double > > getNeighboringNodes(NBNode *node, double maxDist, const std::set< NBNode * > &knownElevation)
collect neighboring nodes with their road distance and maximum between-speed. Search does not continu...
static std::set< std::string > myExtraAttributes
extra attributes to import
bool myImportBikeAccess
import bike path specific permissions and directions
static double interpretDistance(NIOSMNode *node)
read distance value from node and return value in m
NBNode * insertNodeChecking(long long int id, NBNodeCont &nc, NBTrafficLightLogicCont &tlsc)
Builds an NBNode.
static void mergeTurnSigns(std::vector< int > &signs, std::vector< int > signs2)
void extendRailwayDistances(Edge *e, NBTypeCont &tc)
extend kilometrage data for all nodes along railway
std::string usableType(const std::string &type, const std::string &id, NBTypeCont &tc)
check whether the type is known or consists of known type compounds. return empty string otherwise
static void applyChangeProhibition(NBEdge *e, int changeProhibition)
const std::string & getID() const
Returns the id.
Definition: Named.h:74
A storage for options typed value containers)
Definition: OptionsCont.h:89
bool isSet(const std::string &name, bool failOnNonExistant=true) const
Returns the information whether the named option is set.
double getFloat(const std::string &name) const
Returns the double-value of the named option (only for Option_Float)
std::string getString(const std::string &name) const
Returns the string-value of the named option (only for Option_String)
bool getBool(const std::string &name) const
Returns the boolean-value of the named option (only for Option_Bool)
const StringVector & getStringVector(const std::string &name) const
Returns the list of string-value of the named option (only for Option_StringVector)
static OptionsCont & getOptions()
Retrieves the options.
Definition: OptionsCont.cpp:60
void unsetParameter(const std::string &key)
Removes a parameter.
bool hasParameter(const std::string &key) const
Returns whether the parameter is set.
virtual const std::string getParameter(const std::string &key, const std::string defaultValue="") const
Returns the value for a given key.
const Parameterised::Map & getParametersMap() const
Returns the inner key/value map.
virtual void setParameter(const std::string &key, const std::string &value)
Sets a parameter.
void updateParameters(const Parameterised::Map &mapArg)
Adds or updates all given parameters from the map.
A point in 2D or 3D with translation and scaling methods.
Definition: Position.h:37
A list of positions.
double length2D() const
Returns the length.
double length() const
Returns the length.
PositionVector reverse() const
reverse position vector
static RGBColor parseColor(std::string coldef)
Parses a color information.
Definition: RGBColor.cpp:239
Encapsulated SAX-Attributes.
T getOpt(int attr, const char *objectid, bool &ok, T defaultValue=T(), bool report=true) const
Tries to read given attribute assuming it is an int.
virtual std::string getStringSecure(int id, const std::string &def) const =0
Returns the string-value of the named (by its enum-value) attribute.
T get(int attr, const char *objectid, bool &ok, bool report=true) const
Tries to read given attribute assuming it is an int.
virtual bool hasAttribute(int id) const =0
Returns the information whether the named (by its enum-value) attribute is within the current list.
SAX-handler base for SUMO-files.
static StringBijection< TrafficLightType > TrafficLightTypes
traffic light types
T get(const std::string &str) const
int size() const
returns the number of existing substrings
std::vector< std::string > getVector()
return vector of strings
bool hasNext()
returns the information whether further substrings exist
std::string next()
returns the next substring when it exists. Otherwise the behaviour is undefined
static long long int toLong(const std::string &sData)
converts a string into the long value described by it by calling the char-type converter,...
static double toDouble(const std::string &sData)
converts a string into the double value described by it by calling the char-type converter
static std::string escapeXML(const std::string &orig, const bool maskDoubleHyphen=false)
Replaces the standard escapes by their XML entities.
static std::string prune(const std::string &str)
Removes trailing and leading whitechars.
Definition: StringUtils.cpp:58
static double parseDist(const std::string &sData)
parse a distance, length or width value with a unit
static bool startsWith(const std::string &str, const std::string prefix)
Checks whether a given string starts with the prefix.
static double parseSpeed(const std::string &sData, const bool defaultKmph=true)
parse a speed value with a unit
static bool endsWith(const std::string &str, const std::string suffix)
Checks whether a given string ends with the suffix.
static int toInt(const std::string &sData)
converts a string into the integer value described by it by calling the char-type converter,...
static bool toBool(const std::string &sData)
converts a string into the bool value described by it by calling the char-type converter
static SUMOSAXReader * getSAXReader(SUMOSAXHandler &handler, const bool isNet=false, const bool isRoute=false)
Builds a reader and assigns the handler to it.
Definition: XMLSubSys.cpp:132
An (internal) definition of a single lane of an edge.
Definition: NBEdge.h:143
int turnSigns
turning signs printed on the road, bitset of LinkDirection (imported from OSM)
Definition: NBEdge.h:195
An internal representation of an OSM-node.
SVCPermissions permissions
type of pt stop
NBNode * node
the NBNode that was instantiated
double positionMeters
position converted to m (using highest precision available)
std::string position
kilometrage/mileage
const long long int id
The node's id.
bool pedestrianCrossing
Whether this is a pedestrian crossing.
bool tlsControlled
Whether this is a tls controlled junction.
double ptStopLength
The length of the pt stop.
bool ptStopPosition
Whether this is a public transport stop position.
std::string name
The name of the node.
bool railwayCrossing
Whether this is a railway crossing.
double ele
The elevation of this node.
bool railwayBufferStop
Whether this is a railway buffer stop.
const double lon
The longitude the node is located at.
const double lat
The latitude the node is located at.
bool railwaySignal
Whether this is a railway (main) signal.
WayType myRailDirection
Information about the direction(s) of railway usage.