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_DlrNavteq.cpp
11 /// @author  Daniel Krajzewicz
12 /// @author  Jakob Erdmann
13 /// @author  Michael Behrisch
14 /// @date    Mon, 14.04.2008
15 /// @version $Id$
16 ///
17 // Importer for networks stored in Elmar's format
18 /****************************************************************************/
19 
20 
21 // ===========================================================================
22 // included modules
23 // ===========================================================================
24 #include <config.h>
25 
26 #include <string>
27 #include <sstream>
28 #include <limits>
29 #include <utils/importio/LineHandler.h>
30 #include <utils/common/StringTokenizer.h>
31 #include <utils/common/MsgHandler.h>
32 #include <utils/common/UtilExceptions.h>
33 #include <utils/common/StringUtils.h>
34 #include <utils/common/ToString.h>
35 #include <utils/common/StringUtils.h>
36 #include <utils/options/OptionsCont.h>
37 #include <utils/importio/LineReader.h>
38 #include <utils/geom/GeoConvHelper.h>
39 #include <netbuild/NBNetBuilder.h>
40 #include <netbuild/NBNode.h>
41 #include <netbuild/NBNodeCont.h>
42 #include <netbuild/NBEdge.h>
43 #include <netbuild/NBEdgeCont.h>
44 #include <netbuild/NBTypeCont.h>
45 #include <netbuild/NBOwnTLDef.h>
46 #include <netimport/NINavTeqHelper.h>
47 #include "NILoader.h"
48 #include "NIImporter_DlrNavteq.h"
49 
50 
51 // ---------------------------------------------------------------------------
52 // static members
53 // ---------------------------------------------------------------------------
54 const std::string NIImporter_DlrNavteq::GEO_SCALE("1e-5");
55 const int NIImporter_DlrNavteq::EdgesHandler::MISSING_COLUMN = std::numeric_limits<int>::max();
56 const std::string NIImporter_DlrNavteq::UNDEFINED("-1");
57 
58 // ===========================================================================
59 // method definitions
60 // ===========================================================================
61 // ---------------------------------------------------------------------------
62 // static methods
63 // ---------------------------------------------------------------------------
64 void
loadNetwork(const OptionsCont & oc,NBNetBuilder & nb)65 NIImporter_DlrNavteq::loadNetwork(const OptionsCont& oc, NBNetBuilder& nb) {
66     // check whether the option is set (properly)
67     if (!oc.isSet("dlr-navteq-prefix")) {
68         return;
69     }
70     time_t csTime;
71     time(&csTime);
72     // parse file(s)
73     LineReader lr;
74     // load nodes
75     std::map<std::string, PositionVector> myGeoms;
76     PROGRESS_BEGIN_MESSAGE("Loading nodes");
77     std::string file = oc.getString("dlr-navteq-prefix") + "_nodes_unsplitted.txt";
78     NodesHandler handler1(nb.getNodeCont(), file, myGeoms);
79     if (!lr.setFile(file)) {
80         throw ProcessError("The file '" + file + "' could not be opened.");
81     }
82     lr.readAll(handler1);
83     PROGRESS_DONE_MESSAGE();
84 
85     // load street names if given and wished
86     std::map<std::string, std::string> streetNames; // nameID : name
87     if (oc.getBool("output.street-names")) {
88         file = oc.getString("dlr-navteq-prefix") + "_names.txt";
89         if (lr.setFile(file)) {
90             PROGRESS_BEGIN_MESSAGE("Loading Street Names");
91             NamesHandler handler4(file, streetNames);
92             lr.readAll(handler4);
93             PROGRESS_DONE_MESSAGE();
94         } else {
95             WRITE_WARNING("Output will not contain street names because the file '" + file + "' was not found");
96         }
97     }
98 
99     // load edges
100     PROGRESS_BEGIN_MESSAGE("Loading edges");
101     file = oc.getString("dlr-navteq-prefix") + "_links_unsplitted.txt";
102     // parse the file
103     EdgesHandler handler2(nb.getNodeCont(), nb.getEdgeCont(), nb.getTypeCont(), file, myGeoms, streetNames);
104     if (!lr.setFile(file)) {
105         throw ProcessError("The file '" + file + "' could not be opened.");
106     }
107     lr.readAll(handler2);
108     nb.getEdgeCont().recheckLaneSpread();
109     PROGRESS_DONE_MESSAGE();
110 
111     // load traffic lights if given
112     file = oc.getString("dlr-navteq-prefix") + "_traffic_signals.txt";
113     if (lr.setFile(file)) {
114         PROGRESS_BEGIN_MESSAGE("Loading traffic lights");
115         TrafficlightsHandler handler3(nb.getNodeCont(), nb.getTLLogicCont(), nb.getEdgeCont(), file);
116         lr.readAll(handler3);
117         PROGRESS_DONE_MESSAGE();
118     }
119 
120     // load prohibited manoeuvres if given
121     file = oc.getString("dlr-navteq-prefix") + "_prohibited_manoeuvres.txt";
122     if (lr.setFile(file)) {
123         PROGRESS_BEGIN_MESSAGE("Loading prohibited manoeuvres");
124         ProhibitionHandler handler6(nb.getEdgeCont(), file, csTime);
125         lr.readAll(handler6);
126         PROGRESS_DONE_MESSAGE();
127     }
128 
129     // load connected lanes if given
130     file = oc.getString("dlr-navteq-prefix") + "_connected_lanes.txt";
131     if (lr.setFile(file)) {
132         PROGRESS_BEGIN_MESSAGE("Loading connected lanes");
133         ConnectedLanesHandler handler7(nb.getEdgeCont());
134         lr.readAll(handler7);
135         PROGRESS_DONE_MESSAGE();
136     }
137 
138     // load time restrictions if given
139     file = oc.getString("dlr-navteq-prefix") + "_links_timerestrictions.txt";
140     if (lr.setFile(file)) {
141         PROGRESS_BEGIN_MESSAGE("Loading time restrictions");
142         if (!oc.isDefault("construction-date")) {
143             csTime = readDate(oc.getString("construction-date"));
144         }
145         TimeRestrictionsHandler handler5(nb.getEdgeCont(), nb.getDistrictCont(), csTime);
146         lr.readAll(handler5);
147         handler5.printSummary();
148         PROGRESS_DONE_MESSAGE();
149     }
150 }
151 
152 double
readVersion(const std::string & line,const std::string & file)153 NIImporter_DlrNavteq::readVersion(const std::string& line, const std::string& file) {
154     assert(line[0] == '#');
155     const std::string marker = "extraction version: v";
156     const std::string lowerCase = StringUtils::to_lower_case(line);
157     if (lowerCase.find(marker) == std::string::npos) {
158         return -1;
159     }
160     const int vStart = (int)(lowerCase.find(marker) + marker.size());
161     const int vEnd = (int)line.find(" ", vStart);
162     try {
163         const double version = StringUtils::toDouble(line.substr(vStart, vEnd - vStart));
164         if (version < 0) {
165             throw ProcessError("Invalid version number '" + toString(version) + "' in file '" + file + "'.");
166         }
167         return version;
168     } catch (NumberFormatException&) {
169         throw ProcessError("Non-numerical value '" + line.substr(vStart, vEnd - vStart) + "' for version string in file '" + file + "'.");
170     }
171 }
172 
173 
174 // ---------------------------------------------------------------------------
175 // definitions of NIImporter_DlrNavteq::NodesHandler-methods
176 // ---------------------------------------------------------------------------
NodesHandler(NBNodeCont & nc,const std::string & file,std::map<std::string,PositionVector> & geoms)177 NIImporter_DlrNavteq::NodesHandler::NodesHandler(NBNodeCont& nc,
178         const std::string& file,
179         std::map<std::string, PositionVector>& geoms)
180     : myNodeCont(nc), myGeoms(geoms) {
181     UNUSED_PARAMETER(file);
182 }
183 
184 
~NodesHandler()185 NIImporter_DlrNavteq::NodesHandler::~NodesHandler() {}
186 
187 
188 bool
report(const std::string & result)189 NIImporter_DlrNavteq::NodesHandler::report(const std::string& result) {
190     if (result[0] == '#') {
191         return true;
192     }
193     std::string id;
194     double x, y;
195     int no_geoms, intermediate;
196     // parse
197     std::istringstream stream(result);
198     // id
199     stream >> id;
200     if (stream.fail()) {
201         throw ProcessError("Something is wrong with the following data line\n" + result);
202     }
203     // intermediate?
204     stream >> intermediate;
205     if (stream.fail()) {
206         if (myNodeCont.size() == 0) { // be generous with extra data at beginning of file
207             return true;
208         }
209         throw ProcessError("Non-numerical value for intermediate status in node " + id + ".");
210     }
211     // number of geometrical information
212     stream >> no_geoms;
213     if (stream.fail()) {
214         throw ProcessError("Non-numerical value for number of geometries in node " + id + ".");
215     }
216     // geometrical information
217     PositionVector geoms;
218     for (int i = 0; i < no_geoms; i++) {
219         stream >> x;
220         if (stream.fail()) {
221             throw ProcessError("Non-numerical value for x-position in node " + id + ".");
222         }
223         stream >> y;
224         if (stream.fail()) {
225             throw ProcessError("Non-numerical value for y-position in node " + id + ".");
226         }
227         Position pos(x, y);
228         if (!NBNetBuilder::transformCoordinate(pos, true)) {
229             throw ProcessError("Unable to project coordinates for node " + id + ".");
230         }
231         geoms.push_back(pos);
232     }
233 
234     if (intermediate == 0) {
235         NBNode* n = new NBNode(id, geoms[0]);
236         if (!myNodeCont.insert(n)) {
237             delete n;
238             throw ProcessError("Could not add node '" + id + "'.");
239         }
240     } else {
241         myGeoms[id] = geoms;
242     }
243     return true;
244 }
245 
246 
247 // ---------------------------------------------------------------------------
248 // definitions of NIImporter_DlrNavteq::EdgesHandler-methods
249 // ---------------------------------------------------------------------------
EdgesHandler(NBNodeCont & nc,NBEdgeCont & ec,NBTypeCont & tc,const std::string & file,std::map<std::string,PositionVector> & geoms,std::map<std::string,std::string> & streetNames)250 NIImporter_DlrNavteq::EdgesHandler::EdgesHandler(NBNodeCont& nc, NBEdgeCont& ec,
251         NBTypeCont& tc, const std::string& file,
252         std::map<std::string, PositionVector>& geoms,
253         std::map<std::string, std::string>& streetNames):
254     myNodeCont(nc),
255     myEdgeCont(ec),
256     myTypeCont(tc),
257     myGeoms(geoms),
258     myStreetNames(streetNames),
259     myVersion(0),
260     myFile(file) {
261 }
262 
263 
~EdgesHandler()264 NIImporter_DlrNavteq::EdgesHandler::~EdgesHandler() {}
265 
266 
267 bool
report(const std::string & result)268 NIImporter_DlrNavteq::EdgesHandler::report(const std::string& result) {
269     // parse version number from first comment line and initialize column definitions
270     if (result[0] == '#') {
271         if (!myColumns.empty()) {
272             return true;
273         }
274         const double version = readVersion(result, myFile);
275         if (version > 0) {
276             myVersion = version;
277             // init columns
278             const int NUM_COLUMNS = 25; // @note arrays must match this size!
279             const int MC = MISSING_COLUMN;
280             if (myVersion < 3) {
281                 const int columns[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, MC, 12, 13, 14, 15, 16, 17, 18, 19, 20, MC, MC, -21};
282                 myColumns = std::vector<int>(columns, columns + NUM_COLUMNS);
283             } else if (myVersion < 6) {
284                 const int columns[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, MC, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, -23};
285                 myColumns = std::vector<int>(columns, columns + NUM_COLUMNS);
286             } else {
287                 const int columns[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24};
288                 myColumns = std::vector<int>(columns, columns + NUM_COLUMNS);
289             }
290         }
291         return true;
292     }
293     if (myColumns.empty()) {
294         throw ProcessError("Missing version string in file '" + myFile + "'.");
295     }
296     // interpret link attributes
297     StringTokenizer st(result, StringTokenizer::WHITECHARS);
298     const std::string id = getColumn(st, LINK_ID);
299     // form of way (for priority and permissions)
300     int form_of_way;
301     try {
302         form_of_way = StringUtils::toInt(getColumn(st, FORM_OF_WAY));
303     } catch (NumberFormatException&) {
304         throw ProcessError("Non-numerical value for form_of_way of link '" + id + "'.");
305     }
306     // brunnel type (bridge/tunnel/ferry (for permissions)
307     int brunnel_type;
308     try {
309         brunnel_type = StringUtils::toInt(getColumn(st, BRUNNEL_TYPE));
310     } catch (NumberFormatException&) {
311         throw ProcessError("Non-numerical value for brunnel_type of link '" + id + "'.");
312     }
313     // priority based on street_type / frc
314     int priority;
315     try {
316         priority = -StringUtils::toInt(getColumn(st, FUNCTIONAL_ROAD_CLASS));
317         // lower priority using form_of_way
318         if (form_of_way == 11) {
319             priority -= 1; // frontage road, very often with lowered curb
320         } else if (form_of_way > 11) {
321             priority -= 2; // parking/service access assume lowered curb
322         }
323     } catch (NumberFormatException&) {
324         throw ProcessError("Non-numerical value for street_type of link '" + id + "').");
325     }
326     // street name
327     std::string streetName = getStreetNameFromIDs(
328                                  getColumn(st, NAME_ID1_REGIONAL),
329                                  getColumn(st, NAME_ID2_LOCAL));
330     // try to get the nodes
331     const std::string fromID = getColumn(st, NODE_ID_FROM);
332     const std::string toID = getColumn(st, NODE_ID_TO);
333     NBNode* from = myNodeCont.retrieve(fromID);
334     NBNode* to = myNodeCont.retrieve(toID);
335     if (from == nullptr) {
336         throw ProcessError("The from-node '" + fromID + "' of link '" + id + "' could not be found");
337     }
338     if (to == nullptr) {
339         throw ProcessError("The to-node '" + toID + "' of link '" + id + "' could not be found");
340     }
341     // speed
342     double speed;
343     try {
344         speed = StringUtils::toInt(getColumn(st, SPEED_RESTRICTION, "-1")) / 3.6;
345     } catch (NumberFormatException&) {
346         throw ProcessError("Non-numerical value for the SPEED_RESTRICTION of link '" + id + "'.");
347     }
348     if (speed < 0) {
349         // speed category as fallback
350         speed = NINavTeqHelper::getSpeed(id, getColumn(st, SPEED_CATEGORY));
351     }
352     // number of lanes
353     int numLanes;
354     try {
355         // EXTENDED_NUMBER_OF_LANES is prefered but may not be defined
356         numLanes = StringUtils::toInt(getColumn(st, EXTENDED_NUMBER_OF_LANES, "-1"));
357         if (numLanes == -1) {
358             numLanes = NINavTeqHelper::getLaneNumber(id, getColumn(st, NUMBER_OF_LANES), speed);
359         }
360     } catch (NumberFormatException&) {
361         throw ProcessError("Non-numerical value for the number of lanes of link '" + id + "'.");
362     }
363 
364     const std::string navTeqTypeId = getColumn(st, VEHICLE_TYPE) + "_" + getColumn(st, FORM_OF_WAY);
365     // build the edge
366     NBEdge* e = nullptr;
367     const std::string interID = getColumn(st, BETWEEN_NODE_ID);
368     if (interID == "-1") {
369         e = new NBEdge(id, from, to, myTypeCont.knows(navTeqTypeId) ? navTeqTypeId : "", speed, numLanes, priority,
370                        NBEdge::UNSPECIFIED_WIDTH, NBEdge::UNSPECIFIED_OFFSET, streetName);
371     } else {
372         PositionVector geoms = myGeoms[interID];
373         if (getColumn(st, CONNECTION, "0") == "1") {
374             geoms = geoms.reverse();
375         }
376         geoms.insert(geoms.begin(), from->getPosition());
377         geoms.push_back(to->getPosition());
378         const std::string origID = OptionsCont::getOptions().getBool("output.original-names") ? id : "";
379         e = new NBEdge(id, from, to, myTypeCont.knows(navTeqTypeId) ? navTeqTypeId : "", speed, numLanes, priority,
380                        NBEdge::UNSPECIFIED_WIDTH, NBEdge::UNSPECIFIED_OFFSET, geoms, streetName, origID, LANESPREAD_CENTER);
381     }
382 
383     // NavTeq imports can be done with a typemap (if supplied), if not, the old defaults are used
384     if (myTypeCont.knows(navTeqTypeId)) {
385         e->setPermissions(myTypeCont.getPermissions(navTeqTypeId));
386     } else {
387         // add vehicle type information to the edge
388         if (myVersion < 6.0) {
389             NINavTeqHelper::addVehicleClasses(*e, getColumn(st, VEHICLE_TYPE));
390         } else {
391             NINavTeqHelper::addVehicleClassesV6(*e, getColumn(st, VEHICLE_TYPE));
392         }
393         if (e->getPermissions() == SVCAll) {
394             e->setPermissions(myTypeCont.getPermissions(""));
395         }
396         // permission modifications based on form_of_way
397         if (form_of_way == 14) { // pedestrian area (fussgaengerzone)
398             // unfortunately, the veh_type string is misleading in this case
399             e->disallowVehicleClass(-1, SVC_PASSENGER);
400         }
401         // permission modifications based on brunnel_type
402         if (brunnel_type == 10) { // ferry
403             e->setPermissions(SVC_SHIP, -1);
404         }
405     }
406 
407     // insert the edge to the network
408     if (!myEdgeCont.insert(e)) {
409         delete e;
410         throw ProcessError("Could not add edge '" + id + "'.");
411     }
412     return true;
413 }
414 
415 
416 std::string
getColumn(const StringTokenizer & st,ColumnName name,const std::string fallback)417 NIImporter_DlrNavteq::EdgesHandler::getColumn(const StringTokenizer& st, ColumnName name, const std::string fallback) {
418     assert(!myColumns.empty());
419     if (myColumns[name] == MISSING_COLUMN) {
420         if (fallback == "") {
421             throw ProcessError("Missing column " + toString(name) + ".");
422         } else {
423             return fallback;
424         }
425     } else if (myColumns[name] >= 0) {
426         return st.get((int)(myColumns[name]));
427     } else {
428         // negative column number implies an optional column
429         if ((int) st.size() <= -myColumns[name]) {
430             // the column is not present
431             if (fallback == "") {
432                 throw ProcessError("Missing optional column " + toString(name) + " without default value.");
433             } else {
434                 return fallback;
435             }
436         } else {
437             return st.get((int)(-myColumns[name]));
438         }
439     }
440 }
441 
442 
443 std::string
getStreetNameFromIDs(const std::string & regionalID,const std::string & localID) const444 NIImporter_DlrNavteq::EdgesHandler::getStreetNameFromIDs(
445     const std::string& regionalID, const std::string& localID) const {
446     std::string result = "";
447     bool hadRegional = false;
448     if (myStreetNames.count(regionalID) > 0) {
449         hadRegional = true;
450         result += myStreetNames[regionalID];
451     }
452     if (myStreetNames.count(localID) > 0) {
453         if (hadRegional) {
454             result += " / ";
455         }
456         result += myStreetNames[localID];
457     }
458     return result;
459 }
460 
461 // ---------------------------------------------------------------------------
462 // definitions of NIImporter_DlrNavteq::TrafficlightsHandler-methods
463 // ---------------------------------------------------------------------------
TrafficlightsHandler(NBNodeCont & nc,NBTrafficLightLogicCont & tlc,NBEdgeCont & ne,const std::string & file)464 NIImporter_DlrNavteq::TrafficlightsHandler::TrafficlightsHandler(NBNodeCont& nc,
465         NBTrafficLightLogicCont& tlc,
466         NBEdgeCont& ne,
467         const std::string& file) :
468     myNodeCont(nc),
469     myTLLogicCont(tlc),
470     myEdgeCont(ne) {
471     UNUSED_PARAMETER(file);
472 }
473 
474 
~TrafficlightsHandler()475 NIImporter_DlrNavteq::TrafficlightsHandler::~TrafficlightsHandler() {}
476 
477 
478 bool
report(const std::string & result)479 NIImporter_DlrNavteq::TrafficlightsHandler::report(const std::string& result) {
480 // #ID     POICOL-TYPE     DESCRIPTION     LONGITUDE       LATITUDE        NAVTEQ_LINK_ID  NODEID
481 
482     if (result[0] == '#') {
483         return true;
484     }
485     StringTokenizer st(result, StringTokenizer::WHITECHARS);
486     const std::string edgeID = st.get(5);
487     NBEdge* edge = myEdgeCont.retrieve(edgeID);
488     if (edge == nullptr) {
489         WRITE_WARNING("The traffic light edge '" + edgeID + "' could not be found");
490     } else {
491         NBNode* node = edge->getToNode();
492         if (node->getType() != NODETYPE_TRAFFIC_LIGHT) {
493             node->reinit(node->getPosition(), NODETYPE_TRAFFIC_LIGHT);
494             // @note. There may be additional information somewhere in the GDF files about traffic light type ...
495             TrafficLightType type = SUMOXMLDefinitions::TrafficLightTypes.get(OptionsCont::getOptions().getString("tls.default-type"));
496             // @note actually we could use the navteq node ID here
497             NBTrafficLightDefinition* tlDef = new NBOwnTLDef(node->getID(), node, 0, type);
498             if (!myTLLogicCont.insert(tlDef)) {
499                 // actually, nothing should fail here
500                 delete tlDef;
501                 throw ProcessError("Could not allocate tls for '" + node->getID() + "'.");
502             }
503         }
504     }
505     return true;
506 }
507 
508 
509 // ---------------------------------------------------------------------------
510 // definitions of NIImporter_DlrNavteq::NamesHandler-methods
511 // ---------------------------------------------------------------------------
NamesHandler(const std::string & file,std::map<std::string,std::string> & streetNames)512 NIImporter_DlrNavteq::NamesHandler::NamesHandler(
513     const std::string& file, std::map<std::string, std::string>& streetNames) :
514     myStreetNames(streetNames) {
515     UNUSED_PARAMETER(file);
516 }
517 
518 
~NamesHandler()519 NIImporter_DlrNavteq::NamesHandler::~NamesHandler() {}
520 
521 
522 bool
report(const std::string & result)523 NIImporter_DlrNavteq::NamesHandler::report(const std::string& result) {
524 // # NAME_ID    Name
525     if (result[0] == '#') {
526         return true;
527     }
528     StringTokenizer st(result, StringTokenizer::TAB);
529     if (st.size() == 1) {
530         return true; // one line with the number of data containing lines in it (also starts with a comment # since ersion 6.5)
531     }
532     assert(st.size() >= 2);
533     const std::string id = st.next();
534     if (st.size() > 2) {
535         const std::string permanent_id_info = st.next();
536     }
537     myStreetNames[id] = st.next();
538     return true;
539 }
540 
541 
542 // ---------------------------------------------------------------------------
543 // definitions of NIImporter_DlrNavteq::TimeRestrictionsHandler-methods
544 // ---------------------------------------------------------------------------
TimeRestrictionsHandler(NBEdgeCont & ec,NBDistrictCont & dc,time_t constructionTime)545 NIImporter_DlrNavteq::TimeRestrictionsHandler::TimeRestrictionsHandler(NBEdgeCont& ec, NBDistrictCont& dc, time_t constructionTime):
546     myEdgeCont(ec),
547     myDistrictCont(dc),
548     myConstructionTime(constructionTime),
549     myCS_min(std::numeric_limits<time_t>::max()),
550     myCS_max(std::numeric_limits<time_t>::min()),
551     myConstructionEntries(0),
552     myNotStarted(0),
553     myUnderConstruction(0),
554     myFinished(0),
555     myRemovedEdges(0) {
556 }
557 
558 
~TimeRestrictionsHandler()559 NIImporter_DlrNavteq::TimeRestrictionsHandler::~TimeRestrictionsHandler() {}
560 
561 
562 bool
report(const std::string & result)563 NIImporter_DlrNavteq::TimeRestrictionsHandler::report(const std::string& result) {
564 // # NAME_ID    Name
565     if (result[0] == '#') {
566         return true;
567     }
568     StringTokenizer st(result, StringTokenizer::WHITECHARS);
569     const std::string id = st.next();
570     const std::string type = st.next();
571     const std::string directionOfFlow = st.next(); // can be ignored since unidirectional edge ids are referenced in the file
572     const std::string throughTraffic = st.next();
573     const std::string vehicleType = st.next();
574     const std::string validityPeriod = st.next();
575     const std::string warning = "Unrecognized TIME_REC '" + validityPeriod + "'";
576     if (type == "CS") {
577         myConstructionEntries++;
578         if (validityPeriod.size() > 1024) {
579             WRITE_WARNING(warning);
580         }
581         // construction
582         char start[1024];
583         char duration[1024];
584 
585         int matched;
586 
587         matched = sscanf(validityPeriod.c_str(), "[(%[^)]){%[^}]}]", start, duration);
588         if (matched == 2) {
589             time_t tStart = readTimeRec(start, "");
590             time_t tEnd = readTimeRec(start, duration);
591             myCS_min = MIN2(myCS_min, tStart);
592             myCS_max = MAX2(myCS_max, tEnd);
593             //std::cout << " start=" << start << " tStart=" << tStart<< " translation=" << asctime(localtime(&tStart)) << "";
594             //std::cout << " duration=" << duration << " tEnd=" << tEnd << " translation=" << asctime(localtime(&tEnd)) << "\n";
595             if (myConstructionTime < tEnd) {
596                 NBEdge* edge = myEdgeCont.retrieve(id);
597                 if (edge != nullptr) {
598                     myRemovedEdges++;
599                     myEdgeCont.extract(myDistrictCont, edge, true);
600                 }
601                 if (myConstructionTime < tStart) {
602                     myNotStarted++;
603                 } else {
604                     myUnderConstruction++;
605                 }
606             } else {
607                 myFinished++;
608             }
609         } else {
610             WRITE_WARNING(warning);
611         };
612     }
613     return true;
614 }
615 
616 
617 void
printSummary()618 NIImporter_DlrNavteq::TimeRestrictionsHandler::printSummary() {
619     if (myConstructionEntries > 0) {
620         char buff[1024];
621         std::ostringstream msg;
622         strftime(buff, 1024, "%Y-%m-%d", localtime(&myCS_min));
623         msg << "Parsed " << myConstructionEntries << " construction entries between " << buff;
624         strftime(buff, 1024, "%Y-%m-%d", localtime(&myCS_max));
625         msg << " and " << buff << ".\n";
626         strftime(buff, 1024, "%Y-%m-%d", localtime(&myConstructionTime));
627         msg << "Removed " << myRemovedEdges << " edges not yet constructed at " << buff << ".\n";
628         msg << "   not yet started: " << myNotStarted << "\n";
629         msg << "   under construction: " << myUnderConstruction << "\n";
630         msg << "   finished: " << myFinished << "\n";
631         WRITE_MESSAGE(msg.str());
632     }
633 }
634 
635 
636 int
readPrefixedInt(const std::string & s,const std::string & prefix,int fallBack)637 NIImporter_DlrNavteq::readPrefixedInt(const std::string& s, const std::string& prefix, int fallBack) {
638     int result = fallBack;
639     size_t pos = s.find(prefix);
640     if (pos != std::string::npos) {
641         sscanf(s.substr(pos).c_str(), (prefix + "%i").c_str(), &result);
642     }
643     return result;
644 }
645 
646 time_t
readTimeRec(const std::string & start,const std::string & duration)647 NIImporter_DlrNavteq::readTimeRec(const std::string& start, const std::string& duration) {
648     // http://www.cplusplus.com/reference/ctime/mktime/
649     struct tm timeinfo;
650     timeinfo.tm_hour = 0;
651     timeinfo.tm_min = 0;
652     timeinfo.tm_sec = 0;
653     timeinfo.tm_year = 0;
654     timeinfo.tm_mon = 0;
655     timeinfo.tm_mday = 1;
656     timeinfo.tm_wday = 0;
657     timeinfo.tm_yday = 0;
658     timeinfo.tm_isdst = 0;
659 
660     timeinfo.tm_year = readPrefixedInt(start, "y") + readPrefixedInt(duration, "y") - 1900;
661     timeinfo.tm_mon = readPrefixedInt(start, "M") + readPrefixedInt(duration, "M") - 1;
662     timeinfo.tm_mday = 7 * (readPrefixedInt(start, "w") + readPrefixedInt(duration, "w"));
663     timeinfo.tm_mday += readPrefixedInt(start, "d") + readPrefixedInt(duration, "d");
664 
665     time_t result =  mktime(&timeinfo);
666     return result;
667 }
668 
669 
670 time_t
readDate(const std::string & yyyymmdd)671 NIImporter_DlrNavteq::readDate(const std::string& yyyymmdd) {
672     struct tm timeinfo;
673     timeinfo.tm_hour = 0;
674     timeinfo.tm_min = 0;
675     timeinfo.tm_sec = 0;
676     timeinfo.tm_wday = 0;
677     timeinfo.tm_yday = 0;
678     timeinfo.tm_isdst = 0;
679 
680     if (yyyymmdd.size() == 10
681             && yyyymmdd[4] == '-'
682             && yyyymmdd[7] == '-') {
683         try {
684             timeinfo.tm_year = StringUtils::toInt(yyyymmdd.substr(0, 4)) - 1900;
685             timeinfo.tm_mon = StringUtils::toInt(yyyymmdd.substr(5, 2)) - 1;
686             timeinfo.tm_mday = StringUtils::toInt(yyyymmdd.substr(8, 2));
687             return mktime(&timeinfo);
688         } catch (...) {
689         }
690     }
691     WRITE_ERROR("Could not parse YYYY-MM-DD date '" + yyyymmdd + "'");
692     time_t now;
693     time(&now);
694     return now;
695 }
696 
697 // ---------------------------------------------------------------------------
698 // definitions of NIImporter_DlrNavteq::ProhibitionHandler-methods
699 // ---------------------------------------------------------------------------
ProhibitionHandler(NBEdgeCont & ec,const std::string & file,time_t constructionTime)700 NIImporter_DlrNavteq::ProhibitionHandler::ProhibitionHandler(
701     NBEdgeCont& ec, const std::string& file, time_t constructionTime) :
702     myEdgeCont(ec),
703     myFile(file),
704     myVersion(0),
705     myConstructionTime(constructionTime) {
706 }
707 
708 
~ProhibitionHandler()709 NIImporter_DlrNavteq::ProhibitionHandler::~ProhibitionHandler() {}
710 
711 
712 bool
report(const std::string & result)713 NIImporter_DlrNavteq::ProhibitionHandler::report(const std::string& result) {
714 // # NAME_ID    Name
715     if (result[0] == '#') {
716         if (myVersion == 0) {
717             const double version = readVersion(result, myFile);
718             if (version > 0) {
719                 myVersion = version;
720             }
721         }
722         return true;
723     }
724     StringTokenizer st(result, StringTokenizer::TAB);
725     if (st.size() == 1) {
726         return true; // one line with the number of data containing lines in it (also starts with a comment # since ersion 6.5)
727     }
728     if (myVersion >= 6) {
729         assert(st.size() >= 7);
730         const std::string id = st.next();
731         const std::string permanent = st.next();
732         const std::string validityPeriod = st.next();
733         const std::string throughTraffic = st.next();
734         const std::string vehicleType = st.next();
735         if (validityPeriod != UNDEFINED) {
736             WRITE_WARNING("Ignoring temporary prohibited manoeuvre (" + validityPeriod + ")");
737             return true;
738         }
739     }
740     const std::string startEdge = st.next();
741     const std::string endEdge = st.get(st.size() - 1);
742 
743     NBEdge* from = myEdgeCont.retrieve(startEdge);
744     if (from == nullptr) {
745         WRITE_WARNING("Ignoring prohibition from unknown start edge '" + startEdge + "'");
746         return true;
747     }
748     NBEdge* to = myEdgeCont.retrieve(endEdge);
749     if (to == nullptr) {
750         WRITE_WARNING("Ignoring prohibition from unknown end edge '" + endEdge + "'");
751         return true;
752     }
753     from->removeFromConnections(to, -1, -1, true);
754     return true;
755 }
756 
757 
758 // ---------------------------------------------------------------------------
759 // definitions of NIImporter_DlrNavteq::ConnectedLanesHandler-methods
760 // ---------------------------------------------------------------------------
ConnectedLanesHandler(NBEdgeCont & ec)761 NIImporter_DlrNavteq::ConnectedLanesHandler::ConnectedLanesHandler(
762     NBEdgeCont& ec) :
763     myEdgeCont(ec) {
764 }
765 
766 
~ConnectedLanesHandler()767 NIImporter_DlrNavteq::ConnectedLanesHandler::~ConnectedLanesHandler() {}
768 
769 
770 bool
report(const std::string & result)771 NIImporter_DlrNavteq::ConnectedLanesHandler::report(const std::string& result) {
772     if (result[0] == '#') {
773         return true;
774     }
775     StringTokenizer st(result, StringTokenizer::TAB);
776     if (st.size() == 1) {
777         return true; // one line with the number of data containing lines in it (also starts with a comment # since ersion 6.5)
778     }
779     assert(st.size() >= 7);
780     const std::string nodeID = st.next();
781     const std::string vehicleType = st.next();
782     const std::string fromLaneS = st.next();
783     const std::string toLaneS = st.next();
784     const std::string throughTraffic = st.next();
785     const std::string startEdge = st.next();
786     const std::string endEdge = st.get(st.size() - 1);
787 
788     NBEdge* from = myEdgeCont.retrieve(startEdge);
789     if (from == nullptr) {
790         WRITE_WARNING("Ignoring prohibition from unknown start edge '" + startEdge + "'");
791         return true;
792     }
793     NBEdge* to = myEdgeCont.retrieve(endEdge);
794     if (to == nullptr) {
795         WRITE_WARNING("Ignoring prohibition from unknown end edge '" + endEdge + "'");
796         return true;
797     }
798     int fromLane = StringUtils::toInt(fromLaneS) - 1; // one based
799     if (fromLane < 0 || fromLane >= from->getNumLanes()) {
800         WRITE_WARNING("Ignoring invalid lane index '" + fromLaneS + "' in connection from edge '" + startEdge + "' with " + toString(from->getNumLanes()) + " lanes");
801         return true;
802     }
803     int toLane = StringUtils::toInt(toLaneS) - 1; // one based
804     if (toLane < 0 || toLane >= to->getNumLanes()) {
805         WRITE_WARNING("Ignoring invalid lane index '" + toLaneS + "' in connection to edge '" + endEdge + "' with " + toString(to->getNumLanes()) + " lanes");
806         return true;
807     }
808     if (!from->addLane2LaneConnection(fromLane, to, toLane, NBEdge::L2L_USER, true)) {
809         if (OptionsCont::getOptions().getBool("show-errors.connections-first-try")) {
810             WRITE_WARNING("Could not set loaded connection from '" + from->getLaneID(fromLane) + "' to '" + to->getLaneID(toLane) + "'.");
811         }
812         // set as to be re-applied after network processing
813         // if this connection runs across a node cluster it may not be possible to set this
814         const bool warnOnly = st.size() > 7;
815         myEdgeCont.addPostProcessConnection(from->getID(), fromLane, to->getID(), toLane, false, true,
816                                             NBEdge::UNSPECIFIED_CONTPOS, NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE,
817                                             NBEdge::UNSPECIFIED_SPEED, PositionVector::EMPTY, false, warnOnly);
818     }
819     // ensure that connections for other lanes are guessed if not specified
820     from->declareConnectionsAsLoaded(NBEdge::INIT);
821     from->getLaneStruct(fromLane).connectionsDone = true;
822     return true;
823 }
824 
825 /****************************************************************************/
826