From d200dbdab5dcebec41f2c0f2906c1be88c6f0d31 Mon Sep 17 00:00:00 2001 From: peri4 Date: Sat, 11 Nov 2023 23:01:21 +0300 Subject: [PATCH] version 2.21.1 Map download API mapviewer download feature --- CMakeLists.txt | 2 +- libs/map/mapview.cpp | 88 +++++++++++++++++++++- libs/map/mapview.h | 9 ++- libs/map/osm_downloader.cpp | 23 +++++- libs/map/osm_downloader_p.h | 6 +- libs/map/osm_geocoding.cpp | 2 +- libs/map/osm_geocoding.h | 2 +- libs/map/osm_tile_cache.cpp | 15 ++-- libs/map/osm_tile_cache_p.h | 3 + utils/mapviewer/CMakeLists.txt | 2 +- utils/mapviewer/main.cpp | 64 +--------------- utils/mapviewer/mainwindow.cpp | 132 +++++++++++++++++++++++++++++++++ utils/mapviewer/mainwindow.h | 27 +++++++ utils/mapviewer/mainwindow.ui | 75 +++++++++++++++++++ 14 files changed, 373 insertions(+), 77 deletions(-) create mode 100644 utils/mapviewer/mainwindow.cpp create mode 100644 utils/mapviewer/mainwindow.h create mode 100644 utils/mapviewer/mainwindow.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index a53b752..aa1cd8b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_policy(SET CMP0072 NEW) # FindOpenGL prefers GLVND by default project(QAD) set(QAD_MAJOR 2) set(QAD_MINOR 21) -set(QAD_REVISION 0) +set(QAD_REVISION 1) set(QAD_SUFFIX ) set(QAD_COMPANY SHS) set(QAD_DOMAIN org.SHS) diff --git a/libs/map/mapview.cpp b/libs/map/mapview.cpp index 62ada04..ab250d8 100644 --- a/libs/map/mapview.cpp +++ b/libs/map/mapview.cpp @@ -7,8 +7,10 @@ #include #include +#include #include #include +#include #include #include @@ -19,7 +21,9 @@ MapView::MapView(QWidget * parent): QWidget(parent) { downloader = new OSMDownloader(this); cache = new OSMTileCache(this); setMouseTracking(true); - connect(cache, &OSMTileCache::tileReady, this, [this]() { drawBackground(); }); + connect(cache, &OSMTileCache::tileReady, this, [this]() { + if (!is_downloading) drawBackground(); + }); int size = 16; QPixmap px(QSize(size, size) * 2); QPainter p(&px); @@ -77,6 +81,88 @@ void MapView::setZoom(double z) { } +QString MapView::cachePath() const { + return cache->cacheRoot(); +} + + +void MapView::setCachePath(const QString & p) { + cache->setCacheRoot(p); + drawBackground(); +} + + +void collectDownloadIndeces(QQueue & indeces, OSM::TileIndex index, int target_zoom_level) { + indeces << index; + if (index.z >= target_zoom_level) return; + int z = index.z + 1; + int x = index.x * 2; + int y = index.y * 2; + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 2; ++j) { + collectDownloadIndeces(indeces, (OSM::TileIndex){z, x + i, y + j}, target_zoom_level); + } + } +} + + +void MapView::downloadCurrentView(int target_zoom_level) { + int sx = qMax(0, (int)floor(view_rect.left() * tiles_side)); + int sy = qMax(0, (int)floor(view_rect.top() * tiles_side)); + int ex = qMin(tiles_side, (int)ceil(view_rect.right() * tiles_side)); + int ey = qMin(tiles_side, (int)ceil(view_rect.bottom() * tiles_side)); + target_zoom_level = qMin(target_zoom_level, max_level); + QQueue indeces; + for (int ix = sx; ix < ex; ++ix) { + for (int iy = sy; iy < ey; ++iy) { + collectDownloadIndeces(indeces, (OSM::TileIndex){zoom_level, ix, iy}, target_zoom_level); + } + } + if (indeces.size() < 4) return; + QProgressDialog dialog; + dialog.setWindowTitle(tr("MapView")); + dialog.setLabelText(tr("Downloading ...")); + dialog.setCancelButtonText(tr("Cancel")); + dialog.setMaximum(indeces.size()); + dialog.setValue(0); + downloader->clearQueue(); + std::atomic_int value(0); + auto conn = connect( + downloader, + &OSMDownloader::tileDone, + this, + [this, &dialog, &indeces, &value]() { + // qDebug() << "done"; + value++; + int cv = value; + dialog.setValue(cv); + if (indeces.isEmpty()) + dialog.cancel(); + else { + downloader->queueTile(indeces.dequeue(), true); + } + }, + Qt::DirectConnection); + connect(&dialog, &QProgressDialog::canceled, this, [this, &dialog]() { + dialog.cancel(); + downloader->clearQueue(); + }); + is_downloading = true; + for (int i = 0; i < 1; ++i) + downloader->queueTile(indeces.dequeue()); + // dialog.exec(); + dialog.setModal(true); + dialog.show(); + while (!dialog.isHidden()) { + QThread::msleep(1); + QApplication::processEvents(); + } + disconnect(conn); + is_downloading = false; + drawBackground(); +} + + void MapView::mousePressEvent(QMouseEvent * e) { is_pan = false; press_point = e->pos(); diff --git a/libs/map/mapview.h b/libs/map/mapview.h index 4c9558c..33888a5 100644 --- a/libs/map/mapview.h +++ b/libs/map/mapview.h @@ -51,11 +51,18 @@ public: void setCenter(QPointF c); void setCenter(QGeoCoordinate c) { setCenter(QPointF(c.latitude(), c.longitude())); } + int getCurrentZoomLevel() const { return zoom_level; } + int getMaximumZoomLevel() const { return max_level; } double getZoom() const { return zoom_; } void setZoom(double z); QPointF clickedCoordinate() const { return last_click_coord; } + QString cachePath() const; + void setCachePath(const QString & p); + + void downloadCurrentView(int target_zoom_level = 17); + protected: QSize sizeHint() const override { return QSize(200, 200); } void mousePressEvent(QMouseEvent * e) override; @@ -90,7 +97,7 @@ private: QRectF view_rect; QVector items_; QBrush brush_tr; - bool is_pan = false; + bool is_pan = false, is_downloading = false; double zoom_ = 1., scale_ = 1., px2m = 1.; int zoom_level = 0, tiles_side = 1, max_level = 19; diff --git a/libs/map/osm_downloader.cpp b/libs/map/osm_downloader.cpp index 5229ed6..f7fab2b 100644 --- a/libs/map/osm_downloader.cpp +++ b/libs/map/osm_downloader.cpp @@ -20,14 +20,32 @@ OSMDownloader::~OSMDownloader() { } -void OSMDownloader::queueTile(OSM::TileIndex index) { +bool OSMDownloader::queueTile(OSM::TileIndex index, bool force) { // auto hash = tile.hash(); + bool ret = false; cond_mutex.lock(); - if (!queue.contains(index) && !in_progress.contains(index.hash())) { + if ((!queue.contains(index) && !in_progress.contains(index.hash())) || force) { queue.enqueue(index); cond.wakeOne(); + ret = true; } cond_mutex.unlock(); + return ret; +} + + +void OSMDownloader::queueTiles(QList indeces) { + cond_mutex.lock(); + queue << indeces; + cond.wakeOne(); + cond_mutex.unlock(); +} + + +void OSMDownloader::clearQueue() { + cond_mutex.lock(); + queue.clear(); + cond_mutex.unlock(); } @@ -51,6 +69,7 @@ void OSMDownloader::requestTile(OSM::TileIndex index) { cond_mutex.lock(); in_progress.remove(index.hash()); cond_mutex.unlock(); + emit tileDone(); }); } diff --git a/libs/map/osm_downloader_p.h b/libs/map/osm_downloader_p.h index 18fd9cd..bc9b0bc 100644 --- a/libs/map/osm_downloader_p.h +++ b/libs/map/osm_downloader_p.h @@ -40,8 +40,9 @@ public: explicit OSMDownloader(MapView * p); ~OSMDownloader(); - void queueTile(OSM::TileIndex index); - + bool queueTile(OSM::TileIndex index, bool force = false); + void queueTiles(QList indeces); + void clearQueue(); private: void run() override; @@ -60,6 +61,7 @@ public slots: private slots: signals: + void tileDone(); }; diff --git a/libs/map/osm_geocoding.cpp b/libs/map/osm_geocoding.cpp index 40977c4..00fc346 100644 --- a/libs/map/osm_geocoding.cpp +++ b/libs/map/osm_geocoding.cpp @@ -37,7 +37,7 @@ QPointF OSMGeocodingResult::resultCoordinate() const { void OSMGeocodingResult::requestDone() { - emit ready(); + emit ready(this); deleteLater(); } diff --git a/libs/map/osm_geocoding.h b/libs/map/osm_geocoding.h index 9b9e819..4ab65a0 100644 --- a/libs/map/osm_geocoding.h +++ b/libs/map/osm_geocoding.h @@ -52,7 +52,7 @@ private: QNetworkReply * reply = nullptr; signals: - void ready(); + void ready(OSMGeocodingResult *); }; diff --git a/libs/map/osm_tile_cache.cpp b/libs/map/osm_tile_cache.cpp index cd8b34f..8252c14 100644 --- a/libs/map/osm_tile_cache.cpp +++ b/libs/map/osm_tile_cache.cpp @@ -7,11 +7,8 @@ OSMTileCache::OSMTileCache(MapView * p): QThread() { qRegisterMetaType(); qRegisterMetaType(); - parent = p; - cache_root = QAD::userPath(QAD::ltCache, "map_osm") + "/"; - cache_dir.setPath(cache_root); - qDebug() << "[OSMTileCache] save cache to" << cache_root; - if (!cache_dir.exists()) cache_dir.mkpath("."); + parent = p; + setCacheRoot(QAD::userPath(QAD::ltCache, "map_osm") + "/"); setAdditionalCacheSize(64); start(); } @@ -57,6 +54,14 @@ OSM::TilePixmap OSMTileCache::getTile(OSM::TileIndex index, QRectF & rect_src) { } +void OSMTileCache::setCacheRoot(const QString & p) { + cache_root = p; + cache_dir.setPath(cache_root); + qDebug() << "[OSMTileCache] Cache dir" << cache_root; + if (!cache_dir.exists()) cache_dir.mkpath("."); +} + + OSM::TilePixmap OSMTileCache::getTileFromCache(OSM::TileIndex index) { OSM::TilePixmap ret; ret.index = index; diff --git a/libs/map/osm_tile_cache_p.h b/libs/map/osm_tile_cache_p.h index ba0b306..a008a31 100644 --- a/libs/map/osm_tile_cache_p.h +++ b/libs/map/osm_tile_cache_p.h @@ -50,6 +50,9 @@ public: void tileDownloaded(OSM::TileIndex index, const QPixmap & pixmap); OSM::TilePixmap getTile(OSM::TileIndex index, QRectF & rect_src); + QString cacheRoot() const { return cache_root; } + void setCacheRoot(const QString & p); + private: OSM::TilePixmap getTileFromCache(OSM::TileIndex index); void updateCacheSize(); diff --git a/utils/mapviewer/CMakeLists.txt b/utils/mapviewer/CMakeLists.txt index 6d5a248..81720e8 100644 --- a/utils/mapviewer/CMakeLists.txt +++ b/utils/mapviewer/CMakeLists.txt @@ -10,6 +10,6 @@ if (PIP_FOUND) set(APP_ICON "icons/maps.png") endif() set(APP_INFO "Map viewer") - qad_application(${PROJECT_NAME} "Gui;Widgets" "qad_map") + qad_application(${PROJECT_NAME} "Gui;Widgets" "qad_map;qad_application") endif() diff --git a/utils/mapviewer/main.cpp b/utils/mapviewer/main.cpp index b8ceb74..4924fe8 100644 --- a/utils/mapviewer/main.cpp +++ b/utils/mapviewer/main.cpp @@ -1,72 +1,12 @@ -#include "mapitemellipse.h" -#include "mapitemgeopolygon.h" -#include "mapitemgeopolyline.h" -#include "mapitemimage.h" -#include "mapitempolygon.h" -#include "mapitemtext.h" -#include "mapview.h" +#include "mainwindow.h" #include "qad_types.h" #include -#include -#include -#include -#include int main(int argc, char * argv[]) { QApplication a(argc, argv); enableHighDPI(); - MapView w; - w.resize(800, 600); + MainWindow w; w.show(); - /*QVector g = {QPointF(55.583055, 37.580008), QPointF(55.583055, 37.590008), QPointF(55.593055, 37.580008)}; - QVector p = {QPointF(0, 0), QPointF(200, 0), QPointF(0, 100)}; - MapItemPolygon * pol = new MapItemPolygon(QPolygonF(p)); - MapItemGeoPolyline * gpol = new MapItemGeoPolyline(QPolygonF(g)); - MapItemEllipse * ell = new MapItemEllipse(QPointF(), 50, 50); - MapItemImage * im = new MapItemImage(QPixmap(":/icons/maps.png")); - MapItemText * it = new MapItemText(QString::fromUtf8("Это Ваня!")); - im->setPosition({55.583055, 37.580008}); - im->setScale(0.2, 0.5); - im->setAlignment(Qt::AlignRight | Qt::AlignTop); - im->setCursor(Qt::PointingHandCursor); - im->setInteracive(true); - it->setPosition({55.583055, 37.580008}); - it->setBrush(QColor(127, 0, 0, 127)); - it->setPen(QColor(64, 255, 64)); - it->setFont(QFont("times", 18)); - it->setCursor(Qt::OpenHandCursor); - it->setInteracive(true); - pol->setPosition({55.583055, 37.580008}); - pol->setUnits(MapItemNonGeoGeometryBase::Pixels); - gpol->setBrush(QColor(0, 0, 255, 64)); - gpol->setPen(QPen(QColor(64, 64, 255), 3)); - ell->setPosition({55.583055, 37.580008}); - ell->setStartAngle(-20); - ell->setEndAngle(20); - ell->setEllipse(QPointF(100, 0), 50, 50); - ell->setInteracive(true); - QTimer t; - QObject::connect(&w, &MapView::itemClicked, [](MapItemBase * item) { qDebug() << "click" << item; }); - QObject::connect(&w, &MapView::itemEntered, [](MapItemBase * item) { qDebug() << "enter" << item; }); - QObject::connect(&w, &MapView::itemLeaved, [](MapItemBase * item) { qDebug() << "leave" << item; }); - - QObject::connect(&t, &QTimer::timeout, [im, it, pol, ell]() { - im->rotate(1); - it->rotate(-0.1); - // pol->rotate(0.2); - ell->rotate(-0.2); - static double t = 0.; - t += 0.025; - ell->setScale((sin(t) / 2. + 1.)); - }); - t.start(100); - w.addItem(im); - w.addItem(gpol); - w.addItem(pol); - w.addItem(it); - w.addItem(ell);*/ - w.centerTo({55.583055, 37.580008}); - w.zoomTo(2); return a.exec(); } diff --git a/utils/mapviewer/mainwindow.cpp b/utils/mapviewer/mainwindow.cpp new file mode 100644 index 0000000..c835c25 --- /dev/null +++ b/utils/mapviewer/mainwindow.cpp @@ -0,0 +1,132 @@ +#include "mainwindow.h" + +#include "mapitemellipse.h" +#include "mapitemgeopolygon.h" +#include "mapitemgeopolyline.h" +#include "mapitemimage.h" +#include "mapitempolygon.h" +#include "mapitemtext.h" +#include "osm_geocoding.h" +#include "piqt.h" +#include "qad_locations.h" + +#include +#include +#include +#include +#include + +MainWindow::MainWindow(QWidget * parent): EMainWindow(parent), Ui::MainWindow() { + setupUi(this); + session.setFile(QAD::userPath(QAD::ltConfig, "session_mapviewer")); + session.addEntry(this); + session.load(); + /*QVector g = {QPointF(55.583055, 37.580008), QPointF(55.583055, 37.590008), QPointF(55.593055, 37.580008)}; + QVector p = {QPointF(0, 0), QPointF(200, 0), QPointF(0, 100)}; + MapItemPolygon * pol = new MapItemPolygon(QPolygonF(p)); + MapItemGeoPolyline * gpol = new MapItemGeoPolyline(QPolygonF(g)); + MapItemEllipse * ell = new MapItemEllipse(QPointF(), 50, 50); + MapItemImage * im = new MapItemImage(QPixmap(":/icons/maps.png")); + MapItemText * it = new MapItemText(QString::fromUtf8("Это Ваня!")); + im->setPosition({55.583055, 37.580008}); + im->setScale(0.2, 0.5); + im->setAlignment(Qt::AlignRight | Qt::AlignTop); + im->setCursor(Qt::PointingHandCursor); + im->setInteracive(true); + it->setPosition({55.583055, 37.580008}); + it->setBrush(QColor(127, 0, 0, 127)); + it->setPen(QColor(64, 255, 64)); + it->setFont(QFont("times", 18)); + it->setCursor(Qt::OpenHandCursor); + it->setInteracive(true); + pol->setPosition({55.583055, 37.580008}); + pol->setUnits(MapItemNonGeoGeometryBase::Pixels); + gpol->setBrush(QColor(0, 0, 255, 64)); + gpol->setPen(QPen(QColor(64, 64, 255), 3)); + ell->setPosition({55.583055, 37.580008}); + ell->setStartAngle(-20); + ell->setEndAngle(20); + ell->setEllipse(QPointF(100, 0), 50, 50); + ell->setInteracive(true); + QTimer t; + QObject::connect(&w, &MapView::itemClicked, [](MapItemBase * item) { qDebug() << "click" << item; }); + QObject::connect(&w, &MapView::itemEntered, [](MapItemBase * item) { qDebug() << "enter" << item; }); + QObject::connect(&w, &MapView::itemLeaved, [](MapItemBase * item) { qDebug() << "leave" << item; }); + + QObject::connect(&t, &QTimer::timeout, [im, it, pol, ell]() { + im->rotate(1); + it->rotate(-0.1); + // pol->rotate(0.2); + ell->rotate(-0.2); + static double t = 0.; + t += 0.025; + ell->setScale((sin(t) / 2. + 1.)); + }); + t.start(100); + map->addItem(im); + map->addItem(gpol); + map->addItem(pol); + map->addItem(it); + map->addItem(ell); + map->centerTo({55.583055, 37.580008}); + map->zoomTo(2); +*/ +} + + +MainWindow::~MainWindow() { + session.save(); + session.clear(true); +} + + +void MainWindow::changeEvent(QEvent * e) { + QMainWindow::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: retranslateUi(this); break; + default: break; + } +} + + +void MainWindow::loadingSession(QPIConfig & conf) { + map->setCenter(conf.getValue("center", map->center()).toPointF()); + map->setZoom(conf.getValue("zoom", map->getZoom()).toDouble()); +} + + +void MainWindow::savingSession(QPIConfig & conf) { + conf.setValue("center", map->center()); + conf.setValue("zoom", map->getZoom()); +} + + +void MainWindow::on_map_mapClicked(QPointF c) { + connect(OSMGeocoding::queue(c), &OSMGeocodingResult::ready, [this](OSMGeocodingResult * res) { + labelLocation->setText(res->displayName().trimmed()); + }); +} + + +void MainWindow::on_buttonDownload_clicked() { + static QString dir; // = map->cachePath(); + auto d = QFileDialog::getExistingDirectory(nullptr, tr("Select directory"), dir); + if (d.isEmpty()) return; + dir = d; + bool ok = false; + int maxzl = map->getMaximumZoomLevel(); + int curzl = map->getCurrentZoomLevel(); + int tzl = QInputDialog::getInt(nullptr, + windowTitle(), + tr("Select maximum zoom level:"), + qMin(maxzl, curzl + 3), + qMin(maxzl, curzl + 1), + maxzl, + 1, + &ok); + if (!ok) return; + auto prevcd = map->cachePath(); + map->setCachePath(dir); + map->downloadCurrentView(tzl); + map->setCachePath(prevcd); +} diff --git a/utils/mapviewer/mainwindow.h b/utils/mapviewer/mainwindow.h new file mode 100644 index 0000000..81f06d7 --- /dev/null +++ b/utils/mapviewer/mainwindow.h @@ -0,0 +1,27 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include "emainwindow.h" +#include "ui_mainwindow.h" + + +class MainWindow + : public EMainWindow + , private Ui::MainWindow { + Q_OBJECT + +public: + MainWindow(QWidget * parent = 0); + ~MainWindow(); + +protected: + void changeEvent(QEvent * e) override; + void loadingSession(QPIConfig & conf) override; + void savingSession(QPIConfig & conf) override; + +private slots: + void on_map_mapClicked(QPointF c); + void on_buttonDownload_clicked(); +}; + +#endif diff --git a/utils/mapviewer/mainwindow.ui b/utils/mapviewer/mainwindow.ui new file mode 100644 index 0000000..9d3d6b9 --- /dev/null +++ b/utils/mapviewer/mainwindow.ui @@ -0,0 +1,75 @@ + + + MainWindow + + + + 0 + 0 + 822 + 654 + + + + Map Viewer + + + + :/icons/maps.png:/icons/maps.png + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + Click on map + + + true + + + + + + + Download current view ... + + + + :/icons/document-save-as.png:/icons/document-save-as.png + + + + + + + + + + MapView + QWidget +
mapview.h
+
+
+ + + + + +