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