2025-02-07 09:16:22 +00:00
|
|
|
|
#include <QImage>
|
|
|
|
|
#include <QPainter>
|
|
|
|
|
#include <QThreadPool>
|
|
|
|
|
#include <QDir>
|
|
|
|
|
#include <QSqlQuery>
|
|
|
|
|
#include <QSqlError>
|
|
|
|
|
|
2025-02-09 18:16:07 +00:00
|
|
|
|
#include <tmsprovider.h>
|
|
|
|
|
#include <network.h>
|
2025-02-07 09:16:22 +00:00
|
|
|
|
#pragma execution_character_set("utf-8")
|
|
|
|
|
|
|
|
|
|
namespace LAMPMainWidget
|
|
|
|
|
{
|
2025-04-01 10:23:24 +00:00
|
|
|
|
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<double>(i), static_cast<double>(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);
|
|
|
|
|
}
|
2025-02-07 09:16:22 +00:00
|
|
|
|
}
|