目录
前言
姐妹篇:利用GeoHash实现逆地理编码(经纬度坐标转换行政区划)
MySQL从4.x版本开始就对GIS(Geographic Information System)的数据类型进行支持,并且也有对应的空间索引;MBR(Minimum Bounding Rectangle)相关算法也是地图服务常见问题的一个解决方案。同姐妹篇利用GeoHash实现逆地理编码一样,本篇文章主要介绍,如何通过MBR和mysql的GIS数据类型实现逆地理编码,同样精确度也只能达到**区/县**级行政区划解析结果。
理解MBR
MBR全称是Minimum Bounding Rectangle,直译就是最小边界矩形。通常是指,针对一个不规则二维图形的边界,绘制出其最小的边界矩形;并且依托这个矩形产生了一系列算法。
MBR计算
MBR的计算方式非常简单,将一系列边界点进行遍历,获取这一系列边界点【最大经度】【最小经度】【最大纬度】【最小纬度】。那么构成这个MBR图形的四个坐标点应分别为:
坐标位置 | 经度 | 纬度 |
---|---|---|
西南点 | 【最小经度】 | 【最小纬度】 |
西北点 | 【最小经度】 | 【最大纬度】 |
东北点 | 【最大经度】 | 【最大纬度】 |
东南点 | 【最大经度】 | 【最小纬度】 |
行政区划MBR示例
在线绘制地址围栏
以江苏省
(以最大polygon数据为主,去除其他岛屿等独立polygon数据,数据来源高德,再次感谢高德)示例,其为不规则图形,在地图中绘制效果如下:
遍历所有边界点,计算得到其最大最小经纬度分别为:
名称 | 值 |
---|---|
最大经度 | 121.975185 |
最小经度 | 116.362723 |
最大纬度 | 35.124513 |
最小纬度 | 30.757840 |
对应其MBR四个坐标点分别为:
坐标位置 | 经度 | 纬度 |
---|---|---|
西南点 | 116.362723 | 30.757840 |
西北点 | 116.362723 | 35.124513 |
东北点 | 121.975185 | 35.124513 |
东南点 | 121.975185 | 30.757840 |
MBR结果在地图中绘制效果如下:
如图所示,该MBR图形是江苏省行政区划的最小边界外界矩形。
mysql GIS支持
GIS(Geographic Information System)是地图信息系统,mysql 5.6版本及5.6以上版本,提供了地图信息系统相关数据结构的支持。
注意:
1> mysql的GIS相关函数和数据类型在进行压力测试时,即使有空间索引存在,其表现并不好;在并发数较高时,会迅速消耗数据库服务器CPU资源,最终成为整个调用链路的瓶颈,并且无法通过加机器等横向扩展方式解决。
2> mongoDB也存在GIS的支持,并且吞吐量比mysql要高很多,如果是中小型系统,建议选用mongoDB而不是mysql进行GIS相关计算。
3> 最终推荐:GIS相关计算在应用服务器上进行,横向扩展方便。相关算法可选用或参考java awt包中几何图形算法。
几何数据类型
mysql提供了基本上可以覆盖所有地理模型的几何数据类型,如下图所示:
mysql 8.0版本Geometry数据类型官方文档
比较常用的例如:
- Point 点 以x,y平面直角坐标系建立的坐标点
- Polygon 多边形 以多个坐标点顺序连接构成闭环的2维多边形
- MultiPolygon 多多边形 多个2维多边形
类似省、市、区县的行政区划边界点,可以直接使用MultiPolygon数据类型存储入数据库。
几何数据类型相关函数
mysql 5.6版本GIS函数官方文档
mysql 8.0版本GIS函数官方文档
简单介绍一下我们使用场景中需要的函数ST_CONTAINS()或者ST_WITHIN():
**ST_CONTAINS()**函数是用于判断某一个二维几何数据结构是否包含另一个二维几何数据结构,返回1代表在包含,返回0代表不包含。(我们使用到的场景是判断一个经纬度坐标点是否在某一行政区划多边形内)**ST_WITHIN()**函数与其表述方式相反,结果相反。
逆地理编码
逆地理编码指的是,将经纬度坐标转换为具体的标准的行政区划信息;以高德逆地理编码接口为例,坐标点118.797405,32.044227
响应的结果如下:
{
"status": "1",
"regeocode": {
"addressComponent": {
"city": "南京市",
"province": "江苏省",
"adcode": "320102",
"district": "玄武区",
"towncode": "320102002000",
"streetNumber": {
"number": "292号",
"location": "118.797317,32.044008",
"direction": "南",
"distance": "25.7166",
"street": "长江路"
},
"country": "中国",
"township": "梅园新村街道",
"citycode": "025"
},
"formatted_address": "江苏省南京市玄武区梅园新村街道总统府"
},
"info": "OK",
"infocode": "10000"
}
高德逆地理编码接口在线测试
精确度只有三级,同姐妹篇【利用GeoHash实现逆地理编码】
实现逆地理编码的基本思路
1.获取全国范围内所有级别行政区划地址围栏点
- 首先感谢高德,行政区划地址围栏坐标点的数据需要从高德开放平台获取(高德开放平台地址围栏点比腾讯地图提供的要精准),申请一个高德的Key,批量拉去数据即可。注意编写脚本时建议写入文本文件或者直接写入数据库,数据量比较大。
- 高德在线测试获取行政区划围栏点
拉取数据时,注意区/县/三级市级别行政区划名称有可能会重复,建议使用adcode字段进行区分。 - 拉取得围栏点在地图中示例(江苏省南京市玄武区)如下,比较可靠:
- 省、市、区县行政区划围栏点数据存储进mysql,落地结构为MultiPolygon。
2.计算全国范围所有级别行政区划MBR数据
- 遍历全国行政区划围栏点数据,分别获取省、市、区县/三级市行政区划MBR数据并通过mysql进行存储。
- 计算方式:遍历行政区划围栏点数据即可;比较简单,不再赘述。
- MBR数据同样落地在mysql中。
3.实现逆地理编码接口
- 根据目标经纬度坐标,遍历省级行政区划MBR数据;如果目标经纬度坐标在多个省份匹配最大经纬度坐标和最小经纬度坐标范围内,则遍历匹配省份的下一级(市级)行政区划MBR数据,同理找到可能匹配的区县/三级市新政区划。
- 使用空间算法,点是否包含在某一个行政区划内,遍历计算第一步筛选出的三级行政区划围栏点,最终确认目标经纬度坐标所在第三级行政区划。这一步计算不推荐使用mysql来完成(原因见mysql GIS支持章节),推荐java awt包来完成。
import java.awt.Polygon;
函数:
结尾
此篇文章主要是用于记录另一种逆地理编码的实现思路,可优化空间较大。
姐妹篇:利用GeoHash实现逆地理编码