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