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