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', '2.115');
/**
* 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),
),
'line_colors' => array('#00cc00', '#ff0000', '#0000ff'),
);
$defaults['behavior'] = array();
$m = array();
$behaviors = module_invoke_all('gmap', '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(
'views_view_gmap' => array('arguments' => array('element')),
'gmap_views_marker_label' => array('arguments' => array('element')),
Brandon Bergren
committed
'gmap_marker_popup' => array('arguments' => array('label')),
Brandon Bergren
committed
'gmap_overlay_edit' => array('arguments' => array('element')),
'gmap_macrotext' => array('arguments' => array('element')),
'gmap_dimension' => array('arguments' => array('element')),
'gmap_address' => array('arguments' => array('element')),
'gmap_align' => array('arguments' => array('element')),
Brandon Bergren
committed
'gmap' => array('arguments' => array('element')),
);
}
/**
* 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,
),
);
case 'pre_theme_map':
$path = drupal_get_path('module', 'gmap') .'/js/';
if ((isset($map['behavior']['dynmarkers']) && $map['behavior']['dynmarkers']) || !empty($map['markers'])) {
static $header_set = FALSE;
if (!$header_set) {
$header_set = TRUE;
Brandon Bergren
committed
// If the user is using private download method, it's up to them to get the path set up.
if (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) != FILE_DOWNLOADS_PUBLIC) {
$markerpath = variable_get('gmap_private_markerfile', '');
if (empty($markerpath) || !file_exists($markerpath)) {
drupal_set_message(t('GMap marker file settings are not configured properly for Private download method, markers will not work!'), 'error');
}
else {
drupal_add_js($markerpath, 'module', 'header', FALSE, TRUE, FALSE);
}
}
// With public method, we can handle all bookkeeping ourselves.
else {
$markerpath = file_create_path('js');
Brandon Bergren
committed
if (!$markerpath || !file_exists("$markerpath/gmap_markers.js")) {
gmap_regenerate_markers();
$markerpath = file_create_path('js');
}
drupal_add_js("$markerpath/gmap_markers.js", 'module', 'header', FALSE, TRUE, FALSE);
drupal_add_js($path .'icon.js');
drupal_add_js($path .'marker.js');
$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);
}
drupal_add_js($path . $mm .'_marker.js');
if (isset($map['behavior']['locpick']) && $map['behavior']['locpick']) {
drupal_add_js($path .'locpick.js');
}
drupal_add_js($path .'markerloader_static.js');
}
drupal_add_js($path .'shapeloader_static.js');
drupal_add_js($path .'gmap_shapes.js');
}
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,
),
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
);
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;
}
}
/**
* 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');
drupal_add_css($gmap_path .'/gmap.css');
drupal_add_js($gmap_path .'/js/gmap.js');
$mm = variable_get('gmap_mm_type', 'gmap');
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.
drupal_add_js($gmap_path .'/js/icon.js');
Brandon Bergren
committed
}
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/'. $mm .'_marker.js');
drupal_add_js(array('gmap_markermanager' => $mms[$mm]), 'setting');
// @@@
drupal_add_js($gmap_path .'/js/poly.js');
Brandon Bergren
committed
global $language;
$query = array(
'file' => 'api',
'v' => variable_get('gmap_api_version', GMAP_API_VERSION),
'key' => gmap_get_key(),
'hl' => $language->language,
);
Brandon Bergren
committed
drupal_set_html_head('<script src="'. check_url(url('http://maps.google.com/maps', array('query' => $query))) .'" type="text/javascript"></script>');
$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) {
Brandon Bergren
committed
require_once drupal_get_path('module', 'gmap') .'/gmap_parse_macro.inc';
return _gmap_parse_macro($instring, $ver);
}
/**
* Theme a marker popup.
* This will get called for markers embedded in macros.
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
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
392
393
394
395
396
397
398
399
400
401
402
*/
function theme_gmap_marker_popup($label) {
return $label;
}
/**
* 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));
}
$id = 'loc'. $ctr;
$map['id'] = $id;
// This is a locpick map.
$map['behavior']['locpick'] = TRUE;
$element = array(
'#type' => 'gmap',
'#map' => $map['id'],
'#settings' => $map,
);
$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');
}
return theme('gmap', $element);
}
/**
* 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('#settings' => gmap_parse_macro($matches[1][$i], $ver));
$out[1][$i] = theme('gmap', $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.'));
}
}
/**
* Implementation of hook_filter().
*/
function gmap_filter($op, $delta = 0, $format = -1, $text = '') {
switch ($op) {
case 'list':
return (array(0 => t('GMap macro expander')));
case 'name':
return t('Google map filter');
case 'description':
return t('Converts a Google Map macro into the HTML required for inserting a Google Map.');
case 'process':
$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;
}
case 'prepare':
return $text;
case 'no cache':
return TRUE; // @@@ Possibly improve efficiency in the future?
}
}
/**
* Implementation of hook_filter_tips().
*/
function gmap_filter_tips($delta, $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().
*/
function gmap_menu() {
$items['admin/settings/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() {
$contents = '';
// Create the js/ within the files folder.
$jspath = file_create_path('js');
file_check_directory($jspath, FILE_CREATE_DIRECTORY);
$contents .= "// GMap marker image data.\n";
Brandon Bergren
committed
$contents .= "Drupal.gmap.iconpath = ". drupal_to_js(base_path() . variable_get('gmap_markerfiles', drupal_get_path('module', 'gmap') .'/markers')) .";\n";
$contents .= "Drupal.gmap.icondata = ". drupal_to_js(gmap_get_icondata(TRUE)) .";\n";
file_save_data($contents, "$jspath/gmap_markers.js", FILE_EXISTS_REPLACE);
// Also regenerate the cached marker titles array
gmap_get_marker_titles(TRUE);
}
/**
* Implementation of hook_flush_caches().
*/
function gmap_flush_caches() {
gmap_regenerate_markers();
}
/**
* Implementation of hook_elements().
*/
function gmap_elements() {
return array(
'gmap' => array(
'#input' => FALSE, // This isn't a *form* input!!
'#settings' => array_merge(gmap_defaults(), array(
'points' => array(),
'pointsOverlays' => array(),
'lines' => array(),
)),
),
'gmap_macrotext' => array(
'#input' => TRUE,
'#gmap_newtype' => 'textarea',
'#theme' => 'gmap_macrotext',
'gmap_overlay_edit' => array('#input' => TRUE, '#process' => array('process_gmap_overlay_edit')),
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')),
Brandon Bergren
committed
'gmap_align' => array('#input' => TRUE, '#process' => array('process_gmap_align')),
'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')),
);
}
/**
* Generic gmap control #process function.
*/
Brandon Bergren
committed
function process_gmap_control($element, $edit, &$form_state, $complete_form) {
$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, $edit, $form_state, $complete_form);
}
}
}
Brandon Bergren
committed
}
return $element;
}
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
/**
* 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' => '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' => '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' => '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' => '#',
'#disabled' => $isline,
'#attributes' => array('class' => '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' => '%',
'#disabled' => $isline,
'#attributes' => array('class' => 'gmap_style'),
);
unset($element['#input']);
$element += _element_info('fieldset');
return $element;
}
/**
* Theme a gmap_style fieldset.
*/
function theme_gmap_style($element) {
// Fieldsets print #value at the end, so we need to empty it out.
// Otherwise, it puts "Array" at the end of the fieldset.
$element['#value'] = '';
return theme('fieldset', $element, $element['#children']);
}
/**
* Overlay editor #process function.
*/
function process_gmap_overlay_edit($element) {
// Conver the root element into a fieldset.
$element['#type'] = 'fieldset';
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
$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']);
return $element;
}
/**
* Alignment selector #process function.
*/
function process_gmap_align($element) {
$element['#type'] = 'select';
gmap_widget_setup($element, 'align');
$element['#options'] = drupal_map_assoc(array('None', 'Right', 'Left', 'Center'));
$element['#theme'] = 'gmap_align';
return $element;
}
/**
* Address widget #process function.
*/
function process_gmap_address($element) {
$element['#type'] = 'textfield';
gmap_widget_setup($element, 'address');
$element['#theme'] = 'gmap_address';
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;
return $element;
}
/**
* Overlay editor theme function.
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
*/
function theme_gmap_overlay_edit($element) {
$path = drupal_get_path('module', 'gmap');
drupal_add_js($path .'/js/gmap.js');
drupal_add_js($path .'/js/gmap_shapes.js');
drupal_add_js($path .'/js/overlay_edit.js');
return theme('select', $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']);
}
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]);
}
}
}
function theme_gmap_macrotext($element) {
drupal_add_js(drupal_get_path('module', 'gmap') .'/js/macro.js');
// @@@
drupal_add_js(drupal_get_path('module', 'gmap') .'/js/macrobuilder.js');
return theme('textarea', $element);
}
function theme_gmap_address($element) {
drupal_add_js(drupal_get_path('module', 'gmap') .'/js/address.js');
return theme('textfield', $element);
}
function theme_gmap_align($element) {
drupal_add_js(drupal_get_path('module', 'gmap') .'/js/align.js');
return theme('select', $element);
}
/**
* Gmap element theme hook
*/
function theme_gmap($element) {
Brandon Bergren
committed
// Usability: Prevent js errors on first visit to settings page, etc.
// Of course it will still error if the *wrong* key is on file.
if (gmap_get_key() == '') {
return t('Unable to render map: Google Maps API key is missing.');
}
// Track the mapids we've used already.
static $mapids = array();
// Convert from raw map array if needed.
if (!isset($element['#settings'])) {
$element = array(
'#settings' => $element,
);
}
if (isset($element['#map']) && $element['#map']) {
// The default mapid is #map.
$mapid = $element['#map'];
if (isset($element['#settings']['id'])) {
// Settings overrides it.
$mapid = $element['#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 (!$element['#settings']) {
$element['#settings'] = array();
}
// Push the mapid back into #settings.
$element['#settings']['id'] = $mapid;
$mapdefaults = gmap_defaults();
$map = array_merge($mapdefaults, $element['#settings']);
// Styles is a subarray.
if (isset($element['#settings']['styles'])) {
$map['styles'] = array_merge($mapdefaults['styles'], $element['#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':
Brandon Bergren
committed
$element['#attributes']['class'] .= ' gmap-left';
Brandon Bergren
committed
$element['#attributes']['class'] .= ' gmap-right';
break;
case 'center':
case 'centre':
Brandon Bergren
committed
$element['#attributes']['class'] .= ' gmap-center';
}
$style = array();
$style[] = 'width: '. $map['width'];
$style[] = 'height: '. $map['height'];
$element['#attributes']['class'] = trim($element['#attributes']['class'] .' 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']) .'>'. t('Javascript is required to view this map.') .'</div>';
// $map can be manipulated by reference.
foreach (module_implements('gmap') as $module) {
call_user_func_array($module .'_gmap', array('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');
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
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['#settings']['id'])) {
$map = $element['#settings']['id'];
}
}
}
if (!isset($element['#attributes']['class'])) {
$element['#attributes']['class'] = '';
}
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
$element['#attributes']['class'] = trim(implode(' ', array(
$element['#attributes']['class'],
'gmap-control',
'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]++;
return 'gmap-'. $map .'-'. $type . $serial[$map][$type];
}
/**
* Generate a dynamic map identifier.
*/
function gmap_get_auto_mapid() {
static $auto = 0;
$auto++;
return 'auto'. $auto .'map';
}
/**
* 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');
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
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();
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
$data = drupal_http_request('http://maps.google.'. $tld .'/maps/geo?q='. drupal_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);
}
return theme('gmap', array('#settings' => $settings));
}
/**
* Implementation of hook_keys_service(). (from the keys api)
*/
function gmap_keys_service() {
return array(
'gmap' => array(
'name' => t('Gmap'),
'description' => t('Google Maps API Key'),
),
);
}
/**
* Retrieve the Google Maps key that is in use for the site.
*/
function gmap_get_key() {
$key = variable_get('googlemap_api_key', '');
if (module_exists('keys_api')) {
$key = keys_api_get_key('gmap', $_SERVER['HTTP_HOST']);
}
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',
),
),
);
}