forked from phayes/geoPHP
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request phayes#42 from phayes/geohash-revamp
Geohash Revamp
- Loading branch information
Showing
4 changed files
with
171 additions
and
131 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,126 +1,163 @@ | ||
<?php | ||
/** | ||
* PHP Geometry GeoHash encoder/decoder. | ||
* | ||
* @author prinsmc | ||
* @see http://en.wikipedia.org/wiki/Geohash | ||
* | ||
*/ | ||
class GeoHash extends GeoAdapter{ | ||
private $table = "0123456789bcdefghjkmnpqrstuvwxyz"; | ||
|
||
/** | ||
* Convert the geohash to a Point. The point is 2-dimensional. | ||
* @return Point the converted geohash | ||
* @param string $hash a geohash | ||
* @see GeoAdapter::read() | ||
*/ | ||
public function read($hash) { | ||
$ll = $this->decode($hash); | ||
return new Point($ll['lon'], $ll['lat'], NULL); | ||
} | ||
|
||
/** | ||
* Convert the geometry to geohash. | ||
* @return string the geohash or null when the $geometry is not a Point | ||
* @param Point $geometry | ||
* @see GeoAdapter::write() | ||
*/ | ||
public function write(Geometry $geometry){ | ||
if($geometry->geometryType()==='Point'){ | ||
return $this->encode($geometry); | ||
} else { | ||
return NULL; | ||
} | ||
} | ||
|
||
/** | ||
* @return string geohash | ||
* @param Point $point | ||
* @author algorithm based on code by Alexander Songe <[email protected]> | ||
* @see https://github.com/asonge/php-geohash/issues/1 | ||
*/ | ||
private function encode($point){ | ||
$lap = strlen($point->y())-strpos($point->y(),"."); | ||
$lop = strlen($point->x())-strpos($point->x(),"."); | ||
$precision = pow(10,-max($lap-1,$lop-1,0))/2; | ||
|
||
$minlat = -90; | ||
$maxlat = 90; | ||
$minlng = -180; | ||
$maxlng = 180; | ||
$latE = 90; | ||
$lngE = 180; | ||
$i = 0; | ||
$error = 180; | ||
$hash=''; | ||
while($error>=$precision) { | ||
$chr = 0; | ||
for($b=4;$b>=0;--$b) { | ||
if((1&$b) == (1&$i)) { | ||
// even char, even bit OR odd char, odd bit...a lng | ||
$next = ($minlng+$maxlng)/2; | ||
if($point->x()>$next) { | ||
$chr |= pow(2,$b); | ||
$minlng = $next; | ||
} else { | ||
$maxlng = $next; | ||
} | ||
$lngE /= 2; | ||
} else { | ||
// odd char, even bit OR even char, odd bit...a lat | ||
$next = ($minlat+$maxlat)/2; | ||
if($point->y()>$next) { | ||
$chr |= pow(2,$b); | ||
$minlat = $next; | ||
} else { | ||
$maxlat = $next; | ||
} | ||
$latE /= 2; | ||
} | ||
} | ||
$hash .= $this->table[$chr]; | ||
$i++; | ||
$error = min($latE,$lngE); | ||
} | ||
return $hash; | ||
} | ||
|
||
/** | ||
* @param string $hash a geohash | ||
* @author algorithm based on code by Alexander Songe <[email protected]> | ||
* @see https://github.com/asonge/php-geohash/issues/1 | ||
*/ | ||
private function decode($hash){ | ||
$ll = array('lat'=>NULL,'lon'=>NULL); | ||
$minlat = -90; | ||
$maxlat = 90; | ||
$minlng = -180; | ||
$maxlng = 180; | ||
$latE = 90; | ||
$lngE = 180; | ||
for($i=0,$c=strlen($hash);$i<$c;$i++) { | ||
$v = strpos($this->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; | ||
$latE /= 8; | ||
$lngE /= 4; | ||
} 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; | ||
$latE /= 4; | ||
$lngE /= 8; | ||
} | ||
} | ||
$ll['lat'] = round(($minlat+$maxlat)/2, max(1, -round(log10($latE)))-1); | ||
$ll['lon'] = round(($minlng+$maxlng)/2, max(1, -round(log10($lngE)))-1); | ||
return $ll; | ||
} | ||
} | ||
<?php | ||
/** | ||
* PHP Geometry GeoHash encoder/decoder. | ||
* | ||
* @author prinsmc | ||
* @see http://en.wikipedia.org/wiki/Geohash | ||
* | ||
*/ | ||
class GeoHash extends GeoAdapter{ | ||
private $table = "0123456789bcdefghjkmnpqrstuvwxyz"; | ||
|
||
/** | ||
* Convert the geohash to a Point. The point is 2-dimensional. | ||
* @return Point the converted geohash | ||
* @param string $hash a geohash | ||
* @see GeoAdapter::read() | ||
*/ | ||
public function read($hash, $as_grid = FALSE) { | ||
$ll = $this->decode($hash); | ||
if (!$as_grid) { | ||
return new Point($ll['medlon'], $ll['medlat']); | ||
} | ||
else { | ||
return new Polygon(array( | ||
new LineString(array( | ||
new Point($ll['minlon'], $ll['maxlat']), | ||
new Point($ll['maxlon'], $ll['maxlat']), | ||
new Point($ll['maxlon'], $ll['minlat']), | ||
new Point($ll['minlon'], $ll['minlat']), | ||
new Point($ll['minlon'], $ll['maxlat']), | ||
)) | ||
)); | ||
} | ||
} | ||
|
||
/** | ||
* Convert the geometry to geohash. | ||
* @return string the geohash or null when the $geometry is not a Point | ||
* @param Point $geometry | ||
* @see GeoAdapter::write() | ||
*/ | ||
public function write(Geometry $geometry, $precision = NULL){ | ||
if($geometry->geometryType() === 'Point'){ | ||
return $this->encodePoint($geometry, $precision); | ||
} else { | ||
// The geohash is the hash grid ID that fits the envelope | ||
$envelope = $geometry->envelope(); | ||
$geohashes = array(); | ||
$geohash = ''; | ||
foreach ($envelope->getPoints() as $point) { | ||
$geohashes[] = $this->encodePoint($point, 0.0000001); | ||
} | ||
$i = 0; | ||
while ($i < strlen($geohashes[0])) { | ||
$char = $geohashes[0][$i]; | ||
foreach ($geohashes as $hash) { | ||
if ($hash[$i] != $char) { | ||
return $geohash; | ||
} | ||
} | ||
$geohash .= $char; | ||
$i++; | ||
} | ||
return $geohash; | ||
} | ||
} | ||
|
||
/** | ||
* @return string geohash | ||
* @param Point $point | ||
* @author algorithm based on code by Alexander Songe <[email protected]> | ||
* @see https://github.com/asonge/php-geohash/issues/1 | ||
*/ | ||
private function encodePoint($point, $precision = NULL){ | ||
if ($precision === NULL) { | ||
$lap = strlen($point->y())-strpos($point->y(),"."); | ||
$lop = strlen($point->x())-strpos($point->x(),"."); | ||
$precision = pow(10,-max($lap-1,$lop-1,0))/2; | ||
} | ||
|
||
$minlat = -90; | ||
$maxlat = 90; | ||
$minlon = -180; | ||
$maxlon = 180; | ||
$latE = 90; | ||
$lonE = 180; | ||
$i = 0; | ||
$error = 180; | ||
$hash=''; | ||
while($error>=$precision) { | ||
$chr = 0; | ||
for($b=4;$b>=0;--$b) { | ||
if((1&$b) == (1&$i)) { | ||
// even char, even bit OR odd char, odd bit...a lon | ||
$next = ($minlon+$maxlon)/2; | ||
if($point->x()>$next) { | ||
$chr |= pow(2,$b); | ||
$minlon = $next; | ||
} else { | ||
$maxlon = $next; | ||
} | ||
$lonE /= 2; | ||
} else { | ||
// odd char, even bit OR even char, odd bit...a lat | ||
$next = ($minlat+$maxlat)/2; | ||
if($point->y()>$next) { | ||
$chr |= pow(2,$b); | ||
$minlat = $next; | ||
} else { | ||
$maxlat = $next; | ||
} | ||
$latE /= 2; | ||
} | ||
} | ||
$hash .= $this->table[$chr]; | ||
$i++; | ||
$error = min($latE,$lonE); | ||
} | ||
return $hash; | ||
} | ||
|
||
/** | ||
* @param string $hash a geohash | ||
* @author algorithm based on code by Alexander Songe <[email protected]> | ||
* @see https://github.com/asonge/php-geohash/issues/1 | ||
*/ | ||
private function decode($hash){ | ||
$ll = array(); | ||
$minlat = -90; | ||
$maxlat = 90; | ||
$minlon = -180; | ||
$maxlon = 180; | ||
$latE = 90; | ||
$lonE = 180; | ||
for($i=0,$c=strlen($hash);$i<$c;$i++) { | ||
$v = strpos($this->table,$hash[$i]); | ||
if(1&$i) { | ||
if(16&$v)$minlat = ($minlat+$maxlat)/2; else $maxlat = ($minlat+$maxlat)/2; | ||
if(8&$v) $minlon = ($minlon+$maxlon)/2; else $maxlon = ($minlon+$maxlon)/2; | ||
if(4&$v) $minlat = ($minlat+$maxlat)/2; else $maxlat = ($minlat+$maxlat)/2; | ||
if(2&$v) $minlon = ($minlon+$maxlon)/2; else $maxlon = ($minlon+$maxlon)/2; | ||
if(1&$v) $minlat = ($minlat+$maxlat)/2; else $maxlat = ($minlat+$maxlat)/2; | ||
$latE /= 8; | ||
$lonE /= 4; | ||
} else { | ||
if(16&$v)$minlon = ($minlon+$maxlon)/2; else $maxlon = ($minlon+$maxlon)/2; | ||
if(8&$v) $minlat = ($minlat+$maxlat)/2; else $maxlat = ($minlat+$maxlat)/2; | ||
if(4&$v) $minlon = ($minlon+$maxlon)/2; else $maxlon = ($minlon+$maxlon)/2; | ||
if(2&$v) $minlat = ($minlat+$maxlat)/2; else $maxlat = ($minlat+$maxlat)/2; | ||
if(1&$v) $minlon = ($minlon+$maxlon)/2; else $maxlon = ($minlon+$maxlon)/2; | ||
$latE /= 4; | ||
$lonE /= 8; | ||
} | ||
} | ||
$ll['minlat'] = $minlat; | ||
$ll['minlon'] = $minlon; | ||
$ll['maxlat'] = $maxlat; | ||
$ll['maxlon'] = $maxlon; | ||
$ll['medlat'] = round(($minlat+$maxlat)/2, max(1, -round(log10($latE)))-1); | ||
$ll['medlon'] = round(($minlon+$maxlon)/2, max(1, -round(log10($lonE)))-1); | ||
return $ll; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
xn76urx4epb0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
xpssc0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters