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