使用地图瓦片索引实现地理聚合
在处理大规模的散点数据时,有时候我们需要提供一个只读的查询 API 在地图上做可视化。 当数据量过大,比如百万这个量级,将数据一口气全部返回给前端在浏览器上处理是不太合适的。 应当在后端服务内完成一定聚合,将聚合后的搜索返回给前端。 这里介绍下在 Go 中如何使用 MongoDB + Tile 索引实现这件事。
我们从最基础的数据开始,假设地图上的的一个点记录可以用下面的方式定义:
type GeoPoint struct {
Type string `bson:"type" json:"type"`
Coordinates []float64 `bson:"coordinates" json:"coordinates"`
}
type Record struct {
ID primitive.ObjectID `bson:"_id"` // ObjectID
Location GeoPoint `bson:"location" json:"location"` // Raw point
}
那因为要存储不同缩放层级的数据,扩展下 Record
的字段
type Tile struct {
X, Y, Z uint32
Key string
}
type Record struct {
ID primitive.ObjectID `bson:"_id"` // ObjectID
Location GeoPoint `bson:"location" json:"location"` // Raw point
Levels []Tile `bson:"levels" json:"-"` // Not export to outside in JSON
}
将 zoom 在 [min, max]
内的瓦片索引 x,y,z
一并存储。其中 Key
是
{x}-{y}-{z}
的拼接格式。
当需要查找的时候,用 Mongo 的 Aggregate
功能,就能得到每个 Key
的出现次数:
l := 12
pipes := bson.A{
bson.M{
"$match": bson.M{"levels.z": l}, // 过滤出 zoom 等级为需要的数据
},
bson.M{
"$unwind": "$levels", // unwind
},
bson.M{
"$match": bson.M{"levels.z": l}, // 去掉 unwind 后不想要的 zoom
},
bson.M{
"$group": bson.M{
"_id": "$levels.key",
"count": bson.M{"$sum": 1}, // 统计
},
},
}
myCollecion.Aggregate(ctx, pipes)
单个记录样例:
{
"_id": "2413-3079-13",
"count": 12
}
然后从 Key 反解出瓦片的中心点坐标,加上统计结果一并给前端,用相关 SDK 在地图上展现即可。
本文代码在 https://github.com/ringsaturn/geo-agg-tile-index-example
Read other posts