1. 地理范围到瓦片行列号的转换
瓦片地图通常使用 XYZ 坐标系统,其中:
- X:表示列号。
- Y:表示行号。
- Z:表示缩放级别。
计算公式
给定一个地理范围(即左上角和右下角的经纬度),可以通过以下步骤计算出需要请求的瓦片行列号:
-
将经纬度转换为瓦片坐标:
- 使用
lon2tile
和lat2tile
函数将经纬度转换为瓦片坐标。
- 使用
-
确定所需瓦片的行列范围:
- 根据左上角和右下角的瓦片坐标,确定需要请求的所有瓦片的行列号范围。
2. 示例代码
以下是一个完整的 JavaScript 示例,展示了如何根据给定的地理范围和缩放级别计算需要请求的瓦片行列号。
// 将经度转换为瓦片列号
function lon2tile(lon, zoom) {
return Math.floor((lon + 180) / 360 * Math.pow(2, zoom));
}
// 将纬度转换为瓦片行号
function lat2tile(lat, zoom) {
return Math.floor((1 - Math.log(Math.tan(lat * Math.PI / 180) + 1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 * Math.pow(2, zoom));
}
// 根据地理范围和缩放级别计算需要请求的瓦片行列号
function getTilesInRange(bounds, zoom) {
const [minLon, minLat, maxLon, maxLat] = bounds; // bounds: [minLon, minLat, maxLon, maxLat]
// 转换地理范围到瓦片坐标
const startX = lon2tile(minLon, zoom);
const endX = lon2tile(maxLon, zoom);
const startY = lat2tile(maxLat, zoom); // 注意:纬度从北向南递增,所以这里用最大纬度
const endY = lat2tile(minLat, zoom); // 最小纬度对应最大行号
const tiles = [];
// 遍历所有需要的瓦片
for (let x = startX; x <= endX; x++) {
for (let y = startY; y <= endY; y++) {
tiles.push({ x, y, z: zoom });
}
}
return tiles;
}
// 示例使用
const bounds = [116.4074, 39.9042, 116.4174, 39.9142]; // 北京某区域的经纬度范围
const zoom = 15; // 缩放级别
const tiles = getTilesInRange(bounds, zoom);
console.log(tiles);
3. 解释
lon2tile
和 lat2tile
函数
-
lon2tile
:将经度转换为瓦片列号。function lon2tile(lon, zoom) { return Math.floor((lon + 180) / 360 * Math.pow(2, zoom)); }
-
lat2tile
:将纬度转换为瓦片行号。function lat2tile(lat, zoom) { return Math.floor((1 - Math.log(Math.tan(lat * Math.PI / 180) + 1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 * Math.pow(2, zoom)); }
getTilesInRange
函数
-
参数:
bounds
:一个数组[minLon, minLat, maxLon, maxLat]
,表示地理范围。zoom
:当前的缩放级别。
-
逻辑:
- 将地理范围的四个角转换为瓦片坐标。
- 确定需要请求的所有瓦片的行列号范围。
- 遍历该范围内的所有瓦片,并将其添加到结果数组中。
4. 进一步优化
处理边界情况
在某些情况下,可能需要处理瓦片坐标的边界问题(例如,当瓦片坐标超出有效范围时)。可以通过以下方式处理:
function clamp(value, min, max) {
return Math.min(Math.max(value, min), max);
}
// 在 getTilesInRange 中使用 clamp 函数限制瓦片坐标范围
const startX = clamp(lon2tile(minLon, zoom), 0, Math.pow(2, zoom) - 1);
const endX = clamp(lon2tile(maxLon, zoom), 0, Math.pow(2, zoom) - 1);
const startY = clamp(lat2tile(maxLat, zoom), 0, Math.pow(2, zoom) - 1);
const endY = clamp(lat2tile(minLat, zoom), 0, Math.pow(2, zoom) - 1);
请求瓦片
一旦获取了所需的瓦片行列号,你可以通过 HTTP 请求来加载这些瓦片。例如:
tiles.forEach(tile => {
const url = `https://example.com/tiles/${tile.z}/${tile.x}/${tile.y}.png`;
const img = new Image();
img.src = url;
document.body.appendChild(img);
});