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