import QtQuick 2.15
import QtQuick.Controls 2.15
import QtPositioning 5.15
Item {
id: root
width: 640
height: 480
property var baseCoordinate: { “latitude”: 36.35, “longitude”: 117.0 }
property var centerCoordinate: { “latitude”: 36.50, “longitude”: 117.2 } // 中心坐标
property int tileSize: 512
property int zoomLevel: 16
property string tileRoot: “file:///home/orangepi/Layer”
property var tilesCache: ({}) // 缓存瓦片对象
// 外部传入的旋转角度(单位:度)
property real markerAngle: 45
// 猴子标记
Image {
id: monkeyMarker
width: 30
height: 30
source: “qrc:/monkey.png”
z: 10
transformOrigin: Item.Center // 旋转中心在图片中点
rotation: root.markerAngle // 使用外部传入的角度
}
// 画红线
Canvas {
id: lineCanvas
anchors.fill: parent
z: 15
onPaint: {
var ctx = getContext(“2d”);
ctx.clearRect(0, 0, width, height);
ctx.lineWidth = 1;
ctx.strokeStyle = “red”;
var p1 = getPixelPos(root.baseCoordinate);
var p2 = getPixelPos(root.centerCoordinate);
ctx.beginPath();
ctx.moveTo(p1.x, p1.y);
ctx.lineTo(p2.x, p2.y);
ctx.stroke();
}
}
Flickable {
id: flick
anchors.fill: parent
clip: true
interactive: true
contentWidth: 20000
contentHeight: 20000
Item {
id: tileContainer
width: flick.contentWidth
height: flick.contentHeight
}
}
// 经纬度 → 瓦片坐标
function coordToPixel(lat, lon) {
var zoom = root.zoomLevel;
var n = Math.pow(2, zoom);
var x = (lon + 180) / 360 * n;
var y = (1 – Math.log(Math.tan(lat * Math.PI / 180) +
1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 * n;
return { “x”: x, “y”: y };
}
// 转屏幕像素坐标
function getPixelPos(coord) {
var centerTile = coordToPixel(root.centerCoordinate.latitude, root.centerCoordinate.longitude);
var pointTile = coordToPixel(coord.latitude, coord.longitude);
var tilesX = Math.ceil(flick.width / root.tileSize) + 2;
var tilesY = Math.ceil(flick.height / root.tileSize) + 2;
var startX = Math.floor(centerTile.x – tilesX / 2);
var startY = Math.floor(centerTile.y – tilesY / 2);
var px = (pointTile.x – startX) * root.tileSize + tileContainer.x;
var py = (pointTile.y – startY) * root.tileSize + tileContainer.y;
return { “x”: px, “y”: py };
}
// 更新猴子位置(不再计算角度)
function updateMarkerPosition() {
var center = root.centerCoordinate;
var centerTile = coordToPixel(center.latitude, center.longitude);
var tilesX = Math.ceil(flick.width / root.tileSize) + 2;
var tilesY = Math.ceil(flick.height / root.tileSize) + 2;
var startX = Math.floor(centerTile.x – tilesX / 2);
var startY = Math.floor(centerTile.y – tilesY / 2);
// 计算中心点像素位置
var px = (centerTile.x – startX) * root.tileSize + tileContainer.x;
var py = (centerTile.y – startY) * root.tileSize + tileContainer.y;
monkeyMarker.x = px – monkeyMarker.width / 2;
monkeyMarker.y = py – monkeyMarker.height / 2;
}
// 更新瓦片
function updateVisibleTiles() {
if (!tileContainer || !root.centerCoordinate) return;
var zoom = root.zoomLevel;
var n = Math.pow(2, zoom);
var center = root.centerCoordinate;
var centerTile = coordToPixel(center.latitude, center.longitude);
var tilesX = Math.ceil(flick.width / root.tileSize) + 2;
var tilesY = Math.ceil(flick.height / root.tileSize) + 2;
var startX = Math.floor(centerTile.x – tilesX / 2);
var startY = Math.floor(centerTile.y – tilesY / 2);
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 (!tilesCache[key]) {
var tile = Qt.createQmlObject(
'import QtQuick 2.15; Image { width:' + root.tileSize +
'; height:' + root.tileSize + '; source: "' + path + '"; fillMode: Image.PreserveAspectFit }',
tileContainer
);
tilesCache[key] = tile;
}
var tileObj = tilesCache[key];
tileObj.x = (x - startX) * root.tileSize;
tileObj.y = (y - startY) * root.tileSize;
}
}
// 删除多余瓦片
for (var key in tilesCache) {
if (!currentKeys[key]) {
tilesCache[key].destroy();
delete tilesCache[key];
}
}
// 平移 tileContainer,使中心瓦片对齐
var offsetX = (centerTile.x - startX) * root.tileSize - flick.width / 2;
var offsetY = (centerTile.y - startY) * root.tileSize - flick.height / 2;
tileContainer.x = -offsetX;
tileContainer.y = -offsetY;
updateMarkerPosition();
lineCanvas.requestPaint(); // 重新画红线
}
Component.onCompleted: updateVisibleTiles()
}
