Files
dev-chiefworks 47f4fad75c Added Other AP
2022-04-26 11:30:34 -04:00

389 lines
12 KiB
PHP

<?php
class GooglePlacesClient
{
public function get($url)
{
$curl = curl_init();
$options = array(
CURLOPT_URL => $url,
CURLOPT_HEADER => false,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_RETURNTRANSFER => true,
);
curl_setopt_array($curl, $options);
$response = curl_exec($curl);
if ($error = curl_error($curl))
{
throw new \Exception('CURL Error: ' . $error);
}
curl_close($curl);
return $response;
}
}
class GooglePlaces
{
public $client = '';
public $sleep = 3;
private $key = '';
private $base_url = 'https://maps.googleapis.com/maps/api/place';
private $method = null;
private $response = null;
public $keyword = null;
public $language = 'en';
public $location = null;
public $output = 'json';
public $name = null;
public $pagetoken = null;
public $radius = null;
public $rankby = 'prominence';
public $sensor = false;
public $types = null;
public $placeid = null;
public $reference = null;
public $opennow = null;
public $input = null;
public $subradius = null;
public $getmax = true;
private $grid = null;
private $exceptions = array(
'base_url', 'client', 'exceptions', 'getmax', 'grid', 'method',
'output', 'pagetoken', 'response', 'sleep', 'subradius',
);
public function __construct($key, $client = false)
{
$this->key = $key;
$this->client = $client ? $client : new GooglePlacesClient();
}
function __set($variable, $value)
{
// Compensates for mixed variable naming
$variable = str_replace('_', '', strtolower($variable));
$this->$variable = $value;
}
public function __call($method, $arguments)
{
$this->output = strtolower($this->output);
if (!in_array($this->output, array('json', 'xml')))
{
throw new \Exception('Invalid output, please specify either "json" or "xml".');
}
$method = $this->method = strtolower($method);
$url = implode('/', array($this->base_url, $method, $this->output));
$parameters = array();
$parameters = $this->parameterBuilder($parameters);
$parameters = $this->methodChecker($parameters, $method);
if (!empty($this->subradius))
{
return $this->subdivide($url, $parameters);
}
if ($this->pagetoken !== null)
{
$parameters['pagetoken'] = $this->pagetoken;
}
$result = $this->queryGoogle($url, $parameters);
return $result;
}
/**
* Loops through all of our variables to make a parameter list
*/
private function parameterBuilder($parameters)
{
foreach (get_object_vars($this) as $variable => $value)
{
// Except these variables
if (!in_array($variable, $this->exceptions))
{
// Assuming it's not null
if ($value !== null)
{
// Converts boolean to string
if (is_bool($value))
{
$value = $value ? 'true' : 'false';
}
switch ($variable)
{
// Allows LatLng to be passed as an array
case 'location':
if (is_array($value))
{
// Just in case it's an associative array
$value = array_values($value);
$value = $value[0] . ',' . $value[1];
}
break;
// Checks that it's a value rank by value
case 'rankby':
$value = strtolower($value);
if (!in_array($value, array('prominence', 'distance')))
{
throw new \Exception('Invalid rank by value, please specify either "prominence" or "distance".');
}
break;
// Allows types to be passed as an array
case 'types':
if (is_array($value))
{
$value = implode('|', $value);
}
break;
}
$parameters[$variable] = $value;
}
}
}
return $parameters;
}
/**
* takes the parameters and method to throw exceptions or modify parameters as needed
* @todo Method to sanity check passed types
*/
private function methodChecker($parameters, $method)
{
if (!isset($parameters['pagetoken']))
{
switch ($method)
{
case 'nearbysearch':
if (!isset($parameters['location']))
{
throw new \Exception('You must specify a location before calling nearbysearch().');
}
elseif (isset($parameters['rankby']))
{
switch ($parameters['rankby'])
{
case 'distance':
if (!isset($parameters['keyword'])
&& !isset($parameters['name'])
&& !isset($parameters['types']))
{
throw new \Exception('You much specify at least one of the following: "keyword", "name", "types".');
}
if (isset($parameters['radius']))
{
unset($this->radius, $parameters['radius']);
}
break;
case 'prominence':
if (!isset($parameters['radius']))
{
throw new \Exception('You must specify a radius.');
}
break;
}
}
break;
case 'radarsearch':
if (!isset($parameters['location']))
{
throw new \Exception('You must specify a location before calling radarsearch().');
}
elseif (!isset($parameters['radius']))
{
throw new \Exception('You must specify a radius.');
}
elseif (empty($parameters['keyword'])
&& empty($parameters['name'])
&& empty($parameters['types']))
{
throw new \Exception('You much specify at least one of the following: "keyword", "name", "types".');
}
if (isset($parameters['rankby']))
{
unset($this->rankby, $parameters['rankby']);
}
break;
case 'details':
if (!(isset($parameters['reference'])
^ isset($parameters['placeid'])))
{
throw new \Exception('You must specify either a "placeid" or a "reference" (but not both) before calling details().');
}
if (isset($parameters['rankby']))
{
unset($this->rankby, $parameters['rankby']);
}
break;
case 'autocomplete':
/*if (!isset($parameters['location']))
{
throw new \Exception('You must specify a location before calling autocomplete().');
}
elseif (!isset($parameters['radius']))
{
throw new \Exception('You must specify a radius.');
}
else*/if (empty($parameters['input']))
{
throw new \Exception('You much specify the user entered input string.');
}
break;
}
}
return $parameters;
}
/**
* Submits request via curl, sets the response, then returns the response
*/
private function queryGoogle($url, $parameters)
{
if ($this->pagetoken !== null)
{
$parameters['pagetoken'] = $this->pagetoken;
sleep($this->sleep);
}
// Couldn't seem to get http_build_query() to work right so...
$querystring = '';
foreach ($parameters as $variable => $value)
{
if ($querystring != '')
{
$querystring .= '&';
}
$querystring .= $variable . '=' . urlencode($value);
}
$response = $this->client->get($url . '?' . $querystring);
if ($this->output == 'json')
{
$response = json_decode($response, true);
if ($response === null)
{
throw new \Exception('The returned JSON was malformed or nonexistent.');
}
}
else
{
throw new \Exception('XML is terrible, don\'t use it, ever.');
}
$this->response = $response;
return $this->response;
}
/**
* Returns the longitude equal to a given distance (meters) at a given latitude
*/
public function meters2lng($meters, $latitude)
{
return $meters / (cos(deg2rad($latitude)) * 40075160 / 360);
}
/**
* Returns the latitude equal to a given distance (meters)
*/
public function meters2lat($meters)
{
return $meters / (40075160 / 360);
}
/**
* Returns the aggregated responses for a subdivided search
*/
private function subdivide($url, $parameters)
{
if ($this->subradius < 200)
{
throw new \Exception('Subradius should be at least 200 meters.');
}
$quotient = $parameters['radius'] / $this->subradius;
if ($parameters['radius'] % $this->subradius || $quotient % 2)
{
throw new \Exception('Subradius should divide evenly into radius.');
}
$center = explode(',', $parameters['location']);
$centerlat = $center[0];
$centerlng = $center[1];
$count = $quotient;
$lati = $this->meters2lat($this->subradius * 2);
$this->grid['results'] = array();
for ($i = $count / 2 * -1; $i <= $count / 2; $i++)
{
$lat = $centerlat + $i * $lati;
$lngi = $this->meters2lng($this->subradius * 2, $lat);
for ($j = $count / 2 * -1; $j <= $count / 2; $j++)
{
$lng = $centerlng + $j * $lngi;
$loc = $lat . ',' . $lng;
$parameters['location'] = $loc;
$parameters['radius'] = $this->subradius;
$pagetoken = true;
while ($pagetoken)
{
$this->queryGoogle($url, $parameters);
$this->grid[$i][$j] = $this->response;
$this->grid['results'] = array_merge(
$this->grid['results'],
$this->response['results']
);
if (isset($this->response['next_page_token']))
{
$this->pagetoken = $this->response['next_page_token'];
}
else
{
$this->pagetoken = null;
$pagetoken = false;
}
}
}
}
return $this->grid;
}
}