说明
- 在Mysql中可以利用空间函数
ST_GeoHash
方便快捷的实现GeoHash值的计算
SELECT ST_GeoHash(120.190856853749, 30.1862355009353,12)
- 关于GeoHash算法的原理,就是不停地遍历找到最适合目标点的一个区域范围。关于详细说明可以参考下面资料中的第一个链接。
- GeoHash中的精度也很好理解,就是遍历的次数越多,找到的范围越小,误差也就越小。默认的话一般12位就可以,已经是很精确了。
- 已经验证过这个算法和Mysql中的计算结果是一致的。
- 如果代码中有看不明白的地方可以询问。
实现代码
function CreateGeoHash(longitude, latitude, length) {
if (!longitude && !latitude) {
console.error('请传入经纬度')
}
if(typeof longitude =="string") {
longitude = Number(longitude)
}
if(typeof latitude =="string") {
latitude = Number(latitude)
}
// 获取base32编码
const Base32 = function(str) {
let num = str
if(typeof num == "string") {
num = Number(str)
}
if(num >= 0 && num <=9) {
return num.toString()
} else if(num >= 10 && num <= 16){
return String.fromCharCode(98 + str - 10)
} else if(num >= 17 && num <= 18){
return String.fromCharCode(106 + str - 17)
} else if(num >= 19 && num <= 20){
return String.fromCharCode(109 + str - 19)
} else if(num >= 21 && num <= 31){
return String.fromCharCode(112 + str - 21)
}
}
let lngExtent = [-180, 180]
let latExtent = [-90, 90]
let accuracy = length || 12 // 精度
let times = (accuracy * 5) / 2
let result = []
let lngList = [] // 经度的二进制结果
let latList = [] // 纬度的二进制结果
let lngAverage = 0
let latAverage = 0
let lngLeftRange = []
let lngRightRange = []
let latLeftRange = []
let latRightRange = []
for (let i = 0; i < times; i++) {
lngAverage = eval(lngExtent.join("+")) / lngExtent.length
latAverage = eval(latExtent.join("+")) / latExtent.length
lngLeftRange = [lngExtent[0], lngAverage]
lngRightRange = [lngAverage, lngExtent[1]]
latLeftRange = [latExtent[0], latAverage]
latRightRange = [latAverage, latExtent[1]]
if (longitude >= lngLeftRange[0] && longitude < lngLeftRange[1]) {
lngList.push(0)
lngExtent = lngLeftRange
} else {
lngList.push(1)
lngExtent = lngRightRange
}
if (latitude >= latLeftRange[0] && latitude < latLeftRange[1]) {
latList.push(0)
latExtent = latLeftRange
} else {
latList.push(1)
latExtent = latRightRange
}
}
let lngNum = 0
let latNum = 0
// 合并:偶数位放经度,奇数位放纬度
for (let j = 0; j < times * 2; j++) {
if (j % 2 == 0) {
result.push(lngList[lngNum])
lngNum++
} else {
result.push(latList[latNum])
latNum++
}
}
// 转为十进制,再转base32编码
let geohash = ''
let k = 0
for (let k = 0; k < result.length; k = k + 5) {
let arr = result.slice(k, k + 5)
let sum = parseInt(arr.join(''), 2)
let base32 = Base32(sum)
geohash += base32
}
return geohash
}
实现结果
const result = CreateGeoHash(120.190856853749, 30.1862355009353, 12)
console.log(result)
// 输出结果:wtm7xp0r9fns
参考资料
ASC码对照表:
Base32编码对照表: