背景
現在業務中往往有海量點和建築物的關聯,但是傳統關系數據處理時間總不太理想。本文嘗試使用ClickHouse導入面數據,並進行多面和多點的關聯。
數據准備
create table extent (id UInt32,xmin Float64, xmax Float64,ymin Float64, ymax Float64,wkt String) engine = MergeTree() order by (id,xmin,xmax,ymin,ymax);
數據處理
導入面數據(使用arcpy來處理),讀取shp數據的id、最大最小經緯度、wkt(點數據使用之前導入的數據,https://juejin.cn/post/6903100159484395534)
time clickhouse-client --query="INSERT INTO extent FORMAT CSVWithNames" < AmericanPolygon.csv
把wkt轉為陣列為后面計算做准備(分割字符串,得到單個坐標組並轉為float陣列),目前只是使用單面和無洞的多邊形面數據。
CREATE TABLE extents ENGINE = MergeTree() order by (id,xmin,xmax,ymin,ymax) AS select id,xmin,xmax,ymin,ymax,arrayMap(x -> tuple(arrayElement(arrayMap(x -> toFloat64(x),splitByChar(' ',trimLeft(x))),1),arrayElement(arrayMap(x -> toFloat64(x),splitByChar(' ',trimLeft(x))),2)),splitByChar(',', replaceAll(replaceAll(replaceOne( wkt, 'MULTIPOLYGON ', ''),')',''),'(',''))) geo from extent
根據面的范圍生成七級精度下計算最小包含指定的最小圖形的geohash數組,用這些geohash來篩選相應點,點有了geohash再重新關聯面,最后得到一個點面geohash值中間表。(原本要這里就出結果,但是不生成中間表,最后處理速度就非常慢)
CREATE TABLE extentpnt ENGINE = MergeTree() order by (id,Lon,Lat) AS select b.Lon,b.Lat,a.id,a.geo from ( select Lon,Lat,geohashEncode(Lon, Lat,7) geohash FROM pnts where geohashEncode(Lon, Lat,7) in ( select arrayJoin(geohashesInBox(xmin, ymin, xmax , ymax,7)) from extents group by arrayJoin(geohashesInBox(xmin, ymin, xmax , ymax,7))) ) b cross join ( select arrayJoin(geohashesInBox(xmin, ymin, xmax , ymax,7)) geohash,xmin,ymin,xmax,ymax,id,geo from extents group by arrayJoin(geohashesInBox(xmin, ymin, xmax , ymax,7)),xmin,ymin,xmax,ymax,id,geo ) a where a.geohash=b.geohash and b.Lon BETWEEN a.xmin AND a.xmax and b.Lat BETWEEN a.ymin and a.ymax
點面相交得到最后結果
select Lon,Lat,id from extentpnt where pointInPolygon((Lon,Lat),geo)=1
結論
ClickHouse是數據庫黑馬,目前在點面關聯上有不俗的性能,但是空間分析過於稀少,空間數據存儲支持不完善還待優化。
參考資料:
https://clickhouse.tech/docs/zh/sql-reference/functions/string-functions/
https://blog.csdn.net/qq_36951116/article/details/106260189