Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add what3words as a supported provider (#42)
* Fix unit tests for changes on Geocoder.ca data (again) * Add what3words as a provider Calls to error_log begone; that was a stupid oversight on my part * Fix table structure for ip2c provider in README.md * Add check for invalid/missing response from the geocoder endpoint
- Loading branch information
Showing
5 changed files
with
199 additions
and
2 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
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
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,84 @@ | ||
<?php | ||
|
||
namespace Geocoder\Provider; | ||
|
||
use Geocoder\Exception\InvalidCredentials as InvalidCredentialsException; | ||
use Geocoder\Exception\NoResult as NoResultException; | ||
use Geocoder\Exception\UnsupportedOperation as UnsupportedException; | ||
|
||
use Ivory\HttpAdapter\HttpAdapterInterface; | ||
|
||
class What3wordsProvider extends AbstractHttpProvider implements Provider | ||
{ | ||
const FORWARD_URL = 'https://api.what3words.com/v2/forward?addr=%s&key=%s'; | ||
const REVERSE_URL = 'https://api.what3words.com/v2/reverse?coords=%F,%F&key=%s'; | ||
|
||
private $apiKey = null; | ||
|
||
public function __construct(HttpAdapterInterface $adapter, $apiKey) | ||
{ | ||
parent::__construct($adapter); | ||
|
||
$this->apiKey = $apiKey; | ||
} | ||
|
||
public function getName() | ||
{ | ||
return 'what3words'; | ||
} | ||
|
||
public function geocode($value) | ||
{ | ||
if (filter_var($value, FILTER_VALIDATE_IP)) { | ||
throw new UnsupportedException('The what3words provider does not support IP addresses.'); | ||
} | ||
|
||
if (null == $this->apiKey) { | ||
throw new InvalidCredentialsException('No what3words API key provided.'); | ||
} | ||
|
||
$query = sprintf(self::FORWARD_URL, urlencode($value), $this->apiKey); | ||
return $this->executeQuery($query); | ||
} | ||
|
||
public function reverse($latitude, $longitude) | ||
{ | ||
if (null == $this->apiKey) { | ||
throw new InvalidCredentialsException('No what3word API key provided.'); | ||
} | ||
|
||
$query = sprintf(self::REVERSE_URL, $latitude, $longitude, $this->apiKey); | ||
|
||
return $this->executeQuery($query); | ||
} | ||
|
||
protected function executeQuery($query) | ||
{ | ||
$content = $this->getAdapter()->get($query)->getBody(); | ||
|
||
if (null === $data = json_decode($content, true)) { | ||
throw new NoResultException(sprintf('Could not execute query: %s', $query)); | ||
} | ||
|
||
if (!isset($data['status']) && isset($data['code']) && 2 === $data['code']) { | ||
throw new InvalidCredentialsException(sprintf('Invalid credentials: %s', $data['message'])); | ||
} | ||
|
||
$results = []; | ||
$results[] = array_merge($this->getDefaults(), [ | ||
'latitude' => $data['geometry']['lat'], | ||
'longitude' => $data['geometry']['lng'], | ||
'bounds' => [ | ||
'south' => $data['bounds']['southwest']['lat'], | ||
'west' => $data['bounds']['southwest']['lng'], | ||
'north' => $data['bounds']['northeast']['lat'], | ||
'east' => $data['bounds']['northeast']['lng'] | ||
], | ||
'locality' => $data['words'] | ||
]); | ||
|
||
return $results; | ||
} | ||
} | ||
|
||
?> |
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
111 changes: 111 additions & 0 deletions
111
tests/Geocoder/Tests/Provider/What3wordsProviderTest.php
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,111 @@ | ||
<?php | ||
|
||
namespace Geocoder\Tests\Provider; | ||
|
||
use Geocoder\Tests\TestCase; | ||
use Geocoder\Provider\What3wordsProvider; | ||
|
||
class What3wordsProviderTest extends TestCase | ||
{ | ||
public function testGetName() | ||
{ | ||
$provider = new What3wordsProvider($this->getMockAdapter($this->never()), 'api-key'); | ||
$this->assertEquals('what3words', $provider->getName()); | ||
} | ||
|
||
/** | ||
* @expectedException \RuntimeException | ||
*/ | ||
public function testGeocodeWithNullApiKey() | ||
{ | ||
$provider = new What3wordsProvider($this->getMockAdapter($this->never()), null); | ||
$provider->geocode('foo'); | ||
} | ||
|
||
/** | ||
* @expectedException \Geocoder\Exception\NoResult | ||
*/ | ||
public function testGeocodeWithAddress() | ||
{ | ||
$provider = new What3wordsProvider($this->getMockAdapter(), 'api-key'); | ||
$provider->geocode('bar'); | ||
} | ||
|
||
/** | ||
* @expectedException \Geocoder\Exception\InvalidCredentials | ||
*/ | ||
public function testGetInvalidCredentials() | ||
{ | ||
$json = '{"code":2,"message":"Authentication failed; invalid API key"}'; | ||
|
||
$provider = new What3wordsProvider($this->getMockAdapterReturns($json), 'api-key'); | ||
$results = $provider->geocode('baz'); | ||
} | ||
|
||
/** | ||
* @expectedException \Geocoder\Exception\NoResult | ||
* @expectedExceptionMessage Could not execute query: https://api.what3words.com/v2/forward?addr=foobar&key=api_key | ||
*/ | ||
public function testGeocode() | ||
{ | ||
$provider = new What3wordsProvider($this->getMockAdapterReturns('{}'), 'api_key'); | ||
$provider->geocode('foobar'); | ||
} | ||
|
||
public function testGeocodeWithRealAddress() | ||
{ | ||
if (!isset($_SERVER['WHAT3WORDS_API_KEY'])) { | ||
$this->markTestSkipped('You need to configure the WHAT3WORDS_API_KEY value in phpunit.xml'); | ||
} | ||
|
||
$provider = new What3wordsProvider($this->getAdapter(), $_SERVER['WHAT3WORDS_API_KEY']); | ||
$results = $provider->geocode('index.home.raft'); | ||
|
||
$this->assertInternalType('array', $results); | ||
$this->assertCount(1, $results); | ||
|
||
$result = $results[0]; | ||
$this->assertEquals(51.521250999999999, $result['latitude'], 0.01); | ||
$this->assertEquals(-0.20358599999999999, $result['longitude'], 0.01); | ||
$this->assertEquals(51.521237999999997, $result['bounds']['south'], '', 0.01); | ||
$this->assertEquals(-0.20360700000000001, $result['bounds']['west'], '', 0.01); | ||
$this->assertEquals(51.521265, $result['bounds']['north'], '', 0.01); | ||
$this->assertEquals(-0.20356399999999999, $result['bounds']['east'], '', 0.01); | ||
$this->assertSame('index.home.raft', $result['locality']); | ||
} | ||
|
||
/** | ||
* @expectedException \Geocoder\Exception\NoResult | ||
*/ | ||
public function testReverse() | ||
{ | ||
if (!isset($_SERVER['WHAT3WORDS_API_KEY'])) { | ||
$this->markTestSkipped('You need to configure the WHAT3WORDS_API_KEY value in phpunit.xml'); | ||
} | ||
|
||
$provider = new What3wordsProvider($this->getMockAdapter(), $_SERVER['WHAT3WORDS_API_KEY']); | ||
$provider->reverse(1, 2); | ||
} | ||
|
||
public function testReverseWithRealCoordinates() | ||
{ | ||
if (!isset($_SERVER['WHAT3WORDS_API_KEY'])) { | ||
$this->markTestSkipped('You need to configure the WHAT3WORDS_API_KEY value in phpunit.xml'); | ||
} | ||
|
||
$provider = new What3wordsProvider($this->getAdapter(), $_SERVER['WHAT3WORDS_API_KEY']); | ||
$results = $provider->reverse(51.426787, -0.331321); | ||
|
||
$this->assertInternalType('array', $results); | ||
$this->assertCount(1, $results); | ||
|
||
$result = $results[0]; | ||
$this->assertEquals(51.426786999999997, $result['latitude'], 0.01); | ||
$this->assertEquals(-0.33132099999999998, $result['longitude'], 0.01); | ||
$this->assertEquals(51.426772999999997, $result['bounds']['south'], '', 0.01); | ||
$this->assertEquals(-0.331343, $result['bounds']['west'], '', 0.01); | ||
$this->assertEquals(51.4268, $result['bounds']['north'], '', 0.01); | ||
$this->assertEquals(-0.33129999999999998, $result['bounds']['east'], '', 0.01); | ||
$this->assertSame('spoken.land.complains', $result['locality']); | ||
} | ||
} |