Index: location.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/location/location.inc,v
retrieving revision 1.95.2.7
diff -u -p -r1.95.2.7 location.inc
--- location.inc	13 Jun 2010 09:27:10 -0000	1.95.2.7
+++ location.inc	24 Jun 2010 15:05:29 -0000
@@ -225,14 +225,7 @@ function _location_convert_distance_to_m
     return NULL;
   }
 
-  // Force an integer version of distance, just in case anyone wants to add a caching mechanism
-  // for postal code proximity searches.
-
-  if (is_float($distance)) {
-    $distance = intval(ceil($distance));
-  }
-
-  if ($distance < 1) {
+  if ($distance == 0) {
     return NULL;
   }
 
@@ -241,8 +234,6 @@ function _location_convert_distance_to_m
   }
 
   // Convert distance to meters
-  //$distance_float = floatval($distance) * (($distance_unit == 'km') ? 1000.0 : 1609.347);
-  //return round($distance_float, 2);
   $retval = round(floatval($distance) * (($distance_unit == 'km') ? 1000.0 : 1609.347), 2);
   return $retval;
 }
Index: location.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/location/location.install,v
retrieving revision 1.41.2.1
diff -u -p -r1.41.2.1 location.install
--- location.install	25 Feb 2009 18:49:30 -0000	1.41.2.1
+++ location.install	24 Jun 2010 15:05:30 -0000
@@ -1121,3 +1121,55 @@ function location_update_6308() {
   }
   return $ret;
 }
+
+/**
+ * There has been a change in the options for the views distance/proximity filter.
+ * The old options need to be migrated into the new.
+ */
+function location_update_6309() {
+  $ret = array();
+  $updated_views = array();
+
+  $views = views_get_all_views();
+  foreach ($views as $view) {
+    $modified = FALSE;
+    // For each view check all filters of all displays for the distance field filter.
+    foreach ($view->display as $did => $display) {
+      if (isset($display->display_options['filters']) && !empty($display->display_options['filters'])) {
+        foreach ($display->display_options['filters'] as $fid => $filter) {
+          if ($filter['table'] == 'location' && $filter['field'] == 'distance') {
+            // Set the origin (new option) from the type (old option).
+            $origin = '';
+            switch ($filter['type']) {
+              case 'latlon':
+                $origin = 'static';
+                break;
+              case 'postal':
+              case 'postal_default':
+              case 'latlon_gmap':
+                $origin = $filter['type'];
+                break;
+            }
+            if ($origin) {
+              $view->display[$did]->display_options['filters'][$fid]['origin'] = $origin;
+              unset($view->display[$did]->display_options['filters'][$fid]['type']);
+              $modified = TRUE;
+            }
+          }
+        }
+      }
+    }
+    // Save the view if we changed any options on any diplays.
+    // Views caches are cleared during save so we don't have to do it.
+    if ($modified) {
+      $updated_views[] = $view->name;
+      $view->save();
+    }
+  }
+
+  if (!empty($updated_views)) {
+    drupal_set_message(t("Note: The 'Form type' option for the 'Location: Distance / Proximity' filter in views has been changed and is now the 'Origin' option.  The following views were using that setting and have been updated to use the new setting: %views.", array('%views' => implode(', ', $updated_views))));
+  }
+
+  return $ret;
+}
Index: location.views.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/location/location.views.inc,v
retrieving revision 1.16
diff -u -p -r1.16 location.views.inc
--- location.views.inc	3 Dec 2008 22:51:23 -0000	1.16
+++ location.views.inc	24 Jun 2010 15:05:30 -0000
@@ -383,3 +383,158 @@ function location_views_data() {
 
   return $data;
 }
+
+/**
+ * Helper function for proximity handlers.
+ * Gets a list of available node location and cck location fields.
+ *
+ * @return
+ *   An array of the location field options.
+ */
+function location_views_proximity_get_location_field_options() {
+  // Get the CCK location field options.
+  $field_options = array('node' => t('Node location'));
+  if (module_exists('location_cck')) {
+    $fields = content_fields();
+    foreach ($fields as $field) {
+      if ($field['module'] == 'location_cck') {
+        $field_options[$field['field_name']] = t('CCK Location: @label (@name)', array('@label' => $field['widget']['label'], '@name' => $field['field_name']));
+      }
+    }
+  }
+  return $field_options;
+}
+
+/**
+ * Helper function for proximity handlers.
+ * Gets available arguments for argument related handler options.
+ *
+ * @param $view
+ *   The view object.
+ *
+ * @return
+ *   An array containing arrays of the nid arguments and the uid arguments.
+ */
+function location_views_proximity_get_argument_options($view) {
+  // Get the arguments for this view so we can use nid, uid or Global: Null arguments.
+  $uid_argument_options = array();
+  $nid_argument_options = array();
+  $arguments = $view->display_handler->get_handlers('argument');
+  foreach ($arguments as $id => $handler) {
+    if ($handler->field == 'null') {
+      $uid_argument_options[$id] = $handler->ui_name();
+      $nid_argument_options[$id] = $handler->ui_name();
+    }
+    else if ($handler->field == 'nid') {
+      $nid_argument_options[$id] = $handler->ui_name();
+    }
+    else if ($handler->field == 'uid') {
+      $uid_argument_options[$id] = $handler->ui_name();
+    }
+  }
+  return array($nid_argument_options, $uid_argument_options);
+}
+
+/**
+ * Helper function for proximity handlers.
+ * Retrieves the coordinates of the location that this field measures distances against.
+ *
+ * @param $view
+ *   The view object.
+ * @param $options
+ *   An array of the options (or values in the case of the filter) of the handler.
+ *
+ * @return
+ *   An array with keys 'latitude' and 'longitude' or an empty array.
+ */
+function location_views_proximity_get_reference_location($view, $options) {
+  $coordinates = array();
+  switch ($options['origin']) {
+    case 'user':
+    case 'hybrid':
+      global $user;
+      $user_locations = isset($user->locations) ? $user->locations : location_load_locations($user->uid, 'uid');
+      // This user_location_delta will only possibly be set if we are dealing with the filter.
+      $i = (isset($options['user_location_delta']) && !empty($options['user_location_delta'])) ? $options['user_location_delta'] : 0;
+      if (isset($user_locations[$i]['latitude']) || !empty($user_locations[$i]['latitude'])) {
+        $coordinates['latitude'] = (float) $user_locations[$i]['latitude'];
+        $coordinates['longitude'] = (float) $user_locations[$i]['longitude'];
+      }
+      else if ($options['origin'] == 'hybrid') {
+        $coordinates['latitude'] = (float) $options['latitude'];
+        $coordinates['longitude'] = (float) $options['longitude'];
+      }
+      break;
+    case 'static':
+      $coordinates['latitude'] = (float) $options['latitude'];
+      $coordinates['longitude'] = (float) $options['longitude'];
+      break;
+    case 'tied':
+      if (!empty($view->filter)) {
+        foreach ($view->filter as $filter) {
+          if ($filter->table == 'location' && $filter->field == 'distance' && $filter->options['relationship'] == $options['relationship']) {
+            $filter_options = array_merge($filter->options, $filter->options['value'], $filter->value);
+            if ($coords = location_views_proximity_get_reference_location($view, $filter_options)) {
+              $coordinates['latitude'] = (float) $coords['latitude'];
+              $coordinates['longitude'] = (float) $coords['longitude'];
+            }
+          }
+        }
+      }
+      break;
+    case 'postal':
+    case 'postal_default':
+      // Force default for country.
+      if ($options['origin'] == 'postal_default') {
+        $options['country'] = variable_get('location_default_country', 'us');
+      }
+      // Zip code lookup.
+      if (!empty($options['postal_code']) && !empty($options['country'])) {
+        $coords = location_latlon_rough($options);
+        if ($coords) {
+          $coordinates['latitude'] = (float) $coords['lat'];
+          $coordinates['longitude'] = (float) $coords['lon'];
+        }
+      }
+      break;
+    case 'php':
+      ob_start();
+      $result = eval($options['php_code']);
+      ob_end_clean();
+      if ($result && $result['latitude'] && $result['longitude']) {
+        $coordinates['latitude'] = (float) $result['latitude'];
+        $coordinates['longitude'] = (float) $result['longitude'];
+      }
+      break;
+    case 'nid_arg':
+      if ($nodehandler = $view->display_handler->get_handler('argument', $options['nid_arg'])) {
+        $nid = $nodehandler->get_value();
+        if ($nid && is_numeric($nid) && $tempnode = node_load($nid)) {
+          $field_name = $options['nid_loc_field'];
+          if ($field_name == 'node') {
+            $coordinates['latitude'] = (float) $tempnode->location['latitude'];
+            $coordinates['longitude'] = (float) $tempnode->location['longitude'];
+          }
+          else {
+            $cck_location = $tempnode->$field_name;
+            if (isset($cck_location[0]['longitude']) && isset($cck_location[0]['latitude'])) {
+              $coordinates['latitude'] = (float) $cck_location[0]['latitude'];
+              $coordinates['longitude'] = (float) $cck_location[0]['longitude'];
+            }
+          }
+        }
+      }
+      break;
+    case 'uid_arg':
+      if ($userhandler = $view->display_handler->get_handler('argument', $options['uid_arg'])) {
+        $uid = $userhandler->get_value();
+        if ($uid && is_numeric($uid) && $tempuser = user_load(array('uid' => $uid))) {
+          $coordinates['latitude'] = (float) $tempuser->location['latitude'];
+          $coordinates['longitude'] = (float) $tempuser->location['longitude'];
+        }
+      }
+      break;
+  }
+
+  return $coordinates;
+}
Index: handlers/location_handler_field_location_distance.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/location/handlers/location_handler_field_location_distance.inc,v
retrieving revision 1.2.2.2
diff -u -p -r1.2.2.2 location_handler_field_location_distance.inc
--- handlers/location_handler_field_location_distance.inc	28 Mar 2010 22:13:53 -0000	1.2.2.2
+++ handlers/location_handler_field_location_distance.inc	24 Jun 2010 15:05:30 -0000
@@ -14,6 +14,12 @@ class location_handler_field_location_di
     $options['units'] = array('default' => 'km');
     $options['latitude'] = array('default' => '');
     $options['longitude'] = array('default' => '');
+    $options['postal_code'] = array('default' => '');
+    $options['country'] = array('default' => '');
+    $options['php_code'] = array('default' => '');
+    $options['nid_arg'] = array('default' => '');
+    $options['nid_loc_field'] = array('default' => 'node');
+    $options['uid_arg'] = array('default' => '');
     return $options;
   }
 
@@ -29,30 +35,95 @@ class location_handler_field_location_di
         'km' => t('Kilometers'),
         'mi' => t('Miles'),
       ),
-      '#description' => t('FIXME'),
       '#default_value' => $this->options['units'],
     );
     $form['origin'] = array(
-      '#type' => 'radios',
+      '#type' => 'select',
       '#title' => t('Origin'),
       '#options' => array(
-        'user' => t("User's location (blank if unset)"),
-        'hybrid' => t("User's location (fall back to static if unset)"),
-        'static' => t("Static location"),
+        'user' => t("User's Latitude / Longitude (blank if unset)"),
+        'hybrid' => t("User's Latitude / Longitude (fall back to static if unset)"),
+        'static' => t('Static  Latitude / Longitude'),
         'tied' => t("Use Distance / Proximity filter"),
+        'postal' => t('Postal Code / Country'),
+        'postal_default' => t('Postal Code (assume default country)'),
+        'php' => t('Use PHP code to determine latitude/longitude'),
+        'nid_arg' => t("Node's Latitude / Longitude from views nid argument"),
+        'uid_arg' => t("User's Latitude / Longitude from views uid argument"),
       ),
-      '#description' => t('FIXME'),
+      '#description' => t("This will be the way the latitude/longitude of origin is determined. When using the user's latitude / longitude, if a user has multiple locations the first will be used."),
       '#default_value' => $this->options['origin'],
     );
+
     $form['latitude'] = array(
       '#type' => 'textfield',
       '#title' => t('Latitude'),
       '#default_value' => $this->options['latitude'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('hybrid', 'static')),
     );
     $form['longitude'] = array(
       '#type' => 'textfield',
       '#title' => t('Longitude'),
       '#default_value' => $this->options['longitude'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('hybrid', 'static')),
+    );
+
+    $form['postal_code'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Postal code'),
+      '#default_value' => $this->options['postal_code'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('postal', 'postal_default')),
+    );
+    $form['country'] = array(
+      '#type' => 'select',
+      '#title' => t('Country'),
+      '#options' => array('' => '') + location_get_iso3166_list(),
+      '#default_value' => $this->options['country'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('postal')),
+    );
+
+    $form['php_code'] = array(
+      '#type' => 'textarea',
+      '#title' => t('PHP code for latitude, longitude'),
+      '#default_value' => $this->options['php_code'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('php')),
+      '#description' => t("Enter PHP code that returns a latitude/longitude.  Do not use &lt;?php ?&gt;. You must return only an array with float values set for the 'latitude' and 'longitude' keys."),
+    );
+
+    list($nid_argument_options, $uid_argument_options) = location_views_proximity_get_argument_options($this->view);
+    $nid_loc_field_options = location_views_proximity_get_location_field_options();
+
+    $form['nid_arg'] = array(
+      '#type' => 'select',
+      '#title' => t('Node ID argument to use'),
+      '#options' => $nid_argument_options,
+      '#default_value' => $this->options['nid_arg'],
+      '#description' => empty($nid_argument_options) ? t("Select which of the view's arguments to use as the node ID.  The latitude / longitude of the first location of that node will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that node ID. You must have added arguments to the view to use this option.") : t("Select which of the view's arguments to use as the node ID.  The latitude / longitude of the first location of that node will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that node ID."),
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('nid_arg')),
+    );
+    $form['nid_loc_field'] = array(
+      '#type' => 'select',
+      '#title' => t('Location to use'),
+      '#options' => $nid_loc_field_options,
+      '#default_value' => $this->options['nid_loc_field'],
+      '#description' => t("Select which of the node's locations to use as the origin.  Either the node locations or a CCK location field.  If the location supports multiple entries the first one will be used."),
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('nid_arg')),
+    );
+    $form['uid_arg'] = array(
+      '#type' => 'select',
+      '#title' => t('User ID argument to use'),
+      '#options' => $uid_argument_options,
+      '#default_value' => $this->options['uid_arg'],
+      '#description' => empty($uid_argument_options) ? t("Select which of the view's arguments to use as the user ID.  The latitude / longitude of the first location of that user will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that user ID. You must have added arguments to the view to use this option.") : t("Select which of the view's arguments to use as the user ID.  The latitude / longitude of the first location of that user will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that user ID."),
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('uid_arg')),
     );
   }
 
@@ -83,56 +154,13 @@ class location_handler_field_location_di
   function query() {
     $this->ensure_my_table();
 
-    $coordinates = $this->_get_reference_location();
-    if ($coordinates) {
+    $coordinates = location_views_proximity_get_reference_location($this->view, $this->options);
+
+    if (!empty($coordinates)) {
       $this->field_alias = $this->query->add_field(NULL, earth_distance_sql($coordinates['longitude'], $coordinates['latitude'], $this->table_alias), $this->table_alias .'_'. $this->field);
     }
     else {
       $this->field_alias = $this->query->add_field(NULL, "'Unknown'", $this->table_alias .'_'. $this->field);
     }
   }
-
-  /**
-   * Retrieves the coordinates of the location that this field measures
-   * distances against.
-   *
-   * @return array with keys 'latitude' and 'longitude'; null if none was configured
-   */
-  function _get_reference_location() {
-    if ($this->options['origin'] == 'hybrid' || $this->options['origin'] == 'user') {
-      global $user;
-      if (!isset($user->locations)) {
-        $user = user_load($user->uid);
-      }
-      if (isset($user->locations[0]['latitude']) || !empty($user->locations[0]['latitude'])) {
-        $latitude = (float)$user->locations[0]['latitude'];
-        $longitude = (float)$user->locations[0]['longitude'];
-      }
-    }
-
-    if ($this->options['origin'] == 'static') {
-      $latitude = (float)$this->options['latitude'];
-      $longitude = (float)$this->options['longitude'];
-    }
-
-    if ($this->options['origin'] == 'tied') {
-      if (!empty($this->view->filter)) {
-        foreach ($this->view->filter as $k => $v) {
-          if ($v->table == 'location' && $v->field == 'distance' && $v->options['relationship'] == $this->options['relationship']) {
-            if ($v->calculate_coords()) {
-              $latitude = (float)$v->value['latitude'];
-              $longitude = (float)$v->value['longitude'];
-            }
-          }
-        }
-      }
-    }
-
-    if (isset($latitude) && isset($longitude)) {
-      return array('latitude' => $latitude, 'longitude' => $longitude);
-    }
-    else {
-      return null;
-    }
-  }
 }
Index: handlers/location_handler_sort_location_distance.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/location/handlers/location_handler_sort_location_distance.inc,v
retrieving revision 1.1
diff -u -p -r1.1 location_handler_sort_location_distance.inc
--- handlers/location_handler_sort_location_distance.inc	3 Dec 2008 22:51:23 -0000	1.1
+++ handlers/location_handler_sort_location_distance.inc	24 Jun 2010 15:05:30 -0000
@@ -11,9 +11,14 @@ class location_handler_sort_location_dis
   function option_definition() {
     $options = parent::option_definition();
     $options['origin'] = array('default' => 'user');
-    $options['units'] = array('default' => 'km');
     $options['latitude'] = array('default' => '');
     $options['longitude'] = array('default' => '');
+    $options['postal_code'] = array('default' => '');
+    $options['country'] = array('default' => '');
+    $options['php_code'] = array('default' => '');
+    $options['nid_arg'] = array('default' => '');
+    $options['nid_loc_field'] = array('default' => 'node');
+    $options['uid_arg'] = array('default' => '');
     return $options;
   }
 
@@ -22,72 +27,97 @@ class location_handler_sort_location_dis
   }
 
   function extra_options_form(&$form, &$form_state) {
-    $form['units'] = array(
-      '#type' => 'radios',
-      '#title' => t('Units'),
-      '#options' => array(
-        'km' => t('Kilometers'),
-        'mi' => t('Miles'),
-      ),
-      '#description' => t('FIXME'),
-      '#default_value' => $this->options['units'],
-    );
     $form['origin'] = array(
-      '#type' => 'radios',
+      '#type' => 'select',
       '#title' => t('Origin'),
       '#options' => array(
-        'user' => t("User's location (blank if unset)"),
-        'hybrid' => t("User's location (fall back to static if unset)"),
-        'static' => t("Static location"),
+        'user' => t("User's Latitude / Longitude (blank if unset)"),
+        'hybrid' => t("User's Latitude / Longitude (fall back to static if unset)"),
+        'static' => t('Static  Latitude / Longitude'),
         'tied' => t("Use Distance / Proximity filter"),
+        'postal' => t('Postal Code / Country'),
+        'postal_default' => t('Postal Code (assume default country)'),
+        'php' => t('Use PHP code to determine latitude/longitude'),
+        'nid_arg' => t("Node's Latitude / Longitude from views nid argument"),
+        'uid_arg' => t("User's Latitude / Longitude from views uid argument"),
       ),
-      '#description' => t('FIXME'),
+      '#description' => t("This will be the way the latitude/longitude of origin is determined. When using the user's latitude / longitude, if a user has multiple locations the first will be used."),
       '#default_value' => $this->options['origin'],
     );
     $form['latitude'] = array(
       '#type' => 'textfield',
       '#title' => t('Latitude'),
       '#default_value' => $this->options['latitude'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('hybrid', 'static')),
     );
     $form['longitude'] = array(
       '#type' => 'textfield',
       '#title' => t('Longitude'),
       '#default_value' => $this->options['longitude'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('hybrid', 'static')),
     );
-  }
 
-  function query() {
-    $latitude = 0;
-    $longitude = 0;
+    $form['postal_code'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Postal code'),
+      '#default_value' => $this->options['postal_code'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('postal', 'postal_default')),
+    );
+    $form['country'] = array(
+      '#type' => 'select',
+      '#title' => t('Country'),
+      '#options' => array('' => '') + location_get_iso3166_list(),
+      '#default_value' => $this->options['country'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('postal')),
+    );
 
-    if ($this->options['origin'] == 'hybrid' || $this->options['origin'] == 'user') {
-      global $user;
-      if (!isset($user->locations)) {
-        $user = user_load($user->uid);
-      }
-      if (isset($user->locations[0]['latitude']) || !empty($user->locations[0]['latitude'])) {
-        $latitude = (float)$user->locations[0]['latitude'];
-        $longitude = (float)$user->locations[0]['longitude'];
-      }
-    }
+    $form['php_code'] = array(
+      '#type' => 'textarea',
+      '#title' => t('PHP code for latitude, longitude'),
+      '#default_value' => $this->options['php_code'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('php')),
+      '#description' => t("Enter PHP code that returns a latitude/longitude.  Do not use &lt;?php ?&gt;. You must return only an array with float values set for the 'latitude' and 'longitude' keys."),
+    );
 
-    if ($this->options['origin'] == 'static') {
-      $latitude = (float)$this->options['latitude'];
-      $longitude = (float)$this->options['longitude'];
-    }
+    list($nid_argument_options, $uid_argument_options) = location_views_proximity_get_argument_options($this->view);
+    $nid_loc_field_options = location_views_proximity_get_location_field_options();
 
-    if ($this->options['origin'] == 'tied') {
-      if (!empty($this->view->filter)) {
-        foreach ($this->view->filter as $k => $v) {
-          if ($v->table == 'location' && $v->field == 'distance' && $v->options['relationship'] == $this->options['relationship']) {
-            if ($v->calculate_coords()) {
-              $latitude = (float)$v->value['latitude'];
-              $longitude = (float)$v->value['longitude'];
-            }
-          }
-        }
-      }
-    }
+    $form['nid_arg'] = array(
+      '#type' => 'select',
+      '#title' => t('Node ID argument to use'),
+      '#options' => $nid_argument_options,
+      '#default_value' => $this->options['nid_arg'],
+      '#description' => empty($nid_argument_options) ? t("Select which of the view's arguments to use as the node ID.  The latitude / longitude of the first location of that node will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that node ID. You must have added arguments to the view to use this option.") : t("Select which of the view's arguments to use as the node ID.  The latitude / longitude of the first location of that node will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that node ID."),
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('nid_arg')),
+    );
+    $form['nid_loc_field'] = array(
+      '#type' => 'select',
+      '#title' => t('Location to use'),
+      '#options' => $nid_loc_field_options,
+      '#default_value' => $this->options['nid_loc_field'],
+      '#description' => t("Select which of the node's locations to use as the origin.  Either the node locations or a CCK location field.  If the location supports multiple entries the first one will be used."),
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('nid_arg')),
+    );
+    $form['uid_arg'] = array(
+      '#type' => 'select',
+      '#title' => t('User ID argument to use'),
+      '#options' => $uid_argument_options,
+      '#default_value' => $this->options['uid_arg'],
+      '#description' => empty($uid_argument_options) ? t("Select which of the view's arguments to use as the user ID.  The latitude / longitude of the first location of that user will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that user ID. You must have added arguments to the view to use this option.") : t("Select which of the view's arguments to use as the user ID.  The latitude / longitude of the first location of that user will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that user ID."),
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('uid_arg')),
+    );
+  }
+
+  function query() {
+    $coordinates = location_views_proximity_get_reference_location($this->view, $this->options);
 
     $this->ensure_my_table();
 
@@ -95,20 +125,19 @@ class location_handler_sort_location_dis
     // Since the distance calculation is so icky, we try quite hard
     // to save some work for the database.
     // If someone has added a field that matches the sort, we just sort on that column!
-    $alias = $this->table_alias .'_'. $this->field .'_sort';
-    foreach ($this->view->field as $k => $v) {
-      if ($v->table == 'location' && $v->field == 'distance' && $v->options['relationship'] == $this->options['relationship']) {
-        if ($v->options['origin'] == $this->options['origin']
-            && $v->options['units'] == $this->options['units']
-            && $v->options['latitude'] == $this->options['latitude']
-            && $v->options['longitude'] == $this->options['longitude']) {
+    $alias = $this->table_alias . '_' . $this->field . '_sort';
+    foreach ($this->view->field as $filter) {
+      if ($filter->table == 'location' && $filter->field == 'distance' && $filter->options['relationship'] == $this->options['relationship']) {
+        if ($filter->options['origin'] == $this->options['origin']
+            && $filter->options['latitude'] == $this->options['latitude']
+            && $filter->options['longitude'] == $this->options['longitude']) {
           // We have a match! Sync aliases to make it easier on the database.
-          $alias = $v->field_alias;
+          $alias = $filter->field_alias;
         }
       }
     }
 
-    if (empty($latitude) && empty($longitude)) {
+    if (empty($coordinates)) {
       // We don't know the distance.
       // Therefore, we don't need to sort on it.
     }
@@ -116,7 +145,7 @@ class location_handler_sort_location_dis
       // This is done exactly the same as the field version.
       // Views is ok with us redefining the formula for a field.
       // If ANYTHING differs in the configuration, we will use a new alias.
-      $this->query->add_orderby(NULL, earth_distance_sql($longitude, $latitude, $this->table_alias), $this->options['order'], $alias);
+      $this->query->add_orderby(NULL, earth_distance_sql($coordinates['longitude'], $coordinates['latitude'], $this->table_alias), $this->options['order'], $alias);
     }
   }
 }
Index: handlers/location_views_handler_filter_proximity.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/location/handlers/location_views_handler_filter_proximity.inc,v
retrieving revision 1.3.2.2
diff -u -p -r1.3.2.2 location_views_handler_filter_proximity.inc
--- handlers/location_views_handler_filter_proximity.inc	20 Apr 2010 00:48:16 -0000	1.3.2.2
+++ handlers/location_views_handler_filter_proximity.inc	24 Jun 2010 15:05:30 -0000
@@ -11,26 +11,29 @@ class location_views_handler_filter_prox
 
   function option_definition() {
     $options = parent::option_definition();
-    $options['type'] = array('default' => 'latlon');
-
     $options['operator'] = array('default' => 'mbr');
 
     $options['identifier'] = array('default' => 'dist');
 
+    $options['origin'] = array('default' => 'user');
+
     $options['value'] = array(
       'default' => array(
         'latitude' => '',
         'longitude' => '',
         'postal_code' => '',
         'country' => '',
+        'php_code' => '',
+        'nid_arg' => '',
+        'nid_loc_field' => 'node',
+        'uid_arg' => '',
         'search_distance' => 100,
         'search_units' => 'mile',
       ),
     );
 
-    $options['gmap_macro'] = array(
-      'default' => '[gmap ]',
-    );
+    $options['expose']['gmap_macro'] = array('default' => '[gmap ]');
+    $options['expose']['user_location_choose'] = array('default' => FALSE);
 
     return $options;
   }
@@ -48,24 +51,6 @@ class location_views_handler_filter_prox
 
   function expose_form_left(&$form, &$form_state) {
     parent::expose_form_left($form, $form_state);
-    $form['expose']['type'] = array(
-      '#parents' => array('options', 'type'),
-      '#type' => 'select',
-      '#title' => t('Form mode'), // @@@ Less stupid title?
-      '#options' => array(
-        'latlon' => t('Latitude / Longitude input'),
-        'postal' => t('Postal Code / Country'),
-        'postal_default' => t('Postal Code (assume default country)'),
-      ),
-      //'#id' => 'edit-options-type',
-      '#description' => t('FIXME'),
-      '#default_value' => $this->options['type'],
-    );
-
-    if (module_exists('gmap')) {
-      $form['expose']['type']['#options']['latlon_gmap'] = t('Latitude / Longitude input (use map)');
-    }
-
     // @@@ Todo: autohide this.
     $form['expose']['gmap_macro'] = array(
       '#parents' => array('options', 'gmap_macro'),
@@ -75,10 +60,34 @@ class location_views_handler_filter_prox
       '#default_value' => $this->options['gmap_macro'],
     );
 
+    $form['expose']['user_location_choose'] = array(
+      '#type' => 'checkbox',
+      '#title' => t("Allow choice of user location"),
+      '#default_value' => $this->options['expose']['user_location_choose'],
+      '#description' => t("If checked and using a user location origin, the user will be able to choose which of their locations to use.  Otherwise their first location will be used."),
+    );
   }
 
   function value_form(&$form, &$form_state) {
-    $val = $this->value;
+    $form['origin'] = array(
+      '#type' => 'select',
+      '#title' => t('Origin'),
+      '#options' => array(
+        'user' => t("User's Latitude / Longitude (blank if unset)"),
+        'hybrid' => t("User's Latitude / Longitude (fall back to static if unset)"),
+        'static' => t('Static  Latitude / Longitude'),
+        'postal' => t('Postal Code / Country'),
+        'postal_default' => t('Postal Code (assume default country)'),
+        'php' => t('Use PHP code to determine latitude/longitude'),
+        'nid_arg' => t("Node's Latitude / Longitude from views nid argument"),
+        'uid_arg' => t("User's Latitude / Longitude from views uid argument"),
+      ),
+      '#description' => t('This will be the way the latitude/longitude of origin is determined.  If this filter is exposed, this will determine the default values. NOTE: The PHP code, nid argument and uid argument options will not be available when the filter is exposed and the use of map is only available when the filter is exposed.'),
+      '#default_value' => $this->options['origin'] ? $this->options['origin'] : 'user',
+    );
+    if (module_exists('gmap')) {
+      $form['origin']['#options']['latlon_gmap'] = t('Latitude / Longitude input (use map)');
+    }
 
     // [11:44] <merlinofchaos> If you load the page from scratch, $input for your identifier will be empty.
     // [11:44] <merlinofchaos> So what's going on here is that for $_GET forms, there's no form id, no op button or anything, so they always appear to submit.
@@ -108,14 +117,16 @@ class location_views_handler_filter_prox
       '#title' => t('Latitude'),
       '#default_value' => $this->value['latitude'],
       '#process' => array('views_process_dependency'),
-      '#dependency' => array('edit-options-type' => array('latlon', 'latlon_gmap')),
+      '#dependency' => array('edit-options-origin' => array('hybrid', 'static', 'latlon_gmap')),
+      '#weight' => 1,
     );
     $form['value']['longitude'] = array(
       '#type' => 'textfield',
       '#title' => t('Longitude'),
       '#default_value' => $this->value['longitude'],
       '#process' => array('views_process_dependency'),
-      '#dependency' => array('edit-options-type' => array('latlon', 'latlon_gmap')),
+      '#dependency' => array('edit-options-origin' => array('hybrid', 'static', 'latlon_gmap')),
+      '#weight' => 2,
     );
 
     $form['value']['postal_code'] = array(
@@ -123,22 +134,69 @@ class location_views_handler_filter_prox
       '#title' => t('Postal code'),
       '#default_value' => $this->value['postal_code'],
       '#process' => array('views_process_dependency'),
-      '#dependency' => array('edit-options-type' => array('postal', 'postal_default')),
+      '#dependency' => array('edit-options-origin' => array('postal', 'postal_default')),
+      '#weight' => 3,
     );
-
     $form['value']['country'] = array(
       '#type' => 'select',
       '#title' => t('Country'),
       '#options' => array('' => '') + location_get_iso3166_list(),
       '#default_value' => $this->value['country'],
       '#process' => array('views_process_dependency'),
-      '#dependency' => array('edit-options-type' => array('postal')),
+      '#dependency' => array('edit-options-origin' => array('postal')),
+      '#weight' => 4,
+    );
+
+    $form['value']['php_code'] = array(
+      '#type' => 'textarea',
+      '#title' => t('PHP code for latitude, longitude'),
+      '#default_value' => $this->options['php_code'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('php')),
+      '#description' => t("Enter PHP code that returns a latitude/longitude.  Do not use &lt;?php ?&gt;. You must return only an array with float values set for the 'latitude' and 'longitude' keys."),
+      '#default_value' => $this->value['php_code'],
+      '#weight' => 5,
+    );
+
+    list($nid_argument_options, $uid_argument_options) = location_views_proximity_get_argument_options($this->view);
+    $nid_loc_field_options = location_views_proximity_get_location_field_options();
+
+    $form['value']['nid_arg'] = array(
+      '#type' => 'select',
+      '#title' => t('Node ID argument to use'),
+      '#options' => $nid_argument_options,
+      '#default_value' => $this->value['nid_arg'],
+      '#description' => empty($nid_argument_options) ? t("Select which of the view's arguments to use as the node ID.  The latitude / longitude of the first location of that node will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that node ID. You must have added arguments to the view to use this option.") : t("Select which of the view's arguments to use as the node ID.  The latitude / longitude of the first location of that node will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that node ID."),
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('nid_arg')),
+      '#weight' => 6,
+    );
+    $form['value']['nid_loc_field'] = array(
+      '#type' => 'select',
+      '#title' => t('Location to use'),
+      '#options' => $nid_loc_field_options,
+      '#default_value' => $this->value['nid_loc_field'],
+      '#description' => t("Select which of the node's locations to use as the origin.  Either the node locations or a CCK location field.  If the location supports multiple entries the first one will be used."),
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('nid_arg')),
+      '#weight' => 7,
+    );
+    $form['value']['uid_arg'] = array(
+      '#type' => 'select',
+      '#title' => t('User ID argument to use'),
+      '#options' => $uid_argument_options,
+      '#default_value' => $this->value['uid_arg'],
+      '#description' => empty($uid_argument_options) ? t("Select which of the view's arguments to use as the user ID.  The latitude / longitude of the first location of that user will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that user ID. You must have added arguments to the view to use this option.") : t("Select which of the view's arguments to use as the user ID.  The latitude / longitude of the first location of that user will be used as the origin. Use the 'Global: Null' argument if you don't want to also restrict results to that user ID."),
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('edit-options-origin' => array('uid_arg')),
+      '#weight' => 8,
     );
 
     $form['value']['search_distance'] = array(
       '#type' => 'textfield',
       '#title' => t('Distance'),
       '#default_value' => $this->value['search_distance'],
+      '#weight' => 9,
     );
 
     $form['value']['search_units'] = array(
@@ -148,71 +206,76 @@ class location_views_handler_filter_prox
         'km' => t('Kilometers'),
       ),
       '#default_value' => $this->value['search_units'],
+      '#weight' => 10,
     );
   }
 
   function exposed_form(&$form, &$form_state) {
     parent::exposed_form($form, $form_state);
     $key = $this->options['expose']['identifier'];
-    $type = $this->options['type'];
+    $origin = $this->options['origin'];
+    if ($origin == 'latlon_gmap' && module_exists('gmap')) {
+      // Bad things happen if we try to show a gmap in the views live preview.
+      if ($form_state['post']['live_preview']) {
+        $form[$key]['proximity_map'] = array(
+          '#value' => t('Gmap location selection is not available during live preview.'),
+          '#weight' => 0,
+        );
+      }
+      else {
+        $form[$key]['proximity_map'] = array(
+          '#value' => gmap_set_location($this->options['gmap_macro'], $form[$key], array('latitude' => 'latitude', 'longitude' => 'longitude')),
+          '#weight' => 0,
+        );
+      }
+    }
+    else if (($origin == 'user' || $origin == 'hybrid') && $this->options['expose']['user_location_choose']) {
+      global $user;
+      $user_locations = isset($user->locations) ? $user->locations : location_load_locations($user->uid, 'uid');
+      $location_options = array();
+      if (!empty($user_locations)) {
+        foreach ($user_locations as $i => $location) {
+          if (isset($location['latitude']) && isset($location['longitude'])) {
+            if (!empty($location['name'])) {
+              $location_options[$i] = t(check_plain($location['name']));
+            }
+            else {
+              $location_options[$i] = t('Location #@num', array('@num' => $i + 1));
+            }
+          }
+        }
+      }
 
-    if ($type == 'latlon_gmap') {
-      if (module_exists('gmap')) {
-      $form[$key]['proximity_map'] = array(
-        '#type' => 'markup',
-        '#value' => gmap_set_location($this->options['gmap_macro'], $form[$key], array('latitude' => 'latitude', 'longitude' => 'longitude')),
+      $form[$key]['user_location_delta'] = array(
+        '#type' => 'select',
+        '#title' => t('Location'),
+        '#options' => $location_options,
+        '#description' => t('Select which of your locations to use.'),
+        '#weight' => 0,
       );
-      }
     }
 
     // Remove unneeded fields when exposing the form.
     // It's shorter than redefining value_form.
-    if ($type != 'latlon' && $type != 'latlon_gmap') {
+    if ($origin != 'static' && $origin != 'latlon_gmap') {
       unset($form[$key]['latitude']);
       unset($form[$key]['longitude']);
     }
-    if ($type != 'postal' && $type != 'postal_default') {
+    if ($origin != 'postal' && $origin != 'postal_default') {
       unset($form[$key]['postal_code']);
     }
-    if ($type != 'postal') {
+    if ($origin != 'postal') {
       unset($form[$key]['country']);
     }
-  }
 
-  // Used from the distance field.
-  function calculate_coords() {
-    if (!empty($this->value['latitude']) && !empty($this->value['longitude'])) {
-      // If there are already coordinates, there's no work for us.
-      return TRUE;
-    }
-    // @@@ Switch to mock location object and rely on location more?
-
-    if ($this->options['type'] == 'postal' || $this->options['type'] == 'postal_default') {
-      // Force default for country.
-      if ($this->options['type'] == 'postal_default') {
-        $this->value['country'] = variable_get('location_default_country', 'us');
-      }
+    // And we definitely do not want to expose the php code option when exposing the filter
+    unset($form[$key]['php_code']);
+    // The nid/uid arg options are not useful on an exposed form.
+    unset($form[$key]['nid_arg']);
+    unset($form[$key]['nid_loc_field']);
+    unset($form[$key]['uid_arg']);
 
-      // Zip code lookup.
-      if (!empty($this->value['postal_code']) && !empty($this->value['country'])) {
-        $coord = location_latlon_rough($this->value);
-        if ($coord) {
-          $this->value['latitude'] = $coord['lat'];
-          $this->value['longitude'] = $coord['lon'];
-        }
-        else {
-          return false;
-        }
-      }
-      else {
-        // @@@ Implement full address lookup?
-        return false;
-      }
-    }
-    if (empty($this->value['latitude']) || empty($this->value['longitude'])) {
-      return false;
-    }
-    return true;
+    unset($form['origin']);
   }
 
   function query() {
@@ -220,8 +283,13 @@ class location_views_handler_filter_prox
       return;
     }
 
+    // We need to merge with $this->options['value'] for filter values
+    // and $this->value for exposed filter values.
+    $options = array_merge($this->options, $this->options['value'], $this->value);
+    $coordinates = location_views_proximity_get_reference_location($this->view, $options);
+
     // Coordinates available?
-    if (!$this->calculate_coords()) {
+    if (empty($coordinates)) {
       // Distance set?
       if (!empty($this->value['search_distance'])) {
         // Hmm, distance set but unable to resolve coordinates.
@@ -233,16 +301,22 @@ class location_views_handler_filter_prox
 
     $this->ensure_my_table();
 
-    $lat = $this->value['latitude'];
-    $lon = $this->value['longitude'];
+    $lat = $coordinates['latitude'];
+    $lon = $coordinates['longitude'];
 
     $distance_meters = _location_convert_distance_to_meters($this->value['search_distance'], $this->value['search_units']);
-
     $latrange = earth_latitude_range($lon, $lat, $distance_meters);
     $lonrange = earth_longitude_range($lon, $lat, $distance_meters);
 
-    // Add MBR check (always.)
-    $this->query->add_where($this->options['group'], "$this->table_alias.latitude > %f AND $this->table_alias.latitude < %f AND $this->table_alias.longitude > %f AND $this->table_alias.longitude < %f", $latrange[0], $latrange[1], $lonrange[0], $lonrange[1]);
+    // Add MBR check (always).
+    // In case we go past the 180/-180 mark for longitude.
+    if ($lonrange[0] > $lonrange[1]) {
+      $where = "$this->table_alias.latitude > %f AND $this->table_alias.latitude < %f AND (($this->table_alias.longitude < 180 AND $this->table_alias.longitude > %f) OR ($this->table_alias.longitude < %f AND $this->table_alias.longitude > -180))";
+    }
+    else {
+      $where = "$this->table_alias.latitude > %f AND $this->table_alias.latitude < %f AND $this->table_alias.longitude > %f AND $this->table_alias.longitude < %f";
+    }
+    $this->query->add_where($this->options['group'], $where, $latrange[0], $latrange[1], $lonrange[0], $lonrange[1]);
 
     if ($this->operator == 'dist') {
       // Add radius check.
