当前位置: 首页 > 工具软件 > PHP Geo > 使用案例 >

geohash php_php  GeoHash工具类

云京
2023-12-01

namespace Tool\qrcode;

define('TOP', 0);

define('RIGHT', 1);

define('BOTTOM', 2);

define('LEFT', 3);

define('EVEN', 0);

define('ODD', 1);

class GeoHash {

// Base32字符池

private $_charPool = '0123456789bcdefghjkmnpqrstuvwxyz';

// Base32字符池对应的二进制字符串

private $_charPoolBin = array(

'0' => '00000', '1' => '00001', '2' => '00010', '3'

=> '00011', '4' => '00100',

'5' => '00101', '6' => '00110', '7' => '00111', '8'

=> '01000', '9' => '01001',

'b' => '01010', 'c' => '01011', 'd' => '01100', 'e'

=> '01101', 'f' => '01110',

'g' => '01111', 'h' => '10000', 'j' => '10001', 'k'

=> '10010', 'm' => '10011',

'n' => '10100', 'p' => '10101', 'q' => '10110', 'r'

=> '10111', 's' => '11000',

't' => '11001', 'u' => '11010', 'v' => '11011', 'w'

=> '11100', 'x' => '11101',

'y' => '11110', 'z' => '11111',

);

private $_neighborChars = array(

EVEN => array(

TOP =>

'238967debc01fg45kmstqrwxuvhjyznp',

RIGHT =>

'14365h7k9dcfesgujnmqp0r2twvyx8zb',

BOTTOM => 'bc01fg45238967deuvhjyznpkmstqrwx',

LEFT =>

'p0r21436x8zb9dcf5h7kjnmqesgutwvy',

),

);

private $_borderChars = array(

EVEN => array(

TOP =>

'bcfguvyz',

RIGHT => 'prxz',

BOTTOM => '0145hjnp',

LEFT => '028b',

),

);

public function __construct() {

// 根据镜像翻转关系设置奇数位的情况

$this->_neighborChars[ODD] = array(

TOP =>

$this->_neighborChars[EVEN][RIGHT],

RIGHT =>

$this->_neighborChars[EVEN][TOP],

BOTTOM => $this->_neighborChars[EVEN][LEFT],

LEFT =>

$this->_neighborChars[EVEN][BOTTOM],

);

$this->_borderChars[ODD] = array(

TOP =>

$this->_borderChars[EVEN][RIGHT],

RIGHT =>

$this->_borderChars[EVEN][TOP],

BOTTOM => $this->_borderChars[EVEN][LEFT],

LEFT =>

$this->_borderChars[EVEN][BOTTOM],

);

}

public function _calcNeighbor($hash, $direction) {

$length = strlen($hash);

if ($length == 0) {

return '';

}

$lastChar = $hash{$length - 1};

$evenOrOdd = ($length - 1) % 2;

$baseHash = substr($hash, 0, -1);

if (strpos($this->_borderChars[$evenOrOdd][$direction],

$lastChar) !== false) {

$baseHash = $this->_calcNeighbor($baseHash,

$direction);

}

if (isset($baseHash{0})) {

return $baseHash .

$this->_neighborChars[$evenOrOdd][$direction]{strpos($this->_charPool,

$lastChar)};

} else {

return '';

}

}

private function _binEncode($decData, $min, $max, $precision)

{

$result = '';

for ($i = 0; $i < $precision; ++$i) {

$middle = ($min + $max) / 2;

if ($decData < $middle) {

$result .= '0';

$max = $middle;

} else {

$result .= '1';

$min = $middle;

}

}

return $result;

}

private function _binDecode($binData, $min, $max) {

$middle = ($min + $max) / 2;

$binLength = strlen($binData);

for ($i = 0; $i < $binLength; ++$i) {

if ($binData{$i} == '0') {

$max = $middle;

$middle = ($min + $middle) / 2;

} else {

$min = $middle;

$middle = ($middle + $max) / 2;

}

}

return $middle;

}

private function _binCombine($binFirst, $binSecond) {

$result = '';

$i = 0;

while (isset($binFirst{$i}) || isset($binSecond{$i})) {

$result .= (isset($binFirst{$i}) ? $binFirst{$i} : '') .

(isset($binSecond{$i}) ? $binSecond{$i} : '');

++$i;

}

return $result;

}

private function _binExplode($binData) {

$result = array(

0 => '',

1 => '',

);

$binLength = strlen($binData);

for ($i = 0; $i < $binLength; ++$i) {

$result[$i % 2] .= $binData{$i};

}

return $result;

}

private function _base32Encode($binData) {

$binLength = strlen($binData);

$result = '';

if ($binLength == 0) {

return $result;

}

$fix = 5 - ($binLength % 5);

if ($fix < 5) {

$binData .= str_repeat('0', $fix);

$binLength += $fix;

}

for ($i = 0; $i < $binLength; $i += 5) {

$tmp = substr($binData, $i, 5);

$result .= $this->_charPool{bindec($tmp)};

}

return $result;

}

private function _base32Decode($base32Data) {

$len = strlen($base32Data);

$result = '';

for ($i = 0; $i < $len; ++$i) {

$result .= $this->_charPoolBin[$base32Data{$i}];

}

return $result;

}

private function _calcPrecision($data, $basePrecision) {

$dotIndex = strpos($data, '.');

$result = 1;

if ($dotIndex === false) {

return $result;

}

$needPrecision = pow(10, -(strlen($data) - $dotIndex - 1)) /

2;

while ($basePrecision > $needPrecision) {

++$result;

$basePrecision /= 2;

}

return $result;

}

private function _calcError($length, $min, $max) {

$error = ($max - $min) / 2;

while ($length > 0) {

$error /= 2;

--$length;

}

return $error;

}

private function _calcDecodePrecision($length, $min, $max)

{

$error = $this->_calcError($length, $min, $max);

$tmp = 0.1;

$i = 0;

while ($tmp > $error) {

$tmp /= 10;

++$i;

}

return $i;

}

private function _bitsFix(&$longBits, &$latBits)

{

$maxBits = max($longBits, $latBits);

$longBits = $latBits = $maxBits;

$i = 0;

while (($longBits + $latBits) % 5 != 0) {

if ($i % 2 == 0) {

++$longBits;

} else {

++$latBits;

}

++$i;

}

}

public function encode($long, $lat) {

// 计算经纬度转换后所需的二进制长度

$longBit = $this->_calcPrecision($long, 90);

$latBit = $this->_calcPrecision($lat, 45);

// 修正上边的长度,使之和为5的倍数

$this->_bitsFix($longBit, $latBit);

// 对经纬度进行二进制编码

$longBin = $this->_binEncode($long, -180, 180,

$longBit);

$latBin = $this->_binEncode($lat, -90, 90, $latBit);

// 合并两个二进制编码

$combinedBin = $this->_binCombine($longBin, $latBin);

// Base32编码

return $this->_base32Encode($combinedBin);

}

public function decode($hash) {

// Base32解码

$combinedBin = $this->_base32Decode($hash);

// 拆分合并后的二进制编码

$result = $this->_binExplode($combinedBin);

$longBin = $result[0];

$latBin = $result[1];

// 二进制解码

$long = $this->_binDecode($longBin, -180, 180);

$lat = $this->_binDecode($latBin, -90, 90);

// 根据精度修正经纬度

$long = round($long,

$this->_calcDecodePrecision(strlen($longBin), -180, 180));

$lat = round($lat,

$this->_calcDecodePrecision(strlen($latBin), -90, 90));

return array($long, $lat);

}

public function neighbors($hash) {

$hashNorth = $this->_calcNeighbor($hash, TOP);

$hashEast = $this->_calcNeighbor($hash, RIGHT);

$hashSouth = $this->_calcNeighbor($hash, BOTTOM);

$hashWest = $this->_calcNeighbor($hash, LEFT);

$hashNorthEast = $this->_calcNeighbor($hashNorth,

RIGHT);

$hashSouthEast = $this->_calcNeighbor($hashSouth,

RIGHT);

$hashSouthWest = $this->_calcNeighbor($hashSouth,

LEFT);

$hashNorthWest = $this->_calcNeighbor($hashNorth,

LEFT);

return array(

'North' => &$hashNorth,

'East' => &$hashEast,

'South' => &$hashSouth,

'West' => &$hashWest,

'NorthEast' => &$hashNorthEast,

'SouthEast' => &$hashSouthEast,

'SouthWest' => &$hashSouthWest,

'NorthWest' => &$hashNorthWest,

);

}

}

 类似资料: