GeoHash是一种将二维地理坐标(经纬度)转换为一维字符串的技术,通过递归划分地球表面区域实现高效索引。
GeoHash是一种将二维地理坐标(经纬度)转换为一维字符串的技术,通过递归划分地球表面区域实现高效索引。
基本原理
GeoHash将经纬度范围划分为矩形区域,每个区域分配相同编码。编码通过二进制序列表示经纬度位置,通过奇偶位交替记录纬度与经度信息,最终转换为Base32编码的字符串。
核心特点
- 空间划分:通过经纬度二分法递归分割地球表面,精度由划分次数决定(如12次划分生成12位编码)。
- 编码规则:纬度用奇数位记录,经度用偶数位记录,合并后形成Base32编码字符串。
- 应用场景:用于地理位置索引、隐私保护及高效查询,例如Elasticsearch等系统采用GeoHash优化地理数据检索。
示例(以北京为例)
假设北京某点经纬度为(116.389550, 39.928167),通过GeoHash算法可生成类似"WX4ER"的编码,该编码代表北京周边区域。
/**
*
*/
function getGeoHash(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
}
下面是一个用 JavaScript 实现的 GeoHash 解码函数,它可以将 GeoHash 字符串转换为对应的经纬度坐标。
这个函数的工作原理是:
- 使用 GeoHash 标准的 Base32 字符集进行解码
- 交替处理经度和纬度(偶数位处理经度,奇数位处理纬度)
- 对每个字符解析为 5 位二进制数,逐步缩小经纬度范围
- 最终取经纬度区间的中点作为解码结果
- 同时计算出该 GeoHash 对应的位置精度误差
/**
* 解码GeoHash字符串,返回经纬度的最小值和最大值
* @param {string} geohash - GeoHash字符串
* @returns {{lng_min: number, lng_max: number, lng_center: number, lng_error: number, lat_min: number, lat_max: number, lat_center: number, lat_error: number}} - 经纬度的最小值、最大值、中心点、中心点偏差
*/
function decodeGeoHash(geohash) {
// GeoHash 编码使用的 Base32 字符集
const base32 = '0123456789bcdefghjkmnpqrstuvwxyz';
let isEven = true;
let latitude = [-90, 90];
let longitude = [-180, 180];
// 遍历 GeoHash 中的每个字符
for (let i = 0; i < geohash.length; i++) {
const c = geohash[i];
// 找到字符在 base32 中的索引
let index = base32.indexOf(c);
if (index === -1) {
throw new Error('Invalid GeoHash character: ' + c);
}
// 解析 5 位二进制
for (let j = 4; j >= 0; j--) {
const bit = (index >> j) & 1;
if (isEven) {
// 偶数位处理经度
const mid = (longitude[0] + longitude[1]) / 2;
if (bit === 1) {
longitude[0] = mid;
} else {
longitude[1] = mid;
}
} else {
// 奇数位处理纬度
const mid = (latitude[0] + latitude[1]) / 2;
if (bit === 1) {
latitude[0] = mid;
} else {
latitude[1] = mid;
}
}
isEven = !isEven;
}
}
return {
lng_min: longitude[0],
lng_max: longitude[1],
lng_center: (latitude[0] + latitude[1]) / 2,
lng_error: (longitude[1] - longitude[0]) / 2,
lat_min: latitude[0],
lat_max: latitude[1],
lat_center: (latitude[0] + latitude[1]) / 2,
lat_error: (latitude[1] - latitude[0]) / 2
};
}
骑着母猪去打猎的备忘录,如有侵权请联系本人骑着母猪去打猎删除。https://www.cnblogs.com/shichq/p/19021694
如果本文对您有所帮助,麻烦在下面评论里面随便敲上那么几下键盘,谢谢了
浙公网安备 33010602011771号