LCOV - code coverage report
Current view: top level - src/polyconvert - PCLoaderVisum.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 1.4 % 208 3
Test Date: 2024-12-21 15:45:41 Functions: 33.3 % 3 1

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-2024 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    PCLoaderVisum.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @author  Jakob Erdmann
      17              : /// @author  Christoph Sommer
      18              : /// @author  Michael Behrisch
      19              : /// @date    Thu, 02.11.2006
      20              : ///
      21              : // A reader of pois and polygons stored in VISUM-format
      22              : /****************************************************************************/
      23              : #include <config.h>
      24              : 
      25              : #include <string>
      26              : #include <map>
      27              : #include <fstream>
      28              : #include <utils/common/StringTokenizer.h>
      29              : #include <utils/common/UtilExceptions.h>
      30              : #include <utils/common/MsgHandler.h>
      31              : #include <utils/common/StringUtils.h>
      32              : #include <utils/common/StringUtils.h>
      33              : #include <utils/common/ToString.h>
      34              : #include <utils/common/FileHelpers.h>
      35              : #include <utils/options/OptionsCont.h>
      36              : #include <utils/options/Option.h>
      37              : #include <utils/importio/LineReader.h>
      38              : #include <utils/common/StdDefs.h>
      39              : #include <polyconvert/PCPolyContainer.h>
      40              : #include "PCLoaderVisum.h"
      41              : #include <utils/common/RGBColor.h>
      42              : #include <utils/geom/GeomHelper.h>
      43              : #include <utils/geom/Boundary.h>
      44              : #include <utils/geom/Position.h>
      45              : #include <utils/geom/GeoConvHelper.h>
      46              : #include <utils/importio/NamedColumnsParser.h>
      47              : 
      48              : StringBijection<PCLoaderVisum::VISUM_KEY>::Entry PCLoaderVisum::KEYS_DE[] = {
      49              :     // duplicates NIImporter_VISUM::KEYS_DE due to lack of suitable common location
      50              :     { "VSYS", VISUM_SYS },
      51              :     { "STRECKENTYP", VISUM_LINKTYPE },
      52              :     { "KNOTEN", VISUM_NODE },
      53              :     { "BEZIRK", VISUM_DISTRICT },
      54              :     { "PUNKT", VISUM_POINT },
      55              :     { "STRECKE", VISUM_LINK },
      56              :     { "V0IV", VISUM_V0 },
      57              :     { "VSYSSET", VISUM_TYPES },
      58              :     { "RANG", VISUM_RANK },
      59              :     { "KAPIV", VISUM_CAPACITY },
      60              :     { "XKOORD", VISUM_XCOORD },
      61              :     { "YKOORD", VISUM_YCOORD },
      62              :     { "ID", VISUM_ID },
      63              :     { "CODE", VISUM_CODE },
      64              :     { "VONKNOTNR", VISUM_FROMNODE },
      65              :     { "NACHKNOTNR", VISUM_TONODE },
      66              :     { "TYPNR", VISUM_TYPE },
      67              :     { "TYP", VISUM_TYP },
      68              :     { "ANBINDUNG", VISUM_DISTRICT_CONNECTION },
      69              :     { "BEZNR", VISUM_SOURCE_DISTRICT },
      70              :     { "KNOTNR",  VISUM_FROMNODENO },
      71              :     { "RICHTUNG",  VISUM_DIRECTION },
      72              :     { "FLAECHEID",  VISUM_SURFACEID },
      73              :     { "TFLAECHEID",  VISUM_FACEID },
      74              :     { "VONPUNKTID",  VISUM_FROMPOINTID },
      75              :     { "NACHPUNKTID",  VISUM_TOPOINTID },
      76              :     { "KANTE",  VISUM_EDGE },
      77              :     { "ABBIEGER",  VISUM_TURN },
      78              :     { "UEBERKNOTNR",  VISUM_VIANODENO },
      79              :     { "ANZFAHRSTREIFEN",  VISUM_NUMLANES },
      80              :     { "INDEX",  VISUM_INDEX },
      81              :     { "STRECKENPOLY",  VISUM_LINKPOLY },
      82              :     { "FLAECHENELEMENT",  VISUM_SURFACEITEM },
      83              :     { "TEILFLAECHENELEMENT",  VISUM_FACEITEM },
      84              :     { "KANTEID",  VISUM_EDGEID },
      85              :     { "Q",  VISUM_ORIGIN },
      86              :     { "Z",  VISUM_DESTINATION },
      87              :     { "KATNR", VISUM_CATID },
      88              :     { "ZWISCHENPUNKT", VISUM_EDGEITEM },
      89              :     { "POIKATEGORIE", VISUM_POICATEGORY },
      90              :     { "NR", VISUM_NO } // must be the last one
      91              : };
      92              : 
      93              : 
      94              : StringBijection<PCLoaderVisum::VISUM_KEY> PCLoaderVisum::KEYS(PCLoaderVisum::KEYS_DE, VISUM_NO);
      95              : 
      96              : 
      97              : // ===========================================================================
      98              : // method definitions
      99              : // ===========================================================================
     100              : void
     101           41 : PCLoaderVisum::loadIfSet(OptionsCont& oc, PCPolyContainer& toFill,
     102              :                          PCTypeMap& tm) {
     103           82 :     if (!oc.isSet("visum-files")) {
     104           41 :         return;
     105              :     }
     106            0 :     const std::string languageFile = oc.getString("visum.language-file");
     107            0 :     if (languageFile != "") {
     108            0 :         loadLanguage(languageFile);
     109              :     }
     110              :     // parse file(s)
     111            0 :     std::vector<std::string> files = oc.getStringVector("visum-files");
     112            0 :     for (std::vector<std::string>::const_iterator file = files.begin(); file != files.end(); ++file) {
     113            0 :         if (!FileHelpers::isReadable(*file)) {
     114            0 :             throw ProcessError(TLF("Could not open visum-file '%'.", *file));
     115              :         }
     116            0 :         PROGRESS_BEGIN_MESSAGE("Parsing from visum-file '" + *file + "'");
     117            0 :         load(*file, oc, toFill, tm);
     118            0 :         PROGRESS_DONE_MESSAGE();
     119              :     }
     120            0 : }
     121              : 
     122              : 
     123              : 
     124              : void
     125            0 : PCLoaderVisum::load(const std::string& file, OptionsCont& oc, PCPolyContainer& toFill,
     126              :                     PCTypeMap& tm) {
     127              :     GeoConvHelper& geoConvHelper = GeoConvHelper::getProcessing();
     128              :     std::string what;
     129              :     std::map<long long int, Position> punkte;
     130              :     std::map<long long int, PositionVector> kanten;
     131              :     std::map<long long int, PositionVector> teilflaechen;
     132              :     std::map<long long int, long long int> flaechenelemente;
     133            0 :     NamedColumnsParser lineParser;
     134            0 :     LineReader lr(file);
     135            0 :     while (lr.hasMore()) {
     136            0 :         std::string line = lr.readLine();
     137              :         // reset if current is over
     138            0 :         if (line.length() == 0 || line[0] == '*' || line[0] == '$') {
     139              :             what = "";
     140              :         }
     141              :         // read items
     142            0 :         if (what == "$" + KEYS.getString(VISUM_POINT)) {
     143            0 :             lineParser.parseLine(line);
     144            0 :             long long int id = StringUtils::toLong(lineParser.get(KEYS.getString(VISUM_ID)));
     145            0 :             double x = StringUtils::toDouble(lineParser.get(KEYS.getString(VISUM_XCOORD)));
     146            0 :             double y = StringUtils::toDouble(lineParser.get(KEYS.getString(VISUM_YCOORD)));
     147              :             Position pos(x, y);
     148            0 :             if (!geoConvHelper.x2cartesian(pos)) {
     149            0 :                 WRITE_WARNINGF(TL("Unable to project coordinates for point '%'."), toString(id));
     150              :             }
     151            0 :             punkte[id] = pos;
     152              :             continue;
     153            0 :         } else if (what == "$" + KEYS.getString(VISUM_EDGE)) {
     154            0 :             lineParser.parseLine(line);
     155            0 :             long long int id = StringUtils::toLong(lineParser.get(KEYS.getString(VISUM_ID)));
     156            0 :             long long int fromID = StringUtils::toLong(lineParser.get(KEYS.getString(VISUM_FROMPOINTID)));
     157            0 :             long long int toID = StringUtils::toLong(lineParser.get(KEYS.getString(VISUM_TOPOINTID)));
     158            0 :             PositionVector vec;
     159            0 :             vec.push_back(punkte[fromID]);
     160            0 :             vec.push_back(punkte[toID]);
     161            0 :             kanten[id] = vec;
     162              :             continue;
     163            0 :         } else if (what == "$" + KEYS.getString(VISUM_EDGEITEM)) {
     164            0 :             lineParser.parseLine(line);
     165            0 :             long long int id = StringUtils::toLong(lineParser.get(KEYS.getString(VISUM_EDGEID)));
     166            0 :             int index = StringUtils::toInt(lineParser.get(KEYS.getString(VISUM_INDEX)));
     167            0 :             double x = StringUtils::toDouble(lineParser.get(KEYS.getString(VISUM_XCOORD)));
     168            0 :             double y = StringUtils::toDouble(lineParser.get(KEYS.getString(VISUM_YCOORD)));
     169              :             Position pos(x, y);
     170            0 :             if (!geoConvHelper.x2cartesian(pos)) {
     171            0 :                 WRITE_WARNINGF(TL("Unable to project coordinates for edge '%'."), toString(id));
     172              :             }
     173            0 :             kanten[id].insert(kanten[id].begin() + index, pos);
     174              :             continue;
     175            0 :         } else if (what == "$" + KEYS.getString(VISUM_FACEITEM)) {
     176            0 :             lineParser.parseLine(line);
     177            0 :             long long int id = StringUtils::toLong(lineParser.get(KEYS.getString(VISUM_FACEID)));
     178              :             //int index = StringUtils::toInt(lineParser.get("INDEX"));
     179              :             //index = 0; /// hmmmm - assume it's sorted...
     180            0 :             long long int kid = StringUtils::toLong(lineParser.get(KEYS.getString(VISUM_EDGEID)));
     181            0 :             int dir = StringUtils::toInt(lineParser.get(KEYS.getString(VISUM_DIRECTION)));
     182            0 :             if (teilflaechen.find(id) == teilflaechen.end()) {
     183            0 :                 teilflaechen[id] = PositionVector();
     184              :             }
     185            0 :             if (dir == 0) {
     186            0 :                 for (int i = 0; i < (int) kanten[kid].size(); ++i) {
     187            0 :                     teilflaechen[id].push_back_noDoublePos(kanten[kid][i]);
     188              :                 }
     189              :             } else {
     190            0 :                 for (int i = (int) kanten[kid].size() - 1; i >= 0; --i) {
     191            0 :                     teilflaechen[id].push_back_noDoublePos(kanten[kid][i]);
     192              :                 }
     193              :             }
     194              :             continue;
     195            0 :         } else if (what == "$" + KEYS.getString(VISUM_SURFACEITEM)) {
     196            0 :             lineParser.parseLine(line);
     197            0 :             long long int id = StringUtils::toLong(lineParser.get(KEYS.getString(VISUM_SURFACEID)));
     198            0 :             long long int tid = StringUtils::toLong(lineParser.get(KEYS.getString(VISUM_FACEID)));
     199            0 :             flaechenelemente[id] = tid;
     200              :             continue;
     201            0 :         }
     202              :         // set if read
     203            0 :         if (line[0] == '$') {
     204              :             what = "";
     205            0 :             if (line.find("$" + KEYS.getString(VISUM_POINT) + ":") == 0) {
     206            0 :                 what = "$" + KEYS.getString(VISUM_POINT);
     207            0 :             } else if (line.find("$" + KEYS.getString(VISUM_EDGE) + ":") == 0) {
     208            0 :                 what = "$" + KEYS.getString(VISUM_EDGE);
     209            0 :             } else if (line.find("$" + KEYS.getString(VISUM_EDGEITEM) + ":") == 0) {
     210            0 :                 what = "$" + KEYS.getString(VISUM_EDGEITEM);
     211            0 :             } else if (line.find("$" + KEYS.getString(VISUM_FACEITEM) + ":") == 0) {
     212            0 :                 what = "$" + KEYS.getString(VISUM_FACEITEM);
     213            0 :             } else if (line.find("$" + KEYS.getString(VISUM_SURFACEITEM) + ":") == 0) {
     214            0 :                 what = "$" + KEYS.getString(VISUM_SURFACEITEM);
     215              :             }
     216            0 :             if (what != "") {
     217            0 :                 lineParser.reinit(line.substr(what.length() + 1));
     218              :             }
     219              :         }
     220              :     }
     221              : 
     222              :     // do some more sane job...
     223            0 :     RGBColor c = RGBColor::parseColor(oc.getString("color"));
     224              :     std::map<std::string, std::string> typemap;
     225              :     // load the pois/polys
     226            0 :     lr.reinit();
     227              :     bool parsingCategories = false;
     228              :     bool parsingPOIs = false;
     229              :     bool parsingDistrictsDirectly = false;
     230            0 :     PositionVector vec;
     231              :     std::string polyType, lastID;
     232              :     bool first = true;
     233            0 :     while (lr.hasMore()) {
     234            0 :         std::string line = lr.readLine();
     235              :         // do not parse empty lines
     236            0 :         if (line.length() == 0) {
     237            0 :             continue;
     238              :         }
     239              :         // do not parse comment lines
     240            0 :         if (line[0] == '*') {
     241            0 :             continue;
     242              :         }
     243              : 
     244            0 :         if (line[0] == '$') {
     245              :             // reset parsing on new entry type
     246              :             parsingCategories = false;
     247              :             parsingPOIs = false;
     248              :             parsingDistrictsDirectly = false;
     249              :             polyType = "";
     250              :         }
     251              : 
     252            0 :         if (parsingCategories) {
     253              :             // parse the category
     254            0 :             StringTokenizer st(line, ";");
     255            0 :             std::string catid = st.next();
     256            0 :             std::string catname = st.next();
     257            0 :             typemap[catid] = catname;
     258            0 :         }
     259            0 :         if (parsingPOIs) {
     260              :             // parse the poi
     261              :             // $POI:Nr;CATID;CODE;NAME;Kommentar;XKoord;YKoord;
     262            0 :             lineParser.parseLine(line);
     263            0 :             long long int idL = StringUtils::toLong(lineParser.get(KEYS.getString(VISUM_NO)));
     264            0 :             std::string id = toString(idL);
     265            0 :             std::string catid = lineParser.get(KEYS.getString(VISUM_CATID));
     266              :             // process read values
     267            0 :             double x = StringUtils::toDouble(lineParser.get(KEYS.getString(VISUM_XCOORD)));
     268            0 :             double y = StringUtils::toDouble(lineParser.get(KEYS.getString(VISUM_YCOORD)));
     269              :             Position pos(x, y);
     270            0 :             if (!geoConvHelper.x2cartesian(pos)) {
     271            0 :                 WRITE_WARNINGF(TL("Unable to project coordinates for POI '%'."), id);
     272              :             }
     273            0 :             std::string type = typemap[catid];
     274              :             // patch the values
     275            0 :             bool discard = oc.getBool("discard");
     276            0 :             std::string icon = oc.getString("icon");
     277            0 :             double layer = oc.getFloat("layer");
     278            0 :             RGBColor color;
     279            0 :             if (tm.has(type)) {
     280            0 :                 const PCTypeMap::TypeDef& def = tm.get(type);
     281            0 :                 id = def.prefix + id;
     282            0 :                 type = def.id;
     283            0 :                 color = def.color;
     284            0 :                 discard = def.discard;
     285            0 :                 icon = def.icon;
     286            0 :                 layer = def.layer;
     287              :             } else {
     288            0 :                 id = oc.getString("prefix") + id;
     289            0 :                 color = c;
     290              :             }
     291            0 :             if (!discard) {
     292              :                 const std::string origId = id;
     293              :                 int index = 1;
     294            0 :                 while (toFill.getPOIs().get(id) != nullptr) {
     295            0 :                     id = origId + "#" + toString(index++);
     296              :                 }
     297            0 :                 PointOfInterest* poi = new PointOfInterest(id, type, color, pos, false, "", 0, false, 0, icon, layer);
     298            0 :                 toFill.add(poi);
     299              :             }
     300              :         }
     301              : 
     302              :         // poly
     303            0 :         if (polyType != "") {
     304            0 :             StringTokenizer st(line, ";");
     305            0 :             std::string id = st.next();
     306              :             std::string type;
     307            0 :             if (!first && lastID != id) {
     308              :                 // we have parsed a polygon completely
     309            0 :                 RGBColor color;
     310            0 :                 double layer = oc.getFloat("layer");
     311            0 :                 bool discard = oc.getBool("discard");
     312            0 :                 if (tm.has(polyType)) {
     313            0 :                     const PCTypeMap::TypeDef& def = tm.get(polyType);
     314            0 :                     id = def.prefix + id;
     315            0 :                     type = def.id;
     316            0 :                     color = def.color;
     317            0 :                     discard = def.discard;
     318            0 :                     layer = def.layer;
     319              :                 } else {
     320            0 :                     id = oc.getString("prefix") + id;
     321            0 :                     type = oc.getString("type");
     322            0 :                     color = c;
     323              :                 }
     324            0 :                 if (!discard) {
     325              :                     const std::string origId = id;
     326              :                     int index = 1;
     327            0 :                     while (toFill.getPolygons().get(id) != nullptr) {
     328            0 :                         id = origId + "#" + toString(index++);
     329              :                     }
     330            0 :                     SUMOPolygon* poly = new SUMOPolygon(id, type, color, vec, false, false, 1, layer);
     331            0 :                     toFill.add(poly);
     332              :                 }
     333              :                 vec.clear();
     334              :             }
     335              :             lastID = id;
     336              :             first = false;
     337              :             // parse current poly
     338            0 :             std::string index = st.next();
     339            0 :             std::string xpos = st.next();
     340            0 :             std::string ypos = st.next();
     341              :             Position pos2D((double) atof(xpos.c_str()), (double) atof(ypos.c_str()));
     342            0 :             if (!geoConvHelper.x2cartesian(pos2D)) {
     343            0 :                 WRITE_WARNINGF(TL("Unable to project coordinates for polygon '%'."), id);
     344              :             }
     345            0 :             vec.push_back(pos2D);
     346            0 :         }
     347              : 
     348              :         // district refering a shape
     349            0 :         if (parsingDistrictsDirectly) {
     350              :             //$BEZIRK:NR        CODE    NAME    TYPNR   XKOORD  YKOORD  FLAECHEID       BEZART  IVANTEIL_Q      IVANTEIL_Z      OEVANTEIL       METHODEANBANTEILE       ZWERT1  ZWERT2  ZWERT3  ISTINAUSWAHL    OBEZNR  NOM_COM COD_COM
     351            0 :             lineParser.parseLine(line);
     352            0 :             long long int idL = StringUtils::toLong(lineParser.get(KEYS.getString(VISUM_NO)));
     353            0 :             std::string id = toString(idL);
     354            0 :             long long int area = StringUtils::toLong(lineParser.get(KEYS.getString(VISUM_SURFACEID)));
     355            0 :             double x = StringUtils::toDouble(lineParser.get(KEYS.getString(VISUM_XCOORD)));
     356            0 :             double y = StringUtils::toDouble(lineParser.get(KEYS.getString(VISUM_YCOORD)));
     357              :             // patch the values
     358            0 :             std::string type = "district";
     359            0 :             bool discard = oc.getBool("discard");
     360            0 :             std::string icon = oc.getString("icon");
     361            0 :             double layer = oc.getFloat("layer");
     362            0 :             RGBColor color;
     363            0 :             if (tm.has(type)) {
     364            0 :                 const PCTypeMap::TypeDef& def = tm.get(type);
     365            0 :                 id = def.prefix + id;
     366            0 :                 type = def.id;
     367            0 :                 color = def.color;
     368            0 :                 discard = def.discard;
     369            0 :                 icon = def.icon;
     370            0 :                 layer = def.layer;
     371              :             } else {
     372            0 :                 id = oc.getString("prefix") + id;
     373            0 :                 type = oc.getString("type");
     374            0 :                 color = c;
     375              :             }
     376            0 :             if (!discard) {
     377            0 :                 if (teilflaechen[flaechenelemente[area]].size() > 0) {
     378              :                     const std::string origId = id;
     379              :                     int index = 1;
     380            0 :                     while (toFill.getPolygons().get(id) != nullptr) {
     381            0 :                         id = origId + "#" + toString(index++);
     382              :                     }
     383            0 :                     const auto shape = teilflaechen[flaechenelemente[area]];
     384            0 :                     SUMOPolygon* poly = new SUMOPolygon(id, type, color, shape, false, false, 1, layer);
     385            0 :                     toFill.add(poly);
     386            0 :                 } else {
     387              :                     Position pos(x, y);
     388            0 :                     if (!geoConvHelper.x2cartesian(pos)) {
     389            0 :                         WRITE_WARNINGF(TL("Unable to project coordinates for POI '%'."), id);
     390              :                     }
     391              :                     const std::string origId = id;
     392              :                     int index = 1;
     393            0 :                     while (toFill.getPOIs().get(id) != nullptr) {
     394            0 :                         id = origId + "#" + toString(index++);
     395              :                     }
     396            0 :                     PointOfInterest* poi = new PointOfInterest(id, type, color, pos, false, "", 0, false, 0, icon, layer);
     397            0 :                     toFill.add(poi);
     398              :                 }
     399              :             }
     400              :         }
     401              : 
     402              : 
     403            0 :         if (line.find("$POIKATEGORIEDEF:") == 0 || line.find("$" + KEYS.getString(VISUM_POICATEGORY) + ":") == 0) {
     404              :             // ok, got categories, begin parsing from next line
     405              :             parsingCategories = true;
     406            0 :             lineParser.reinit(line.substr(line.find(":") + 1));
     407              :         }
     408            0 :         if ((line.find("$POI:") == 0) || line.find("$POIOFCAT") != std::string::npos) {
     409              :             // ok, got pois, begin parsing from next line
     410              :             parsingPOIs = true;
     411            0 :             lineParser.reinit(line.substr(line.find(":") + 1));
     412              :         }
     413            0 :         if (line.find("$" + KEYS.getString(VISUM_DISTRICT)) == 0 && line.find(KEYS.getString(VISUM_SURFACEID)) != std::string::npos) {
     414              :             // ok, have a district header, and it seems like districts would reference shapes...
     415              :             parsingDistrictsDirectly = true;
     416            0 :             lineParser.reinit(line.substr(line.find(":") + 1));
     417              :         }
     418              : 
     419              : 
     420            0 :         if (line.find("$BEZIRKPOLY") != std::string::npos) {
     421              :             polyType = "district";
     422              :         }
     423            0 :         if (line.find("$GEBIETPOLY") != std::string::npos) {
     424              :             polyType = "area";
     425              :         }
     426              : 
     427              :     }
     428            0 : }
     429              : 
     430              : 
     431              : void
     432            0 : PCLoaderVisum::loadLanguage(const std::string& file) {
     433            0 :     std::ifstream strm(file.c_str());
     434            0 :     if (!strm.good()) {
     435            0 :         throw ProcessError(TLF("Could not load VISUM language map from '%'.", file));
     436              :     }
     437            0 :     while (strm.good()) {
     438              :         std::string keyDE;
     439              :         std::string keyNew;
     440            0 :         strm >> keyDE;
     441            0 :         strm >> keyNew;
     442              :         if (KEYS.hasString(keyDE)) {
     443            0 :             VISUM_KEY key = KEYS.get(keyDE);
     444            0 :             KEYS.remove(keyDE, key);
     445            0 :             KEYS.insert(keyNew, key);
     446              :         } else if (keyDE != "") {
     447              :             // do not warn about network-related keys (NIImporter_VISUM)
     448              :             //WRITE_WARNINGF(TL("Unknown entry '%' in VISUM language map"), keyDE);
     449              :         }
     450              :     }
     451              : 
     452            0 : }
     453              : 
     454              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1