一、实现方案概述
PostGIS数据库 → Python服务端(FastAPI + asyncpg)→ 实时生成矢量瓦片(MVT)→ Mapbox地图前端展示
二、环境搭建
需要安装的依赖包:
pip install fastapi uvicorn asyncpg psycopg2 shapely pygeoif
# 解决跨域
pip install fastapi uvicorn "python-multipart"
三、Python后端实现矢量瓦片(MVT)生成接口
from fastapi import FastAPI, Response
from fastapi.middleware.cors import CORSMiddleware
import asyncpg
app = FastAPI()
# 添加CORS中间件允许跨域访问
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # 或 ["http://localhost:5500"] 明确限定来源
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
DATABASE_URL = "postgresql://username:password@localhost:5432/yourdb"
@app.get("/tiles/{z}/{x}/{y}.pbf")
async def get_mvt_tile(z: int, x: int, y: int):
conn = await asyncpg.connect(DATABASE_URL)
sql = f"""
WITH
bounds AS (
SELECT ST_TileEnvelope({z}, {x}, {y}) AS geom
),
mvtgeom AS (
SELECT ST_AsMVTGeom(
ST_Transform(t.geom, 3857),
bounds.geom,
4096,
256,
true
) AS geom, t.name
FROM roads t, bounds
WHERE ST_Intersects(ST_Transform(t.geom, 3857), bounds.geom)
)
SELECT ST_AsMVT(mvtgeom.*, 'roads_layer', 4096, 'geom') AS tile
FROM mvtgeom;
"""
tile = await conn.fetchval(sql)
await conn.close()
if tile is None:
return Response(status_code=204)
return Response(content=bytes(tile), media_type="application/x-protobuf")
关键解释:
ST_TileEnvelope(z, x, y)
:生成瓦片对应的边界范围。ST_AsMVTGeom()
:生成矢量瓦片格式的几何数据。ST_AsMVT()
:将查询的几何数据转换成矢量瓦片(MVT)格式。
四、启动Python服务端
uvicorn main:app --reload
五、前端Mapbox地图可视化实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>实时矢量瓦片展示</title>
<link href="https://api.mapbox.com/mapbox-gl-js/v3.10.0/mapbox-gl.css" rel="stylesheet">
<script src="https://api.mapbox.com/mapbox-gl-js/v3.10.0/mapbox-gl.js"></script>
<style>
#map {
width: 100%;
height: 100vh;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
mapboxgl.accessToken = 'pk.eyJ1IjoiamFuZ2xlIiwiYSI6ImNqdmtjcXUxMTBlYTEzenBodHZiZG5kOWkifQ.GPYsVamvlFm5N_OioA9fwQ';
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v12',
center: [116.4, 39.9],
zoom: 10
});
map.on('load', function () {
map.addSource('roads', {
type: 'vector',
tiles: ['http://localhost:8000/tiles/{z}/{x}/{y}.pbf'],
minzoom: 0,
maxzoom: 22
});
map.addLayer({
id: 'roads_layer',
type: 'line',
source: 'roads',
'source-layer': 'roads_layer', // 必须与ST_AsMVT设置的layer名一致
paint: {
'line-color': '#ff0000',
'line-width': 2
}
});
});
</script>
</body>
</html>
六、运行测试与查看效果
打开index.html
,即可实时展示来自PostGIS的矢量瓦片线数据:
地图实时动态加载矢量瓦片数据。
缩放和平移时,实时请求并更新数据。