<?php
/**
* 生成指定區(qū)域內的隨機經緯度(以北京區(qū)域為例,方便驗證效果)
* 北京大致范圍:緯度39.4°~41.6°,經度115.4°~117.8°
* @return array [lat, lng] 隨機緯度、經度
*/
function generate_random_lnglat() {
// 隨機緯度(39.4 ~ 41.6)
$lat = 39.4 + mt_rand(0, 220000) / 100000;
// 隨機經度(115.4 ~ 117.8)
$lng = 115.4 + mt_rand(0, 240000) / 100000;
// 保留6位小數(shù)(與數(shù)據(jù)表字段一致)
return [
round($lat, 6),
round($lng, 6)
];
}
/**
* GeoHash編碼核心函數(shù):將經緯度轉換為GeoHash字符串
* GeoHash原理:將地球表面劃分為網格,每個網格用一串字符表示,字符越長精度越高
* @param float $lat 緯度(-90 ~ 90)
* @param float $lng 經度(-180 ~ 180)
* @param int $precision GeoHash精度(默認8位,6位約對應10km范圍,8位約對應19m范圍)
* @return string 生成的GeoHash字符串
*/
function geohash_encode($lat, $lng, $precision = 8) {
$base32 = '0123456789bcdefghjkmnpqrstuvwxyz';
$latRange = [-90.0, 90.0];
$lngRange = [-180.0, 180.0];
$binary = '';
while (strlen($binary) < $precision * 5) {
$lngMid = ($lngRange[0] + $lngRange[1]) / 2;
if ($lng > $lngMid) {
$binary .= '1';
$lngRange[0] = $lngMid;
} else {
$binary .= '0';
$lngRange[1] = $lngMid;
}
$latMid = ($latRange[0] + $latRange[1]) / 2;
if ($lat > $latMid) {
$binary .= '1';
$latRange[0] = $latMid;
} else {
$binary .= '0';
$latRange[1] = $latMid;
}
}
$geohash = '';
for ($i = 0; $i < strlen($binary); $i += 5) {
$chunk = substr($binary, $i, 5);
$index = bindec($chunk);
$geohash .= $base32[$index];
}
return substr($geohash, 0, $precision);
}
/**
* 計算6位GeoHash的8個相鄰區(qū)域(上、右上、右、右下、下、左下、左、左上)
* 6位GeoHash是常用精度,對應約1.2km×0.6km的矩形區(qū)域
* @param string $geohash6 6位的GeoHash字符串
* @return array 包含自身和8個鄰居的GeoHash數(shù)組(共9個)
*/
function geohash_get_6bit_neighbors($geohash6) {
$neighbors = [
'north' => ['p0r21436x8zb9dcf5h7kjnmqesgutwvy', 'bc01fg45238967deuvhjyznpkmstqrwx'],
'northeast' => ['14365h7k9dcfesgujnmqp0r2twvyx8zb', '238967debc01fg45kmstqrwxuvhjyznp'],
'east' => ['bc01fg45238967deuvhjyznpkmstqrwx', 'p0r21436x8zb9dcf5h7kjnmqesgutwvy'],
'southeast' => ['238967debc01fg45kmstqrwxuvhjyznp', '14365h7k9dcfesgujnmqp0r2twvyx8zb'],
'south' => ['14365h7k9dcfesgujnmqp0r2twvyx8zb', '238967debc01fg45kmstqrwxuvhjyznp'],
'southwest' => ['p0r21436x8zb9dcf5h7kjnmqesgutwvy', 'bc01fg45238967deuvhjyznpkmstqrwx'],
'west' => ['bc01fg45238967deuvhjyznpkmstqrwx', 'p0r21436x8zb9dcf5h7kjnmqesgutwvy'],
'northwest' => ['238967debc01fg45kmstqrwxuvhjyznp', '14365h7k9dcfesgujnmqp0r2twvyx8zb']
];
$borders = [
'north' => ['prxz', 'bcfguvyz'],
'northeast' => ['prxz', 'bcfguvyz'],
'east' => ['bcfguvyz', 'prxz'],
'southeast' => ['bcfguvyz', 'prxz'],
'south' => ['028b', '0145hjnp'],
'southwest' => ['028b', '0145hjnp'],
'west' => ['0145hjnp', '028b'],
'northwest' => ['0145hjnp', '028b']
];
$geohash = strtolower($geohash6);
$len = strlen($geohash);
if ($len !== 6) {
return [$geohash6];
}
$base32 = '0123456789bcdefghjkmnpqrstuvwxyz';
$base32Idx = array_flip(str_split($base32));
$neighborsList = [$geohash6];
// 遍歷8個方向
$directions = ['north', 'northeast', 'east', 'southeast', 'south', 'southwest', 'west', 'northwest'];
foreach ($directions as $dir) {
$neighbor = geohash_calculate_neighbor($geohash, $dir, $neighbors, $borders, $base32, $base32Idx);
if ($neighbor && !in_array($neighbor, $neighborsList)) {
$neighborsList[] = $neighbor;
}
}
return $neighborsList;
}
/**
* 遞歸計算單個方向的GeoHash鄰居(核心輔助函數(shù))
* @param string $hash 原始GeoHash字符串
* @param string $dir 方向(north/east/south/west等)
* @param array $neighbors 鄰居字符映射表
* @param array $borders 邊界字符表
* @param string $base32 32進制編碼表
* @param array $base32Idx 32進制字符索引映射
* @return string|null 計算出的鄰居GeoHash,越界則返回null
*/
function geohash_calculate_neighbor($hash, $dir, $neighbors, $borders, $base32, $base32Idx) {
$len = strlen($hash);
$last = substr($hash, -1);
$prefix = substr($hash, 0, $len - 1);
$isLat = ($len % 2) == 1; // true: 最后一位是緯度
// 檢查是否在邊界,需要遞歸進位
$borderChars = $borders[$dir][$isLat ? 1 : 0];
if (in_array($last, str_split($borderChars)) && $prefix !== '') {
$prefix = geohash_calculate_neighbor($prefix, $dir, $neighbors, $borders, $base32, $base32Idx);
if ($prefix === null) {
return null; // 越界,無鄰居
}
}
// 獲取原字符在 base32 中的位置
if (!isset($base32Idx[$last])) {
return null; // 非法字符
}
$idx = $base32Idx[$last];
// 從 neighbor 映射表中取對應字符
$neighborMap = $neighbors[$dir][$isLat ? 1 : 0];
if ($idx >= strlen($neighborMap)) {
return null; // 安全檢查
}
$newLast = $neighborMap[$idx];
return $prefix . $newLast;
}
geohash編碼和周邊查詢
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
相關閱讀更多精彩內容
- Laravel GeoHash Laravel GeoHash LBS地理位置距離計算方法geohash,將一個經...
- 1.場景 隨著智能手機和傳感器技術的發(fā)展,LBS(Location based service)類的應用也逐漸多了...
- 一、聯(lián)聯(lián)周邊游邀請碼填什么填多少 1、聯(lián)聯(lián)周邊游邀請碼填寫:555888 (這是高省APP的) ,這樣可以獲得高傭...
- 21世紀當下互聯(lián)網時代,網購逐漸的取代我們線下實體店購物。聯(lián)聯(lián)周邊游是順勢互聯(lián)網為廣大用戶研發(fā)的一款新型返利App...