前面的两篇文章介绍了geohash的基本原理及c#代码相关实现,其中geohash 5位编码单个网格覆盖面积大约在24平方千米,6位编码单网格覆盖面大约在0.73平方千米, 相邻编码长度之间单网格覆盖面积跨度太大,在实际应用中希望计算出3平方千米、5平方千米、10平方千米的覆盖率,可行的方案在Geohash6对应的网格上进行4、9、16网格的合并。
基本思路 :
- 将城市坐标看成一个基于经纬度线的坐标系,取出所在城市的经纬度范围(为了减少网格的个数,在满足业务的需求的前提下,确定的范围值越小越好)
- 从最小的经纬度开始计算网格,在 geohash基本原理 一文中,通过在网上收集的资料,geohash码相邻网格的经纬度分别是为:0.0055 和 0.0027, 通过验证,相邻网格正确的偏移量分别为:
DIFF_LAT = 0.0054931640625; DIFF_LON = 0.010986328125;
- 在计算的过程中,将最小经纬度画出的表格定位在坐标系中的第一行、第一列,根据上面提供的偏差量依次进行循环,计算出范围内所有网格及其所在的行、列,以青岛为例,计算出来的示例数据如下:
- 数据清洗完成后,接下来需要做的就是进行网格的合并(本质是给合并的网格一个相同的编码),如下图:
- 红框内以4格合并为例,每个合并后的网格占2行2列,框内的11表示第一行第一列,12表示第一行第二列,21表示第二行第一行,当4格合并后新生成的风格占第一行第一列,由此不难发现,取geohash6网格所有行、列数除2向下取整即可得到合并后网格所在的新行和新列,对数据进行组合,确定唯一码
- 同理,对于9格、16格、25格的合并岂是一样,它们分别可以使用3行3列、4行4列、5行5列的明细风格进行合并,然后取出合并后网格的最大和最小经纬度即可计算出坐标范围,如下:
/*group4*/ update ETL_GeoCityGrid set geogroup4 = cityID + '-' + 'G4-' + cast(CEILING(cast(grow as decimal)/2) as varchar) + '-' + cast(CEILING(cast(gcol as decimal)/2) as varchar); /*group9*/ update ETL_GeoCityGrid set geogroup9 = cityID + '-' + 'G9-' + cast(CEILING(cast(grow as decimal)/3) as varchar) + '-' + cast(CEILING(cast(gcol as decimal)/3) as varchar); /*group16*/ update ETL_GeoCityGrid set geogroup16 = cityID + '-' + 'G16-' + cast(CEILING(cast(grow as decimal)/4) as varchar) + '-' + cast(CEILING(cast(gcol as decimal)/4) as varchar);
-
合并后网格的唯一码确定后,计算每个合并后网格的坐标范围就相对容易,如下:
select geogroup4,min(minlat) as minlat,min(minlng) as minlng,max(maxlat) as maxlat,max(maxlng) as maxlng from ETL_GeoCityGrid where cityid = '3702' group by geogroup4
- 效果图: