You are here

search_api_location.module in Search API Location 7.2

Same filename and directory in other branches
  1. 8 search_api_location.module
  2. 7 search_api_location.module

Provides location based search functionality for the Search API.

File

search_api_location.module
View source
<?php

/**
 * @file
 * Provides location based search functionality for the Search API.
 */

/**
 * Implements hook_search_api_data_type_info().
 */
function search_api_location_search_api_data_type_info() {
  return array(
    'location' => array(
      'name' => t('Latitude/longitude'),
      'fallback' => 'string',
    ),
  );
}

/**
 * Returns the location fields of an index (if it has any).
 *
 * @param SearchApiIndex $index
 *   The index for which the location fields should be found.
 * @param $reset
 *   Whether to reset the static cache for the function. Defaults to FALSE.
 *
 * @return array
 *   An array of fields with type "location", keyed by field name and containing
 *   the same options as returned by
 */
function search_api_location_get_location_fields(SearchApiIndex $index, $reset = FALSE) {
  $fields =& drupal_static(__FUNCTION__, array());
  if ($reset || empty($fields[$index->machine_name])) {
    $fields[$index->machine_name] = array();
    try {
      if (($server = $index
        ->server()) && $server
        ->supportsFeature('search_api_data_type_location')) {
        foreach ($index
          ->getFields() as $key => $field) {
          if (!empty($field['real_type']) && search_api_extract_inner_type($field['real_type']) == 'location') {
            $fields[$index->machine_name][$key] = $field;
          }
        }
      }
    } catch (SearchApiException $e) {

      // Ignore missing servers.
    }
  }
  return $fields[$index->machine_name];
}

/**
 * Returns an array of options for used distance units.
 */
function search_api_location_get_units() {
  $units =& drupal_static(__FUNCTION__);
  if (!isset($units)) {
    $units = array(
      '1' => t('Kilometers'),
      '1.60935' => t('Miles'),
    );
    drupal_alter('search_api_location_units', $units);
  }
  return $units;
}

/**
 * Helper function for validating a distance input.
 */
function search_api_location_validate_distance(array $element, array &$form_state) {
  $value = $element['#value'];
  if ($value != '' && !is_numeric($value) || (double) $value < 0) {
    form_error($element, t('%name must be a non-negative number.', array(
      '%name' => $element['#title'],
    )));
  }
}

/**
 * Implements hook_ctools_plugin_api().
 */
function search_api_location_ctools_plugin_api() {
  return array(
    'version' => 1,
  );
}

/**
 * Implements hook_ctools_plugin_dierctory().
 */
function search_api_location_ctools_plugin_directory($module, $plugin) {
  if ($module == 'search_api_location') {
    return 'plugins/' . $plugin;
  }
}

/**
 * Implements hook_ctools_plugin_type().
 */
function search_api_location_ctools_plugin_type() {
  return array(
    'location_input' => array(
      'use hooks' => TRUE,
    ),
  );
}

/**
 * Returns a list of location input plugins.
 *
 * @param $plugin
 *   (optional) The identifier of a specific plugin to return.
 *
 * @return array
 *   If $plugin is given, either the specified plugin or NULL. Otherwise a list
 *   of all available plugins, keyed by identifier.
 */
function search_api_location_get_input_plugins($plugin = NULL) {
  $plugins =& drupal_static(__FUNCTION__);
  if ($plugin) {
    if (isset($plugins)) {
      return isset($plugins[$plugin]) ? $plugins[$plugin] : NULL;
    }
    ctools_include('plugins');
    $plugin = ctools_get_plugins('search_api_location', 'location_input', $plugin);
    if (empty($plugin['title']) || empty($plugin['input callback']) || !function_exists($plugin['input callback'])) {

      // Don't return incomplete plugins.
      return NULL;
    }
    if (!empty($plugin['form callback']) && !function_exists($plugin['form callback'])) {

      // Unset form callback if it doesn't exist.
      unset($plugin['form callback']);
    }
    return $plugin;
  }
  if (!isset($plugins)) {
    ctools_include('plugins');
    $plugins = array();
    foreach (ctools_get_plugins('search_api_location', 'location_input') as $id => $plugin) {
      $plugin += array(
        'description' => NULL,
      );
      if (empty($plugin['title']) || empty($plugin['input callback']) || !function_exists($plugin['input callback'])) {

        // Filter out incomplete plugins.
        continue;
      }
      if (!empty($plugin['form callback']) && !function_exists($plugin['form callback'])) {

        // Unset form callback if it doesn't exist.
        unset($plugin['form callback']);
      }
      $plugins[$id] = $plugin;
    }
  }
  return $plugins;
}

/**
 * Returns all available location input plugins as an option list.
 *
 * @return array
 *   All available location_input plugins, with their identifiers mapped to
 *   their human-readable names.
 */
function search_api_location_get_input_plugin_options() {
  $options =& drupal_static(__FUNCTION__);
  if (!isset($options)) {
    $options = array();
    foreach (search_api_location_get_input_plugins() as $id => $plugin) {
      $options[$id] = check_plain($plugin['title']);
    }
  }
  return $options;
}

/**
 * Implements hook_field_info_alter().
 *
 * Adds support for Location fields.
 */
function search_api_location_field_info_alter(&$info) {
  if (isset($info['location'])) {
    $info['location']['property_callbacks'][] = 'search_api_location_location_properties';
  }
}

/**
 * Adds additional properties to "Location" fields.
 *
 * @param array $info
 *   The entire entity property info.
 * @param string $entity_type
 *   The entity type to which the field is attached.
 * @param array $field
 *   The field info array.
 * @param array $instance
 *   The field instance info array.
 */
function search_api_location_location_properties(&$info, $entity_type, $field, $instance) {
  $property =& $info[$entity_type]['bundles'][$instance['bundle']]['properties'][$field['field_name']];
  $property['property info']['latlon'] = array(
    'type' => 'text',
    'label' => t('Latitude/longitude pair'),
    'getter callback' => 'search_api_location_location_latlon_get',
  );
}

/**
 * Retrieves the "latlon" property value for a location field.
 *
 * @param array $data
 *   The available data for the field.
 *
 * @return string|null
 *   The "latlon" property value, as a latitude/longitude pair in format
 *   "LAT,LON", if both values are set. NULL otherwise.
 */
function search_api_location_location_latlon_get($data) {
  if (isset($data['latitude']) && isset($data['longitude'])) {
    return $data['latitude'] . ',' . $data['longitude'];
  }
  return NULL;
}

/**
 * Helper function for finding a key anywhere in an array.
 *
 * @param array $array
 *   The array to search through.
 * @param $key
 *   The key to find.
 *
 * @return
 *   The value mapped to the array key, or NULL if the key couldn't be found.
 */
function _search_api_location_array_get_nested_value(array $array, $key) {
  foreach (element_children($array) as $k) {
    $v = $array[$k];
    if ($key == $k) {
      return $v;
    }
    if (is_array($v)) {
      $nested[] = $v;
    }
  }
  if (!empty($nested)) {
    foreach ($nested as $array) {
      $value = _search_api_location_array_get_nested_value($array, $key);
      if ($value) {
        return $value;
      }
    }
  }
  return NULL;
}

/**
 * Calculates the distance between two lat/long values.
 *
 * @link http://rosettacode.org/wiki/Haversine_formula
 *
 * @param array $start
 *   Array with the keys "lat" and "lon".
 * @param array $destination
 *   Array with the keys "lat" and "lon".
 *
 * @return float
 *   Distance in kilometres.
 */
function search_api_location_calculate_distance($start, $destination) {
  $start = array_map('deg2rad', $start);
  $destination = array_map('deg2rad', $destination);

  // Earth's radius in kilometers.
  $radius_of_earth = 6371;
  $diff_latitude = $start['lat'] - $destination['lat'];
  $diff_longitude = $start['lon'] - $destination['lon'];
  $a = sin($diff_latitude / 2) * sin($diff_latitude / 2) + cos($destination['lat']) * cos($start['lat']) * sin($diff_longitude / 2) * sin($diff_longitude / 2);
  $c = 2 * asin(sqrt($a));
  return $radius_of_earth * $c;
}

Functions

Namesort descending Description
search_api_location_calculate_distance Calculates the distance between two lat/long values.
search_api_location_ctools_plugin_api Implements hook_ctools_plugin_api().
search_api_location_ctools_plugin_directory Implements hook_ctools_plugin_dierctory().
search_api_location_ctools_plugin_type Implements hook_ctools_plugin_type().
search_api_location_field_info_alter Implements hook_field_info_alter().
search_api_location_get_input_plugins Returns a list of location input plugins.
search_api_location_get_input_plugin_options Returns all available location input plugins as an option list.
search_api_location_get_location_fields Returns the location fields of an index (if it has any).
search_api_location_get_units Returns an array of options for used distance units.
search_api_location_location_latlon_get Retrieves the "latlon" property value for a location field.
search_api_location_location_properties Adds additional properties to "Location" fields.
search_api_location_search_api_data_type_info Implements hook_search_api_data_type_info().
search_api_location_validate_distance Helper function for validating a distance input.
_search_api_location_array_get_nested_value Helper function for finding a key anywhere in an array.