1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file NIImporter_OpenStreetMap.cpp
11 /// @author Daniel Krajzewicz
12 /// @author Jakob Erdmann
13 /// @author Michael Behrisch
14 /// @author Walter Bamberger
15 /// @author Gregor Laemmel
16 /// @date Mon, 14.04.2008
17 /// @version $Id$
18 ///
19 // Importer for networks stored in OpenStreetMap format
20 /****************************************************************************/
21
22
23 // ===========================================================================
24 // included modules
25 // ===========================================================================
26 #include <config.h>
27 #include <algorithm>
28 #include <set>
29 #include <functional>
30 #include <sstream>
31 #include <limits>
32 #include <utils/xml/SUMOSAXHandler.h>
33 #include <utils/common/UtilExceptions.h>
34 #include <utils/common/StringUtils.h>
35 #include <utils/common/ToString.h>
36 #include <utils/common/MsgHandler.h>
37 #include <utils/common/StringUtils.h>
38 #include <utils/common/StringTokenizer.h>
39 #include <netbuild/NBEdge.h>
40 #include <netbuild/NBEdgeCont.h>
41 #include <netbuild/NBNode.h>
42 #include <netbuild/NBNodeCont.h>
43 #include <netbuild/NBNetBuilder.h>
44 #include <netbuild/NBOwnTLDef.h>
45 #include <utils/xml/SUMOXMLDefinitions.h>
46 #include <utils/geom/GeoConvHelper.h>
47 #include <utils/geom/GeomConvHelper.h>
48 #include <utils/options/OptionsCont.h>
49 #include <utils/common/FileHelpers.h>
50 #include <utils/xml/XMLSubSys.h>
51 #include <netbuild/NBPTLine.h>
52 #include <netbuild/NBPTLineCont.h>
53 #include "NILoader.h"
54 #include "NIImporter_OpenStreetMap.h"
55
56 //#define DEBUG_LAYER_ELEVATION
57
58 // ---------------------------------------------------------------------------
59 // static members
60 // ---------------------------------------------------------------------------
61 const double NIImporter_OpenStreetMap::MAXSPEED_UNGIVEN = -1;
62
63 const long long int NIImporter_OpenStreetMap::INVALID_ID = std::numeric_limits<long long int>::max();
64
65 // ===========================================================================
66 // Private classes
67 // ===========================================================================
68
69 /** @brief Functor which compares two Edges
70 */
71 class NIImporter_OpenStreetMap::CompareEdges {
72 public:
operator ()(const Edge * e1,const Edge * e2) const73 bool operator()(const Edge* e1, const Edge* e2) const {
74 if (e1->myHighWayType != e2->myHighWayType) {
75 return e1->myHighWayType > e2->myHighWayType;
76 }
77 if (e1->myNoLanes != e2->myNoLanes) {
78 return e1->myNoLanes > e2->myNoLanes;
79 }
80 if (e1->myNoLanesForward != e2->myNoLanesForward) {
81 return e1->myNoLanesForward > e2->myNoLanesForward;
82 }
83 if (e1->myMaxSpeed != e2->myMaxSpeed) {
84 return e1->myMaxSpeed > e2->myMaxSpeed;
85 }
86 if (e1->myIsOneWay != e2->myIsOneWay) {
87 return e1->myIsOneWay > e2->myIsOneWay;
88 }
89 return e1->myCurrentNodes > e2->myCurrentNodes;
90 }
91 };
92
93 // ===========================================================================
94 // method definitions
95 // ===========================================================================
96 // ---------------------------------------------------------------------------
97 // static methods
98 // ---------------------------------------------------------------------------
99 const std::string NIImporter_OpenStreetMap::compoundTypeSeparator("|"); //clang-tidy says: "compundTypeSeparator with
100 // static storage duration my throw an exception that cannot be caught
101
102 void
loadNetwork(const OptionsCont & oc,NBNetBuilder & nb)103 NIImporter_OpenStreetMap::loadNetwork(const OptionsCont& oc, NBNetBuilder& nb) {
104 NIImporter_OpenStreetMap importer;
105 importer.load(oc, nb);
106 }
107
108 NIImporter_OpenStreetMap::NIImporter_OpenStreetMap() = default;
109
~NIImporter_OpenStreetMap()110 NIImporter_OpenStreetMap::~NIImporter_OpenStreetMap() {
111 // delete nodes
112 for (auto myUniqueNode : myUniqueNodes) {
113 delete myUniqueNode;
114 }
115 // delete edges
116 for (auto& myEdge : myEdges) {
117 delete myEdge.second;
118 }
119 // delete platform shapes
120 for (auto& myPlatformShape : myPlatformShapes) {
121 delete myPlatformShape.second;
122 }
123 }
124
125 void
load(const OptionsCont & oc,NBNetBuilder & nb)126 NIImporter_OpenStreetMap::load(const OptionsCont& oc, NBNetBuilder& nb) {
127 // check whether the option is set (properly)
128 if (!oc.isSet("osm-files")) {
129 return;
130 }
131 /* Parse file(s)
132 * Each file is parsed twice: first for nodes, second for edges. */
133 std::vector<std::string> files = oc.getStringVector("osm-files");
134 // load nodes, first
135 NodesHandler nodesHandler(myOSMNodes, myUniqueNodes, oc);
136 for (std::vector<std::string>::const_iterator file = files.begin(); file != files.end(); ++file) {
137 // nodes
138 if (!FileHelpers::isReadable(*file)) {
139 WRITE_ERROR("Could not open osm-file '" + *file + "'.");
140 return;
141 }
142 nodesHandler.setFileName(*file);
143 PROGRESS_BEGIN_MESSAGE("Parsing nodes from osm-file '" + *file + "'");
144 if (!XMLSubSys::runParser(nodesHandler, *file)) {
145 return;
146 }
147 PROGRESS_DONE_MESSAGE();
148 }
149 // load edges, then
150 EdgesHandler edgesHandler(myOSMNodes, myEdges, myPlatformShapes);
151 for (std::vector<std::string>::const_iterator file = files.begin(); file != files.end(); ++file) {
152 // edges
153 edgesHandler.setFileName(*file);
154 PROGRESS_BEGIN_MESSAGE("Parsing edges from osm-file '" + *file + "'");
155 XMLSubSys::runParser(edgesHandler, *file);
156 PROGRESS_DONE_MESSAGE();
157 }
158
159 /* Remove duplicate edges with the same shape and attributes */
160 if (!oc.getBool("osm.skip-duplicates-check")) {
161 PROGRESS_BEGIN_MESSAGE("Removing duplicate edges");
162 if (myEdges.size() > 1) {
163 std::set<const Edge*, CompareEdges> dupsFinder;
164 for (auto it = myEdges.begin(); it != myEdges.end();) {
165 if (dupsFinder.count(it->second) > 0) {
166 WRITE_MESSAGE("Found duplicate edges. Removing " + toString(it->first));
167 delete it->second;
168 myEdges.erase(it++);
169 } else {
170 dupsFinder.insert(it->second);
171 it++;
172 }
173 }
174 }
175 PROGRESS_DONE_MESSAGE();
176 }
177
178 /* Mark which nodes are used (by edges or traffic lights).
179 * This is necessary to detect which OpenStreetMap nodes are for
180 * geometry only */
181 std::map<long long int, int> nodeUsage;
182 // Mark which nodes are used by edges (begin and end)
183 for (std::map<long long int, Edge*>::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
184 Edge* e = (*i).second;
185 assert(e->myCurrentIsRoad);
186 for (std::vector<long long int>::const_iterator j = e->myCurrentNodes.begin();
187 j != e->myCurrentNodes.end();
188 ++j) {
189 if (nodeUsage.find(*j) == nodeUsage.end()) {
190 nodeUsage[*j] = 0;
191 }
192 nodeUsage[*j] = nodeUsage[*j] + 1;
193 }
194 }
195 // Mark which nodes are used by traffic lights
196 for (std::map<long long int, NIOSMNode*>::const_iterator nodesIt = myOSMNodes.begin();
197 nodesIt != myOSMNodes.end();
198 ++nodesIt) {
199 if (nodesIt->second->tlsControlled || nodesIt->second->railwaySignal /* || nodesIt->second->railwayCrossing*/) {
200 // If the key is not found in the map, the value is automatically
201 // initialized with 0.
202 nodeUsage[nodesIt->first] += 1;
203 }
204 }
205
206 /* Instantiate edges
207 * Only those nodes in the middle of an edge which are used by more than
208 * one edge are instantiated. Other nodes are considered as geometry nodes. */
209 NBNodeCont& nc = nb.getNodeCont();
210 NBTrafficLightLogicCont& tlsc = nb.getTLLogicCont();
211 for (auto& myEdge : myEdges) {
212 Edge* e = myEdge.second;
213 assert(e->myCurrentIsRoad);
214 if (e->myCurrentNodes.size() < 2) {
215 WRITE_WARNING("Discarding way '" + toString(e->id) + "' because it has only " +
216 toString(e->myCurrentNodes.size()) + " node(s)");
217 continue;
218 }
219 // build nodes;
220 // - the from- and to-nodes must be built in any case
221 // - the in-between nodes are only built if more than one edge references them
222 NBNode* currentFrom = insertNodeChecking(*e->myCurrentNodes.begin(), nc, tlsc);
223 NBNode* last = insertNodeChecking(*(e->myCurrentNodes.end() - 1), nc, tlsc);
224 int running = 0;
225 std::vector<long long int> passed;
226 for (auto j = e->myCurrentNodes.begin(); j != e->myCurrentNodes.end(); ++j) {
227 passed.push_back(*j);
228 if (nodeUsage[*j] > 1 && j != e->myCurrentNodes.end() - 1 && j != e->myCurrentNodes.begin()) {
229 NBNode* currentTo = insertNodeChecking(*j, nc, tlsc);
230 running = insertEdge(e, running, currentFrom, currentTo, passed, nb);
231 currentFrom = currentTo;
232 passed.clear();
233 passed.push_back(*j);
234 }
235 }
236 if (running == 0) {
237 running = -1;
238 }
239 insertEdge(e, running, currentFrom, last, passed, nb);
240 }
241
242 const double layerElevation = oc.getFloat("osm.layer-elevation");
243 if (layerElevation > 0) {
244 reconstructLayerElevation(layerElevation, nb);
245 }
246
247 //revise pt stops; remove stops on deleted edges
248 if (OptionsCont::getOptions().isSet("ptstop-output")) {
249 nb.getPTStopCont().cleanupDeleted(nb.getEdgeCont());
250 }
251
252 // load relations (after edges are built since we want to apply
253 // turn-restrictions directly to NBEdges)
254 RelationHandler relationHandler(myOSMNodes, myEdges, &(nb.getPTStopCont()), myPlatformShapes,
255 &(nb.getPTLineCont()), oc);
256 for (std::vector<std::string>::const_iterator file = files.begin(); file != files.end(); ++file) {
257 // relations
258 relationHandler.setFileName(*file);
259 PROGRESS_BEGIN_MESSAGE("Parsing relations from osm-file '" + *file + "'");
260 XMLSubSys::runParser(relationHandler, *file);
261 PROGRESS_DONE_MESSAGE();
262 }
263 }
264
265 NBNode*
insertNodeChecking(long long int id,NBNodeCont & nc,NBTrafficLightLogicCont & tlsc)266 NIImporter_OpenStreetMap::insertNodeChecking(long long int id, NBNodeCont& nc, NBTrafficLightLogicCont& tlsc) {
267 NBNode* node = nc.retrieve(toString(id));
268 if (node == nullptr) {
269 NIOSMNode* n = myOSMNodes.find(id)->second;
270 Position pos(n->lon, n->lat, n->ele);
271 if (!NBNetBuilder::transformCoordinate(pos, true)) {
272 WRITE_ERROR("Unable to project coordinates for junction '" + toString(id) + "'.");
273 return nullptr;
274 }
275 node = new NBNode(toString(id), pos);
276 if (!nc.insert(node)) {
277 WRITE_ERROR("Could not insert junction '" + toString(id) + "'.");
278 delete node;
279 return nullptr;
280 }
281 n->node = node;
282 if (n->railwayCrossing) {
283 node->reinit(pos, NODETYPE_RAIL_CROSSING);
284 } else if (n->railwaySignal) {
285 node->reinit(pos, NODETYPE_RAIL_SIGNAL);
286 } else if (n->tlsControlled) {
287 // ok, this node is a traffic light node where no other nodes
288 // participate
289 // @note: The OSM-community has not settled on a schema for differentiating between fixed and actuated lights
290 TrafficLightType type = SUMOXMLDefinitions::TrafficLightTypes.get(
291 OptionsCont::getOptions().getString("tls.default-type"));
292 NBOwnTLDef* tlDef = new NBOwnTLDef(toString(id), node, 0, type);
293 if (!tlsc.insert(tlDef)) {
294 // actually, nothing should fail here
295 delete tlDef;
296 throw ProcessError("Could not allocate tls '" + toString(id) + "'.");
297 }
298 }
299 if (n->railwayBufferStop) {
300 node->setParameter("buffer_stop", "true");
301 }
302 }
303 return node;
304 }
305
306 int
insertEdge(Edge * e,int index,NBNode * from,NBNode * to,const std::vector<long long int> & passed,NBNetBuilder & nb)307 NIImporter_OpenStreetMap::insertEdge(Edge* e, int index, NBNode* from, NBNode* to,
308 const std::vector<long long int>& passed, NBNetBuilder& nb) {
309 NBNodeCont& nc = nb.getNodeCont();
310 NBEdgeCont& ec = nb.getEdgeCont();
311 NBTypeCont& tc = nb.getTypeCont();
312 NBPTStopCont& sc = nb.getPTStopCont();
313
314 NBTrafficLightLogicCont& tlsc = nb.getTLLogicCont();
315 // patch the id
316 std::string id = toString(e->id);
317 if (from == nullptr || to == nullptr) {
318 WRITE_ERROR("Discarding edge '" + id + "' because the nodes could not be built.");
319 return index;
320 }
321 if (index >= 0) {
322 id = id + "#" + toString(index);
323 } else {
324 index = 0;
325 }
326 if (from == to) {
327 assert(passed.size() >= 2);
328 if (passed.size() == 2) {
329 WRITE_WARNING("Discarding edge '" + id + "' which connects two identical nodes without geometry.");
330 return index;
331 }
332 // in the special case of a looped way split again using passed
333 int intermediateIndex = (int) passed.size() / 2;
334 NBNode* intermediate = insertNodeChecking(passed[intermediateIndex], nc, tlsc);
335 std::vector<long long int> part1(passed.begin(), passed.begin() + intermediateIndex + 1);
336 std::vector<long long int> part2(passed.begin() + intermediateIndex, passed.end());
337 index = insertEdge(e, index, from, intermediate, part1, nb);
338 return insertEdge(e, index, intermediate, to, part2, nb);
339 }
340 const int newIndex = index + 1;
341
342 // convert the shape
343 PositionVector shape;
344 for (long long i : passed) {
345 NIOSMNode* n = myOSMNodes.find(i)->second;
346
347 if (n->ptStopPosition) {
348 NBPTStop* existingPtStop = sc.get(toString(n->id));
349 if (existingPtStop != nullptr) {
350 existingPtStop->registerAdditionalEdge(toString(e->id), id);
351 } else {
352 Position ptPos(n->lon, n->lat, n->ele);
353 if (!NBNetBuilder::transformCoordinate(ptPos)) {
354 WRITE_ERROR("Unable to project coordinates for node '" + toString(n->id) + "'.");
355 }
356 NBPTStop* ptStop = new NBPTStop(toString(n->id), ptPos, id, toString(e->id), n->ptStopLength, n->name,
357 n->permissions);
358
359 sc.insert(ptStop);
360 }
361 }
362 Position pos(n->lon, n->lat, n->ele);
363 shape.push_back(pos);
364 }
365 if (!NBNetBuilder::transformCoordinates(shape)) {
366 WRITE_ERROR("Unable to project coordinates for edge '" + id + "'.");
367 }
368 // shape.in
369
370 std::string type = e->myHighWayType;
371 if (!tc.knows(type)) {
372 if (myUnusableTypes.count(type) > 0) {
373 return newIndex;
374 }
375 if (myKnownCompoundTypes.count(type) > 0) {
376 type = myKnownCompoundTypes[type];
377 } else {
378 // this edge has a type which does not yet exist in the TypeContainer
379 StringTokenizer tok = StringTokenizer(type, compoundTypeSeparator);
380 std::vector<std::string> types;
381 while (tok.hasNext()) {
382 std::string t = tok.next();
383 if (tc.knows(t)) {
384 if (std::find(types.begin(), types.end(), t) == types.end()) {
385 types.push_back(t);
386 }
387 } else if (tok.size() > 1) {
388 WRITE_WARNING(
389 "Discarding unknown compound '" + t + "' in type '" + type + "' (first occurence for edge '"
390 + id
391 + "').");
392 }
393 }
394 if (types.empty()) {
395 WRITE_WARNING("Discarding unusable type '" + type + "' (first occurence for edge '" + id + "').");
396 myUnusableTypes.insert(type);
397 return newIndex;
398 }
399
400 const std::string newType = joinToString(types, "|");
401 if (tc.knows(newType)) {
402 myKnownCompoundTypes[type] = newType;
403 type = newType;
404 } else if (myKnownCompoundTypes.count(newType) > 0) {
405 type = myKnownCompoundTypes[newType];
406 } else {
407 // build a new type by merging all values
408 int numLanes = 0;
409 double maxSpeed = 0;
410 int prio = 0;
411 double width = NBEdge::UNSPECIFIED_WIDTH;
412 double sidewalkWidth = NBEdge::UNSPECIFIED_WIDTH;
413 double bikelaneWidth = NBEdge::UNSPECIFIED_WIDTH;
414 bool defaultIsOneWay = false;
415 SVCPermissions permissions = 0;
416 bool discard = true;
417 for (auto& type2 : types) {
418 if (!tc.getShallBeDiscarded(type2)) {
419 numLanes = MAX2(numLanes, tc.getNumLanes(type2));
420 maxSpeed = MAX2(maxSpeed, tc.getSpeed(type2));
421 prio = MAX2(prio, tc.getPriority(type2));
422 defaultIsOneWay &= tc.getIsOneWay(type2);
423 //std::cout << "merging component " << type2 << " into type " << newType << " allows=" << getVehicleClassNames(tc.getPermissions(type2)) << "\n";
424 permissions |= tc.getPermissions(type2);
425 width = MAX2(width, tc.getWidth(type2));
426 sidewalkWidth = MAX2(sidewalkWidth, tc.getSidewalkWidth(type2));
427 bikelaneWidth = MAX2(bikelaneWidth, tc.getBikeLaneWidth(type2));
428 discard = false;
429 }
430 }
431 if (width != NBEdge::UNSPECIFIED_WIDTH) {
432 width = MAX2(width, SUMO_const_laneWidth);
433 }
434 // ensure pedestrians don't run into trains
435 if (sidewalkWidth == NBEdge::UNSPECIFIED_WIDTH
436 && (permissions & SVC_PEDESTRIAN) != 0
437 && (permissions & SVC_RAIL_CLASSES) != 0) {
438 //std::cout << "patching sidewalk for type '" << newType << "' which allows=" << getVehicleClassNames(permissions) << "\n";
439 sidewalkWidth = OptionsCont::getOptions().getFloat("default.sidewalk-width");
440 }
441
442 if (discard) {
443 WRITE_WARNING(
444 "Discarding compound type '" + newType + "' (first occurence for edge '" + id + "').");
445 myUnusableTypes.insert(newType);
446 return newIndex;
447 }
448
449 WRITE_MESSAGE("Adding new type '" + type + "' (first occurence for edge '" + id + "').");
450 tc.insert(newType, numLanes, maxSpeed, prio, permissions, width, defaultIsOneWay, sidewalkWidth,
451 bikelaneWidth);
452 for (auto& type3 : types) {
453 if (!tc.getShallBeDiscarded(type3)) {
454 tc.copyRestrictionsAndAttrs(type3, newType);
455 }
456 }
457 myKnownCompoundTypes[type] = newType;
458 type = newType;
459
460 }
461
462 }
463 }
464
465 // otherwise it is not an edge and will be ignored
466 bool ok = true;
467 int numLanesForward = tc.getNumLanes(type);
468 int numLanesBackward = tc.getNumLanes(type);
469 double speed = tc.getSpeed(type);
470 bool defaultsToOneWay = tc.getIsOneWay(type);
471 SVCPermissions forwardPermissions = tc.getPermissions(type);
472 SVCPermissions backwardPermissions = tc.getPermissions(type);
473 const std::string streetName = isRailway(forwardPermissions) && e->ref != "" ? e->ref : e->streetName;
474 double forwardWidth = tc.getWidth(type);
475 double backwardWidth = tc.getWidth(type);
476 const bool addSidewalk = (tc.getSidewalkWidth(type) != NBEdge::UNSPECIFIED_WIDTH);
477 const bool addBikeLane = (tc.getBikeLaneWidth(type) != NBEdge::UNSPECIFIED_WIDTH);
478 // check directions
479 bool addForward = true;
480 bool addBackward = true;
481 if (e->myIsOneWay == "true" || e->myIsOneWay == "yes" || e->myIsOneWay == "1"
482 || (defaultsToOneWay && e->myIsOneWay != "no" && e->myIsOneWay != "false" && e->myIsOneWay != "0" &&
483 e->getParameter("railway:preferred_direction", "") != "both")) {
484 addBackward = false;
485 }
486 if (e->myIsOneWay == "-1" || e->myIsOneWay == "reverse") {
487 // one-way in reversed direction of way
488 addForward = false;
489 addBackward = true;
490 }
491 if (!e->myIsOneWay.empty() && e->myIsOneWay != "false" && e->myIsOneWay != "no" && e->myIsOneWay != "true"
492 && e->myIsOneWay != "yes" && e->myIsOneWay != "-1" && e->myIsOneWay != "1" && e->myIsOneWay != "reverse") {
493 WRITE_WARNING("New value for oneway found: " + e->myIsOneWay);
494 }
495 // if we had been able to extract the number of lanes, override the highway type default
496 if (e->myNoLanes > 0) {
497 if (addForward && !addBackward) {
498 numLanesForward = e->myNoLanes;
499 } else if (!addForward && addBackward) {
500 numLanesBackward = e->myNoLanes;
501 } else {
502 if (e->myNoLanesForward > 0) {
503 numLanesForward = e->myNoLanesForward;
504 } else if (e->myNoLanesForward < 0) {
505 numLanesForward = e->myNoLanes + e->myNoLanesForward;
506 } else {
507 numLanesForward = (int) std::ceil(e->myNoLanes / 2.0);
508 }
509 numLanesBackward = e->myNoLanes - numLanesForward;
510 // sometimes ways are tagged according to their physical width of a single
511 // lane but they are intended for traffic in both directions
512 numLanesForward = MAX2(1, numLanesForward);
513 numLanesBackward = MAX2(1, numLanesBackward);
514 }
515 } else if (e->myNoLanes == 0) {
516 WRITE_WARNING("Skipping edge '" + id + "' because it has zero lanes.");
517 ok = false;
518 }
519 // if we had been able to extract the maximum speed, override the type's default
520 if (e->myMaxSpeed != MAXSPEED_UNGIVEN) {
521 speed = (double)(e->myMaxSpeed / 3.6);
522 }
523 if (speed <= 0) {
524 WRITE_WARNING("Skipping edge '" + id + "' because it has speed " + toString(speed));
525 ok = false;
526 }
527 // deal with cycleways that run in the opposite direction of a one-way street
528 WayType cyclewayType = e->myCyclewayType; // make a copy because we do some temporary modifications
529 if (addBikeLane) {
530 if (!addForward && (cyclewayType & WAY_FORWARD) != 0) {
531 addForward = true;
532 forwardPermissions = SVC_BICYCLE;
533 forwardWidth = tc.getBikeLaneWidth(type);
534 numLanesForward = 1;
535 // do not add an additional cycle lane
536 cyclewayType = (WayType)(cyclewayType & ~WAY_FORWARD); //clang tidy thinks "!WAY_FORWARD" is always false
537 }
538 if (!addBackward && (cyclewayType & WAY_BACKWARD) != 0) {
539 addBackward = true;
540 backwardPermissions = SVC_BICYCLE;
541 backwardWidth = tc.getBikeLaneWidth(type);
542 numLanesBackward = 1;
543 // do not add an additional cycle lane
544 cyclewayType = (WayType)(cyclewayType & ~WAY_BACKWARD); //clang tidy thinks "!WAY_BACKWARD" is always false
545 }
546 }
547 // deal with sidewalks that run in the opposite direction of a one-way street
548 WayType sidewalkType = e->mySidewalkType; // make a copy because we do some temporary modifications
549 if (addSidewalk) {
550 if (!addForward && (sidewalkType & WAY_FORWARD) != 0) {
551 addForward = true;
552 forwardPermissions = SVC_PEDESTRIAN;
553 forwardWidth = tc.getSidewalkWidth(type);
554 numLanesForward = 1;
555 // do not add an additional sidewalk
556 sidewalkType = (WayType)(sidewalkType & ~WAY_FORWARD); //clang tidy thinks "!WAY_FORWARD" is always false
557 }
558 if (!addBackward && (sidewalkType & WAY_BACKWARD) != 0) {
559 addBackward = true;
560 backwardPermissions = SVC_PEDESTRIAN;
561 backwardWidth = tc.getSidewalkWidth(type);
562 numLanesBackward = 1;
563 // do not add an additional cycle lane
564 sidewalkType = (WayType)(sidewalkType & ~WAY_BACKWARD); //clang tidy thinks "!WAY_BACKWARD" is always false
565 }
566 }
567 // deal with busways that run in the opposite direction of a one-way street
568 if (!addForward && (e->myBuswayType & WAY_FORWARD) != 0) {
569 addForward = true;
570 forwardPermissions = SVC_BUS;
571 numLanesForward = 1;
572 }
573 if (!addBackward && (e->myBuswayType & WAY_BACKWARD) != 0) {
574 addBackward = true;
575 backwardPermissions = SVC_BUS;
576 numLanesBackward = 1;
577 }
578
579 const std::string origID = OptionsCont::getOptions().getBool("output.original-names") ? toString(e->id) : "";
580 if (ok) {
581 LaneSpreadFunction lsf = (addBackward || OptionsCont::getOptions().getBool("osm.oneway-spread-right")) &&
582 e->getParameter("railway:preferred_direction", "") != "both" ? LANESPREAD_RIGHT : LANESPREAD_CENTER;
583
584 id = StringUtils::escapeXML(id);
585 const std::string reverseID = "-" + id;
586
587 if (addForward) {
588 assert(numLanesForward > 0);
589 NBEdge* nbe = new NBEdge(id, from, to, type, speed, numLanesForward, tc.getPriority(type),
590 forwardWidth, NBEdge::UNSPECIFIED_OFFSET, shape,
591 StringUtils::escapeXML(streetName), origID, lsf, true);
592 nbe->setPermissions(forwardPermissions);
593 if ((e->myBuswayType & WAY_FORWARD) != 0) {
594 nbe->setPermissions(SVC_BUS, 0);
595 }
596 if (addBikeLane && (cyclewayType == WAY_UNKNOWN || (cyclewayType & WAY_FORWARD) != 0)) {
597 nbe->addBikeLane(tc.getBikeLaneWidth(type));
598 } else if (nbe->getPermissions(0) == SVC_BUS) {
599 // bikes drive on buslanes if no separate cycle lane is available
600 nbe->setPermissions(SVC_BUS | SVC_BICYCLE, 0);
601 }
602 if (addSidewalk && (sidewalkType == WAY_UNKNOWN || (sidewalkType & WAY_FORWARD) != 0)) {
603 nbe->addSidewalk(tc.getSidewalkWidth(type));
604 }
605 nbe->updateParameter(e->getParametersMap());
606 if (!ec.insert(nbe)) {
607 delete nbe;
608 throw ProcessError("Could not add edge '" + id + "'.");
609 }
610 }
611 if (addBackward) {
612 assert(numLanesBackward > 0);
613 NBEdge* nbe = new NBEdge(reverseID, to, from, type, speed, numLanesBackward, tc.getPriority(type),
614 backwardWidth, NBEdge::UNSPECIFIED_OFFSET, shape.reverse(),
615 StringUtils::escapeXML(streetName), origID, lsf, true);
616 nbe->setPermissions(backwardPermissions);
617 if ((e->myBuswayType & WAY_BACKWARD) != 0) {
618 nbe->setPermissions(SVC_BUS, 0);
619 }
620 if (addBikeLane && (cyclewayType == WAY_UNKNOWN || (cyclewayType & WAY_BACKWARD) != 0)) {
621 nbe->addBikeLane(tc.getBikeLaneWidth(type));
622 } else if (nbe->getPermissions(0) == SVC_BUS) {
623 // bikes drive on buslanes if no separate cycle lane is available
624 nbe->setPermissions(SVC_BUS | SVC_BICYCLE, 0);
625 }
626 if (addSidewalk && (sidewalkType == WAY_UNKNOWN || (sidewalkType & WAY_BACKWARD) != 0)) {
627 nbe->addSidewalk(tc.getSidewalkWidth(type));
628 }
629 nbe->updateParameter(e->getParametersMap());
630 if (!ec.insert(nbe)) {
631 delete nbe;
632 throw ProcessError("Could not add edge '-" + id + "'.");
633 }
634 }
635 if ((e->myParkingType & PARKING_BOTH) != 0 && OptionsCont::getOptions().isSet("parking-output")) {
636 if ((e->myParkingType & PARKING_RIGHT) != 0) {
637 if (addForward) {
638 nb.getParkingCont().push_back(NBParking(id, id));
639 } else {
640 /// XXX parking area should be added on the left side of a reverse one-way street
641 if ((e->myParkingType & PARKING_LEFT) == 0 && !addBackward) {
642 /// put it on the wrong side (better than nothing)
643 nb.getParkingCont().push_back(NBParking(reverseID, reverseID));
644 }
645 }
646 }
647 if ((e->myParkingType & PARKING_LEFT) != 0) {
648 if (addBackward) {
649 nb.getParkingCont().push_back(NBParking(reverseID, reverseID));
650 } else {
651 /// XXX parking area should be added on the left side of an one-way street
652 if ((e->myParkingType & PARKING_RIGHT) == 0 && !addForward) {
653 /// put it on the wrong side (better than nothing)
654 nb.getParkingCont().push_back(NBParking(id, id));
655 }
656 }
657 }
658 }
659 }
660 return newIndex;
661 }
662
663 // ---------------------------------------------------------------------------
664 // definitions of NIImporter_OpenStreetMap::NodesHandler-methods
665 // ---------------------------------------------------------------------------
NodesHandler(std::map<long long int,NIOSMNode * > & toFill,std::set<NIOSMNode *,CompareNodes> & uniqueNodes,const OptionsCont & oc)666 NIImporter_OpenStreetMap::NodesHandler::NodesHandler(std::map<long long int, NIOSMNode*>& toFill,
667 std::set<NIOSMNode*, CompareNodes>& uniqueNodes,
668 const OptionsCont& oc)
669
670 :
671 SUMOSAXHandler("osm - file"),
672 myToFill(toFill),
673 myLastNodeID(-1),
674 myIsInValidNodeTag(false),
675 myHierarchyLevel(0),
676 myUniqueNodes(uniqueNodes),
677 myImportElevation(oc.getBool("osm.elevation")),
678 myOptionsCont(oc) {
679 }
680
681 NIImporter_OpenStreetMap::NodesHandler::~NodesHandler() = default;
682
683 void
myStartElement(int element,const SUMOSAXAttributes & attrs)684 NIImporter_OpenStreetMap::NodesHandler::myStartElement(int element, const SUMOSAXAttributes& attrs) {
685 ++myHierarchyLevel;
686 if (element == SUMO_TAG_NODE) {
687 bool ok = true;
688 if (myHierarchyLevel != 2) {
689 WRITE_ERROR("Node element on wrong XML hierarchy level (id='" + toString(attrs.get<long
690 long
691 int>(SUMO_ATTR_ID,
692 nullptr, ok))
693 + "', level='" + toString(myHierarchyLevel) + "').");
694 return;
695 }
696 const long long int id = attrs.get<long long int>(SUMO_ATTR_ID, nullptr, ok);
697 const std::string action = attrs.hasAttribute("action") ? attrs.getStringSecure("action", "") : "";
698 if (action == "delete" || !ok) {
699 return;
700 }
701 myLastNodeID = -1;
702 if (myToFill.find(id) == myToFill.end()) {
703 myLastNodeID = id;
704 // assume we are loading multiple files...
705 // ... so we won't report duplicate nodes
706 bool ok2 = true;
707 double tlat, tlon;
708 std::istringstream lon(attrs.get<std::string>(SUMO_ATTR_LON, toString(id).c_str(), ok2));
709 if (!ok2) {
710 return;
711 }
712 lon >> tlon;
713 if (lon.fail()) {
714 WRITE_ERROR("Node's '" + toString(id) + "' lon information is not numeric.");
715 return;
716 }
717 std::istringstream lat(attrs.get<std::string>(SUMO_ATTR_LAT, toString(id).c_str(), ok2));
718 if (!ok2) {
719 return;
720 }
721 lat >> tlat;
722 if (lat.fail()) {
723 WRITE_ERROR("Node's '" + toString(id) + "' lat information is not numeric.");
724 return;
725 }
726 auto* toAdd = new NIOSMNode(id, tlon, tlat);
727 myIsInValidNodeTag = true;
728
729 auto similarNode = myUniqueNodes.find(toAdd);
730 if (similarNode == myUniqueNodes.end()) {
731 myUniqueNodes.insert(toAdd);
732 } else {
733 delete toAdd;
734 toAdd = *similarNode;
735 WRITE_MESSAGE("Found duplicate nodes. Substituting " + toString(id) + " with " + toString(toAdd->id));
736 }
737 myToFill[id] = toAdd;
738 }
739 }
740 if (element == SUMO_TAG_TAG && myIsInValidNodeTag) {
741 if (myHierarchyLevel != 3) {
742 WRITE_ERROR("Tag element on wrong XML hierarchy level.");
743 return;
744 }
745 bool ok = true;
746 std::string key = attrs.get<std::string>(SUMO_ATTR_K, toString(myLastNodeID).c_str(), ok, false);
747 // we check whether the key is relevant (and we really need to transcode the value) to avoid hitting #1636
748 if (key == "highway" || key == "ele" || key == "crossing" || key == "railway" || key == "public_transport"
749 || key == "name" || key == "train" || key == "bus" || key == "tram" || key == "light_rail" || key == "subway" || key == "station" || key == "noexit"
750 || StringUtils::startsWith(key, "railway:signal")) {
751 std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myLastNodeID).c_str(), ok, false);
752 if (key == "highway" && value.find("traffic_signal") != std::string::npos) {
753 myToFill[myLastNodeID]->tlsControlled = true;
754 } else if (key == "crossing" && value.find("traffic_signals") != std::string::npos) {
755 myToFill[myLastNodeID]->tlsControlled = true;
756 } else if ((key == "noexit" && value == "yes")
757 || (key == "railway" && value == "buffer_stop")) {
758 myToFill[myLastNodeID]->railwayBufferStop = true;
759 } else if (key == "railway" && value.find("crossing") != std::string::npos) {
760 myToFill[myLastNodeID]->railwayCrossing = true;
761 } else if (StringUtils::startsWith(key, "railway:signal") && (
762 value == "block" || value == "entry" || value == "exit" || value == "intermediate")) {
763 myToFill[myLastNodeID]->railwaySignal = true;
764 } else if ((key == "public_transport" && value == "stop_position") ||
765 (key == "highway" && value == "bus_stop")) {
766 myToFill[myLastNodeID]->ptStopPosition = true;
767 if (myToFill[myLastNodeID]->ptStopLength == 0) {
768 // default length
769 myToFill[myLastNodeID]->ptStopLength = myOptionsCont.getFloat("osm.stop-output.length");
770 }
771 } else if (key == "name") {
772 myToFill[myLastNodeID]->name = value;
773 } else if (key == "train") {
774 myToFill[myLastNodeID]->permissions = SVC_RAIL;
775 myToFill[myLastNodeID]->ptStopLength = myOptionsCont.getFloat("osm.stop-output.length.train");
776 } else if (key == "subway" || key == "light_rail"
777 || (key == "station" && (value == "subway" || value == "light_rail"))) {
778 myToFill[myLastNodeID]->permissions = SVC_RAIL_URBAN;
779 myToFill[myLastNodeID]->ptStopLength = myOptionsCont.getFloat("osm.stop-output.length.train");
780 } else if (key == "bus") {
781 myToFill[myLastNodeID]->permissions = SVC_BUS;
782 myToFill[myLastNodeID]->ptStopLength = myOptionsCont.getFloat("osm.stop-output.length.bus");
783 } else if (key == "tram") {
784 myToFill[myLastNodeID]->permissions = SVC_TRAM;
785 myToFill[myLastNodeID]->ptStopLength = myOptionsCont.getFloat("osm.stop-output.length.tram");
786 } else if (myImportElevation && key == "ele") {
787 try {
788 myToFill[myLastNodeID]->ele = StringUtils::toDouble(value);
789 } catch (...) {
790 WRITE_WARNING("Value of key '" + key + "' is not numeric ('" + value + "') in node '" +
791 toString(myLastNodeID) + "'.");
792 }
793 }
794 }
795 }
796 }
797
798 void
myEndElement(int element)799 NIImporter_OpenStreetMap::NodesHandler::myEndElement(int element) {
800 if (element == SUMO_TAG_NODE && myHierarchyLevel == 2) {
801 myLastNodeID = -1;
802 myIsInValidNodeTag = false;
803 }
804 --myHierarchyLevel;
805 }
806
807 // ---------------------------------------------------------------------------
808 // definitions of NIImporter_OpenStreetMap::EdgesHandler-methods
809 // ---------------------------------------------------------------------------
EdgesHandler(const std::map<long long int,NIOSMNode * > & osmNodes,std::map<long long int,Edge * > & toFill,std::map<long long int,Edge * > & platformShapes)810 NIImporter_OpenStreetMap::EdgesHandler::EdgesHandler(
811 const std::map<long long int, NIOSMNode*>& osmNodes,
812 std::map<long long int, Edge*>& toFill, std::map<long long int, Edge*>& platformShapes):
813 SUMOSAXHandler("osm - file"),
814 myOSMNodes(osmNodes),
815 myEdgeMap(toFill),
816 myPlatformShapesMap(platformShapes) {
817 mySpeedMap["signals"] = MAXSPEED_UNGIVEN;
818 mySpeedMap["none"] = 142.; // Auswirkungen eines allgemeinen Tempolimits auf Autobahnen im Land Brandeburg (2007)
819 mySpeedMap["no"] = 142.;
820 mySpeedMap["walk"] = 5.;
821 mySpeedMap["DE:rural"] = 100.;
822 mySpeedMap["DE:urban"] = 50.;
823 mySpeedMap["DE:living_street"] = 10.;
824 myAllAttributes = OptionsCont::getOptions().getBool("osm.all-attributes");
825
826 }
827
828 NIImporter_OpenStreetMap::EdgesHandler::~EdgesHandler() = default;
829
830 void
myStartElement(int element,const SUMOSAXAttributes & attrs)831 NIImporter_OpenStreetMap::EdgesHandler::myStartElement(int element,
832 const SUMOSAXAttributes& attrs) {
833 myParentElements.push_back(element);
834 // parse "way" elements
835 if (element == SUMO_TAG_WAY) {
836 bool ok = true;
837 const long long int id = attrs.get<long long int>(SUMO_ATTR_ID, nullptr, ok);
838 std::string action = attrs.hasAttribute("action") ? attrs.getStringSecure("action", "") : "";
839 if (action == "delete" || !ok) {
840 myCurrentEdge = nullptr;
841 return;
842 }
843 myCurrentEdge = new Edge(id);
844 }
845 // parse "nd" (node) elements
846 if (element == SUMO_TAG_ND && myCurrentEdge != nullptr) {
847 bool ok = true;
848 long long int ref = attrs.get<long long int>(SUMO_ATTR_REF, nullptr, ok);
849 if (ok) {
850 auto node = myOSMNodes.find(ref);
851 if (node == myOSMNodes.end()) {
852 WRITE_WARNING("The referenced geometry information (ref='" + toString(ref) + "') is not known");
853 return;
854 }
855
856 ref = node->second->id; // node may have been substituted
857 if (myCurrentEdge->myCurrentNodes.empty() ||
858 myCurrentEdge->myCurrentNodes.back() != ref) { // avoid consecutive duplicates
859 myCurrentEdge->myCurrentNodes.push_back(ref);
860 }
861
862 }
863 }
864 // parse values
865 if (element == SUMO_TAG_TAG && myParentElements.size() > 2
866 && myParentElements[myParentElements.size() - 2] == SUMO_TAG_WAY) {
867 if (myCurrentEdge == nullptr) {
868 return;
869 }
870 bool ok = true;
871 std::string key = attrs.get<std::string>(SUMO_ATTR_K, toString(myCurrentEdge->id).c_str(), ok, false);
872 if (key.size() > 8 && StringUtils::startsWith(key, "cycleway:")) {
873 // handle special cycleway keys
874 const std::string cyclewaySpec = key.substr(9);
875 key = "cycleway";
876 if (cyclewaySpec == "right") {
877 myCurrentEdge->myCyclewayType = (WayType)(myCurrentEdge->myCyclewayType | WAY_FORWARD);
878 } else if (cyclewaySpec == "left") {
879 myCurrentEdge->myCyclewayType = (WayType)(myCurrentEdge->myCyclewayType | WAY_BACKWARD);
880 } else if (cyclewaySpec == "both") {
881 myCurrentEdge->myCyclewayType = (WayType)(myCurrentEdge->myCyclewayType | WAY_BOTH);
882 } else {
883 key = "ignore";
884 }
885 if ((myCurrentEdge->myCyclewayType & WAY_BOTH) != 0) {
886 // now we have some info on directionality
887 myCurrentEdge->myCyclewayType = (WayType)(myCurrentEdge->myCyclewayType & ~WAY_UNKNOWN);
888 }
889 } else if (key.size() > 6 && StringUtils::startsWith(key, "busway:")) {
890 // handle special busway keys
891 const std::string buswaySpec = key.substr(7);
892 key = "busway";
893 if (buswaySpec == "right") {
894 myCurrentEdge->myBuswayType = (WayType)(myCurrentEdge->myBuswayType | WAY_FORWARD);
895 } else if (buswaySpec == "left") {
896 myCurrentEdge->myBuswayType = (WayType)(myCurrentEdge->myBuswayType | WAY_BACKWARD);
897 } else if (buswaySpec == "both") {
898 myCurrentEdge->myBuswayType = (WayType)(myCurrentEdge->myBuswayType | WAY_BOTH);
899 } else {
900 key = "ignore";
901 }
902 }
903 if (myAllAttributes && (key == "bridge" || key == "tunnel")) {
904 myCurrentEdge->setParameter(key, "true"); // could be differentiated further if necessary
905 }
906 // we check whether the key is relevant (and we really need to transcode the value) to avoid hitting #1636
907 if (!StringUtils::endsWith(key, "way") && !StringUtils::startsWith(key, "lanes")
908 && key != "maxspeed" && key != "junction" && key != "name" && key != "tracks" && key != "layer"
909 && key != "route"
910 && key != "sidewalk"
911 && key != "ref"
912 && !StringUtils::startsWith(key, "parking")
913 && key != "postal_code" && key != "railway:preferred_direction" && key != "public_transport") {
914 return;
915 }
916 std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentEdge->id).c_str(), ok, false);
917
918 if ((key == "highway" && value != "platform") || key == "railway" || key == "waterway" || key == "cycleway"
919 || key == "busway" || key == "route" || key == "sidewalk") {
920 myCurrentEdge->myCurrentIsRoad = true;
921 // special cycleway stuff
922 if (key == "cycleway") {
923 if (value == "no") {
924 return;
925 }
926 if (value == "opposite_track") {
927 myCurrentEdge->myCyclewayType = WAY_BACKWARD;
928 } else if (value == "opposite_lane") {
929 myCurrentEdge->myCyclewayType = WAY_BACKWARD;
930 }
931 }
932 // special sidewalk stuff
933 if (key == "sidewalk") {
934 if (value == "no" || value == "none") {
935 myCurrentEdge->mySidewalkType = WAY_NONE;
936 } else if (value == "both") {
937 myCurrentEdge->mySidewalkType = WAY_BOTH;
938 } else if (value == "right") {
939 myCurrentEdge->mySidewalkType = WAY_FORWARD;
940 } else if (value == "left") {
941 myCurrentEdge->mySidewalkType = WAY_BACKWARD;
942 }
943 // no need to extend the type id
944 return;
945 }
946 // special busway stuff
947 if (key == "busway") {
948 if (value == "no") {
949 return;
950 }
951 if (value == "opposite_track") {
952 myCurrentEdge->myBuswayType = WAY_BACKWARD;
953 } else if (value == "opposite_lane") {
954 myCurrentEdge->myBuswayType = WAY_BACKWARD;
955 }
956 // no need to extend the type id
957 return;
958 }
959 // build type id
960 const std::string singleTypeID = key + "." + value;
961 if (!myCurrentEdge->myHighWayType.empty()) {
962 // osm-ways may be used by more than one mode (eg railway.tram + highway.residential. this is relevant for multimodal traffic)
963 // we create a new type for this kind of situation which must then be resolved in insertEdge()
964 std::vector<std::string> types = StringTokenizer(myCurrentEdge->myHighWayType,
965 compoundTypeSeparator).getVector();
966 types.push_back(singleTypeID);
967 myCurrentEdge->myHighWayType = joinToStringSorting(types, compoundTypeSeparator);
968 } else {
969 myCurrentEdge->myHighWayType = singleTypeID;
970 }
971 } else if (key == "lanes") {
972 try {
973 myCurrentEdge->myNoLanes = StringUtils::toInt(value);
974 } catch (NumberFormatException&) {
975 // might be a list of values
976 StringTokenizer st(value, ";", true);
977 std::vector<std::string> list = st.getVector();
978 if (list.size() >= 2) {
979 int minLanes = std::numeric_limits<int>::max();
980 try {
981 for (auto& i : list) {
982 int numLanes = StringUtils::toInt(StringUtils::prune(i));
983 minLanes = MIN2(minLanes, numLanes);
984 }
985 myCurrentEdge->myNoLanes = minLanes;
986 WRITE_WARNING(
987 "Using minimum lane number from list (" + value + ") for edge '"
988 + toString(myCurrentEdge->id)
989 + "'.");
990 } catch (NumberFormatException&) {
991 WRITE_WARNING("Value of key '" + key + "' is not numeric ('" + value + "') in edge '" +
992 toString(myCurrentEdge->id) + "'.");
993 }
994 }
995 } catch (EmptyData&) {
996 WRITE_WARNING("Value of key '" + key + "' is not numeric ('" + value + "') in edge '" +
997 toString(myCurrentEdge->id) + "'.");
998 }
999 } else if (key == "lanes:forward") {
1000 try {
1001 myCurrentEdge->myNoLanesForward = StringUtils::toInt(value);
1002 } catch (...) {
1003 WRITE_WARNING("Value of key '" + key + "' is not numeric ('" + value + "') in edge '" +
1004 toString(myCurrentEdge->id) + "'.");
1005 }
1006 } else if (key == "lanes:backward") {
1007 try {
1008 // denote backwards count with a negative sign
1009 myCurrentEdge->myNoLanesForward = -StringUtils::toInt(value);
1010 } catch (...) {
1011 WRITE_WARNING("Value of key '" + key + "' is not numeric ('" + value + "') in edge '" +
1012 toString(myCurrentEdge->id) + "'.");
1013 }
1014 } else if (key == "maxspeed") {
1015 if (mySpeedMap.find(value) != mySpeedMap.end()) {
1016 myCurrentEdge->myMaxSpeed = mySpeedMap[value];
1017 } else {
1018 double conversion = 1; // OSM default is km/h
1019 if (StringUtils::to_lower_case(value).find("km/h") != std::string::npos) {
1020 value = StringUtils::prune(value.substr(0, value.find_first_not_of("0123456789")));
1021 } else if (StringUtils::to_lower_case(value).find("mph") != std::string::npos) {
1022 value = StringUtils::prune(value.substr(0, value.find_first_not_of("0123456789")));
1023 conversion = 1.609344; // kilometers per mile
1024 }
1025 try {
1026 myCurrentEdge->myMaxSpeed = StringUtils::toDouble(value) * conversion;
1027 } catch (...) {
1028 WRITE_WARNING("Value of key '" + key + "' is not numeric ('" + value + "') in edge '" +
1029 toString(myCurrentEdge->id) + "'.");
1030 }
1031 }
1032 } else if (key == "junction") {
1033 if ((value == "roundabout") && (myCurrentEdge->myIsOneWay.empty())) {
1034 myCurrentEdge->myIsOneWay = "yes";
1035 }
1036 } else if (key == "oneway") {
1037 myCurrentEdge->myIsOneWay = value;
1038 } else if (key == "name") {
1039 myCurrentEdge->streetName = value;
1040 } else if (key == "ref") {
1041 myCurrentEdge->ref = value;
1042 } else if (key == "layer") {
1043 if (myAllAttributes) {
1044 myCurrentEdge->setParameter(key, value);
1045 }
1046 try {
1047 myCurrentEdge->myLayer = StringUtils::toInt(value);
1048 } catch (...) {
1049 WRITE_WARNING("Value of key '" + key + "' is not numeric ('" + value + "') in edge '" +
1050 toString(myCurrentEdge->id) + "'.");
1051 }
1052 } else if (key == "tracks") {
1053 try {
1054 if (StringUtils::toInt(value) > 1) {
1055 myCurrentEdge->myIsOneWay = "false";
1056 } else {
1057 myCurrentEdge->myIsOneWay = "true";
1058 }
1059 } catch (...) {
1060 WRITE_WARNING("Value of key '" + key + "' is not numeric ('" + value + "') in edge '" +
1061 toString(myCurrentEdge->id) + "'.");
1062 }
1063 } else if (myAllAttributes && key == "postal_code") {
1064 myCurrentEdge->setParameter(key, value);
1065 } else if (key == "railway:preferred_direction") {
1066 // this param is special because it influences network building (duplicate rail edges)
1067 myCurrentEdge->setParameter(key, value);
1068 } else if (key == "public_transport" && value == "platform") {
1069 myCurrentEdge->myCurrentIsPlatform = true;
1070 } else if (key == "parking:lane:both" && !StringUtils::startsWith(value, "no")) {
1071 myCurrentEdge->myParkingType |= PARKING_BOTH;
1072 } else if (key == "parking:lane:left" && !StringUtils::startsWith(value, "no")) {
1073 myCurrentEdge->myParkingType |= PARKING_LEFT;
1074 } else if (key == "parking:lane:right" && !StringUtils::startsWith(value, "no")) {
1075 myCurrentEdge->myParkingType |= PARKING_RIGHT;
1076 }
1077 }
1078 }
1079
1080 void
myEndElement(int element)1081 NIImporter_OpenStreetMap::EdgesHandler::myEndElement(int element) {
1082 myParentElements.pop_back();
1083 if (element == SUMO_TAG_WAY && myCurrentEdge != nullptr) {
1084 if (myCurrentEdge->myCurrentIsRoad) {
1085 myEdgeMap[myCurrentEdge->id] = myCurrentEdge;
1086 } else if (myCurrentEdge->myCurrentIsPlatform) {
1087 myPlatformShapesMap[myCurrentEdge->id] = myCurrentEdge;
1088 } else {
1089 delete myCurrentEdge;
1090 }
1091 myCurrentEdge = nullptr;
1092 }
1093 }
1094
1095 // ---------------------------------------------------------------------------
1096 // definitions of NIImporter_OpenStreetMap::RelationHandler-methods
1097 // ---------------------------------------------------------------------------
RelationHandler(const std::map<long long int,NIOSMNode * > & osmNodes,const std::map<long long int,Edge * > & osmEdges,NBPTStopCont * nbptStopCont,const std::map<long long int,Edge * > & platformShapes,NBPTLineCont * nbptLineCont,const OptionsCont & oc)1098 NIImporter_OpenStreetMap::RelationHandler::RelationHandler(
1099 const std::map<long long int, NIOSMNode*>& osmNodes,
1100 const std::map<long long int, Edge*>& osmEdges, NBPTStopCont* nbptStopCont,
1101 const std::map<long long int, Edge*>& platformShapes,
1102 NBPTLineCont* nbptLineCont,
1103 const OptionsCont& oc)
1104 :
1105 SUMOSAXHandler("osm - file"),
1106 myOSMNodes(osmNodes),
1107 myOSMEdges(osmEdges),
1108 myPlatformShapes(platformShapes),
1109 myNBPTStopCont(nbptStopCont),
1110 myNBPTLineCont(nbptLineCont),
1111 myOptionsCont(oc) {
1112 resetValues();
1113 }
1114
1115 NIImporter_OpenStreetMap::RelationHandler::~RelationHandler() = default;
1116
1117 void
resetValues()1118 NIImporter_OpenStreetMap::RelationHandler::resetValues() {
1119 myCurrentRelation = INVALID_ID;
1120 myIsRestriction = false;
1121 myFromWay = INVALID_ID;
1122 myToWay = INVALID_ID;
1123 myViaNode = INVALID_ID;
1124 myViaWay = INVALID_ID;
1125 myRestrictionType = RESTRICTION_UNKNOWN;
1126 myPlatforms.clear();
1127 myStops.clear();
1128 myWays.clear();
1129 myIsStopArea = false;
1130 myIsRoute = false;
1131 myPTRouteType = "";
1132 }
1133
1134 void
myStartElement(int element,const SUMOSAXAttributes & attrs)1135 NIImporter_OpenStreetMap::RelationHandler::myStartElement(int element,
1136 const SUMOSAXAttributes& attrs) {
1137 myParentElements.push_back(element);
1138 // parse "way" elements
1139 if (element == SUMO_TAG_RELATION) {
1140 bool ok = true;
1141 myCurrentRelation = attrs.get<long long int>(SUMO_ATTR_ID, nullptr, ok);
1142 const std::string action = attrs.hasAttribute("action") ? attrs.getStringSecure("action", "") : "";
1143 if (action == "delete" || !ok) {
1144 myCurrentRelation = INVALID_ID;
1145 }
1146 myName = "";
1147 myRef = "";
1148 myInterval = -1;
1149 myNightService = "";
1150 return;
1151 }
1152 if (myCurrentRelation == INVALID_ID) {
1153 return;
1154 }
1155 // parse member elements
1156 if (element == SUMO_TAG_MEMBER) {
1157 bool ok = true;
1158 std::string role = attrs.hasAttribute("role") ? attrs.getStringSecure("role", "") : "";
1159 auto ref = attrs.get<long
1160 long
1161 int>(SUMO_ATTR_REF, nullptr, ok);
1162 if (role == "via") {
1163 // u-turns for divided ways may be given with 2 via-nodes or 1 via-way
1164 std::string memberType = attrs.get<std::string>(SUMO_ATTR_TYPE, nullptr, ok);
1165 if (memberType == "way" && checkEdgeRef(ref)) {
1166 myViaWay = ref;
1167 } else if (memberType == "node") {
1168 if (myOSMNodes.find(ref) != myOSMNodes.end()) {
1169 myViaNode = ref;
1170 } else {
1171 WRITE_WARNING(
1172 "No node found for reference '" + toString(ref) + "' in relation '"
1173 + toString(myCurrentRelation)
1174 + "'");
1175 }
1176 }
1177 } else if (role == "from" && checkEdgeRef(ref)) {
1178 myFromWay = ref;
1179 } else if (role == "to" && checkEdgeRef(ref)) {
1180 myToWay = ref;
1181 } else if (role == "stop") {
1182 myStops.push_back(ref);
1183 } else if (role == "platform") {
1184 std::string memberType = attrs.get<std::string>(SUMO_ATTR_TYPE, nullptr, ok);
1185 if (memberType == "way") {
1186 const std::map<long long int,
1187 NIImporter_OpenStreetMap::Edge*>::const_iterator& wayIt = myPlatformShapes.find(ref);
1188 if (wayIt != myPlatformShapes.end()) {
1189
1190 NIIPTPlatform platform;
1191 platform.isWay = true;
1192 platform.ref = ref;
1193 myPlatforms.push_back(platform);
1194 }
1195 } else if (memberType == "node") {
1196 NIIPTPlatform platform;
1197 platform.isWay = false;
1198 platform.ref = ref;
1199 myPlatforms.push_back(platform);
1200 }
1201
1202 } else if (role.empty()) {
1203 std::string memberType = attrs.get<std::string>(SUMO_ATTR_TYPE, nullptr, ok);
1204 if (memberType == "way") {
1205 myWays.push_back(ref);
1206 }
1207 }
1208 return;
1209 }
1210 // parse values
1211 if (element == SUMO_TAG_TAG) {
1212 bool ok = true;
1213 std::string key = attrs.get<std::string>(SUMO_ATTR_K, toString(myCurrentRelation).c_str(), ok, false);
1214 // we check whether the key is relevant (and we really need to transcode the value) to avoid hitting #1636
1215 if (key == "type" || key == "restriction") {
1216 std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);
1217 if (key == "type" && value == "restriction") {
1218 myIsRestriction = true;
1219 return;
1220 }
1221 if (key == "type" && value == "route") {
1222 myIsRoute = true;
1223 return;
1224 }
1225 if (key == "restriction") {
1226 // @note: the 'right/left/straight' part is ignored since the information is
1227 // redundantly encoded in the 'from', 'to' and 'via' members
1228 if (value.substr(0, 5) == "only_") {
1229 myRestrictionType = RESTRICTION_ONLY;
1230 } else if (value.substr(0, 3) == "no_") {
1231 myRestrictionType = RESTRICTION_NO;
1232 } else {
1233 WRITE_WARNING(
1234 "Found unknown restriction type '" + value + "' in relation '" + toString(myCurrentRelation)
1235 + "'");
1236 }
1237 return;
1238 }
1239 } else if (key == "public_transport") {
1240 std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);
1241 if (value == "stop_area") {
1242 myIsStopArea = true;
1243 }
1244 } else if (key == "route") {
1245 std::string value = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);
1246 if (value == "train" || value == "subway" || value == "light_rail" || value == "monorail" || value == "tram" || value == "bus"
1247 || value == "trolleybus" || value == "arialway" || value == "ferry") {
1248 myPTRouteType = value;
1249 }
1250
1251 } else if (key == "name") {
1252 myName = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);
1253 } else if (key == "ref") {
1254 myRef = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);
1255 } else if (key == "interval" || key == "headway") {
1256 myInterval = attrs.get<int>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);
1257 } else if (key == "by_night") {
1258 myNightService = attrs.get<std::string>(SUMO_ATTR_V, toString(myCurrentRelation).c_str(), ok, false);
1259 }
1260 }
1261 }
1262
1263 bool
checkEdgeRef(long long int ref) const1264 NIImporter_OpenStreetMap::RelationHandler::checkEdgeRef(long long int ref) const {
1265 if (myOSMEdges.find(ref) != myOSMEdges.end()) {
1266 return true;
1267 }
1268
1269 WRITE_WARNING(
1270 "No way found for reference '" + toString(ref) + "' in relation '" + toString(myCurrentRelation) + "'");
1271 return false;
1272
1273 }
1274
1275 void
myEndElement(int element)1276 NIImporter_OpenStreetMap::RelationHandler::myEndElement(int element) {
1277 myParentElements.pop_back();
1278 if (element == SUMO_TAG_RELATION) {
1279 if (myIsRestriction) {
1280 assert(myCurrentRelation != INVALID_ID);
1281 bool ok = true;
1282 if (myRestrictionType == RESTRICTION_UNKNOWN) {
1283 WRITE_WARNING("Ignoring restriction relation '" + toString(myCurrentRelation) + "' with unknown type.");
1284 ok = false;
1285 }
1286 if (myFromWay == INVALID_ID) {
1287 WRITE_WARNING(
1288 "Ignoring restriction relation '" + toString(myCurrentRelation) + "' with unknown from-way.");
1289 ok = false;
1290 }
1291 if (myToWay == INVALID_ID) {
1292 WRITE_WARNING(
1293 "Ignoring restriction relation '" + toString(myCurrentRelation) + "' with unknown to-way.");
1294 ok = false;
1295 }
1296 if (myViaNode == INVALID_ID && myViaWay == INVALID_ID) {
1297 WRITE_WARNING("Ignoring restriction relation '" + toString(myCurrentRelation) + "' with unknown via.");
1298 ok = false;
1299 }
1300 if (ok && !applyRestriction()) {
1301 WRITE_WARNING("Ignoring restriction relation '" + toString(myCurrentRelation) + "'.");
1302 }
1303 } else if (myIsStopArea && OptionsCont::getOptions().isSet("ptstop-output")) {
1304 for (long long ref : myStops) {
1305 if (myOSMNodes.find(ref) == myOSMNodes.end()) {
1306 //WRITE_WARNING(
1307 // "Referenced node: '" + toString(ref) + "' in relation: '" + toString(myCurrentRelation)
1308 // + "' does not exist. Probably OSM file is incomplete.");
1309 continue;
1310 }
1311
1312 NIOSMNode* n = myOSMNodes.find(ref)->second;
1313 NBPTStop* ptStop = myNBPTStopCont->get(toString(n->id));
1314 if (ptStop == nullptr) {
1315 //WRITE_WARNING(
1316 // "Relation '" + toString(myCurrentRelation) + "' refers to a non existing pt stop at node: '"
1317 // + toString(n->id) + "'. Probably OSM file is incomplete.");
1318 continue;
1319 }
1320 for (NIIPTPlatform& myPlatform : myPlatforms) {
1321 if (myPlatform.isWay) {
1322 assert(myPlatformShapes.find(myPlatform.ref) != myPlatformShapes.end()); //already tested earlier
1323 Edge* edge = (*myPlatformShapes.find(myPlatform.ref)).second;
1324 if (edge->myCurrentNodes[0] == *(edge->myCurrentNodes.end() - 1)) {
1325 WRITE_WARNING("Platform '" + toString(myPlatform.ref) + "' in relation: '" + toString(myCurrentRelation)
1326 + "' is given as polygon, which currently is not supported.");
1327 continue;
1328
1329 }
1330 PositionVector p;
1331 for (auto nodeRef : edge->myCurrentNodes) {
1332 if (myOSMNodes.find(nodeRef) == myOSMNodes.end()) {
1333 //WRITE_WARNING(
1334 // "Referenced node: '" + toString(ref) + "' in relation: '" + toString(myCurrentRelation)
1335 // + "' does not exist. Probably OSM file is incomplete.");
1336 continue;
1337 }
1338 NIOSMNode* pNode = myOSMNodes.find(nodeRef)->second;
1339 Position pNodePos(pNode->lon, pNode->lat, pNode->ele);
1340 if (!NBNetBuilder::transformCoordinate(pNodePos)) {
1341 WRITE_ERROR("Unable to project coordinates for node '" + toString(pNode->id) + "'.");
1342 continue;
1343 }
1344 p.push_back(pNodePos);
1345 }
1346 if (p.size() == 0) {
1347 WRITE_WARNING(
1348 "Referenced platform: '" + toString(myPlatform.ref) + "' in relation: '" + toString(myCurrentRelation)
1349 + "' is corrupt. Probably OSM file is incomplete.");
1350 continue;
1351 }
1352 NBPTPlatform platform(p[(int)p.size() / 2], p.length());
1353 ptStop->addPlatformCand(platform);
1354 } else {
1355 if (myOSMNodes.find(myPlatform.ref) == myOSMNodes.end()) {
1356 //WRITE_WARNING(
1357 // "Referenced node: '" + toString(ref) + "' in relation: '" + toString(myCurrentRelation)
1358 // + "' does not exist. Probably OSM file is incomplete.");
1359 continue;
1360 }
1361 NIOSMNode* pNode = myOSMNodes.find(myPlatform.ref)->second;
1362 Position platformPos(pNode->lon, pNode->lat, pNode->ele);
1363 if (!NBNetBuilder::transformCoordinate(platformPos)) {
1364 WRITE_ERROR("Unable to project coordinates for node '" + toString(pNode->id) + "'.");
1365 }
1366 NBPTPlatform platform(platformPos, myOptionsCont.getFloat(
1367 "osm.stop-output.length"));
1368 ptStop->addPlatformCand(platform);
1369
1370 }
1371 }
1372 ptStop->setIsMultipleStopPositions(myStops.size() > 1);;
1373 }
1374 } else if (myPTRouteType != "" && myIsRoute && OptionsCont::getOptions().isSet("ptline-output") && myStops.size() > 1) {
1375 NBPTLine* ptLine = new NBPTLine(toString(myCurrentRelation), myName, myPTRouteType, myRef, myInterval, myNightService);
1376 ptLine->setMyNumOfStops((int)myStops.size());
1377 for (long long ref : myStops) {
1378 if (myOSMNodes.find(ref) == myOSMNodes.end()) {
1379 //WRITE_WARNING(
1380 // "Referenced node: '" + toString(ref) + "' in relation: '" + toString(myCurrentRelation)
1381 // + "' does not exist. Probably OSM file is incomplete.");
1382 // resetValues();
1383 // return;
1384 if (!ptLine->getStops().empty()) {
1385 WRITE_WARNING("Done reading first coherent chunk of pt stops. Further stops in relation " + toString(myCurrentRelation) + " are ignored");
1386 break;
1387 }
1388 continue;
1389 }
1390
1391 NIOSMNode* n = myOSMNodes.find(ref)->second;
1392 NBPTStop* ptStop = myNBPTStopCont->get(toString(n->id));
1393 if (ptStop == nullptr) {
1394 //WRITE_WARNING("Relation '" + toString(myCurrentRelation)
1395 // + "' refers to a non existing pt stop at node: '" + toString(n->id)
1396 // + "'. Probably OSM file is incomplete.");
1397 // resetValues();
1398 // return;
1399 if (!ptLine->getStops().empty()) {
1400 WRITE_WARNING("Done reading first coherent chunk of pt stops. Further stops in relation " + toString(myCurrentRelation) + " are ignored");
1401 break;
1402 }
1403 continue;
1404 }
1405 ptLine->addPTStop(ptStop);
1406 }
1407 for (long long& myWay : myWays) {
1408 auto entr = myOSMEdges.find(myWay);
1409 if (entr != myOSMEdges.end()) {
1410 Edge* edge = entr->second;
1411 for (long long& myCurrentNode : edge->myCurrentNodes) {
1412 ptLine->addWayNode(myWay, myCurrentNode);
1413 }
1414 }
1415 }
1416 if (ptLine->getStops().empty()) {
1417 WRITE_WARNING("PT line in relation " + toString(myCurrentRelation) + " with no stops ignored. Probably OSM file is incomplete.");
1418 resetValues();
1419 return;
1420 }
1421 if (myNBPTLineCont->getLines().count(ptLine->getLineID()) == 0) {
1422 myNBPTLineCont->insert(ptLine);
1423 } else {
1424 WRITE_WARNING("Ignoring duplicate PT line " + toString(myCurrentRelation) + ".");
1425 delete ptLine;
1426 }
1427 }
1428 // other relations might use similar subelements so reset in any case
1429 resetValues();
1430 }
1431 }
1432
1433 bool
applyRestriction() const1434 NIImporter_OpenStreetMap::RelationHandler::applyRestriction() const {
1435 // since OSM ways are bidirectional we need the via to figure out which direction was meant
1436 if (myViaNode != INVALID_ID) {
1437 NBNode* viaNode = myOSMNodes.find(myViaNode)->second->node;
1438 if (viaNode == nullptr) {
1439 WRITE_WARNING("Via-node '" + toString(myViaNode) + "' was not instantiated");
1440 return false;
1441 }
1442 NBEdge* from = findEdgeRef(myFromWay, viaNode->getIncomingEdges());
1443 NBEdge* to = findEdgeRef(myToWay, viaNode->getOutgoingEdges());
1444 if (from == nullptr) {
1445 WRITE_WARNING("from-edge of restriction relation could not be determined");
1446 return false;
1447 }
1448 if (to == nullptr) {
1449 WRITE_WARNING("to-edge of restriction relation could not be determined");
1450 return false;
1451 }
1452 if (myRestrictionType == RESTRICTION_ONLY) {
1453 from->addEdge2EdgeConnection(to);
1454 } else {
1455 from->removeFromConnections(to, -1, -1, true);
1456 }
1457 } else {
1458 // XXX interpreting via-ways or via-node lists not yet implemented
1459 WRITE_WARNING("direction of restriction relation could not be determined");
1460 return false;
1461 }
1462 return true;
1463 }
1464
1465 NBEdge*
findEdgeRef(long long int wayRef,const std::vector<NBEdge * > & candidates) const1466 NIImporter_OpenStreetMap::RelationHandler::findEdgeRef(long long int wayRef,
1467 const std::vector<NBEdge*>& candidates) const {
1468 const std::string prefix = toString(wayRef);
1469 const std::string backPrefix = "-" + prefix;
1470 NBEdge* result = nullptr;
1471 int found = 0;
1472 for (auto candidate : candidates) {
1473 if ((candidate->getID().substr(0, prefix.size()) == prefix) ||
1474 (candidate->getID().substr(0, backPrefix.size()) == backPrefix)) {
1475 result = candidate;
1476 found++;
1477 }
1478 }
1479 if (found > 1) {
1480 WRITE_WARNING("Ambigous way reference '" + prefix + "' in restriction relation");
1481 result = nullptr;
1482 }
1483 return result;
1484 }
1485
1486 void
reconstructLayerElevation(const double layerElevation,NBNetBuilder & nb)1487 NIImporter_OpenStreetMap::reconstructLayerElevation(const double layerElevation, NBNetBuilder& nb) {
1488 NBNodeCont& nc = nb.getNodeCont();
1489 NBEdgeCont& ec = nb.getEdgeCont();
1490 // reconstruct elevation from layer info
1491 // build a map of raising and lowering forces (attractor and distance)
1492 // for all nodes unknownElevation
1493 std::map<NBNode*, std::vector<std::pair<double, double> > > layerForces;
1494
1495 // collect all nodes that belong to a way with layer information
1496 std::set<NBNode*> knownElevation;
1497 for (auto& myEdge : myEdges) {
1498 Edge* e = myEdge.second;
1499 if (e->myLayer != 0) {
1500 for (auto j = e->myCurrentNodes.begin(); j != e->myCurrentNodes.end(); ++j) {
1501 NBNode* node = nc.retrieve(toString(*j));
1502 if (node != nullptr) {
1503 knownElevation.insert(node);
1504 layerForces[node].emplace_back(e->myLayer * layerElevation, POSITION_EPS);
1505 }
1506 }
1507 }
1508 }
1509 #ifdef DEBUG_LAYER_ELEVATION
1510 std::cout << "known elevations:\n";
1511 for (std::set<NBNode*>::iterator it = knownElevation.begin(); it != knownElevation.end(); ++it) {
1512 const std::vector<std::pair<double, double> >& primaryLayers = layerForces[*it];
1513 std::cout << " node=" << (*it)->getID() << " ele=";
1514 for (std::vector<std::pair<double, double> >::const_iterator it_ele = primaryLayers.begin(); it_ele != primaryLayers.end(); ++it_ele) {
1515 std::cout << it_ele->first << " ";
1516 }
1517 std::cout << "\n";
1518 }
1519 #endif
1520 // layer data only provides a lower bound on elevation since it is used to
1521 // resolve the relation among overlapping ways.
1522 // Perform a sanity check for steep inclines and raise the knownElevation if necessary
1523 std::map<NBNode*, double> knownEleMax;
1524 for (auto it : knownElevation) {
1525 double eleMax = -std::numeric_limits<double>::max();
1526 const std::vector<std::pair<double, double> >& primaryLayers = layerForces[it];
1527 for (const auto& primaryLayer : primaryLayers) {
1528 eleMax = MAX2(eleMax, primaryLayer.first);
1529 }
1530 knownEleMax[it] = eleMax;
1531 }
1532 const double gradeThreshold = OptionsCont::getOptions().getFloat("osm.layer-elevation.max-grade") / 100;
1533 bool changed = true;
1534 while (changed) {
1535 changed = false;
1536 for (auto it = knownElevation.begin(); it != knownElevation.end(); ++it) {
1537 std::map<NBNode*, std::pair<double, double> > neighbors = getNeighboringNodes(*it,
1538 knownEleMax[*it]
1539 / gradeThreshold * 3,
1540 knownElevation);
1541 for (auto& neighbor : neighbors) {
1542 if (knownElevation.count(neighbor.first) != 0) {
1543 const double grade = fabs(knownEleMax[*it] - knownEleMax[neighbor.first])
1544 / MAX2(POSITION_EPS, neighbor.second.first);
1545 #ifdef DEBUG_LAYER_ELEVATION
1546 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";
1547 #endif
1548 if (grade > gradeThreshold * 50 / 3.6 / neighbor.second.second) {
1549 // raise the lower node to the higher level
1550 const double eleMax = MAX2(knownEleMax[*it], knownEleMax[neighbor.first]);
1551 if (knownEleMax[*it] < eleMax) {
1552 knownEleMax[*it] = eleMax;
1553 } else {
1554 knownEleMax[neighbor.first] = eleMax;
1555 }
1556 changed = true;
1557 }
1558 }
1559 }
1560 }
1561 }
1562
1563 // collect all nodes within a grade-dependent range around knownElevation-nodes and apply knowElevation forces
1564 std::set<NBNode*> unknownElevation;
1565 for (auto it = knownElevation.begin(); it != knownElevation.end(); ++it) {
1566 const double eleMax = knownEleMax[*it];
1567 const double maxDist = fabs(eleMax) * 100 / layerElevation;
1568 std::map<NBNode*, std::pair<double, double> > neighbors = getNeighboringNodes(*it, maxDist, knownElevation);
1569 for (auto& neighbor : neighbors) {
1570 if (knownElevation.count(neighbor.first) == 0) {
1571 unknownElevation.insert(neighbor.first);
1572 layerForces[neighbor.first].emplace_back(eleMax, neighbor.second.first);
1573 }
1574 }
1575 }
1576
1577 // apply forces to ground-level nodes (neither in knownElevation nor unknownElevation)
1578 for (auto it = unknownElevation.begin(); it != unknownElevation.end(); ++it) {
1579 double eleMax = -std::numeric_limits<double>::max();
1580 const std::vector<std::pair<double, double> >& primaryLayers = layerForces[*it];
1581 for (const auto& primaryLayer : primaryLayers) {
1582 eleMax = MAX2(eleMax, primaryLayer.first);
1583 }
1584 const double maxDist = fabs(eleMax) * 100 / layerElevation;
1585 std::map<NBNode*, std::pair<double, double> > neighbors = getNeighboringNodes(*it, maxDist, knownElevation);
1586 for (auto& neighbor : neighbors) {
1587 if (knownElevation.count(neighbor.first) == 0 && unknownElevation.count(neighbor.first) == 0) {
1588 layerForces[*it].emplace_back(0, neighbor.second.first);
1589 }
1590 }
1591 }
1592 // compute the elevation for each node as the weighted average of all forces
1593 #ifdef DEBUG_LAYER_ELEVATION
1594 std::cout << "summation of forces\n";
1595 #endif
1596 std::map<NBNode*, double> nodeElevation;
1597 for (auto& layerForce : layerForces) {
1598 const std::vector<std::pair<double, double> >& forces = layerForce.second;
1599 if (knownElevation.count(layerForce.first) != 0) {
1600 // use the maximum value
1601 /*
1602 double eleMax = -std::numeric_limits<double>::max();
1603 for (std::vector<std::pair<double, double> >::const_iterator it_force = forces.begin(); it_force != forces.end(); ++it_force) {
1604 eleMax = MAX2(eleMax, it_force->first);
1605 }
1606 */
1607 #ifdef DEBUG_LAYER_ELEVATION
1608 std::cout << " node=" << it->first->getID() << " knownElevation=" << knownEleMax[it->first] << "\n";
1609 #endif
1610 nodeElevation[layerForce.first] = knownEleMax[layerForce.first];
1611 } else if (forces.size() == 1) {
1612 nodeElevation[layerForce.first] = forces.front().first;
1613 } else {
1614 // use the weighted sum
1615 double distSum = 0;
1616 for (const auto& force : forces) {
1617 distSum += force.second;
1618 }
1619 double weightSum = 0;
1620 double elevation = 0;
1621 #ifdef DEBUG_LAYER_ELEVATION
1622 std::cout << " node=" << it->first->getID() << " distSum=" << distSum << "\n";
1623 #endif
1624 for (const auto& force : forces) {
1625 const double weight = (distSum - force.second) / distSum;
1626 weightSum += weight;
1627 elevation += force.first * weight;
1628
1629 #ifdef DEBUG_LAYER_ELEVATION
1630 std::cout << " force=" << it_force->first << " dist=" << it_force->second << " weight=" << weight << " ele=" << elevation << "\n";
1631 #endif
1632 }
1633 nodeElevation[layerForce.first] = elevation / weightSum;
1634 }
1635 }
1636 #ifdef DEBUG_LAYER_ELEVATION
1637 std::cout << "final elevations:\n";
1638 for (std::map<NBNode*, double>::iterator it = nodeElevation.begin(); it != nodeElevation.end(); ++it) {
1639 std::cout << " node=" << (it->first)->getID() << " ele=" << it->second << "\n";;
1640 }
1641 #endif
1642 // apply node elevations
1643 for (auto& it : nodeElevation) {
1644 NBNode* n = it.first;
1645 Position pos = n->getPosition();
1646 n->reinit(n->getPosition() + Position(0, 0, it.second), n->getType());
1647 }
1648
1649 // apply way elevation to all edges that had layer information
1650 for (const auto& it : ec) {
1651 NBEdge* edge = it.second;
1652 const PositionVector& geom = edge->getGeometry();
1653 const double length = geom.length2D();
1654 const double zFrom = nodeElevation[edge->getFromNode()];
1655 const double zTo = nodeElevation[edge->getToNode()];
1656 // XXX if the from- or to-node was part of multiple ways with
1657 // different layers, reconstruct the layer value from origID
1658 double dist = 0;
1659 PositionVector newGeom;
1660 for (auto it_pos = geom.begin(); it_pos != geom.end(); ++it_pos) {
1661 if (it_pos != geom.begin()) {
1662 dist += (*it_pos).distanceTo2D(*(it_pos - 1));
1663 }
1664 newGeom.push_back((*it_pos) + Position(0, 0, zFrom + (zTo - zFrom) * dist / length));
1665 }
1666 edge->setGeometry(newGeom);
1667 }
1668 }
1669
1670 std::map<NBNode*, std::pair<double, double> >
getNeighboringNodes(NBNode * node,double maxDist,const std::set<NBNode * > & knownElevation)1671 NIImporter_OpenStreetMap::getNeighboringNodes(NBNode* node, double maxDist, const std::set<NBNode*>& knownElevation) {
1672 std::map<NBNode*, std::pair<double, double> > result;
1673 std::set<NBNode*> visited;
1674 std::vector<NBNode*> open;
1675 open.push_back(node);
1676 result[node] = std::make_pair(0, 0);
1677 while (!open.empty()) {
1678 NBNode* n = open.back();
1679 open.pop_back();
1680 if (visited.count(n) != 0) {
1681 continue;
1682 }
1683 visited.insert(n);
1684 const EdgeVector& edges = n->getEdges();
1685 for (auto e : edges) {
1686 NBNode* s = nullptr;
1687 if (n->hasIncoming(e)) {
1688 s = e->getFromNode();
1689 } else {
1690 s = e->getToNode();
1691 }
1692 const double dist = result[n].first + e->getGeometry().length2D();
1693 const double speed = MAX2(e->getSpeed(), result[n].second);
1694 if (result.count(s) == 0) {
1695 result[s] = std::make_pair(dist, speed);
1696 } else {
1697 result[s] = std::make_pair(MIN2(dist, result[s].first), MAX2(speed, result[s].second));
1698 }
1699 if (dist < maxDist && knownElevation.count(s) == 0) {
1700 open.push_back(s);
1701 }
1702 }
1703 }
1704 result.erase(node);
1705 return result;
1706 }
1707
1708
1709 /****************************************************************************/
1710
1711