'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; } /** * 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, ), ); case 'pre_theme_map': $path = drupal_get_path('module', 'gmap') .'/js/'; // Activate markers if needed if ($map['behavior']['dynmarkers'] || !empty($map['markers'])) { // The marker data is not a real file. We need to respect // clean url capabilities here. if (variable_get('clean_url', '0')) { drupal_add_js('map/markerdata.js'); } else { drupal_add_js('?q=map/markerdata.js'); } drupal_add_js($path .'icon.js'); drupal_add_js($path .'marker.js'); drupal_add_js($path . variable_get('gmap_mm_type', 'gmap') .'_marker.js'); } if ($map['behavior']['locpick']) { drupal_add_js($path .'locpick.js'); } if (variable_get('gmap_load_zoom_plugin', TRUE) && !$map['behavior']['nomousezoom']) { drupal_add_js(drupal_get_path('module', 'gmap') .'/thirdparty/mousewheel.js'); } if ($map['markers'] || $map['lines']) { drupal_add_js($path .'markerloader_static.js'); } if ($map['shapes']) { drupal_add_js($path .'shapeloader_static.js'); drupal_add_js($path .'gmap_shapes.js'); } if (is_array($map['feed'])) { drupal_add_js($path .'markerloader_georss.js'); } break; case 'macro_multiple': return array('points', 'markers', 'feed', 'circle', 'rpolygon', 'polygon', 'line'); 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.'), ), '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, ), ); 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. */ 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'); if ($mm=='clusterer') { drupal_add_js($gmap_path .'/js/icon.js'); drupal_add_js($gmap_path .'/thirdparty/Clusterer2.js'); } drupal_add_js($gmap_path .'/js/marker.js'); drupal_add_js($gmap_path .'/js/'. $mm .'_marker.js'); $mms = variable_get('gmap_markermanager', array()); if (empty($mms[$mm])) { $mms[$mm] = array(); } drupal_add_js(array('gmap_markermanager' => $mms[$mm]), 'setting'); // @@@ drupal_add_js($gmap_path .'/js/poly.js'); $key = variable_get('googlemap_api_key', ''); if (module_exists('keys_api')) { $key = keys_api_get_key('gmap', $_SERVER['HTTP_HOST']); } drupal_set_html_head(''); drupal_add_js(array( 'gmap_init' => array( 'querypath' => base_path() .'map/query', ), ), 'setting'); $gmap_initialized = TRUE; } /** * Parse a macro style definition. * Example: #111111/1/100/#111111/1 */ function _gmap_parse_style($style) { $styles = explode('/', $style); // @@@ Todo: Fix up old xmaps stuff. Possibly detect by looking for array length 7? // Strip off # signs, they get added by code. if (isset($styles[0]) && substr($styles[0], 0, 1) == '#') { $styles[0] = substr($styles[0], 1); } if (isset($styles[3]) && substr($styles[3], 0, 1) == '#') { $styles[3] = substr($styles[3], 1); } // Assume anything > 0 and < 1.1 was an old representation. if ($styles[2] > 0 && $styles[2] < 1.1) { $styles[2] = $styles[2] * 100; } if (isset($styles[4])) { if ($styles[4] > 0 && $styles[4] < 1.1) { $styles[4] = $styles[4] * 100; } } return $styles; } /** * 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) { // Get a list of keys that are "multiple." $m = array(); $multiple = module_invoke_all('gmap', 'macro_multiple', $m); // Remove leading and trailing tags if (substr(trim($instring), -1)==']') { $instring = substr(trim($instring), 0, -1); } if (substr($instring, 0, 5)=='[gmap') { $instring = substr($instring, 6); } // Chop the macro into an array $temp = explode('|', $instring); $m = array(); foreach ($temp as $row) { $offset = strpos($row, '='); if ($offset !== FALSE) { $k = trim(substr($row, 0, $offset)); $r = trim(substr($row, $offset+1)); if (in_array($k, $multiple)) { // Things that can appear multiple times if (!isset($m[$k])) { $m[$k] = array(); } $m[$k][] = $r; } else { $m[$k] = $r; } } } // Synonyms if ($m['type']) { $m['maptype'] = $m['type']; unset($m['type']); } if ($m['control']) { $m['controltype'] = $m['control']; unset($m['control']); } if (is_array($m['feed'])) { foreach ($m['feed'] as $k => $v) { $temp = explode('::', $v); // Normalize url if (substr($temp[1], 0, 1) == '/') { $temp[1] = substr($temp[1], 1); } $temp[1] = url($temp[1]); $m['feed'][$k] = array( 'markername' => $temp[0], 'url' => $temp[1], ); } } // Merge points and markers if (!is_array($m['points'])) $m['points'] = array(); if (!is_array($m['markers'])) $m['markers'] = array(); $m['markers-temp'] = array_merge($m['points'], $m['markers']); unset($m['points']); unset($m['markers']); // all shapes in 1 array if ($m['circle']) { foreach ($m['circle'] as $shape) { $s = array('type' => 'circle'); $cp = strpos($shape, ':'); if ($cp !== FALSE) { $stylestr = substr($shape, 0, $cp); $s['style'] = _gmap_parse_style($stylestr); $shape = substr($shape, $cp+1); } $tmp = explode('+', $shape); $s['radius'] = $tmp[1] ? $tmp[1] : 100; if ($tmp[2]) $s['numpoints'] = trim($tmp[2]); $tmp = _gmap_str2coord($tmp[0]); $s['center'] = $tmp[0]; $m['shapes'][] = $s; } unset($m['circle']); } // Fixup legacy lines. if ($m['line1']) { if (!isset($m['line'])) $m['line'] = array(); $m['line'][] = GMAP_LINECOLOR1 .':'. $m['line1']; unset($m['line1']); } if ($m['line2']) { if (!isset($m['line'])) $m['line'] = array(); $m['line'][] = GMAP_LINECOLOR2 .':'. $m['line3']; unset($m['line2']); } if ($m['line3']) { if (!isset($m['line'])) $m['line'] = array(); $m['line'][] = GMAP_LINECOLOR3 .':'. $m['line3']; unset($m['line3']); } if ($m['line']) { foreach ($m['line'] as $shape) { $s = array('type' => 'line'); $cp = strpos($shape, ':'); if ($cp != FALSE) { $stylestr = substr($shape, 0, $cp); $s['style'] = _gmap_parse_style($stylestr); $shape = substr($shape, $cp+1); } $s['points'] = _gmap_str2coord($shape); $m['shapes'][] = $s; } unset($m['line']); } if ($m['rpolygon']) { foreach ($m['rpolygon'] as $shape) { $s = array('type' => 'rpolygon'); $cp = strpos($shape, ':'); if ($cp !== FALSE) { $stylestr = substr($shape, 0, $cp); $s['style'] = _gmap_parse_style($stylestr); $shape = substr($shape, $cp+1); } $tmp = explode('+', $shape); if ($tmp[2]) { $s['numpoints'] = (int)trim($tmp[2]); $tmp = array_slice($tmp, 0, 2); } $shape = implode('+', $tmp); $tmp = _gmap_str2coord($shape); $s['center'] = $tmp[0]; $s['point2'] = $tmp[1]; $m['shapes'][] = $s; } unset($m['rpolygon']); } if ($m['polygon']) { foreach ($m['polygon'] as $shape) { $s = array('type' => 'polygon'); $cp = strpos($shape, ':'); if ($cp !== FALSE) { $stylestr = substr($shape, 0, $cp); $s['style'] = _gmap_parse_style($stylestr); $shape = substr($shape, $cp+1); } $s['points'] = _gmap_str2coord($shape); $m['shapes'][] = $s; } unset($m['polygon']); } // Version 1 -> 2 conversion if ($ver == 1) { // Zoom is flipped if ($m['zoom']) { $m['zoom'] = 18 - $m['zoom']; if ($m['zoom'] < 1) { $m['zoom'] = 1; } } } // Center -> latitude and longitude if ($m['center']) { list($m['latitude'], $m['longitude']) = explode(',', $m['center']); unset($m['center']); } // Behavior if ($m['behaviour']) { $m['behavior'] = $m['behaviour']; unset($m['behaviour']); } if ($m['behavior']) { $m['behavior-temp'] = explode(' ', $m['behavior']); $m['behavior'] = array(); foreach ($m['behavior-temp'] as $v) { $m['behavior'][substr($v, 1)] = (substr($v, 0, 1) == '+') ? TRUE : FALSE; } unset($m['behavior-temp']); } // tcontrol now is mtc. if ($m['tcontrol']) { if (strtolower(trim($m['tcontrol'])) == 'on') { $m['mtc'] = 'standard'; } else { $m['mtc'] = 'none'; } unset($m['tcontrol']); } // notype also controls mtc. if (isset($m['behavior']['notype'])) { if ($m['behavior']['notype']) { $m['mtc']= 'none'; } unset($m['behavior']['notype']); } // Stuff that was converted to behavior flags // Scale control. if ($m['scontrol']) { if (strtolower(trim($m['scontrol'])) == 'on') { $m['behavior']['scale'] = TRUE; } else { $m['behavior']['scale'] = FALSE; } unset($m['scontrol']); } // Draggability. if ($m['drag']) { if (strtolower(trim($m['drag'])) == 'yes') { $m['behavior']['nodrag'] = FALSE; } else { $m['behavior']['nodrag'] = TRUE; } unset($m['drag']); } // Markers fixup foreach ($m['markers-temp'] as $t) { unset($markername); // Named? if (strpos($t, '::')) { // Single : gets handled below. list($markername, $t) = explode('::', $t, 2); } // Break down into points $points = explode('+', $t); $offset = -1; foreach ($points as $point) { $marker = array(); $offset++; $marker['options'] = array(); // Labelled? // @@@ Gmap allows both a tooltip and a popup, how to represent? if (strpos($point, ':')) { list($point, $marker['text']) = explode(':', $point, 2); $marker['text'] = theme('gmap_marker_popup', $marker['text']); } if (strpos($point, '%')) { list($point, $addons) = explode('%', $point, 2); $motemp = explode('%', $addons); foreach ($motemp as $option) { $marker['options'][trim($option)] = TRUE; } } list($marker['latitude'], $marker['longitude']) = explode(',', $point, 2); // Named markers get an offset too. if (isset($markername)) { $marker['markername'] = $markername; $marker['offset'] = $offset; } $m['markers'][] = $marker; } } unset($m['markers-temp']); // Assign an id if one wasn't specified. if (!$m['id']) { $m['id'] = gmap_get_auto_mapid(); } // The macro can now be manipulated by reference. foreach (module_implements('gmap') as $module) { $additions = call_user_func_array($module .'_gmap', array('parse_macro', &$m)); if (!empty($additions)) { foreach ($additions as $k => $v) { $m[$k] = $v; } } } return $m; } /** * Parse "x.xxxxx , y.yyyyyy (+ x.xxxxx, y.yyyyy ...)" into an array of points. */ function _gmap_str2coord($str) { // Explode along + axis $arr = explode('+', $str); // Explode along , axis $points = array(); foreach ($arr as $pt) { list($lat, $lon) = explode(',', $pt); $points[] = array((float)trim($lat), (float)trim($lon)); } return $points; } /** * Theme a marker popup. * This will get called for markers embedded in macros. */ 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) { $s = strtolower($instring); $matches = array(); 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) { if (!gmap_todim($elem['#value'])) { 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 filter'))); 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) { if (user_access('create macro')) { // only display macro if user can create one return t('Insert Google Map macro.') .''. t('Create a macro') .''; } else { return t('Insert Google Map macro.'); } } /** * Implementation of hook_menu(). */ function gmap_menu($may_cache) { if ($may_cache) { $items = array(); /* $items[] = array( 'path' => 'map', 'type' => MENU_ITEM_GROUPING, 'title' => t('google maps'), 'access' => user_access('create macro')||user_access('show user map')||user_access('show node map'), ); */ $items[] = array( 'path' => 'admin/settings/gmap', 'title' => t('GMap'), 'description' => t('Configure GMap settings'), 'callback' => 'drupal_get_form', 'callback arguments' => 'gmap_admin_settings', 'access' => user_access('administer site configuration'), 'type' => MENU_NORMAL_ITEM, ); $items[] = array( 'path' => 'map/query', 'type' => MENU_CALLBACK, 'access' => user_access('access content'), 'callback' => 'gmap_json_query', ); $items[] = array( 'path' => 'map/markerdata.js', 'type' => MENU_CALLBACK, 'access' => user_access('access content'), 'callback' => 'gmap_markerdata_js', ); return $items; } } /** * Generate the markerdata file. */ function gmap_markerdata_js() { drupal_set_header('Content-Type: text/javascript'); echo "// GMap marker image data.\n"; echo "Drupal.gmap.iconpath = ". drupal_to_js(base_path() . drupal_get_path('module', 'gmap') .'/markers') .";\n"; echo "Drupal.gmap.icondata = ". drupal_to_js(gmap_get_icondata()) .";\n"; } /** * JSON request interface. */ function gmap_json_query() { if (arg(2)=='markers') { drupal_set_header('Content-Type: text/javascript'); echo drupal_to_js(array( 'path' => base_path() . drupal_get_path('module', 'gmap') .'/markers', 'markers' => gmap_get_icondata(TRUE), )); exit(); } } /** * Settings page. (remove for d6) */ function gmap_admin_settings() { require_once(drupal_get_path('module', 'gmap') .'/gmap_settings_ui.inc'); return _gmap_admin_settings(); } /** * 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(), )), '#process' => array('expand_gmap' => array()), ), 'gmap_macrotext' => array( '#input' => TRUE, '#cols' => 60, '#rows' => 5, '#process' => array( 'process_gmap_control' => array('textarea', 'macrotext'), ), ), 'gmap_overlay_edit' => array('#input' => FALSE, '#process' => array('process_gmap_overlay_edit' => array())), 'gmap_style' => array('#input' => TRUE, '#tree' => TRUE, '#gmap_style_type' => 'poly', '#process' => array('process_gmap_style' => array())), 'gmap_address' => array('#input' => FALSE, '#process' => array('process_gmap_address' => array())), 'gmap_align' => array('#input' => TRUE, '#process' => array('process_gmap_align' => array())), 'gmap_latitude' => array('#input' => TRUE, '#process' => array('process_gmap_control' => array('textfield', 'latitude', 'gmap_coord'))), 'gmap_longitude' => array('#input' => TRUE, '#process' => array('process_gmap_control' => array('textfield', 'longitude', 'gmap_coord'))), 'gmap_latlon' => array('#input' => TRUE, '#process' => array('process_gmap_control' => array('textfield', 'latlon', 'gmap_coord'))), 'gmap_markerchooser' => array('#input' => TRUE, '#process' => array('process_gmap_markerchooser' => array())), 'gmap_dimension' => array('#input' => TRUE, '#theme' => 'textfield', '#validate' => array('gmap_dimension_validate' => array())), ); } /** * Gmap element #process function. */ function expand_gmap($element) { $mapid = 'map'; if ($element['#map']) { $mapid = $element['#map']; } else { $element['#map'] = $mapid; } if (!$element['#settings']) { $element['#settings'] = array(); } // @@@ $element['#settings'] = array_merge(gmap_defaults(), array( 'id' => $mapid, 'points' => array(), 'pointsOverlays' => array(), 'lines' => array(), ), $element['#settings']); gmap_widget_setup($element, 'gmap'); return $element; } /** * Generic gmap control #process function. */ function process_gmap_control($element, $edit, $fieldtype, $control, $theme='') { $element['#type'] = $fieldtype; gmap_widget_setup($element, $control); if (!empty($theme)) { $element['#theme'] = $theme; } else { $element['#theme'] = 'gmap_'. $control; } 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' => '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: #00AA33'), '#size' => 6, '#maxlength' => 6, '#field_prefix' => '#', '#value' => $element['#value'][3], '#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' => '%', '#value' => $element['#value'][4], '#disabled' => $isline, '#attributes' => array('class' => 'gmap_style'), ); 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'; if (!$element['#title']) { $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'; $element['#options'] = gmap_get_marker_titles(); return $element; } /** * Overlay editor theme function. */ 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_coord($element) { //drupal_add_js return theme('textfield', $element); } 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) { _gmap_doheader(); // If the theme function is called directly, the map is not properly set up yet. if (!$element['#id']) { gmap_widget_setup($element, 'gmap'); } // Possible if this gmap was created manually. if (!$element['#settings']) { $element['#settings'] = array(); } $map = array_merge(gmap_defaults(), $element['#settings']); gmap_map_cleanup($map); switch (strtolower($map['align'])) { case 'left': $element['#attributes']['class'] += ' gmap-left'; break; case 'right': $element['#attributes']['class'] += ' gmap-right'; break; case 'center': case 'centre': $element['#attributes']['class'] += ' gmap-center'; } $style = array(); $style[] = 'width: '. $map['width']; $style[] = 'height: '. $map['height']; $element['#attributes']['class'] = trim($element['#attributes']['class'] .'gmap gmap-map gmap-'. $map['id'] .'-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 = '
'. t('Javascript is required to view this map.') .'
'; // $map can be manipulated by reference. foreach (module_implements('gmap') as $module) { call_user_func_array($module .'_gmap', array('pre_theme_map', &$map)); } // Inline settings extend. $o .= '\n"; 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']; } } } $element['#attributes']['class'] = trim(implode(' ', array( $element['#attributes']['class'], 'gmap-control', 'gmap-'. $type, ))); $element['#id'] = gmap_get_id($map, $type); $element['#attributes']['id'] = $element['#id']; $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 (is_array($titles) && !$reset) { return $titles; } $titles = cache_get('gmap_marker_titles'); if ($titles) { $titles = unserialize($titles->data); } if ($reset || !$titles) { require_once(drupal_get_path('module', 'gmap') .'/gmap_markerinfo.inc'); $titles = _gmap_get_marker_titles(); } cache_set('gmap_marker_titles', 'cache', serialize($titles)); return $titles; } /** * Get the JSON icon data for all the default markers. */ function gmap_get_icondata($reset = FALSE) { static $icons; if (is_array($icons) && !$reset) { return $icons; } $icons = cache_get('gmap_icondata'); if ($icons) { $icons = unserialize($icons->data); } if ($reset || !$icons) { require_once(drupal_get_path('module', 'gmap') .'/gmap_markerinfo.inc'); $icons = _gmap_get_icondata(); } cache_set('gmap_icondata', 'cache', serialize($icons)); 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 = variable_get('googlemap_api_key', ''); if (module_exists('keys_api')) { $key = keys_api_get_key('gmap', $_SERVER['HTTP_HOST']); } $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'), ), ); }