gmap_geo.module

<?php

/**
 * @file
 * - Provides a Google maps location picker widget for Geo cck fields.
 * - Provides a field formatter for Geo fields that displays point, line, or
 *   polygon data on a Google map.
 */

/**
 * Implementation of FAPI hook_elements().
 */
function gmap_geo_elements() {
  return array(
    'gmap_geo_picker' => array(
      '#input' => TRUE,
      '#columns' => array('lat', 'lon', 'wkt'),
      '#delta' => 0,
      '#process' => array('gmap_geo_picker_process'),
    ),
  );
}

/**
 * Implementation of hook_widget_info().
 */
function gmap_geo_widget_info() {
  return array(
    'gmap_geo_picker' => array(
      'label' => t('GMap picker'),
      'field types' => array('geo'),
    ),
  );
}

/**
 * Implementation of hook_widget().
 */
function gmap_geo_widget(&$form, &$form_state, $field, $items, $delta = 0) {
  return array(
    '#type' => $field['widget']['type'],
    '#default_value' => isset($items[$delta]) ? $items[$delta] : '',
  );
}

/**
 * Implementation of hook_widget_settings().
 * Adds a field to set a GMap macro for the location picker map.
 */
function gmap_geo_widget_settings($op, $widget) {
  switch ($op) {
    case 'form':
      $form = array();
      $form['gmap_geo_picker_macro'] = array(
        '#type' => 'textfield',
        '#title' => 'GMap macro',
        '#default_value' => $widget['gmap_geo_picker_macro'] ? $widget['gmap_geo_picker_macro'] : '[gmap]',
        '#description' => t("A GMap macro describing the initial location picker map. <em>[gmap]</em> displays a map with the settings from !gmap-admin.", array('!gmap-admin' => l('admin/settings/gmap', 'admin/settings/gmap'))),
      );
      return $form;
    case 'save':
      return array('gmap_geo_picker_macro');
  }
}

/**
 * See the gmap_set_location() function in gmap.module and its' companion
 * location_latlon_form() in location.inc
 */
function gmap_geo_picker_process($element, $edit, $form_state, $form) {
  $field = $form['#field_info'][$element['#field_name']];
  
  $element['#title'] = $field['widget']['label'];
  $element['#type'] = 'fieldset';

  $element['map'] = array();  // reserve spot at top of form for map

  $element['lat'] = array(
    '#type' => 'textfield',
    '#title' => t('Latitude'),
    '#default_value' => isset($element['#value']['lat']) ? $element['#value']['lat'] : NULL,
    '#required' => $field['required'],
    '#size' => 15,
    '#prefix' => '<div class="container-inline">',
  );
  $element['lon'] = array(
    '#type' => 'textfield',
    '#title' => t('Longitude'),
    '#default_value' => isset($element['#value']['lon']) ? $element['#value']['lon'] : NULL,
    '#required' => $field['required'],
    '#size' => 15,
    '#suffix' => '</div>',
  );
  $element['wkt'] = array(
    '#type' => 'value',
    '#value' => isset($element['#value']['wkt']) ? $element['#value']['wkt'] : NULL,
  );

  $element['map']['gmap']['#value'] = gmap_set_location($field['widget']['gmap_geo_picker_macro'], $element, array('latitude' => 'lat', 'longitude' => 'lon'));
  $element['map_instructions'] = array(
    '#type' => 'markup',
    '#weight' => 2,
    '#prefix' => '<div class=\'description\'>',
    '#value' => t('You may set the location by clicking on the map, or dragging the location marker.  To clear the location, click on the marker.'), 
    '#suffix' => '</div>',
  );

  return $element;
}

/**
 * Implementation of hook_theme().
 */
function gmap_geo_theme() {
  return array(
    'gmap_geo_picker' => array(
      'arguments' => array('element' => NULL),
    ),
    'gmap_geo_formatter_gmap_geo' => array(
      'function' => 'theme_gmap_geo_formatter',
      'arguments' => array('element' => NULL),
    ),
  );
}

/**
 * Theme the gmap_picker widget.
 */
function theme_gmap_geo_picker($element) {
  return $element['#children'];
}

/**
 * Implementation of hook_field_formatter_info().
 * Note that the formatters don't actually work for geo_data fields yet, because
 * that field type doesn't (yet) provide its data in WKT.
 */
function gmap_geo_field_formatter_info() {
  return array(
    'gmap_geo' => array(
      'label' => t('GMap'),
      'field types' => array('geo', 'geo_data'),
      'multiple values' => CONTENT_HANDLE_MODULE,
    ),
  );
}

/**
 * Themes a geo field as a gmap.
 * 
 * This uses gmap_picker widget settings from the cck field settings, if they
 * exist, and otherwise uses GMap's default map settings. Bdragon and I (Bec)
 * have talked briefly about a "gmap profile" setup, in which case we could
 * provide a field formatter for each gmap profile ("GMap: profile_name"). This
 * would be ideal.
 *
 * - Autozoom doesn't yet zoom for polygons and lines; this field formatter 
 * doesn't provide any additional autozoom functionality.
 *
 * - Linestrings and polygons are rendered as "encoded polylines" in Google Maps,
 * because they tend to be too complex to render as a plain set of points.
 * Encoded polylines reveal more complexity as the map is zoomed.
 */
function theme_gmap_geo_formatter($element) {
  module_load_include('inc', 'geo', 'includes/geo.wkb');
  module_load_include('inc', 'gmap', 'gmap_polyutil');

  foreach (element_children($element) as $i) {
    $item = $element[$i]['#item'];

    // get the geographic feature's points as an array
    $wkb = (isset($item['wkb']) ? $item['wkb'] : $item[$element['#field_name'] . '_wkb']);
    $feature = geo_wkb_get_data($wkb, 'array');

    switch ($feature['type']) {
      case 'point':
        $map['markers'][] = array('latitude' => $item['lat'], 'longitude' => $item['lon']);
        break;
      case 'linestring':
        $polyline = gmap_polyutil_polyline($feature['value']);
        $polyline['type'] = 'encoded_line';
        $map['shapes'][] = $polyline;
        break;
      case 'polygon':
        $polyline = gmap_polyutil_polyline($feature['value']);
        $map['shapes'][] = array(
          'type' => 'encoded_polygon',
          'polylines' => array($polyline),
        );
        break;
    }
  }

  if ($map) {
    // Load field instance info. If the field uses the gmap_picker widget, use
    // the widget's gmap macro to build the map array.
    $field = content_fields($element['#field_name'], $element['#type_name']);
    if (isset($field['widget']['gmap_geo_picker_macro'])) {
      $field_map = gmap_parse_macro($field['widget']['gmap_geo_picker_macro']);
      $map = array_merge($field_map, $map);
    }
  
    $map['behavior'] = array('autozoom' => TRUE);
    if (isset($field_map['zoom'])) {
      $map['maxzoom'] = $field_map['zoom'];
    }
    return theme('gmap', $map);
  }
}