# geohash 附近搜索

2018/03/15 17:21

<?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

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];
}
}


0
1 收藏

3Q~~
2019/01/29 12:35

2019/01/29 11:53

2019/01/29 11:53

2019/01/28 16:28

2019/01/28 16:22

5 评论
1 收藏
0