<?php
/**
* Abstract superclass for Geocoder classes
*
* @author Jan Bruyndonckx
* @copyright Copyright (c) 2008 Banzora http://software.banzora.com
* @package main
* @subpackage lib
*
*/
require_once "lib/HTTP/Request.php";
require_once "lib/HTTP/Header.php";
require_once "StringUtils.inc" ;
require_once "WorldGeocoder.inc" ;
require_once "GoogleGeocoder.inc" ;
/**
Abstract class to geocode a location.
*/
define ('GEO_STRICT', true) ;
abstract class Geocoder
{
protected $zipcode ;
protected $city ;
protected $state ;
protected $country ;
/**--------------------------------------------------------------------
Constructor, takes zipcode, city and state as arguments.
city & state are optional.
*/
public function __construct ($country, $zipcode, $city=NULL, $state=NULL)
{
// maybe state is part of the city for the US
if (($country == 'US') && ($state == NULL)) {
$t = explode (',', $city) ;
array_trim($t) ;
$city = $t[0] ;
if (isset ($t[1]) && (strlen ($t[1]) == 2)) {
$state = $t[1] ;
}
}
$this->country = $country ;
$this->zipcode = $zipcode ;
$this->city = $city ;
$this->state = $state ;
if ($this->zipcode != NULL)
$this->zipcode = urlencode($this->zipcode) ;
if ($this->city != NULL) {
$this->city = urlencode(remove_accents($this->city)) ;
}
}
/**--------------------------------------------------------------------
Submit a geocoding query.
Return NULL if query failed, or: array ("longitude"=>float, "latitude"=>float)
*/
public function submit ()
{
$result = $this->submitQuery() ;
if (($result == NULL) ||
(is_array($result)))
{
return $this->createResult(NULL, NULL, "Could not geocode $this->city,$this->country") ;
}
return $this->parseResult ($result) ;
}
/**
@return string the full url with parameters encoded
*/
abstract protected function createRequest () ;
/**
@param string the XML body as replied by the service
@return array the result as made by createResult() or NULL
*/
abstract public function parseResult ($serviceURL) ;
/**--------------------------------------------------------------------
Perform the query to the internet service provider.
Return NULL in case of error, or an xml text
*/
private function submitQuery ()
{
$url=$this->createRequest() ;
if ($url == NULL) {
return NULL ;
}
$req = new HTTP_Request($url);
$req->sendRequest() ;
$header = new HTTP_Header () ;
$code = $req->getResponseCode() ;
//var_dump($code) ;
if ($code == 400)
{
// bad request, probably zip code wrong?
return $this->createResult (NULL, NULL, "bad_request") ;
}
if (!$header->isSuccessful($code))
{
return NULL;
}
return ($req->getResponseBody());
}
/**
@return array the longitude and latitude as an array with the data as a key
*/
protected function createResult ($longitude, $latitude, $error=NULL)
{
return array (
"latitude" => $latitude,
"longitude" => $longitude,
"error" => $error) ;
}
//---------------------------------------------------------------------
/**
* Geocode the address. If not found on the world geocoder, fall back on Google
*
* @param string $country
* @param string $zipcode
* @param string $city, optional
* @param string $state, optional
* @param boolean $strict, optional; use only world geocoder
* @return array
*/
static public function geoCode ($country, $zipcode, $city=NULL, $state=NULL, $strict=false) {
//Logger::log ("geocode: $city, $country") ;
// If we have a zipcode, then give precedence to Google
if (($zipcode != NULL)) {
$geo = new GoogleGeocoder ($country, $zipcode, $city, $state) ;
$result = $geo->submit () ;
if (!isset ($result['error'])) {
return $result ;
}
}
// not found, try world geocoder
$geo = new WorldGeocoder($country, $zipcode, $city, $state) ;
$result = $geo->submit () ;
// not found, try google again (but now implicitly without zip)
if (($strict == false) && isset($result['error'])) {
$geo = new GoogleGeocoder ($country, $zipcode, $city, $state) ;
$result = $geo->submit () ;
}
return $result ;
}
/**
* Check if the city/country combination is correct by gettings its geocode
*
* @param string $city
* @param string $country
* @param boolean $strict
* @return boolean
*/
public static function checkValidPlace ($city, $country, $strict = false) {
$geo = new WorldGeocoder($country, NULL, $city, NULL) ;
$result = $geo->submit () ;
$result = !(isset($result['error'])) ;
if (($result == false) && ($strict === false)) {
$geo = new GoogleGeocoder ($country, NULL, $city, NULL) ;
$result = $geo->submit () ;
$result = !(isset($result['error'])) ;
}
return $result ;
}
}
// end class Geocoder
?>