geolocation_googlemaps.module in Geolocation Field 7
Same filename and directory in other branches
Google Maps widget and formatters for Geolocation.
File
modules/geolocation_googlemaps/geolocation_googlemaps.moduleView source
<?php
/**
* @file
* Google Maps widget and formatters for Geolocation.
*/
/**
* Implements hook_field_formatter_info().
*/
function geolocation_googlemaps_field_formatter_info() {
return array(
'geolocation_googlemaps_static' => array(
'label' => t('Static Google Map'),
'field types' => array(
'geolocation_latlng',
'geofield',
),
'settings' => array(
'map_width' => '300px',
'map_height' => '300px',
'map_zoomlevel' => '7',
'map_imageformat' => 'png8',
'map_link' => 0,
'map_maptype' => 'roadmap',
'marker_icon' => '',
),
),
'geolocation_googlemaps_dynamic' => array(
'label' => t('Dynamic Google Map'),
'field types' => array(
'geolocation_latlng',
'geofield',
),
'settings' => array(
'map_width' => '300px',
'map_height' => '300px',
'map_zoomlevel' => '7',
'map_imageformat' => 'png8',
'map_maptype' => 'roadmap',
'marker_icon' => '',
'map_scrollwheel' => FALSE,
),
),
);
}
/**
* Implements hook_field_formatter_settings_form().
*/
function geolocation_googlemaps_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
$display = $instance['display'][$view_mode];
$type = $display['type'];
$settings = $display['settings'];
$element = array();
// Implement some configuration options.
// http://code.google.com/intl/en/apis/maps/documentation/staticmaps/
$element['map_width'] = array(
'#type' => 'textfield',
'#title' => t('Map width'),
'#description' => t('Enter custom map width (e.g. 300px or 300%), default is 300px. Percentages do not work with static maps.'),
'#default_value' => $settings['map_width'],
);
$element['map_height'] = array(
'#type' => 'textfield',
'#title' => t('Map height'),
'#description' => t('Enter custom map height (e.g. 300px or 300%), default is 300px. Percentages do not work with static maps.'),
'#default_value' => $settings['map_height'],
);
$element['marker_icon'] = array(
'#type' => 'textfield',
'#title' => t('Marker icon'),
'#description' => t('Set the path to the icon marker you wish to use. Path can be absolute (with http://) or relative to the Drupal instalation. If left blank, the default will be used'),
'#default_value' => $settings['marker_icon'],
);
// Image format is used only for static maps.
switch ($type) {
case 'geolocation_googlemaps_static':
$element['map_imageformat'] = array(
'#type' => 'select',
'#title' => t('Image format'),
'#options' => array(
'png8' => '8-bit PNG (default)',
'png32' => '32-bit PNG',
'gif' => 'GIF',
'jpg' => 'JPEG',
'jpg-baseline' => 'JPEG (non-progressive)',
),
'#description' => t('Choose an Image Format. jpg and jpg-baseline typically provide the smallest image size, though they do so through "lossy" compression which may degrade the image. gif, png8 and png32 provide lossless compression.'),
'#default_value' => $settings['map_imageformat'],
);
$element['map_link'] = array(
'#type' => 'checkbox',
'#title' => t('Create link to Google Maps'),
'#description' => t('Link the static map image to Google Maps.'),
'#default_value' => $settings['map_link'],
);
break;
case 'geolocation_googlemaps_dynamic':
$element['map_scrollwheel'] = array(
'#type' => 'checkbox',
'#title' => t('Scroll Zoom'),
'#description' => t('By default zooming is done with double click and/or using the map controls to avoid interrupting the normal window scroll. It can optionally be enabled here.'),
'#default_value' => $settings['map_scrollwheel'],
);
break;
}
$element['map_maptype'] = array(
'#type' => 'select',
'#title' => t('Map Type'),
'#options' => array(
'roadmap' => 'Roadmap (default)',
'satellite' => 'Satellite',
'terrain' => 'Terrain',
'hybrid' => 'Hybrid',
),
'#description' => t('Choose map type. <em>roadmap</em> (default) specifies a standard roadmap image, as is normally shown on the Google Maps website. <em>satellite</em> specifies a satellite image. <em>terrain</em> specifies a physical relief map image, showing terrain and vegetation. <em>hybrid</em> specifies a hybrid of the satellite and roadmap image, showing a transparent layer of major streets and place names on the satellite image.'),
'#default_value' => $settings['map_maptype'],
);
$element['map_zoomlevel'] = array(
'#type' => 'select',
'#title' => t('Zoom level'),
'#options' => array(
'1' => '1',
'2' => '2',
'3' => '3',
'4' => '4',
'5' => '5',
'6' => '6',
'7' => '7 (default)',
'8' => '8',
'9' => '9',
'10' => '10',
'11' => '11',
'12' => '12',
'13' => '13',
'14' => '14',
'15' => '15',
'16' => '16',
'17' => '17',
'18' => '18',
'19' => '19',
),
'#description' => t('Choose a custom zoom level - the higher the number the closer. <em>High zoom warning:</em> Google might not return images for any combination of Map Type and Zoom Level.'),
'#default_value' => $settings['map_zoomlevel'],
);
return $element;
}
/**
* Implements hook_field_formatter_settings_summary().
*/
function geolocation_googlemaps_field_formatter_settings_summary($field, $instance, $view_mode) {
$display = $instance['display'][$view_mode];
$type = $display['type'];
$settings = $display['settings'];
$summary = t("<strong>Map:</strong> !mapwidth by !mapheight", array(
'!mapwidth' => $settings['map_width'],
'!mapheight' => $settings['map_height'],
));
switch ($type) {
case 'geolocation_googlemaps_static':
$summary .= ' (' . $settings['map_imageformat'] . ')';
$summary .= '<br />';
break;
case 'geolocation_googlemaps_dynamic':
$summary .= '<br />';
$scrl = $settings['map_scrollwheel'] ? t('Yes') : t('No');
$summary .= '<strong>Scrollable:</strong> ' . $scrl . '<br />';
break;
}
$summary .= '<strong>Type:</strong> ' . $settings['map_maptype'] . '<br />';
$summary .= '<strong>Zoom:</strong> ' . $settings['map_zoomlevel'];
if (!empty($settings['marker_icon'])) {
$vars = array(
'path' => $settings['marker_icon'],
);
$summary .= '<br /><strong>Icon:</strong> ' . theme('image', $vars);
}
return $summary;
}
/**
* Implements hook_field_formatter_view().
*/
function geolocation_googlemaps_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
$settings = $display['settings'];
$settings['api_key'] = variable_get('geolocation_googlemaps_api_key', '');
$element = array();
$lat_key = 'lat';
if (strpos($settings['map_height'], 'px')) {
$height = strtok($settings['map_height'], 'p');
}
elseif (strpos($settings['map_height'], '%')) {
$height = strtok($settings['map_height'], '%');
}
elseif (!strpos($settings['map_height'], '%') || !strpos($settings['map_height'], 'px')) {
$height = $settings['map_height'];
}
if (strpos($settings['map_width'], 'px')) {
$width = strtok($settings['map_width'], 'p');
}
elseif (strpos($settings['map_width'], '%')) {
$width = strtok($settings['map_width'], '%');
}
elseif (!strpos($settings['map_width'], '%') || !strpos($settings['map_width'], 'px')) {
$width = $settings['map_width'];
}
// To make this widget compatible with geofiled we need to rename the keys for
// longitude. Geofield uses "lon" while Geolocation Field uses "lng".
$lng_key = $field['type'] == 'geofield' ? 'lon' : 'lng';
switch ($display['type']) {
case 'geolocation_googlemaps_static':
foreach ($items as $delta => $item) {
$query = array(
'zoom' => $settings['map_zoomlevel'],
'size' => $width . "x" . $height,
'format' => $settings['map_imageformat'],
'maptype' => $settings['map_maptype'],
'markers' => 'size:mid|color:red|' . $item[$lat_key] . ',' . $item[$lng_key],
);
if ($settings['marker_icon']) {
$path = file_create_url($settings['marker_icon']);
$query['markers'] = 'icon:' . $path . '|' . $query['markers'];
}
if (!empty($settings['api_key'])) {
$query['key'] = $settings['api_key'];
}
$variables = array(
'path' => url('//maps.google.com/maps/api/staticmap', array(
'query' => $query,
'external' => TRUE,
)),
'alt' => 'Geolocation',
'attributes' => array(
'class' => 'geolocation-googlemaps-static',
),
);
$map_img = theme('image', $variables);
if ($settings['map_link'] === 1) {
$map_link_query['q'] = $item[$lat_key] . ',' . $item[$lng_key];
$element[$delta]['#markup'] = '<div>' . l($map_img, '//maps.google.com/maps', array(
'query' => $map_link_query,
'external' => TRUE,
'html' => TRUE,
)) . '</div>';
}
else {
$element[$delta]['#markup'] = '<div>' . $map_img . '</div>';
}
}
break;
case 'geolocation_googlemaps_dynamic':
$info = entity_get_info($entity_type);
$key = isset($info['entity keys']['name']) ? $info['entity keys']['name'] : $info['entity keys']['id'];
$eid = $entity->{$key};
foreach ($items as $delta => $item) {
$width = $settings['map_width'];
$height = $settings['map_height'];
$id = 'geolocation-googlemaps-dynamic-' . 'e_' . $eid . 'i_' . $instance['id'] . '-d_' . $delta;
$map_element['googlemap'] = array(
'#prefix' => '<div id="' . $id . '" class="geolocation-map geolocation-googlemaps-dynamic" ' . ' style="width:' . htmlentities($width) . ';height:' . htmlentities($height) . ';">',
'#suffix' => '</div>',
);
// Attach CSS and JS files via FAPI '#attached'.
$map_element['googlemap']['#attached']['css'][] = drupal_get_path('module', 'geolocation_googlemaps') . '/geolocation_googlemaps.css';
$api_key = variable_get('geolocation_googlemaps_api_key', '');
$data = array();
if (!empty($api_key)) {
$data['key'] = $api_key;
}
$query = drupal_http_build_query($data);
$map_element['googlemap']['#attached']['js'][] = array(
'data' => '//maps.google.com/maps/api/js?' . $query,
'type' => 'external',
);
$map_element['googlemap']['#attached']['js'][] = array(
'data' => '//www.google.com/jsapi',
'type' => 'external',
);
$map_element['googlemap']['#attached']['js'][] = array(
'data' => drupal_get_path('module', 'geolocation_googlemaps') . '/geolocation_googlemaps_dynamic_formatter.js',
'type' => 'file',
'scope' => 'footer',
);
// Create correct url for marker_icon.
if ($settings['marker_icon']) {
$settings['marker_icon'] = file_create_url($settings['marker_icon']);
}
// Add each delta to the settings array.
$data = array(
'formatters' => array(
'e_' . $eid . 'i_' . $instance['id'] => array(
"settings" => $settings,
"deltas" => array(
'd_' . $delta => array(
'lat' => $item[$lat_key],
'lng' => $item[$lng_key],
),
),
),
),
);
$map_element['googlemap']['#attached']['js'][] = array(
'data' => array(
'geolocationGooglemaps' => $data,
),
'type' => 'setting',
);
$element[$delta] = $map_element;
}
break;
}
return $element;
}
/**
* Implements hook_field_widget_info().
*/
function geolocation_googlemaps_field_widget_info() {
return array(
'geolocation_googlemap' => array(
'label' => t('Google Map'),
'field types' => array(
'geolocation_latlng',
'geofield',
),
),
);
}
/**
* Implements hook_field_widget_settings_form().
*/
function geolocation_googlemaps_field_widget_settings_form($field, $instance) {
$widget = $instance['widget'];
$settings = $widget['settings'];
$form['scrollwheel'] = array(
'#type' => 'checkbox',
'#title' => t('Scroll Zoom'),
'#description' => t('By default zooming is done with double click and/or using the map controls to avoid interrupting the normal window scroll. It can optionally be enabled here.'),
'#default_value' => isset($settings['scrollwheel']) ? $settings['scrollwheel'] : FALSE,
);
$form['marker_draggable'] = array(
'#type' => 'checkbox',
'#title' => t('Draggable Marker'),
'#description' => t('Enabling this will allow the user to drag/drop the marker to select a location.'),
'#default_value' => isset($settings['marker_draggable']) ? $settings['marker_draggable'] : FALSE,
);
return $form;
}
/**
* Implements hook_field_widget_form().
*/
function geolocation_googlemaps_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
// In order to make Geolocation field work with the popular Field collection
// module we check if our map widget is part of a Field collection and add the
// #field_parents delta.
if ($instance['entity_type'] == 'field_collection_item') {
$depth = count($element['#field_parents']) - 1;
$parent_delta = $element['#field_parents'][$depth];
$id = $instance['id'] . '-' . $parent_delta . '-' . $delta;
}
else {
$id = $instance['id'] . '-' . $delta;
}
$lat_value = isset($items[$delta]['lat']) ? $items[$delta]['lat'] : NULL;
// To make this widget compatible with geofiled we need to rename the keys for
// longitude. Geofield uses "lon" while Geolocation Field uses "lng".
if ($field['type'] == 'geofield') {
$lng_value = isset($items[$delta]['lon']) ? $items[$delta]['lon'] : NULL;
}
else {
$lng_value = isset($items[$delta]['lng']) ? $items[$delta]['lng'] : NULL;
}
$element += array(
'#delta' => $delta,
);
switch ($instance['widget']['type']) {
case 'geolocation_googlemap':
$element['address'] = array(
'#type' => 'item',
'#title' => $element['#title'],
'#prefix' => '<div id="geolocation-address-' . $id . '" class="geolocation-address">',
'#suffix' => '</div>',
'#required' => $instance['required'],
);
$element['address']['field'] = array(
'#type' => 'textfield',
'#maxlength' => 120,
);
$element['address']['geocode'] = array(
'#prefix' => '<span id="geolocation-address-geocode-' . $id . '" class="geolocation-address-geocode">',
'#suffix' => '</span>',
'#markup' => t('Get location'),
);
$element['address']['geocode-suggestions'] = array(
'#prefix' => '<div id="geolocation-address-geocode-suggestions-' . $id . '" class="geolocation-address-geocode-suggestions">',
'#suffix' => '</div>',
'#markup' => '<label>' . t('Suggestions') . '</label><div class="suggestion-options">' . t('Not found') . '</div>',
);
$element['help'] = array(
'#prefix' => '<div id="geolocation-help-' . $id . '" class="geolocation-help">',
'#suffix' => '</div>',
'#markup' => t('Enter an address / location / Google map URL in the textfield or you can also click on the map to set a marker'),
);
$element['googlemap'] = array(
'#prefix' => '<div id="geolocation-map-' . $id . '" class="geolocation-map" style="width:100%;height:400px;">',
'#suffix' => '</div>',
);
// Presentational item.
$element['latitem'] = array(
'#type' => 'item',
'#title' => t('Latitude:'),
'#prefix' => '<div id="geolocation-lat-item-' . $id . '" class="geolocation-lat-item">',
'#suffix' => '</div>',
'#markup' => '<span class="geolocation-lat-item-value">' . $lat_value . '</span>',
'#required' => $instance['required'],
);
$element['lat'] = array(
'#type' => 'hidden',
'#prefix' => '<div id="geolocation-lat-' . $id . '" class="geolocation-lat">',
'#suffix' => '</div>',
'#default_value' => $lat_value,
);
// Presentational item.
$element['lngitem'] = array(
'#type' => 'item',
'#title' => t('Longitude:'),
'#prefix' => '<div id="geolocation-lng-item-' . $id . '" class="geolocation-lng-item">',
'#suffix' => '</div>',
'#markup' => '<span class="geolocation-lat-item-value">' . $lat_value . '</span>',
'#required' => $instance['required'],
);
$element['lng'] = array(
'#type' => 'hidden',
'#prefix' => '<div id="geolocation-lng-' . $id . '" class="geolocation-lng">',
'#suffix' => '</div>',
'#default_value' => $lng_value,
);
$element['remove'] = array(
'#prefix' => '<div id="geolocation-remove-' . $id . '" class="geolocation-remove"><span>',
'#suffix' => '</span></div>',
'#markup' => t('Remove'),
);
// Attach CSS and JS files via FAPI '#attached'.
$element['googlemap']['#attached']['css'][] = drupal_get_path('module', 'geolocation_googlemaps') . '/geolocation_googlemaps.css';
$element['googlemap']['#attached']['js'][] = array(
'data' => drupal_get_path('module', 'geolocation_googlemaps') . '/geolocation_googlemaps_widget.js',
'type' => 'file',
'scope' => 'footer',
);
geolocation_googlemaps_attach_google_js($element);
// Make defaults available as javascript settings. In JS files use:
// Drupal.settings.mapDefaults.lat.
$map_defaults_lat = $lat_value ? $lat_value : '';
$map_defaults_lng = $lng_value ? $lng_value : '';
$map_defaults = array(
$id => array(
'lat' => $map_defaults_lat,
'lng' => $map_defaults_lng,
),
);
$data = array(
'defaults' => $map_defaults,
'settings' => $instance['widget']['settings'],
);
$element['googlemap']['#attached']['js'][] = array(
'data' => array(
'geolocation' => $data,
),
'type' => 'setting',
);
$element['field_type'] = array(
'#type' => 'value',
'#value' => $field['type'],
);
$element['#element_validate'] = array(
'geolocation_googlemaps_field_widget_validate',
);
$element['#element_validate'][] = 'geolocation_googlemaps_field_widget_set_value';
break;
}
return $element;
}
/**
* Validation handler for geolocation_googlemaps_field_widget_form().
*/
function geolocation_googlemaps_field_widget_validate($element, &$form_state, $form) {
if ($element['#required']) {
if (!$element['lat']['#value'] || !$element['lng']['#value']) {
form_error($element, t('!name field is required.', array(
'!name' => $element['#title'],
)));
}
}
else {
switch (TRUE) {
case $element['lng']['#value'] && !$element['lat']['#value']:
form_error($element, t('!name field is incomplete, latitude value is missing.', array(
'!name' => $element['#title'],
)));
break;
case !$element['lng']['#value'] && $element['lat']['#value']:
form_error($element, t('!name field is incomplete, longitude value is missing.', array(
'!name' => $element['#title'],
)));
break;
}
}
}
function geolocation_googlemaps_field_widget_set_value($element, &$form_state, $form) {
$values =& drupal_array_get_nested_value($form_state['values'], $element['#parents']);
if ($values['field_type'] == 'geofield') {
// Geofield needs the values in their own format which is exactly what
// geofield_compute_values does, but we have to change first the longitude
// key because geofield uses a different one.
$values['lon'] = $values['lng'];
$values = geofield_compute_values($values);
}
}
/**
* Implements hook_field_widget_error().
*/
function geolocation_googlemaps_field_widget_error($element, $error, $form, &$form_state) {
switch ($error['error']) {
case 'geolocation_invalid_lat':
case 'geolocation_invalid_lng':
form_error($element, $error['message']);
break;
}
}
/**
* Implements hook_menu().
*/
function geolocation_googlemaps_menu() {
$items['admin/config/services/googlemaps'] = array(
'title' => 'Geolocation Google Maps',
'description' => 'Configure Geolocation Google Maps settings',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'geolocation_googlemaps_admin_settings',
),
'access arguments' => array(
'administer site configuration',
),
'type' => MENU_NORMAL_ITEM,
);
return $items;
}
function geolocation_googlemaps_admin_settings() {
$form['geolocation_googlemaps_api_key'] = array(
'#title' => t('Your Google Simple API Access key'),
'#type' => 'textfield',
'#default_value' => variable_get('geolocation_googlemaps_api_key', ''),
'#size' => 50,
'#maxlength' => 255,
'#description' => t('Using an API key is mandatory to view Google maps. You can find more information about API keys on <a href="https://developers.google.com/maps/documentation/javascript/tutorial#api_key">Google Maps JavaScript API v3</a>.'),
);
return system_settings_form($form);
}
/**
* Implements hook_field_widget_form_alter().
*/
function geolocation_googlemaps_field_widget_form_alter(&$element, &$form_state, $context) {
// Attaches widget JS to each media reference widget.
if (isset($element['#type']) && $element['#type'] == 'media') {
geolocation_googlemaps_attach_google_js($element);
}
}
/**
* Helper function that attaches JS to the given element.
*
* @param array $element
* Element.
*/
function geolocation_googlemaps_attach_google_js(&$element) {
$js_added_already =& drupal_static(__FUNCTION__, FALSE);
if (!$js_added_already) {
$data = array();
$api_key = variable_get('geolocation_googlemaps_api_key', '');
if (!empty($api_key)) {
$data['key'] = $api_key;
}
$query = drupal_http_build_query($data);
$element['#attached']['js'][] = array(
'data' => '//maps.google.com/maps/api/js?' . $query,
'type' => 'external',
);
$element['#attached']['js'][] = array(
'data' => '//www.google.com/jsapi',
'type' => 'external',
);
$js_added_already = TRUE;
}
}