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() { $message = "Failed to run scooter directions"; $addresses = $this->requestParams["addresses"] ?? array(); $options = $this->requestParams["options"] ?? array(); try { throw new Exception("We are sorry but scooters are not yet launched in your country.  Please check back again soon!"); if ($addresses==NULL || !is_array($addresses) || count($addresses)<1 || !isset($addresses[0]["address"])) { throw new Exception("Invalid input"); } $db = new Db(); $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) = self::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 = self::options($db, $input, $result, $options); } return $this->response( array( 'data'=>$result, 'options'=>$options), 200); } catch (Exception $e) { error_log(json_encode($e)); $message = $e->getMessage(); } return $this->response( array( "error" => $message ), 500); } public function updateAction() { return $this->response( array( "error" => "Update error" ), 400); } public function deleteAction() { return $this->response( array( "error" => "Delete error" ), 500); } public function scooter($db, $input) { global $savvyext; $httpAuthToken = $savvyext->cfgReadChar('system.oauth2_token'); $oauth2_url = $savvyext->cfgReadChar('system.oauth2_url'); // Call scooter service $gps1 = $input[1]["coordinates"]; $gps2 = $input[2]["coordinates"]; $data = http_build_query( array( 'gps' => sprintf("%s,%s,%s,%s",$gps1["lat"],$gps1["lng"],$gps2["lat"],$gps2["lng"]), 'from' => $input[1]["address"], 'to' => $input[2]["address"] ) ); $url = $oauth2_url."scooter?" . $data; //error_log($url); $opts = array( 'http' => array( 'method' => "GET", '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); $scooter = json_decode($body,true); if (is_array($scooter) && is_array($scooter["data"]) && !isset($scooter["error"])) { // Cache the result in DB //$scooter["data"]["id"] = Scooter::save($db->getConnect(), $scooter["data"]); return array($scooter["data"], NULL); } else if (is_array($scooter) && isset($scooter["error"])) { $body = $scooter["error"]; } return array(NULL, "Scooter service call error: ".$body); } public function options($db, $input, $result, $options) { if (isset($options["travel_time"]) && $options["travel_time"]) { $travel_time = 0; foreach ($result as $step) { $travel_time += $step["duration"]; } $options["travel_time"] = (int)$travel_time; } if (isset($options["quote"]) && $options["quote"] && is_array($result) && count($result)>1 && isset($result[1]["duration"]) && $result[1]["duration"]>0) { $scooter_time = $result[1]["duration"]; // 2nd entry is the scooter step list($res,$err) = self::quote($db, $scooter_time, 'SG'); if ($err!=NULL) { $options["error"] = $err; } else { $options["quote"] = $res; } } if (isset($options["route_overlay"]) && $options["route_overlay"]) { $options = self::directions($db, $input, $result, $options); } return $options; } public function quote($db, $seconds, $country='SG') { global $scooterAuthToken, $savvyext; // Check local DB cache /* list ($res, $err) = Scooter::checkScooterQuote($db->getConnect(), $minutes, $country); if (is_array($res) && isset($res[0]["quote"])) { return array($res, NULL); } */ // Call scooter quote service $data = http_build_query( array( 'minutes' => (int)($seconds/60), 'country' => $country ) ); $url = $savvyext->cfgReadChar('microservices.catalog') . "/api/v1/price-quote/?" . $data; $opts = array( 'http' => array( 'method' => "GET", 'header' => "Content-Type: application/x-www-form-urlencoded\r\n" . "Accept: application/json\r\n" . "Authorization: Server-Token ${scooterAuthToken}\r\n", ), "ssl" => array( "verify_peer"=>false, "verify_peer_name"=>false, ) ); $context = stream_context_create($opts); $body = file_get_contents($url, false, $context); $scooter = json_decode($body,true); if (is_array($scooter) && count($scooter)>0 && isset($scooter[0]["quote"])) { // Cache the result in DB //$scooter["data"]["id"] = Scooter::saveQuote($db->getConnect(), $scooter); return array($scooter, NULL); } else if (is_array($scooter) && isset($scooter["error"])) { $body = $scooter["error"]; } return array(NULL, "Scooter quote service call error: ".$body); } public function directions($db, $input, $result, $options) { // Walking with waypoints or 3 routes $mode='walking'; $waypoints=[ $result[1]["data"]["location_start_lat"], $result[1]["data"]["location_start_lng"], $result[1]["data"]["location_end_lat"], $result[1]["data"]["location_end_lng"] ]; $fromLat = $input[1]["coordinates"]["lat"]; $fromLng = $input[1]["coordinates"]["lng"]; $toLat = $input[2]["coordinates"]["lat"]; $toLng = $input[2]["coordinates"]["lng"]; list($res, $err) = GeocodeApi::route( $db, $fromLat, $fromLng, $toLat, $toLng, $mode, $waypoints); 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; } // ["route_overlay"] return $options; } }