diff --git a/README.txt b/README.txt
index 25fbf82..6700e84 100644
--- a/README.txt
+++ b/README.txt
@@ -48,6 +48,11 @@ Regarding third-party features, the following are supported:
 - search_api_spellcheck
   Introduced by module: search_api_spellcheck
   Gives the option to display automatic spellchecking for searches.
+- search_api_data_type_location
+  Introduced by module: search_api_location
+  Lets you index, filter and sort on location fields. Note, however, that only
+  single-valued fields are currently supported for Solr 3.x, and that the option
+  isn't supported at all in Solr 1.4.
 
 If you feel some service option is missing, or have other ideas for improving
 this implementation, please file a feature request in the project's issue queue,
diff --git a/service.inc b/service.inc
index 9047b08..04a2fdc 100644
--- a/service.inc
+++ b/service.inc
@@ -534,13 +534,13 @@ class SearchApiSolrService extends SearchApiAbstractService {
 
     // Extract sort
     $sort = array();
-    foreach ($query->getSort() as $f => $order) {
-      $f = $fields[$f];
+    foreach ($query->getSort() as $field => $order) {
+      $f = $fields[$field];
       if (substr($f, 0, 3) == 'ss_') {
         $f = 'sort_' . substr($f, 3);
       }
       $order = strtolower($order);
-      $sort[] = "$f $order";
+      $sort[$field] = "$f $order";
     }
 
     // Get facet fields
@@ -567,6 +567,80 @@ class SearchApiSolrService extends SearchApiAbstractService {
       $keys = 'id:' . SearchApiSolrConnection::phrase($this->createId($index->machine_name, $mlt['id']));
     }
 
+    // Handle spatial filters.
+    if ($spatials = $query->getOption('search_api_location')) {
+      foreach ($spatials as $i => $spatial) {
+        if (empty($spatial['field']) || empty($spatial['lat']) || empty($spatial['lon'])) {
+          continue;
+        }
+
+        unset($radius);
+        $field = $fields[$spatial['field']];
+        $escaped_field = SearchApiSolrConnection::escapeFieldName($field);
+        $point = ((float) $spatial['lat']) . ',' . ((float) $spatial['lon']);
+
+        // Prepare the filter settings.
+        if (isset($spatial['radius'])) {
+          $radius = (float) $spatial['radius'];
+        }
+        $spatial_method = 'geofilt';
+        if (isset($spatial['method']) && in_array($spatial['method'], array('geofilt', 'bbox'))) {
+          $spatial_method = $spatial['method'];
+        }
+
+        // Change the fq facet ranges to the correct fq.
+        foreach ($fq as $key => $value) {
+          // If the fq consists only of a filter on this field, replace it with
+          // a range.
+          $preg_field = preg_quote($escaped_field, '/');
+          if (preg_match('/^' . $preg_field . ':\["?(\*|\d+(?:\.\d+)?)"? TO "?(\*|\d+(?:\.\d+)?)"?\]$/', $value, $m)) {
+            unset($fq[$key]);
+            if (is_numeric($lower = trim($m[1], '"')) && $lower) {
+              // Sadly, this doesn't work. We usually don't need lower bounds,
+              // though (our facetting implementation doesn't create them, so it
+              // should be fine to ignore this for now.
+              //$fq[] = "-{!$spatial_method pt=$point sfield=$field d=$lower}";
+            }
+            if (is_numeric($upper = trim($m[2], '"'))) {
+              // Make the radius tighter accordingly.
+              $radius = isset($radius) ? min($radius, $upper) : $upper;
+            }
+          }
+        }
+
+        // If either a radius was given in the option, or a filter was
+        // encountered, set a filter for the lowest value.
+        if (isset($radius)) {
+          $fq[] = "{!$spatial_method pt=$point sfield=$field d=$radius}";
+        }
+
+        // Change sort on the field, if set.
+        if (isset($sort[$spatial['field']])) {
+          $sort[$spatial['field']] = str_replace($field, "geodist($field,$point)", $sort[$spatial['field']]);
+        }
+
+        // Change the facet parameters for spatial fields to return distance
+        // facets.
+        if (!empty($facet_params)) {
+          foreach ($facet_params['facet.field'] as $j => $f) {
+            if ($f != $field) {
+              continue;
+            }
+            unset($facet_params['facet.field'][$j]);
+            $step = (isset($radius) ? $radius : 100) / 5;
+            for ($k = 1; $k < 5; $k++) {
+              $distance = $step * $k;
+              $key = "spatial-$i-$distance";
+              $facet_params['facet.query'][] = "{!$spatial_method pt=$point sfield=$field d=$distance key=$key}";
+            }
+            foreach (array('limit', 'mincount', 'missing') as $setting) {
+              unset($facet_params["f.$field.facet.$setting"]);
+            }
+          }
+        }
+      }
+    }
+
     // Set defaults
     if (!$keys) {
       $keys = NULL;
@@ -595,6 +669,9 @@ class SearchApiSolrService extends SearchApiAbstractService {
     if (!empty($mlt_params['mlt.fl'])) {
       $params += $mlt_params;
     }
+    if (!empty($spatial_params)) {
+      $params += $spatial_params;
+    }
     if (!empty($this->options['retrieve_data'])) {
       $params['fl'] = '*,score';
     }
@@ -784,15 +861,19 @@ class SearchApiSolrService extends SearchApiAbstractService {
    *   An array describing facets that apply to the current results.
    */
   protected function extractFacets(SearchApiQueryInterface $query, Apache_Solr_Response $response) {
-    if (isset($response->facet_counts->facet_fields)) {
-      $index = $query->getIndex();
-      $fields = $this->getFieldNames($index);
+    $facets = array();
 
-      $facets = array();
-      $facet_fields = $response->facet_counts->facet_fields;
+    if (!isset($response->facet_counts)) {
+      return $facets;
+    }
 
-      $extract_facets = $query->getOption('search_api_facets');
-      $extract_facets = ($extract_facets ? $extract_facets : array());
+    $index = $query->getIndex();
+    $fields = $this->getFieldNames($index);
+
+    $extract_facets = $query->getOption('search_api_facets', array());
+
+    if (isset($response->facet_counts->facet_fields)) {
+      $facet_fields = $response->facet_counts->facet_fields;
 
       foreach ($extract_facets as $delta => $info) {
         $field = $fields[$info['field']];
@@ -844,8 +925,38 @@ class SearchApiSolrService extends SearchApiAbstractService {
         }
       }
 
-      return $facets;
     }
+
+    if (isset($response->facet_counts->facet_queries)) {
+      if ($spatials = $query->getOption('search_api_location')) {
+        $queries = array();
+        foreach ($response->facet_counts->facet_queries as $key => $count) {
+          if (!preg_match('/^spatial-(.*)-(\d+(?:\.\d+)?)$/', $key, $m)) {
+            continue;
+          }
+          if (empty($spatials[$m[1]]['field'])) {
+            continue;
+          }
+          $queries[$spatials[$m[1]]['field']][] = array(
+            'filter' => "[* {$m[2]}]",
+            'count' => $count,
+          );
+        }
+        foreach ($extract_facets as $delta => $info) {
+          $field = $info['field'];
+          if (!empty($queries[$field])) {
+            foreach ($queries[$field] as $facet) {
+              if ($facet['count'] > $info['min_count']) {
+                $facets[$delta][] = $facet;
+              }
+            }
+            unset($queries[$field]);
+          }
+        }
+      }
+    }
+
+    return $facets;
   }
 
   /**
