You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
159 lines
4.9 KiB
159 lines
4.9 KiB
<?php
|
|
namespace Lvht;
|
|
|
|
class GeoHash
|
|
{
|
|
private static $table = "0123456789bcdefghjkmnpqrstuvwxyz";
|
|
private static $bits = array(
|
|
0b10000, 0b01000, 0b00100, 0b00010, 0b00001
|
|
);
|
|
|
|
public static function encode($lng, $lat, $prec = 0.00001)
|
|
{
|
|
$minlng = -180;
|
|
$maxlng = 180;
|
|
$minlat = -90;
|
|
$maxlat = 90;
|
|
|
|
$hash = array();
|
|
$error = 180;
|
|
$isEven = true;
|
|
$chr = 0b00000;
|
|
$b = 0;
|
|
|
|
while ($error >= $prec) {
|
|
if ($isEven) {
|
|
$next = ($minlng + $maxlng) / 2;
|
|
if ($lng > $next) {
|
|
$chr |= self::$bits[$b];
|
|
$minlng = $next;
|
|
} else {
|
|
$maxlng = $next;
|
|
}
|
|
} else {
|
|
$next = ($minlat + $maxlat) / 2;
|
|
if ($lat > $next) {
|
|
$chr |= self::$bits[$b];
|
|
$minlat = $next;
|
|
} else {
|
|
$maxlat = $next;
|
|
}
|
|
}
|
|
$isEven = !$isEven;
|
|
|
|
if ($b < 4) {
|
|
$b++;
|
|
} else {
|
|
$hash[] = self::$table[$chr];
|
|
$error = max($maxlng - $minlng, $maxlat - $minlat);
|
|
$b = 0;
|
|
$chr = 0b00000;
|
|
}
|
|
}
|
|
|
|
return join('', $hash);
|
|
}
|
|
|
|
public static function expand($hash, $prec = 0.00001)
|
|
{
|
|
list($minlng, $maxlng, $minlat, $maxlat) = self::decode($hash);
|
|
$dlng = ($maxlng - $minlng) / 2;
|
|
$dlat = ($maxlat - $minlat) / 2;
|
|
|
|
return array(
|
|
self::encode($minlng - $dlng, $maxlat + $dlat, $prec),
|
|
self::encode($minlng + $dlng, $maxlat + $dlat, $prec),
|
|
self::encode($maxlng + $dlng, $maxlat + $dlat, $prec),
|
|
self::encode($minlng - $dlng, $maxlat - $dlat, $prec),
|
|
self::encode($maxlng + $dlng, $maxlat - $dlat, $prec),
|
|
self::encode($minlng - $dlng, $minlat - $dlat, $prec),
|
|
self::encode($minlng + $dlng, $minlat - $dlat, $prec),
|
|
self::encode($maxlng + $dlng, $minlat - $dlat, $prec),
|
|
);
|
|
}
|
|
|
|
public static function getRect($hash)
|
|
{
|
|
list($minlng, $maxlng, $minlat, $maxlat) = self::decode($hash);
|
|
|
|
return array(
|
|
array($minlng, $minlat),
|
|
array($minlng, $maxlat),
|
|
array($maxlng, $maxlat),
|
|
array($maxlng, $minlat),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* decode a geohash string to a geographical area
|
|
*
|
|
* @var $hash string geohash
|
|
* @return array array($minlng, $maxlng, $minlat, $maxlat);
|
|
*/
|
|
public static function decode($hash)
|
|
{
|
|
$minlng = -180;
|
|
$maxlng = 180;
|
|
$minlat = -90;
|
|
$maxlat = 90;
|
|
|
|
for ($i=0,$c=strlen($hash); $i<$c; $i++) {
|
|
$v = strpos(self::$table, $hash[$i]);
|
|
if (1&$i) {
|
|
if (16&$v) {
|
|
$minlat = ($minlat + $maxlat) / 2;
|
|
} else {
|
|
$maxlat = ($minlat + $maxlat) / 2;
|
|
}
|
|
if (8&$v) {
|
|
$minlng = ($minlng + $maxlng) / 2;
|
|
} else {
|
|
$maxlng = ($minlng + $maxlng) / 2;
|
|
}
|
|
if (4&$v) {
|
|
$minlat = ($minlat + $maxlat) / 2;
|
|
} else {
|
|
$maxlat = ($minlat + $maxlat) / 2;
|
|
}
|
|
if (2&$v) {
|
|
$minlng = ($minlng + $maxlng) / 2;
|
|
} else {
|
|
$maxlng = ($minlng + $maxlng) / 2;
|
|
}
|
|
if (1&$v) {
|
|
$minlat = ($minlat + $maxlat) / 2;
|
|
} else {
|
|
$maxlat = ($minlat + $maxlat) / 2;
|
|
}
|
|
} else {
|
|
if (16&$v) {
|
|
$minlng = ($minlng + $maxlng) / 2;
|
|
} else {
|
|
$maxlng = ($minlng + $maxlng) / 2;
|
|
}
|
|
if (8&$v) {
|
|
$minlat = ($minlat + $maxlat) / 2;
|
|
} else {
|
|
$maxlat = ($minlat + $maxlat) / 2;
|
|
}
|
|
if (4&$v) {
|
|
$minlng = ($minlng + $maxlng) / 2;
|
|
} else {
|
|
$maxlng = ($minlng + $maxlng) / 2;
|
|
}
|
|
if (2&$v) {
|
|
$minlat = ($minlat + $maxlat) / 2;
|
|
} else {
|
|
$maxlat = ($minlat + $maxlat) / 2;
|
|
}
|
|
if (1&$v) {
|
|
$minlng = ($minlng + $maxlng) / 2;
|
|
} else {
|
|
$maxlng = ($minlng + $maxlng) / 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
return array($minlng, $maxlng, $minlat, $maxlat);
|
|
}
|
|
}
|
|
|