附近搜索 geohash.neighbors 生成周边8个geohash(如下图),生成的长方形区域,与我要的搜索附近半径几公里以内肯定不相符。找到老外写的 https://github.com/ashwin711/proximityhash 。
需要 php版本代码 @ 我
<?php
/**
* author https://my.oschina.net/jszhang
* copy2php https://github.com/ashwin711/proximityhash
*/
// demo
// ProximityHash::create_geohash($lat, $lng, 1000, 6);
class ProximityHash
{
public static function in_circle_check($latitude, $longitude, $centre_lat, $centre_lon, $radius)
{
$x_diff = $longitude - $centre_lon;
$y_diff = $latitude - $centre_lat;
if (pow($x_diff, 2) + pow($y_diff, 2) <= pow($radius, 2)) {
return true;
}
return false;
}
public static function get_centroid($latitude, $longitude, $height, $width)
{
$y_cen = $latitude + ($height / 2);
$x_cen = $longitude + ($width / 2);
return [$x_cen, $y_cen];
}
public static function convert_to_latlon($y, $x, $latitude, $longitude)
{
$pi = 3.14159265359;
$r_earth = 6371000;
$lat_diff = ($y / $r_earth) * (180 / $pi);
$lon_diff = ($x / $r_earth) * (180 / $pi) / cos($latitude * $pi / 180);
$final_lat = $latitude + $lat_diff;
$final_lon = $longitude + $lon_diff;
return [$final_lat, $final_lon];
}
public static function create_geohash($latitude, $longitude, $radius, $precision, $georaptor_flag = false, $minlevel = 1, $maxlevel = 12)
{
$x = 0.0;
$y = 0.0;
$points = [];
$geohashes = [];
$grid_width = [5009400.0, 1252300.0, 156500.0, 39100.0, 4900.0, 1200.0, 152.9, 38.2, 4.8, 1.2, 0.149, 0.0370];
$grid_height = [4992600.0, 624100.0, 156000.0, 19500.0, 4900.0, 609.4, 152.4, 19.0, 4.8, 0.595, 0.149, 0.0199];
$height = ($grid_height[$precision - 1]) / 2;
$width = ($grid_width[$precision - 1]) / 2;
$lat_moves = intval(ceil($radius / $height));
$lon_moves = intval(ceil($radius / $width));
foreach (range(0, $lat_moves) as $i) {
$temp_lat = $y + $height * $i;
foreach (range(0, $lon_moves) as $j) {
$temp_lon = $x + $width * $j;
if (self::in_circle_check($temp_lat, $temp_lon, $y, $x, $radius)) {
list($x_cen, $y_cen) = self::get_centroid($temp_lat, $temp_lon, $height, $width);
$points[] = self::convert_to_latlon($y_cen, $x_cen, $latitude, $longitude);
$points[] = self::convert_to_latlon(-$y_cen, $x_cen, $latitude, $longitude);
$points[] = self::convert_to_latlon($y_cen, -$x_cen, $latitude, $longitude);
$points[] = self::convert_to_latlon(-$y_cen, -$x_cen, $latitude, $longitude);
}
}
}
foreach ($points as $point) {
$geohashes[] = GeoHash::encode($point[0], $point[1], $precision);
}
$geohashes = array_unique($geohashes);
if ($georaptor_flag) {
// GeoRaptor我没copy
// return GeoRaptor::compress($geohashes, $minlevel, $maxlevel);
return $geohashes;
} else {
return $geohashes;
}
}
}
<?php
// author https://my.oschina.net/jszhang
class GeoHash
{
const BASE32_CODES = "0123456789bcdefghjkmnpqrstuvwxyz";
const BASE32_CODES_DICT = ['0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9, 'b' => 10, 'c' => 11, 'd' => 12, 'e' => 13, 'f' => 14, 'g' => 15, 'h' => 16, 'j' => 17, 'k' => 18, 'm' => 19, 'n' => 20, 'p' => 21, 'q' => 22, 'r' => 23, 's' => 24, 't' => 25, 'u' => 26, 'v' => 27, 'w' => 28, 'x' => 29, 'y' => 30, 'z' => 31];
// const BASE32_CODES_DICT = array_flip(explode('', self::BASE32_CODES));
/**
* Significant Figure Hash Length
*
* This is a quick and dirty lookup to figure out how long our hash
* should be in order to guarantee a certain amount of trailing
* significant figures. This was calculated by determining the error:
* 45/2^(n-1) where n is the number of bits for a latitude or
* longitude. Key is # of desired sig figs, value is minimum length of
* the geohash.
* @type Array
*/
// Desired sig figs: 0 1 2 3 4 5 6 7 8 9 10
const SIGFIG_HASH_LENGTH = [0, 5, 7, 8, 11, 12, 13, 15, 16, 17, 18];
public static function encode($latitude, $longitude, $numberOfChars = '')
{
if ($numberOfChars === 'auto') {
$decSigFigsLat = strlen(strstr($latitude, '.')) - 1;
$decSigFigsLong = strlen(strstr($longitude, '.')) - 1;
$numberOfSigFigs = max($decSigFigsLat, $decSigFigsLong);
$numberOfChars = self::SIGFIG_HASH_LENGTH[$numberOfSigFigs];
} else if (empty($numberOfChars)) {
$numberOfChars = 9;
}
$chars = [];
$bits = 0;
$bitsTotal = 0;
$hash_value = 0;
$maxLat = 90;
$minLat = -90;
$maxLon = 180;
$minLon = -180;
$mid = 0;
while (count($chars) < $numberOfChars) {
if ($bitsTotal % 2 === 0) {
$mid = ($maxLon + $minLon) / 2;
if ($longitude > $mid) {
$hash_value = ($hash_value << 1) + 1;
$minLon = $mid;
} else {
$hash_value = ($hash_value << 1) + 0;
$maxLon = $mid;
}
} else {
$mid = ($maxLat + $minLat) / 2;
if ($latitude > $mid) {
$hash_value = ($hash_value << 1) + 1;
$minLat = $mid;
} else {
$hash_value = ($hash_value << 1) + 0;
$maxLat = $mid;
}
}
$bits++;
$bitsTotal++;
if ($bits === 5) {
$chars[] = self::BASE32_CODES[$hash_value];
$bits = 0;
$hash_value = 0;
}
}
return implode('', $chars);
}
public static function decode($hash_string)
{
$bbox = self::decode_bbox($hash_string);
$lat = ($bbox[0] + $bbox[2]) / 2;
$lon = ($bbox[1] + $bbox[3]) / 2;
$latErr = $bbox[2] - $lat;
$lonErr = $bbox[3] - $lon;
return [
'latitude' => $lat,
'longitude' => $lon,
'error' => ['latitude' => $latErr, 'longitude' => $lonErr],
];
}
public static function decode_bbox($hash_string)
{
$isLon = true;
$maxLat = 90;
$minLat = -90;
$maxLon = 180;
$minLon = -180;
$mid;
$hashValue = 0;
for ($i = 0, $l = strlen($hash_string); $i < $l; $i++) {
$code = $hash_string[$i];
$hashValue = self::BASE32_CODES_DICT[$code];
for ($bits = 4; $bits >= 0; $bits--) {
$bit = ($hashValue >> $bits) & 1;
if ($isLon) {
$mid = ($maxLon + $minLon) / 2;
if ($bit === 1) {
$minLon = $mid;
} else {
$maxLon = $mid;
}
} else {
$mid = ($maxLat + $minLat) / 2;
if ($bit === 1) {
$minLat = $mid;
} else {
$maxLat = $mid;
}
}
$isLon = !$isLon;
}
}
return [$minLat, $minLon, $maxLat, $maxLon];
}
/**
* Neighbors
*
* Returns all neighbors' hashstrings clockwise from north around to northwest
* 7 0 1
* 6 x 2
* 5 4 3
* @param {String} hash_string
* @returns {encoded neighborHashList|Array}
*/
public static function neighbors($hash_string)
{
$hashstringLength = strlen($hash_string);
$lonlat = self::decode($hash_string);
$lat = $lonlat['latitude'];
$lon = $lonlat['longitude'];
$latErr = $lonlat['error']['latitude'] * 2;
$lonErr = $lonlat['error']['longitude'] * 2;
$List = [
[1, 0],
[1, 1],
[0, 1],
[-1, 1],
[-1, 0],
[-1, -1],
[0, -1],
[1, -1],
];
$neighborHashList = array_map(function ($neighbor) use ($lat, $lon, $latErr, $lonErr, $hashstringLength) {
$neighbor_lat = $lat + $neighbor[0] * $latErr;
$neighbor_lon = $lon + $neighbor[1] * $lonErr;
return self::encode($neighbor_lat, $neighbor_lon, $hashstringLength);
}, $List);
return $neighborHashList;
}
public function get_box($arr)
{
$minLat = $arr[0][1];
$minLon = $arr[0][0];
$maxLat = $arr[0][1];
$maxLon = $arr[0][0];
foreach ($arr as $point) {
$minLat = $point[1] > $minLat ? $minLat : $point[1];
$minLon = $point[0] > $minLon ? $minLon : $point[0];
$maxLat = $point[1] < $maxLat ? $maxLat : $point[1];
$maxLon = $point[0] < $maxLon ? $maxLon : $point[0];
}
return [$minLat, $minLon, $maxLat, $maxLon];
}
}