void MainWindow::updateCoordinates(double lat, double lon)
{
QQuickWidget *quickWidget = ui->quickWidget;
if (!quickWidget) return;
QQuickItem *rootObject = quickWidget->rootObject();
if (!rootObject) return;
QVariantMap coord;
coord[“latitude”] = lat;
coord[“longitude”] = lon;
rootObject->setProperty(“centerCoordinate”, coord);
QMetaObject::invokeMethod(rootObject, “updateVisibleTiles”);
}
//QML
import QtQuick 2.15
import QtQuick.Controls 2.15
Item {
id: root
width: 640
height: 480
property var centerCoordinate: { “latitude”: 36.35, “longitude”: 117.0 }
property int tileSize: 512
property int zoomLevel: 16
property string tileRoot: “file:///home/orangepi/Layer”
Flickable {
id: flick
anchors.fill: parent
clip: true
interactive: true
contentWidth: 20000
contentHeight: 20000
Item {
id: tileContainer
width: flick.contentWidth
height: flick.contentHeight
property var tilesCache: ({}) // 缓存瓦片对象
}
Rectangle {
id: redDot
width: 12
height: 12
color: “red”
radius: 6
anchors.centerIn: parent
}
onContentXChanged: updateVisibleTiles()
onContentYChanged: updateVisibleTiles()
}
function updateVisibleTiles() {
if (!tileContainer || !root.centerCoordinate) return;
var zoom = root.zoomLevel;
var n = Math.pow(2, zoom);
var center = root.centerCoordinate;
// 中心瓦片浮点坐标
var centerTileXf = (center.longitude + 180) / 360 * n;
var centerTileYf = (1 – Math.log(Math.tan(center.latitude*Math.PI/180) + 1/Math.cos(center.latitude*Math.PI/180)) / Math.PI) / 2 * n;
var tilesX = Math.ceil(flick.width / root.tileSize) + 2;
var tilesY = Math.ceil(flick.height / root.tileSize) + 2;
var startX = Math.floor(centerTileXf – tilesX/2);
var startY = Math.floor(centerTileYf – tilesY/2);
// 记录当前视野瓦片 key
var currentKeys = {};
for (var x = startX; x < startX + tilesX; x++) {
for (var y = startY; y < startY + tilesY; y++) {
var key = x + "_" + y;
currentKeys[key] = true;
var path = root.tileRoot + "/" + zoom + "/" + x + "/" + y + ".png";
if (!tileContainer.tilesCache[key]) {
var tile = Qt.createQmlObject(
'import QtQuick 2.15; Image { width:' + root.tileSize +
'; height:' + root.tileSize + '; source: "' + path + '"; fillMode: Image.PreserveAspectFit }',
tileContainer
);
tileContainer.tilesCache[key] = tile;
}
// 更新位置
var tileObj = tileContainer.tilesCache[key];
tileObj.x = (x - startX) * root.tileSize;
tileObj.y = (y - startY) * root.tileSize;
}
}
// 删除超出视野的瓦片
for (var key in tileContainer.tilesCache) {
if (!currentKeys[key]) {
tileContainer.tilesCache[key].destroy();
delete tileContainer.tilesCache[key];
}
}
// 移动 tileContainer,使中心瓦片在 Flickable 中心
var offsetX = (centerTileXf - startX) * root.tileSize - flick.width/2;
var offsetY = (centerTileYf - startY) * root.tileSize - flick.height/2;
tileContainer.x = -offsetX;
tileContainer.y = -offsetY;
// 红点始终在视口中心
redDot.x = flick.width / 2 - redDot.width/2;
redDot.y = flick.height / 2 - redDot.height/2;
}
Component.onCompleted: updateVisibleTiles()
}
