diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt index 04b5afb..aec7c77 100644 --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -1,31 +1,32 @@ add_definitions(-DSOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}") ecm_add_test(osmtypetest.cpp LINK_LIBRARIES Qt5::Test KOSM) ecm_add_test(o5mparsertest.cpp LINK_LIBRARIES Qt5::Test KOSM) ecm_add_test(indexeddatatabletest LINK_LIBRARIES Qt5::Test) ecm_add_test(mergeutiltest.cpp LINK_LIBRARIES Qt5::Test KPublicTransport) ecm_add_test(locationtest.cpp LINK_LIBRARIES Qt5::Test KPublicTransport) ecm_add_test(linetest.cpp LINK_LIBRARIES Qt5::Test KPublicTransport) ecm_add_test(departuretest.cpp LINK_LIBRARIES Qt5::Test KPublicTransport) ecm_add_test(journeytest.cpp LINK_LIBRARIES Qt5::Test KPublicTransport) ecm_add_test(platformtest.cpp LINK_LIBRARIES Qt5::Test KPublicTransport) ecm_add_test(notestest.cpp LINK_LIBRARIES Qt5::Test KPublicTransport) ecm_add_test(backendtest.cpp LINK_LIBRARIES Qt5::Test KPublicTransport) ecm_add_test(linemetadatatest.cpp LINK_LIBRARIES Qt5::Test KPublicTransport) ecm_add_test(navitiaparsertest.cpp LINK_LIBRARIES Qt5::Test KPublicTransport) ecm_add_test(hafasmgateparsertest.cpp LINK_LIBRARIES Qt5::Test KPublicTransport) ecm_add_test(hafasqueryparsertest.cpp LINK_LIBRARIES Qt5::Test KPublicTransport) ecm_add_test(efaparsertest.cpp LINK_LIBRARIES Qt5::Test KPublicTransport) ecm_add_test(deutschebahntest.cpp LINK_LIBRARIES Qt5::Test KPublicTransport) ecm_add_test(otpparsertest.cpp LINK_LIBRARIES Qt5::Test KPublicTransport) ecm_add_test(publictransportmanagertest.cpp LINK_LIBRARIES Qt5::Test KPublicTransport) ecm_add_test(cachetest.cpp LINK_LIBRARIES Qt5::Test KPublicTransport) if (TARGET KOSMIndoorMap) ecm_add_test(mapviewtest.cpp LINK_LIBRARIES Qt5::Test KOSMIndoorMap) ecm_add_test(mapcssparsertest.cpp LINK_LIBRARIES Qt5::Test KOSMIndoorMap) ecm_add_test(scenegeometrytest.cpp LINK_LIBRARIES Qt5::Test KOSMIndoorMap) + ecm_add_test(tilecachetest.cpp LINK_LIBRARIES Qt5::Test KOSMIndoorMap) endif() diff --git a/autotests/tilecachetest.cpp b/autotests/tilecachetest.cpp new file mode 100644 index 0000000..6484588 --- /dev/null +++ b/autotests/tilecachetest.cpp @@ -0,0 +1,56 @@ +/* + Copyright (C) 2020 Volker Krause + + This program 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 program 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 General Public License + along with this program. If not, see . +*/ + +#include + +#include + +using namespace KOSMIndoorMap; + +class TileCacheTest: public QObject +{ + Q_OBJECT +private Q_SLOTS: + void testTileFromCoordinate_data() + { + QTest::addColumn("z"); + QTest::addColumn("lat"); + QTest::addColumn("lon"); + QTest::addColumn("x"); + QTest::addColumn("y"); + + QTest::newRow("z17") << 17 << 52.5258 << 13.3684 << 70403 << 42982; + } + + void testTileFromCoordinate() + { + QFETCH(int, z); + QFETCH(double, lat); + QFETCH(double, lon); + QFETCH(int, x); + QFETCH(int, y); + + const auto tile = Tile::fromCoordinate(lat, lon, z); + QCOMPARE(tile.x, x); + QCOMPARE(tile.y, y); + QCOMPARE(tile.z, z); + } +}; + +QTEST_GUILESS_MAIN(TileCacheTest) + +#include "tilecachetest.moc" diff --git a/src/map/CMakeLists.txt b/src/map/CMakeLists.txt index 867e911..1ce75e9 100644 --- a/src/map/CMakeLists.txt +++ b/src/map/CMakeLists.txt @@ -1,77 +1,79 @@ flex_target(mapcssscanner style/mapcsslexer.l ${CMAKE_CURRENT_BINARY_DIR}/mapcsslexer.cpp DEFINES_FILE ${CMAKE_CURRENT_BINARY_DIR}/mapcssscanner.h COMPILE_FLAGS "--nounistd" ) bison_target(mapcssparser style/mapcssparser.y ${CMAKE_CURRENT_BINARY_DIR}/mapcssparser_p.cpp DEFINES_FILE ${CMAKE_CURRENT_BINARY_DIR}/mapcssparser_p.h ) add_flex_bison_dependency(mapcssscanner mapcssparser) add_library(KOSMIndoorMap STATIC assets/assets.qrc loader/mapdata.cpp loader/maploader.cpp + loader/tilecache.cpp renderer/painterrenderer.cpp renderer/view.cpp scene/scenecontroller.cpp scene/scenegeometry.cpp scene/scenegraph.cpp scene/scenegraphitem.cpp style/mapcsscondition.cpp style/mapcssdeclaration.cpp style/mapcssparser.cpp style/mapcssresult.cpp style/mapcssrule.cpp style/mapcssselector.cpp style/mapcssstate.cpp style/mapcssstyle.cpp ${BISON_mapcssparser_OUTPUTS} ${FLEX_mapcssscanner_OUTPUTS} ) target_include_directories(KOSMIndoorMap PRIVATE $) target_include_directories(KOSMIndoorMap PUBLIC $) target_link_libraries(KOSMIndoorMap PUBLIC Qt5::Gui KOSM + PRIVATE Qt5::Network ) ecm_generate_headers(KOSMIndoorMap_Loader_FORWARDING_HEADERS HEADER_NAMES MapLoader MapData PREFIX KOSMIndoorMap REQUIRED_HEADERS KOSMIndoorMap_Loader_HEADERS RELATIVE loader ) ecm_generate_headers(KOSMIndoorMap_Renderer_FORWARDING_HEADERS HEADER_NAMES PainterRenderer View PREFIX KOSMIndoorMap REQUIRED_HEADERS KOSMIndoorMap_Renderer_HEADERS RELATIVE renderer ) ecm_generate_headers(KOSMIndoorMap_Scene_FORWARDING_HEADERS HEADER_NAMES SceneController SceneGraph PREFIX KOSMIndoorMap REQUIRED_HEADERS KOSMIndoorMap_Scene_HEADERS RELATIVE scene ) ecm_generate_headers(KOSMIndoorMap_Style_FORWARDING_HEADERS HEADER_NAMES MapCSSParser MapCSSStyle PREFIX KOSMIndoorMap REQUIRED_HEADERS KOSMIndoorMap_Style_HEADERS RELATIVE style ) diff --git a/src/map/loader/tilecache.cpp b/src/map/loader/tilecache.cpp new file mode 100644 index 0000000..60059a4 --- /dev/null +++ b/src/map/loader/tilecache.cpp @@ -0,0 +1,100 @@ +/* + Copyright (C) 2020 Volker Krause + + This program 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 program 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 General Public License + along with this program. If not, see . +*/ + +#include "tilecache.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace KOSMIndoorMap; + +Tile Tile::fromCoordinate(double lat, double lon, uint8_t z) +{ + Tile t; + t.x = std::floor((lon + 180.0) / 360.0 * (1 << z)); + const auto latrad = OSM::degToRad(lat); + t.y = std::floor((1.0 - std::asinh(std::tan(latrad)) / M_PI) / 2.0 * (1 << z)); + t.z = z; + return t; +} + +TileCache::TileCache(QObject *parent) + : QObject(parent) + , m_nam(new QNetworkAccessManager(this)) +{ + m_nam->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy); + m_nam->enableStrictTransportSecurityStore(true, QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1String("/org.kde.osm/hsts/")); + m_nam->setStrictTransportSecurityEnabled(true); +} + +TileCache::~TileCache() = default; + +QString TileCache::cachedTile(Tile tile) const +{ + const auto p = cachePath(tile); + if (QFile::exists(p)) { + return p; + } + return {}; +} + +void TileCache::downloadTile(Tile tile) +{ + // TODO queue multiple requests and process at most N in parallel + + QUrl url; + url.setScheme(QStringLiteral("https")); + url.setHost(QStringLiteral("maps.kde.org")); + url.setPath(QLatin1String("/earth/vectorosm/v1/") + QString::number(tile.z) + QLatin1Char('/') + QString::number(tile.x) + QLatin1Char('/') + QString::number(tile.y) + QLatin1String(".o5m")); + + // TODO stream incoming data to the final destination (or a file with a .part suffix to avoid reads while downlaoding) + + QNetworkRequest req(url); + auto reply = m_nam->get(req); + qDebug() << reply << url; + connect(reply, &QNetworkReply::finished, this, [this, reply, tile]() { + reply->deleteLater(); + qDebug() << reply->errorString() << reply->url(); + + QFileInfo fi(cachePath(tile)); + QDir().mkpath(fi.absolutePath()); + QFile f(fi.absoluteFilePath()); + if (!f.open(QFile::WriteOnly)) { + qWarning() << f.fileName() << f.errorString(); + return; + } + f.write(reply->readAll()); + }); +} + +QString TileCache::cachePath(Tile tile) const +{ + return QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + + QLatin1String("/org.kde.osm/vectorosm/") + + QString::number(tile.z) + QLatin1Char('/') + + QString::number(tile.x) + QLatin1Char('/') + + QString::number(tile.y) + QLatin1String(".o5m"); +} diff --git a/src/map/loader/tilecache.h b/src/map/loader/tilecache.h new file mode 100644 index 0000000..d7461b5 --- /dev/null +++ b/src/map/loader/tilecache.h @@ -0,0 +1,62 @@ +/* + Copyright (C) 2020 Volker Krause + + This program 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 program 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 General Public License + along with this program. If not, see . +*/ + +#ifndef KOSMINDOORMAP_TILECACHE_H +#define KOSMINDOORMAP_TILECACHE_H + +#include + +class QNetworkAccessManager; + +namespace KOSMIndoorMap { + +/** Identifier of a slippy map tile. + * @see https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames + */ +class Tile +{ +public: + static Tile fromCoordinate(double lat, double lon, uint8_t z); + + uint32_t x = 0; + uint32_t y = 0; + uint8_t z = 0; +}; + +/** OSM vector tile downloading and cache management. */ +class TileCache : public QObject +{ + Q_OBJECT +public: + explicit TileCache(QObject *parent = nullptr); + ~TileCache(); + + /** Returns the path to the cached content of @p tile, if present locally. */ + QString cachedTile(Tile tile) const; + + /** Triggers the download of tile @p tile. */ + void downloadTile(Tile tile); + +private: + QString cachePath(Tile tile) const; + + QNetworkAccessManager *m_nam; +}; + +} + +#endif // KOSMINDOORMAP_TILECACHE_H