1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file    NIImporter_VISUM.cpp
11 /// @author  Daniel Krajzewicz
12 /// @author  Jakob Erdmann
13 /// @author  Sascha Krieg
14 /// @author  Michael Behrisch
15 /// @date    Fri, 19 Jul 2002
16 /// @version $Id$
17 ///
18 // A VISUM network importer
19 /****************************************************************************/
20 
21 
22 // ===========================================================================
23 // included modules
24 // ===========================================================================
25 #include <config.h>
26 
27 #include <string>
28 #include <utils/common/MsgHandler.h>
29 #include <utils/common/StringUtils.h>
30 #include <utils/common/ToString.h>
31 #include <utils/common/StringUtils.h>
32 #include <utils/options/OptionsCont.h>
33 #include <utils/geom/GeoConvHelper.h>
34 #include <netbuild/NBDistrict.h>
35 
36 #include <netbuild/NBNetBuilder.h>
37 #include "NILoader.h"
38 #include "NIImporter_VISUM.h"
39 
40 
41 // ===========================================================================
42 // method definitions
43 // ===========================================================================
44 // ---------------------------------------------------------------------------
45 // static methods (interface in this case)
46 // ---------------------------------------------------------------------------
47 void
loadNetwork(const OptionsCont & oc,NBNetBuilder & nb)48 NIImporter_VISUM::loadNetwork(const OptionsCont& oc, NBNetBuilder& nb) {
49     // check whether the option is set (properly)
50     if (!oc.isSet("visum-file")) {
51         return;
52     }
53     // build the handler
54     NIImporter_VISUM loader(nb, oc.getString("visum-file"),
55                             NBCapacity2Lanes(oc.getFloat("lanes-from-capacity.norm")),
56                             oc.getBool("visum.use-type-priority"));
57     loader.load();
58 }
59 
60 
61 
62 // ---------------------------------------------------------------------------
63 // loader methods
64 // ---------------------------------------------------------------------------
NIImporter_VISUM(NBNetBuilder & nb,const std::string & file,NBCapacity2Lanes capacity2Lanes,bool useVisumPrio)65 NIImporter_VISUM::NIImporter_VISUM(NBNetBuilder& nb,
66                                    const std::string& file,
67                                    NBCapacity2Lanes capacity2Lanes,
68                                    bool useVisumPrio)
69     : myNetBuilder(nb), myFileName(file),
70       myCapacity2Lanes(capacity2Lanes), myUseVisumPrio(useVisumPrio) {
71     // the order of process is important!
72     // set1
73     addParser("VSYS", &NIImporter_VISUM::parse_VSysTypes);
74     addParser("STRECKENTYP", &NIImporter_VISUM::parse_Types);
75     addParser("KNOTEN", &NIImporter_VISUM::parse_Nodes);
76     addParser("BEZIRK", &NIImporter_VISUM::parse_Districts);
77     addParser("PUNKT", &NIImporter_VISUM::parse_Point);
78 
79     // set2
80     // two types of "strecke"
81     addParser("STRECKE", &NIImporter_VISUM::parse_Edges);
82     addParser("STRECKEN", &NIImporter_VISUM::parse_Edges);
83     addParser("KANTE", &NIImporter_VISUM::parse_Kante);
84 
85     // set3
86     addParser("ANBINDUNG", &NIImporter_VISUM::parse_Connectors);
87     // two types of "abbieger"
88     addParser("ABBIEGEBEZIEHUNG", &NIImporter_VISUM::parse_Turns);
89     addParser("ABBIEGER", &NIImporter_VISUM::parse_Turns);
90 
91     addParser("STRECKENPOLY", &NIImporter_VISUM::parse_EdgePolys);
92     addParser("FAHRSTREIFEN", &NIImporter_VISUM::parse_Lanes);
93     addParser("FLAECHENELEMENT", &NIImporter_VISUM::parse_PartOfArea);
94 
95     // set4
96     // two types of lsa
97     addParser("LSA", &NIImporter_VISUM::parse_TrafficLights);
98     addParser("SIGNALANLAGE", &NIImporter_VISUM::parse_TrafficLights);
99     // two types of knotenzulsa
100     addParser("KNOTENZULSA", &NIImporter_VISUM::parse_NodesToTrafficLights);
101     addParser("LSAZUKNOTEN", &NIImporter_VISUM::parse_NodesToTrafficLights);
102     addParser("SIGNALANLAGEZUKNOTEN", &NIImporter_VISUM::parse_NodesToTrafficLights);
103     // two types of signalgruppe
104     addParser("LSASIGNALGRUPPE", &NIImporter_VISUM::parse_SignalGroups);
105     addParser("SIGNALGRUPPE", &NIImporter_VISUM::parse_SignalGroups);
106     // three types of ABBZULSASIGNALGRUPPE
107     addParser("ABBZULSASIGNALGRUPPE", &NIImporter_VISUM::parse_TurnsToSignalGroups);
108     addParser("SIGNALGRUPPEZUABBIEGER", &NIImporter_VISUM::parse_TurnsToSignalGroups);
109     addParser("SIGNALGRUPPEZUFSABBIEGER", &NIImporter_VISUM::parse_TurnsToSignalGroups);
110 
111     addParser("TEILFLAECHENELEMENT", &NIImporter_VISUM::parse_AreaSubPartElement);
112 
113     // two types of LSAPHASE
114     addParser("LSAPHASE", &NIImporter_VISUM::parse_Phases);
115     addParser("PHASE", &NIImporter_VISUM::parse_Phases);
116 
117     addParser("LSASIGNALGRUPPEZULSAPHASE", &NIImporter_VISUM::parse_SignalGroupsToPhases);
118     addParser("FAHRSTREIFENABBIEGER", &NIImporter_VISUM::parse_LanesConnections);
119 }
120 
121 
~NIImporter_VISUM()122 NIImporter_VISUM::~NIImporter_VISUM() {
123     for (NIVisumTL_Map::iterator j = myTLS.begin(); j != myTLS.end(); j++) {
124         delete j->second;
125     }
126 }
127 
128 
129 void
addParser(const std::string & name,ParsingFunction function)130 NIImporter_VISUM::addParser(const std::string& name, ParsingFunction function) {
131     TypeParser p;
132     p.name = name;
133     p.function = function;
134     p.position = -1;
135     mySingleDataParsers.push_back(p);
136 }
137 
138 
139 void
load()140 NIImporter_VISUM::load() {
141     // open the file
142     if (!myLineReader.setFile(myFileName)) {
143         throw ProcessError("Can not open visum-file '" + myFileName + "'.");
144     }
145     // scan the file for data positions
146     while (myLineReader.hasMore()) {
147         std::string line = myLineReader.readLine();
148         if (line.length() > 0 && line[0] == '$') {
149             ParserVector::iterator i;
150             for (i = mySingleDataParsers.begin(); i != mySingleDataParsers.end(); i++) {
151                 std::string dataName = "$" + (*i).name + ":";
152                 if (line.substr(0, dataName.length()) == dataName) {
153                     (*i).position = myLineReader.getPosition();
154                     (*i).pattern = line.substr(dataName.length());
155                     WRITE_MESSAGE("Found: " + dataName + " at " + toString<int>(myLineReader.getPosition()));
156                 }
157             }
158         }
159     }
160     // go through the parsers and process all entries
161     for (ParserVector::iterator i = mySingleDataParsers.begin(); i != mySingleDataParsers.end(); i++) {
162         if ((*i).position < 0) {
163             // do not process using parsers for which no information was found
164             continue;
165         }
166         // ok, the according information is stored in the file
167         PROGRESS_BEGIN_MESSAGE("Parsing " + (*i).name);
168         // reset the line reader and let it point to the begin of the according data field
169         myLineReader.reinit();
170         myLineReader.setPos((*i).position);
171         // prepare the line parser
172         myLineParser.reinit((*i).pattern);
173         // read
174         bool singleDataEndFound = false;
175         while (myLineReader.hasMore() && !singleDataEndFound) {
176             std::string line = myLineReader.readLine();
177             if (line.length() == 0 || line[0] == '*' || line[0] == '$') {
178                 singleDataEndFound = true;
179             } else {
180                 myLineParser.parseLine(line);
181                 try {
182                     myCurrentID = "<unknown>";
183                     (this->*(*i).function)();
184                 } catch (OutOfBoundsException&) {
185                     WRITE_ERROR("Too short value line in " + (*i).name + " occurred.");
186                 } catch (NumberFormatException&) {
187                     WRITE_ERROR("A value in " + (*i).name + " should be numeric but is not (id='" + myCurrentID + "').");
188                 } catch (UnknownElement& e) {
189                     WRITE_ERROR("One of the needed values ('" + std::string(e.what()) + "') is missing in " + (*i).name + ".");
190                 }
191             }
192         }
193         // close single reader processing
194         PROGRESS_DONE_MESSAGE();
195     }
196     // build traffic lights
197     for (NIVisumTL_Map::iterator j = myTLS.begin(); j != myTLS.end(); j++) {
198         j->second->build(myNetBuilder.getEdgeCont(), myNetBuilder.getTLLogicCont());
199     }
200     // build district shapes
201     for (std::map<NBDistrict*, PositionVector>::const_iterator k = myDistrictShapes.begin(); k != myDistrictShapes.end(); ++k) {
202         (*k).first->addShape((*k).second);
203     }
204 }
205 
206 
207 
208 
209 
210 void
parse_VSysTypes()211 NIImporter_VISUM::parse_VSysTypes() {
212     std::string name = myLineParser.know("VSysCode") ? myLineParser.get("VSysCode").c_str() : myLineParser.get("CODE").c_str();
213     std::string type = myLineParser.know("VSysMode") ? myLineParser.get("VSysMode").c_str() : myLineParser.get("Typ").c_str();
214     myVSysTypes[name] = type;
215 }
216 
217 
218 void
parse_Types()219 NIImporter_VISUM::parse_Types() {
220     // get the id
221     myCurrentID = NBHelpers::normalIDRepresentation(myLineParser.get("Nr"));
222     // get the maximum speed
223     double speed = getWeightedFloat2("v0-IV", "V0IV", "km/h");
224     if (speed == 0) {
225         // unlimited speed
226         speed = 3600;
227     } else if (speed < 0) {
228         WRITE_ERROR("Type '" + myCurrentID + "' has speed " + toString(speed));
229     }
230     // get the permissions
231     SVCPermissions permissions = getPermissions("VSYSSET", true);
232     // get the priority
233     const int priority = 1000 - StringUtils::toInt(myLineParser.get("Rang"));
234     // try to retrieve the number of lanes
235     const int numLanes = myCapacity2Lanes.get(getNamedFloat("Kap-IV", "KAPIV"));
236     // insert the type
237     myNetBuilder.getTypeCont().insert(myCurrentID, numLanes, speed / (double) 3.6, priority, permissions, NBEdge::UNSPECIFIED_WIDTH, false, NBEdge::UNSPECIFIED_WIDTH, NBEdge::UNSPECIFIED_WIDTH);
238     myNetBuilder.getTypeCont().markAsSet(myCurrentID, SUMO_ATTR_NUMLANES);
239     myNetBuilder.getTypeCont().markAsSet(myCurrentID, SUMO_ATTR_SPEED);
240     myNetBuilder.getTypeCont().markAsSet(myCurrentID, SUMO_ATTR_PRIORITY);
241     myNetBuilder.getTypeCont().markAsSet(myCurrentID, SUMO_ATTR_ONEWAY);
242     myNetBuilder.getTypeCont().markAsSet(myCurrentID, SUMO_ATTR_ALLOW);
243 }
244 
245 
246 void
parse_Nodes()247 NIImporter_VISUM::parse_Nodes() {
248     // get the id
249     myCurrentID = NBHelpers::normalIDRepresentation(myLineParser.get("Nr"));
250     // get the position
251     double x = getNamedFloat("XKoord");
252     double y = getNamedFloat("YKoord");
253     Position pos(x, y);
254     if (!NBNetBuilder::transformCoordinate(pos)) {
255         WRITE_ERROR("Unable to project coordinates for node " + myCurrentID + ".");
256         return;
257     }
258     // add to the list
259     if (!myNetBuilder.getNodeCont().insert(myCurrentID, pos)) {
260         WRITE_ERROR("Duplicate node occurred ('" + myCurrentID + "').");
261     }
262 }
263 
264 
265 void
parse_Districts()266 NIImporter_VISUM::parse_Districts() {
267     // get the id
268     myCurrentID = NBHelpers::normalIDRepresentation(myLineParser.get("Nr"));
269     // get the information whether the source and the destination
270     //  connections are weighted
271     //bool sourcesWeighted = getWeightedBool("Proz_Q");
272     //bool destWeighted = getWeightedBool("Proz_Z");
273     // get the node information
274     double x = getNamedFloat("XKoord");
275     double y = getNamedFloat("YKoord");
276     Position pos(x, y);
277     if (!NBNetBuilder::transformCoordinate(pos, false)) {
278         WRITE_ERROR("Unable to project coordinates for district " + myCurrentID + ".");
279         return;
280     }
281     // build the district
282     NBDistrict* district = new NBDistrict(myCurrentID, pos);
283     if (!myNetBuilder.getDistrictCont().insert(district)) {
284         WRITE_ERROR("Duplicate district occurred ('" + myCurrentID + "').");
285         delete district;
286         return;
287     }
288     if (myLineParser.know("FLAECHEID")) {
289         long long int flaecheID = StringUtils::toLong(myLineParser.get("FLAECHEID"));
290         myShapeDistrictMap[flaecheID] = district;
291     }
292 }
293 
294 
295 void
parse_Point()296 NIImporter_VISUM::parse_Point() {
297     long long int id = StringUtils::toLong(myLineParser.get("ID"));
298     double x = StringUtils::toDouble(myLineParser.get("XKOORD"));
299     double y = StringUtils::toDouble(myLineParser.get("YKOORD"));
300     Position pos(x, y);
301     if (!NBNetBuilder::transformCoordinate(pos, false)) {
302         WRITE_ERROR("Unable to project coordinates for point " + toString(id) + ".");
303         return;
304     }
305     myPoints[id] = pos;
306 }
307 
308 
309 void
parse_Edges()310 NIImporter_VISUM::parse_Edges() {
311     if (myLineParser.know("VSYSSET") && myLineParser.get("VSYSSET") == "") {
312         // no vehicle allowed; don't add
313         return;
314     }
315     // get the id
316     myCurrentID = NBHelpers::normalIDRepresentation(myLineParser.get("Nr"));
317     // get the from- & to-node and validate them
318     NBNode* from = getNamedNode("VonKnot", "VonKnotNr");
319     NBNode* to = getNamedNode("NachKnot", "NachKnotNr");
320     if (!checkNodes(from, to)) {
321         return;
322     }
323     // get the type
324     std::string type = myLineParser.know("Typ") ? myLineParser.get("Typ") : myLineParser.get("TypNr");
325     // get the speed
326     double speed = myNetBuilder.getTypeCont().getSpeed(type);
327     if (!OptionsCont::getOptions().getBool("visum.use-type-speed")) {
328         try {
329             std::string speedS = myLineParser.know("v0-IV") ? myLineParser.get("v0-IV") : myLineParser.get("V0IV");
330             if (speedS.find("km/h") != std::string::npos) {
331                 speedS = speedS.substr(0, speedS.find("km/h"));
332             }
333             speed = StringUtils::toDouble(speedS) / 3.6;
334         } catch (OutOfBoundsException&) {}
335     }
336     if (speed <= 0) {
337         speed = myNetBuilder.getTypeCont().getSpeed(type);
338     }
339 
340     // get the information whether the edge is a one-way
341     bool oneway = myLineParser.know("Einbahn")
342                   ? StringUtils::toBool(myLineParser.get("Einbahn"))
343                   : true;
344     // get the number of lanes
345     int nolanes = myNetBuilder.getTypeCont().getNumLanes(type);
346     if (!OptionsCont::getOptions().getBool("visum.recompute-lane-number")) {
347         if (!OptionsCont::getOptions().getBool("visum.use-type-laneno")) {
348             if (myLineParser.know("Fahrstreifen")) {
349                 nolanes = StringUtils::toInt(myLineParser.get("Fahrstreifen"));
350             } else if (myLineParser.know("ANZFAHRSTREIFEN")) {
351                 nolanes = StringUtils::toInt(myLineParser.get("ANZFAHRSTREIFEN"));
352             }
353         }
354     } else {
355         if (myLineParser.know("KAPIV")) {
356             nolanes = myCapacity2Lanes.get(StringUtils::toDouble(myLineParser.get("KAPIV")));
357         } else if (myLineParser.know("KAP-IV")) {
358             nolanes = myCapacity2Lanes.get(StringUtils::toDouble(myLineParser.get("KAP-IV")));
359         }
360     }
361     // check whether the id is already used
362     //  (should be the opposite direction)
363     bool oneway_checked = oneway;
364     NBEdge* previous = myNetBuilder.getEdgeCont().retrieve(myCurrentID);
365     if (previous != nullptr) {
366         myCurrentID = '-' + myCurrentID;
367         previous->setLaneSpreadFunction(LANESPREAD_RIGHT);
368         oneway_checked = false;
369     }
370     if (find(myTouchedEdges.begin(), myTouchedEdges.end(), myCurrentID) != myTouchedEdges.end()) {
371         oneway_checked = false;
372     }
373     std::string tmpid = '-' + myCurrentID;
374     if (find(myTouchedEdges.begin(), myTouchedEdges.end(), tmpid) != myTouchedEdges.end()) {
375         previous = myNetBuilder.getEdgeCont().retrieve(tmpid);
376         if (previous != nullptr) {
377             previous->setLaneSpreadFunction(LANESPREAD_RIGHT);
378         }
379         oneway_checked = false;
380     }
381     // add the edge
382     const SVCPermissions permissions = getPermissions("VSYSSET", false, myNetBuilder.getTypeCont().getPermissions(type));
383     int prio = myUseVisumPrio ? myNetBuilder.getTypeCont().getPriority(type) : -1;
384     if (nolanes != 0 && speed != 0) {
385         LaneSpreadFunction lsf = oneway_checked ? LANESPREAD_CENTER : LANESPREAD_RIGHT;
386         // @todo parse name from visum files
387         NBEdge* e = new NBEdge(myCurrentID, from, to, type, speed, nolanes, prio,
388                                NBEdge::UNSPECIFIED_WIDTH, NBEdge::UNSPECIFIED_OFFSET, "", lsf);
389         e->setPermissions(permissions);
390         if (!myNetBuilder.getEdgeCont().insert(e)) {
391             delete e;
392             WRITE_ERROR("Duplicate edge occurred ('" + myCurrentID + "').");
393         }
394     }
395     myTouchedEdges.push_back(myCurrentID);
396     // nothing more to do, when the edge is a one-way street
397     if (oneway) {
398         return;
399     }
400     // add the opposite edge
401     myCurrentID = '-' + myCurrentID;
402     if (nolanes != 0 && speed != 0) {
403         LaneSpreadFunction lsf = oneway_checked ? LANESPREAD_CENTER : LANESPREAD_RIGHT;
404         // @todo parse name from visum files
405         NBEdge* e = new NBEdge(myCurrentID, from, to, type, speed, nolanes, prio,
406                                NBEdge::UNSPECIFIED_WIDTH, NBEdge::UNSPECIFIED_OFFSET, "", lsf);
407         e->setPermissions(permissions);
408         if (!myNetBuilder.getEdgeCont().insert(e)) {
409             delete e;
410             WRITE_ERROR("Duplicate edge occurred ('" + myCurrentID + "').");
411         }
412     }
413     myTouchedEdges.push_back(myCurrentID);
414 }
415 
416 
417 void
parse_Kante()418 NIImporter_VISUM::parse_Kante() {
419     long long int id = StringUtils::toLong(myLineParser.get("ID"));
420     long long int from = StringUtils::toLong(myLineParser.get("VONPUNKTID"));
421     long long int to = StringUtils::toLong(myLineParser.get("NACHPUNKTID"));
422     myEdges[id] = std::make_pair(from, to);
423 }
424 
425 
426 void
parse_PartOfArea()427 NIImporter_VISUM::parse_PartOfArea() {
428     long long int flaecheID = StringUtils::toLong(myLineParser.get("FLAECHEID"));
429     long long int flaechePartID = StringUtils::toLong(myLineParser.get("TFLAECHEID"));
430     if (mySubPartsAreas.find(flaechePartID) == mySubPartsAreas.end()) {
431         mySubPartsAreas[flaechePartID] = std::vector<long long int>();
432     }
433     mySubPartsAreas[flaechePartID].push_back(flaecheID);
434 }
435 
436 
437 void
parse_Connectors()438 NIImporter_VISUM::parse_Connectors() {
439     if (OptionsCont::getOptions().getBool("visum.no-connectors")) {
440         // do nothing, if connectors shall not be imported
441         return;
442     }
443     // get the source district
444     std::string bez = NBHelpers::normalIDRepresentation(myLineParser.get("BezNr"));
445     // get the destination node
446     NBNode* dest = getNamedNode("KnotNr");
447     if (dest == nullptr) {
448         return;
449     }
450     // get the weight of the connection
451     double proz = 1;
452     if (myLineParser.know("Proz") || myLineParser.know("Proz(IV)")) {
453         proz = getNamedFloat("Proz", "Proz(IV)") / 100;
454     }
455     // get the duration to wait (unused)
456 //     double retard = -1;
457 //     if (myLineParser.know("t0-IV")) {
458 //         retard = getNamedFloat("t0-IV", -1);
459 //     }
460     // get the type;
461     //  use a standard type with a large speed when a type is not given
462     std::string type = myLineParser.know("Typ")
463                        ? NBHelpers::normalIDRepresentation(myLineParser.get("Typ"))
464                        : "";
465     // add the connectors as an edge
466     std::string id = bez + "-" + dest->getID();
467     // get the information whether this is a sink or a source
468     std::string dir = myLineParser.get("Richtung");
469     if (dir.length() == 0) {
470         dir = "QZ";
471     }
472     // build the source when needed
473     if (dir.find('Q') != std::string::npos) {
474         const EdgeVector& edges = dest->getOutgoingEdges();
475         bool hasContinuation = false;
476         for (EdgeVector::const_iterator i = edges.begin(); i != edges.end(); ++i) {
477             if (!(*i)->isMacroscopicConnector()) {
478                 hasContinuation = true;
479             }
480         }
481         if (!hasContinuation) {
482             // obviously, there is no continuation on the net
483             WRITE_WARNING("Incoming connector '" + id + "' will not be build - would be not connected to network.");
484         } else {
485             NBNode* src = buildDistrictNode(bez, dest, true);
486             if (src == nullptr) {
487                 WRITE_ERROR("The district '" + bez + "' could not be built.");
488                 return;
489             }
490             NBEdge* edge = new NBEdge(id, src, dest, "VisumConnector",
491                                       OptionsCont::getOptions().getFloat("visum.connector-speeds"),
492                                       OptionsCont::getOptions().getInt("visum.connectors-lane-number"),
493                                       -1, NBEdge::UNSPECIFIED_WIDTH, NBEdge::UNSPECIFIED_OFFSET,
494                                       "", LANESPREAD_RIGHT);
495             edge->setAsMacroscopicConnector();
496             if (!myNetBuilder.getEdgeCont().insert(edge)) {
497                 WRITE_ERROR("A duplicate edge id occurred (ID='" + id + "').");
498                 return;
499             }
500             edge = myNetBuilder.getEdgeCont().retrieve(id);
501             if (edge != nullptr) {
502                 myNetBuilder.getDistrictCont().addSource(bez, edge, proz);
503             }
504         }
505     }
506     // build the sink when needed
507     if (dir.find('Z') != std::string::npos) {
508         const EdgeVector& edges = dest->getIncomingEdges();
509         bool hasPredeccessor = false;
510         for (EdgeVector::const_iterator i = edges.begin(); i != edges.end(); ++i) {
511             if (!(*i)->isMacroscopicConnector()) {
512                 hasPredeccessor = true;
513             }
514         }
515         if (!hasPredeccessor) {
516             // obviously, the network is not connected to this node
517             WRITE_WARNING("Outgoing connector '" + id + "' will not be build - would be not connected to network.");
518         } else {
519             NBNode* src = buildDistrictNode(bez, dest, false);
520             if (src == nullptr) {
521                 WRITE_ERROR("The district '" + bez + "' could not be built.");
522                 return;
523             }
524             id = "-" + id;
525             NBEdge* edge = new NBEdge(id, dest, src, "VisumConnector",
526                                       OptionsCont::getOptions().getFloat("visum.connector-speeds"),
527                                       OptionsCont::getOptions().getInt("visum.connectors-lane-number"),
528                                       -1, NBEdge::UNSPECIFIED_WIDTH, NBEdge::UNSPECIFIED_OFFSET,
529                                       "", LANESPREAD_RIGHT);
530             edge->setAsMacroscopicConnector();
531             if (!myNetBuilder.getEdgeCont().insert(edge)) {
532                 WRITE_ERROR("A duplicate edge id occurred (ID='" + id + "').");
533                 return;
534             }
535             edge = myNetBuilder.getEdgeCont().retrieve(id);
536             if (edge != nullptr) {
537                 myNetBuilder.getDistrictCont().addSink(bez, edge, proz);
538             }
539         }
540     }
541 }
542 
543 
544 void
parse_Turns()545 NIImporter_VISUM::parse_Turns() {
546     if (myLineParser.know("VSYSSET") && myLineParser.get("VSYSSET") == "") {
547         // no vehicle allowed; don't add
548         return;
549     }
550     // retrieve the nodes
551     NBNode* from = getNamedNode("VonKnot", "VonKnotNr");
552     NBNode* via = getNamedNode("UeberKnot", "UeberKnotNr");
553     NBNode* to = getNamedNode("NachKnot", "NachKnotNr");
554     if (from == nullptr || via == nullptr || to == nullptr) {
555         return;
556     }
557     // all nodes are known
558     std::string type = myLineParser.know("VSysCode")
559                        ? myLineParser.get("VSysCode")
560                        : myLineParser.get("VSYSSET");
561     if (myVSysTypes.find(type) != myVSysTypes.end() && myVSysTypes.find(type)->second == "IV") {
562         // try to set the turning definition
563         NBEdge* src = from->getConnectionTo(via);
564         NBEdge* dest = via->getConnectionTo(to);
565         // check both
566         if (src == nullptr) {
567             if (OptionsCont::getOptions().getBool("visum.verbose-warnings")) {
568                 WRITE_WARNING("There is no edge from node '" + from->getID() + "' to node '" + via->getID() + "'.");
569             }
570             return;
571         }
572         if (dest == nullptr) {
573             if (OptionsCont::getOptions().getBool("visum.verbose-warnings")) {
574                 WRITE_WARNING("There is no edge from node '" + via->getID() + "' to node '" + to->getID() + "'.");
575             }
576             return;
577         }
578         // both edges found
579         //  set them into the edge
580         src->addEdge2EdgeConnection(dest);
581     }
582 }
583 
584 
585 void
parse_EdgePolys()586 NIImporter_VISUM::parse_EdgePolys() {
587     // get the from- & to-node and validate them
588     NBNode* from = getNamedNode("VonKnot", "VonKnotNr");
589     NBNode* to = getNamedNode("NachKnot", "NachKnotNr");
590     if (!checkNodes(from, to)) {
591         return;
592     }
593     bool failed = false;
594     int index;
595     double x, y;
596     try {
597         index = StringUtils::toInt(myLineParser.get("INDEX"));
598         x = getNamedFloat("XKoord");
599         y = getNamedFloat("YKoord");
600     } catch (NumberFormatException&) {
601         WRITE_ERROR("Error in geometry description from node '" + from->getID() + "' to node '" + to->getID() + "'.");
602         return;
603     }
604     Position pos(x, y);
605     if (!NBNetBuilder::transformCoordinate(pos)) {
606         WRITE_ERROR("Unable to project coordinates for node '" + from->getID() + "'.");
607         return;
608     }
609     NBEdge* e = from->getConnectionTo(to);
610     if (e != nullptr) {
611         e->addGeometryPoint(index, pos);
612     } else {
613         failed = true;
614     }
615     e = to->getConnectionTo(from);
616     if (e != nullptr) {
617         e->addGeometryPoint(-index, pos);
618         failed = false;
619     }
620     // check whether the operation has failed
621     if (failed) {
622         if (OptionsCont::getOptions().getBool("visum.verbose-warnings")) {
623             WRITE_WARNING("There is no edge from node '" + from->getID() + "' to node '" + to->getID() + "'.");
624         }
625     }
626 }
627 
628 
629 void
parse_Lanes()630 NIImporter_VISUM::parse_Lanes() {
631     // The base number of lanes for the edge was already defined in STRECKE
632     // this refines lane specific attribute (width) and optionally introduces splits for additional lanes
633     // It is permitted for KNOTNR to be 0
634     //
635     // get the edge
636     NBEdge* baseEdge = getNamedEdge("STRNR");
637     if (baseEdge == nullptr) {
638         return;
639     }
640     NBEdge* edge = baseEdge;
641     // get the node
642     NBNode* node = getNamedNodeSecure("KNOTNR");
643     if (node == nullptr) {
644         node = edge->getToNode();
645     } else {
646         edge = getNamedEdgeContinuating("STRNR", node);
647     }
648     // check
649     if (edge == nullptr) {
650         return;
651     }
652     // get the lane
653     std::string laneS = myLineParser.know("FSNR")
654                         ? NBHelpers::normalIDRepresentation(myLineParser.get("FSNR"))
655                         : NBHelpers::normalIDRepresentation(myLineParser.get("NR"));
656     int lane = -1;
657     try {
658         lane = StringUtils::toInt(laneS);
659     } catch (NumberFormatException&) {
660         WRITE_ERROR("A lane number for edge '" + edge->getID() + "' is not numeric (" + laneS + ").");
661         return;
662     }
663     lane -= 1;
664     if (lane < 0) {
665         WRITE_ERROR("A lane number for edge '" + edge->getID() + "' is not positive (" + laneS + ").");
666         return;
667     }
668     // get the direction
669     std::string dirS = NBHelpers::normalIDRepresentation(myLineParser.get("RICHTTYP"));
670     int prevLaneNo = baseEdge->getNumLanes();
671     if ((dirS == "1" && !(node->hasIncoming(edge))) || (dirS == "0" && !(node->hasOutgoing(edge)))) {
672         // get the last part of the turnaround direction
673         NBEdge* cand = getReversedContinuating(edge, node);
674         if (cand) {
675             edge = cand;
676         }
677     }
678     // get the length
679     std::string lengthS = NBHelpers::normalIDRepresentation(myLineParser.get("LAENGE"));
680     double length = -1;
681     try {
682         length = StringUtils::toDouble(lengthS);
683     } catch (NumberFormatException&) {
684         WRITE_ERROR("A lane length for edge '" + edge->getID() + "' is not numeric (" + lengthS + ").");
685         return;
686     }
687     if (length < 0) {
688         WRITE_ERROR("A lane length for edge '" + edge->getID() + "' is not positive (" + lengthS + ").");
689         return;
690     }
691     //
692     if (dirS == "1") {
693         lane -= prevLaneNo;
694     }
695     //
696     if (length == 0) {
697         if ((int) edge->getNumLanes() > lane) {
698             // ok, we know this already...
699             return;
700         }
701         // increment by one
702         edge->incLaneNo(1);
703     } else {
704         // check whether this edge already has been created
705         if (isSplitEdge(edge, node)) {
706             if (edge->getID().substr(edge->getID().find('_')) == "_" + toString(length) + "_" + node->getID()) {
707                 if ((int) edge->getNumLanes() > lane) {
708                     // ok, we know this already...
709                     return;
710                 }
711                 // increment by one
712                 edge->incLaneNo(1);
713                 return;
714             }
715         }
716         // nope, we have to split the edge...
717         //  maybe it is not the proper edge to split - VISUM seems not to sort the splits...
718         bool mustRecheck = true;
719         double seenLength = 0;
720         while (mustRecheck) {
721             if (isSplitEdge(edge, node)) {
722                 // ok, we have a previously created edge here
723                 std::string sub = edge->getID();
724                 sub = sub.substr(sub.rfind('_', sub.rfind('_') - 1));
725                 sub = sub.substr(1, sub.find('_', 1) - 1);
726                 double dist = StringUtils::toDouble(sub);
727                 if (dist < length) {
728                     seenLength += edge->getLength();
729                     if (dirS == "1") {
730                         // incoming -> move back
731                         edge = edge->getFromNode()->getIncomingEdges()[0];
732                     } else {
733                         // outgoing -> move forward
734                         edge = edge->getToNode()->getOutgoingEdges()[0];
735                     }
736                 } else {
737                     mustRecheck = false;
738                 }
739             } else {
740                 // we have the center edge - do not continue...
741                 mustRecheck = false;
742             }
743         }
744         // compute position
745         Position p;
746         double useLength = length - seenLength;
747         useLength = edge->getLength() - useLength;
748         if (useLength < 0 || useLength > edge->getLength()) {
749             WRITE_WARNING("Could not find split position for edge '" + edge->getID() + "'.");
750             return;
751         }
752         std::string edgeID = edge->getID();
753         p = edge->getGeometry().positionAtOffset(useLength);
754         if (isSplitEdge(edge, node)) {
755             edgeID = edgeID.substr(0, edgeID.find('_'));
756         }
757         NBNode* rn = new NBNode(edgeID + "_" +  toString((int) length) + "_" + node->getID(), p);
758         if (!myNetBuilder.getNodeCont().insert(rn)) {
759             throw ProcessError("Ups - could not insert node!");
760         }
761         std::string nid = edgeID + "_" +  toString((int) length) + "_" + node->getID();
762         myNetBuilder.getEdgeCont().splitAt(myNetBuilder.getDistrictCont(), edge, useLength, rn,
763                                            edge->getID(), nid, edge->getNumLanes() + 0, edge->getNumLanes() + 1);
764         NBEdge* nedge = myNetBuilder.getEdgeCont().retrieve(nid);
765         nedge = nedge->getToNode()->getOutgoingEdges()[0];
766         while (isSplitEdge(edge, node)) {
767             assert(nedge->getToNode()->getOutgoingEdges().size() > 0);
768             nedge->incLaneNo(1);
769             nedge = nedge->getToNode()->getOutgoingEdges()[0];
770         }
771     }
772 }
773 
774 
775 void
parse_TrafficLights()776 NIImporter_VISUM::parse_TrafficLights() {
777     myCurrentID = NBHelpers::normalIDRepresentation(myLineParser.get("Nr"));
778     SUMOTime cycleTime = (SUMOTime) getWeightedFloat2("Umlaufzeit", "UMLZEIT", "s");
779     SUMOTime intermediateTime = (SUMOTime) getWeightedFloat2("StdZwischenzeit", "STDZWZEIT", "s");
780     bool phaseBased = myLineParser.know("PhasenBasiert")
781                       ? StringUtils::toBool(myLineParser.get("PhasenBasiert"))
782                       : false;
783     SUMOTime offset = myLineParser.know("ZEITVERSATZ") ? TIME2STEPS(getWeightedFloat("ZEITVERSATZ", "s")) : 0;
784     // add to the list
785     myTLS[myCurrentID] = new NIVisumTL(myCurrentID, cycleTime, offset, intermediateTime, phaseBased);
786 }
787 
788 
789 void
parse_NodesToTrafficLights()790 NIImporter_VISUM::parse_NodesToTrafficLights() {
791     std::string node = myLineParser.get("KnotNr").c_str();
792     if (node == "0") {
793         // this is a dummy value which cannot be assigned to
794         return;
795     }
796     std::string trafficLight = myLineParser.get("LsaNr").c_str();
797     // add to the list
798     NBNode* n = myNetBuilder.getNodeCont().retrieve(node);
799     auto tlIt = myTLS.find(trafficLight);
800     if (n != nullptr && tlIt != myTLS.end()) {
801         tlIt->second->addNode(n);
802     } else {
803         WRITE_ERROR("Could not assign" + std::string(n == nullptr ? " missing" : "") + " node '" + node
804                     + "' to" + std::string(tlIt == myTLS.end() ? " missing" : "") + " traffic light '" + trafficLight + "'");
805     }
806 }
807 
808 
809 void
parse_SignalGroups()810 NIImporter_VISUM::parse_SignalGroups() {
811     myCurrentID = NBHelpers::normalIDRepresentation(myLineParser.get("Nr"));
812     std::string LSAid = NBHelpers::normalIDRepresentation(myLineParser.get("LsaNr"));
813     double startTime = getNamedFloat("GzStart", "GRUENANF");
814     double endTime = getNamedFloat("GzEnd", "GRUENENDE");
815     double yellowTime = myLineParser.know("GELB") ? getNamedFloat("GELB") : -1;
816     // add to the list
817     if (myTLS.find(LSAid) == myTLS.end()) {
818         WRITE_ERROR("Could not find TLS '" + LSAid + "' for setting the signal group.");
819         return;
820     }
821     myTLS.find(LSAid)->second->addSignalGroup(myCurrentID, (SUMOTime) startTime, (SUMOTime) endTime, (SUMOTime) yellowTime);
822 }
823 
824 
825 void
parse_TurnsToSignalGroups()826 NIImporter_VISUM::parse_TurnsToSignalGroups() {
827     // get the id
828     std::string SGid = getNamedString("SGNR", "SIGNALGRUPPENNR");
829     if (!myLineParser.know("LsaNr")) {
830         /// XXX could be retrieved from context
831         WRITE_WARNING("Ignoring SIGNALGRUPPEZUFSABBIEGER because LsaNr is not known");
832         return;
833     }
834     std::string LSAid = getNamedString("LsaNr");
835     // nodes
836     NBNode* from = myLineParser.know("VonKnot") ? getNamedNode("VonKnot") : nullptr;
837     NBNode* via = myLineParser.know("KNOTNR")
838                   ? getNamedNode("KNOTNR")
839                   : getNamedNode("UeberKnot", "UeberKnotNr");
840     NBNode* to = myLineParser.know("NachKnot") ? getNamedNode("NachKnot") : nullptr;
841     // edges
842     NBEdge* edg1 = nullptr;
843     NBEdge* edg2 = nullptr;
844     if (from == nullptr && to == nullptr) {
845         edg1 = getNamedEdgeContinuating("VONSTRNR", via);
846         edg2 = getNamedEdgeContinuating("NACHSTRNR", via);
847     } else {
848         edg1 = getEdge(from, via);
849         edg2 = getEdge(via, to);
850     }
851     // add to the list
852     NIVisumTL::SignalGroup& SG = myTLS.find(LSAid)->second->getSignalGroup(SGid);
853     if (edg1 != nullptr && edg2 != nullptr) {
854         if (!via->hasIncoming(edg1)) {
855             std::string sid;
856             if (edg1->getID()[0] == '-') {
857                 sid = edg1->getID().substr(1);
858             } else {
859                 sid = "-" + edg1->getID();
860             }
861             if (sid.find('_') != std::string::npos) {
862                 sid = sid.substr(0, sid.find('_'));
863             }
864             edg1 = getNamedEdgeContinuating(myNetBuilder.getEdgeCont().retrieve(sid),  via);
865         }
866         if (!via->hasOutgoing(edg2)) {
867             std::string sid;
868             if (edg2->getID()[0] == '-') {
869                 sid = edg2->getID().substr(1);
870             } else {
871                 sid = "-" + edg2->getID();
872             }
873             if (sid.find('_') != std::string::npos) {
874                 sid = sid.substr(0, sid.find('_'));
875             }
876             edg2 = getNamedEdgeContinuating(myNetBuilder.getEdgeCont().retrieve(sid),  via);
877         }
878         SG.connections().push_back(NBConnection(edg1, edg2));
879     }
880 }
881 
882 
883 void
parse_AreaSubPartElement()884 NIImporter_VISUM::parse_AreaSubPartElement() {
885     long long int id = StringUtils::toLong(myLineParser.get("TFLAECHEID"));
886     long long int edgeid = StringUtils::toLong(myLineParser.get("KANTEID"));
887     if (myEdges.find(edgeid) == myEdges.end()) {
888         WRITE_ERROR("Unknown edge in TEILFLAECHENELEMENT");
889         return;
890     }
891     std::string dir = myLineParser.get("RICHTUNG");
892 // get index (unused)
893 //     std::string indexS = NBHelpers::normalIDRepresentation(myLineParser.get("INDEX"));
894 //     int index = -1;
895 //     try {
896 //         index = StringUtils::toInt(indexS) - 1;
897 //     } catch (NumberFormatException&) {
898 //         WRITE_ERROR("An index for a TEILFLAECHENELEMENT is not numeric (id='" + toString(id) + "').");
899 //         return;
900 //     }
901     PositionVector shape;
902     shape.push_back(myPoints[myEdges[edgeid].first]);
903     shape.push_back(myPoints[myEdges[edgeid].second]);
904     if (dir.length() > 0 && dir[0] == '1') {
905         shape = shape.reverse();
906     }
907     if (mySubPartsAreas.find(id) == mySubPartsAreas.end()) {
908         WRITE_ERROR("Unkown are for area part '" + myCurrentID + "'.");
909         return;
910     }
911 
912     const std::vector<long long int>& areas = mySubPartsAreas.find(id)->second;
913     for (std::vector<long long int>::const_iterator i = areas.begin(); i != areas.end(); ++i) {
914         NBDistrict* d = myShapeDistrictMap[*i];
915         if (d == nullptr) {
916             continue;
917         }
918         if (myDistrictShapes.find(d) == myDistrictShapes.end()) {
919             myDistrictShapes[d] = PositionVector();
920         }
921         if (dir.length() > 0 && dir[0] == '1') {
922             myDistrictShapes[d].push_back(myPoints[myEdges[edgeid].second]);
923             myDistrictShapes[d].push_back(myPoints[myEdges[edgeid].first]);
924         } else {
925             myDistrictShapes[d].push_back(myPoints[myEdges[edgeid].first]);
926             myDistrictShapes[d].push_back(myPoints[myEdges[edgeid].second]);
927         }
928     }
929 }
930 
931 
932 void
parse_Phases()933 NIImporter_VISUM::parse_Phases() {
934     // get the id
935     std::string phaseid = NBHelpers::normalIDRepresentation(myLineParser.get("Nr"));
936     std::string LSAid = NBHelpers::normalIDRepresentation(myLineParser.get("LsaNr"));
937     double startTime = getNamedFloat("GzStart", "GRUENANF");
938     double endTime = getNamedFloat("GzEnd", "GRUENENDE");
939     double yellowTime = myLineParser.know("GELB") ? getNamedFloat("GELB") : -1;
940     myTLS.find(LSAid)->second->addPhase(phaseid, (SUMOTime) startTime, (SUMOTime) endTime, (SUMOTime) yellowTime);
941 }
942 
943 
parse_SignalGroupsToPhases()944 void NIImporter_VISUM::parse_SignalGroupsToPhases() {
945     // get the id
946     std::string Phaseid = NBHelpers::normalIDRepresentation(myLineParser.get("PsNr"));
947     std::string LSAid = NBHelpers::normalIDRepresentation(myLineParser.get("LsaNr"));
948     std::string SGid = NBHelpers::normalIDRepresentation(myLineParser.get("SGNR"));
949     // insert
950     NIVisumTL* LSA = myTLS.find(LSAid)->second;
951     NIVisumTL::SignalGroup& SG = LSA->getSignalGroup(SGid);
952     NIVisumTL::Phase* PH = LSA->getPhases().find(Phaseid)->second;
953     SG.phases()[Phaseid] = PH;
954 }
955 
956 
parse_LanesConnections()957 void NIImporter_VISUM::parse_LanesConnections() {
958     NBNode* node = nullptr;
959     NBEdge* fromEdge = nullptr;
960     NBEdge* toEdge = nullptr;
961     // get the node and edges depending on network format
962     const std::string nodeID = getNamedString("KNOTNR", "KNOT");
963     if (nodeID == "0") {
964         fromEdge = getNamedEdge("VONSTRNR", "VONSTR");
965         toEdge = getNamedEdge("NACHSTRNR", "NACHSTR");
966         if (fromEdge == nullptr) {
967             return;
968         }
969         node = fromEdge->getToNode();
970         WRITE_WARNING("Ignoring lane-to-lane connection (not yet implemented for this format version)");
971         return;
972     } else {
973         node = getNamedNode("KNOTNR", "KNOT");
974         if (node  == nullptr) {
975             return;
976         }
977         fromEdge = getNamedEdgeContinuating("VONSTRNR", "VONSTR", node);
978         toEdge = getNamedEdgeContinuating("NACHSTRNR", "NACHSTR", node);
979     }
980     if (fromEdge == nullptr || toEdge == nullptr) {
981         return;
982     }
983 
984     int fromLaneOffset = 0;
985     if (!node->hasIncoming(fromEdge)) {
986         fromLaneOffset = fromEdge->getNumLanes();
987         fromEdge = getReversedContinuating(fromEdge, node);
988     } else {
989         fromEdge = getReversedContinuating(fromEdge, node);
990         NBEdge* tmp = myNetBuilder.getEdgeCont().retrieve(fromEdge->getID().substr(0, fromEdge->getID().find('_')));
991         fromLaneOffset = tmp->getNumLanes();
992     }
993 
994     int toLaneOffset = 0;
995     if (!node->hasOutgoing(toEdge)) {
996         toLaneOffset = toEdge->getNumLanes();
997         toEdge = getReversedContinuating(toEdge, node);
998     } else {
999         NBEdge* tmp = myNetBuilder.getEdgeCont().retrieve(toEdge->getID().substr(0, toEdge->getID().find('_')));
1000         toLaneOffset = tmp->getNumLanes();
1001     }
1002     // get the from-lane
1003     std::string fromLaneS = NBHelpers::normalIDRepresentation(myLineParser.get("VONFSNR"));
1004     int fromLane = -1;
1005     try {
1006         fromLane = StringUtils::toInt(fromLaneS);
1007     } catch (NumberFormatException&) {
1008         WRITE_ERROR("A from-lane number for edge '" + fromEdge->getID() + "' is not numeric (" + fromLaneS + ").");
1009         return;
1010     }
1011     fromLane -= 1;
1012     if (fromLane < 0) {
1013         WRITE_ERROR("A from-lane number for edge '" + fromEdge->getID() + "' is not positive (" + fromLaneS + ").");
1014         return;
1015     }
1016     // get the from-lane
1017     std::string toLaneS = NBHelpers::normalIDRepresentation(myLineParser.get("NACHFSNR"));
1018     int toLane = -1;
1019     try {
1020         toLane = StringUtils::toInt(toLaneS);
1021     } catch (NumberFormatException&) {
1022         WRITE_ERROR("A to-lane number for edge '" + toEdge->getID() + "' is not numeric (" + toLaneS + ").");
1023         return;
1024     }
1025     toLane -= 1;
1026     if (toLane < 0) {
1027         WRITE_ERROR("A to-lane number for edge '" + toEdge->getID() + "' is not positive (" + toLaneS + ").");
1028         return;
1029     }
1030     // !!! the next is probably a hack
1031     if (fromLane - fromLaneOffset < 0) {
1032         //fromLaneOffset = 0;
1033     } else {
1034         fromLane = (int)fromEdge->getNumLanes() - (fromLane - fromLaneOffset) - 1;
1035     }
1036     if (toLane - toLaneOffset < 0) {
1037         //toLaneOffset = 0;
1038     } else {
1039         toLane = (int)toEdge->getNumLanes() - (toLane - toLaneOffset) - 1;
1040     }
1041     //
1042     if ((int) fromEdge->getNumLanes() <= fromLane) {
1043         WRITE_ERROR("A from-lane number for edge '" + fromEdge->getID() + "' is larger than the edge's lane number (" + fromLaneS + ").");
1044         return;
1045     }
1046     if ((int) toEdge->getNumLanes() <= toLane) {
1047         WRITE_ERROR("A to-lane number for edge '" + toEdge->getID() + "' is larger than the edge's lane number (" + toLaneS + ").");
1048         return;
1049     }
1050     //
1051     fromEdge->addLane2LaneConnection(fromLane, toEdge, toLane, NBEdge::L2L_VALIDATED);
1052 }
1053 
1054 
1055 
1056 
1057 
1058 
1059 
1060 
1061 
1062 
1063 
1064 
1065 
1066 double
getWeightedFloat(const std::string & name,const std::string & suffix)1067 NIImporter_VISUM::getWeightedFloat(const std::string& name, const std::string& suffix) {
1068     try {
1069         std::string val = myLineParser.get(name);
1070         if (val.find(suffix) != std::string::npos) {
1071             val = val.substr(0, val.find(suffix));
1072         }
1073         return StringUtils::toDouble(val);
1074     } catch (...) {}
1075     return -1;
1076 }
1077 
1078 
1079 double
getWeightedFloat2(const std::string & name,const std::string & name2,const std::string & suffix)1080 NIImporter_VISUM::getWeightedFloat2(const std::string& name, const std::string& name2, const std::string& suffix) {
1081     double result = getWeightedFloat(name, suffix);
1082     if (result != -1) {
1083         return result;
1084     } else {
1085         return getWeightedFloat(name2, suffix);
1086     }
1087 }
1088 
1089 bool
getWeightedBool(const std::string & name)1090 NIImporter_VISUM::getWeightedBool(const std::string& name) {
1091     try {
1092         return StringUtils::toBool(myLineParser.get(name));
1093     } catch (...) {}
1094     try {
1095         return StringUtils::toBool(myLineParser.get((name + "(IV)")));
1096     } catch (...) {}
1097     return false;
1098 }
1099 
1100 SVCPermissions
getPermissions(const std::string & name,bool warn,SVCPermissions unknown)1101 NIImporter_VISUM::getPermissions(const std::string& name, bool warn, SVCPermissions unknown) {
1102     SVCPermissions result = 0;
1103     for (std::string v : StringTokenizer(myLineParser.get(name), ",").getVector()) {
1104         // common values in english and german
1105         std::transform(v.begin(), v.end(), v.begin(), tolower);
1106         if (v == "bus") {
1107             result |= SVC_BUS;
1108         } else if (v == "walk" || v == "w" || v == "f") {
1109             result |= SVC_PEDESTRIAN;
1110         } else if (v == "l" || v == "lkw" || v == "h" || v == "hgv" || v == "lw" || v == "truck" || v == "tru") {
1111             result |= SVC_TRUCK;
1112         } else if (v == "b" || v == "bike") {
1113             result |= SVC_BICYCLE;
1114         } else if (v == "train") {
1115             result |= SVC_RAIL;
1116         } else if (v == "tram") {
1117             result |= SVC_TRAM;
1118         } else if (v == "p" || v == "pkw" || v == "car" || v == "c") {
1119             result |= SVC_PASSENGER;
1120         } else {
1121             if (warn) {
1122                 WRITE_WARNING("Encountered unknown vehicle category '" + v + "' in type '" + myLineParser.get("Nr") + "'");
1123             }
1124             result |= unknown;
1125         }
1126     }
1127     return result;
1128 }
1129 
1130 NBNode*
getNamedNode(const std::string & fieldName)1131 NIImporter_VISUM::getNamedNode(const std::string& fieldName) {
1132     std::string nodeS = NBHelpers::normalIDRepresentation(myLineParser.get(fieldName));
1133     NBNode* node = myNetBuilder.getNodeCont().retrieve(nodeS);
1134     if (node == nullptr) {
1135         WRITE_ERROR("The node '" + nodeS + "' is not known.");
1136     }
1137     return node;
1138 }
1139 
1140 NBNode*
getNamedNodeSecure(const std::string & fieldName,NBNode * fallback)1141 NIImporter_VISUM::getNamedNodeSecure(const std::string& fieldName, NBNode* fallback) {
1142     std::string nodeS = NBHelpers::normalIDRepresentation(myLineParser.get(fieldName));
1143     NBNode* node = myNetBuilder.getNodeCont().retrieve(nodeS);
1144     if (node == nullptr) {
1145         return fallback;
1146     }
1147     return node;
1148 }
1149 
1150 
1151 NBNode*
getNamedNode(const std::string & fieldName1,const std::string & fieldName2)1152 NIImporter_VISUM::getNamedNode(const std::string& fieldName1, const std::string& fieldName2) {
1153     if (myLineParser.know(fieldName1)) {
1154         return getNamedNode(fieldName1);
1155     } else {
1156         return getNamedNode(fieldName2);
1157     }
1158 }
1159 
1160 
1161 NBEdge*
getNamedEdge(const std::string & fieldName)1162 NIImporter_VISUM::getNamedEdge(const std::string& fieldName) {
1163     std::string edgeS = NBHelpers::normalIDRepresentation(myLineParser.get(fieldName));
1164     NBEdge* edge = myNetBuilder.getEdgeCont().retrieve(edgeS);
1165     if (edge == nullptr) {
1166         WRITE_ERROR("The edge '" + edgeS + "' is not known.");
1167     }
1168     return edge;
1169 }
1170 
1171 
1172 NBEdge*
getNamedEdge(const std::string & fieldName1,const std::string & fieldName2)1173 NIImporter_VISUM::getNamedEdge(const std::string& fieldName1, const std::string& fieldName2) {
1174     if (myLineParser.know(fieldName1)) {
1175         return getNamedEdge(fieldName1);
1176     } else {
1177         return getNamedEdge(fieldName2);
1178     }
1179 }
1180 
1181 
1182 
1183 NBEdge*
getReversedContinuating(NBEdge * edge,NBNode * node)1184 NIImporter_VISUM::getReversedContinuating(NBEdge* edge, NBNode* node) {
1185     std::string sid;
1186     if (edge->getID()[0] == '-') {
1187         sid = edge->getID().substr(1);
1188     } else {
1189         sid = "-" + edge->getID();
1190     }
1191     if (sid.find('_') != std::string::npos) {
1192         sid = sid.substr(0, sid.find('_'));
1193     }
1194     return getNamedEdgeContinuating(myNetBuilder.getEdgeCont().retrieve(sid),  node);
1195 }
1196 
1197 
1198 NBEdge*
getNamedEdgeContinuating(NBEdge * begin,NBNode * node)1199 NIImporter_VISUM::getNamedEdgeContinuating(NBEdge* begin, NBNode* node) {
1200     if (begin == nullptr) {
1201         return nullptr;
1202     }
1203     NBEdge* ret = begin;
1204     std::string edgeID = ret->getID();
1205     // hangle forward
1206     while (ret != nullptr) {
1207         // ok, this is the edge we are looking for
1208         if (ret->getToNode() == node) {
1209             return ret;
1210         }
1211         const EdgeVector& nedges = ret->getToNode()->getOutgoingEdges();
1212         if (nedges.size() != 1) {
1213             // too many edges follow
1214             ret = nullptr;
1215             continue;
1216         }
1217         NBEdge* next = nedges[0];
1218         if (ret->getID().substr(0, edgeID.length()) != next->getID().substr(0, edgeID.length())) {
1219             // ok, another edge is next...
1220             ret = nullptr;
1221             continue;
1222         }
1223         if (next->getID().substr(next->getID().length() - node->getID().length()) != node->getID()) {
1224             ret = nullptr;
1225             continue;
1226         }
1227         ret = next;
1228     }
1229 
1230     ret = begin;
1231     // hangle backward
1232     while (ret != nullptr) {
1233         // ok, this is the edge we are looking for
1234         if (ret->getFromNode() == node) {
1235             return ret;
1236         }
1237         const EdgeVector& nedges = ret->getFromNode()->getIncomingEdges();
1238         if (nedges.size() != 1) {
1239             // too many edges follow
1240             ret = nullptr;
1241             continue;
1242         }
1243         NBEdge* next = nedges[0];
1244         if (ret->getID().substr(0, edgeID.length()) != next->getID().substr(0, edgeID.length())) {
1245             // ok, another edge is next...
1246             ret = nullptr;
1247             continue;
1248         }
1249         if (next->getID().substr(next->getID().length() - node->getID().length()) != node->getID()) {
1250             ret = nullptr;
1251             continue;
1252         }
1253         ret = next;
1254     }
1255     return nullptr;
1256 }
1257 
1258 
1259 NBEdge*
getNamedEdgeContinuating(const std::string & fieldName,NBNode * node)1260 NIImporter_VISUM::getNamedEdgeContinuating(const std::string& fieldName, NBNode* node) {
1261     std::string edgeS = NBHelpers::normalIDRepresentation(myLineParser.get(fieldName));
1262     NBEdge* edge = myNetBuilder.getEdgeCont().retrieve(edgeS);
1263     if (edge == nullptr) {
1264         WRITE_ERROR("The edge '" + edgeS + "' is not known.");
1265     }
1266     return getNamedEdgeContinuating(edge, node);
1267 }
1268 
1269 
1270 NBEdge*
getNamedEdgeContinuating(const std::string & fieldName1,const std::string & fieldName2,NBNode * node)1271 NIImporter_VISUM::getNamedEdgeContinuating(const std::string& fieldName1, const std::string& fieldName2,
1272         NBNode* node) {
1273     if (myLineParser.know(fieldName1)) {
1274         return getNamedEdgeContinuating(fieldName1, node);
1275     } else {
1276         return getNamedEdgeContinuating(fieldName2, node);
1277     }
1278 }
1279 
1280 
1281 NBEdge*
getEdge(NBNode * FromNode,NBNode * ToNode)1282 NIImporter_VISUM::getEdge(NBNode* FromNode, NBNode* ToNode) {
1283     EdgeVector::const_iterator i;
1284     for (i = FromNode->getOutgoingEdges().begin(); i != FromNode->getOutgoingEdges().end(); i++) {
1285         if (ToNode == (*i)->getToNode()) {
1286             return (*i);
1287         }
1288     }
1289     //!!!
1290     return nullptr;
1291 }
1292 
1293 
1294 double
getNamedFloat(const std::string & fieldName)1295 NIImporter_VISUM::getNamedFloat(const std::string& fieldName) {
1296     std::string value = myLineParser.get(fieldName);
1297     if (StringUtils::endsWith(myLineParser.get(fieldName), "km/h")) {
1298         value = value.substr(0, value.length() - 4);
1299     }
1300     return StringUtils::toDouble(value);
1301 }
1302 
1303 
1304 double
getNamedFloat(const std::string & fieldName,double defaultValue)1305 NIImporter_VISUM::getNamedFloat(const std::string& fieldName, double defaultValue) {
1306     try {
1307         return StringUtils::toDouble(myLineParser.get(fieldName));
1308     } catch (...) {
1309         return defaultValue;
1310     }
1311 }
1312 
1313 
1314 double
getNamedFloat(const std::string & fieldName1,const std::string & fieldName2)1315 NIImporter_VISUM::getNamedFloat(const std::string& fieldName1, const std::string& fieldName2) {
1316     if (myLineParser.know(fieldName1)) {
1317         return getNamedFloat(fieldName1);
1318     } else {
1319         return getNamedFloat(fieldName2);
1320     }
1321 }
1322 
1323 
1324 double
getNamedFloat(const std::string & fieldName1,const std::string & fieldName2,double defaultValue)1325 NIImporter_VISUM::getNamedFloat(const std::string& fieldName1, const std::string& fieldName2,
1326                                 double defaultValue) {
1327     if (myLineParser.know(fieldName1)) {
1328         return getNamedFloat(fieldName1, defaultValue);
1329     } else {
1330         return getNamedFloat(fieldName2, defaultValue);
1331     }
1332 }
1333 
1334 
1335 std::string
getNamedString(const std::string & fieldName)1336 NIImporter_VISUM::getNamedString(const std::string& fieldName) {
1337     return NBHelpers::normalIDRepresentation(myLineParser.get(fieldName));
1338 }
1339 
1340 
1341 std::string
getNamedString(const std::string & fieldName1,const std::string & fieldName2)1342 NIImporter_VISUM::getNamedString(const std::string& fieldName1,
1343                                  const std::string& fieldName2) {
1344     if (myLineParser.know(fieldName1)) {
1345         return getNamedString(fieldName1);
1346     } else {
1347         return getNamedString(fieldName2);
1348     }
1349 }
1350 
1351 
1352 
1353 
1354 
1355 
1356 NBNode*
buildDistrictNode(const std::string & id,NBNode * dest,bool isSource)1357 NIImporter_VISUM::buildDistrictNode(const std::string& id, NBNode* dest,
1358                                     bool isSource) {
1359     // get the district
1360     NBDistrict* dist = myNetBuilder.getDistrictCont().retrieve(id);
1361     if (dist == nullptr) {
1362         return nullptr;
1363     }
1364     // build the id
1365     std::string nid;
1366     nid = id + "-" + dest->getID();
1367     if (!isSource) {
1368         nid = "-" + nid;
1369     }
1370     // insert the node
1371     if (!myNetBuilder.getNodeCont().insert(nid, dist->getPosition())) {
1372         WRITE_ERROR("Could not build connector node '" + nid + "'.");
1373     }
1374     // return the node
1375     return myNetBuilder.getNodeCont().retrieve(nid);
1376 }
1377 
1378 
1379 bool
checkNodes(NBNode * from,NBNode * to)1380 NIImporter_VISUM::checkNodes(NBNode* from, NBNode* to)  {
1381     if (from == nullptr) {
1382         WRITE_ERROR(" The from-node was not found within the net");
1383     }
1384     if (to == nullptr) {
1385         WRITE_ERROR(" The to-node was not found within the net");
1386     }
1387     if (from == to) {
1388         WRITE_ERROR(" Both nodes are the same");
1389     }
1390     return from != nullptr && to != nullptr && from != to;
1391 }
1392 
1393 bool
isSplitEdge(NBEdge * edge,NBNode * node)1394 NIImporter_VISUM::isSplitEdge(NBEdge* edge, NBNode* node) {
1395     return (edge->getID().length() > node->getID().length() + 1
1396             && (edge->getID().substr(edge->getID().length() - node->getID().length() - 1) == "_" + node->getID()));
1397 }
1398 
1399 /****************************************************************************/
1400 
1401