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