google-s2简介

s2-geometry-lib是一个谷歌开发的空间几何坐标库,提供很多空间操作的工具方法,包括Polygon、Loop、Line、Point等。

如果要定位一个用户所在的省市区,一般的思路是先构建每一个区域的Ploygon对象,然后根据用户的经纬度坐标,判断该经纬度属于哪一个区域中。这个操作每判断一个点都会进行遍历(最坏情况所有区域)和计算(区域是否contain),这是非常消耗资源的,对现在互联网的一些服务,也很难满足性能需要。

利用s2库就可能很好的优化上面的问题,s2库提供了一个Cell的概念,每个Cell相当于一个单元格,在一个单元格内的所有Point,都可以算出同一个CellId。

同时可以认为,一个Polygon可以用一个Cell的集合来表示。

那么这时的步骤就是通过区域的坐标,来初始化其内包含的所有的CellId,将这些CellId的id放入redis中做key,对应的value是区域名称(譬如,上海浦东新区),构建完全国范围的CellId后,查询用户的区域就可以通过用户的坐标获得一个CellId,然后去redis中查询,在使用LocalCache后,基本就没有什么计算成本,完全可以满足性能需求。

下面是判断点的CellId是否能够匹配到一个区域的某个CellId的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class App {
public static void main(String[] args) {
S2RegionCoverer coverer = new S2RegionCoverer();
ArrayList<S2CellId> covering = new ArrayList<S2CellId>();
String city = "30:121,30:122,32:121,32:122";
coverer.setMaxCells(100000);
coverer.setMinLevel(11);//设定ZoomLevel
// 获得能够覆盖city这个区域的所有CellId(Cell的集合表示的范围略微大于city的范围,外接四边形)
coverer.getCovering(GeometryTestCase.makePolygon(city), covering);
// 获得能够覆盖city这个区域的所有CellId(Cell的集合表示的范围略微小于city的范围,内接四边形)
// coverer.getInteriorCovering(GeometryTestCase.makePolygon(city), covering);
//GeometryTestCase是s2库的一个测试类,makePolygon方法本来是默认的,我改成公共的
S2CellId s2cellid = S2CellId.fromLatLng(S2LatLng.fromDegrees(30.5, 121.5));
S2CellId paCellId = s2cellid.parent(11);//获得相同ZoomLevel的CellId
// 只有相同的ZoomLevel下才能找到相匹配的
for (int i = 0; i < covering.size(); i++) {
if (paCellId.id() == covering.get(i).id()) {
System.out.println(covering.get(i).id());//数据匹配的CellId
}
}
}
}