贾国辉
发布于 2025-03-28 / 51 阅读
0
0

PostGIS发布矢量瓦片服务

一、实现方案概述

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的矢量瓦片线数据:

  • 地图实时动态加载矢量瓦片数据。

  • 缩放和平移时,实时请求并更新数据。


评论