search_api_location.module in Search API Location 7.2
Same filename and directory in other branches
Provides location based search functionality for the Search API.
File
search_api_location.moduleView 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;
}