1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2012-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file    NWWriter_DlrNavteq.cpp
11 /// @author  Jakob Erdmann
12 /// @author  Michael Behrisch
13 /// @date    26.10.2012
14 /// @version $Id$
15 ///
16 // Exporter writing networks using DlrNavteq (Elmar) format
17 /****************************************************************************/
18 
19 
20 // ===========================================================================
21 // included modules
22 // ===========================================================================
23 #include <config.h>
24 #include <algorithm>
25 #include <ctime>
26 #include <cmath>
27 #include <utils/common/MsgHandler.h>
28 #include <netbuild/NBEdge.h>
29 #include <netbuild/NBEdgeCont.h>
30 #include <netbuild/NBNode.h>
31 #include <netbuild/NBNodeCont.h>
32 #include <netbuild/NBNetBuilder.h>
33 #include <utils/common/ToString.h>
34 #include <utils/common/IDSupplier.h>
35 #include <utils/common/StringUtils.h>
36 #include <utils/common/StringTokenizer.h>
37 #include <utils/options/OptionsCont.h>
38 #include <utils/iodevices/OutputDevice.h>
39 #include <utils/geom/GeoConvHelper.h>
40 #include "NWFrame.h"
41 #include "NWWriter_DlrNavteq.h"
42 
43 
44 // ---------------------------------------------------------------------------
45 // static members
46 // ---------------------------------------------------------------------------
47 const std::string NWWriter_DlrNavteq::UNDEFINED("-1");
48 
49 // ---------------------------------------------------------------------------
50 // static methods
51 // ---------------------------------------------------------------------------
52 void
writeNetwork(const OptionsCont & oc,NBNetBuilder & nb)53 NWWriter_DlrNavteq::writeNetwork(const OptionsCont& oc, NBNetBuilder& nb) {
54     // check whether a matsim-file shall be generated
55     if (!oc.isSet("dlr-navteq-output")) {
56         return;
57     }
58     std::map<NBEdge*, std::string> internalNodes;
59     writeNodesUnsplitted(oc, nb.getNodeCont(), nb.getEdgeCont(), internalNodes);
60     writeLinksUnsplitted(oc, nb.getEdgeCont(), internalNodes);
61     writeTrafficSignals(oc, nb.getNodeCont());
62     writeProhibitedManoeuvres(oc, nb.getNodeCont(), nb.getEdgeCont());
63     writeConnectedLanes(oc, nb.getNodeCont());
64 }
65 
66 
writeHeader(OutputDevice & device,const OptionsCont & oc)67 void NWWriter_DlrNavteq::writeHeader(OutputDevice& device, const OptionsCont& oc) {
68     device << "# Format matches Extraction version: V6.5 \n";
69     std::stringstream tmp;
70     oc.writeConfiguration(tmp, true, false, false);
71     tmp.seekg(std::ios_base::beg);
72     std::string line;
73     while (!tmp.eof()) {
74         std::getline(tmp, line);
75         device << "# " << line << "\n";
76     }
77     device << "#\n";
78 }
79 
80 void
writeNodesUnsplitted(const OptionsCont & oc,NBNodeCont & nc,NBEdgeCont & ec,std::map<NBEdge *,std::string> & internalNodes)81 NWWriter_DlrNavteq::writeNodesUnsplitted(const OptionsCont& oc, NBNodeCont& nc, NBEdgeCont& ec, std::map<NBEdge*, std::string>& internalNodes) {
82     // For "real" nodes we simply use the node id.
83     // For internal nodes (geometry vectors describing edge geometry in the parlance of this format)
84     // we use the id of the edge and do not bother with
85     // compression (each direction gets its own internal node).
86     OutputDevice& device = OutputDevice::getDevice(oc.getString("dlr-navteq-output") + "_nodes_unsplitted.txt");
87     writeHeader(device, oc);
88     const GeoConvHelper& gch = GeoConvHelper::getFinal();
89     const bool haveGeo = gch.usingGeoProjection();
90     const double geoScale = pow(10.0f, haveGeo ? 5 : 2); // see NIImporter_DlrNavteq::GEO_SCALE
91     device.setPrecision(oc.getInt("dlr-navteq.precision"));
92     if (!haveGeo) {
93         WRITE_WARNING("DlrNavteq node data will be written in (floating point) cartesian coordinates");
94     }
95     // write format specifier
96     device << "# NODE_ID\tIS_BETWEEN_NODE\tamount_of_geocoordinates\tx1\ty1\t[x2 y2  ... xn  yn]\n";
97     // write header
98     Boundary boundary = gch.getConvBoundary();
99     Position min(boundary.xmin(), boundary.ymin());
100     Position max(boundary.xmax(), boundary.ymax());
101     gch.cartesian2geo(min);
102     min.mul(geoScale);
103     gch.cartesian2geo(max);
104     max.mul(geoScale);
105     int multinodes = 0;
106     for (std::map<std::string, NBEdge*>::const_iterator i = ec.begin(); i != ec.end(); ++i) {
107         if ((*i).second->getGeometry().size() > 2) {
108             multinodes++;
109         }
110     }
111     device << "# [xmin_region] " << min.x() << "\n";
112     device << "# [xmax_region] " << max.x() << "\n";
113     device << "# [ymin_region] " << min.y() << "\n";
114     device << "# [ymax_region] " << max.y() << "\n";
115     device << "# [elements_multinode] " << multinodes << "\n";
116     device << "# [elements_normalnode] " << nc.size() << "\n";
117     device << "# [xmin] " << min.x() << "\n";
118     device << "# [xmax] " << max.x() << "\n";
119     device << "# [ymin] " << min.y() << "\n";
120     device << "# [ymax] " << max.y() << "\n";
121     // write normal nodes
122     for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
123         NBNode* n = (*i).second;
124         Position pos = n->getPosition();
125         gch.cartesian2geo(pos);
126         pos.mul(geoScale);
127         device << n->getID() << "\t0\t1\t" << pos.x() << "\t" << pos.y() << "\n";
128     }
129     // write "internal" nodes
130     std::vector<std::string> avoid;
131     std::set<std::string> reservedNodeIDs;
132     const bool numericalIDs = oc.getBool("numerical-ids");
133     if (oc.isSet("reserved-ids")) {
134         NBHelpers::loadPrefixedIDsFomFile(oc.getString("reserved-ids"), "node:", reservedNodeIDs); // backward compatibility
135         NBHelpers::loadPrefixedIDsFomFile(oc.getString("reserved-ids"), "junction:", reservedNodeIDs); // selection format
136     }
137     if (numericalIDs) {
138         avoid = nc.getAllNames();
139         std::vector<std::string> avoid2 = ec.getAllNames();
140         avoid.insert(avoid.end(), avoid2.begin(), avoid2.end());
141         avoid.insert(avoid.end(), reservedNodeIDs.begin(), reservedNodeIDs.end());
142     }
143     IDSupplier idSupplier("", avoid);
144     for (std::map<std::string, NBEdge*>::const_iterator i = ec.begin(); i != ec.end(); ++i) {
145         NBEdge* e = (*i).second;
146         PositionVector geom = e->getGeometry();
147         if (geom.size() > 2) {
148             // the import NIImporter_DlrNavteq checks for the presence of a
149             // negated edge id to determine spread type. We may need to do some
150             // shifting to make this consistent
151             const bool hasOppositeID = ec.getOppositeByID(e->getID()) != nullptr;
152             if (e->getLaneSpreadFunction() == LANESPREAD_RIGHT && !hasOppositeID) {
153                 // need to write center-line geometry instead
154                 try {
155                     geom.move2side(e->getTotalWidth() / 2);
156                 } catch (InvalidArgument& exception) {
157                     WRITE_WARNING("Could not reconstruct shape for edge:'" + e->getID() + "' (" + exception.what() + ").");
158                 }
159             } else if (e->getLaneSpreadFunction() == LANESPREAD_CENTER && hasOppositeID) {
160                 // need to write left-border geometry instead
161                 try {
162                     geom.move2side(-e->getTotalWidth() / 2);
163                 } catch (InvalidArgument& exception) {
164                     WRITE_WARNING("Could not reconstruct shape for edge:'" + e->getID() + "' (" + exception.what() + ").");
165                 }
166             }
167 
168             std::string internalNodeID = e->getID();
169             if (internalNodeID == UNDEFINED
170                     || (nc.retrieve(internalNodeID) != nullptr)
171                     || reservedNodeIDs.count(internalNodeID) > 0
172                ) {
173                 // need to invent a new name to avoid clashing with the id of a 'real' node or a reserved name
174                 if (numericalIDs) {
175                     internalNodeID = idSupplier.getNext();
176                 } else {
177                     internalNodeID += "_geometry";
178                 }
179             }
180             internalNodes[e] = internalNodeID;
181             device << internalNodeID << "\t1\t" << geom.size() - 2;
182             for (int ii = 1; ii < (int)geom.size() - 1; ++ii) {
183                 Position pos = geom[(int)ii];
184                 gch.cartesian2geo(pos);
185                 pos.mul(geoScale);
186                 device << "\t" << pos.x() << "\t" << pos.y();
187             }
188             device << "\n";
189         }
190     }
191     device.close();
192 }
193 
194 
195 void
writeLinksUnsplitted(const OptionsCont & oc,NBEdgeCont & ec,std::map<NBEdge *,std::string> & internalNodes)196 NWWriter_DlrNavteq::writeLinksUnsplitted(const OptionsCont& oc, NBEdgeCont& ec, std::map<NBEdge*, std::string>& internalNodes) {
197     std::map<const std::string, std::string> nameIDs;
198     OutputDevice& device = OutputDevice::getDevice(oc.getString("dlr-navteq-output") + "_links_unsplitted.txt");
199     writeHeader(device, oc);
200     // write format specifier
201     device << "# LINK_ID\tNODE_ID_FROM\tNODE_ID_TO\tBETWEEN_NODE_ID\tLENGTH\tVEHICLE_TYPE\tFORM_OF_WAY\tBRUNNEL_TYPE\tFUNCTIONAL_ROAD_CLASS\tSPEED_CATEGORY\tNUMBER_OF_LANES\tSPEED_LIMIT\tSPEED_RESTRICTION\tNAME_ID1_REGIONAL\tNAME_ID2_LOCAL\tHOUSENUMBERS_RIGHT\tHOUSENUMBERS_LEFT\tZIP_CODE\tAREA_ID\tSUBAREA_ID\tTHROUGH_TRAFFIC\tSPECIAL_RESTRICTIONS\tEXTENDED_NUMBER_OF_LANES\tISRAMP\tCONNECTION\n";
202     // write edges
203     for (std::map<std::string, NBEdge*>::const_iterator i = ec.begin(); i != ec.end(); ++i) {
204         NBEdge* e = (*i).second;
205         const int kph = speedInKph(e->getSpeed());
206         const std::string& betweenNodeID = (e->getGeometry().size() > 2) ? internalNodes[e] : UNDEFINED;
207         std::string nameID = UNDEFINED;
208         if (oc.getBool("output.street-names")) {
209             const std::string& name = i->second->getStreetName();
210             if (name != "") {
211                 if (nameIDs.count(name) == 0) {
212                     nameIDs[name] = toString(nameIDs.size());
213                 }
214                 nameID = nameIDs[name];
215             }
216         }
217         device << e->getID() << "\t"
218                << e->getFromNode()->getID() << "\t"
219                << e->getToNode()->getID() << "\t"
220                << betweenNodeID << "\t"
221                << getGraphLength(e) << "\t"
222                << getAllowedTypes(e->getPermissions()) << "\t"
223                << getFormOfWay(e) << "\t"
224                << getBrunnelType(e) << "\t"
225                << getRoadClass(e) << "\t"
226                << getSpeedCategory(kph) << "\t"
227                << getNavteqLaneCode(e->getNumLanes()) << "\t"
228                << getSpeedCategoryUpperBound(kph) << "\t"
229                << kph << "\t"
230                << UNDEFINED << "\t" // NAME_ID1_REGIONAL XXX
231                << nameID << "\t" // NAME_ID2_LOCAL
232                << UNDEFINED << "\t" // housenumbers_right
233                << UNDEFINED << "\t" // housenumbers_left
234                << getSinglePostalCode(e->getParameter("postal_code", UNDEFINED), e->getID()) << "\t" // ZIP_CODE
235                << UNDEFINED << "\t" // AREA_ID
236                << UNDEFINED << "\t" // SUBAREA_ID
237                << "1\t" // through_traffic (allowed)
238                << UNDEFINED << "\t" // special_restrictions
239                << UNDEFINED << "\t" // extended_number_of_lanes
240                << UNDEFINED << "\t" // isRamp
241                << "0\t" // connection (between nodes always in order)
242                << "\n";
243     }
244     if (oc.getBool("output.street-names")) {
245         OutputDevice& namesDevice = OutputDevice::getDevice(oc.getString("dlr-navteq-output") + "_names.txt");
246         writeHeader(namesDevice, oc);
247         // write format specifier
248         namesDevice << "# NAME_ID\tPERMANENT_ID_INFO\tName\n";
249         namesDevice << "# [elements] " << nameIDs.size() << "\n";
250         for (std::map<const std::string, std::string>::const_iterator i = nameIDs.begin(); i != nameIDs.end(); ++i) {
251             namesDevice
252                     << i->second << "\t"
253                     << 0 << "\t"
254                     << i->first << "\n";
255         }
256         namesDevice.close();
257     }
258     device.close();
259 }
260 
261 
262 std::string
getAllowedTypes(SVCPermissions permissions)263 NWWriter_DlrNavteq::getAllowedTypes(SVCPermissions permissions) {
264     if (permissions == SVCAll) {
265         return "100000000000";
266     }
267     std::ostringstream oss;
268     oss << "0";
269     oss << ((permissions & SVC_PASSENGER)              > 0 ? 1 : 0);
270     oss << ((permissions & SVC_PASSENGER)              > 0 ? 1 : 0); // residential
271     oss << ((permissions & SVC_HOV)                    > 0 ? 1 : 0);
272     oss << ((permissions & SVC_EMERGENCY)              > 0 ? 1 : 0);
273     oss << ((permissions & SVC_TAXI)                   > 0 ? 1 : 0);
274     oss << ((permissions & (SVC_BUS | SVC_COACH))      > 0 ? 1 : 0);
275     oss << ((permissions & SVC_DELIVERY)               > 0 ? 1 : 0);
276     oss << ((permissions & (SVC_TRUCK | SVC_TRAILER))  > 0 ? 1 : 0);
277     oss << ((permissions & SVC_MOTORCYCLE)             > 0 ? 1 : 0);
278     oss << ((permissions & SVC_BICYCLE)                > 0 ? 1 : 0);
279     oss << ((permissions & SVC_PEDESTRIAN)             > 0 ? 1 : 0);
280     return oss.str();
281 }
282 
283 
284 int
getRoadClass(NBEdge * edge)285 NWWriter_DlrNavteq::getRoadClass(NBEdge* edge) {
286     // quoting the navteq manual:
287     // As a general rule, Functional Road Class assignments have no direct
288     // correlation with other road attributes like speed, controlled access, route type, etc.
289     // if the network is based on OSM, we can use the highway types for determining FRC
290     std::string type = edge->getTypeID();
291     if (StringUtils::startsWith(type, "highway.")) {
292         type = type.substr(8);
293     }
294     if (StringUtils::startsWith(type, "motorway")) {
295         return 0;
296     } else if (StringUtils::startsWith(type, "trunk")) {
297         return 1;
298     } else if (StringUtils::startsWith(type, "primary")) {
299         return 1;
300     } else if (StringUtils::startsWith(type, "secondary")) {
301         return 2;
302     } else if (StringUtils::startsWith(type, "tertiary")) {
303         return 3;
304     } else if (type == "unclassified") {
305         return 3;
306     } else if (type == "living_street" || type == "residential" || type == "road" || type == "service" || type == "track" || type == "cycleway" || type == "path" || type == "footway") {
307         return 4;
308     }
309     // as a fallback we do a simple speed / lane-count mapping anyway
310     // the resulting functional road class layers probably won't be connected as required
311     const int kph = speedInKph(edge->getSpeed());
312     if ((kph) > 100) {
313         return 0;
314     }
315     if ((kph) > 70) {
316         return 1;
317     }
318     if ((kph) > 50) {
319         return (edge->getNumLanes() > 1 ? 2 : 3);
320     }
321     if ((kph) > 30) {
322         return 3;
323     }
324     return 4;
325 }
326 
327 
328 int
getSpeedCategory(int kph)329 NWWriter_DlrNavteq::getSpeedCategory(int kph) {
330     if ((kph) > 130) {
331         return 1;
332     }
333     if ((kph) > 100) {
334         return 2;
335     }
336     if ((kph) > 90) {
337         return 3;
338     }
339     if ((kph) > 70) {
340         return 4;
341     }
342     if ((kph) > 50) {
343         return 5;
344     }
345     if ((kph) > 30) {
346         return 6;
347     }
348     if ((kph) > 10) {
349         return 7;
350     }
351     return 8;
352 }
353 
354 
355 int
getSpeedCategoryUpperBound(int kph)356 NWWriter_DlrNavteq::getSpeedCategoryUpperBound(int kph) {
357     if ((kph) > 130) {
358         return 131;
359     }
360     if ((kph) > 100) {
361         return 130;
362     }
363     if ((kph) > 90) {
364         return 100;
365     }
366     if ((kph) > 70) {
367         return 90;
368     }
369     if ((kph) > 50) {
370         return 70;
371     }
372     if ((kph) > 30) {
373         return 50;
374     }
375     if ((kph) > 10) {
376         return 30;
377     }
378     return 10;
379 }
380 
381 
382 int
getNavteqLaneCode(const int numLanes)383 NWWriter_DlrNavteq::getNavteqLaneCode(const int numLanes) {
384     const int code = (numLanes == 1 ? 1 :
385                       (numLanes < 4 ?  2 : 3));
386     return numLanes * 10 + code;
387 }
388 
389 
390 int
getBrunnelType(NBEdge * edge)391 NWWriter_DlrNavteq::getBrunnelType(NBEdge* edge) {
392     if (edge->knowsParameter("bridge")) {
393         return 1;
394     } else if (edge->knowsParameter("tunnel")) {
395         return 4;
396     } else if (edge->getTypeID() == "route.ferry") {
397         return 10;
398     }
399     return -1; // UNDEFINED
400 }
401 
402 
403 int
getFormOfWay(NBEdge * edge)404 NWWriter_DlrNavteq::getFormOfWay(NBEdge* edge) {
405     if (edge->getPermissions() == SVC_PEDESTRIAN) {
406         return 15;
407     } else if (edge->getJunctionPriority(edge->getToNode()) == NBEdge::ROUNDABOUT) {
408         return 4;
409     } else if (edge->getTypeID() == "highway.service") {
410         return 14;
411     } else if (edge->getTypeID().find("_link") != std::string::npos) {
412         return 10;
413     }
414     return 3; // speed category 1-8;
415 }
416 
417 
418 double
getGraphLength(NBEdge * edge)419 NWWriter_DlrNavteq::getGraphLength(NBEdge* edge) {
420     PositionVector geom = edge->getGeometry();
421     geom.push_back_noDoublePos(edge->getToNode()->getPosition());
422     geom.push_front_noDoublePos(edge->getFromNode()->getPosition());
423     return geom.length();
424 }
425 
426 
427 std::string
getSinglePostalCode(const std::string & zipCode,const std::string edgeID)428 NWWriter_DlrNavteq::getSinglePostalCode(const std::string& zipCode, const std::string edgeID) {
429     // might be multiple codes
430     if (zipCode.find_first_of(" ,;") != std::string::npos) {
431         WRITE_WARNING("ambiguous zip code '" + zipCode + "' for edge '" + edgeID + "'. (using first value)");
432         StringTokenizer st(zipCode, " ,;", true);
433         std::vector<std::string> ret = st.getVector();
434         return ret[0];
435     } else if (zipCode.size() > 16) {
436         WRITE_WARNING("long zip code '" + zipCode + "' for edge '" + edgeID + "'");
437     }
438     return zipCode;
439 }
440 
441 void
writeTrafficSignals(const OptionsCont & oc,NBNodeCont & nc)442 NWWriter_DlrNavteq::writeTrafficSignals(const OptionsCont& oc, NBNodeCont& nc) {
443     OutputDevice& device = OutputDevice::getDevice(oc.getString("dlr-navteq-output") + "_traffic_signals.txt");
444     writeHeader(device, oc);
445     const GeoConvHelper& gch = GeoConvHelper::getFinal();
446     const bool haveGeo = gch.usingGeoProjection();
447     const double geoScale = pow(10.0f, haveGeo ? 5 : 2); // see NIImporter_DlrNavteq::GEO_SCALE
448     device.setPrecision(oc.getInt("dlr-navteq.precision"));
449     // write format specifier
450     device << "#Traffic signal related to LINK_ID and NODE_ID with location relative to driving direction.\n#column format like pointcollection.\n#DESCRIPTION->LOCATION: 1-rechts von LINK; 2-links von LINK; 3-oberhalb LINK -1-keineAngabe\n#RELATREC_ID\tPOICOL_TYPE\tDESCRIPTION\tLONGITUDE\tLATITUDE\tLINK_ID\n";
451     // write record for every edge incoming to a tls controlled node
452     for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
453         NBNode* n = (*i).second;
454         if (n->isTLControlled()) {
455             Position pos = n->getPosition();
456             gch.cartesian2geo(pos);
457             pos.mul(geoScale);
458             const EdgeVector& incoming = n->getIncomingEdges();
459             for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
460                 NBEdge* e = *it;
461                 device << e->getID() << "\t"
462                        << "12\t" // POICOL_TYPE
463                        << "LSA;NODEIDS#" << n->getID() << "#;LOCATION#-1#;\t"
464                        << pos.x() << "\t"
465                        << pos.y() << "\t"
466                        << e->getID() << "\n";
467             }
468         }
469     }
470     device.close();
471 }
472 
473 
474 void
writeProhibitedManoeuvres(const OptionsCont & oc,const NBNodeCont & nc,const NBEdgeCont & ec)475 NWWriter_DlrNavteq::writeProhibitedManoeuvres(const OptionsCont& oc, const NBNodeCont& nc, const NBEdgeCont& ec) {
476     OutputDevice& device = OutputDevice::getDevice(oc.getString("dlr-navteq-output") + "_prohibited_manoeuvres.txt");
477     writeHeader(device, oc);
478     // need to invent id for relation
479     std::set<std::string> reservedRelIDs;
480     if (oc.isSet("reserved-ids")) {
481         NBHelpers::loadPrefixedIDsFomFile(oc.getString("reserved-ids"), "rel:", reservedRelIDs);
482     }
483     std::vector<std::string> avoid = ec.getAllNames(); // already used for tls RELATREC_ID
484     avoid.insert(avoid.end(), reservedRelIDs.begin(), reservedRelIDs.end());
485     IDSupplier idSupplier("", avoid); // @note: use a global relRecIDsupplier if this is used more often
486     // write format specifier
487     device << "#No driving allowed from ID1 to ID2 or the complete chain from ID1 to IDn\n";
488     device << "#RELATREC_ID\tPERMANENT_ID_INFO\tVALIDITY_PERIOD\tTHROUGH_TRAFFIC\tVEHICLE_TYPE\tNAVTEQ_LINK_ID1\t[NAVTEQ_LINK_ID2 ...]\n";
489     // write record for every pair of incoming/outgoing edge that are not connected despite having common permissions
490     for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
491         NBNode* n = (*i).second;
492         const EdgeVector& incoming = n->getIncomingEdges();
493         const EdgeVector& outgoing = n->getOutgoingEdges();
494         for (EdgeVector::const_iterator j = incoming.begin(); j != incoming.end(); ++j) {
495             NBEdge* inEdge = *j;
496             const SVCPermissions inPerm = inEdge->getPermissions();
497             for (EdgeVector::const_iterator k = outgoing.begin(); k != outgoing.end(); ++k) {
498                 NBEdge* outEdge = *k;
499                 const SVCPermissions outPerm = outEdge->getPermissions();
500                 const SVCPermissions commonPerm = inPerm & outPerm;
501                 if (commonPerm != 0 && commonPerm != SVC_PEDESTRIAN && !inEdge->isConnectedTo(outEdge)) {
502                     device
503                             << idSupplier.getNext() << "\t"
504                             << 1 << "\t" // permanent id
505                             << UNDEFINED << "\t"
506                             << 1 << "\t"
507                             << getAllowedTypes(SVCAll) << "\t"
508                             << inEdge->getID() << "\t" << outEdge->getID() << "\n";
509                 }
510             }
511         }
512     }
513     device.close();
514 }
515 
516 
517 void
writeConnectedLanes(const OptionsCont & oc,NBNodeCont & nc)518 NWWriter_DlrNavteq::writeConnectedLanes(const OptionsCont& oc, NBNodeCont& nc) {
519     OutputDevice& device = OutputDevice::getDevice(oc.getString("dlr-navteq-output") + "_connected_lanes.txt");
520     writeHeader(device, oc);
521     // write format specifier
522     device << "#Lane connections related to LINK-IDs and NODE-ID.\n";
523     device << "#column format like pointcollection.\n";
524     device << "#NODE-ID\tVEHICLE-TYPE\tFROM_LANE\tTO_LANE\tTHROUGH_TRAFFIC\tLINK_IDs[2..*]\n";
525     // write record for every connection
526     for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
527         NBNode* n = (*i).second;
528         const EdgeVector& incoming = n->getIncomingEdges();
529         for (EdgeVector::const_iterator j = incoming.begin(); j != incoming.end(); ++j) {
530             NBEdge* from = *j;
531             const SVCPermissions fromPerm = from->getPermissions();
532             const std::vector<NBEdge::Connection>& connections = from->getConnections();
533             for (std::vector<NBEdge::Connection>::const_iterator it_c = connections.begin(); it_c != connections.end(); it_c++) {
534                 const NBEdge::Connection& c = *it_c;
535                 device
536                         << n->getID() << "\t"
537                         << getAllowedTypes(fromPerm & c.toEdge->getPermissions()) << "\t"
538                         << c.fromLane + 1 << "\t" // one-based
539                         << c.toLane + 1 << "\t" // one-based
540                         << 1 << "\t" // no information regarding permissibility of through traffic
541                         << from->getID() << "\t"
542                         << c.toEdge->getID() << "\t"
543                         << "\n";
544             }
545         }
546     }
547     device.close();
548 }
549 
550 /****************************************************************************/
551 
552