#include #include #include #include #include #include #include #include #include #include #pragma execution_character_set("utf-8") namespace LAMPMainWidget { TileDownloadTask::TileDownloadTask(TileInfo tile, tileReadyCallback cb, QObject* parent) : QObject(parent), mTile(std::move(tile)), mCallback(cb) { // 程序初始化时注册(如main函数或类构造函数) qRegisterMetaType("TileInfo"); } void TileDownloadTask::run() { Network web{}; QByteArray data = web.httpsRequest(mTile.url); mTile.data = data; if (mCallback) { mCallback(mTile); } else {} } 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()) { std::cout << u8"缓存数据库打开失败"; 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()) { std::cout << pos.x()<<","<" << select.lastError().text().toUtf8().constData() << std::endl; 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()) { std::cout << "瓦片数据为空"; 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()) { std::cout << pos.x() << "," << pos.y() << "=>" << sql.lastError().text().toUtf8().constData() << std::endl; 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); if (!QObject::connect(task, SIGNAL(tileReady(TileInfo)), this, SLOT(tileReady(TileInfo)))) { qDebug() << "连接失败:" << QObject::tr(qPrintable(Q_FUNC_INFO)); } QThreadPool::globalInstance()->start(task); */ // 回调函数版本 QPointer safeThis(this); auto* task = new TileDownloadTask(tile, [safeThis]( TileInfo info) { if (safeThis) { // 自动检测对象是否存活 QMetaObject::invokeMethod(safeThis.data(), [safeThis, info] { safeThis->tileReady(info); }, Qt::QueuedConnection); // 强制主线程执行[6](@ref) } }); 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); } }