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

1087 lines
49 KiB
PHP

<?php
class ActivityApi extends Api
{
public $apiName = 'activity';
public function indexAction()
{
return $this->response(
array(
'error' => 'Data not found'
), 404);
}
/**
* Method GET
* Get single record (by id)
* http://DOMAIN/trips/1
* @return string
*/
public function viewAction()
{
return $this->response(
array(
'error'=> 'Data not found'
), 404);
}
public function createAction()
{
syslog(LOG_WARNING,'ActivityApi::createAction()');
$message = 'We could not find any recent activity in this area';
$code = 404;
$time = $this->requestParams['time'] ?? '';
$location = $this->requestParams['location'] ?? [];
$country = $this->requestParams['country'] ?? '';
$member_id = $this->requestParams['member_id'] ?? 0;
try {
$db = new Db();
$country = Geocode::getCountryByGPS($db->getConnect(), $location, $country, Activity::DEFAULT_COUNTRY);
syslog(LOG_WARNING,'country='.$country);
$country = Geocode::mockGPSCountry($db->getConnect(), $member_id, $country, 'default');
if ($country!='US' && $country!='SG') {
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 ($member_id<1) {
throw new RuntimeException('Invalid member ID',500);
}
if ($time=='' || strtotime($time)<Activity::TIME_LIMIT) {
throw new RuntimeException('Invalid time',500);
}
$address = NULL;
$street_address = "";
syslog(LOG_WARNING, "location => " . json_encode($location));
if (is_array($location) && isset($location["lat"]) && isset($location["lng"])) {
// DEBUG
list($location["lat"],$location["lng"]) = Geocode::mockGPSLocation(
$db->getConnect(), $member_id, $location["lat"], $location["lng"], 'default');
syslog(LOG_WARNING,"lat=".$location["lat"].",lng=".$location["lng"]);
list($street_address,$err) = GeocodeApi::reverseGeocode($db, $location["lat"], $location["lng"]);
if ($street_address=="") {
list($address,$err) = GeofenceApi::getAnchor($db, $location["lat"], $location["lng"]);
if (is_array($address) && array_key_exists("address",$address) && $address["address"]!="") {
$street_address = $address["address"];
}
}
if ($street_address=="") {
syslog(LOG_WARNING,'Reverse geocoder failed for ('.$location["lat"].', '.$location["lng"].')');
throw new RuntimeException($err?"Geocoder failed: $err":'Cannot geocode your GPS location',500);
}
syslog(LOG_WARNING,"street_address=".json_encode($street_address));
if (!is_array($address) || !array_key_exists("address",$address) || $address["address"]=="") {
list($address,$err) = Geocode::checkLatLngByAddress($db->getConnect(), $street_address, $country);
}
if (!is_array($address) || !isset($address["address"])) {
syslog(LOG_WARNING,'Geocoder failed for "'.$street_address.'"');
throw new RuntimeException($err?"Geocoder failed: $err":'Cannot get address for your GPS location '.$street_address,500);
}
// Adjust GPS coordinates if any
if (array_key_exists($address["latitude"]) && $address["latitude"]!=null &&
array_key_exists($address["longitude"]) && $address["longitude"]!=null &&
$address["latitude"]!=0 && $address["longitude"]!=0) {
$location["lat"] = $address["latitude"];
$location["lng"] = $address["longitude"];
$address["lat"] = $address["latitude"];
$address["lng"] = $address["longitude"];
} else if (array_key_exists($address["lat"]) && $address["lat"]!=null &&
array_key_exists($address["lng"]) && $address["lng"]!=null &&
$address["lat"]!=0 && $address["lng"]!=0) {
$location["lat"] = $address["lat"];
$location["lng"] = $address["lng"];
$address["latitude"] = $address["lat"];
$address["longitude"] = $address["lng"];
}
list($location,$address) = ActivityApi::adjustAddressLocationGPS($location,$address);
}
// Step 0. Check if we are in the service area
/* GeocodeApi::checkWithinTheServiceArea($country,[
[
"type"=>1,
"geocode" => [
"lat" => $location["lat"],
"lng" => $location["lng"]
]
]
]); //*/
syslog(LOG_WARNING, "location => " . json_encode($location));
// Step 1. Get activities
list($res,$err) = Activity::getActivity(
$db->getConnect(), $db->getConnectGPS(), $member_id,
$location["lat"], $location["lng"], Activity::ACTIVITY_RADIUS,
$time, Activity::TIME_DELTA, $country);
if (!$res || count($res)<1) {
if ($err!="") {
throw new RuntimeException($err, 500);
}
throw new RuntimeException($message, 404);
}
syslog(LOG_WARNING,'Recent activity has '.count($res).' trips!');
//ActivityApi::debugTrips($res,'0');
$destination_cache = [];
$result = [];
$i = 0;
foreach ($res as $trip) {
list($trip, $destination_cache) = ActivityApi::processAddressTrip($db, $country, $address, $trip, $destination_cache);
ActivityApi::debugTrips([$trip],'1');
if ($trip==NULL) continue;
$advice = ActivityApi::processAdvice($db, $member_id, $country, $trip);
ActivityApi::debugTrips([$advice],'2');
if ($advice && count($advice)>0) {
$result[] = $advice;
$i++;
}
if ($i>=Activity::DEFAULT_OPTIONS) {
break;
}
}
$result = ActivityApi::uniqueFilter($db, $result);
//syslog(LOG_WARNING,json_encode($result));
ActivityApi::debugTrips($result,'3');
$result = ActivityApi::processQuotesTrips($db, $member_id, $country, $result);
ActivityApi::debugTrips($result,'4');
//$result = ActivityApi::processGojekOption($db, $member_id, $country, $result);
//ActivityApi::debugTrips($result,'5');
//syslog(LOG_WARNING,json_encode($result));
return $this->response(
array(
"trips" => $result,
"street_address" => html_entity_decode($street_address)
), 200);
} catch (RuntimeException $e) {
$message = $e->getMessage();
$code = $e->getCode();
syslog(LOG_WARNING,$message);
}
return $this->response(
array(
"error" => $message
), $code);
}
private static function adjustAddressLocationGPS($location,$address) {
syslog(LOG_WARNING,'ActivityApi::adjustAddressLocationGPS($location,$address)');
if (array_key_exists($address["latitude"]) && $address["latitude"]!=null &&
array_key_exists($address["longitude"]) && $address["longitude"]!=null &&
$address["latitude"]!=0 && $address["longitude"]!=0) {
$location["lat"] = $address["latitude"];
$location["lng"] = $address["longitude"];
$address["lat"] = $address["latitude"];
$address["lng"] = $address["longitude"];
} else if (array_key_exists($address["lat"]) && $address["lat"]!=null &&
array_key_exists($address["lng"]) && $address["lng"]!=null &&
$address["lat"]!=0 && $address["lng"]!=0) {
$location["lat"] = $address["lat"];
$location["lng"] = $address["lng"];
$address["latitude"] = $address["lat"];
$address["longitude"] = $address["lng"];
}
//syslog(LOG_WARNING,json_encode($address));
//syslog(LOG_WARNING,"location => " . json_encode($location));
return [$location,$address];
}
public static function debugTrips($trips,$what='0') {
return; /*
foreach ($trips as $trip) {
$leg_fare = $trip['multimodal']['options']['leg_fare'];
syslog(LOG_WARNING,'>>>>>>> '.$what.' >>>>>>> '.json_encode($leg_fare));
}*/
}
public function updateAction()
{
return $this->response(
array(
"error" => "Update error"
), 400);
}
public function deleteAction()
{
return $this->response(
array(
"error" => "Delete error"
), 500);
}
protected function uniqueFilter($db, $result) {
syslog(LOG_WARNING,'ActivityApi::uniqueFilter()');
$cache = [];
$filtered = [];
foreach ($result as $trip) {
list ($cached, $cache) = ActivityApi::checkCache($trip["trip"], $cache);
if ($cached) {
$filtered[] = $trip;
} else {
syslog(LOG_WARNING,'Duplicate trip? id=' . $trip["trip"]["id"]);
syslog(LOG_WARNING,json_encode($trip["trip"]));
}
}
if (count($filtered)>0) {
return $filtered;
} // Let's hope we will show something...
return $result;
}
protected function checkCache($trip, $cache) {
syslog(LOG_WARNING,'ActivityApi::checkCache()');
foreach ($cache as $item) {
if ($item["id"]>0 && $item["id"]==$trip["id"]) {
return [false, $cache];
}
if ($item["location_end_id"]>0 && $item["location_end_id"]==$trip["location_end_id"]) {
return [false, $cache];
}
}
$cache[] = $trip;
return [true, $cache];
}
public function processAddressTrip($db, $country, $address, $trip, $cache) {
syslog(LOG_WARNING,'ActivityApi::processAddressTrip()');
/*
{ "id":"10888",
"travel_date":"2019-08-23 23:54:00",
"duration":"13",
"cost_raw":"SGD 18.00",
"trackedemail_item_id":"2117062",
"cost":"18.00","updated":
"2019-08-23 19:31:39.916296",
"distance":"7.38",
"transport_provider_id":"3",
"scheduled":null,
"travel_date_end":"2019-08-24 00:07:10",
"dup_id":null,"location_start_id":"523",
"location_end_id":"42",
"private":"f",
"dm":"1434"}
*/
if (!is_array($address) || !isset($address["lat"]) || !isset($address["lng"])
|| ($address["lat"]==0 && $address["lng"]==0)) {
return [NULL, $cache];
}
if ($address["id"]==$trip["location_start_id"]) {
list($pass, $cache) = ActivityApi::checkCache($trip, $cache);
if ($pass) {
return [$trip, $cache];
}
}
list($trip1,$err) = Trips::tripByLocations($db->getConnect(), $address["id"], $trip["location_start_id"]);
if (is_array($trip1) && isset($trip1["id"]) && $trip1["id"]>0) {
list($pass, $cache) = ActivityApi::checkCache($trip1, $cache);
if ($pass) {
return [$trip1, $cache];
}
}
$loc_start = Address::getAddressById($db->getConnect(), $trip["location_start_id"]);
syslog(LOG_WARNING,json_encode($loc_start));
if (round($address["lat"],3)==round($loc_start["latitude"],3) && round($address["lng"],3)==round($loc_start["longitude"],3)) {
list($pass, $cache) = ActivityApi::checkCache($trip1, $cache);
if ($pass) {
return [$trip1, $cache];
}
}
list($trip2,$err) = Trips::tripByLocations($db->getConnect(), $address["id"], $trip["location_end_id"]);
if (is_array($trip2) && isset($trip2["id"]) && $trip2["id"]>0) {
list($pass, $cache) = ActivityApi::checkCache($trip2, $cache);
if ($pass) {
return [$trip2, $cache];
}
}
$loc_end = Address::getAddressById($db->getConnect(), $trip["location_end_id"]);
syslog(LOG_WARNING,json_encode($loc_end));
if (round($address["lat"],3)==round($loc_end["latitude"],3) && round($address["lng"],3)==round($loc_end["longitude"],3)) {
list($pass, $cache) = ActivityApi::checkCache($trip2, $cache);
if ($pass) {
return [$trip2, $cache];
}
}
// Get distance, duration
$distance = sprintf("%0.02f",Activity::CITY_CURVATURE * GeocodeApi::distanceBetweenTwoGpsCoordinates($address["lat"],$address["lng"],$loc_start["latitude"],$loc_start["longitude"],'K'));
$duration = (int)($distance / Activity::CITY_SPEED);
syslog(LOG_WARNING,"distance1=${distance}");
syslog(LOG_WARNING,json_encode([$address["lat"],$address["lng"],$loc_start["latitude"],$loc_start["longitude"]]));
if (Activity::MIN_ACTIVITY_DISTANCE<=$distance && $distance<=Activity::MAX_ACTIVITY_DISTANCE) { // 50m? < x < 50km?
$trip1 = ActivityApi::createTrip($db, $country, $duration, $distance, $address["id"], $trip["location_start_id"]);
if (is_array($trip1) && isset($trip1["id"]) && $trip1["id"]>0) {
list($pass, $cache) = ActivityApi::checkCache($trip1, $cache);
if ($pass) {
return [$trip1, $cache];
}
}
}
$distance = sprintf("%0.02f",Activity::CITY_CURVATURE * GeocodeApi::distanceBetweenTwoGpsCoordinates($address["lat"],$address["lng"],$loc_end["latitude"],$loc_end["longitude"],'K'));
$duration = (int)($distance / Activity::CITY_SPEED);
syslog(LOG_WARNING,"distance2=${distance}");
syslog(LOG_WARNING,json_encode([$address["lat"],$address["lng"],$loc_end["latitude"],$loc_end["longitude"]]));
if (Activity::MIN_ACTIVITY_DISTANCE<=$distance && $distance<=Activity::MAX_ACTIVITY_DISTANCE) { // 50m? < x < 50km?
$trip2 = ActivityApi::createTrip($db, $country, $duration, $distance, $address["id"], $trip["location_end_id"]);
if (is_array($trip2) && isset($trip2["id"]) && $trip2["id"]>0) {
list($pass, $cache) = ActivityApi::checkCache($trip2, $cache);
if ($pass) {
return [$trip2, $cache];
}
}
}
//syslog(LOG_WARNING,json_encode($trip));
return [NULL, $cache];
}
public function createTrip($db, $country, $duration, $distance, $location_start_id, $location_end_id) {
syslog(LOG_WARNING,"ActivityApi::createTrip(\$db, $country, $duration, $distance, $location_start_id, $location_end_id)");
$data = [
'travel_date' => MultiModal::getDate($country),
'duration' => (int)$duration,
'cost_raw' => NULL,
'trackedemail_item_id' => NULL,
'cost' => NULL,
'distance' => $distance,
'transport_provider_id' => NULL,
'scheduled' => NULL,
'travel_date_end' => MultiModal::getDate($country),
'dup_id' => NULL,
'location_start_id' => $location_start_id,
'location_end_id' => $location_end_id,
'private' => 't' /* this is a private entry without the source e-mail */
];
list($id, $err) = Trips::create($db->getConnect(), $data);
if ($id && $id>0) {
$trip = Trips::getById($db->getConnect(), $id);
return $trip;
}
return NULL; // WTF?
}
public function processAdvice($db, $member_id, $country, $trip) {
syslog(LOG_WARNING,"ActivityApi::processAdvice(\$db, ${member_id}, ${country}, \$trip)");
$advice = [
"trip"=>$trip,
"scooter"=>[],
"multimodal"=>[],
"rideshare"=>[],
"faster"=>NULL,
"cheaper"=>NULL
];
$durations = [
"scooter" => PHP_INT_MAX,
"multimodal" => PHP_INT_MAX,
"rideshare" => PHP_INT_MAX
];
try {
$id = $trip["id"];
// Get addresses
$address_start = Address::getAddressById($db->getConnect(), $trip["location_start_id"]);
$address_end = Address::getAddressById($db->getConnect(), $trip["location_end_id"]);
if (!$address_start || !$address_end || count($address_start)<3 || count($address_end)<3 ||
($address_start["latitude"]==0 && $address_start["longitude"]==0) ||
($address_end["latitude"]==0 && $address_end["longitude"]==0)) {
throw new Exception("Invalid address");
}
// {"id":"10888","travel_date":"2019-08-23 23:54:00","duration":"13","cost_raw":"SGD 18.00","trackedemail_item_id":"2117062","cost":"18.00","updated":"2019-08-23 19:31:39.916296","distance":"7.38","transport_provider_id":"3","scheduled":null,"travel_date_end":"2019-08-24 00:07:10","dup_id":null,"location_start_id":"523","location_end_id":"42","private":"f","dm":"1434"}
// Step 2. Get advice
// Step 2.0. Geocode?
// Step 2.1. Scooter
$data = []; $options = []; $err = NULL;
if ($country!='SG') {
list($data, $options, $err) = ActivityApi::scooter($db, [
[
"address" => $address_start["address"],
"type" => 1,
"coordinates" => [
"lat" => $address_start["latitude"],
"lng" => $address_start["longitude"]
]
],
[
"address" => $address_end["address"],
"type" => 2,
"coordinates" => [
"lat" => $address_end["latitude"],
"lng" => $address_end["longitude"]
]
]
], [
"travel_time" => true,
"quote" => true,
"route_overlay" => true
]);
}
if ($data && count($data)) {
$advice["scooter"] = ["data"=>$data,"options"=>$options];
$durations["scooter"] = 0;
foreach ($data as $step) {
$durations["scooter"] += $step["duration"];
}
}
// Step 2.2. Multimodal
$leg_id = 0;
$checkOptions = [
[
"type"=>1,
"geocode" => [
"lat" => $address_start["latitude"],
"lng" => $address_end["longitude"]
]
]
];
syslog(LOG_WARNING,"ActivityApi: ".json_encode($checkOptions));
$withinTheServiceArea = GeocodeApi::checkWithinTheServiceArea($country,$checkOptions,false);
if ($withinTheServiceArea) {
syslog(LOG_WARNING,"ActivityApi: WITHIN SERVICE AREA!");
ActivityApi::debugTrips([['multimodal'=>$trip]],'11');
$trip = MultiModalApi::multimodal(
$db, $address_start, $address_end, $country, false/*no cache?*/, $member_id,
MultiModalApi::CACHE_TIMEOUT, $trip["distance"], $trip["duration"]
);
ActivityApi::debugTrips([['multimodal'=>$trip]],'12');
if ($trip && count($trip) && array_key_exists("options",$trip)) {
$advice["multimodal"] = $trip;
foreach ($trip["options"]["legs"] as $leg) {
if ($leg["duration"]<$durations["multimodal"]) {
$durations["multimodal"] = $leg["duration"];
$leg_id = $leg["id"];
}
}
}
} else {
syslog(LOG_WARNING,'ActivityApi: IS NOT WITHIN SERVICE AREA!');
$trip = ActivityApi::createTrip(
$db, $country, $trip["duration"], $trip["distance"],
$trip["location_start_id"], $trip["location_end_id"]
);
}
// Step 2.3. Rideshare
/******* Start GOJEK ******/
//$trip = MultiModalApi::processGojekOption($db, $member_id, $country, $trip);
/******* End GOJEK ******/
if (!$withinTheServiceArea && $country=='US') {
$trip = MultiModalApi::processRidesharesOption($db, $member_id, $country, $trip);
syslog(LOG_WARNING,'ActivityApi: >>>>>>> '.json_encode($trip));
if (is_array($trip) && count($trip)>0 && array_key_exists("options",$trip)) {
$advice["rideshare"] = $trip;
foreach ($trip["options"]["legs"] as $leg) {
if ($leg["duration"]<$durations["multimodal"]) {
$durations["rideshare"] = $leg["duration"];
$leg_id = $leg["id"];
}
}
}
}
// Durations...
foreach ($durations as $key=>$val) {
if ($val==PHP_INT_MAX) {
$durations[$key] = 0;
}
}
// Process advice...
return $advice;
// END
} catch (Exception $e) {
syslog(LOG_WARNING,$e->getMessage());
}
return NULL;
}
public function scooter($db, $addresses, $options) {
syslog(LOG_WARNING,"ActivityApi::scooter()");
$message = "Failed to run scooter directions";
try {
if ($addresses==NULL || !is_array($addresses) || count($addresses)<1 || !isset($addresses[0]["address"])) {
throw new Exception("Invalid input");
}
$result = [];
$input = [];
foreach ($addresses as $item) {
if (isset($item["type"]) && $item["type"]>0 && $item["type"]<3) {
$input[$item["type"]] = $item;
}
}
if (isset($input[1]) && isset($input[2]) &&
isset($input[1]["address"]) && isset($input[2]["address"])) {
list($result, $err) = ScooterApi::scooter($db, $input);
if (!is_array($result) || count($result)<1) {
throw new Exception($err!=''? $err : 'No scooter option available');
}
} else {
throw new Exception("Invalid address");
}
if ((isset($options["travel_time"]) && $options["travel_time"]) ||
(isset($options["quote"]) && $options["quote"]) ||
(isset($options["route_overlay"]) && $options["route_overlay"])) {
$options = ScooterApi::options($db, $input, $result, $options);
}
return [$result, $options, NULL];
} catch (Exception $e) {
syslog(LOG_WARNING,json_encode($e));
$message = $e->getMessage();
}
return [NULL, NULL, $message];
}
public static function processQuotesTrips($db, $member_id, $country, $trips) {
syslog(LOG_WARNING,"ActivityApi::processQuotes(\$db, $member_id, $country, \$trips)");
if (!is_array($trips) || count($trips)<1) {
syslog(LOG_WARNING,'Result is missing trips!');
return $trips; // Whatever it was - not our fault here
}
foreach ($trips as $key=>$trip) {
$trips[$key] = ActivityApi::processQuotesTrip($db, $member_id, $country, $trip);
}
return $trips;
}
protected static function processQuotesTrip($db, $member_id, $country, $trip) {
syslog(LOG_WARNING,"ActivityApi::processQuotesTrip(\$db, $member_id, $country, \$trip)");
if (!is_array($trip) || !array_key_exists('multimodal', $trip) || !is_array($trip['multimodal']) || count($trip['multimodal'])<1) {
syslog(LOG_WARNING,'Trip is missing multimodal!');
return $trip; // Whatever it was - not our fault here
}
// Process multimodal
$trip['multimodal'] = ActivityApi::processQuotesTripMultimodal($db, $member_id, $country, $trip);
// Process scooter
if (array_key_exists('scooter', $trip) && count($trip['scooter'])>0) {
$trip['scooter'] = ActivityApi::processQuotesTripScooter($db, $member_id, $country, $trip);
}
// Process rideshare
if (array_key_exists('rideshare', $trip) && count($trip['rideshare'])>0) {
$trip['rideshare'] = ActivityApi::processQuotesTripRideshare($db, $member_id, $country, $trip);
}
return $trip;
}
protected static function processQuotesTripMultimodal($db, $member_id, $country, $trip) {
syslog(LOG_WARNING,"ActivityApi::processQuotesTripMultimodal(\$db, $member_id, $country, \$trip)");
if (!array_key_exists('options',$trip['multimodal']) || count($trip['multimodal']['options'])<1) {
syslog(LOG_WARNING,'Multimodal trip is missing options!');
return $trip['multimodal']; // we had checked its existance up the call stack
}
$multimodal = $trip['multimodal'];
$multimodal['options'] = ActivityApi::processQuotesTripMultimodalOptions(
$db, $member_id, $country, $multimodal['options']);
return $multimodal;
}
public static function processQuotesTripMultimodalOptions($db, $member_id, $country, $options) {
syslog(LOG_WARNING,"ActivityApi::processQuotesTripMultimodalOptions(\$db, $member_id, $country, \$options)");
if (!is_array($options) || !array_key_exists('legs',$options) || !array_key_exists('leg_steps',$options)
|| !is_array($options['legs']) || !is_array($options['leg_steps'])
|| count($options['legs'])<1 || count($options['leg_steps'])<1) {
syslog(LOG_WARNING,'Multimodal trip options is missing data!');
return $options; // we had checked its existance up the call stack
}
$legs = $options['legs'];
$leg_steps = $options['leg_steps'];
foreach ($legs as $key=>$leg) {
$steps = $leg_steps[$leg['id']];
list($leg, $steps) = ActivityApi::processQuotesTripMultimodalLegSteps($db, $member_id, $country, $leg, $steps);
$leg_steps[$leg['id']] = $steps;
$legs[$key] = $leg;
}
$options['leg_steps'] = $leg_steps;
$options['legs'] = $legs;
return $options;
}
protected static function processQuotesTripMultimodalLegSteps($db, $member_id, $country, $leg, $steps) {
syslog(LOG_WARNING,"ActivityApi::processQuotesTripMultimodalLegSteps(\$db, $member_id, $country, \$leg, \$steps)");
if (!is_array($leg) || !is_array($steps) || !array_key_exists('id',$leg) || count($steps)<1) {
syslog(LOG_WARNING,'Multimodal trip option leg is missing data!');
return [$leg, $steps];
}
$total = 0;
foreach ($steps as $key=>$step) {
$step = ActivityApi::processQuotesTripMultimodalLegStep($db, $member_id, $country, $step, $leg['id']);
if (array_key_exists('fare_raw',$step) && $step['fare_raw']>0) {
$total += $step['fare_raw'];
}
$steps[$key] = $step;
}
if (!array_key_exists('fare_raw',$leg) || $leg['fare_raw']<$total) {
$leg['fare_raw'] = $total;
}
return [$leg, $steps];
}
protected static function processQuotesTripMultimodalLegStep($db, $member_id, $country, $step, $leg_id) {
syslog(LOG_WARNING,"ActivityApi::processQuotesTripMultimodalLegStep(\$db, $member_id, $country, \$step, $leg_id)");
if (!is_array($step) || !array_key_exists('travel_mode',$step) || ActivityApi::noGPSCoordinates($step)) {
syslog(LOG_WARNING,'Multimodal trip option leg step is missing data!');
return $step;
}
if ($step['travel_mode']=='WALKING') {
// Walking is free!
return $step;
}
if ($step['travel_mode']=='BIKE') {
// Biking is free!
return $step;
}
if ($step['travel_mode']=='NUS_TRANSIT') {
// NUS shuttle is free!
return $step;
}
if ($step['travel_mode']=='PARK') {
// I do not know what to do with parking right now
return $step;
}
if ($step['travel_mode']=='SCOOTER') {
return ActivityApi::processQuotesTripMultimodalLegStepScooter($db, $member_id, $country, $step, $leg_id);
}
if ($step['travel_mode']=='TAXI') {
return ActivityApi::processQuotesTripMultimodalLegStepTaxi($db, $member_id, $country, $step, $leg_id);
}
if ($step['travel_mode']=='TRANSIT') {
return ActivityApi::processQuotesTripMultimodalLegStepTransit($db, $member_id, $country, $step, $leg_id);
}
return $step;
}
protected static function processQuotesTripMultimodalLegStepScooter($db, $member_id, $country, $step, $leg_id) {
syslog(LOG_WARNING,"ActivityApi::processQuotesTripMultimodalLegStepScooter(\$db, $member_id, $country, \$step, $leg_id)");
// TODO: Scooter
return $step;
}
protected static function processQuotesTripMultimodalLegStepTaxi($db, $member_id, $country, $step, $leg_id) {
syslog(LOG_WARNING,"ActivityApi::processQuotesTripMultimodalLegStepTaxi(\$db, $member_id, $country, \$step, $leg_id)");
// Quote Taxi
if ($country!='SG') { // We support Singapore/ComfortDelGro only for now!
return $step;
}
// Do we have a quote already?
if (array_key_exists('fare_quote',$step) && $step['fare_quote']>0) {
$step['fare_raw'] = $step['fare_quote'];
return $step;
}
list ($origin, $err) = GeocodeApi::reverseGeocode($db, $step['location_start_lat'], $step['location_start_lng']);
list ($destination, $err) = GeocodeApi::reverseGeocode($db, $step['location_end_lat'], $step['location_end_lng']);
$transport_provider_id = Quotes::VENDOR_COMFORTDELGRO; // ComfortDelGro
$quote_group_id = NULL; // Not groupped
$trackedemail_item_id = NULL; // Not from a receipt
try {
list($message,$code,$action,$data) = QuoteAPI::createReal(
$db, $origin, $destination, $member_id, $transport_provider_id,
$quote_group_id, $trackedemail_item_id, $country);
if ($code==200) {
$load = true;
if ($action=="view") {
$quote = Quotes::getById($db->getConnect(), (int)$data["id"]);
if (is_array($quote) && count($quote)>0 && $quote["completed"]!="" && (int)$quote["cost"]!=0) {
$load = false;
$step['fare_raw'] = (int)$quote["cost"];
}
}
if ($load) {
// Load
for ($i=0; $i<3; $i++) {
sleep(1); // wait for a sec
$quote = Quotes::getById($db->getConnect(), (int)$data["id"]);
if (is_array($quote) && count($quote)>0 && $quote["completed"]!="" && (int)$quote["cost"]!=0) {
$step['fare_raw'] = (int)$quote["cost"];
break;
}
}
}
}
} catch (Exception $e) {
syslog(LOG_WARNING,$e->getMessage());
}
return $step;
}
protected static function processQuotesTripMultimodalLegStepTransit($db, $member_id, $country, $step, $leg_id) {
syslog(LOG_WARNING,"ActivityApi::processQuotesTripMultimodalLegStepTransit(\$db, $member_id, $country, \$step, $leg_id)");
// Quote Transit
if ($country!='SG' && $country!='US') { // We support Singapore & US only for now!
return $step;
}
if ($country=='US') { // We support BART only
return ActivityApi::processQuotesTripMultimodalLegStepTransitUS($db, $member_id, $step, $leg_id);
}
return ActivityApi::processQuotesTripMultimodalLegStepTransitSG($db, $member_id, $step, $leg_id);
}
protected static function processQuotesTripMultimodalLegStepTransitUS($db, $member_id, $step, $leg_id) {
syslog(LOG_WARNING,"ActivityApi::processQuotesTripMultimodalLegStepTransitUS(\$db, $member_id, \$step, $leg_id)");
// TODO: !!!
return $step;
}
protected static function processQuotesTripMultimodalLegStepTransitSG($db, $member_id, $step, $leg_id) {
syslog(LOG_WARNING,"ActivityApi::processQuotesTripMultimodalLegStepTransitSG(\$db, $member_id, \$step, $leg_id)");
if (array_key_exists('fare_raw',$step) && $step['fare_raw']>0) {
return $step; // There is a quote already
}
$result = MultiModalApi::mytansportsgServiceLeg($leg_id);
if (is_array($result) && array_key_exists("code",$result) && $result["code"]==0 && array_key_exists("data",$result)) {
$data = $result["data"];
if (is_array($data) && array_key_exists($leg_id,$data) && is_array($data[$leg_id]) && array_key_exists('total',$data[$leg_id]) && $data[$leg_id]['total']>0) {
// Get from the DB!
list ($res, $err) = Quotes::getLegStepQuoteBySID($db->getConnect(), $step['sid']);
if (is_array($res) && array_key_exists('fare_raw',$res) && $res['fare_raw']>0) {
$step['fare_raw'] = $res['fare_raw'];
} else {
syslog(LOG_WARNING,'No step(sid='.$step['sid'].') quote found in the DB from mytransport.sg service');
}
} else {
syslog(LOG_WARNING,'Invalid response or no quote from mytransport.sg service');
syslog(LOG_WARNING,json_encode($data));
}
} else {
syslog(LOG_WARNING,'Failed to get the quote from mytransport.sg service');
}
return $step;
}
protected static function noGPSCoordinates($step) {
syslog(LOG_WARNING,"ActivityApi::noGPSCoordinates(\$step)");
if (!array_key_exists('location_start_lat',$step) || !array_key_exists('location_start_lng',$step)
|| !array_key_exists('location_end_lat',$step) || !array_key_exists('location_end_lng',$step)
|| ($step['location_start_lat']==NULL && $step['location_start_lng']==NULL)
|| ($step['location_end_lat']==NULL && $step['location_end_lng']==NULL)
|| ($step['location_start_lat']==0 && $step['location_start_lng']==0)
|| ($step['location_end_lat']==0 && $step['location_end_lng']==0)
) {
return true;
}
return false;
}
protected static function processQuotesTripScooter($db, $member_id, $country, $trip) {
syslog(LOG_WARNING,"ActivityApi::processQuotesTripScooter(\$db, $member_id, $country, \$trip)");
$scooter = $trip['scooter'];
return $scooter;
}
protected static function processQuotesTripRideshare($db, $member_id, $country, $trip) {
syslog(LOG_WARNING,"ActivityApi::processQuotesTripRideshare(\$db, $member_id, $country, \$trip)");
$rideshare = $trip['rideshare'];
return $rideshare;
}
public static function processRidesharesOption($db, $member_id, $country, $trips) {
syslog(LOG_WARNING,"ActivityApi::processRidesharesOption(\$db, $member_id, $country, \$trips)");
if ($country=='SG') {
return ActivityApi::processRideshareOption(
$db, $member_id, $country, $trips, 'Gojek', Quotes::VENDOR_GOJEK, "Asia/Singapore");
} else if ($country=='US') {
$tz1 = Address::getTzByAddressId($db->getConnect(),$trips[0]["trip"]["location_start_id"]);
if ($tz1=="") $tz1 = "America/Los_Angeles";
$trips_1 = ActivityApi::processRideshareOption(
$db, $member_id, $country, $trips, 'Uber', Quotes::VENDOR_UBER, $tz1);
$tz2 = Address::getTzByAddressId($db->getConnect(),$trips[0]["trip"]["location_end_id"]);
if ($tz2=="") $tz2 = "America/Los_Angeles";
$trips_2 = ActivityApi::processRideshareOption(
$db, $member_id, $country, $trips, 'Lyft', Quotes::VENDOR_LYFT, $tz2);
$trips_3 = ActivityApi::processRideshareOption(
$db, $member_id, $country, $trips, 'Autocab', Quotes::VENDOR_AUTOCAB, $tz1);
// Basic merge?
$n1 = count($trips_1) - 1;
$n2 = count($trips_2) - 1;
$n3 = count($trips_3) - 1;
$res = [];
if ($n1>=0 && $n2>=0 && $n3>=0) {
$n = rand(0,12);
//syslog(LOG_WARNING,"ActivityApi rand is ".$n);
if ($n<4) {
$res[] = $trips_1[$n1];
$res[] = $trips_2[$n2];
$res[] = $trips_3[$n3];
} else if ($n >=4 && $n < 8){
$res[] = $trips_2[$n2];
$res[] = $trips_3[$n3];
$res[] = $trips_1[$n1];
} else {
$res[] = $trips_3[$n3];
$res[] = $trips_1[$n1];
$res[] = $trips_2[$n2];
}
} else if ($n1>=0 && $n2>=0) {
$n = rand(0,10);
//syslog(LOG_WARNING,"ActivityApi rand is ".$n);
if ($n<5) {
$res[] = $trips_1[$n1];
$res[] = $trips_2[$n2];
} else {
$res[] = $trips_2[$n2];
$res[] = $trips_1[$n1];
}
} else if ($n1>=0 && $n3>=0) {
$n = rand(0,10);
//syslog(LOG_WARNING,"ActivityApi rand is ".$n);
if ($n<5) {
$res[] = $trips_1[$n1];
$res[] = $trips_3[$n3];
} else {
$res[] = $trips_3[$n3];
$res[] = $trips_1[$n1];
}
} else if ($n2>=0 && $n3>=0) {
$n = rand(0,10);
//syslog(LOG_WARNING,"ActivityApi rand is ".$n);
if ($n<5) {
$res[] = $trips_2[$n2];
$res[] = $trips_3[$n3];
} else {
$res[] = $trips_3[$n3];
$res[] = $trips_2[$n2];
}
} else if ($n1>=0) {
$res[] = $trips_1[$n1];
} else if ($n2>=0) {
$res[] = $trips_2[$n2];
} else if ($n3>=0) {
$res[] = $trips_3[$n3];
}
return $res;
}
return [];
}
public static function processGojekOption($db, $member_id, $country, $trips) {
syslog(LOG_WARNING,"ActivityApi::processGojekOption(\$db, $member_id, $country, \$trips)");
return ActivityApi::processRideshareOption(
$db, $member_id, $country, $trips, 'Gojek', Quotes::VENDOR_GOJEK, "Asia/Singapore");
}
public static function processRideshareOption(
$db, $member_id, $country, $trips, $rideshare, $transport_provider_id, $tz="Asia/Singapore") {
syslog(LOG_WARNING,"ActivityApi::processRideshareOption(\$db, $member_id, $country, \$trips, $rideshare, $transport_provider_id, $tz)");
//"trips"=>$trips;
if (!is_array($trips) && count($trips)<1) {
syslog(LOG_WARNING,"Invalid trips!");
return $trips; // No idea how to handle it
}
$trip = $trips[0];
if (!array_key_exists('trip',$trip) || !is_array($trip['trip']) || count($trip['trip'])<10) {
syslog(LOG_WARNING,"Invalid trip[0]!");
return $trips; // No idea how to handle it
}
$oldTrip = $trip['trip'];
$newTrip = [
'cheaper' => [],
'faster' => [],
'multimodal' => [],
'rideshare' => [],
'scooter' => [],
'trip' => $trip['trip']
];
$origin = Address::getAddressById($db->getConnect(), $oldTrip['location_start_id']);
$destination = Address::getAddressById($db->getConnect(), $oldTrip['location_end_id']);
$availableTransportProviders = Gis::getCityServicesAvailableForCoordinates($db->getConnect(), $origin["lat"], $origin["lng"],[$transport_provider_id]);
if (count($availableTransportProviders) == 0) {
$availableTransportProviders = Gis::getCountryServicesAvailableForCoordinates( $db->getConnect(), $origin["lat"], $origin["lng"],[$transport_provider_id]);
}
if (count($availableTransportProviders) == 0) {
return [];
}
list($res, $err) = GeocodeApi::route($db,
$origin['latitude'], $origin['longitude'],
$destination['latitude'], $destination['longitude'], 'driving'
);
if ($err!=NULL || !is_array($res) || !isset($res["routes"]) || count($res["routes"])<1) {
syslog(LOG_WARNING,'No available routes!');
syslog(LOG_WARNING,$err);
return $trips; // We cannot route driving!
}
$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'];
}
$route_overlay[] = $r;
}
$oldTrip["duration"] = $travel_time==PHP_INT_MAX ? NULL : $travel_time;
$oldTrip["distance"] = $travel_distance==PHP_INT_MAX ? NULL : $travel_distance;
/*
$result = [
'polyline' => array(),
'distance' => NULL,
'duration' => NULL,
'summary' => $route["summary"],
'overview_polyline' => $route["overview_polyline"],
'bounds' => $route["bounds"]
];
*/
//syslog(LOG_WARNING,json_encode($res));
$route = $res["routes"][0];
$route_leg = $route["legs"][0];
$route_leg_steps = $route_leg["steps"];
$overview_polyline = $route["overview_polyline"]["points"];
$leg_steps = [];
$distance = 0;
$duration = 0;
$location_start_lat = 0;
$location_start_lng = 0;
$location_end_lat = 0;
$location_end_lng = 0;
$polyline = [];
$i = 0; $j=1; $n = count($route_leg_steps);
for (; $i<$n; $i++) {
$step = $route_leg_steps[$i];
if ($step["travel_mode"] == "DRIVING") {
$location_start_lat = $step["start_location"]["lat"];
$location_start_lng = $step["start_location"]["lng"];
break;
}
$leg_steps[] = [
'distance' => $step["distance"]["value"],
'duration' => $step["duration"]["value"],
'html_instructions' => $step["html_instructions"],
'location_end_lat' => $step["end_location"]["lat"],
'location_end_lng' => $step["end_location"]["lng"],
'location_start_lat' => $step["start_location"]["lat"],
'location_start_lng' => $step["start_location"]["lng"],
'polyline' => $step["polyline"]["points"], /*"yz|Fm_nxRi@e@_@P",*/
'sid' => $j++,
'travel_mode' => $step["travel_mode"]
];
}
for (;$i<$n; $i++) {
$step = $route_leg_steps[$i];
if ($step["travel_mode"] != "DRIVING") {
break;
}
$distance += $step["distance"]["value"];
$duration += $step["duration"]["value"];
$location_end_lat = $step["end_location"]["lat"];
$location_end_lng = $step["end_location"]["lng"];
$polyline[] = $step["polyline"]["points"];
}
$leg_steps[] = [
'distance' => $distance,
'duration' => $duration,
'fare_quote' => null,
'fare_raw' => null,
'html_instructions' => "Take ".$rideshare." to ".$destination["address"],
'location_end_lat' => $location_end_lat,
'location_end_lng' => $location_end_lng,
'location_start_lat' => $location_start_lat,
'location_start_lng' => $location_start_lng,
'polyline' => MultiModal::processPolyline($polyline),
'quote_group_id' => null,
'sid' => $j++,
'travel_mode' => strtoupper($rideshare)
];
for (;$i<$n; $i++) {
$step = $route_leg_steps[$i];
$leg_steps[] = [
'distance' => $step["distance"]["value"],
'duration' => $step["duration"]["value"],
'html_instructions' => $step["html_instructions"],
'location_end_lat' => $step["end_location"]["lat"],
'location_end_lng' => $step["end_location"]["lng"],
'location_start_lat' => $step["start_location"]["lat"],
'location_start_lng' => $step["start_location"]["lng"],
'polyline' => $step["polyline"]["points"], /*"yz|Fm_nxRi@e@_@P",*/
'sid' => $j++,
'travel_mode' => $step["travel_mode"]
];
}
list($cost,$quote_id) = QuoteApi::getRideshareQuote(
$db, $origin["address"], $destination["address"], $member_id, $transport_provider_id, $country);
$cost = ((int)100*$cost);
$rideshare = [
'cost' => $cost,
'cost_raw' => $cost,
'count' => 1,
'country' => $country,
'distance' => $oldTrip['distance'],
'dup_id' => null,
'duration' => $oldTrip['duration'],
'id' => $oldTrip['id'],
'location_end' => $destination['address'],
'location_end_id' => $oldTrip['location_end_id'],
'location_end_lat' => $destination['latitude'],
'location_end_lng' => $destination['longitude'],
'location_end_tz' => $destination['timezone'],
'location_start' => $origin['address'],
'location_start_id' => $oldTrip['location_start_id'],
'location_start_lat' => $origin['latitude'],
'location_start_lng' => $origin['longitude'],
'location_start_tz' => $origin['timezone'],
'member_id' => $member_id,
'options' => [],
'parsedemail_item_id' => null, /* $trip['id'] ??? */
'private' => 't',
'scheduled' => null,
'trackedemail_item_id' => null,
'transport_provider_id' => $transport_provider_id,
'travel_date' => $oldTrip['travel_date'],
'travel_date_end' => $oldTrip['travel_date_end'],
'updated' => $oldTrip['updated']
];
$leg_id = rand(1,10000); // virtual temporary...
$leg_fare = $cost;
// Get departure and arrival times...
$date = new DateTime("now", new DateTimeZone($tz));
$date->add(new DateInterval('PT15M'));
$departure_time = $date->getTimestamp();
$duration = $oldTrip['duration']>0 ? $oldTrip['duration'] : 15;
$date->add(new DateInterval('PT'.$duration.'M'));
$arrival_time = $date->getTimestamp();
$leg = [
'id' => $leg_id,
'arrival_time' => $arrival_time,
'arrival_time_zone' => $destination['timezone'],
'arrival_timezone' => $tz,
'departure_time' => $departure_time,
'departure_time_zone' => $origin['timezone'],
'departure_timezone' => $tz,
'distance' => $oldTrip['distance'],
'duration' => $oldTrip['duration'],
'fare_raw' => $cost,
'polyline' => $overview_polyline,
'quote' => $cost,
'steps' => count($leg_steps),
];
$options = [
'gid' => $leg_id, /* ??? */
'leg_fare' => [$leg_id => $leg_fare],
'leg_steps' => [$leg_id => $leg_steps],
'legs' => [$leg],
'routes' => 1
];
$rideshare['options'] = $options;
$newTrip['rideshare'] = $rideshare;
if (count($trips)>2) {
array_shift($trips); // Remove the first element
}
$trips[] = $newTrip;
syslog(LOG_WARNING,"ActivityApi: ".json_encode($trips));
return $trips;
}
}