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