Added Other AP
This commit is contained in:
@@ -0,0 +1,736 @@
|
||||
<?php
|
||||
|
||||
class GeocodeApi extends Api
|
||||
{
|
||||
public $apiName = 'geocode';
|
||||
const DEFAULT_COUNTRY_CODE = 'SG';
|
||||
|
||||
public function indexAction() {
|
||||
$message = 'Data not found';
|
||||
$address = $this->requestParams["address"] ?? "";
|
||||
$member_id = $this->requestParams["member_id"] ?? 0;
|
||||
$country = $this->requestParams["country"] ?? self::DEFAULT_COUNTRY_CODE;
|
||||
try {
|
||||
if ($address==NULL || $address=="") {
|
||||
throw new Exception("Invalid input");
|
||||
}
|
||||
$db = new Db();
|
||||
|
||||
// DEBUG
|
||||
$country = Geocode::mockGPSCountry($db->getConnect(), $member_id, $country, 'default');
|
||||
|
||||
$result = [];
|
||||
$result["address"] = $address;
|
||||
list($result["geocode"], $result["error"]) = GeocodeApi::geocode($db, $address, $country);
|
||||
return $this->response($result, 200);
|
||||
} catch (Exception $e) {
|
||||
error_log(json_encode($e));
|
||||
$message = $e->getMessage();
|
||||
}
|
||||
return $this->response(
|
||||
array(
|
||||
'error' => $message
|
||||
), 404);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method GET
|
||||
* Get single record (by id)
|
||||
* http://DOMAIN/geocode/1
|
||||
* @return string
|
||||
*/
|
||||
public function viewAction() {
|
||||
//id must be the first parameter after /geocode/x
|
||||
$id = array_shift($this->requestUri);
|
||||
|
||||
if($id && (int)$id>0){
|
||||
$db = new Db();
|
||||
$address = Geocode::getAddressById($db->getConnect(), (int)$id);
|
||||
if(is_array($address) && count($address)>0){
|
||||
return $this->response($address, 200);
|
||||
}
|
||||
}
|
||||
return $this->response(
|
||||
array(
|
||||
'error'=> 'Data not found'
|
||||
), 404);
|
||||
}
|
||||
|
||||
public function createAction() {
|
||||
error_log('GeocodeApi::createAction()');
|
||||
$message = "";
|
||||
$addresses = $this->requestParams["addresses"] ?? array();
|
||||
$options = $this->requestParams["options"] ?? array();
|
||||
$gps_country_code = $this->requestParams["gps_country_code"] ?? null;
|
||||
$country = $this->requestParams["country"] ?? SELF::DEFAULT_COUNTRY_CODE;
|
||||
$member_id = $this->requestParams["member_id"] ?? 0;
|
||||
$address_data = $this->requestParams["address_data"] ?? null;
|
||||
try {
|
||||
if ($addresses==NULL || !is_array($addresses) || count($addresses)<1 || !isset($addresses[0]["address"])) {
|
||||
throw new Exception("Invalid input");
|
||||
}
|
||||
|
||||
$db = new Db();
|
||||
|
||||
// DEBUG
|
||||
$country = Geocode::mockGPSCountry($db->getConnect(), $member_id, $country, 'default');
|
||||
|
||||
// Check country
|
||||
if ($country!='SG' && $country!='US') {
|
||||
throw new RuntimeException('Trips has not yet launched in your country. You can still track your travel activity and access exclusive deals. Start exploring!',500);
|
||||
}
|
||||
if ($gps_country_code==null) {
|
||||
$gps_country_code = $country;
|
||||
}
|
||||
|
||||
$result = array();
|
||||
foreach ($addresses as $item) {
|
||||
if (isset($item["type"]) && $item["type"]>0 && is_array($address_data)) {
|
||||
if ($item["type"]==1
|
||||
&& array_key_exists('location_start',$address_data)
|
||||
&& is_array($address_data['location_start'])
|
||||
&& array_key_exists('id',$address_data['location_start'])
|
||||
&& $address_data['location_start']['id']>0) {
|
||||
$item["geocode"] = GeocodeApi::addressDataToGeocode($address_data['location_start']);
|
||||
$item["error"] = NULL;
|
||||
$result[] = $item;
|
||||
continue;
|
||||
}
|
||||
if ($item["type"]==2
|
||||
&& array_key_exists('location_end',$address_data)
|
||||
&& is_array($address_data['location_end'])
|
||||
&& array_key_exists('id',$address_data['location_end'])
|
||||
&& $address_data['location_end']['id']>0
|
||||
) {
|
||||
$item["geocode"] = GeocodeApi::addressDataToGeocode($address_data['location_end']);
|
||||
$item["error"] = NULL;
|
||||
$result[] = $item;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (isset($item["address"]) && $item["address"]!="") {
|
||||
list($item["geocode"], $item["error"]) = GeocodeApi::geocode($db, $item["address"], $country);
|
||||
} else {
|
||||
$item["geocode"] = NULL;
|
||||
$item["error"] = "Invalid address";
|
||||
}
|
||||
$result[] = $item;
|
||||
}
|
||||
if (count($result)<1) throw new Exception('Sorry, but we cannot find your address');
|
||||
|
||||
$withinTheServiceArea = GeocodeApi::checkWithinTheServiceArea($country,$result, false);
|
||||
|
||||
if ((isset($options["travel_time"]) && $options["travel_time"]) ||
|
||||
(isset($options["route_overlay"]) && $options["route_overlay"])) {
|
||||
$options = GeocodeApi::options($db, $result, $options);
|
||||
}
|
||||
|
||||
list($providers,$country,$service_enabled, $currency_code) = GeocodeApi::getProviders(
|
||||
$db, $gps_country_code, $country, $options, $member_id);
|
||||
return $this->response(array(
|
||||
'addresses' => $result,
|
||||
'options' => $options,
|
||||
'providers' => $providers,
|
||||
'country' => $country,
|
||||
'service' => $service_enabled,
|
||||
'currency' => $currency_code), 200);
|
||||
} catch (Exception $e) {
|
||||
error_log(json_encode($e));
|
||||
$message = $e->getMessage();
|
||||
}
|
||||
return $this->response(
|
||||
array(
|
||||
"error" => $message==""?"Failed to run geocoder":$message
|
||||
), 500);
|
||||
}
|
||||
|
||||
protected function addressDataToGeocode($address_data) {
|
||||
// TODO: check the timeZoneId?
|
||||
return [
|
||||
"address" => $address_data["address"],
|
||||
"lat" => array_key_exists("latitude",$address_data) ? $address_data["latitude"] : $address_data["lat"],
|
||||
"lng" => array_key_exists("longitude",$address_data) ? $address_data["longitude"] : $address_data["lng"],
|
||||
"postal" => $address_data["postal"],
|
||||
"timezone" => $address_data["timezone"],
|
||||
"id" => $address_data["id"],
|
||||
"country" => $address_data["country"],
|
||||
"timeZoneId" => $address_data["timeZoneId"]
|
||||
];
|
||||
/*
|
||||
id: "12400"
|
||||
address: "2600 Bentley Rd SE, Marietta, GA 30067, USA"
|
||||
latitude: "33.9212128"
|
||||
longitude: "-84.4766081"
|
||||
timezone: "3"
|
||||
geocoding_date: "2019-12-31"
|
||||
postal: "30067"
|
||||
country: "US"
|
||||
geometry: "0101000020E61000008FA042BF801E55C00B54104DEAF54040"
|
||||
description: null
|
||||
timeZoneId: "America/New_York"
|
||||
*/
|
||||
}
|
||||
|
||||
public function updateAction() {
|
||||
$message = 'Data not found';
|
||||
$latitude = $this->requestParams["latitude"] ?? 0;
|
||||
$longitude = $this->requestParams["longitude"] ?? 0;
|
||||
$country = $this->requestParams["country"] ?? GeocodeApi::DEFAULT_COUNTRY_CODE;
|
||||
$internal = $this->requestParams["internal"] ?? false;
|
||||
try {
|
||||
if ($latitude===NULL || $longitude===NULL || ($latitude==0 && $longitude==0)) {
|
||||
$message = "Invalid input: $latitude,$longitude,$country";
|
||||
$message.= json_encode($this->requestParams);
|
||||
throw new Exception($message);
|
||||
//throw new Exception("Invalid input");
|
||||
}
|
||||
if ($internal) {
|
||||
$this->encryption = false;
|
||||
}
|
||||
$db = new Db();
|
||||
list($address, $err) = GeocodeApi::reverseGeocode(
|
||||
$db, $latitude, $longitude);
|
||||
if ($address!=NULL && strlen($address)>5) {
|
||||
return $this->response([
|
||||
"address" => $address,
|
||||
"latitude" => $latitude,
|
||||
"longitude" => $longitude,
|
||||
"country" => $country
|
||||
], 200);
|
||||
}
|
||||
if ($err && $err!="") {
|
||||
$message = $err;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
error_log(json_encode($e));
|
||||
$message = $e->getMessage();
|
||||
}
|
||||
return $this->response(
|
||||
array(
|
||||
'error' => $message
|
||||
), 404);
|
||||
}
|
||||
|
||||
public function deleteAction() {
|
||||
return $this->response(
|
||||
array(
|
||||
"error" => "Delete error"
|
||||
), 500);
|
||||
}
|
||||
|
||||
public static function processAddressCountry($db, $address, $country) {
|
||||
syslog(LOG_WARNING,"GeocodeApi::processAddressCountry(\$db, $address, $country)");
|
||||
$code = $country;
|
||||
$name = "";
|
||||
$short_name = "";
|
||||
$res = Geocode::getCountry($db->getConnect(),$country);
|
||||
if (is_array($res) && array_key_exists('country',$res) && $res['country']!='') {
|
||||
$name = $res['country'];
|
||||
$short_name = $res['short_name'];
|
||||
$code = $res['code'];
|
||||
}
|
||||
// if it has the country name in address?
|
||||
if ($name!="" && strripos($address,$name)!==false) {
|
||||
return $address;
|
||||
}
|
||||
|
||||
$length = strlen($code); // if it ends with the country code?
|
||||
$lengthcode = strlen($short_name); // if it ends with the country code?
|
||||
if ((strripos($address," ${code}")!==false || strrpos($address,",${short_name}")!==false)) {
|
||||
return $address;
|
||||
}
|
||||
$address.= ", ".($name!=""?$name:$code); // append country name or code to address
|
||||
return $address;
|
||||
}
|
||||
|
||||
public static function geocode($db, $address, $country, $client_id="Unknown") {
|
||||
|
||||
syslog(LOG_WARNING,"GeocodeApi::geocode(\$db,'${address}',$country,'${client_id}')");
|
||||
global $savvyext;
|
||||
$httpAuthToken = $savvyext->cfgReadChar('system.oauth2_token');
|
||||
$oauth2_url = $savvyext->cfgReadChar('system.oauth2_url');
|
||||
// Check for broken clients and assume default country for now
|
||||
if ($country==null || $country=="") {
|
||||
$country = self::DEFAULT_COUNTRY_CODE;
|
||||
}
|
||||
$address_input = $address;
|
||||
// Check local DB cache
|
||||
list ($res, $err) = Geocode::checkLatLngByAddress($db->getConnect(), $address, $country);
|
||||
if (is_array($res) && isset($res["timeZoneId"]) && !empty($res["postal"])) {
|
||||
/* if(empty($res['plus_code']) || $res['address'] == $res['plus_code']){
|
||||
$reverse_info = self::reverseGeocodeService($db, $res['lat'],$res['lng']);
|
||||
$log = [
|
||||
'message' => 'GeocodeApi::reverseGeocodeService',
|
||||
'data' => ['reverse_info'=>$reverse_info]
|
||||
];
|
||||
syslog(LOG_WARNING, "GeocodeApi::reverseGeocodeService => ".json_encode($log));
|
||||
Logger::debug($log);
|
||||
if(!empty($reverse_info[0]['plus_code'])){
|
||||
$dataUpdate = [
|
||||
'plus_code' => $reverse_info[0]['plus_code'],
|
||||
'address' => $reverse_info[0]['address'],
|
||||
];
|
||||
$condition = "id = '".$res['id']."'";
|
||||
Address::update($db->getConnect(),$condition, $dataUpdate );
|
||||
$res['plus_code'] = $reverse_info[0]['plus_code'];
|
||||
$res['address'] = $reverse_info[0]['address'];
|
||||
}
|
||||
} */
|
||||
return [$res, NULL];
|
||||
} else {
|
||||
syslog(LOG_WARNING, "GeocodeApi::geocode => ".json_encode($res));
|
||||
}
|
||||
// Massage address
|
||||
$address = GeocodeApi::processAddressCountry($db, $address, $country);
|
||||
|
||||
// Call geocoding service
|
||||
$data = http_build_query(
|
||||
array(
|
||||
'address' => $address,
|
||||
)
|
||||
);
|
||||
$headers = getallheaders();
|
||||
$client_id = isset($headers['client_id'])?$headers['client_id']:$client_id;
|
||||
syslog(LOG_WARNING,"GeocodeApi::oauth2_geocode(\$db,'${address}',$country,'${client_id}')");
|
||||
$url = $oauth2_url."geocode?" . $data;
|
||||
$opts = array(
|
||||
'http' => array(
|
||||
'method' => "GET",
|
||||
'timeout' => 60, /* 1 minute */
|
||||
'header' =>
|
||||
//"Content-Type: application/x-www-form-urlencoded\r\n" .
|
||||
//"Accept: application/json\r\n" .
|
||||
"Authorization: Server-Token ${httpAuthToken}\r\n".
|
||||
"client_id: ${client_id}\r\n",
|
||||
),
|
||||
"ssl" => array(
|
||||
"verify_peer"=>false,
|
||||
"verify_peer_name"=>false,
|
||||
)
|
||||
);
|
||||
$benchmarks['function'] = 'GeocodeApi::geocode';
|
||||
Utilities::startBenchmark($benchmarks);
|
||||
$context = stream_context_create($opts);
|
||||
$body = file_get_contents($url, false, $context);
|
||||
$geocoded = json_decode($body,true);
|
||||
$log = [
|
||||
'message' => 'GeocodeApi::geocode call',
|
||||
'data' => ['address_input'=>$address_input],
|
||||
'errror' => $err
|
||||
];
|
||||
Logger::debug($log);
|
||||
Utilities::finishBenchmark($benchmarks);
|
||||
|
||||
if (is_array($geocoded) && is_array($geocoded["data"]) && !isset($geocoded["error"])) {
|
||||
// Cache the result in DB
|
||||
syslog(LOG_WARNING, "GeocodeApi::geocode success => ".json_encode($geocoded));
|
||||
$address_output = pg_escape_string(html_entity_decode($geocoded["data"]["address"],ENT_QUOTES|ENT_HTML5,"UTF-8"));
|
||||
$geocoded["data"]['address_input'] = $address_input;
|
||||
$geocoded["data"]['country'] = $country;
|
||||
/* if(empty($geocoded["data"]['plus_code'])){
|
||||
$benchmarks['function'] = 'google geocode';
|
||||
|
||||
$reverse_info = self::reverseGeocodeService($db,$geocoded["data"]['lat'],$geocoded["data"]['lng']);
|
||||
|
||||
if(!empty($reverse_info[0]['plus_code'])){
|
||||
$geocoded["data"]['plus_code'] = $reverse_info[0]['plus_code'];
|
||||
}
|
||||
} */
|
||||
|
||||
$address = Geocode::save($db->getConnect(), $geocoded["data"]);
|
||||
if(isset($address['id'])){
|
||||
if($address_input!=$address["address"]){
|
||||
Address::saveAliasAddress($db->getConnect(), $address['id'], $address_input,"UserInput");
|
||||
}
|
||||
$geocoded["data"] = array_merge($geocoded["data"], $address);
|
||||
}
|
||||
return array($geocoded["data"], NULL);
|
||||
} else if (is_array($geocoded) && isset($geocoded["error"])) {
|
||||
$body = $geocoded["error"];
|
||||
}
|
||||
syslog(LOG_WARNING, "GeocodeApi::geocode => ".$body);
|
||||
return array(NULL, "Geocoding service call error: ".$body);
|
||||
}
|
||||
|
||||
public static function reverseGeocode($db, $lat, $lng) {
|
||||
syslog(LOG_WARNING,"GeocodeApi::reverseGeocode(${lat}, ${lng})");
|
||||
syslog(LOG_WARNING,"lat=${lat},lng=${lng}");
|
||||
if (($lat==NULL && $lng==NULL) || ($lat==0 && $lng==0)) {
|
||||
return [NULL, "Invalid latitude and longitude"];
|
||||
}
|
||||
// Reverse geocode using local DB
|
||||
list($res,$err) = Geocode::reverseGeocode($db->getConnect(), $lat, $lng);
|
||||
if (is_array($res) && isset($res["address"])) {
|
||||
return [$res["address"],NULL];
|
||||
}
|
||||
|
||||
// Revers geocode using gps DB
|
||||
list($res,$err) = Geocode::reverseGeocodeGps(
|
||||
$db->getConnect(), $db->getConnectGPS(), $lat, $lng);
|
||||
if (is_array($res) && isset($res["address"])) {
|
||||
return [$res["address"],NULL];
|
||||
}
|
||||
|
||||
// Reverse geocode service using Google Maps API
|
||||
list($res,$err) = GeocodeApi::reverseGeocodeService($db, $lat, $lng);
|
||||
if (is_array($res) && isset($res["address"]) && $res["address"]!="") {
|
||||
// Cache!
|
||||
Geocode::reverseGeocodeCache(
|
||||
$db->getConnect(), $res["address"], $lat, $lng, $res["postal"], $res["country"], $res["timeZoneId"], $res['plus_code']
|
||||
);
|
||||
return [$res["address"],NULL];
|
||||
} else {
|
||||
syslog(LOG_WARNING,"reverseGeocodeService() failed: ${err}");
|
||||
}
|
||||
return [NULL, $err];
|
||||
}
|
||||
|
||||
public static function reverseGeocodeService($db, $lat, $lng,$client_id="Unknown") {
|
||||
syslog(LOG_WARNING,"GeocodeApi::reverseGeocodeService(${lat},${lng})");
|
||||
global $savvyext;
|
||||
$httpAuthToken = $savvyext->cfgReadChar('system.oauth2_token');
|
||||
$oauth2_url = $savvyext->cfgReadChar('system.oauth2_url');
|
||||
$data = http_build_query(
|
||||
array(
|
||||
'lat' => $lat,
|
||||
'lng' => $lng
|
||||
)
|
||||
);
|
||||
$url = $oauth2_url."reverse?" . $data;
|
||||
$opts = array(
|
||||
'http' => array(
|
||||
'method' => "GET",
|
||||
'timeout' => 60, /* 1 minute */
|
||||
'header' =>
|
||||
"Content-Type: application/x-www-form-urlencoded\r\n" .
|
||||
"Accept: application/json\r\n" .
|
||||
"Authorization: Server-Token ${httpAuthToken}\r\n",
|
||||
),
|
||||
"ssl" => array(
|
||||
"verify_peer"=>false,
|
||||
"verify_peer_name"=>false,
|
||||
)
|
||||
);
|
||||
$context = stream_context_create($opts);
|
||||
$body = file_get_contents($url, false, $context);
|
||||
$headers = getallheaders();
|
||||
$client_id = isset($headers['client_id'])?$headers['client_id']:$client_id;
|
||||
//syslog(LOG_WARNING,"BODY: ".$body);
|
||||
$log = [
|
||||
'message' => 'GeocodeApi::reverse call',
|
||||
'data' => ['client_id'=>$client_id, 'lat'=>$lat, 'lng'=>$lng]
|
||||
];
|
||||
Logger::debug($log);
|
||||
$geocoded = json_decode($body,true);
|
||||
if (is_array($geocoded) && is_array($geocoded["data"]) && !isset($geocoded["error"])) {
|
||||
//syslog(LOG_WARNING,json_encode($geocoded));
|
||||
// Cache the result in DB
|
||||
/*Geocode::saveDistanceCache(
|
||||
$db->getConnect(), $fromLat, $fromLng, $toLat, $toLng, $geocoded["data"]);
|
||||
*/
|
||||
$address_output = pg_escape_string(html_entity_decode($geocoded["data"]["address"],ENT_QUOTES|ENT_HTML5,"UTF-8"));
|
||||
return [$geocoded["data"], NULL];
|
||||
} else if (is_array($geocoded) && isset($geocoded["error"])) {
|
||||
$body = $geocoded["error"];
|
||||
}
|
||||
return [NULL, "Reverse geocoding service call error: ".$body];
|
||||
}
|
||||
|
||||
public function checkWithinTheServiceArea($country,$result,$ex=true) {
|
||||
error_log("GeocodeApi::checkWithinTheServiceArea($country,\$result,".($ex?"TRUE":"FALSE").")");
|
||||
$limits = Geocode::COUNTRY_LIMITS;
|
||||
if (!is_array($limits) || !array_key_exists($country,$limits) || !is_array($limits[$country])) {
|
||||
return true; // Error, unhandled country or no limits
|
||||
}
|
||||
$fromLat = $fromLng = NULL;
|
||||
foreach ($result as $element) {
|
||||
if ($element["type"]==1) {
|
||||
$fromLat = $element["geocode"]["lat"];
|
||||
$fromLng = $element["geocode"]["lng"];
|
||||
|
||||
}
|
||||
}
|
||||
if ($fromLat==NULL || $fromLng==NULL) return true; // Do not know how to handle it - throw exception?
|
||||
$within = false;
|
||||
foreach ($limits[$country] as $limit) {
|
||||
$radius = $limit['radius']/1000.0;
|
||||
$d = GeocodeApi::distanceBetweenTwoGpsCoordinates(
|
||||
$fromLat,$fromLng,$limit['latitude'],$limit['longitude'],'K');
|
||||
//error_log("$d <= $radius");
|
||||
if ($d <= $radius) {
|
||||
$within = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($within) return true; // OK
|
||||
if ($ex) { // soft or hard fail?
|
||||
throw new RuntimeException('Origin address is out of the service area');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function options($db, $result, $options) {
|
||||
error_log('GeocodeApi::options()');
|
||||
$fromLat = $fromLng = $toLat = $toLng = NULL;
|
||||
foreach ($result as $element) {
|
||||
//error_log(json_encode($element));
|
||||
if ($element["type"]==1) {
|
||||
$fromLat = $element["geocode"]["lat"];
|
||||
$fromLng = $element["geocode"]["lng"];
|
||||
}
|
||||
if ($element["type"]==2) {
|
||||
$toLat = $element["geocode"]["lat"];
|
||||
$toLng = $element["geocode"]["lng"];
|
||||
}
|
||||
}
|
||||
//error_log(json_encode($result));
|
||||
if ($fromLat==NULL || $toLat==NULL) {
|
||||
$options['error'] = 'No coordinates available';
|
||||
return $options;
|
||||
}
|
||||
if ($options["travel_time"] && !$options["route_overlay"]) {
|
||||
//error_log('*** Google distance matrix ***');
|
||||
// Use distance matrix
|
||||
list($res, $err) = GeocodeApi::distancegps($db, $fromLat, $fromLng, $toLat, $toLng);
|
||||
//error_log(json_encode($res));
|
||||
if ($err!=NULL) {
|
||||
$options['error'] = $err;
|
||||
return $options;
|
||||
}
|
||||
$options["travel_time"] = $res["duration"];
|
||||
$options["travel_distance"] = $res["distance"];
|
||||
$options["route_overlay"] = NULL;
|
||||
} else {
|
||||
//error_log('*** Google directions ***');
|
||||
// Use directions
|
||||
list($res, $err) = GeocodeApi::route($db, $fromLat, $fromLng, $toLat, $toLng);
|
||||
if ($err!=NULL || !is_array($res) || !isset($res["routes"]) || count($res["routes"])<1) {
|
||||
$options['error'] = $err!=NULL ? $err : 'No available routes';
|
||||
return $options;
|
||||
}
|
||||
$options["route_overlay"] = [];
|
||||
$travel_time = PHP_INT_MAX;
|
||||
$travel_distance = PHP_INT_MAX;
|
||||
foreach ($res["routes"] as $route) {
|
||||
$r = GeocodeApi::processRoute($route);
|
||||
if ($travel_time>$r['duration']) {
|
||||
$travel_time = $r['duration'];
|
||||
$travel_distance = $r['distance'];
|
||||
}
|
||||
$options["route_overlay"][] = $r;
|
||||
}
|
||||
$options["travel_time"] = $travel_time==PHP_INT_MAX ? NULL : $travel_time;
|
||||
$options["travel_distance"] = $travel_distance==PHP_INT_MAX ? NULL : $travel_distance;
|
||||
}
|
||||
//https://developers.google.com/maps/documentation/utilities/polylinealgorithm
|
||||
//overview_polyline
|
||||
//bounds contains the viewport bounding box of the overview_polyline.
|
||||
return $options;
|
||||
}
|
||||
|
||||
public function distancegps($db, $fromLat, $fromLng, $toLat, $toLng) {
|
||||
global $savvyext;
|
||||
$httpAuthToken = $savvyext->cfgReadChar('system.oauth2_token');
|
||||
$oauth2_url = $savvyext->cfgReadChar('system.oauth2_url');
|
||||
// Check local DB cache
|
||||
/*
|
||||
list ($res, $err) = Geocode::checkDistanceCache(
|
||||
$db->getConnect(), $fromLat, $fromLng, $toLat, $toLng);
|
||||
if (is_array($res) && isset($res["duration"])) {
|
||||
return array($res, NULL);
|
||||
}
|
||||
*/
|
||||
// Call geocoding service
|
||||
$data = http_build_query(
|
||||
array(
|
||||
'gps' => implode(",",array($fromLat, $fromLng, $toLat, $toLng)),
|
||||
)
|
||||
);
|
||||
$url = $oauth2_url."distancegps?" . $data;
|
||||
$opts = array(
|
||||
'http' => array(
|
||||
'method' => "GET",
|
||||
'timeout' => 60, /* 1 minute */
|
||||
'header' =>
|
||||
"Content-Type: application/x-www-form-urlencoded\r\n" .
|
||||
"Accept: application/json\r\n" .
|
||||
"Authorization: Server-Token ${httpAuthToken}\r\n",
|
||||
),
|
||||
"ssl" => array(
|
||||
"verify_peer"=>false,
|
||||
"verify_peer_name"=>false,
|
||||
)
|
||||
);
|
||||
$context = stream_context_create($opts);
|
||||
$body = file_get_contents($url, false, $context);
|
||||
$geocoded = json_decode($body,true);
|
||||
if (is_array($geocoded) && is_array($geocoded["data"]) && !isset($geocoded["error"])) {
|
||||
// Cache the result in DB
|
||||
Geocode::saveDistanceCache(
|
||||
$db->getConnect(), $fromLat, $fromLng, $toLat, $toLng, $geocoded["data"]);
|
||||
return array($geocoded["data"], NULL);
|
||||
} else if (is_array($geocoded) && isset($geocoded["error"])) {
|
||||
$body = $geocoded["error"];
|
||||
}
|
||||
return array(NULL, "Geocoding service call error: ".$body);
|
||||
}
|
||||
|
||||
public function route($db, $fromLat, $fromLng, $toLat, $toLng, $mode='driving',$waypoints=[]) {
|
||||
syslog(LOG_WARNING,"GeocodeApi::route(\$db, $fromLat, $fromLng, $toLat, $toLng, $mode, \$waypoints=[])");
|
||||
global $savvyext;
|
||||
$httpAuthToken = $savvyext->cfgReadChar('system.oauth2_token');
|
||||
$oauth2_url = $savvyext->cfgReadChar('system.oauth2_url');
|
||||
// Check local DB cache
|
||||
/*list ($res, $err) = Geocode::checkDistanceCache(
|
||||
$db->getConnect(), $fromLat, $fromLng, $toLat, $toLng);
|
||||
if (is_array($res) && isset($res["duration"])) {
|
||||
return array($res, NULL);
|
||||
}*/
|
||||
// Call geocoding service
|
||||
$data = http_build_query(
|
||||
array(
|
||||
'gps' => implode(",",array($fromLat, $fromLng, $toLat, $toLng)),
|
||||
'mode' => $mode,
|
||||
'waypoints' => implode(",",$waypoints)
|
||||
)
|
||||
);
|
||||
$url = $oauth2_url."route?" . $data;
|
||||
$opts = array(
|
||||
'http' => array(
|
||||
'method' => "GET",
|
||||
'timeout' => 60, /* 1 minute */
|
||||
'header' =>
|
||||
"Content-Type: application/x-www-form-urlencoded\r\n" .
|
||||
"Accept: application/json\r\n" .
|
||||
"Authorization: Server-Token ${httpAuthToken}\r\n",
|
||||
),
|
||||
"ssl" => array(
|
||||
"verify_peer"=>false,
|
||||
"verify_peer_name"=>false,
|
||||
)
|
||||
);
|
||||
$context = stream_context_create($opts);
|
||||
$body = file_get_contents($url, false, $context);
|
||||
file_put_contents('/home/savvy/FloatWeb/adminsavvy/routes.json', $body);
|
||||
//file_put_contents('/home/savvy/FloatWeb/routes.json',json_encode(array('test'=>false)));
|
||||
$geocoded = json_decode($body,true);
|
||||
if (is_array($geocoded) && is_array($geocoded["data"]) && !isset($geocoded["error"])) {
|
||||
// Cache the result in DB
|
||||
/*Geocode::saveDistanceCache(
|
||||
$db->getConnect(), $fromLat, $fromLng, $toLat, $toLng, $geocoded["data"]);
|
||||
*/
|
||||
return array($geocoded["data"], NULL);
|
||||
} else if (is_array($geocoded) && isset($geocoded["error"])) {
|
||||
$body = $geocoded["error"];
|
||||
}
|
||||
return array(NULL, "Routing service call error: ".$body);
|
||||
}
|
||||
|
||||
public function processRoute($route) {
|
||||
require_once('../common/Polyline.php');
|
||||
$result = [
|
||||
'polyline' => array(),
|
||||
'distance' => NULL,
|
||||
'duration' => NULL,
|
||||
'summary' => $route["summary"],
|
||||
'overview_polyline' => $route["overview_polyline"],
|
||||
'bounds' => $route["bounds"]
|
||||
];
|
||||
// ["bounds"]=> ["northeast"] => (["lat"],["lng"])
|
||||
// ["bounds"]=> ["southwest"] => (["lat"],["lng"])
|
||||
$distance = 0;
|
||||
$duration = 0;
|
||||
foreach ($route["legs"] as $leg) {
|
||||
$distance += $leg["distance"]["value"];
|
||||
$duration += $leg["duration"]["value"];
|
||||
// ["start_address"]=> "10 Bayfront Ave, Singapore 018956"
|
||||
// ["end_address"] => "97 Meyer Rd, Singapore 437918"
|
||||
// ["start_location"] => (["lat"],["lng"])
|
||||
// ["end_location"]=> (["lat"],["lng"])
|
||||
foreach ($leg["steps"] as $step) {
|
||||
// ["distance"]=> ["value"]
|
||||
// ["duration"]=> ["value"]
|
||||
// ["start_location"] => (["lat"],["lng"])
|
||||
// ["end_location"]=> (["lat"],["lng"])
|
||||
// ["html_instructions"]=> "Head <b>north</b> on <b>Sheares Ave</b> toward <b>Exit 15</b>"
|
||||
// ["polyline"]=> array(1) { ["points"] } string(45) "osyFek|xR}ASyCY}@Ga@Eo@CsDCaBAaB?u@As@?q@?q@?"
|
||||
// ["travel_mode"]=> string(7) "DRIVING"
|
||||
$points = Polyline::decode($step["polyline"]["points"]);
|
||||
$result['polyline'] = array_merge($result['polyline'],Polyline::pair($points));
|
||||
}
|
||||
}
|
||||
//=> array(1) { ["points"]=> string(429) "osyFek|xR}ASyCY_BMcFGcEAmEAw@NwBBsB@aADeDBoC@cEHoCNiALuARa@?O?g@HeCf@_Bn@UPOL{@b@{@p@iAnAwA~AeAfAcC`CeDtC{@r@aCrB{BpBOPMK{BmB}GyFiBgBcBeBeD}DcDaEkEaFaBiBeCkCeAkA{BaCc@m@Q_@Om@WwA_@oC_BqKi@aCi@}BSsAaAiFW{@Ui@[i@kE}FsA}Bw@{A]u@e@iB_@aBAGvBU`AMzBc@hB_@dEu@|Di@b@K`@W^a@\_AZ_AHi@Ro@x@mDr@{BvAoFt@}BZw@l@gAbBcC`@q@fA}An@u@hAmAnAmAZ[hCuA~@MjAQ~AElBGbAEZEb@MTOR]P_@HWBo@N}FLiCBu@Ly@JYp@_EdGz@hALz@FdDd@Fa@TiBh@aE^oCCe@yAeHj@GN@NRPRN?r@O"}
|
||||
//=> string(14) "Mountbatten Rd"
|
||||
$result['distance'] = $distance;
|
||||
$result['duration'] = $duration;
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function withinCity($address,$addrLat,$addrLng,$city,$cityLat,$cityLng,$radius=25) {
|
||||
//error_log("public function withinCity($address,$addrLat,$addrLng,$city,$cityLat,$cityLng,$radius)");
|
||||
if (stripos($address,$city)!==false) {
|
||||
//error_log("'${address}' is within the '${city}'");
|
||||
return true;
|
||||
}
|
||||
$distance = GeocodeApi::distanceBetweenTwoGpsCoordinates($addrLat,$addrLng,$cityLat,$cityLng,'K');
|
||||
if ($distance!=0 && $distance<$radius) {
|
||||
//error_log("${addrLat},${addrLng} is less(${distance}) than ${radius}km from ${cityLat},${cityLng}");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function distanceBetweenTwoGpsCoordinates($lat1,$lon1,$lat2,$lon2,$unit) {
|
||||
//error_log("public function distanceBetweenTwoGpsCoordinates($lat1,$lon1,$lat2,$lon2,$unit)");
|
||||
if (($lat1 == $lat2) && ($lon1 == $lon2)) {
|
||||
return 0;
|
||||
}
|
||||
$theta = $lon1 - $lon2;
|
||||
$dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
|
||||
$dist = acos($dist);
|
||||
$dist = rad2deg($dist);
|
||||
$miles = $dist * 60 * 1.1515;
|
||||
$unit = strtoupper($unit);
|
||||
if ($unit == "K") {
|
||||
return ($miles * 1.609344);
|
||||
}
|
||||
if ($unit == "N") {
|
||||
($miles * 0.8684);
|
||||
}
|
||||
return $miles;
|
||||
}
|
||||
|
||||
|
||||
private function getProviders($db, $gps_country_code, $country, $options, $member_id) {
|
||||
error_log("GeocodeApi::getProviders(\$db, $gps_country_code, $country, \$options, $member_id");
|
||||
$service_enabled = true;
|
||||
$currency_code = 'SGD';
|
||||
error_log('gps_country_code='.$gps_country_code);
|
||||
error_log('country='.$country);
|
||||
if ($gps_country_code=='UA') { // DEBUG
|
||||
$service_enabled = true;
|
||||
$gps_country_code = 'US';
|
||||
$country = 'US';
|
||||
}
|
||||
// For not we support US & SG only!
|
||||
if ($gps_country_code!='US' && $gps_country_code!='SG') {
|
||||
// We will show Singapore, but disable service
|
||||
$service_enabled = false;
|
||||
$gps_country_code = 'SG';
|
||||
$country = 'SG';
|
||||
}
|
||||
// TODO: change to select from "country" table
|
||||
if ($gps_country_code=='US') {
|
||||
$currency_code = 'USD';
|
||||
} else {
|
||||
$currency_code = 'SGD';
|
||||
}
|
||||
list ($result, $error) = Geocode::getTransportProvidersByCountry(
|
||||
$db->getConnect(), $gps_country_code);
|
||||
if ($result==NULL) {
|
||||
error_log($error);
|
||||
}
|
||||
//error_log("gps_country_code=$gps_country_code, service_enabled=$service_enabled, currency_code=$currency_code");
|
||||
return array($result, $gps_country_code, $service_enabled, $currency_code);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user