You are here

geolocation.module in Geolocation Field 7

A geolocation field using the Field API.

File

geolocation.module
View source
<?php

/**
 * @file
 * A geolocation field using the Field API.
 */

/**
 * Implements hook_field_info().
 */
function geolocation_field_info() {
  return array(
    'geolocation_latlng' => array(
      'label' => t('Geolocation'),
      'description' => t('Geolocation input.'),
      'default_widget' => 'geolocation_latlng',
      'default_formatter' => 'geolocation_text',
      'property_type' => 'geolocation',
      'property_callbacks' => array(
        'geolocation_property_info_callback',
      ),
    ),
  );
}

/**
 * Callback to alter the property info of geolocation fields.
 *
 * @see geolocation_field_info()
 */
function geolocation_property_info_callback(&$info, $entity_type, $field, $instance, $field_type) {
  $name = $field['field_name'];
  $property =& $info[$entity_type]['bundles'][$instance['bundle']]['properties'][$name];
  $property['type'] = $field['cardinality'] != 1 ? 'list<geolocation>' : 'geolocation';
  $property['getter callback'] = 'entity_metadata_field_verbatim_get';
  $property['setter callback'] = 'entity_metadata_field_verbatim_set';
  $property['property info'] = geolocation_data_property_info();
  unset($property['query callback']);
}

/**
 * Defines info for the properties of the geolocation field data structure.
 */
function geolocation_data_property_info() {

  // Build an array of basic property information for the geolocation field.
  $properties = array(
    'lat' => array(
      'label' => 'Latitude',
      'type' => 'decimal',
      'required' => TRUE,
      'description' => '',
      'getter callback' => 'entity_property_verbatim_get',
    ),
    'lng' => array(
      'label' => 'Longitude',
      'type' => 'decimal',
      'description' => '',
      'required' => TRUE,
      'getter callback' => 'entity_property_verbatim_get',
    ),
    'lat_sin' => array(
      'label' => 'the sine of latitude',
      'description' => '',
      'type' => 'decimal',
      'getter callback' => 'entity_property_verbatim_get',
    ),
    'lat_cos' => array(
      'label' => 'the cosine of latitude',
      'description' => '',
      'type' => 'decimal',
      'getter callback' => 'entity_property_verbatim_get',
    ),
    'lng_rad' => array(
      'label' => 'the radian longitude',
      'description' => '',
      'type' => 'decimal',
      'getter callback' => 'entity_property_verbatim_get',
    ),
    'latlng' => array(
      'label' => 'the latlng of point',
      'description' => '',
      'type' => 'string',
      'getter callback' => 'geolocation_return_latlon',
    ),
  );
  return $properties;
}

/**
 * Gets the a latlong property.
 */
function geolocation_return_latlon($data, array $options, $name) {
  if (is_array($data) || is_object($data) && $data instanceof ArrayAccess) {
    return $data['lat'] . ',' . $data['lng'];
  }
  return NULL;
}

/**
 * Implements hook_field_validate().
 */
function geolocation_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
  foreach ($items as $delta => $item) {
    if (!geolocation_field_is_empty($item, $field)) {
      switch (TRUE) {
        case !is_numeric($item['lat']):
          $errors[$field['field_name']][$langcode][$delta][] = array(
            'error' => 'geolocation_invalid_lat',
            'message' => t('Invalid Latitude. Value must be numeric.'),
          );
          break;
        case $item['lat'] > 90:
        case $item['lat'] < -90:
          $errors[$field['field_name']][$langcode][$delta][] = array(
            'error' => 'geolocation_invalid_lat',
            'message' => t('Invalid Latitude. Value must be between 90 and -90.'),
          );
          break;
      }
      switch (TRUE) {
        case !is_numeric($item['lng']):
          $errors[$field['field_name']][$langcode][$delta][] = array(
            'error' => 'geolocation_invalid_lng',
            'message' => t('Invalid Longitude. Value must be numeric.'),
          );
        case $item['lng'] > 180:
        case $item['lng'] < -180:
          $errors[$field['field_name']][$langcode][$delta][] = array(
            'error' => 'geolocation_invalid_lng',
            'message' => t('Invalid Longitude. Value must be between 180 and -180'),
          );
          break;
      }
    }
  }
}

/**
 * Implements hook_field_presave().
 */
function geolocation_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
  foreach ($items as $delta => $item) {

    // Precalculate some goodness.
    $items[$delta] = _geolocation_field_precalculate_values($item);
  }
}

/**
 * Implements hook_field_is_empty().
 */
function geolocation_field_is_empty($item, $field) {
  if (empty($item['lat']) && (string) $item['lat'] !== '0' && empty($item['lng']) && (string) $item['lng'] !== '0') {
    return TRUE;
  }
}

/**
 * Implements hook_field_formatter_info().
 */
function geolocation_field_formatter_info() {
  return array(
    'geolocation_text' => array(
      'label' => t('Simple text-based formatter'),
      'field types' => array(
        'geolocation_latlng',
      ),
    ),
    'geolocation_latlng' => array(
      'label' => t('Simple microdata formatter'),
      'field types' => array(
        'geolocation_latlng',
      ),
    ),
    'geolocation_latitude' => array(
      'label' => t('Latitude text-based formatter'),
      'field types' => array(
        'geolocation_latlng',
      ),
    ),
    'geolocation_longitude' => array(
      'label' => t('Longitude text-based formatter'),
      'field types' => array(
        'geolocation_latlng',
      ),
    ),
  );
}

/**
 * Implements hook_field_formatter_view().
 */
function geolocation_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  $element = array();
  switch ($display['type']) {
    case 'geolocation_text':
      foreach ($items as $delta => $item) {
        $element[$delta]['#markup'] = '<p>' . t('Geolocation is @lat, @lng', array(
          '@lat' => $item['lat'],
          '@lng' => $item['lng'],
        )) . '</p>';
      }
      break;
    case 'geolocation_latlng':
      foreach ($items as $delta => $item) {
        $element[$delta] = array(
          '#theme' => 'geolocation_latlng',
          '#lat' => $item['lat'],
          '#lng' => $item['lng'],
        );
      }
      break;
    case 'geolocation_latitude':
      foreach ($items as $delta => $item) {
        $element[$delta]['#markup'] = $item['lat'];
      }
      break;
    case 'geolocation_longitude':
      foreach ($items as $delta => $item) {
        $element[$delta]['#markup'] = $item['lng'];
      }
      break;
  }
  return $element;
}

/**
 * Implements hook_theme().
 */
function geolocation_theme() {
  return array(
    'geolocation_latlng' => array(
      'template' => 'geolocation_latlng',
      'variables' => array(
        'lat' => NULL,
        'lng' => NULL,
      ),
    ),
  );
}

/**
 * Implements hook_field_widget_info().
 */
function geolocation_field_widget_info() {
  return array(
    'geolocation_text' => array(
      'label' => t('Latitude/Longitude'),
      'field types' => array(
        'geolocation_latlng',
      ),
    ),
  );
}

/**
 * Implements hook_field_widget_form().
 */
function geolocation_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  $lat_value = isset($items[$delta]['lat']) ? $items[$delta]['lat'] : '';
  $lng_value = isset($items[$delta]['lng']) ? $items[$delta]['lng'] : '';
  $element += array(
    '#delta' => $delta,
  );

  // Wrap in a fieldset for single fields.
  if ($field['cardinality'] == 1) {
    $element['#type'] = 'fieldset';
  }
  $element['lat'] = array();
  $element['lng'] = array();
  switch ($instance['widget']['type']) {
    case 'geolocation_text':
      $element['lat'] += array(
        '#title' => t('Latitude'),
        '#type' => 'textfield',
        '#default_value' => $lat_value,
        '#size' => 30,
        '#maxlength' => 30,
        '#required' => $instance['required'],
      );
      $element['lng'] += array(
        '#title' => t('Longitude'),
        '#type' => 'textfield',
        '#default_value' => $lng_value,
        '#size' => 30,
        '#maxlength' => 30,
        '#required' => $instance['required'],
      );
      $element['#attached']['css'][] = drupal_get_path('module', 'geolocation') . '/geolocation.css';
      break;
  }
  return $element;
}

/**
 * Implements hook_field_widget_error().
 */
function geolocation_field_widget_error($element, $error, $form, &$form_state) {
  switch ($error['error']) {
    case 'geolocation_invalid_lat':
      form_error($element['lat'], $error['message']);
      break;
    case 'geolocation_invalid_lng':
      form_error($element['lng'], $error['message']);
      break;
  }
}

/**
 * Helper function. Precalculates values for geolocation field item.
 *
 * @param array $item
 *   Geolocation field item with the following keys:
 *     - lat: (required) Latitude
 *     - lng: (required) Longitude
 *
 * @return array
 *   Geolocation field item with added precalculated values:
 *     - lat_sin: Latitude sine
 *     - lat_cos: Latitude cosine
 *     - lng_rad: Radian longitude
 */
function _geolocation_field_precalculate_values(array $item) {
  $item['lat_sin'] = sin(deg2rad($item['lat']));
  $item['lat_cos'] = cos(deg2rad($item['lat']));
  $item['lng_rad'] = deg2rad($item['lng']);
  return $item;
}

Functions

Namesort descending Description
geolocation_data_property_info Defines info for the properties of the geolocation field data structure.
geolocation_field_formatter_info Implements hook_field_formatter_info().
geolocation_field_formatter_view Implements hook_field_formatter_view().
geolocation_field_info Implements hook_field_info().
geolocation_field_is_empty Implements hook_field_is_empty().
geolocation_field_presave Implements hook_field_presave().
geolocation_field_validate Implements hook_field_validate().
geolocation_field_widget_error Implements hook_field_widget_error().
geolocation_field_widget_form Implements hook_field_widget_form().
geolocation_field_widget_info Implements hook_field_widget_info().
geolocation_property_info_callback Callback to alter the property info of geolocation fields.
geolocation_return_latlon Gets the a latlong property.
geolocation_theme Implements hook_theme().
_geolocation_field_precalculate_values Helper function. Precalculates values for geolocation field item.