You are here

function geocoder_google in Geocoder 7

Process Markup.

1 call to geocoder_google()
geocoder_google_field in plugins/geocoder_handler/google.inc
Plugin callback.
1 string reference to 'geocoder_google'
google.inc in plugins/geocoder_handler/google.inc

File

plugins/geocoder_handler/google.inc, line 30

Code

function geocoder_google($address, $options = array()) {
  $delay_trigger =& drupal_static(__FUNCTION__);
  $delay = variable_get('geocoder_google_delay', 0);
  if ($delay > 0 && $delay_trigger) {
    usleep($delay * 1000);
  }
  geophp_load();
  $query = array(
    'address' => $address,
  );
  if (isset($options['extra_query']) && is_array($options['extra_query'])) {
    $query += $options['extra_query'];
  }

  // Add any given biasing.
  if (!empty($options['biasing']['bounds'])) {
    $query['bounds'] = $options['biasing']['bounds'];
  }
  if (!empty($options['biasing']['region'])) {
    $query['region'] = $options['biasing']['region'];
  }
  if (!empty($options['biasing']['components'])) {
    $query['components'] = $options['biasing']['components'];
  }

  // Error message in case no key/client ID has been provided.
  $msg = t('The current HTTP request to google API does not have a key or client ID to identify your application.');

  // Add Google API authentication parameters.
  switch (variable_get('geocoder_google_auth_method')) {

    // Google Geocoding API Key.
    case GEOCODER_GOOGLE_AUTH_KEY:
      $geocoder_settings = variable_get("geocoder_settings", array());
      if (!empty($geocoder_settings['geocoder_apikey_google'])) {
        $query['key'] = $geocoder_settings['geocoder_apikey_google'];
      }
      else {
        throw new Exception($msg, WATCHDOG_ERROR);
      }
      break;

    // Google Maps for Work signature.
    case GEOCODER_GOOGLE_AUTH_WORK:
      $client_id = variable_get('geocoder_google_client_id', FALSE);
      $private_key = variable_get('geocoder_google_private_key', FALSE);
      if (!empty($client_id) && !empty($private_key)) {
        $google_maps_for_work = TRUE;
        $query['client'] = $client_id;
      }
      else {
        throw new Exception($msg, WATCHDOG_ERROR);
      }
      break;
    default:
      throw new Exception($msg, WATCHDOG_ERROR);
  }
  $google_url = 'https://maps.googleapis.com/maps/api/geocode/json';

  // Build the URL.
  $url = url($google_url, array(
    'query' => $query,
  ));

  // If authenticating via Google Maps for Work, add signature.
  // @see https://developers.google.com/maps/documentation/business/webservices/auth#generating_valid_signatures
  if (!empty($google_maps_for_work)) {

    // Strip off the protocol and host for signing.
    $parsed = parse_url($url);
    $url_to_sign = $parsed['path'] . '?' . $parsed['query'];

    // Decode the key in a URL-safe way (RFC 4648 Section 5).
    // @see https://www.ietf.org/rfc/rfc4648.txt
    $decoded_key = base64_decode(strtr($private_key, '-_', '+/'), TRUE);

    // Use HMAC SHA1 to sign the URL with the decoded key.
    $signature = hash_hmac('sha1', $url_to_sign, $decoded_key, TRUE);

    // Append the signature to the URL's query parameters. Do this manually to
    // avoid URL encoding.
    $url .= '&signature=' . strtr(base64_encode($signature), '+/', '-_');
  }

  // Send the request.
  $result = drupal_http_request($url);
  $delay_trigger = TRUE;
  if (isset($result->error)) {
    $args = array(
      '@code' => $result->code,
      '@error' => $result->error,
    );
    $msg = t("HTTP request to google API failed.\nCode: @code\nError: @error", $args);
    throw new Exception($msg);
  }
  $data = json_decode($result->data);
  if ($data->status === 'ZERO_RESULTS') {
    return FALSE;
  }
  elseif ($data->status !== 'OK') {
    $args = array(
      '@status' => $data->status,
      '@message' => !empty($data->error_message) ? $data->error_message : t('No specific message'),
    );
    $msg = t("Google API returned bad status.\nStatus: @status\nMessage: @message", $args);
    throw new Exception($msg);
  }
  $geometries = array();
  foreach ($data->results as $item) {

    // Check if we should reject these results.
    if (isset($options['reject_results'])) {
      if (in_array($item->geometry->location_type, $options['reject_results'], TRUE)) {
        continue;
      }
    }

    // Construct a geoPHP Geometry depending on what type of geometry we want
    // returned (defaults to point).
    if (!isset($options['geometry_type']) || $options['geometry_type'] === 'point') {
      $geom = new Point($item->geometry->location->lng, $item->geometry->location->lat);
    }
    elseif ($options['geometry_type'] === 'bounds') {
      if (isset($item->geometry->bounds)) {
        $points = array(
          new Point($item->geometry->bounds->southwest->lng, $item->geometry->bounds->southwest->lat),
          new Point($item->geometry->bounds->southwest->lng, $item->geometry->bounds->northeast->lat),
          new Point($item->geometry->bounds->northeast->lng, $item->geometry->bounds->northeast->lat),
          new Point($item->geometry->bounds->northeast->lng, $item->geometry->bounds->southwest->lat),
          new Point($item->geometry->bounds->southwest->lng, $item->geometry->bounds->southwest->lat),
        );
        $geom = new Polygon(array(
          new LineString($points),
        ));
      }
    }
    elseif ($options['geometry_type'] === 'viewport') {
      $points = array(
        new Point($item->geometry->viewport->southwest->lng, $item->geometry->viewport->southwest->lat),
        new Point($item->geometry->viewport->southwest->lng, $item->geometry->viewport->northeast->lat),
        new Point($item->geometry->viewport->northeast->lng, $item->geometry->viewport->northeast->lat),
        new Point($item->geometry->viewport->northeast->lng, $item->geometry->viewport->southwest->lat),
        new Point($item->geometry->viewport->southwest->lng, $item->geometry->viewport->southwest->lat),
      );
      $geom = new Polygon(array(
        new LineString($points),
      ));
    }

    // Add additional metadata to the geometry - it might be useful!
    $geom->data = array();
    $geom->data['geocoder_accuracy'] = $item->geometry->location_type;
    $geom->data['geocoder_formatted_address'] = $item->formatted_address;
    $geom->data['geocoder_address_components'] = $item->address_components;
    $geometries[] = $geom;
  }
  if (empty($geometries)) {
    return;
  }

  // Check if we should return all results as a compound geometry.
  if (isset($options['all_results'])) {
    if ($options['all_results']) {
      return geoPHP::geometryReduce($geometries);
    }
  }

  // The canonical geometry is the first result (best guess)
  $geometry = array_shift($geometries);

  // If there are any other geometries, these are auxiliary geometries that
  // represent "alternatives".
  if (count($geometries)) {
    $geometry->data['geocoder_alternatives'] = $geometries;
  }
  return $geometry;
}