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