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