Monday, December 31, 2018

Implementing A Very Simple Weather Service - Part 1

I've got this travel hack in mind that depends on a really simple weather API. Specifically, I need a web service that will take in a date, forecast attribute (say, high temperature for the day) and a location and return back the single value that was asked for. From what I can tell, such a simple weather API doesn't exist. But it also shouldn't be hard to build one.

My plan is simple: use an existing weather API (like DarkSky's) and write a wrapper around it that makes it behave like the simple service I have in mind.

The first step in implementing my super simple weather service has nothing to do with the weather; it has to do with the location aspect of the code. Specifically, I need to turn human friendly addresses into a latitude, longitude pairs. This is known as Geocoding and Google Offers an API to do just this.

Here's a snippet of the code I've written that does geocoding:

function geocode($location, $options = []) {
  $info = curl_get('', [
    'address' => $location,
    'key'     => GEOCODING_API_KEY,
  ], ['ttl' => 60 * 60 * 25 * 365 * 10]);
  if(g($options, 'debug')) {
  if(is_array($info)) {
    return g($info, ['results', 0, 'geometry', 'location']);
  } else {
    return false;

And here's the curl_get function that powers the above code:

function curl_get($url, $params, $options = []) {
  $ttl = g($options, 'ttl', 0);
  $cache_file = CURL_CACHE_DIR . "/" . md5($url . serialize($params)) . '.json';
  if(file_exists($cache_file) && ((time() - filemtime($cache_file)) < $ttl)) {
    $body = file_get_contents($cache_file);
    return json_decode($body, true);
  $fd = fopen($cache_file, "w");
  $ch = curl_init($url . ($params ? ('?' . http_build_query($params, false, '&')) : ''));
  curl_setopt($ch, CURLOPT_TIMEOUT, g($options, 'timeout', 4));
  curl_setopt($ch, CURLOPT_FILE, $fd);
  $info = curl_getinfo($ch);
  if($info['http_code'] == 200) {
    $body = file_get_contents($cache_file);
    return json_decode($body, true);
  } else {
    return $ch;

There's not much to it, but I did implement support for caching. The idea is that once I've geocoded a location I shouldn't need to geocode that location ever again. Note that I setup curl_get's caching to support a time-to-live (TTL) parameter. This should be useful for when I re-purpose curl_get to implement the call to the underlying weather API. For these calls, I expect I'll set the TTL to say 6 hours, to allow the system to get refreshed forecast values yet still make aggressive use of caching.

You can view the source code for this project here. If you're inclined, you can mess with an instance of the code here, though there really isn't much to see yet.

Stay tuned, as I've got plans for finishing up this API soon.

No comments:

Post a Comment