diff --git a/src/knowledgedb-generator/CMakeLists.txt b/src/knowledgedb-generator/CMakeLists.txt index aced3a1..421fd2e 100644 --- a/src/knowledgedb-generator/CMakeLists.txt +++ b/src/knowledgedb-generator/CMakeLists.txt @@ -1,69 +1,53 @@ add_executable(generate-knowledgedb main.cpp codegen.cpp timezones.cpp wikidata.cpp airportdbgenerator.cpp countrydbgenerator.cpp osmairportdb.cpp timezonedbgenerator.cpp trainstationdbgenerator.cpp util.cpp ../stringutil.cpp ) target_compile_definitions(generate-knowledgedb PRIVATE "KITINERARY_STATIC_DEFINE") target_include_directories(generate-knowledgedb PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../knowledgedb ${CMAKE_CURRENT_BINARY_DIR}/.. ) target_link_libraries(generate-knowledgedb PRIVATE Qt5::Network Qt5::Gui KOSM) -find_program(XSLTPROC_EXECUTABLE xsltproc) - -add_custom_command( - OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/timezones.colormap - COMMAND - ${XSLTPROC_EXECUTABLE} --novalid - ${CMAKE_CURRENT_SOURCE_DIR}/timezones.xsl - ${CMAKE_CURRENT_SOURCE_DIR}/timezones.qgs - > ${CMAKE_CURRENT_SOURCE_DIR}/timezones.colormap - DEPENDS - ${CMAKE_CURRENT_SOURCE_DIR}/timezones.xsl - ${CMAKE_CURRENT_SOURCE_DIR}/timezones.qgs -) - # extract all elements we are interested in (airports, terminals, stations at airports) osm_filter(OUTPUT airports.o5m FILTER --keep=\"iata=* or aeroway=terminal or public_transport=station or railway=station or railway=halt or railway=tram_stop\") # augment bounding box information osm_convert(INPUT airports.o5m OUTPUT airports-bbox.o5m ADD_BBOX ON) # convert the output to OSM XML format, so the code generator can consume it osm_convert(INPUT airports-bbox.o5m OUTPUT airports-bbox.osm) set(outfiles "") function(generate_db dbtype outfile) set(extra_args "") if (ARGN) set(extra_args "--osm-data=${ARGN}") endif() add_custom_command( OUTPUT ${outfile} COMMAND generate-knowledgedb -o ${CMAKE_CURRENT_SOURCE_DIR}/../knowledgedb/${outfile} -d ${dbtype} ${extra_args} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS - ${CMAKE_CURRENT_SOURCE_DIR}/timezones.colormap - ${CMAKE_CURRENT_SOURCE_DIR}/timezones.png ${ARGN} generate-knowledgedb ) list(APPEND outfiles ${outfile}) set(outfiles ${outfiles} PARENT_SCOPE) endfunction() generate_db(country countrydb_data.cpp) generate_db(timezone timezonedb_data.cpp) generate_db(timezoneheader timezonedb_data.h) generate_db(airport airportdb_data.cpp ${OSM_PLANET_DIR}/airports-bbox.osm) generate_db(trainstation trainstationdb_data.cpp) -add_custom_target(rebuild-knowledgedb DEPENDS ${outfiles} ${CMAKE_CURRENT_SOURCE_DIR}/timezones.png) +add_custom_target(rebuild-knowledgedb DEPENDS ${outfiles}) diff --git a/src/knowledgedb-generator/timezones.cpp b/src/knowledgedb-generator/timezones.cpp index e20bd40..8e38459 100644 --- a/src/knowledgedb-generator/timezones.cpp +++ b/src/knowledgedb-generator/timezones.cpp @@ -1,226 +1,106 @@ /* Copyright (c) 2017 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "timezones.h" #include #include #include #include using namespace KItinerary::Generator; Timezones::Timezones() { - // load the color to timezone mapping file - QFile colorMap(QStringLiteral("timezones.colormap")); - if (!colorMap.open(QFile::ReadOnly)) { - qCritical() << "Unable to open timezone colormap file: " << colorMap.errorString(); - exit(1); - } - - while (!colorMap.atEnd()) { - const auto line = colorMap.readLine(); - const auto split = line.split(','); - if (split.size() < 5) { - continue; - } - - const auto tzName = split.at(4).trimmed(); - if (tzName.isEmpty()) { - continue; - } - - QColor c; - c.setRed(split.at(0).toInt()); - c.setGreen(split.at(1).toInt()); - c.setBlue(split.at(2).toInt()); - if (m_colorMap.contains(c.rgb())) { - qWarning() << "Color collision on timezones:" << m_colorMap.value(c.rgb()) << split.at(4).trimmed(); - } - m_colorMap.insert(c.rgb(), split.at(4).trimmed()); - } - - // load the wold file for correcting the pixel to coordinate mapping in the timezone image - // see https://en.wikipedia.org/wiki/World_file for format and math behind this - QFile worldFile(QStringLiteral("timezones.pgw")); - if (!worldFile.open(QFile::ReadOnly|QFile::Text)) { - qCritical() << "Unable to open world file: " << worldFile.errorString(); - exit(1); - } - const auto worldFileContent = worldFile.readAll().split('\n'); - if (worldFileContent.size() < 6) { - qCritical() << "Invalid world map file format."; - exit(1); - } - if (worldFileContent[1].toDouble() != 0.0 || worldFileContent[2].toDouble() != 0.0) { - qCritical() << "Timezone map is rotated, that is not supported!"; - exit(1); - } - m_xMapUnitsPerPixel = worldFileContent[0].toDouble(); - m_yMapUnitsPerPixel = worldFileContent[3].toDouble(); - m_topLeftMapUnitX = worldFileContent[4].toDouble(); - m_topLeftMapUnitY = worldFileContent[5].toDouble(); - // load zone.tab for country mapping QFile zoneTab(QStringLiteral("/usr/share/zoneinfo/zone.tab")); if (!zoneTab.open(QFile::ReadOnly)) { qCritical() << "Unable to open zonetab file: " << zoneTab.errorString(); exit(1); } const auto lines = QString::fromUtf8(zoneTab.readAll()).split(QLatin1Char('\n')); for (const auto &line : lines) { if (line.startsWith(QLatin1Char('#'))) { continue; } const auto cols = line.split(QLatin1Char('\t')); if (cols.size() < 3) { continue; } const auto countries = cols.at(0).split(QLatin1Char(',')); const auto tzName = cols.at(2).toUtf8(); m_zones.push_back(tzName); for (const auto &country : countries) { m_countryZones[country].push_back(tzName); } if (countries.size() == 1) { if (m_countryForZone.find(tzName) != m_countryForZone.end()) { m_countryForZone[tzName] = QString(); } else { m_countryForZone[tzName] = countries[0]; } } } std::sort(m_zones.begin(), m_zones.end()); m_zoneOffsets.reserve(m_zones.size()); uint16_t offset = 0; for (const auto &tz : m_zones) { m_zoneOffsets.push_back(offset); offset += tz.size() + 1; // +1 of the trailing null byte } /* Manual overrides for countries that de-facto only have a single timezone, * even if the IANA database doesn't reflect that. */ m_countryZones[QStringLiteral("AR")] = { "America/Argentina/Buenos_Aires" }; m_countryZones[QStringLiteral("CN")] = { "Asia/Shanghai" }; m_countryZones[QStringLiteral("CY")] = { "Asia/Nicosia" }; m_countryZones[QStringLiteral("DE")] = { "Europe/Berlin" }; m_countryZones[QStringLiteral("MY")] = { "Asia/Kuala_Lumpur" }; /* Manual overrides for timezones that do not belong to a unique country, contrary what zonetab claims. */ setCountryForZone("Asia/Bangkok", {}); // also used in northern Vietnam setCountryForZone("Europe/Simferopol", {}); // disputed area } Timezones::~Timezones() = default; -QByteArray Timezones::timezoneForLocation(const QString &isoCode, KnowledgeDb::Coordinate coord) const -{ - // look up by country - QSet countryTzs; - const auto it = m_countryZones.find(isoCode); - if (it != m_countryZones.end()) { - for (const auto &tz : (*it).second) { - countryTzs.insert(tz); - } - } - - // look up by coordinate - QByteArray coordTz; // search radius 0 - QSet coordTzs; // search radius 1 - if (coord.isValid()) { - if (m_map.isNull() && !m_map.load(QStringLiteral("timezones.png"))) { - qCritical() << "Unable to open timezone map."; - exit(1); - } - - const auto p = coordinateToPixel(coord); - //qDebug() << p.x() << p.y() << m_map.width() << m_map.height() << coord.longitude << coord.latitude << QColor(m_map.pixel(p)) << m_colorMap.value(m_map.pixel(p)); - coordTz = timezoneForPixel(p.x(), p.y()); - - // search the vicinity, helps with islands/costal regions or border regions - const QPoint offsets[] = { {-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1} }; - for (auto offset : offsets) { - const auto tz = timezoneForPixel(p.x() + offset.x(), p.y() + offset.y()); - if (!tz.isEmpty()) { - coordTzs.insert(tz); - } - } - } - - // determine the result: either just one method yieled a unique result, or both have a unique intersection - if (coordTz.isEmpty() && countryTzs.size() == 1) { - return *countryTzs.constBegin(); - } - if (!coordTz.isEmpty() && (countryTzs.isEmpty() || countryTzs.contains(coordTz))) { - return coordTz; - } - - // if the above wasn't the case, look for a unique intersection in the vicinity of the coordinate - // this covers cases of locations within the 1.5km resolution of the timezone image - coordTzs.insert(coordTz); - const auto tzs = coordTzs.intersect(countryTzs); - if (tzs.size() == 1) { - return *tzs.constBegin(); - } - - // if the above still doesn't help, we take the coodinate-based result, can't be entirely wrong - //qDebug() << "Timezone candidates:" << isoCode << coordTz << coordTzs << countryTzs << coord.latitude << coord.longitude; - return coordTz; -} - -QByteArray Timezones::timezoneForPixel(int x, int y) const -{ - x %= m_map.width(); - y %= m_map.height(); - return m_colorMap.value(m_map.pixel(x, y)); -} - uint16_t Timezones::offset(const QByteArray& tz) const { const auto it = std::lower_bound(m_zones.begin(), m_zones.end(), tz); if (it == m_zones.end() || (*it) != tz) { return std::numeric_limits::max(); } return m_zoneOffsets[std::distance(m_zones.begin(), it)]; } -QPoint Timezones::coordinateToPixel(KnowledgeDb::Coordinate coord) const -{ - QPoint p; - p.setX(qRound((coord.longitude - m_topLeftMapUnitX) / m_xMapUnitsPerPixel)); - p.setY(qRound((coord.latitude - m_topLeftMapUnitY) / m_yMapUnitsPerPixel)); - return p; -} - void Timezones::setCountryForZone(const QByteArray &tz, const QString &country) { auto it = m_countryForZone.find(tz); if (it == m_countryForZone.end()) { return; } (*it).second = country; } diff --git a/src/knowledgedb-generator/timezones.h b/src/knowledgedb-generator/timezones.h index cd6195f..65b6468 100644 --- a/src/knowledgedb-generator/timezones.h +++ b/src/knowledgedb-generator/timezones.h @@ -1,69 +1,61 @@ /* Copyright (c) 2017 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KITINERARY_GENERATOR_TIMEZONES #define KITINERARY_GENERATOR_TIMEZONES #include -#include -#include +#include + +#include +#include class QByteArray; -class QColor; namespace KItinerary { namespace Generator { class Timezones { public: Timezones(); ~Timezones(); QByteArray timezoneForLocation(const QString &isoCode, KnowledgeDb::Coordinate coord) const; // the offset into the timezone string table uint16_t offset(const QByteArray &tz) const; private: friend class TimezoneDbGenerator; - QPoint coordinateToPixel(KnowledgeDb::Coordinate coord) const; - QByteArray timezoneForPixel(int x, int y) const; void setCountryForZone(const QByteArray &tz, const QString &country); - mutable QImage m_map; - QHash m_colorMap; - double m_xMapUnitsPerPixel; - double m_yMapUnitsPerPixel; - double m_topLeftMapUnitX; - double m_topLeftMapUnitY; - std::vector m_zones; std::vector m_zoneOffsets; std::map> m_countryZones; std::map m_countryForZone; }; } } #endif diff --git a/src/knowledgedb-generator/timezones.xsl b/src/knowledgedb-generator/timezones.xsl deleted file mode 100644 index d9f32be..0000000 --- a/src/knowledgedb-generator/timezones.xsl +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - -