本文来源于阿里云社区电子书《阿里云产品四月刊》
《阿里云产品四月刊》—Ganos H3 地理网格能力解析与最佳实践(2)https://developer.aliyun.com/article/1554159
附录
可视化前端 Python 脚本如下:
from quart import Quart, send file, render template import asyncpg import io import re # 数据库连接参数 CONNECTION = {"host": "YOUR HOST NAME OR IP", "port": PORT NO, "database": "DATABASE NAME", "user": "USER NAME", "password": "PASSWORD"} # 目标表名/字段/ID TABLE = "h3 count lev13" H3 COL = "h3 lev13" H3 GEOM COL = "geometry" AGG VAL COL = "count" COL SRID = 4326 app = Quart( name , template folder='./') @app.before serving async def create db pool(): app.db pool = await asyncpg.create pool(**CONNECTION) @app.after serving async def close db pool(): await app.db pool.close() @app.route("/") async def home(): sql = f''' SELECT ST Extent(ST Transform(ST Envelope({H3 GEOM COL}), 4326)) FROM {TABLE}; ''' async with app.db pool.acquire() as connection: box = await connection.fetchval(sql) box = re.findall('BOX\((.*?) (.*?),(.*?) (.*?)\)', box)[0] min x, min y, max x, max y = list(map(float, box)) bounds = [[min x, min y], [max x, max y]] center = [(min x + max x) / 2, (min y + max y) / 2] return await render template('./index.html', center=str(center), bounds=str(bounds)) @app.route("/h3 mvt/<int:z>/<int:x>/<int:y>") async def h3 mvt(z, x, y): sql = f''' SELECT ST AsMVT(tile.*) FROM (SELECT ST AsMVTGeom({H3 COL}, ST Transform(ST TileEnvelope($1,$2,$3),{COL SRID}), 4096, 512, true) geometry, {AGG VAL COL} count FROM {TABLE} WHERE ({H3 COL} && ST Transform(ST TileEnvelope($1,$2,$3),{COL SRID}))) tile''' async with app.db pool.acquire() as connection: tile = await connection.fetchval(sql, z, x, y) return await send file(io.BytesIO(tile), mimetype='application/vnd.mapbox vector tile') if name == " main ": app.run(port=5100)
index.html 文件内容如下:
<!DOCTYPE html> <html> <head> <meta charset="utf 8"> <title>map viewer</title> <meta name="viewport" content="initial scale=1,maximum scale=1,user scalable=no"> <link href="https://api.mapbox.com/mapbox gl js/v2.14.1/mapbox gl.css" rel="stylesheet"> <script src="https://api.mapbox.com/mapbox gl js/v2.14.1/mapbox gl.js"></scr ipt> <script src="https://cdnjs.cloudflare.com/ajax/libs/chroma js/2.4.2/chroma.m in.js"></script> </head> <body> <div id="map" style="position: absolute;left:0; top: 0; bottom: 0; width: 100%;cursor:pointer;"></div> <div class="counter" style="position: absolute;left:2%;font size: 20px;padding: .1em .1em;text shadow: 3px 3px 3px black;"> <span>当前网格计数:</span><span id="count">0</span> </div> <script> let YOUR TOKEN = "input your mapbox token" mapboxgl.accessToken = YOUR TOKEN; const map = new mapboxgl.Map({ container: "map", style: "mapbox://styles/mapbox/navigation night v1", center: {{ center }}, zoom: 1 }) map.on("load", () => { map.fitBounds({{ bounds }}) map.on('mousemove', 'h3', (e) => { map.getCanvas().style.cursor = "default"; if (e.features.length > 0) document.getElementById('count').innerText = e.features[0].properties.count }) map.on('mouseleave', 'h3', () => { map.getCanvas().style.cursor = "grab"; document.getElementById('count').innerText = 0 }) map.addSource("h3 source", { type: "vector", tiles: [`${window.location.href}h3 mvt/{z}/{x}/{y}`], tileSize: 512 }); // make color map const MIN = 1 const MAX = 600 const STEP = 10 color map = chroma.scale(["#536edb", "#5d96a5", "#68be70", "#91d54d", "#cddf37", "#fede28", "#fda938", "#fb7447", "#f75a40", "#f24734", "#e9352a", "#da2723", "#cb181d"]) .domain([MIN, MAX]); let colors = [] for (let i = MIN; i < MAX; i += STEP) colors.push(color map(i).hex(), i) colors.push(color map(MAX).hex()) map.addLayer({ id: "h3", type: "fill", source: "h3 source", "source layer": "default", paint: { "fill color": [ "step", ["get", "count"], ...colors ], "fill opacity": 0.8 } }); }); </script> </body> </html>