gmap.module

Tracking 7.x-2.x branch
  1. drupal
    1. 5 contributions/gmap/gmap.module
    2. 6 contributions/gmap/gmap.module
    3. 7 contributions/gmap/gmap.module

GMap -- Routines to use the Google Maps API in Drupal.

Constants

NameDescription
GMAP_API_VERSIONDefine the Google Maps API version being used.

Functions & methods

NameDescription
gmap_decimalUtility function to allow high-precision decimals to work with the SQL layer. Use concatenation. (Apparently unquoted %s is bad.)
gmap_defaultsGet the defaults for a gmap.
gmap_dimension_validateEnsure a textfield is a valid css dimension string.
gmap_element_infoImplement hook_element_info().
gmap_filter_infoImplement hook_filter_info().
gmap_flush_cachesImplementation of hook_flush_caches().
gmap_geocodeUtility 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…
gmap_get_auto_mapidGenerate a dynamic map identifier.
gmap_get_icondataGet the JSON icon data for all the default markers.
gmap_get_idGet a CSS id for a map and type. Since CSS ids have to be unique, GMap related IDs are assigned by this function.
gmap_get_keyRetrieve the Google Maps key that is in use for the site.
gmap_get_marker_titlesGet the list of marker titles.
gmap_gmapImplementation of hook_gmap().
gmap_keys_serviceImplementation of hook_keys_service(). (from the keys api)
gmap_map_cleanupPerform some normalization on the map object to prevent errors.
gmap_menuImplementation of hook_menu().
gmap_module_invokeInvokes hook_gmap() in every module.
gmap_parse_macroConvert a macro string into a GMap array.
gmap_regenerate_markersRegenerate the markerdata file.
gmap_set_locationLocation chooser utility function.
gmap_simple_mapSimple way to draw a map from inside a theme.
gmap_themeImplementation of hook_theme().
gmap_todimMake sure a string is a valid css dimension.
gmap_views_ajax_data_alterImplementation of hook_views_ajax_data_alter().
gmap_views_pluginsImplementation of hook_views_plugins().
gmap_views_pre_renderImplementation of hook_views_pre_render().
gmap_views_protocolDetermine the site protocol (http or https)
gmap_widget_setupSet up widget. This function will change a form element's ID so it is found by the GMap handlers system.
process_gmap_addressAddress widget #process function.
process_gmap_controlGeneric gmap control #process function.
process_gmap_markerchooserMarker chooser #process function.
process_gmap_overlay_editOverlay editor #process function.
process_gmap_styleStyle fieldset #process function.
template_preprocess_gmap_view_gmapPreprocess function for theme_gmap_view_gmap().
theme_gmapGmap element theme hook
theme_gmap_marker_popupTheme a marker popup. This will get called for markers embedded in macros.
theme_gmap_views_ui_gmapextended
_gmap_base_jsGet the basic js files needed for a GMap.
_gmap_doheaderSet 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.
_gmap_filter_processFilter process callback for gmap_macro.
_gmap_filter_tipsImplement tips callback for gmap_macro.
_gmap_prepareHandle filter preparation.
_gmap_pre_render_mapPre render function to make sure all required JS is available.

File

View source
  1. <?php
  2. /**
  3. * @file
  4. * GMap -- Routines to use the Google Maps API in Drupal.
  5. */
  6. /**
  7. * Define the Google Maps API version being used.
  8. *
  9. * Current minimum version: 2.113
  10. *
  11. * Minimum version last changed on: June 9 2008
  12. *
  13. * Reason: G_SATELLITE_3D_MAP support in gmap_addons. See http://code.google.com/apis/earth/.
  14. *
  15. * See http://groups.google.com/group/Google-Maps-API/web/api-version-changes
  16. * for details on using other version numbers.
  17. */
  18. define('GMAP_API_VERSION', '3');
  19. /**
  20. * Get the defaults for a gmap.
  21. */
  22. function gmap_defaults() {
  23. $defaults = array(
  24. 'width' => '300px',
  25. 'height' => '200px',
  26. 'zoom' => 3,
  27. 'maxzoom' => 14,
  28. 'controltype' => 'Small',
  29. 'align' => 'None',
  30. 'latlong' => '40,0',
  31. 'maptype' => 'Map',
  32. 'mtc' => 'standard',
  33. 'baselayers' => array('Map', 'Satellite', 'Hybrid'),
  34. 'styles' => array(
  35. 'line_default' => array('0000ff', 5, 45, '', 0, 0),
  36. 'poly_default' => array('000000', 3, 25, 'ff0000', 45),
  37. 'highlight_color' => 'ff0000',
  38. ),
  39. 'line_colors' => array('#00cc00', '#ff0000', '#0000ff'),
  40. );
  41. $defaults['behavior'] = array();
  42. $m = array();
  43. $behaviors = gmap_module_invoke('behaviors', $m);
  44. foreach ($behaviors as $k => $v) {
  45. $defaults['behavior'][$k] = $v['default'];
  46. }
  47. $defaults = array_merge($defaults, variable_get('gmap_default', array()));
  48. return $defaults;
  49. }
  50. /**
  51. * Implementation of hook_theme().
  52. */
  53. function gmap_theme() {
  54. return array(
  55. 'gmap_views_ui_gmapextended' => array('render element' => 'form'),
  56. 'views_view_gmap' => array('render element' => 'element'),
  57. 'gmap_views_marker_label' => array('render element' => 'element'),
  58. 'gmap_marker_popup' => array('variables' => array('label' => '')),
  59. // 'gmap_overlay_edit' => array('render element' => 'element'),
  60. // 'gmap_macrotext' => array('render element' => 'element'),
  61. 'gmap_dimension' => array('render element' => 'element'),
  62. // 'gmap_address' => array('render element' => 'element'),
  63. // 'gmap_align' => array('render element' => 'element'),
  64. 'gmap' => array('render element' => 'element'),
  65. );
  66. }
  67. /**
  68. * Invokes hook_gmap() in every module.
  69. *
  70. * We can't use module_invoke_all() because we pass $map by reference.
  71. */
  72. function gmap_module_invoke($op, &$map) {
  73. $return = array();
  74. foreach (module_implements('gmap') as $module) {
  75. $function = $module . '_gmap';
  76. $result = $function($op, $map);
  77. if (isset($result) && is_array($result)) {
  78. $return = array_merge_recursive($return, $result);
  79. }
  80. elseif (isset($result)) {
  81. $return[] = $result;
  82. }
  83. }
  84. return $return;
  85. }
  86. /**
  87. * Implementation of hook_gmap().
  88. */
  89. function gmap_gmap($op, &$map) {
  90. switch ($op) {
  91. case 'macro':
  92. return array(
  93. 'points' => array(
  94. 'multiple' => TRUE,
  95. ),
  96. 'markers' => array(
  97. 'multiple' => TRUE,
  98. ),
  99. 'feed' => array(
  100. 'multiple' => TRUE,
  101. ),
  102. 'style' => array(
  103. 'multiple' => TRUE,
  104. ),
  105. );
  106. case 'pre_theme_map':
  107. $path = drupal_get_path('module', 'gmap') . '/js/';
  108. // Activate markers if needed.
  109. if ((isset($map['behavior']['dynmarkers']) && $map['behavior']['dynmarkers']) || !empty($map['markers'])) {
  110. static $header_set = FALSE;
  111. if (!$header_set) {
  112. $header_set = TRUE;
  113. if (!variable_get('gmap_marker_file', FALSE)) {
  114. gmap_regenerate_markers();
  115. }
  116. }
  117. $mm = variable_get('gmap_mm_type', 'gmap');
  118. // If you really really want to override the marker manager, implement
  119. // this, take $mm by ref, and have fun. --Bdragon
  120. if (function_exists('_gmap_markermanager_override')) {
  121. _gmap_markermanager_override($mm);
  122. }
  123. }
  124. // add required base js
  125. drupal_add_js('http://maps.google.com/maps?file=api&v=2.115&key='.variable_get('googlemap_api_key').'&;hl=en');
  126. drupal_add_js($path . 'gmap.js');
  127. drupal_add_js($path . 'icon.js');
  128. drupal_add_js($path . 'marker.js');
  129. drupal_add_js($path . 'highlight.js');
  130. drupal_add_js(variable_get('file_public_path', conf_path() . '/files') . '/js/gmap_markers.js');
  131. drupal_add_js($path . 'gmap_marker.js');
  132. drupal_add_js($path . 'poly.js');
  133. //$path = drupal_get_path('module', 'gmap') . '/js';
  134. if (isset($map['behavior']['locpick']) && $map['behavior']['locpick']) {
  135. drupal_add_js($path . 'locpick.js');
  136. }
  137. if (!empty($map['markers']) || !empty($map['lines'])) {
  138. drupal_add_js($path . 'markerloader_static.js');
  139. }
  140. if (!empty($map['shapes'])) {
  141. drupal_add_js($path . 'shapeloader_static.js');
  142. drupal_add_js($path . 'gmap_shapes.js');
  143. }
  144. if (isset($map['feed']) && is_array($map['feed'])) {
  145. drupal_add_js(path . 'markerloader_georss.js');
  146. }
  147. break;
  148. case 'macro_multiple':
  149. return array('points', 'markers', 'feed', 'circle', 'rpolygon', 'polygon', 'line', 'style');
  150. case 'behaviors':
  151. return array(
  152. 'locpick' => array(
  153. 'title' => t('Location chooser'),
  154. 'default' => FALSE,
  155. 'help' => t('Used to activate location choosing using a gmap.'),
  156. 'internal' => TRUE,
  157. ),
  158. 'nodrag' => array(
  159. 'title' => t('Disable dragging'),
  160. 'default' => FALSE,
  161. 'help' => t('Remove the ability for the user to drag the map. If dragging is disabled, keyboard shortcuts are implicitly disabled.'),
  162. ),
  163. 'nokeyboard' => array(
  164. 'title' => t('Disable keyboard'),
  165. 'default' => TRUE,
  166. 'help' => t('Disable the keyboard shortcuts.'),
  167. ),
  168. 'nomousezoom' => array(
  169. 'title' => t('Disable mousezoom'),
  170. 'default' => FALSE,
  171. 'help' => t('Disable using the scroll wheel to zoom the map.'),
  172. ),
  173. 'nocontzoom' => array(
  174. 'title' => t('Disable Continuous Zoom'),
  175. 'default' => FALSE,
  176. 'help' => t('Disable dynamically resizing images while waiting for tiles to load when zooming.'),
  177. ),
  178. 'autozoom' => array(
  179. 'title' => t('Use AutoZoom'),
  180. 'default' => FALSE,
  181. 'help' => t('Automatically zoom the map to fit all markers when markers are added.'),
  182. ),
  183. 'dynmarkers' => array(
  184. 'title' => t('Unconditionally enable marker interface'),
  185. 'default' => FALSE,
  186. 'help' => t('Load the marker loader system even if no markers to load are detected. Useful if you are injecting markers from somewhere else.'),
  187. ),
  188. 'overview' => array(
  189. 'title' => t('Enable Overview Map'),
  190. 'default' => FALSE,
  191. 'help' => t('Enable the "overview map" in the bottom right corner.'),
  192. 'previewable' => TRUE,
  193. ),
  194. /* 'notype' => array(
  195. 'title' => t('Disable map type control'),
  196. 'default' => FALSE,
  197. 'help' => t('Removes the map type control from the upper right corner. Recommended for very narrow maps.'),
  198. 'previewable' => TRUE,
  199. ), */
  200. 'collapsehack' => array(
  201. 'title' => t('Work around bugs when maps appear in collapsible fieldsets'),
  202. 'default' => FALSE,
  203. 'help' => t('Enabling this will work around some issues that can occur when maps appear inside collapsible fieldsets.'),
  204. ),
  205. // Note to myself, who keeps forgetting what a scale control actually IS.:
  206. // |------------ 1mi ------------|
  207. 'scale' => array(
  208. 'title' => t('Add scale control to map.'),
  209. 'default' => FALSE,
  210. 'help' => t('Adds a scale control to the map in the default position.'),
  211. 'previewable' => TRUE,
  212. ),
  213. 'extramarkerevents' => array(
  214. 'title' => t('Enable extra marker events.'),
  215. 'default' => FALSE,
  216. 'help' => t('Used for advanced javascript work, this will enable the <em>mouseovermarker</em>, <em>mouseoutmarker</em>, and <em>dblclickmarker</em> events.'),
  217. 'internal' => TRUE,
  218. ),
  219. 'clickableshapes' => array(
  220. 'title' => t('Enable clickable shapes.'),
  221. 'default' => FALSE,
  222. 'help' => t('Used for advanced javascript work, this will enable the <em>clickshape</em> event.'),
  223. 'internal' => TRUE,
  224. ),
  225. 'googlebar' => array(
  226. 'title' => t('Enable Google Bar'),
  227. 'default' => FALSE,
  228. 'help' => t('Enable the "Google Bar" at bottom of the map.'),
  229. 'previewable' => TRUE,
  230. ),
  231. 'highlight' => array(
  232. 'title' => t('Highlight marker on rollover'),
  233. 'default' => FALSE,
  234. 'help' => t('Highlight marker by creating circle on mouse rollover event.'),
  235. 'previewable' => TRUE,
  236. ),
  237. );
  238. break;
  239. case 'baselayers':
  240. $map['Google']['Map'] = array(
  241. 'title' => t('Map: Standard street map.'),
  242. 'default' => TRUE,
  243. 'help' => t('The standard default street map. Internal name: G_NORMAL_MAP'),
  244. );
  245. $map['Google']['Satellite'] = array(
  246. 'title' => t('Satellite: Standard satellite map.'),
  247. 'default' => TRUE,
  248. 'help' => t('Satellite view without street overlay. Internal name: G_SATELLITE_MAP'),
  249. );
  250. $map['Google']['Hybrid'] = array(
  251. 'title' => t('Hybrid: Hybrid satellite map.'),
  252. 'default' => TRUE,
  253. 'help' => t('Satellite view with street overlay. Internal name: G_HYBRID_MAP'),
  254. );
  255. $map['Google']['Physical'] = array(
  256. 'title' => t('Terrain: Physical feature map.'),
  257. 'default' => FALSE,
  258. 'help' => t('Map with physical data (terrain, vegetation.) Internal name: G_PHYSICAL_MAP'),
  259. );
  260. break;
  261. }
  262. }
  263. /**
  264. * Get the basic js files needed for a GMap.
  265. */
  266. function _gmap_base_js() {
  267. $ret = array();
  268. $path = drupal_get_path('module', 'gmap');
  269. $ret[$path . '/js/gmap.js'] = array('weight' => 1);
  270. $ret[$path . '/js/icon.js'] = array('weight' => 2);
  271. /*
  272. $mms = variable_get('gmap_markermanager', array());
  273. if (empty($mms[$mm])) {
  274. $mms[$mm] = array();
  275. }
  276. // If you really really want to override the marker manager, implement
  277. // this, take $mm by ref, and have fun. --Bdragon
  278. if (function_exists('_gmap_markermanager_override')) {
  279. _gmap_markermanager_override($mm, $mms);
  280. }
  281. if ($mm == 'clusterer' || $mm == 'clustermarker') {
  282. // Needed for access to clusterer marker.
  283. drupal_add_js($gmap_path . '/js/icon.js');
  284. }
  285. if (isset($mms[$mm]['filename'])) {
  286. drupal_add_js($gmap_path . '/thirdparty/' . $mms[$mm]['filename']);
  287. }
  288. */
  289. $ret[$path . '/js/marker.js'] = array('weight' => 2);
  290. $ret[$path . '/js/highlight.js'] = array('weight' => 2);
  291. // Add the markermanager.
  292. // @@@TODO Need to allow multiple inclusion and have marker manager set at the map level.
  293. $mm = variable_get('gmap_mm_type', 'gmap');
  294. $mms = variable_get('gmap_markermanager', array());
  295. if (isset($mms[$mm]['filename'])) {
  296. $ret[$path . '/thirdparty/' . $mms[$mm]['filename']] = array('weight' => 3);
  297. }
  298. $ret[$path . '/js/' . $mm . '_marker.js'] = array('weight' => 4);
  299. /*
  300. drupal_add_js(array('gmap_markermanager' => $mms[$mm]), 'setting');
  301. */
  302. $ret[$path . '/js/poly.js'] = array('weight' => 3);
  303. global $language;
  304. $file = 'api/js';
  305. $query = array(
  306. 'v' => variable_get('gmap_api_version', GMAP_API_VERSION),
  307. 'language' => $language->language,
  308. 'sensor' => 'false',
  309. );
  310. if ($query['language'] == 'zh-hans') {
  311. $query['language'] = 'zh-CN';
  312. $ret[url(gmap_views_protocol() . '://ditu.google.cn/maps/'.$file, array('query' => $query))] = array(
  313. 'type' => 'external',
  314. 'weight' => 2
  315. );
  316. }
  317. elseif ($query['language'] == 'zh-hant') {
  318. $query['language'] = 'zh-TW';
  319. $ret[url(gmap_views_protocol() . '://maps.google.com/maps/'.$file, array('query' => $query))] = array(
  320. 'type' => 'external',
  321. 'weight' => 2
  322. );
  323. }
  324. else {
  325. $ret[url(gmap_views_protocol() . '://maps.google.com/maps/'.$file, array('query' => $query))] = array(
  326. 'type' => 'external',
  327. 'weight' => 2
  328. );
  329. }
  330. $ret[file_create_url('public://js/gmap_markers.js')] = array(
  331. 'type' => 'external',
  332. 'weight' => 2,
  333. );
  334. return $ret;
  335. }
  336. /**
  337. * Set up the HTML header for GMap.
  338. * If you are going to include a custom JS file that extends GMap, you probabaly
  339. * want to call this first to ensure that the core js files have been added.
  340. */
  341. function _gmap_doheader() {
  342. static $gmap_initialized = FALSE;
  343. if ($gmap_initialized) {
  344. return;
  345. }
  346. $gmap_path = drupal_get_path('module', 'gmap');
  347. $mm = variable_get('gmap_mm_type', 'gmap');
  348. $mms = variable_get('gmap_markermanager', array());
  349. if (empty($mms[$mm])) {
  350. $mms[$mm] = array();
  351. }
  352. // If you really really want to override the marker manager, implement
  353. // this, take $mm by ref, and have fun. --Bdragon
  354. if (function_exists('_gmap_markermanager_override')) {
  355. _gmap_markermanager_override($mm, $mms);
  356. }
  357. if ($mm == 'clusterer' || $mm == 'clustermarker') {
  358. // Needed for access to clusterer marker.
  359. // drupal_add_js($gmap_path . '/js/icon.js');
  360. }
  361. if (isset($mms[$mm]['filename'])) {
  362. // drupal_add_js($gmap_path . '/thirdparty/' . $mms[$mm]['filename']);
  363. }
  364. // drupal_add_js($gmap_path . '/js/marker.js');
  365. // drupal_add_js($gmap_path . '/js/highlight.js');
  366. // drupal_add_js($gmap_path . '/js/' . $mm . '_marker.js');
  367. drupal_add_js(array('gmap_markermanager' => $mms[$mm]), 'setting');
  368. // @@@
  369. drupal_add_js($gmap_path . '/js/poly.js');
  370. global $language;
  371. $file = 'api/js';
  372. $query = array(
  373. 'v' => variable_get('gmap_api_version', GMAP_API_VERSION),
  374. 'language' => $language->language,
  375. 'sensor' => 'false',
  376. );
  377. if ($query['language'] == 'zh-hans') {
  378. $query['language'] = 'zh-CN';
  379. drupal_add_js(url('http://ditu.google.cn/maps/' . $file, array('query' => $query)));
  380. }
  381. elseif ($query['language'] == 'zh-hant') {
  382. $query['language'] = 'zh-TW';
  383. drupal_add_js(url('http://maps.google.com/maps/' . $file, array('query' => $query)));
  384. }
  385. else {
  386. drupal_add_js(url('http://maps.google.com/maps/' . $file, array('query' => $query)));
  387. }
  388. $gmap_initialized = TRUE;
  389. }
  390. /**
  391. * Convert a macro string into a GMap array.
  392. *
  393. * @param $instring
  394. * Macro to process.
  395. * @param $ver
  396. * Version to treat macro as.
  397. * Set to 1 when processing very old macros, otherwise leave as is.
  398. * @return
  399. * A GMap array.
  400. */
  401. function gmap_parse_macro($instring, $ver = 2) {
  402. require_once drupal_get_path('module', 'gmap') . '/gmap_parse_macro.inc';
  403. return _gmap_parse_macro($instring, $ver);
  404. }
  405. /**
  406. * Theme a marker popup.
  407. * This will get called for markers embedded in macros.
  408. * @ingroup themeable
  409. */
  410. function theme_gmap_marker_popup($vars) {
  411. return $vars['label'];
  412. }
  413. /**
  414. * Location chooser utility function.
  415. *
  416. * Creates a map that can be interactively used to fill a form with a
  417. * location (latitude, longitude and zoom level).
  418. *
  419. * Note: This is a utility function designed for location.module, there is no
  420. * guarantee it will not be removed eventually.
  421. *
  422. * @param $map
  423. * Either a macro to use as the base map for setting a location, or an already set map associative array.
  424. * @param $form
  425. * A formset associative array. Cannot be more than one deep.
  426. * @param $fields
  427. * An associative array for the field names. 'latitude', 'longitude'=>name of respective array, 'address' is optional.
  428. * @return
  429. * A string with the google map code to be inserted onto the page.
  430. *
  431. */
  432. function gmap_set_location($map, &$form, $fields) {
  433. static $ctr = 0;
  434. $ctr++;
  435. if (!is_array($map)) {
  436. $map = array_merge(gmap_defaults(), gmap_parse_macro($map));
  437. }
  438. $id = 'loc' . $ctr;
  439. $map['id'] = $id;
  440. // This is a locpick map.
  441. $map['behavior']['locpick'] = TRUE;
  442. $element = array(
  443. '#type' => 'gmap',
  444. '#map' => $map['id'],
  445. '#gmap_settings' => $map,
  446. );
  447. $form[$fields['latitude']]['#map']=$id;
  448. gmap_widget_setup($form[$fields['latitude']], 'locpick_latitude');
  449. $form[$fields['longitude']]['#map']=$id;
  450. gmap_widget_setup($form[$fields['longitude']], 'locpick_longitude');
  451. if (isset($fields['address'])) {
  452. $form[$fields['address']]['#map'] = $id;
  453. gmap_widget_setup($form[$fields['address']], 'locpick_address');
  454. }
  455. return drupal_render($element);
  456. }
  457. /**
  458. * Handle filter preparation.
  459. */
  460. function _gmap_prepare($intext) {
  461. $out = FALSE;
  462. $matches = array();
  463. preg_match_all('/\[gmap([^\[\]]+ )* \] /x', $intext, $matches);
  464. $i = 0;
  465. while (isset($matches[1][$i])) {
  466. $out[0][$i] = $matches[0][$i];
  467. if ($matches[1][$i][0] == '1') {
  468. $ver = 1;
  469. $matches[1][$i] = substr($matches[0][$i], 1);
  470. }
  471. else {
  472. $ver = 2;
  473. }
  474. $map = array(
  475. '#type' => 'gmap',
  476. '#gmap_settings' => gmap_parse_macro($matches[1][$i], $ver),
  477. );
  478. $out[1][$i] = drupal_render($map);
  479. $i++;
  480. } // endwhile process macro
  481. return $out;
  482. }
  483. /**
  484. * Make sure a string is a valid css dimension.
  485. */
  486. function gmap_todim($instring) {
  487. if (!is_string($instring)) {
  488. return FALSE;
  489. }
  490. $s = strtolower(trim($instring));
  491. $matches = array();
  492. if (preg_match('/^([\d.]+)\s*(em|ex|px|in|cm|mm|pt|pc|%)$/', $s, $matches)) {
  493. return $matches[1] . $matches[2];
  494. }
  495. else {
  496. return FALSE;
  497. }
  498. }
  499. /**
  500. * Ensure a textfield is a valid css dimension string.
  501. */
  502. function gmap_dimension_validate(&$elem, &$form_state) {
  503. $value = gmap_todim($elem['#value']);
  504. if ($value) {
  505. // Normalize the css dimension string.
  506. form_set_value($elem, $value, $form_state);
  507. }
  508. else {
  509. form_error($elem, t('The specified value is not a valid CSS dimension.'));
  510. }
  511. }
  512. /**
  513. * Implement hook_filter_info().
  514. */
  515. function gmap_filter_info() {
  516. $filters['gmap_macro'] = array(
  517. 'title' => t('GMap Macro expander'),
  518. 'description' => t('GMap macros will be displayed as interactive maps.'),
  519. 'process callback' => '_gmap_filter_process',
  520. 'tips callback' => '_gmap_filter_tips',
  521. 'cache' => FALSE, // @@@ FIX ME ALREADY!!!
  522. );
  523. return $filters;
  524. }
  525. /**
  526. * Filter process callback for gmap_macro.
  527. */
  528. function _gmap_filter_process($text, $filter, $format) {
  529. $gmaps = _gmap_prepare($text); //returns an array of $tables[0] = table macro $table[1]= table html
  530. if ($gmaps) { // there are table macros in this node
  531. return str_replace($gmaps[0], $gmaps[1], $text);
  532. }
  533. else {
  534. return $text;
  535. }
  536. }
  537. /**
  538. * Implement tips callback for gmap_macro.
  539. */
  540. function _gmap_filter_tips($filter, $format, $long = FALSE) {
  541. if (user_access('create gmap macro')) { // only display macro if user can create one
  542. return t('Insert Google Map macro.') . '<a href="' . url('map/macro') . '" target="_blank" >' . t('Create a macro') . '</a>';
  543. }
  544. else {
  545. return t('Insert Google Map macro.');
  546. }
  547. }
  548. /**
  549. * Implementation of hook_menu().
  550. */
  551. function gmap_menu() {
  552. $items['admin/config/services/gmap'] = array(
  553. 'title' => 'GMap',
  554. 'description' => 'Configure GMap settings',
  555. 'page callback' => 'drupal_get_form',
  556. 'page arguments' => array('gmap_admin_settings'),
  557. 'file' => 'gmap_settings_ui.inc',
  558. 'access arguments' => array('administer site configuration'),
  559. 'type' => MENU_NORMAL_ITEM,
  560. );
  561. return $items;
  562. }
  563. /**
  564. * Regenerate the markerdata file.
  565. */
  566. function gmap_regenerate_markers() {
  567. $contents = '';
  568. $contents .= "// GMap marker image data.\n";
  569. $contents .= "Drupal.gmap = Drupal.gmap || {};\n";
  570. $contents .= "Drupal.gmap.iconpath = " . drupal_json_encode(base_path() . variable_get('gmap_markerfiles', drupal_get_path('module', 'gmap') . '/markers')) . ";\n";
  571. $contents .= "Drupal.gmap.icondata = " . drupal_json_encode(gmap_get_icondata(TRUE)) . ";\n";
  572. $dir = "public://js/";
  573. // Make sure js/ exists in the files folder.
  574. if (file_prepare_directory($dir, FILE_CREATE_DIRECTORY)) {
  575. $file = file_save_data($contents, 'public://js/gmap_markers.js', FILE_EXISTS_REPLACE);
  576. if (!empty($file)) {
  577. variable_set('gmap_marker_file', $file->fid);
  578. }
  579. }
  580. else {
  581. drupal_set_message(t('GMap is unable to save the marker bundle. Markers will not work!'), 'error');
  582. }
  583. // Also regenerate the cached marker titles array
  584. gmap_get_marker_titles(TRUE);
  585. }
  586. /**
  587. * Implementation of hook_flush_caches().
  588. */
  589. function gmap_flush_caches() {
  590. gmap_regenerate_markers();
  591. }
  592. /**
  593. * Implement hook_element_info().
  594. */
  595. function gmap_element_info() {
  596. $path = drupal_get_path('module', 'gmap');
  597. return array(
  598. 'gmap' => array(
  599. '#input' => FALSE, // This isn't a *form* input!!
  600. '#gmap_settings' => array_merge(gmap_defaults(), array(
  601. 'points' => array(),
  602. 'pointsOverlays' => array(),
  603. 'lines' => array(),
  604. )),
  605. '#attached' => array(
  606. 'css' => array(
  607. $path . '/gmap.css',
  608. ),
  609. 'js' => _gmap_base_js(),
  610. ),
  611. '#pre_render' => array('_gmap_pre_render_map'),
  612. '#theme' => 'gmap',
  613. ),
  614. 'gmap_macrotext' => array(
  615. '#input' => TRUE,
  616. '#gmap_newtype' => 'textarea',
  617. // '#theme' => 'gmap_macrotext',
  618. '#process' => array('process_gmap_control'),
  619. '#attached' => array(
  620. 'js' => array(
  621. "$path/js/macro.js" => array('weight' => 2),
  622. "$path/js/macrobuilder.js" => array('weight' => 2),
  623. ),
  624. ),
  625. '#theme' => 'textarea',
  626. ),
  627. 'gmap_overlay_edit' => array(
  628. '#input' => TRUE,
  629. '#process' => array('process_gmap_overlay_edit'),
  630. '#attached' => array(
  631. 'js' => array(
  632. "$path/js/gmap_shapes.js" => array('weight' => 2),
  633. "$path/js/overlay_edit.js" => array('weight' => 2),
  634. "$path/js/polylineEdit/src/polylineEdit_packed.js" => array('weight' => 3),
  635. "$path/js/polygonEdit/src/polygonEdit_packed.js" => array('weight' =>3),
  636. ),
  637. ),
  638. // '#theme' => 'select',
  639. ),
  640. 'gmap_style' => array('#input' => TRUE, '#tree' => TRUE, '#gmap_style_type' => 'poly', '#process' => array('process_gmap_style')),
  641. 'gmap_address' => array(
  642. '#input' => TRUE,
  643. '#process' => array('process_gmap_address'),
  644. '#attached' => array(
  645. 'js' => array(
  646. "$path/js/address.js" => array('weight' => 2),
  647. ),
  648. ),
  649. '#autocomplete_path' => '',
  650. '#theme' => 'textfield',
  651. ),
  652. 'gmap_latitude' => array('#input' => TRUE, '#gmap_newtype' => 'textfield', '#process' => array('process_gmap_control')),
  653. 'gmap_longitude' => array('#input' => TRUE, '#gmap_newtype' => 'textfield', '#process' => array('process_gmap_control')),
  654. 'gmap_latlon' => array('#input' => TRUE, '#gmap_newtype' => 'textfield', '#process' => array('process_gmap_control')),
  655. 'gmap_markerchooser' => array('#input' => TRUE, '#process' => array('process_gmap_markerchooser')),
  656. 'gmap_dimension' => array('#input' => TRUE, '#gmap_newtype' => 'textfield', '#process' => array('process_gmap_control'), '#element_validate' => array('gmap_dimension_validate')),
  657. );
  658. }
  659. /**
  660. * Pre render function to make sure all required JS is available.
  661. */
  662. function _gmap_pre_render_map($element) {
  663. $path = drupal_get_path('module', 'gmap') . '/js';
  664. if (!isset($element['#gmap_settings'])) {
  665. $element['#gmap_settings'] = $element['#settings'];
  666. }
  667. $map = $element['#gmap_settings'];
  668. if (isset($map['behavior']['locpick']) && $map['behavior']['locpick']) {
  669. $element['#attached']['js']["$path/locpick.js"] = array('weight' => 2);
  670. }
  671. if (!empty($map['markers']) || !empty($map['lines'])) {
  672. $element['#attached']['js']["$path/markerloader_static.js"] = array('weight' => 5);
  673. }
  674. if (!empty($map['shapes'])) {
  675. $element['#attached']['js']["$path/shapeloader_static.js"] = array('weight' => 5);
  676. $element['#attached']['js']["$path/gmap_shapes.js"] = array('weight' => 5);
  677. }
  678. if (isset($map['feed']) && is_array($map['feed'])) {
  679. $element['#attached']['js']["$path/markerloader_georss.js"] = array('weight' => 5);
  680. }
  681. return $element;
  682. }
  683. /**
  684. * Generic gmap control #process function.
  685. */
  686. function process_gmap_control($element, &$form_state, $complete_form) {
  687. $control = substr($element['#type'], 5);
  688. $element['#type'] = $element['#gmap_newtype'];
  689. unset($element['#gmap_newtype']);
  690. gmap_widget_setup($element, $control);
  691. // Attempt to run any #process handlers of our transmogrified type.
  692. // However, if we aren't the only #process handler, we assume someone else
  693. // is taking care of setting up any default handlers.
  694. $chain = FALSE;
  695. if (count($element['#process']) == 1) {
  696. // Unset #process so we can pull in the default.
  697. unset($element['#process']);
  698. $chain = TRUE;
  699. }
  700. // Inherit #input from the new type.
  701. unset($element['#input']);
  702. // Merge in the defaults for the target element type.
  703. $element += element_info($element['#type']);
  704. if (isset($element['#input']) && $element['#input']) {
  705. if (isset($element['#process']) && $chain) {
  706. // Chain process.
  707. foreach ($element['#process'] as $process) {
  708. if (function_exists($process)) {
  709. $form = $process($element, $form_state, $complete_form);
  710. }
  711. }
  712. }
  713. }
  714. return $element;
  715. }
  716. /**
  717. * Style fieldset #process function.
  718. */
  719. function process_gmap_style($element) {
  720. $isline = ($element['#gmap_style_type'] == 'line');
  721. // Stroke color
  722. $element[0] = array(
  723. '#type' => 'textfield',
  724. '#size' => 6,
  725. '#maxlength' => 6,
  726. '#field_prefix' => '#',
  727. '#title' => t('Stroke color'),
  728. '#value' => $element['#value'][0],
  729. '#attributes' => array('class' => array('gmap_style')),
  730. );
  731. // Stroke weight
  732. $element[1] = array(
  733. '#type' => 'textfield',
  734. '#title' => t('Stroke weight'),
  735. '#description' => t('Thickness of line, in pixels.'),
  736. '#size' => 3,
  737. '#maxlength' => 3,
  738. '#field_suffix' => t('px'),
  739. '#value' => $element['#value'][1],
  740. '#attributes' => array('class' => array('gmap_style')),
  741. );
  742. // Stroke opacity
  743. $element[2] = array(
  744. '#type' => 'textfield',
  745. '#title' => t('Stroke opacity'),
  746. '#size' => 3,
  747. '#maxlength' => 3,
  748. '#field_suffix' => '%',
  749. '#value' => $element['#value'][2],
  750. '#attributes' => array('class' => array('gmap_style')),
  751. );
  752. // Fill color
  753. $element[3] = array(
  754. '#type' => 'textfield',
  755. '#title' => t('Fill color'),
  756. '#description' => t('Hex color value for fill color. Example: #<em>00AA33</em>'),
  757. '#size' => 6,
  758. '#maxlength' => 6,
  759. '#field_prefix' => '#',
  760. '#value' => $isline ? '' : $element['#value'][3],
  761. '#disabled' => $isline,
  762. '#attributes' => array('class' => array('gmap_style')),
  763. );
  764. $element[4] = array(
  765. '#type' => 'textfield',
  766. '#title' => t('Fill opacity'),
  767. '#description' => t('Opacity of fill, from 0 to 100%.'),
  768. '#size' => 3,
  769. '#maxlength' => 3,
  770. '#field_suffix' => '%',
  771. '#value' => $isline ? '' : $element['#value'][4],
  772. '#disabled' => $isline,
  773. '#attributes' => array('class' => array('gmap_style')),
  774. );
  775. unset($element['#input']);
  776. $element_info = element_info('fieldset');
  777. $element = array_merge($element, $element_info);
  778. return $element;
  779. }
  780. /**
  781. * Overlay editor #process function.
  782. */
  783. function process_gmap_overlay_edit($element) {
  784. // Conver the root element into a fieldset.
  785. $element['#type'] = 'fieldset';
  786. if (!isset($element['#title'])) {
  787. $element['#title'] = t('Overlay editor');
  788. }
  789. $element['#tree'] = TRUE;
  790. $element['mapclicktype'] = array(
  791. '#type' => 'select',
  792. '#title' => t('Click map'),
  793. '#map' => $element['#map'],
  794. '#options' => array(
  795. 'Points' => t('Points'),
  796. 'Lines' => t('Lines'),
  797. 'Circles' => t('Circles'),
  798. 'GPolygon' => t('Filled Polygons'),
  799. ),
  800. );
  801. gmap_widget_setup($element['mapclicktype'], 'overlayedit_mapclicktype');
  802. $element['markerclicktype'] = array(
  803. '#type' => 'select',
  804. '#title' => t('Click marker / segment'),
  805. '#map' => $element['#map'],
  806. '#options' => array(
  807. 'Remove' => t('Remove'),
  808. // 'updatestyle' => t('Update Styles'),
  809. // 'removestyle' => t('Remove Styles'),
  810. 'Edit Info' => t('Edit Info'),
  811. ),
  812. );
  813. gmap_widget_setup($element['markerclicktype'], 'overlayedit_markerclicktype');
  814. $element['marker'] = array(
  815. '#type' => 'select',
  816. '#map' => $element['#map'],
  817. '#options' => gmap_get_marker_titles(),
  818. '#title' => t('Marker'),
  819. // '#theme' => 'gmap_overlay_edit',
  820. );
  821. gmap_widget_setup($element['marker'], 'overlayedit');
  822. $element['linestyle'] = array(
  823. '#type' => 'gmap_style',
  824. '#title' => t('Line style'),
  825. '#gmap_style_type' => 'line',
  826. '#default_value' => array('0000ff', 5, 45, '', ''), // @@@
  827. );
  828. gmap_widget_setup($element['linestyle'], 'overlayedit_linestyle', $element['#map']);
  829. $element['linestyle']['linestyle_apply'] = array(
  830. '#tree' => FALSE,
  831. '#type' => 'checkbox',
  832. '#title' => t('Use for new and changed lines'),
  833. '#default_value' => FALSE,
  834. );
  835. gmap_widget_setup($element['linestyle']['linestyle_apply'], 'overlayedit_linestyle_apply', $element['#map']);
  836. $element['polystyle'] = array(
  837. '#type' => 'gmap_style',
  838. '#title' => t('Polygon style'),
  839. '#gmap_style_type' => 'poly',
  840. '#default_value' => array('000000', 3, 25, 'ff0000', 45), // @@@
  841. );
  842. gmap_widget_setup($element['polystyle'], 'overlayedit_polystyle', $element['#map']);
  843. $element['polystyle']['polystyle_apply'] = array(
  844. '#tree' => FALSE,
  845. '#type' => 'checkbox',
  846. '#title' => t('Use for new and changed polygons'),
  847. '#default_value' => FALSE,
  848. );
  849. gmap_widget_setup($element['polystyle']['polystyle_apply'], 'overlayedit_polystyle_apply', $element['#map']);
  850. $element += element_info('fieldset');
  851. return $element;
  852. }
  853. /**
  854. * Address widget #process function.
  855. */
  856. function process_gmap_address($element) {
  857. $element['#type'] = 'textfield';
  858. gmap_widget_setup($element, 'address');
  859. $element += element_info('textfield');
  860. return $element;
  861. }
  862. /**
  863. * Marker chooser #process function.
  864. */
  865. function process_gmap_markerchooser($element) {
  866. $element['#type'] = 'select';
  867. $options = gmap_get_marker_titles();
  868. if (!isset($element['#required']) || !$element['#required']) {
  869. $options = array('' => t('Default')) + $options;
  870. }
  871. $element['#options'] = $options;
  872. $element += element_info('select');
  873. return $element;
  874. }
  875. /**
  876. * Perform some normalization on the map object
  877. * to prevent errors.
  878. */
  879. function gmap_map_cleanup(&$map) {
  880. // Google is picky about this one.
  881. $map['zoom'] = (int)$map['zoom'];
  882. // Normalize latitude / longitude
  883. if ($map['latlong']) {
  884. $map['latlon'] = $map['latlong'];
  885. unset($map['latlong']);
  886. }
  887. // Normalize extent.
  888. if (isset($map['extent']) && is_string($map['extent'])) {
  889. $tmp = explode(',', $map['extent']);
  890. if (count($tmp) == 4) {
  891. // String form of extent has x,y, but native array form is lat,lon, so need to flip them a bit.
  892. $map['extent'] = array(array($tmp[1], $tmp[0]), array($tmp[3], $tmp[2]));
  893. }
  894. else {
  895. // Extent was unparseable, don't pass it.
  896. unset($map['extent']);
  897. }
  898. }
  899. if (isset($map['latlon']) && (!isset($map['latitude']) || !isset($map['longitude']))) {
  900. list($map['latitude'], $map['longitude']) = explode(',', $map['latlon']);
  901. }
  902. unset($map['latlon']);
  903. foreach ($map['baselayers'] as $k => $v) {
  904. if (!$v) {
  905. unset($map['baselayers'][$k]);
  906. }
  907. }
  908. }
  909. /**
  910. * Gmap element theme hook
  911. */
  912. function theme_gmap($variables) {
  913. $element = $variables['element'];
  914. // Track the mapids we've used already.
  915. static $mapids = array();
  916. _gmap_doheader();
  917. $mapid = FALSE;
  918. if (isset($element['#map']) && $element['#map']) {
  919. // The default mapid is #map.
  920. $mapid = $element['#map'];
  921. }
  922. if (isset($element['#gmap_settings']['id'])) {
  923. // Settings overrides it.
  924. $mapid = $element['#gmap_settings']['id'];
  925. }
  926. if (!$mapid) {
  927. // Hmm, no mapid. Generate one.
  928. $mapid = gmap_get_auto_mapid();
  929. }
  930. // Push the mapid back into #map.
  931. $element['#map'] = $mapid;
  932. gmap_widget_setup($element, 'gmap', $mapid);
  933. if (!isset($element['#gmap_settings'])) {
  934. $element['#gmap_settings'] = array();
  935. }
  936. // Push the mapid back into #gmap_settings.
  937. $element['#gmap_settings']['id'] = $mapid;
  938. $mapdefaults = gmap_defaults();
  939. $map = array_merge($mapdefaults, $element['#gmap_settings']);
  940. // Styles is a subarray.
  941. if (isset($element['#gmap_settings']['styles'])) {
  942. $map['styles'] = array_merge($mapdefaults['styles'], $element['#gmap_settings']['styles']);
  943. }
  944. gmap_map_cleanup($map);
  945. // Add a class around map bubble contents.
  946. // @@@ Bdragon sez: Becw, this doesn't belong here. Theming needs to get fixed instead..
  947. if (isset($map['markers'])) {
  948. foreach ($map['markers'] as $i => $marker) {
  949. if (isset($marker['text'])) {
  950. $map['markers'][$i]['text'] = '<div class="gmap-popup">' . $marker['text'] . '</div>';
  951. }
  952. }
  953. }
  954. switch (strtolower($map['align'])) {
  955. case 'left':
  956. $element['#attributes']['class'][] = 'gmap-left';
  957. break;
  958. case 'right':
  959. $element['#attributes']['class'][] = 'gmap-right';
  960. break;
  961. case 'center':
  962. case 'centre':
  963. $element['#attributes']['class'][] = 'gmap-center';
  964. }
  965. $style = array();
  966. $style[] = 'width: ' . $map['width'];
  967. $style[] = 'height: ' . $map['height'];
  968. $element['#attributes']['class'] = array_merge($element['#attributes']['class'], array('gmap', 'gmap-map', 'gmap-' . $mapid . '-gmap'));
  969. // Some markup parsers (IE) don't handle empty inners well. Use the space to let users know javascript is required.
  970. // @@@ Bevan sez: Google static maps could be useful here.
  971. // @@@ Bdragon sez: Yeah, would be nice, but hard to guarantee functionality. Not everyone uses the static markerloader.
  972. $o = '<div style="' . implode('; ', $style) . ';" id="' . $element['#id'] . '"' . drupal_attributes($element['#attributes']) . '>' . t('Javascript is required to view this map.') . '</div>';
  973. gmap_module_invoke('pre_theme_map', $map);
  974. if (isset($mapids[$element['#map']])) {
  975. drupal_set_message(t('Duplicate map detected! GMap does not support multiplexing maps onto one MapID! GMap MapID: %mapid', array('%mapid' => $element['#map'])), 'error');
  976. // Return the div anyway. All but one map for a given id will be a graymap,
  977. // because obj.map gets stomped when trying to multiplex maps!
  978. return $o;
  979. }
  980. $mapids[$element['#map']] = TRUE;
  981. // Put map data in a setting.
  982. drupal_add_js(array('gmap' => array($element['#map'] => $map)), 'setting');
  983. return $o;
  984. }
  985. /**
  986. * Set up widget.
  987. * This function will change a form element's ID so it is found
  988. * by the GMap handlers system.
  989. * @param &$element
  990. * The form element to modify.
  991. * @param $type
  992. * The gmap widget type to map to.
  993. * @param $map
  994. * The map id. If not defined, $element['#map'] will be used.
  995. * @return
  996. * None.
  997. */
  998. function gmap_widget_setup(&$element, $type, $map=NULL) {
  999. if (!$map) {
  1000. if (isset($element['#map'])) {
  1001. $map = $element['#map'];
  1002. }
  1003. else {
  1004. // Hmm, missing #map. Try to figure it out.
  1005. if (isset($element['#gmap_settings']['id'])) {
  1006. $map = $element['#gmap_settings']['id'];
  1007. }
  1008. }
  1009. }
  1010. if (!isset($element['#attributes']['class'])) {
  1011. $element['#attributes']['class'] = array();
  1012. }
  1013. $element['#attributes']['class'] = array_merge($element['#attributes']['class'], array(
  1014. 'gmap-control',
  1015. 'gmap-' . $type,
  1016. ));
  1017. $element['#id'] = gmap_get_id($map, $type);
  1018. $element['#map'] = $map;
  1019. }
  1020. /**
  1021. * Get a CSS id for a map and type.
  1022. * Since CSS ids have to be unique, GMap related IDs are assigned by
  1023. * this function.
  1024. */
  1025. function gmap_get_id($map, $type) {
  1026. static $serial = array();
  1027. if (!isset($serial[$map])) {
  1028. $serial[$map] = array();
  1029. }
  1030. if (!isset($serial[$map][$type])) {
  1031. $serial[$map][$type] = -1;
  1032. }
  1033. $serial[$map][$type]++;
  1034. return 'gmap-' . $map . '-' . $type . $serial[$map][$type];
  1035. }
  1036. /**
  1037. * Generate a dynamic map identifier.
  1038. */
  1039. function gmap_get_auto_mapid() {
  1040. static $auto = 0;
  1041. $auto++;
  1042. return 'auto' . $auto . 'map';
  1043. }
  1044. /**
  1045. * Get the list of marker titles.
  1046. */
  1047. function gmap_get_marker_titles($reset = FALSE) {
  1048. static $titles;
  1049. if (!$reset) {
  1050. if (is_array($titles)) {
  1051. return $titles;
  1052. }
  1053. $cached = cache_get('gmap_marker_titles', 'cache');
  1054. if (!empty($cached)) {
  1055. $titles = $cached->data;
  1056. if (is_array($titles)) {
  1057. return $titles;
  1058. }
  1059. }
  1060. }
  1061. require_once(drupal_get_path('module', 'gmap') . '/gmap_markerinfo.inc');
  1062. $titles = _gmap_get_marker_titles();
  1063. cache_set('gmap_marker_titles', $titles, 'cache');
  1064. return $titles;
  1065. }
  1066. /**
  1067. * Get the JSON icon data for all the default markers.
  1068. */
  1069. function gmap_get_icondata($reset = FALSE) {
  1070. static $icons;
  1071. if (is_array($icons) && !$reset) {
  1072. return $icons;
  1073. }
  1074. $icons = cache_get('gmap_icondata');
  1075. if ($icons) {
  1076. $icons = $icons->data;
  1077. }
  1078. if ($reset || !$icons) {
  1079. require_once(drupal_get_path('module', 'gmap') . '/gmap_markerinfo.inc');
  1080. $icons = _gmap_get_icondata();
  1081. }
  1082. cache_set('gmap_icondata', $icons, 'cache');
  1083. return $icons;
  1084. }
  1085. /**
  1086. * Utility function to allow high-precision decimals to work with the SQL layer.
  1087. * Use concatenation. (Apparently unquoted %s is bad.)
  1088. */
  1089. function gmap_decimal($num) {
  1090. // Paraphrased from postgresql documentation:
  1091. //
  1092. // Numbers in SQL can be in one of these forms:
  1093. // digits
  1094. // digits.[digits][e[+-]digits]
  1095. // [digits].digits[e[+-]digits]
  1096. // digitse[+-]digits
  1097. // where "digits" is one or more decimal digits.
  1098. // Trim extra whitespace
  1099. $num = trim($num);
  1100. // Check if we're in an acceptable form.
  1101. if (preg_match('/^[+\-]?((\d+)|(\d+\.\d*)|(\d*\.\d+))(e[+\-]?\d+)?$/', $num)===1) {
  1102. // Good, we can pass that right along.
  1103. return $num;
  1104. }
  1105. // Otherwise, cast to float, possibly losing precision.
  1106. return (float) $num;
  1107. }
  1108. /**
  1109. * Utility function to use the google maps geocoder server side.
  1110. * This is an easy, quick way to geocode a single address.
  1111. * Note: This is a REMOTE CALL TO GOOGLE. Do NOT use this where performance matters,
  1112. * as it could possibly take several seconds for this function to return.
  1113. * See http://www.google.com/apis/maps/documentation/reference.html#GGeoStatusCode
  1114. * for a description of the possible status codes.
  1115. */
  1116. function gmap_geocode($address, $tld = 'com') {
  1117. $key = gmap_get_key();
  1118. $data = drupal_http_request(gmap_views_protocol() . '://maps.google.' . $tld . '/maps/geo?q=' . urlencode($address) . '&output=csv&key=' . $key);
  1119. if ($data->code == 200) {
  1120. $r = explode(',', $data->data);
  1121. return array(
  1122. 'status' => (int)$r[0],
  1123. 'accuracy' => (int)$r[1],
  1124. 'latitude' => $r[2],
  1125. 'longitude' => $r[3],
  1126. );
  1127. }
  1128. // Non 200 is G_GEO_SERVER_ERROR (500).
  1129. return array(
  1130. 'status' => 500,
  1131. );
  1132. }
  1133. /**
  1134. * Simple way to draw a map from inside a theme.
  1135. * @param $latitude
  1136. * Latitude of marker.
  1137. * @param $longitude
  1138. * Longitude of marker.
  1139. * @param $markername
  1140. * Marker to use.
  1141. * '' will fall back to google's default marker.
  1142. * @param $info
  1143. * What to show in the bubble when the marker is clicked.
  1144. * Leave blank if you don't want a bubble.
  1145. * @param $zoom
  1146. * Map zoom.
  1147. * 'default' will use the default zoom from the settings page.
  1148. * 3 is usually a good value to use.
  1149. * @param $width
  1150. * Map width.
  1151. * 'default' will use the default width from the settings page.
  1152. * @param $height
  1153. * Map height.
  1154. * 'default' will use the default height from the settings page.
  1155. * @param $autoshow
  1156. * If set to TRUE, automatically show the marker bubble.
  1157. * @param $map
  1158. * Override parts of the map array.
  1159. * If you need to do much with this, you should probabaly be putting together
  1160. * the map array manually.
  1161. */
  1162. function gmap_simple_map($latitude, $longitude, $markername = '', $info = '', $zoom = 'auto', $width = 'default', $height = 'default', $autoshow = FALSE, $map = array()) {
  1163. $settings = array(
  1164. 'id' => gmap_get_auto_mapid(),
  1165. 'latitude' => $latitude, // Center the map
  1166. 'longitude' => $longitude, // on the marker.
  1167. );
  1168. if ($zoom != 'default') {
  1169. $settings['zoom'] = $zoom;
  1170. }
  1171. if ($width != 'default') {
  1172. $settings['width'] = $width;
  1173. }
  1174. if ($height != 'default') {
  1175. $settings['height'] = $height;
  1176. }
  1177. $settings['markers'] = array(array(
  1178. 'latitude' => $latitude,
  1179. 'longitude' => $longitude,
  1180. 'markername' => $markername,
  1181. 'offset' => 0,
  1182. ));
  1183. if (!empty($info)) {
  1184. $settings['markers'][0]['text'] = $info;
  1185. }
  1186. if ($autoshow) {
  1187. $settings['markers'][0]['autoclick'] = TRUE;
  1188. }
  1189. if (!empty($map)) {
  1190. $settings = array_merge($settings, $map);
  1191. }
  1192. $element = array(
  1193. '#type' => 'gmap',
  1194. '#gmap_settings' => $settings,
  1195. );
  1196. return drupal_render($element);
  1197. }
  1198. /**
  1199. * Implementation of hook_keys_service(). (from the keys api)
  1200. */
  1201. function gmap_keys_service() {
  1202. // @@@ Remove after everyone has upgraded.
  1203. if (module_exists('keys_api')) {
  1204. return array(
  1205. 'gmap' => array(
  1206. 'name' => t('Gmap'),
  1207. 'description' => t('Google Maps API Key'),
  1208. ),
  1209. );
  1210. }
  1211. elseif (module_exists('keys')) {
  1212. // @greenSkin:
  1213. // What is your reasoning behind predefining this?
  1214. // I'll avoid overriding you for now, but this seems rather arbitrary.
  1215. // Reference: http://drupal.org/cvs?commit=310498
  1216. // Probe keys to determine if it is defining our key for us.
  1217. $test = array();
  1218. if (function_exists('keys_keys_service')) {
  1219. $test = keys_keys_service();
  1220. }
  1221. if (!isset($test['google_maps'])) {
  1222. // Be forward compatible with future versions of keys api
  1223. // that no longer define it.
  1224. return array(
  1225. 'google_maps' => array(
  1226. 'name' => t('Google Maps'),
  1227. 'description' => t('Google Maps API Key'),
  1228. ),
  1229. );
  1230. }
  1231. }
  1232. }
  1233. /**
  1234. * Retrieve the Google Maps key that is in use for the site.
  1235. */
  1236. function gmap_get_key() {
  1237. $key = variable_get('googlemap_api_key', '');
  1238. if (module_exists('keys_api')) {
  1239. $key = keys_api_get_key('gmap', $_SERVER['HTTP_HOST']);
  1240. }
  1241. elseif (module_exists('keys')) {
  1242. $key = keys_get_key('google_maps');
  1243. }
  1244. return $key;
  1245. }
  1246. /**
  1247. * Implementation of hook_views_plugins().
  1248. */
  1249. function gmap_views_plugins() {
  1250. return array(
  1251. 'module' => 'gmap',
  1252. 'style' => array(
  1253. 'gmap' => array(
  1254. 'title' => t('GMap'),
  1255. 'help' => t('Displays rows as a map.'),
  1256. 'handler' => 'gmap_plugin_style_gmap',
  1257. 'theme' => 'gmap_view_gmap',
  1258. 'uses row plugin' => TRUE,
  1259. 'uses grouping' => TRUE,
  1260. 'uses options' => TRUE,
  1261. 'type' => 'normal',
  1262. ),
  1263. 'gmapextended' => array(
  1264. 'title' => t('Extended GMap'),
  1265. 'help' => t('Displays a map of markers.'),
  1266. 'handler' => 'gmap_plugin_style_gmapextended',
  1267. 'theme' => 'gmap_views_view_gmapextended',
  1268. 'uses row plugin' => TRUE,
  1269. 'uses fields' => TRUE,
  1270. 'uses options' => TRUE,
  1271. //'uses grouping' => TRUE,
  1272. 'type' => 'normal',
  1273. ),
  1274. ),
  1275. );
  1276. }
  1277. /**
  1278. * Implementation of hook_views_pre_render().
  1279. */
  1280. function gmap_views_pre_render(&$view) {
  1281. static $gmap_processed;
  1282. // Add js only for gmap style views with ajax and not already processed.
  1283. if (($view->plugin_name != 'gmap' && $view->plugin_name != 'gmapextended')
  1284. || !$view->use_ajax || $gmap_processed) {
  1285. return;
  1286. }
  1287. // Mark the view as already processed.
  1288. $gmap_processed = TRUE;
  1289. // Add js with new views callback.
  1290. drupal_add_js(drupal_get_path('module', 'gmap') . '/js/gmap_views_ajax.js', array('group' => JS_DEFAULT));
  1291. }
  1292. /**
  1293. * Implementation of hook_views_ajax_data_alter().
  1294. */
  1295. function gmap_views_ajax_data_alter(&$commands, $view) {
  1296. // Add js callback only with gmap style plugins and with ajax.
  1297. $plugin_styles = array ($view->plugin_name);
  1298. foreach ($view->display as $display)
  1299. $plugin_styles[] = $display->display_options['style_plugin'];
  1300. if (! (in_array ('gmap', $plugin_styles) || in_array ('gmap', $plugin_styles)))
  1301. return;
  1302. // Find the JQuery selector for the view's wrapper in the DOM
  1303. $target = '';
  1304. foreach ($commands as $command) {
  1305. if ($command['command'] == 'insert') {
  1306. $target = $command['selector'];
  1307. }
  1308. }
  1309. $command = array('command' => 'gmapAjaxViewsFix', 'target' => $target);
  1310. // Save settings.
  1311. $js = drupal_add_js(NULL, array('scope' => 'header'));
  1312. $command['settings'] = $js['settings']['data'];
  1313. $commands[] = $command;
  1314. }
  1315. function theme_gmap_views_ui_gmapextended($variables) {
  1316. $form = $variables['form'];
  1317. $output = drupal_render($form['description_markup']);
  1318. $header = array(
  1319. t('Field'),
  1320. t('Purpose'),
  1321. t('Separator'),
  1322. /* array(
  1323. 'data' => t('Sortable'),
  1324. 'align' => 'center',
  1325. ),
  1326. array(
  1327. 'data' => t('Default sort'),
  1328. 'align' => 'center',
  1329. ),*/
  1330. );
  1331. $rows = array();
  1332. foreach (element_children($form['field_purposes']) as $id) {
  1333. $row = array();
  1334. $row[] = drupal_render($form['info'][$id]['name']);
  1335. $row[] = drupal_render($form['field_purposes'][$id]);
  1336. $row[] = drupal_render($form['info'][$id]['separator']);
  1337. $rows[] = $row;
  1338. }
  1339. // Add the special 'None' row.
  1340. // $rows[] = array(t('None'), '', '', '', array('align' => 'center', 'data' => drupal_render($form['default'][-1])));
  1341. $output .= theme('table', array('header' => $header, 'rows' => $rows));
  1342. $output .= drupal_render_children($form);
  1343. return $output;
  1344. }
  1345. /**
  1346. * Preprocess function for theme_gmap_view_gmap().
  1347. */
  1348. function template_preprocess_gmap_view_gmap(&$vars) {
  1349. $vars['map_object'] = $vars['rows'];
  1350. // Rows is actually our map object.
  1351. unset($vars['rows']);
  1352. $vars['map_element'] = array(
  1353. '#type' => 'gmap',
  1354. '#gmap_settings' => $vars['map_object'],
  1355. );
  1356. // Theme the map.
  1357. $vars['map'] = drupal_render($vars['map_element']);
  1358. }
  1359. /**
  1360. * Determine the site protocol (http or https)
  1361. */
  1362. function gmap_views_protocol() {
  1363. return (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http';
  1364. }