#include #include #include #include #include #include #include #include #pragma execution_character_set("utf-8") namespace LAMPMainWidget { TileDownloadTask::TileDownloadTask(TileInfo tile, QObject* parent) : QObject(parent), mTile(std::move(tile)) { } void TileDownloadTask::run() { Network web{}; QByteArray data = web.httpsRequest(mTile.url); mTile.data = data; tileReady(mTile); } TmsProvider::TmsProvider(QObject* parent) : LayerProvider(parent), mImage(nullptr) { if (!QFile::exists(mDbName)) { QFile dbFile{mDbName}; dbFile.open(QIODevice::ReadWrite); dbFile.close(); } } TmsProvider::~TmsProvider() { delete mImage; } bool TmsProvider::initCache() { mDbConn = QSqlDatabase::addDatabase("QSQLITE", id()); mDbConn.setDatabaseName(mDbName); if (!mDbConn.open()) { qCritical() << "缓存数据库打开失败"; return false; } QSqlQuery sql{mDbConn}; if (!mDbConn.tables().contains(mTableName)) { sql.prepare(QString("create table %1 (" "id integer primary key autoincrement," "provider text not null," "zoom integer not null," "position text not null," "data blob not null" ")").arg(mTableName)); if (!sql.exec()) { qCritical() << "缓存表创建失败" << sql.lastError().text(); return false; } } mDbConn.close(); return true; } QByteArray TmsProvider::getCache(const QPoint& pos, int zoom) { QByteArray res{}; mDbConn.open(); QSqlQuery select{mDbConn}; select.prepare(QString("select data from %1 " "where provider = :provider " "and zoom = :zoom " "and position = :position " "order by id desc limit 1" ).arg(mTableName)); select.bindValue(":provider", id()); select.bindValue(":zoom", zoom); select.bindValue(":position", QString("%1:%2").arg(pos.x()).arg(pos.y())); if (!select.exec()) { qDebug() << pos << "查询失败=>" << select.lastError().text(); mDbConn.close(); return res; } if (!select.seek(0)) { mDbConn.close(); return res; } res = select.value(0).toByteArray(); mDbConn.close(); return res; } bool TmsProvider::addCache(const QPoint& pos, int zoom, QByteArray data) { if (data.isEmpty()) { qWarning() << "瓦片数据为空"; return false; } mDbConn.open(); QSqlQuery sql{mDbConn}; sql.prepare(QString{ "insert into %1 (provider, zoom, position, data)" "values( :provider, :zoom, :position, :data)" }.arg(mTableName)); sql.bindValue(":tbname", mTableName); sql.bindValue(":provider", id()); sql.bindValue(":zoom", zoom); sql.bindValue(":position", QString("%1:%2").arg(pos.x()).arg(pos.y())); sql.bindValue(":data", data); if (!sql.exec()) { qCritical() << pos << "=>" << sql.lastError().text(); mDbConn.close(); return false; } mDbConn.close(); return true; } bool TmsProvider::cacheContains(const QPoint& pos, int zoom) { QByteArray res = getCache(pos, zoom); return !res.isEmpty(); } void TmsProvider::createTask(const QRectF& rect, int zoom) { newImage(rect); /// 创建下载瓦片任务 const QSize sz = tileSize(); int xMin = qFloor(rect.topLeft().x() / sz.width()); double xOffset = rect.topLeft().x() / sz.width() - xMin; int xMax = qFloor(rect.bottomRight().x() / sz.width()); int yMin = qFloor(rect.topLeft().y() / sz.height()); double yOffset = rect.topLeft().y() / sz.height() - yMin; int yMax = qFloor(rect.bottomRight().y() / sz.height()); for (int i = xMin; i <= xMax; ++i) { for (int j = yMin; j <= yMax; ++j) { TileInfo tile{}; tile.index = QPointF{i - xMin - xOffset, j - yMin - yOffset}; tile.position = QPoint{i, j}; tile.zoom = zoom; tile.coord = QPoint{i - xMin, j - yMin}; tile.url = tileUrl(PointXY{static_cast(i), static_cast(j)}, zoom); auto tileData = getCache(tile.position, tile.zoom); if (!tileData.isEmpty()) { tile.data = tileData; tileReady(tile); continue; } auto* task = new TileDownloadTask(tile); QObject::connect(task, &TileDownloadTask::tileReady, this, &TmsProvider::tileReady); QThreadPool::globalInstance()->start(task); } } } void TmsProvider::tileReady(TileInfo tile) { if (!cacheContains(tile.position, tile.zoom)) { addCache(tile.position, tile.zoom, tile.data); } QPainter painter{mImage}; QImage img = QImage::fromData(tile.data); if (img.isNull()) { return; } double xPos = tile.index.x() * tileSize().width(); double yPos = tile.index.y() * tileSize().height(); painter.drawImage(QPointF(xPos, yPos), img); } void TmsProvider::newImage(const QRectF& rect) { QSize imgSize{int(rect.width()), int(rect.height())}; if (!mImage || imgSize != mImage->size()) { mImage = new QImage(imgSize, QImage::Format_RGB32); } mImage->fill(Qt::white); } }