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