Skip to content
Snippets Groups Projects
gmap.module 45.5 KiB
Newer Older
 * Gmap element theme hook
 */
function theme_gmap($variables) {
Brandon Bergren's avatar
Brandon Bergren committed
  $element = $variables['element'];

  // Track the mapids we've used already.
  static $mapids = array();

  _gmap_doheader();

  if (isset($element['#map']) && $element['#map']) {
    // The default mapid is #map.
Brandon Bergren's avatar
Brandon Bergren committed
  if (isset($element['#gmap_settings']['id'])) {
    // Settings overrides it.
Brandon Bergren's avatar
Brandon Bergren committed
    $mapid = $element['#gmap_settings']['id'];
  }
  if (!$mapid) {
    // Hmm, no mapid. Generate one.
    $mapid = gmap_get_auto_mapid();
  // Push the mapid back into #map.
  $element['#map'] = $mapid;
  gmap_widget_setup($element, 'gmap', $mapid);
  if (!isset($element['#gmap_settings'])) {
Brandon Bergren's avatar
Brandon Bergren committed
    $element['#gmap_settings'] = array();
Brandon Bergren's avatar
Brandon Bergren committed
  // Push the mapid back into #gmap_settings.
  $element['#gmap_settings']['id'] = $mapid;
  $mapdefaults = gmap_defaults();
Brandon Bergren's avatar
Brandon Bergren committed
  $map = array_merge($mapdefaults, $element['#gmap_settings']);
  // Styles is a subarray.
Brandon Bergren's avatar
Brandon Bergren committed
  if (isset($element['#gmap_settings']['styles'])) {
    $map['styles'] = array_merge($mapdefaults['styles'], $element['#gmap_settings']['styles']);
  gmap_map_cleanup($map);

  // Add a class around map bubble contents.
  // @@@ Bdragon sez: Becw, this doesn't belong here. Theming needs to get fixed instead..
  if (isset($map['markers'])) {
    foreach ($map['markers'] as $i => $marker) {
Brandon Bergren's avatar
Brandon Bergren committed
      if (isset($marker['text'])) {
        $map['markers'][$i]['text'] = '<div class="gmap-popup">' . $marker['text'] . '</div>';
      }
  switch (strtolower($map['align'])) {
    case 'left':
      $element['#attributes']['class'][] = 'gmap-left';
      break;
    case 'right':
      $element['#attributes']['class'][] = 'gmap-right';
      break;
    case 'center':
    case 'centre':
      $element['#attributes']['class'][] = 'gmap-center';
Brandon Bergren's avatar
 
Brandon Bergren committed
  $style[] = 'width: ' . $map['width'];
  $style[] = 'height: ' . $map['height'];
  $element['#attributes']['class'] = array_merge($element['#attributes']['class'], array(
    'gmap',
    'gmap-map',
    'gmap-' . $mapid . '-gmap'
  ));

  // Some markup parsers (IE) don't handle empty inners well. Use the space to let users know javascript is required.
  // @@@ Bevan sez: Google static maps could be useful here.
  // @@@ Bdragon sez: Yeah, would be nice, but hard to guarantee functionality. Not everyone uses the static markerloader.
  $o = '<div style="' . implode('; ', $style) . ';" id="' . $element['#id'] . '"' . drupal_attributes($element['#attributes']) . '><noscript>' . t('Javascript is required to view this map.') . '</noscript></div>';
  if (isset($mapids[$element['#map']])) {
    drupal_set_message(t('Duplicate map detected! GMap does not support multiplexing maps onto one MapID! GMap MapID: %mapid', array('%mapid' => $element['#map'])), 'error');
    // Return the div anyway. All but one map for a given id will be a graymap,
    // because obj.map gets stomped when trying to multiplex maps!
    return $o;
  }
  $mapids[$element['#map']] = TRUE;

  // Put map data in a setting.
  drupal_add_js(array('gmap' => array($element['#map'] => $map)), 'setting');

  return $o;
}

/**
 * Set up widget.
 * This function will change a form element's ID so it is found
 * by the GMap handlers system.
 * @param &$element
 *   The form element to modify.
 * @param $type
 *   The gmap widget type to map to.
 * @param $map
 *   The map id. If not defined, $element['#map'] will be used.
 * @return
 *   None.
 */
function gmap_widget_setup(&$element, $type, $map = NULL) {
  if (!$map) {
    if (isset($element['#map'])) {
      $map = $element['#map'];
    }
    else {
      // Hmm, missing #map. Try to figure it out.
Brandon Bergren's avatar
Brandon Bergren committed
      if (isset($element['#gmap_settings']['id'])) {
        $map = $element['#gmap_settings']['id'];
Brandon Bergren's avatar
Brandon Bergren committed
  if (!isset($element['#attributes']['class'])) {
    $element['#attributes']['class'] = array();
Brandon Bergren's avatar
Brandon Bergren committed
  }
  $element['#attributes']['class'] = array_merge($element['#attributes']['class'], array(
    'gmap-control',
Brandon Bergren's avatar
 
Brandon Bergren committed
    'gmap-' . $type,
  $element['#id'] = gmap_get_id($map, $type);
  $element['#map'] = $map;
}

/**
 * Get a CSS id for a map and type.
 * Since CSS ids have to be unique, GMap related IDs are assigned by
 * this function.
 */
function gmap_get_id($map, $type) {
  static $serial = array();
  if (!isset($serial[$map])) {
    $serial[$map] = array();
  }
  if (!isset($serial[$map][$type])) {
    $serial[$map][$type] = -1;
  }
  $serial[$map][$type]++;
Brandon Bergren's avatar
 
Brandon Bergren committed
  return 'gmap-' . $map . '-' . $type . $serial[$map][$type];
}

/**
 * Generate a dynamic map identifier.
 */
function gmap_get_auto_mapid() {
  static $auto = 0;
  $auto++;
Brandon Bergren's avatar
 
Brandon Bergren committed
  return 'auto' . $auto . 'map';
}

/**
 * Get the list of marker titles.
 */
function gmap_get_marker_titles($reset = FALSE) {
  static $titles;

    $cached = cache_get('gmap_marker_titles', 'cache');
    if (!empty($cached)) {
      $titles = $cached->data;
      if (is_array($titles)) {
        return $titles;
      }
    }
Brandon Bergren's avatar
 
Brandon Bergren committed
  require_once(drupal_get_path('module', 'gmap') . '/gmap_markerinfo.inc');
  cache_set('gmap_marker_titles', $titles, 'cache');
  return $titles;
}

/**
 * Get the JSON icon data for all the default markers.
 */
function gmap_get_icondata($reset = FALSE) {
  static $icons;
  if (is_array($icons) && !$reset) {
    return $icons;
  }

  $icons = cache_get('gmap_icondata');
  if ($icons) {
    $icons = $icons->data;
  }

  if ($reset || !$icons) {
Brandon Bergren's avatar
 
Brandon Bergren committed
    require_once(drupal_get_path('module', 'gmap') . '/gmap_markerinfo.inc');
    $icons = _gmap_get_icondata();
  }
  cache_set('gmap_icondata', $icons, 'cache');
  return $icons;
}

/**
 * Utility function to allow high-precision decimals to work with the SQL layer.
 * Use concatenation. (Apparently unquoted %s is bad.)
 */
function gmap_decimal($num) {
  // Paraphrased from postgresql documentation:
  //
  // Numbers in SQL can be in one of these forms:
  //   digits
  //   digits.[digits][e[+-]digits]
  //   [digits].digits[e[+-]digits]
  //   digitse[+-]digits
  // where "digits" is one or more decimal digits.

  // Trim extra whitespace
  $num = trim($num);
  // Check if we're in an acceptable form.
  if (preg_match('/^[+\-]?((\d+)|(\d+\.\d*)|(\d*\.\d+))(e[+\-]?\d+)?$/', $num) === 1) {
    // Good, we can pass that right along.
    return $num;
  }
  // Otherwise, cast to float, possibly losing precision.
  return (float) $num;
}

/**
 * Utility function to use the google maps geocoder server side.
 * This is an easy, quick way to geocode a single address.
 * Note: This is a REMOTE CALL TO GOOGLE. Do NOT use this where performance matters,
 * as it could possibly take several seconds for this function to return.
 * See http://www.google.com/apis/maps/documentation/reference.html#GGeoStatusCode
 *  for a description of the possible status codes.
 */
function gmap_geocode($address, $tld = 'com') {
  $key = gmap_get_key();
  $data = drupal_http_request(gmap_views_protocol() . '://maps.google.' . $tld . '/maps/geo?q=' . urlencode($address) . '&output=csv&key=' . $key);
  if ($data->code == 200) {
    $r = explode(',', $data->data);
    return array(
      'status' => (int) $r[0],
      'accuracy' => (int) $r[1],
      'latitude' => $r[2],
      'longitude' => $r[3],
    );
  }
  // Non 200 is G_GEO_SERVER_ERROR (500).
  return array(
    'status' => 500,
  );
}

/**
 * Simple way to draw a map from inside a theme.
 * @param $latitude
 *   Latitude of marker.
 * @param $longitude
 *   Longitude of marker.
 * @param $markername
 *   Marker to use.
 *   '' will fall back to google's default marker.
 * @param $info
 *   What to show in the bubble when the marker is clicked.
 *   Leave blank if you don't want a bubble.
 * @param $zoom
 *   Map zoom.
 *   'default' will use the default zoom from the settings page.
 *   3 is usually a good value to use.
 * @param $width
 *   Map width.
 *   'default' will use the default width from the settings page.
 * @param $height
 *   Map height.
 *   'default' will use the default height from the settings page.
 * @param $autoshow
 *   If set to TRUE, automatically show the marker bubble.
 * @param $map
 *   Override parts of the map array.
 *   If you need to do much with this, you should probabaly be putting together
 *   the map array manually.
 */
function gmap_simple_map($latitude, $longitude, $markername = '', $info = '', $zoom = 'auto', $width = 'default', $height = 'default', $autoshow = FALSE, $map = array()) {
  $settings = array(
    'id' => gmap_get_auto_mapid(),
    'latitude' => $latitude, // Center the map
    'longitude' => $longitude, // on the marker.
  );
  if ($zoom != 'default') {
    $settings['zoom'] = $zoom;
  }
  if ($width != 'default') {
    $settings['width'] = $width;
  }
  if ($height != 'default') {
    $settings['height'] = $height;
  }

  $settings['markers'] = array(
    array(
      'latitude' => $latitude,
      'longitude' => $longitude,
      'markername' => $markername,
      'offset' => 0,
    )
  );

  if (!empty($info)) {
    $settings['markers'][0]['text'] = $info;
  }

  if ($autoshow) {
    $settings['markers'][0]['autoclick'] = TRUE;
  }

  if (!empty($map)) {
    $settings = array_merge($settings, $map);
  }

Brandon Bergren's avatar
Brandon Bergren committed
  $element = array(
    '#type' => 'gmap',
    '#gmap_settings' => $settings,
  );
  return drupal_render($element);
}

/**
 * Implementation of hook_keys_service(). (from the keys api)
 */
function gmap_keys_service() {
  // @@@ Remove after everyone has upgraded.
  if (module_exists('keys_api')) {
    return array(
      'gmap' => array(
        'name' => t('Gmap'),
        'description' => t('Google Maps API Key'),
      ),
    );
  }
  elseif (module_exists('keys')) {
    // @greenSkin:
    // What is your reasoning behind predefining this?
    // I'll avoid overriding you for now, but this seems rather arbitrary.
    // Reference: http://drupal.org/cvs?commit=310498

    // Probe keys to determine if it is defining our key for us.
    $test = array();
    if (function_exists('keys_keys_service')) {
      $test = keys_keys_service();
    }
    if (!isset($test['google_maps'])) {
      // Be forward compatible with future versions of keys api
      // that no longer define it.
      return array(
        'google_maps' => array(
          'name' => t('Google Maps'),
          'description' => t('Google Maps API Key'),
        ),
      );
    }
  }
/**
 * Retrieve the Google Maps key that is in use for the site.
 */
function gmap_get_key() {
  if (module_exists('keys_api')) {
    $key = keys_api_get_key('gmap', $_SERVER['HTTP_HOST']);
  }
  elseif (module_exists('keys')) {
    $key = keys_get_key('google_maps');
  }
  return $key;
}

/**
 * Implementation of hook_views_plugins().
 */
function gmap_views_plugins() {
  return array(
    'module' => 'gmap',
    'style' => array(
      'gmap' => array(
        'title' => t('GMap'),
        'help' => t('Displays rows as a map.'),
        'handler' => 'gmap_plugin_style_gmap',
        'theme' => 'gmap_view_gmap',
        'uses row plugin' => TRUE,
        'uses grouping' => TRUE,
        'uses options' => TRUE,
        'type' => 'normal',
      ),
      'gmapextended' => array(
        'title' => t('Extended GMap'),
        'help' => t('Displays a map of markers.'),
        'handler' => 'gmap_plugin_style_gmapextended',
        'theme' => 'gmap_views_view_gmapextended',
        'uses row plugin' => TRUE,
        'uses fields' => TRUE,
        'uses options' => TRUE,
        //'uses grouping' => TRUE,
        'type' => 'normal',
      ),
/**
 * Implementation of hook_views_pre_render().
 */
function gmap_views_pre_render(&$view) {
  static $gmap_processed;
  // Add js only for gmap style views with ajax and not already processed.
  if (($view->plugin_name != 'gmap' && $view->plugin_name != 'gmapextended')
    || !$view->use_ajax || $gmap_processed
  ) {
    return;
  }
  // Mark the view as already processed.
  $gmap_processed = TRUE;
  // Add js with new views callback.
  drupal_add_js(drupal_get_path('module', 'gmap') . '/js/gmap_views_ajax.js', array('group' => JS_DEFAULT));
}

/**
 * Implementation of hook_views_ajax_data_alter().
 */
function gmap_views_ajax_data_alter(&$commands, $view) {
  // Add js callback only with gmap style plugins and with ajax.
  $plugin_styles = array($view->plugin_name);
  foreach ($view->display as $display) {
    $plugin_styles[] = $display->display_options['style_plugin'];
  }
  if (!(in_array('gmap', $plugin_styles) || in_array('gmap', $plugin_styles))) {

  // Find the JQuery selector for the view's wrapper in the DOM
  $target = '';
  foreach ($commands as $command) {
    if ($command['command'] == 'insert') {
      $target = $command['selector'];
    }
  }
  $command = array('command' => 'gmapAjaxViewsFix', 'target' => $target);
  // Save settings.
  $js = drupal_add_js(NULL, array('scope' => 'header'));
  $command['settings'] = $js['settings']['data'];
  $commands[] = $command;
}

function theme_gmap_views_ui_gmapextended($variables) {
  $form = $variables['form'];

  $output = drupal_render($form['description_markup']);

  $header = array(
    t('Field'),
    t('Purpose'),
    t('Separator'),
    /*    array(
          'data' => t('Sortable'),
          'align' => 'center',
        ),
        array(
          'data' => t('Default sort'),
          'align' => 'center',
        ),*/
  );
  $rows = array();
  foreach (element_children($form['field_purposes']) as $id) {
    $row = array();
    $row[] = drupal_render($form['info'][$id]['name']);
    $row[] = drupal_render($form['field_purposes'][$id]);
    $row[] = drupal_render($form['info'][$id]['separator']);
    $rows[] = $row;
  }

  // Add the special 'None' row.
//  $rows[] = array(t('None'), '', '', '', array('align' => 'center', 'data' => drupal_render($form['default'][-1])));

  $output .= theme('table', array('header' => $header, 'rows' => $rows));
  $output .= drupal_render_children($form);
  return $output;
}

/**
 * Preprocess function for theme_gmap_view_gmap().
 */
function template_preprocess_gmap_view_gmap(&$vars) {
  $vars['map_object'] = $vars['rows'];
  // Rows is actually our map object.
  unset($vars['rows']);
  $vars['map_element'] = array(
    '#type' => 'gmap',
    '#gmap_settings' => $vars['map_object'],
  );
  // Theme the map.
  $vars['map'] = drupal_render($vars['map_element']);
}

/**
 * Determine the site protocol (http or https)
 */
function gmap_views_protocol() {
  return (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http';
}