# 怎么用Python實現OSM切片計算
## 1. 什么是OSM切片
OpenStreetMap(OSM)切片是將地圖數據按照特定規則切割成小塊瓦片(Tile)的技術,通常用于Web地圖服務。每個切片對應特定縮放級別(Zoom Level)下的地圖區域,采用`{z}/{x}/{y}`的命名規范:
- z: 縮放級別(0-20+)
- x: 橫向瓦片索引
- y: 縱向瓦片索引
## 2. 核心計算原理
### 2.1 經緯度轉瓦片坐標
使用Mercator投影將地理坐標(經度lon, 緯度lat)轉換為平面坐標,再計算對應的瓦片坐標:
```python
import math
def deg2num(lat_deg, lon_deg, zoom):
lat_rad = math.radians(lat_deg)
n = 2.0 ** zoom
xtile = int((lon_deg + 180.0) / 360.0 * n)
ytile = int((1.0 - math.asinh(math.tan(lat_rad)) / math.pi) / 2.0 * n)
return (xtile, ytile)
反向計算瓦片對應的地理邊界:
def num2deg(xtile, ytile, zoom):
n = 2.0 ** zoom
lon_deg = xtile / n * 360.0 - 180.0
lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * ytile / n)))
lat_deg = math.degrees(lat_rad)
return (lat_deg, lon_deg)
pip install numpy requests
class OSMTileCalculator:
def __init__(self):
self.EARTH_RADIUS = 6378137
self.ORIGIN_SHIFT = 2 * math.pi * self.EARTH_RADIUS / 2.0
def latlon_to_meters(self, lat, lon):
mx = lon * self.ORIGIN_SHIFT / 180.0
my = math.log(math.tan((90 + lat) * math.pi / 360.0)) / (math.pi / 180.0)
my = my * self.ORIGIN_SHIFT / 180.0
return mx, my
def meters_to_latlon(self, mx, my):
lon = (mx / self.ORIGIN_SHIFT) * 180.0
lat = (my / self.ORIGIN_SHIFT) * 180.0
lat = 180 / math.pi * (2 * math.atan(math.exp(lat * math.pi / 180.0)) - math.pi / 2.0)
return lat, lon
def get_tile_bbox(self, x, y, zoom):
n = 2 ** zoom
lon_min = x / n * 360.0 - 180.0
lat_max = math.degrees(math.atan(math.sinh(math.pi * (1 - 2 * y / n))))
lon_max = (x + 1) / n * 360.0 - 180.0
lat_min = math.degrees(math.atan(math.sinh(math.pi * (1 - 2 * (y + 1) / n))))
return (lon_min, lat_min, lon_max, lat_max)
def get_tiles_in_bbox(min_lon, min_lat, max_lon, max_lat, zoom):
x_min, y_max = deg2num(max_lat, min_lon, zoom)
x_max, y_min = deg2num(min_lat, max_lon, zoom)
tiles = []
for x in range(x_min, x_max + 1):
for y in range(y_min, y_max + 1):
tiles.append((x, y, zoom))
return tiles
def get_tile_url(x, y, zoom, style="standard"):
base_url = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
subdomains = ['a', 'b', 'c']
return base_url.format(
s=subdomains[(x + y) % len(subdomains)],
z=zoom,
x=x,
y=y
)
import os
import requests
def download_tiles(bbox, zoom, output_dir):
tiles = get_tiles_in_bbox(*bbox, zoom)
os.makedirs(output_dir, exist_ok=True)
for x, y, z in tiles:
url = get_tile_url(x, y, z)
path = f"{output_dir}/{z}_{x}_{y}.png"
try:
r = requests.get(url, stream=True)
with open(path, 'wb') as f:
for chunk in r.iter_content(1024):
f.write(chunk)
print(f"Saved {path}")
except Exception as e:
print(f"Failed {url}: {str(e)}")
import matplotlib.pyplot as plt
def plot_tile_coverage(tiles):
x_coords = [t[0] for t in tiles]
y_coords = [t[1] for t in tiles]
plt.figure(figsize=(10, 6))
plt.scatter(x_coords, y_coords, s=5)
plt.title(f"OSM Tile Coverage (Zoom {tiles[0][2]})")
plt.xlabel("X Tile")
plt.ylabel("Y Tile")
plt.grid(True)
plt.show()
concurrent.futures
實現多線程下載mercantile
等專業庫通過Python實現OSM切片計算的核心在于理解墨卡托投影轉換和瓦片坐標系的數學關系。本文提供的代碼示例涵蓋了從基礎計算到實際應用的完整流程,讀者可根據需求擴展更多功能如: - 支持不同地圖服務提供商 - 實現矢量切片計算 - 開發動態地圖渲染服務 “`
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。