Created
November 1, 2016 20:43
-
-
Save lukepolo/c91bedc998f179a6cc0ca054cc390af4 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
<?php | |
use Geocoder\Model\Coordinates; | |
use GuzzleHttp\Client as HttpClient; | |
/** | |
* Class GeoLocation | |
*/ | |
class GeoLocation implements Contracts\GeoLocation | |
{ | |
const KM_MILES_CONVERSION = '1.60934'; | |
const BING_MAPS_API = 'http://dev.virtualearth.net/REST/v1/Routes'; | |
/** | |
* Resolves the geo-location based on a address | |
* | |
* @param $address | |
* @param null $zip | |
* | |
* @return array|bool | |
*/ | |
public function getGeoLocation($address, $zip = null) | |
{ | |
return \Cache::remember($address . $zip, 1440, function () use ($address, $zip) { | |
// TODO - We will run out of limit very quickly, either use a different provider or pay for it | |
$geocoder = new \Geocoder\Provider\GoogleMaps( | |
new \Ivory\HttpAdapter\CurlHttpAdapter() | |
); | |
try { | |
$address = $geocoder->limit(1)->geocode($address . ' ' . $zip)->first(); | |
return $address; | |
} catch (\Exception $e) { | |
return false; | |
} | |
}); | |
} | |
/** | |
* Finds the mathematical distance from lat+lng's of a straight line | |
* | |
* http://www.geodatasource.com/developers/php | |
* | |
* @param Coordinates $fromLocation | |
* @param Coordinates $toLocation | |
* @param $unit - Either Miles (mi) or Kilometers (km) | |
* | |
* @return string | |
*/ | |
public function calculateDistance(Coordinates $fromLocation, Coordinates $toLocation, $unit = 'mi') | |
{ | |
return \Cache::remember(json_encode(['distance', (array)$fromLocation, (array)$toLocation, $unit]), 1440, | |
function () use ($fromLocation, $toLocation, $unit) { | |
$theta = $fromLocation->getLongitude() - $toLocation->getLongitude(); | |
$fromLatitude = $fromLocation->getLatitude(); | |
$toLatitude = $toLocation->getLatitude(); | |
$distance = rad2deg( | |
acos( | |
sin(deg2rad($fromLatitude)) * sin(deg2rad($toLatitude)) + cos(deg2rad($fromLatitude)) * cos(deg2rad($toLatitude)) * cos(deg2rad($theta)) | |
) | |
); | |
$miles = $distance * 60 * 1.1515; | |
switch (strtolower($unit)) { | |
case 'km' : | |
return round($miles * static::KM_MILES_CONVERSION, 2); | |
break; | |
default: | |
case 'mi' : | |
return $miles; | |
break; | |
} | |
}); | |
} | |
/** | |
* Calculates the driving distance to a particular UNIT | |
* | |
* @param Coordinates $fromLocation | |
* @param Coordinates $toLocation | |
* @param string $unit Miles (mi) | Kilometers (km) | |
* | |
* @return float | |
* @throws \Exception | |
*/ | |
public function calculateDrivingDistance(Coordinates $fromLocation, Coordinates $toLocation, $unit = 'mi') | |
{ | |
return \Cache::remember(json_encode([ | |
'drivingDistance', | |
(array)$fromLocation, | |
(array)$toLocation, | |
$unit | |
]), 1440, | |
function () use ($fromLocation, $toLocation, $unit) { | |
$client = new HttpClient(); | |
$response = json_decode( | |
$client->get(static::BING_MAPS_API, [ | |
'query' => [ | |
'wayPoint.1' => $fromLocation->getLatitude() . "," . $fromLocation->getLongitude(), | |
'waypoint.2' => $toLocation->getLatitude() . "," . $toLocation->getLongitude(), | |
'distanceUnit' => $unit, | |
'key' => env('BING_MAPS_API_KEY'), | |
], | |
] | |
)->getBody()->getContents()); | |
if ($response->statusCode == 200) { | |
return $response->resourceSets[0]->resources[0]->travelDistance; | |
} else { | |
throw new \Exception('Invalid Request'); | |
} | |
}); | |
} | |
/** | |
* Checks if point is inside the polygon ref::http://assemblysys.com/php-point-in-polygon-algorithm/ | |
* | |
* @param Coordinates $point | |
* @param $polygonCoordinates | |
* @param bool|true $pointOnVertex | |
* | |
* @return bool | |
*/ | |
public function isPointInPoly(Coordinates $point, $polygonCoordinates, $pointOnVertex = true) | |
{ | |
$this->pointOnVertex = $pointOnVertex; | |
$point = $this->pointCoordinatesObjectToCoordinates($point); | |
$vertices = []; | |
foreach ($polygonCoordinates as $vertex) { | |
$vertices[] = $this->pointCoordinatesObjectToCoordinates($vertex); | |
} | |
if (current($vertices) != end($vertices)) { | |
$vertices[] = $vertices[0]; | |
} | |
if ($this->pointOnVertex == true and $this->pointOnVertex($point, $vertices) == true) { | |
return true; | |
} | |
$intersections = 0; | |
$vertices_count = count($vertices); | |
for ($i = 1; $i < $vertices_count; $i++) { | |
$vertex1 = $vertices[$i - 1]; | |
$vertex2 = $vertices[$i]; | |
if ($vertex1['y'] == $vertex2['y'] and $vertex1['y'] == $point['y'] and $point['x'] > min($vertex1['x'], | |
$vertex2['x']) and $point['x'] < max($vertex1['x'], $vertex2['x']) | |
) { | |
return true; | |
} | |
if ($point['y'] > min($vertex1['y'], $vertex2['y']) and $point['y'] <= max($vertex1['y'], | |
$vertex2['y']) and $point['x'] <= max($vertex1['x'], | |
$vertex2['x']) and $vertex1['y'] != $vertex2['y'] | |
) { | |
$xinters = ($point['y'] - $vertex1['y']) * ($vertex2['x'] - $vertex1['x']) / ($vertex2['y'] - $vertex1['y']) + $vertex1['x']; | |
if ($xinters == $point['x']) { | |
return true; | |
} | |
if ($vertex1['x'] == $vertex2['x'] || $point['x'] <= $xinters) { | |
$intersections++; | |
} | |
} | |
} | |
if ($intersections % 2 != 0) { | |
return true; | |
} else { | |
return false; | |
} | |
} | |
/** | |
* Converts a point GeoCoder Coordinate object to coordinates | |
* | |
* @param $pointObject | |
* | |
* @return array | |
*/ | |
private function pointCoordinatesObjectToCoordinates($pointObject) | |
{ | |
return [ | |
"x" => $pointObject->getLatitude(), | |
"y" => $pointObject->getLongitude(), | |
]; | |
} | |
/** | |
* Checks is point is a vertex of the polygon | |
* | |
* @param $point | |
* @param $vertices | |
* | |
* @return bool | |
*/ | |
private function pointOnVertex($point, $vertices) | |
{ | |
foreach ($vertices as $vertex) { | |
if ($point == $vertex) { | |
return true; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment