本文介紹基於PolarDB PostgreSQL版的GanosBase即時熱力彙總查詢並動態輸出熱力瓦片能力,該功能可用於將查詢處理結果即時返回給用戶端的資料交換結構。
關於熱力瓦片
什麼是熱力瓦片
熱力瓦片(HeatMap Tile,簡稱HMT)底層基於GanosBase首創的大規模向量/軌跡資料即時熱力彙總查詢技術,用於將查詢處理結果即時返回用戶端的資料交換結構。該技術改變了熱力統計分析中“彙總需要預打碼、展示需要預切片”的傳統方式,可針對百萬級、千萬級、億級規模資料秒級彙總並渲染。HMT支援多種常用彙總函式與代數運算式,支援選擇統計與業務相關的指標,並隨地圖的放大縮小進行不同層級的動態計算和即時繪製,極大程度提升業務效率,為客戶產品帶來的更多可能。在2022年度的雲棲大會上,GanosBase發布了這一能力,並現場展示了基於HMT構建的大規模運輸軌跡即時查詢彙總的案例,將以往需要線下預先處理才能發布的資料產品實現完全線上化。該功能獲得了行業內的廣泛反響與認可。
使用情境
熱力瓦片的特點在於能夠實現空間資料的即時彙總與渲染,主要應用於處理海量向量資料並需要即時統計分析的業務情境,比如:
交通運輸類:根據運輸工具(如車輛、船舶等)的歷史軌跡,彙總全域範圍內的即時熱力資料,並能夠根據時間(如冬季、夏季)、起點/終點及類型(如貨車、客車)等附加條件進行過濾,從而即時產生相應的熱力圖。
城市管理類:根據房屋建築底面資料,彙總全域範圍內城市和農村的建築密度、建築平均高度、建築總面積等單項指標,並結合地塊資訊,彙總容積率等複合指標。
共用出行類:根據共用出行裝置的軌跡點,彙總全域範圍內裝置的停靠地區熱力圖,並可基於裝置事件(如開鎖/關鎖、上車/下車、事故、損壞)、流向等條件分析共用出行裝置的調度與營運策略。
技術優勢
相較於基於H3或S2網格預打碼彙總的方式,HMT熱力瓦片擁有如下優勢:
效率極高,無需預打碼,不增加儲存成本。HMT的彙總技術與H3/S2等網格彙總方式在技術特性及應用情境上存在顯著差異。網格彙總方式通常針對需要以網格編碼作為檢索條件的應用情境,這要求在預先設定某一精度層級後,對向量資料進行編碼,並基於該編碼進行相關統計。然而,HMT技術不需要事先對資料進行編碼,能夠根據當前視口範圍進行動態彙總。隨著視口的放大或縮小,彙總過程將即時進行,且在處理各種幾何對象時效率一致,均可實現億級規模的秒級彙總渲染。
方便宜用,彙總結果直接可視化。HMT提供了彙總結果快速瓦片化的能力,能夠將彙總結果直接與前端渲染引擎進行可視化對接,確保所見即所得 (WYSIWYG)。此外,HMT還提供了一系列統計函數,以協助使用者快速自動化產生最佳的渲染色表,從而確保前端的最佳表現。
經過多個真實情境中的測試,HMT的彙總技術展現出極高的效率,基本能夠在秒級時間內完成億級規模的全圖彙總:
應用情境 | 資料量 | 瓦片範圍 | 彙總效率 |
軌跡彙總 |
|
| 372 ms |
建築底面彙總 | 建築底面:3.08億 |
| 17 s |
上表資料均為在全球展示尺度下的全量資料彙總效率,隨著地圖的持續放大,彙總效率將不斷提升。
功能介紹
熱力瓦片包含一系列的SQL函數,用於解決熱力瓦片的產生與統計問題,具體包括:
ST_AsHMT:將一組幾何對象或軌跡對象按照指定範圍和指定解析度轉為熱力矩陣瓦片。
ST_HMTAsArray:將熱力圖瓦片轉換為基於數組矩陣的表示方法,以便於進行查看。
ST_HMTStats:計算熱力圖瓦片的統計值資訊,以便在渲染過程中使用。
ST_HMTAsRaster:將熱力瓦片轉換為Raster對象,以便於進行查看和計算操作。
提示
採用並行提升效能
當資料量較大時,可以採用平行處理以提升效能。在實際應用中,可根據視口範圍進行相應設定,例如在較高層級下使用16個並行進程,而在較低層級下則不使用平行處理等。在CPU資源充足的情況下,為確保每個查詢均可利用平行處理,需要將max_worker_processes和max_parallel_workers的值設定為並行度與並發數的乘積,相關介紹可參考社區文檔。以下樣本將為您展示如何設定並行度為16:
SET max_worker_processes = 300;
SET max_parallel_workers = 260;
SET max_parallel_workers_per_gather = 16;
ALTER TABLE table_name SET (parallel_workers=16);
SET force_parallel_mode = on;瓦片大小
通常情況下,採用512*512的瓦片重採樣至256*256的瓦片以避免鋸齒現象。然而,在特定情況下,如資料量極大時,每個瓦片的計算過程會非常耗時,此時可以考慮使用大瓦片(1024*1024)以減少瓦片擷取的數量,從而提升效能。
使用&&操作符進行空間過濾
從計算效能上看,ST_AsHMT的計算速度明顯快於ST_Intersects。在進行索引過濾時,還可以使用&&操作符進行過濾。
SELECT ST_AsHMT(column_name, --geometry類型列
ST_MakeEnvelope(0, 0, 10, 10, 4326), -- 定義空間範圍
512,
512,
value -- 用於產生輸出的參考值列
)
FROM table_name
WHERE column_name && ST_MakeEnvelope(0, 0, 10, 10, 4326);查詢範圍的空間參考轉換
當查詢範圍與幾何對象的空間範圍不一致時,應對查詢範圍進行空間參考轉換後再進行查詢。否則,自動轉換可能會導致效能下降。在擷取瓦片後,將圖片轉換為指定的空間參考後進行展示。
SELECT ST_AsHMT(column_name, -- 空間參考系統為WGS 84座標系
ST_Transform(ST_TileEnvelope(6, 48, 32), 4326), -- 定義瓦片的範圍
512,
512,
value -- 指定用於渲染瓦片映像的值
)
FROM table_name
WHERE column_name && ST_Transform(ST_TileEnvelope(6, 48, 32), 4326));對空間表進行VACCUM FULL和CLUSTER操作
VACCUM FULL操作可以回收空閑空間,降低磁碟檔案大小,從而在查詢時降低IO操作的數量。VACUUM full table_name; Cluster table_name using index_name;CLUSTER操作可以確保資料群組織與索引保持一致,相鄰的空間資料將被儲存在相鄰的資料頁面中,從而在訪問時降低資料庫的磁碟訪問負擔。詳細用法請參考社區文檔。CLUSTER table_name USING index_name;
最佳實務
資料庫端配置。
將幾何或軌跡資料匯入資料庫中,建議採用FDW機制進行操作,詳細操作可以參考向量格資料快速入庫。請確保所有對象均具備相同的空間參考系統,您可以通過ST_Srid函數進行確認。
在幾何列或軌跡列建立空間索引。
CREATE INDEX index_name ON table_name USING GIST(column_name);根據空間範圍進行熱力瓦片的查詢。
說明以下SQL語句中的ST_MakeEnvelope可以使用ST_TileEnvelope函數擷取瓦片範圍。
針對網格內對象數量的熱力彙總進行分析。
SELECT ST_AsHMT(column_name, --geometry type ST_MakeEnvelope(0, 0, 10, 10, 4326), -- Extent 512, -- Width 512 -- height ) FROM table_name WHERE column_name && ST_MakeEnvelope(0, 0, 10, 10, 4326);對特定網格內的數值進行彙總時,可採用value欄位中的數值進行求和操作。
SELECT ST_AsHMT(column_name, --geometry type ST_MakeEnvelope(0, 0, 10, 10, 4326), -- Extent 512, -- Width 512, -- height value -- value column ) FROM table_name WHERE column_name && ST_MakeEnvelope(0, 0, 10, 10, 4326);對特定網格內的數值進行彙總並採用value欄位中的數值進行求和操作時,還可以增加其他過濾條件。
SELECT ST_AsHMT(column_name, --geometry type ST_MakeEnvelope(0, 0, 10, 10, 4326), -- Extent 512, -- Width 512, -- height value -- value column ) FROM table_name WHERE column_name && ST_MakeEnvelope(0, 0, 10, 10, 4326); AND name like 'xxxx%' AND value > 100;
本案例使用Node.js編寫一個簡易應用,以示範熱力瓦片的實際應用情境。
檔案結構如下:
└── hmt_server ├── app.js ├── hmt.proto ├── index.html └── package.json其中,
hmt.proto為ST_AsHMT中介紹的proto檔案,其他檔案內容將在下文給出。後端代碼package.json和app.js檔案如下:
package.json:
{ "name": "hmt_server", "version": "1.0.0", "main": "app.js", "license": "ISC", "dependencies": { "chroma-js": "^2.4.2", "express": "^4.18.2", "lru-cache": "^10.1.0", "pg": "^8.11.3", "protobufjs": "^7.2.5", "sharp": "^0.32.6" } }app.js:
const express = require('express'); const { Pool } = require('pg'); const chroma = require('chroma-js'); const sharp = require("sharp"); const protobuf = require('protobufjs'); const { LRUCache } = require('lru-cache'); // 設定資料庫連接 const CONNECTION = { user: 'YOUR_USER', password: 'YOUR_PWD', host: 'YOUR_HOST', database: 'YOUR_DB', port: YOUR_PORT }; // 目標表名 const TABLE_NAME = 'YOUR_TABLE'; // 目標幾何欄位名 const GEOMETRY_COLUMN = 'YOUR_GEOM_COLUMN'; // 設定無資料值 const NO_DATA_VALUE = 0; // 目標幾何欄位空間參考 const SRID = 4326 // 設定色帶 const COLOR_MAP = [ ['#536edb', 1], ['#5d96a5', 3], ['#68be70', 5], ['#91d54d', 7], ['#cddf37', 9], ['#fede28', 11], ['#fda938', 13], ['#fb7447', 15], ['#f75a40', 17], ['#f24734', 19], ['#e9352a', 21], ['#da2723', 23], ['#cb181d', 25] ]; // 建立資料庫連接池,預設為10個串連 const pool = new Pool(CONNECTION); // 配置色彩轉換 const [colors, domains] = COLOR_MAP.reduce(([c, d], [colors, domains]) => [[...c, colors], [...d, domains]], [[], []]); const colorMap = chroma.scale(colors).domain(domains).mode('rgb') // 載入protobuf const hmtDecoder = protobuf.loadSync('./hmt.proto').lookupType('HMT'); // 建立一個1x1的透明png,作為空白瓦片返回 const emptyPng = Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAADUlEQVQImWP4//8/AwAI/AL+hc2rNAAAAABJRU5ErkJggg==', 'base64'); // 對於小比例尺瓦片(z<5),因更新相對不明顯,設定一個24小時到期的緩衝 const globalCache = new LRUCache({ max: 1000, ttl: 1000 * 3600 * 24 }); // 對於更大比例尺瓦片(z>=5),設定一個12小時到期的緩衝,也可根據實際情況自行修改 const localCache = new LRUCache({ max: 2000, ttl: 1000 * 3600 * 12 }); // 註冊Express路由 express() // 響應HTML頁面 .get("/", (_, res) => res.sendFile('index.html', { root: __dirname })) // 響應熱力瓦片服務 .get('/hmt/:z/:x/:y', async ({ params: { z, x, y } }, res) => { const cache = z < 5 ? globalCache : localCache; const key = `${z},${x},${y}` if (!cache.has(key)) { // 設定並行度,並調用ST_AsHMT函數,請求該地區256x256的熱力瓦片 const parallel = z <= 5 ? 10 : 5; const sql = ` set max_parallel_workers = ${parallel}; set max_parallel_workers_per_gather = ${parallel}; WITH _PARAMS(_BORDER) as (VALUES(ST_Transform(ST_TileEnvelope(${key}),${SRID}))) SELECT ST_AsHMT(${GEOMETRY_COLUMN},_BORDER,256,256) tile FROM ${TABLE_NAME},_PARAMS WHERE _BORDER && ${GEOMETRY_COLUMN};` // 跳過set語句,擷取ST_AsHMT函數的結果 const { rows: [{ tile }] } = (await pool.query(sql))[2]; // 若該地區無資料則直接返回空瓦片 if (!tile) cache.set(key, emptyPng); else { // 解析protobuf結果 const { type, doubleValues, intValues } = hmtDecoder.decode(tile); const { values } = type == 1 ? doubleValues : intValues; // 將數值轉換為對應的顏色,並剔除無資料值 const pixels = values.reduce((_pixels, value) => { _pixels.push(...colorMap(value).rgb()); _pixels.push(value <= NO_DATA_VALUE ? 0 : 255); return _pixels; }, []) // 渲染為png瓦片 const rawConfig = { raw: { width: 256, height: 256, channels: 4 } }; const renderedPng = await sharp(Uint8Array.from(pixels), rawConfig) .png().toBuffer(); cache.set(key, renderedPng); } } const tile = cache.get(key) res.set("Content-Type", "image/png").send(tile); }) // 監聽5500連接埠 .listen(5500, () => console.log('HMT server started.'));其中:
色帶為連續色帶,支援十六進位字串顏色、CSS3名稱顏色等多種表達方式,詳細介紹可參見chroma.js文檔。
若希望瓦片渲染效果更加平滑,可以請求512x512的未經處理資料,然後降採樣至256x256解析度,但這將增加回應時間。
並行度的設定應根據資料量的大小、資料庫叢集的配置以及對響應速度的需求進行調整。
說明本案例在
Z<=5時適當降低並行度。
前端代碼index.html採用
Mapbox作為前端地圖SDK,其token可在此處申請查看。由於熱力瓦片最終以PNG格式渲染,因此與絕大多數地圖SDK相容。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>HMT 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"></script> </head> <body> <div id="map" style="position: absolute;left:0; top: 0; bottom: 0; width: 100%;"></div> <script> let CENTER = [YOUR_LONGITUDE, YOUR_LATITUDE] mapboxgl.accessToken = YOUR_MAPBOX_TOKEN; const map = new mapboxgl.Map({ container: 'map', style: "mapbox://styles/mapbox/navigation-night-v1", center: CENTER, zoom: 5 }) map.on("load", () => { map.addSource('hmt_source', { type: 'raster', minzoom: 3, tiles: [`${window.location.href}hmt/{z}/{x}/{y}`], tileSize: 256, }); map.addLayer({ id: 'hmt', type: 'raster', source: 'hmt_source', }); }); </script> </body> </html>
安裝與發布。
## 定位到hmt_server目錄 cd ./hmt_server ## 安裝依賴庫 npm i ## 運行熱力瓦片服務 node . ## 此時可以開啟瀏覽器,登入地址 http://localhost:5500/ 查看效果效果預覽
船舶軌跡線即時彙總
3100萬軌跡點,45萬軌跡線即時彙總。


建築底面即時彙總
3.08億建築底面即時彙總。

總結
目前,GanosBase已成功支撐數十個行業領域的數千個應用情境。穩定性、成本效益、效能與易用性始終是GanosBase長期追求的目標。HMT熱力瓦片是GanosBase在大規模空間資料高效彙總與可視化領域的核心級核心競爭力,為大規模資料分析挖掘提供了真正高效、易用的方案,歡迎您體驗。
試用體驗
您可以訪問PolarDB免費試用頁面,選擇試用“雲原生資料庫PolarDB PostgreSQL版”,體驗GanosBase的HMT即時熱力統計查詢能力。