Skip to content

Commit

Permalink
Add getNeighbors function to compute 8 neighbor hashes of a hash. Upg…
Browse files Browse the repository at this point in the history
…rade PHP version. Update README. (#4)
  • Loading branch information
craastad authored and skthon committed Aug 24, 2019
1 parent 90b3d24 commit 9cd7e05
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 4 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,22 @@ The result is
latitude : 17.38, longitude : 78.42
~~~

Get geohashes of 8 neighbors of a geohash
~~~
use Sk\Geohash\Geohash;
$g = new Geohash();
$hash = $g->encode(25.813646, -80.133761, 7);
$neighbors = $g->getNeighbors($hash);
echo "Hash: $hash\n";
echo "Neighbors: " . json_encode($neighors) . "\n";
~~~
The result is
~~~
Hash:
Neighbors: {"North":"dhx4be2","East":"dhx4be1","South":"dhx4bdb","West":"dhx4b7p","NorthEast":"dhx4be3","SouthEast":"dhx4bdc","SouthWest":"dhx4b6z","NorthWest":"dhx4b7r"}
~~~

Running the unit tests
-------
Go to this directory from your project folder
Expand Down
9 changes: 7 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@
{
"name": "Saikiran Ch",
"email": "[email protected]"
},{
"name": "Chris Raastad",
"email": "[email protected]"
}
],
"require": {},
"require": {
"php": ">=7.0.0"
},
"require-dev": {
"phpunit/phpunit": "4.0.*"
"phpunit/phpunit": "~6.0"
},
"autoload": {
"psr-4": {
Expand Down
95 changes: 95 additions & 0 deletions src/Geohash.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,53 @@
*/
class Geohash
{
const NORTH = 0;
const EAST = 1;
const SOUTH = 2;
const WEST = 3;

const EVEN = 0;
const ODD = 1;

/**
* Used for decoding the hash from base32
*/
protected $base32Mapping = "0123456789bcdefghjkmnpqrstuvwxyz";

private $borderChars = [
self::EVEN => [
self::NORTH => 'bcfguvyz',
self::EAST => 'prxz',
self::SOUTH => '0145hjnp',
self::WEST => '028b',
]
];

private $neighborChars = [
self::EVEN => [
self::NORTH => '238967debc01fg45kmstqrwxuvhjyznp',
self::EAST => '14365h7k9dcfesgujnmqp0r2twvyx8zb',
self::SOUTH => 'bc01fg45238967deuvhjyznpkmstqrwx',
self::WEST => 'p0r21436x8zb9dcf5h7kjnmqesgutwvy',
],
];

public function __construct() {
$this->neighborChars[self::ODD] = array(
self::NORTH => $this->neighborChars[self::EVEN][self::EAST],
self::EAST => $this->neighborChars[self::EVEN][self::NORTH],
self::SOUTH => $this->neighborChars[self::EVEN][self::WEST],
self::WEST => $this->neighborChars[self::EVEN][self::SOUTH],
);

$this->borderChars[self::ODD] = array(
self::NORTH => $this->borderChars[self::EVEN][self::EAST],
self::EAST => $this->borderChars[self::EVEN][self::NORTH],
self::SOUTH => $this->borderChars[self::EVEN][self::WEST],
self::WEST => $this->borderChars[self::EVEN][self::SOUTH],
);
}

/**
* Encode the latitude and longitude into a hashed string
* @param float Latitude
Expand Down Expand Up @@ -145,4 +187,57 @@ public function getBits($coordinate, $min, $max, $bitsLength)
}
return $binaryString;
}

/**
* Computes neighboring geohash values for given geohash.
*
* @param string $hash
* @return array
*/
public function getNeighbors($hash) {
$hashNorth = $this->calculateNeighbor($hash, self::NORTH);
$hashEast = $this->calculateNeighbor($hash, self::EAST);
$hashSouth = $this->calculateNeighbor($hash, self::SOUTH);
$hashWest = $this->calculateNeighbor($hash, self::WEST);

$hashNorthEast = $this->calculateNeighbor($hashNorth, self::EAST);
$hashSouthEast = $this->calculateNeighbor($hashSouth, self::EAST);
$hashSouthWest = $this->calculateNeighbor($hashSouth, self::WEST);
$hashNorthWest = $this->calculateNeighbor($hashNorth, self::WEST);
return [
'North' => $hashNorth,
'East' => $hashEast,
'South' => $hashSouth,
'West' => $hashWest,
'NorthEast' => $hashNorthEast,
'SouthEast' => $hashSouthEast,
'SouthWest' => $hashSouthWest,
'NorthWest' => $hashNorthWest,
];
}

/**
* Calculates neighbor geohash for given geohash and direction
*
* @param string $hash
* @param string $direction
* @return string $neighborHash
*/
private function calculateNeighbor($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->calculateNeighbor($baseHash, $direction);
}
if (isset($baseHash{0})) {
return $baseHash . $this->neighborChars[$evenOrOdd][$direction]{strpos($this->base32Mapping, $lastChar)};
} else {
return '';
}
}
}
23 changes: 21 additions & 2 deletions tests/GeohashTest.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<?php


use PHPUnit\Framework\TestCase;
use Sk\Geohash\Geohash;

class GeohashTest extends PHPUnit_Framework_TestCase
class GeohashTest extends TestCase
{
/**
* @dataProvider encodeProvider
Expand Down Expand Up @@ -56,4 +57,22 @@ public function decodeProvider()
array(17.3850320186, 78.48672056569, "tepffhb71hue")
);
}

public function testGetNeighbors() {
$geohash = new Geohash();
list($lat1, $lon1) = [25.813646, -80.133761];
$hash = $geohash->encode($lat1, $lon1, 7);
$neighbors = $geohash->getNeighbors($hash);
$this->assertEquals('dhx4be0', $hash);
$this->assertEquals([
'North' => 'dhx4be2',
'East'=> 'dhx4be1',
'South' => 'dhx4bdb',
'West' => 'dhx4b7p',
'NorthEast' => 'dhx4be3',
'SouthEast' => 'dhx4bdc',
'SouthWest' => 'dhx4b6z',
'NorthWest' => 'dhx4b7r',
], $neighbors);
}
}

0 comments on commit 9cd7e05

Please sign in to comment.