| xqd
@@ -0,0 +1,142 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+
|
|
|
+namespace App\Services;
|
|
|
+
|
|
|
+
|
|
|
+class GeohashService
|
|
|
+{
|
|
|
+ private $coding = "0123456789bcdefghjkmnpqrstuvwxyz";
|
|
|
+ private $codingMap = array();
|
|
|
+
|
|
|
+ public function Geohash() {
|
|
|
+ for($i = 0; $i < 32; $i++) {
|
|
|
+ $this->codingMap[substr($this->coding, $i, 1)] = str_pad(decbin($i), 5, "0", STR_PAD_LEFT);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public function decode($hash) {
|
|
|
+ $binary = "";
|
|
|
+ $hl = strlen($hash);
|
|
|
+ for($i = 0; $i < $hl; $i++) {
|
|
|
+ $binary .= $this->codingMap[substr($hash, $i, 1)];
|
|
|
+ }
|
|
|
+
|
|
|
+ $bl = strlen($binary);
|
|
|
+ $blat = "";
|
|
|
+ $blong = "";
|
|
|
+ for ($i = 0; $i < $bl; $i++) {
|
|
|
+ if ($i%2) {
|
|
|
+ $blat = $blat.substr($binary, $i, 1);
|
|
|
+ } else {
|
|
|
+ $blong = $blong.substr($binary, $i, 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ $lat = $this->binDecode($blat, -90, 90);
|
|
|
+ $long = $this->binDecode($blong, -180, 180);
|
|
|
+
|
|
|
+ $latErr = $this->calcError(strlen($blat), -90, 90);
|
|
|
+ $longErr = $this->calcError(strlen($blong), -180, 180);
|
|
|
+
|
|
|
+ $latPlaces = max(1, -round(log10($latErr))) - 1;
|
|
|
+ $longPlaces = max(1, -round(log10($longErr))) - 1;
|
|
|
+
|
|
|
+ $lat = round($lat, $latPlaces);
|
|
|
+ $long = round($long, $longPlaces);
|
|
|
+
|
|
|
+ return array($lat,$long);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function encode($lat,$long) {
|
|
|
+ $plat = $this->precision($lat);
|
|
|
+ $latbits = 1;
|
|
|
+ $err = 45;
|
|
|
+ while($err > $plat) {
|
|
|
+ $latbits++;
|
|
|
+ $err/= 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ $plong = $this->precision($long);
|
|
|
+ $longbits = 1;
|
|
|
+ $err = 90;
|
|
|
+ while($err > $plong) {
|
|
|
+ $longbits++;
|
|
|
+ $err /= 2;
|
|
|
+ }
|
|
|
+ $bits = max($latbits,$longbits);
|
|
|
+ $longbits = $bits;
|
|
|
+ $latbits = $bits;
|
|
|
+ $addlong = 1;
|
|
|
+ while (($longbits+$latbits) % 5 != 0) {
|
|
|
+ $longbits += $addlong;
|
|
|
+ $latbits += !$addlong;
|
|
|
+ $addlong = !$addlong;
|
|
|
+ }
|
|
|
+ $blat = $this->binEncode($lat, -90, 90, $latbits);
|
|
|
+ $blong = $this->binEncode($long, -180, 180, $longbits);
|
|
|
+ $binary = "";
|
|
|
+ $uselong = 1;
|
|
|
+ while (strlen($blat)+strlen($blong)) {
|
|
|
+ if ($uselong) {
|
|
|
+ $binary = $binary.substr($blong, 0, 1);
|
|
|
+ $blong = substr($blong, 1);
|
|
|
+ } else {
|
|
|
+ $binary = $binary.substr($blat, 0, 1);
|
|
|
+ $blat = substr($blat, 1);
|
|
|
+ }
|
|
|
+ $uselong = !$uselong;
|
|
|
+ }
|
|
|
+ $hash = "";
|
|
|
+ for ($i = 0; $i < strlen($binary); $i += 5) {
|
|
|
+ $n = bindec(substr($binary, $i, 5));
|
|
|
+ $hash = $hash . $this->coding[$n];
|
|
|
+ }
|
|
|
+ return $hash;
|
|
|
+ }
|
|
|
+
|
|
|
+ private function calcError($bits, $min, $max) {
|
|
|
+ $err = ($max - $min) / 2;
|
|
|
+ while ($bits--) {
|
|
|
+ $err /= 2;
|
|
|
+ }
|
|
|
+ return $err;
|
|
|
+ }
|
|
|
+
|
|
|
+ private function precision($number) {
|
|
|
+ $precision = 0;
|
|
|
+ $pt = strpos($number,'.');
|
|
|
+ if ($pt!== false) {
|
|
|
+ $precision = -(strlen($number) - $pt - 1);
|
|
|
+ }
|
|
|
+ return pow(10, $precision) / 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ private function binEncode($number, $min, $max, $bitcount) {
|
|
|
+ if ($bitcount == 0) {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ $mid = ($min + $max) / 2;
|
|
|
+ if ($number > $mid) {
|
|
|
+ return "1" . $this->binEncode($number, $mid, $max, $bitcount - 1);
|
|
|
+ } else {
|
|
|
+ return "0" . $this->binEncode($number, $min, $mid, $bitcount - 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private function binDecode($binary, $min, $max) {
|
|
|
+ $mid = ($min + $max) / 2;
|
|
|
+
|
|
|
+ if (strlen($binary) == 0) {
|
|
|
+ return $mid;
|
|
|
+ }
|
|
|
+ $bit = substr($binary, 0, 1);
|
|
|
+ $binary = substr($binary, 1);
|
|
|
+
|
|
|
+ if ($bit == 1) {
|
|
|
+ return $this->binDecode($binary, $mid, $max);
|
|
|
+ } else {
|
|
|
+ return $this->binDecode($binary, $min, $mid);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|