Newer
Older
Brandon Bergren
committed
* GMap -- Routines to use the Google Maps API in Drupal.
/**
* Define the Google Maps API version being used.
*
* Current minimum version: 2.113
*
* Minimum version last changed on: June 9 2008
*
* Reason: G_SATELLITE_3D_MAP support in gmap_addons. See http://code.google.com/apis/earth/.
*
* See http://groups.google.com/group/Google-Maps-API/web/api-version-changes
* for details on using other version numbers.
*/
define('GMAP_API_VERSION', '3');
/**
* Get the defaults for a gmap.
*/
function gmap_defaults() {
$defaults = array(
'width' => '300px',
'height' => '200px',
'zoom' => 3,
'maxzoom' => 14,
'controltype' => 'Small',
'align' => 'None',
'latlong' => '40,0',
'maptype' => 'Map',
'mtc' => 'standard',
'baselayers' => array('Map', 'Satellite', 'Hybrid'),
'styles' => array(
'line_default' => array('0000ff', 5, 45, '', 0, 0),
'poly_default' => array('000000', 3, 25, 'ff0000', 45),
'highlight_color' => 'ff0000',
),
'line_colors' => array('#00cc00', '#ff0000', '#0000ff'),
);
$defaults['behavior'] = array();
$m = array();
Brandon Bergren
committed
$behaviors = gmap_module_invoke('behaviors', $m);
foreach ($behaviors as $k => $v) {
$defaults['behavior'][$k] = $v['default'];
}
$defaults = array_merge($defaults, variable_get('gmap_default', array()));
return $defaults;
}
Brandon Bergren
committed
/**
* Implementation of hook_theme().
*/
function gmap_theme() {
return array(
'gmap_views_ui_gmapextended' => array('render element' => 'form'),
'views_view_gmap' => array('render element' => 'element'),
'gmap_views_marker_label' => array('render element' => 'element'),
Brandon Bergren
committed
'gmap_marker_popup' => array('variables' => array('label' => '')),
// 'gmap_overlay_edit' => array('render element' => 'element'),
// 'gmap_macrotext' => array('render element' => 'element'),
'gmap_dimension' => array('render element' => 'element'),
// 'gmap_address' => array('render element' => 'element'),
// 'gmap_align' => array('render element' => 'element'),
'gmap' => array('render element' => 'element'),
Brandon Bergren
committed
);
}
Brandon Bergren
committed
/**
* Invokes hook_gmap() in every module.
*
* We can't use module_invoke_all() because we pass $map by reference.
*/
function gmap_module_invoke($op, &$map) {
Brandon Bergren
committed
$return = array();
Brandon Bergren
committed
foreach (module_implements('gmap') as $module) {
$function = $module . '_gmap';
$result = $function($op, $map);
if (isset($result) && is_array($result)) {
$return = array_merge_recursive($return, $result);
}
elseif (isset($result)) {
$return[] = $result;
}
}
return $return;
}
/**
* Implementation of hook_gmap().
*/
function gmap_gmap($op, &$map) {
switch ($op) {
case 'macro':
return array(
'points' => array(
'multiple' => TRUE,
),
'markers' => array(
'multiple' => TRUE,
),
'feed' => array(
'multiple' => TRUE,
),
'style' => array(
'multiple' => TRUE,
),
if ((isset($map['behavior']['dynmarkers']) && $map['behavior']['dynmarkers']) || !empty($map['markers'])) {
static $header_set = FALSE;
if (!$header_set) {
$header_set = TRUE;
if (!variable_get('gmap_marker_file', FALSE)) {
gmap_regenerate_markers();
$mm = variable_get('gmap_mm_type', 'gmap');
// If you really really want to override the marker manager, implement
// this, take $mm by ref, and have fun. --Bdragon
if (function_exists('_gmap_markermanager_override')) {
_gmap_markermanager_override($mm);
}
// add required base js
drupal_add_js($path . 'gmap.js');
drupal_add_js($path . 'icon.js');
drupal_add_js($path . 'marker.js');
drupal_add_js($path . 'highlight.js');
drupal_add_js('/' . variable_get('file_public_path', conf_path() . '/files') . '/js/gmap_markers.js');
drupal_add_js($path . 'gmap_marker.js');
drupal_add_js($path . 'poly.js');
//$path = drupal_get_path('module', 'gmap') . '/js';
if (isset($map['behavior']['locpick']) && $map['behavior']['locpick']) {
drupal_add_js($path . 'locpick.js');
}
if (!empty($map['markers']) || !empty($map['lines'])) {
drupal_add_js($path . 'markerloader_static.js');
}
if (!empty($map['shapes'])) {
drupal_add_js($path . 'shapeloader_static.js');
drupal_add_js($path . 'gmap_shapes.js');
}
if (isset($map['feed']) && is_array($map['feed'])) {
drupal_add_js(path . 'markerloader_georss.js');
}
break;
case 'macro_multiple':
return array('points', 'markers', 'feed', 'circle', 'rpolygon', 'polygon', 'line', 'style');
case 'behaviors':
return array(
'locpick' => array(
'title' => t('Location chooser'),
'default' => FALSE,
'help' => t('Used to activate location choosing using a gmap.'),
'internal' => TRUE,
),
'nodrag' => array(
'title' => t('Disable dragging'),
'default' => FALSE,
'help' => t('Remove the ability for the user to drag the map. If dragging is disabled, keyboard shortcuts are implicitly disabled.'),
),
'nokeyboard' => array(
'title' => t('Disable keyboard'),
'default' => TRUE,
'help' => t('Disable the keyboard shortcuts.'),
),
'nomousezoom' => array(
'title' => t('Disable mousezoom'),
'default' => FALSE,
'help' => t('Disable using the scroll wheel to zoom the map.'),
),
Brandon Bergren
committed
'nocontzoom' => array(
'title' => t('Disable Continuous Zoom'),
'default' => FALSE,
'help' => t('Disable dynamically resizing images while waiting for tiles to load when zooming.'),
),
'autozoom' => array(
'title' => t('Use AutoZoom'),
'default' => FALSE,
'help' => t('Automatically zoom the map to fit all markers when markers are added.'),
),
'dynmarkers' => array(
'title' => t('Unconditionally enable marker interface'),
'default' => FALSE,
'help' => t('Load the marker loader system even if no markers to load are detected. Useful if you are injecting markers from somewhere else.'),
),
'overview' => array(
'title' => t('Enable Overview Map'),
'default' => FALSE,
'help' => t('Enable the "overview map" in the bottom right corner.'),
'previewable' => TRUE,
),
/* 'notype' => array(
'title' => t('Disable map type control'),
'default' => FALSE,
'help' => t('Removes the map type control from the upper right corner. Recommended for very narrow maps.'),
'previewable' => TRUE,
), */
'collapsehack' => array(
'title' => t('Work around bugs when maps appear in collapsible fieldsets'),
'default' => FALSE,
'help' => t('Enabling this will work around some issues that can occur when maps appear inside collapsible fieldsets.'),
),
// Note to myself, who keeps forgetting what a scale control actually IS.:
// |------------ 1mi ------------|
'scale' => array(
'title' => t('Add scale control to map.'),
'default' => FALSE,
'help' => t('Adds a scale control to the map in the default position.'),
'previewable' => TRUE,
),
'extramarkerevents' => array(
'title' => t('Enable extra marker events.'),
'default' => FALSE,
'help' => t('Used for advanced javascript work, this will enable the <em>mouseovermarker</em>, <em>mouseoutmarker</em>, and <em>dblclickmarker</em> events.'),
'internal' => TRUE,
),
'clickableshapes' => array(
'title' => t('Enable clickable shapes.'),
'default' => FALSE,
'help' => t('Used for advanced javascript work, this will enable the <em>clickshape</em> event.'),
'internal' => TRUE,
),
'googlebar' => array(
'title' => t('Enable Google Bar'),
'default' => FALSE,
'help' => t('Enable the "Google Bar" at bottom of the map.'),
'previewable' => TRUE,
),
'highlight' => array(
'title' => t('Highlight marker on rollover'),
'default' => FALSE,
'help' => t('Highlight marker by creating circle on mouse rollover event.'),
'previewable' => TRUE,
),
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
);
break;
case 'baselayers':
$map['Google']['Map'] = array(
'title' => t('Map: Standard street map.'),
'default' => TRUE,
'help' => t('The standard default street map. Internal name: G_NORMAL_MAP'),
);
$map['Google']['Satellite'] = array(
'title' => t('Satellite: Standard satellite map.'),
'default' => TRUE,
'help' => t('Satellite view without street overlay. Internal name: G_SATELLITE_MAP'),
);
$map['Google']['Hybrid'] = array(
'title' => t('Hybrid: Hybrid satellite map.'),
'default' => TRUE,
'help' => t('Satellite view with street overlay. Internal name: G_HYBRID_MAP'),
);
$map['Google']['Physical'] = array(
'title' => t('Terrain: Physical feature map.'),
'default' => FALSE,
'help' => t('Map with physical data (terrain, vegetation.) Internal name: G_PHYSICAL_MAP'),
);
break;
}
}
/**
* Get the basic js files needed for a GMap.
function _gmap_base_js() {
$ret = array();
$path = drupal_get_path('module', 'gmap');
$ret[$path . '/js/gmap.js'] = array('weight' => 1);
$ret[$path . '/js/icon.js'] = array('weight' => 2);
Brandon Bergren
committed
$mms = variable_get('gmap_markermanager', array());
if (empty($mms[$mm])) {
$mms[$mm] = array();
}
// If you really really want to override the marker manager, implement
// this, take $mm by ref, and have fun. --Bdragon
if (function_exists('_gmap_markermanager_override')) {
Brandon Bergren
committed
_gmap_markermanager_override($mm, $mms);
Brandon Bergren
committed
if ($mm == 'clusterer' || $mm == 'clustermarker') {
// Needed for access to clusterer marker.
Brandon Bergren
committed
}
if (isset($mms[$mm]['filename'])) {
$ret[$path . '/js/marker.js'] = array('weight' => 2);
$ret[$path . '/js/highlight.js'] = array('weight' => 2);
// Add the markermanager.
// @@@TODO Need to allow multiple inclusion and have marker manager set at the map level.
$mm = variable_get('gmap_mm_type', 'gmap');
$mms = variable_get('gmap_markermanager', array());
if (isset($mms[$mm]['filename'])) {
$ret[$path . '/thirdparty/' . $mms[$mm]['filename']] = array('weight' => 3);
$ret[$path . '/js/' . $mm . '_marker.js'] = array('weight' => 4);
drupal_add_js(array('gmap_markermanager' => $mms[$mm]), 'setting');
$ret[$path . '/js/poly.js'] = array('weight' => 3);
Brandon Bergren
committed
global $language;
$file = 'api/js';
Brandon Bergren
committed
$query = array(
'v' => variable_get('gmap_api_version', GMAP_API_VERSION),
'language' => $language->language,
'sensor' => 'false',
Brandon Bergren
committed
);
tmsimont
committed
if ($key = gmap_get_key()) {
$query['key'] = $key;
}
if ($query['language'] == 'zh-hans') {
$query['language'] = 'zh-CN';
$ret[url(gmap_views_protocol() . '://ditu.google.cn/maps/'.$file, array('query' => $query))] = array(
'type' => 'external',
johnv
committed
'weight' => 2,
);
}
elseif ($query['language'] == 'zh-hant') {
$query['language'] = 'zh-TW';
tmsimont
committed
$ret[url(gmap_views_protocol() . '://maps.googleapis.com/maps/'.$file, array('query' => $query))] = array(
'type' => 'external',
johnv
committed
'weight' => 2,
);
}
else {
tmsimont
committed
$ret[url(gmap_views_protocol() . '://maps.googleapis.com/maps/'.$file, array('query' => $query))] = array(
'type' => 'external',
johnv
committed
'weight' => 2,
$ret['/' . variable_get('file_public_path', conf_path() . '/files') . '/js/gmap_markers.js'] = array(
'type' => 'external',
'weight' => 2,
);
return $ret;
}
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
/**
* Set up the HTML header for GMap.
* If you are going to include a custom JS file that extends GMap, you probabaly
* want to call this first to ensure that the core js files have been added.
*/
function _gmap_doheader() {
static $gmap_initialized = FALSE;
if ($gmap_initialized) {
return;
}
$gmap_path = drupal_get_path('module', 'gmap');
$mm = variable_get('gmap_mm_type', 'gmap');
$mms = variable_get('gmap_markermanager', array());
if (empty($mms[$mm])) {
$mms[$mm] = array();
}
// If you really really want to override the marker manager, implement
// this, take $mm by ref, and have fun. --Bdragon
if (function_exists('_gmap_markermanager_override')) {
_gmap_markermanager_override($mm, $mms);
}
if ($mm == 'clusterer' || $mm == 'clustermarker') {
// Needed for access to clusterer marker.
// drupal_add_js($gmap_path . '/js/icon.js');
}
if (isset($mms[$mm]['filename'])) {
// drupal_add_js($gmap_path . '/thirdparty/' . $mms[$mm]['filename']);
}
// drupal_add_js($gmap_path . '/js/marker.js');
// drupal_add_js($gmap_path . '/js/highlight.js');
// drupal_add_js($gmap_path . '/js/' . $mm . '_marker.js');
drupal_add_js(array('gmap_markermanager' => $mms[$mm]), 'setting');
// @@@
drupal_add_js($gmap_path . '/js/poly.js');
global $language;
$file = 'api/js';
$query = array(
'v' => variable_get('gmap_api_version', GMAP_API_VERSION),
'language' => $language->language,
'sensor' => 'false',
);
if ($query['language'] == 'zh-hans') {
$query['language'] = 'zh-CN';
drupal_add_js(url('http://ditu.google.cn/maps/' . $file, array('query' => $query)));
}
elseif ($query['language'] == 'zh-hant') {
$query['language'] = 'zh-TW';
drupal_add_js(url('http://maps.google.com/maps/' . $file, array('query' => $query)));
}
else {
drupal_add_js(url('http://maps.google.com/maps/' . $file, array('query' => $query)));
}
$gmap_initialized = TRUE;
}
/**
* Convert a macro string into a GMap array.
*
* @param $instring
* Macro to process.
* @param $ver
* Version to treat macro as.
* Set to 1 when processing very old macros, otherwise leave as is.
* @return
* A GMap array.
*/
function gmap_parse_macro($instring, $ver = 2) {
require_once drupal_get_path('module', 'gmap') . '/gmap_parse_macro.inc';
Brandon Bergren
committed
return _gmap_parse_macro($instring, $ver);
}
/**
* Theme a marker popup.
* This will get called for markers embedded in macros.
Brandon Bergren
committed
function theme_gmap_marker_popup($vars) {
return $vars['label'];
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
}
/**
* Location chooser utility function.
*
* Creates a map that can be interactively used to fill a form with a
* location (latitude, longitude and zoom level).
*
* Note: This is a utility function designed for location.module, there is no
* guarantee it will not be removed eventually.
*
* @param $map
* Either a macro to use as the base map for setting a location, or an already set map associative array.
* @param $form
* A formset associative array. Cannot be more than one deep.
* @param $fields
* An associative array for the field names. 'latitude', 'longitude'=>name of respective array, 'address' is optional.
* @return
* A string with the google map code to be inserted onto the page.
*
*/
function gmap_set_location($map, &$form, $fields) {
static $ctr = 0;
$ctr++;
if (!is_array($map)) {
$map = array_merge(gmap_defaults(), gmap_parse_macro($map));
}
$map['id'] = $id;
// This is a locpick map.
$map['behavior']['locpick'] = TRUE;
$element = array(
'#type' => 'gmap',
'#map' => $map['id'],
);
$form[$fields['latitude']]['#map']=$id;
gmap_widget_setup($form[$fields['latitude']], 'locpick_latitude');
$form[$fields['longitude']]['#map']=$id;
gmap_widget_setup($form[$fields['longitude']], 'locpick_longitude');
if (isset($fields['address'])) {
$form[$fields['address']]['#map'] = $id;
gmap_widget_setup($form[$fields['address']], 'locpick_address');
}
}
/**
* Handle filter preparation.
*/
function _gmap_prepare($intext) {
$out = FALSE;
$matches = array();
preg_match_all('/\[gmap([^\[\]]+ )* \] /x', $intext, $matches);
$i = 0;
while (isset($matches[1][$i])) {
$out[0][$i] = $matches[0][$i];
if ($matches[1][$i][0] == '1') {
$ver = 1;
$matches[1][$i] = substr($matches[0][$i], 1);
}
else {
$ver = 2;
}
$map = array(
'#type' => 'gmap',
'#gmap_settings' => gmap_parse_macro($matches[1][$i], $ver),
);
$out[1][$i] = drupal_render($map);
$i++;
} // endwhile process macro
return $out;
}
/**
* Make sure a string is a valid css dimension.
*/
function gmap_todim($instring) {
if (!is_string($instring)) {
return FALSE;
}
$s = strtolower(trim($instring));
if (preg_match('/^([\d.]+)\s*(em|ex|px|in|cm|mm|pt|pc|%)$/', $s, $matches)) {
return $matches[1] . $matches[2];
}
else {
return FALSE;
}
}
/**
* Ensure a textfield is a valid css dimension string.
*/
function gmap_dimension_validate(&$elem, &$form_state) {
$value = gmap_todim($elem['#value']);
if ($value) {
// Normalize the css dimension string.
form_set_value($elem, $value, $form_state);
}
else {
form_error($elem, t('The specified value is not a valid CSS dimension.'));
}
}
/**
* Implement hook_filter_info().
function gmap_filter_info() {
$filters['gmap_macro'] = array(
'title' => t('GMap Macro expander'),
'description' => t('GMap macros will be displayed as interactive maps.'),
'process callback' => '_gmap_filter_process',
'tips callback' => '_gmap_filter_tips',
'cache' => FALSE, // @@@ FIX ME ALREADY!!!
);
return $filters;
}
/**
* Filter process callback for gmap_macro.
*/
function _gmap_filter_process($text, $filter, $format) {
$gmaps = _gmap_prepare($text); //returns an array of $tables[0] = table macro $table[1]= table html
if ($gmaps) { // there are table macros in this node
return str_replace($gmaps[0], $gmaps[1], $text);
}
else {
return $text;
* Implement tips callback for gmap_macro.
function _gmap_filter_tips($filter, $format, $long = FALSE) {
Brandon Bergren
committed
if (user_access('create gmap macro')) { // only display macro if user can create one
return t('Insert Google Map macro.') . '<a href="' . url('map/macro') . '" target="_blank" >' . t('Create a macro') . '</a>';
}
else {
return t('Insert Google Map macro.');
}
}
/**
* Implementation of hook_menu().
*/
$items['admin/config/services/gmap'] = array(
'title' => 'GMap',
'description' => 'Configure GMap settings',
'page callback' => 'drupal_get_form',
'page arguments' => array('gmap_admin_settings'),
'file' => 'gmap_settings_ui.inc',
'access arguments' => array('administer site configuration'),
'type' => MENU_NORMAL_ITEM,
);
return $items;
* Regenerate the markerdata file.
function gmap_regenerate_markers() {
Andriy Podanenko
committed
// This often fails in cli mode, so bail.
if (drupal_is_cli()) {
return;
}
$contents .= "// GMap marker image data.\n";
$contents .= "Drupal.gmap = Drupal.gmap || {};\n";
$contents .= "Drupal.gmap.iconpath = " . drupal_json_encode(base_path() . variable_get('gmap_markerfiles', drupal_get_path('module', 'gmap') . '/markers')) . ";\n";
$contents .= "Drupal.gmap.icondata = " . drupal_json_encode(gmap_get_icondata(TRUE)) . ";\n";
http://druler.com
committed
$dir = "public://js/";
// Make sure js/ exists in the files folder.
if (file_prepare_directory($dir, FILE_CREATE_DIRECTORY)) {
$file = file_save_data($contents, 'public://js/gmap_markers.js', FILE_EXISTS_REPLACE);
if (!empty($file)) {
variable_set('gmap_marker_file', $file->fid);
}
drupal_set_message(t('GMap is unable to save the marker bundle. Markers will not work!'), 'error');
// Also regenerate the cached marker titles array
gmap_get_marker_titles(TRUE);
}
/**
* Implementation of hook_flush_caches().
*/
function gmap_flush_caches() {
gmap_regenerate_markers();
* Implement hook_element_info().
function gmap_element_info() {
$path = drupal_get_path('module', 'gmap');
return array(
'gmap' => array(
'#input' => FALSE, // This isn't a *form* input!!
'#gmap_settings' => array_merge(gmap_defaults(), array(
'points' => array(),
'pointsOverlays' => array(),
'lines' => array(),
)),
'#attached' => array(
'css' => array(
$path . '/gmap.css',
),
'js' => _gmap_base_js(),
),
'#pre_render' => array('_gmap_pre_render_map'),
'#theme' => 'gmap',
),
'gmap_macrotext' => array(
'#input' => TRUE,
'#gmap_newtype' => 'textarea',
// '#theme' => 'gmap_macrotext',
'#attached' => array(
'js' => array(
"$path/js/macro.js" => array('weight' => 2),
"$path/js/macrobuilder.js" => array('weight' => 2),
),
),
'#theme' => 'textarea',
),
'gmap_overlay_edit' => array(
'#input' => TRUE,
'#process' => array('process_gmap_overlay_edit'),
'#attached' => array(
'js' => array(
"$path/js/gmap_shapes.js" => array('weight' => 2),
"$path/js/overlay_edit.js" => array('weight' => 2),
"$path/js/polylineEdit/src/polylineEdit_packed.js" => array('weight' => 3),
"$path/js/polygonEdit/src/polygonEdit_packed.js" => array('weight' =>3),
),
),
// '#theme' => 'select',
Brandon Bergren
committed
'gmap_style' => array('#input' => TRUE, '#tree' => TRUE, '#gmap_style_type' => 'poly', '#process' => array('process_gmap_style')),
'gmap_address' => array(
'#input' => TRUE,
'#process' => array('process_gmap_address'),
'#attached' => array(
'js' => array(
"$path/js/address.js" => array('weight' => 2),
),
),
'#autocomplete_path' => '',
'#theme' => 'textfield',
),
'gmap_latitude' => array('#input' => TRUE, '#gmap_newtype' => 'textfield', '#process' => array('process_gmap_control')),
'gmap_longitude' => array('#input' => TRUE, '#gmap_newtype' => 'textfield', '#process' => array('process_gmap_control')),
'gmap_latlon' => array('#input' => TRUE, '#gmap_newtype' => 'textfield', '#process' => array('process_gmap_control')),
Brandon Bergren
committed
'gmap_markerchooser' => array('#input' => TRUE, '#process' => array('process_gmap_markerchooser')),
'gmap_dimension' => array('#input' => TRUE, '#gmap_newtype' => 'textfield', '#process' => array('process_gmap_control'), '#element_validate' => array('gmap_dimension_validate')),
/**
* Pre render function to make sure all required JS is available.
*/
function _gmap_pre_render_map($element) {
$path = drupal_get_path('module', 'gmap') . '/js';
if (!isset($element['#gmap_settings'])) {
$element['#gmap_settings'] = $element['#settings'];
}
$map = $element['#gmap_settings'];
if (isset($map['behavior']['locpick']) && $map['behavior']['locpick']) {
$element['#attached']['js']["$path/locpick.js"] = array('weight' => 2);
}
if (!empty($map['markers']) || !empty($map['lines'])) {
Brandon Bergren
committed
$element['#attached']['js']["$path/markerloader_static.js"] = array('weight' => 5);
}
if (!empty($map['shapes'])) {
Brandon Bergren
committed
$element['#attached']['js']["$path/shapeloader_static.js"] = array('weight' => 5);
$element['#attached']['js']["$path/gmap_shapes.js"] = array('weight' => 5);
}
if (isset($map['feed']) && is_array($map['feed'])) {
Brandon Bergren
committed
$element['#attached']['js']["$path/markerloader_georss.js"] = array('weight' => 5);
}
return $element;
}
/**
* Generic gmap control #process function.
*/
function process_gmap_control($element, &$form_state, $complete_form) {
Brandon Bergren
committed
$control = substr($element['#type'], 5);
$element['#type'] = $element['#gmap_newtype'];
unset($element['#gmap_newtype']);
Brandon Bergren
committed
gmap_widget_setup($element, $control);
// Attempt to run any #process handlers of our transmogrified type.
// However, if we aren't the only #process handler, we assume someone else
// is taking care of setting up any default handlers.
$chain = FALSE;
if (count($element['#process']) == 1) {
// Unset #process so we can pull in the default.
unset($element['#process']);
$chain = TRUE;
}
// Inherit #input from the new type.
unset($element['#input']);
// Merge in the defaults for the target element type.
$element += element_info($element['#type']);
if (isset($element['#input']) && $element['#input']) {
if (isset($element['#process']) && $chain) {
// Chain process.
foreach ($element['#process'] as $process) {
if (function_exists($process)) {
$form = $process($element, $form_state, $complete_form);
Brandon Bergren
committed
}
return $element;
}
/**
* Style fieldset #process function.
*/
function process_gmap_style($element) {
$isline = ($element['#gmap_style_type'] == 'line');
// Stroke color
$element[0] = array(
'#type' => 'textfield',
'#size' => 6,
'#maxlength' => 6,
'#field_prefix' => '#',
'#title' => t('Stroke color'),
'#value' => $element['#value'][0],
'#attributes' => array('class' => array('gmap_style')),
);
// Stroke weight
$element[1] = array(
'#type' => 'textfield',
'#title' => t('Stroke weight'),
'#description' => t('Thickness of line, in pixels.'),
'#size' => 3,
'#maxlength' => 3,
'#field_suffix' => t('px'),
'#value' => $element['#value'][1],
'#attributes' => array('class' => array('gmap_style')),
);
// Stroke opacity
$element[2] = array(
'#type' => 'textfield',
'#title' => t('Stroke opacity'),
'#size' => 3,
'#maxlength' => 3,
'#field_suffix' => '%',
'#value' => $element['#value'][2],
'#attributes' => array('class' => array('gmap_style')),
);
// Fill color
$element[3] = array(
'#type' => 'textfield',
'#title' => t('Fill color'),
'#description' => t('Hex color value for fill color. Example: #<em>00AA33</em>'),
'#size' => 6,
'#maxlength' => 6,
'#field_prefix' => '#',
'#attributes' => array('class' => array('gmap_style')),
);
$element[4] = array(
'#type' => 'textfield',
'#title' => t('Fill opacity'),
'#description' => t('Opacity of fill, from 0 to 100%.'),
'#size' => 3,
'#maxlength' => 3,
'#field_suffix' => '%',
'#attributes' => array('class' => array('gmap_style')),
$element_info = element_info('fieldset');
$element = array_merge($element, $element_info);
return $element;
}
/**
* Overlay editor #process function.
*/
function process_gmap_overlay_edit($element) {
// Conver the root element into a fieldset.
$element['#type'] = 'fieldset';
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
$element['#title'] = t('Overlay editor');
}
$element['#tree'] = TRUE;
$element['mapclicktype'] = array(
'#type' => 'select',
'#title' => t('Click map'),
'#map' => $element['#map'],
'#options' => array(
'Points' => t('Points'),
'Lines' => t('Lines'),
'Circles' => t('Circles'),
'GPolygon' => t('Filled Polygons'),
),
);
gmap_widget_setup($element['mapclicktype'], 'overlayedit_mapclicktype');
$element['markerclicktype'] = array(
'#type' => 'select',
'#title' => t('Click marker / segment'),
'#map' => $element['#map'],
'#options' => array(
'Remove' => t('Remove'),
// 'updatestyle' => t('Update Styles'),
// 'removestyle' => t('Remove Styles'),
'Edit Info' => t('Edit Info'),
),
);
gmap_widget_setup($element['markerclicktype'], 'overlayedit_markerclicktype');
$element['marker'] = array(
'#type' => 'select',
'#map' => $element['#map'],
'#options' => gmap_get_marker_titles(),
'#title' => t('Marker'),
// '#theme' => 'gmap_overlay_edit',
);
gmap_widget_setup($element['marker'], 'overlayedit');
$element['linestyle'] = array(
'#type' => 'gmap_style',
'#title' => t('Line style'),
'#gmap_style_type' => 'line',
'#default_value' => array('0000ff', 5, 45, '', ''), // @@@
);
gmap_widget_setup($element['linestyle'], 'overlayedit_linestyle', $element['#map']);
$element['linestyle']['linestyle_apply'] = array(
'#tree' => FALSE,
'#type' => 'checkbox',
'#title' => t('Use for new and changed lines'),
'#default_value' => FALSE,
);
gmap_widget_setup($element['linestyle']['linestyle_apply'], 'overlayedit_linestyle_apply', $element['#map']);
$element['polystyle'] = array(
'#type' => 'gmap_style',
'#title' => t('Polygon style'),
'#gmap_style_type' => 'poly',
'#default_value' => array('000000', 3, 25, 'ff0000', 45), // @@@
);
gmap_widget_setup($element['polystyle'], 'overlayedit_polystyle', $element['#map']);
$element['polystyle']['polystyle_apply'] = array(
'#tree' => FALSE,
'#type' => 'checkbox',
'#title' => t('Use for new and changed polygons'),
'#default_value' => FALSE,
);
gmap_widget_setup($element['polystyle']['polystyle_apply'], 'overlayedit_polystyle_apply', $element['#map']);
$element += element_info('fieldset');
return $element;
}
/**
* Address widget #process function.
*/
function process_gmap_address($element) {
$element['#type'] = 'textfield';
gmap_widget_setup($element, 'address');
$element += element_info('textfield');
return $element;
}
/**
* Marker chooser #process function.
*/
function process_gmap_markerchooser($element) {
$element['#type'] = 'select';
Brandon Bergren
committed
$options = gmap_get_marker_titles();
if (!isset($element['#required']) || !$element['#required']) {
$options = array('' => t('Default')) + $options;
}
$element['#options'] = $options;
$element += element_info('select');
return $element;
}
/**
* Perform some normalization on the map object
* to prevent errors.
*/
function gmap_map_cleanup(&$map) {
// Google is picky about this one.
$map['zoom'] = (int)$map['zoom'];
// Normalize latitude / longitude
if ($map['latlong']) {
$map['latlon'] = $map['latlong'];
unset($map['latlong']);
}
Brandon Bergren
committed
// Normalize extent.
if (isset($map['extent']) && is_string($map['extent'])) {
$tmp = explode(',', $map['extent']);
if (count($tmp) == 4) {
// String form of extent has x,y, but native array form is lat,lon, so need to flip them a bit.
$map['extent'] = array(array($tmp[1], $tmp[0]), array($tmp[3], $tmp[2]));
}
else {
// Extent was unparseable, don't pass it.
unset($map['extent']);
}
}
if (isset($map['latlon']) && (!isset($map['latitude']) || !isset($map['longitude']))) {
list($map['latitude'], $map['longitude']) = explode(',', $map['latlon']);
}
unset($map['latlon']);
foreach ($map['baselayers'] as $k => $v) {
if (!$v) {
unset($map['baselayers'][$k]);
}
}
}
/**
* Gmap element theme hook
*/
function theme_gmap($variables) {
// Track the mapids we've used already.
static $mapids = array();
if (isset($element['#map']) && $element['#map']) {
// The default mapid is #map.
$mapid = $element['#map'];
// Settings overrides it.
}
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'])) {
// Push the mapid back into #gmap_settings.
$element['#gmap_settings']['id'] = $mapid;
$map = array_merge($mapdefaults, $element['#gmap_settings']);
if (isset($element['#gmap_settings']['styles'])) {
$map['styles'] = array_merge($mapdefaults['styles'], $element['#gmap_settings']['styles']);
Rebecca White
committed
// Add a class around map bubble contents.
Brandon Bergren
committed
// @@@ Bdragon sez: Becw, this doesn't belong here. Theming needs to get fixed instead..
if (isset($map['markers'])) {
foreach ($map['markers'] as $i => $marker) {
if (isset($marker['text'])) {
$map['markers'][$i]['text'] = '<div class="gmap-popup">' . $marker['text'] . '</div>';
}
Brandon Bergren
committed
}
Rebecca White
committed
}
switch (strtolower($map['align'])) {
case 'left':
$element['#attributes']['class'][] = 'gmap-left';
$element['#attributes']['class'][] = 'gmap-right';
break;
case 'center':
case 'centre':
$element['#attributes']['class'][] = 'gmap-center';
$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.
c960657
committed
$o = '<div style="' . implode('; ', $style) . ';" id="' . $element['#id'] . '"' . drupal_attributes($element['#attributes']) . '><noscript>' . t('Javascript is required to view this map.') . '</noscript></div>';
Brandon Bergren
committed
gmap_module_invoke('pre_theme_map', $map);
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');
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
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.
if (isset($element['#gmap_settings']['id'])) {
$map = $element['#gmap_settings']['id'];
$element['#attributes']['class'] = array();
$element['#attributes']['class'] = array_merge($element['#attributes']['class'], array(
$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]++;
}
/**
* Generate a dynamic map identifier.
*/
function gmap_get_auto_mapid() {
static $auto = 0;
$auto++;
}
/**
* Get the list of marker titles.
*/
function gmap_get_marker_titles($reset = FALSE) {
static $titles;
if (!$reset) {
if (is_array($titles)) {
return $titles;
}
$cached = cache_get('gmap_marker_titles', 'cache');
if (!empty($cached)) {
$titles = $cached->data;
if (is_array($titles)) {
return $titles;
}
}
require_once(drupal_get_path('module', 'gmap') . '/gmap_markerinfo.inc');
$titles = _gmap_get_marker_titles();
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) {
}
if ($reset || !$icons) {
require_once(drupal_get_path('module', 'gmap') . '/gmap_markerinfo.inc');
$icons = _gmap_get_icondata();
}
cache_set('gmap_icondata', $icons, 'cache');
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
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') {
$data = drupal_http_request(gmap_views_protocol() . '://maps.google.' . $tld . '/maps/geo?q=' . urlencode($address) . '&output=csv&key=' . $key);
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
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);
}
$element = array(
'#type' => 'gmap',
'#gmap_settings' => $settings,
);
return drupal_render($element);
}
/**
* Implementation of hook_keys_service(). (from the keys api)
*/
function gmap_keys_service() {
Brandon Bergren
committed
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
// @@@ 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() {
tmsimont
committed
$key = variable_get('gmap_api_key', FALSE);
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',
),
),
);
}
Brandon Bergren
committed
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
/**
* 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)))
return;
// 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;
}
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
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;
}
Brandon Bergren
committed
/**
* 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']);
}