Index: gcg/gcg_gmap_views.module =================================================================== --- gcg/gcg_gmap_views.module (revision 89) +++ gcg/gcg_gmap_views.module (working copy) @@ -118,4 +118,97 @@ return $output; break; } -} \ No newline at end of file +} + + +function gcg_gmap_views_views_arguments() { + $arguments = array( + 'gcg_gmap_views_arg_proximity' => array( + 'name' => t('GCG GMAP: Nodes within proximity'), + 'handler' => 'gcg_gmap_views_arg_proximity', + 'option' => 'string', + 'help' => t('Within distance of defined lat and long, set Option to latitude,longitude,distance, ie: -90.00,23.45,5km '), + ) + ); + + return $arguments; +} + + +/** + * Process the comma-seperate argument into an assoc array + * + * Note: You could use this to provide a whole markup system for doing clever things + * - ie ",nid: 300" find nodes within distance from this + * ",uid: 22" nodes close to this user + * + * @param string $arg + */ +function _gcg_gmap_views_arg_process($arg) { + + $tmp=explode(',',urldecode($arg)); + if(!is_array($tmp) || sizeof($tmp)<2) { + return false; + } + + // basic sanity check, replace anything not a digit, a decimal or a - with '' + $proximity_info['lat']=preg_replace('/[^\d\.-]/i','',$tmp[0]); + $proximity_info['lon']=preg_replace('/[^\d\.-]/i','',$tmp[1]); + + if(sizeof($tmp)>2) { + $proximity_info['unit']=eregi('mi$',$tmp[2]) ? 'miles' : 'kilometers'; + $proximity_info['distance']=preg_replace('/[^\d\.]/i','',$tmp[2]); + } else { + // default to something + $proximity_info['distance']=50; + $proximity_info['unit']='kilometers'; + } + + return $proximity_info; +} + +/** + * GCG GMAP Views argument handler + * + * note: this uses earth.inc from location module, which is a really inefficient and mathematically + * expensive way todo things, BUT IT DOES WORK. + * + * @param unknown_type $op + * @param unknown_type $query + * @param unknown_type $argtype + * @param unknown_type $arg + * @param unknown_type $table + */ +function gcg_gmap_views_arg_proximity($op, &$query, $argtype, $arg = '', $table = 'gcg_views') { + + // ensure this doesnt collide with locations module's include of earth.inc + if(! function_exists('earth_latitude_range') ) { + $module_path = drupal_get_path('module', 'gcg_gmap_views'); + include_once($module_path.'/earth.inc'); + } + + switch($op) { + case 'filter': + $_info = _gcg_gmap_views_arg_process($arg); + if(!is_array($_info) || !strlen($_info['lat']) || !strlen($_info['lon']) || ! strlen($_info['distance'])) { + drupal_set_message('Invalid Longitude, Latitude or Distance defined.'); + return; + } + + $divisor = $_info['unit'] == 'kilometers' ? 1000 : 1609.347; + $latrange = earth_latitude_range($_info['lon'], $_info['lat'], ($_info['distance'] * $divisor)); + $lonrange = earth_longitude_range($_info['lon'], $_info['lat'], ($_info['distance'] * $divisor)); + + // a large enough distance will cause the earth function to pass something bad to asin() + if( is_nan($latrange[0]) || is_nan($lonrange[0]) ) { + drupal_set_message('Unable to calculate items within that distance, please specify a smaller radius to check within.'); + return; + } + $query->ensure_table($table); + $query->add_orderby(NULL, "((". earth_distance_sql($_info['lon'], $_info['lat'],$table) .") / $divisor)", 'ASC', 'distance'); + $query->add_where("$table.longitude IS NOT NULL"); + $query->add_where("$table.latitude > %f AND $table.latitude < %f AND $table.longitude > %f AND $table.longitude < %f", $latrange[0], $latrange[1], $lonrange[0], $lonrange[1]); + break; + } + +} Index: gcg/earth.inc =================================================================== --- gcg/earth.inc (revision 0) +++ gcg/earth.inc (revision 103) @@ -0,0 +1,155 @@ + pi()) { $maxlong = $maxlong - pi()*2; } + return array(rad2deg($minlong), rad2deg($maxlong)); +} + +function earth_latitude_range($longitude, $latitude, $distance) { + // Estimate the min and max latitudes within $distance of a given location. + $long = deg2rad($longitude); + $lat = deg2rad($latitude); + $radius = earth_radius($latitude); + + $angle = $distance / $radius; + $minlat = $lat - $angle; + $maxlat = $lat + $angle; + $rightangle = pi()/2; + if ($minlat < -$rightangle) { // wrapped around the south pole + $overshoot = -$minlat - $rightangle; + $minlat = -$rightangle + $overshoot; + if ($minlat > $maxlat) { $maxlat = $minlat; } + $minlat = -$rightangle; + } + if ($maxlat > $rightangle) { // wrapped around the north pole + $overshoot = $maxlat - $rightangle; + $maxlat = $rightangle - $overshoot; + if ($maxlat < $minlat) { $minlat = $maxlat; } + $maxlat = $rightangle; + } + return array(rad2deg($minlat), rad2deg($maxlat)); +} + +?>