diff --git location/location.admin.inc location/location.admin.inc
index 37200bc..e61c99a 100644
--- location/location.admin.inc
+++ location/location.admin.inc
@@ -157,6 +157,55 @@ function location_map_link_options_form() {
 function location_geocoding_options_form() {
   $form = array();
 
+  $form['ajax_geocoding'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Ajax geocoding'),
+    '#collapsible' => FALSE,
+    '#collapsed' => FALSE,
+  );
+
+  $form['ajax_geocoding']['location_geocode_ajax'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Enable ajax geocoding'),
+    '#default_value' => variable_get('location_geocode_ajax', FALSE),
+    '#description' => t('Enable geocoding via Ajax. The special button will be added for every location form.'),
+  );
+
+  $form['ajax_geocoding']['location_geocode_ajax_throbber'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Use system throbber'),
+    '#default_value' => variable_get('location_geocode_ajax_throbber', TRUE),
+    '#description' => t('Enable using system throbber (misc/throbber.gif). Disable this option if you would like to use your own throbber (in this case you should do it in your theme CSS file).'),
+  );
+
+  $form['ajax_geocoding']['location_geocode_ajax_confirm'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Ask user before changing coordinates'),
+    '#default_value' => variable_get('location_geocode_ajax_confirm', TRUE),
+    '#description' => t('Confirmation dialog will be shown before geocoder changes the coordinates in the location form. User can disacard new coordinates delivered by geocoder.'),
+  );
+
+  $form['ajax_geocoding']['location_geocode_ajax_zoom'] = array(
+    '#type' => 'textfield',
+    '#title' => t('GMap zoom'),
+    '#default_value' => variable_get('location_geocode_ajax_zoom', ''),
+    '#description' => t('Zoom level to which Google Map will be zoomed if coordinates has been changed by geocoder. Leave it empty if zoom shouldn\'t be changed.'),
+  );
+
+  $form['ajax_geocoding']['location_geocode_ajax_title'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Button title'),
+    '#default_value' => variable_get('location_geocode_ajax_title', t('Geocode')),
+    '#description' => t('Title of the button.'),
+  );
+
+  $form['ajax_geocoding']['location_geocode_ajax_description'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Button description'),
+    '#default_value' => variable_get('location_geocode_ajax_description', ''),
+    '#description' => t('Description of the button.'),
+  );
+
   $form['location_geocode_google_minimum_accuracy'] = array(
     '#type' => 'select',
     '#title' => t('Google Maps geocoding minimum accuracy'),
@@ -286,6 +335,13 @@ function location_geocoding_options_form_submit($form, &$form_state) {
 
   variable_set('location_geocode_google_minimum_accuracy', $form_state['values']['location_geocode_google_minimum_accuracy']);
   variable_set('location_general_geocoders_in_use', $general_geocoders_in_use);
+
+  variable_set('location_geocode_ajax', $form_state['values']['location_geocode_ajax']);
+  variable_set('location_geocode_ajax_throbber', $form_state['values']['location_geocode_ajax_throbber']);
+  variable_set('location_geocode_ajax_confirm', $form_state['values']['location_geocode_ajax_confirm']);
+  variable_set('location_geocode_ajax_zoom', $form_state['values']['location_geocode_ajax_zoom']);
+  variable_set('location_geocode_ajax_title', $form_state['values']['location_geocode_ajax_title']);
+  variable_set('location_geocode_ajax_description', $form_state['values']['location_geocode_ajax_description']);
 }
 
 function theme_location_map_link_options($form) {
@@ -310,7 +366,8 @@ function theme_location_map_link_options($form) {
 }
 
 function theme_location_geocoding_options($form) {
-  $output = drupal_render($form['location_geocode_google_minimum_accuracy']);
+  $output = drupal_render($form['ajax_geocoding']);
+  $output .= drupal_render($form['location_geocode_google_minimum_accuracy']);
   $header = array(
     array('align' => 'center', 'data' => '<center>'. t('Country') .'</center>'),
     array('align' => 'center', 'data' => '<center>'. t('Options') .'</center>'),
diff --git location/location.module location/location.module
index b31b149..b031908 100644
--- location/location.module
+++ location/location.module
@@ -38,6 +38,18 @@ function location_menu() {
     'type' => MENU_CALLBACK,
   );
 
+  $items['location/geocode'] = array(
+    'access arguments' => array('access content'),
+    'page callback' => 'location_geocode_js',
+    'type' => MENU_CALLBACK,
+  );
+
+  $items['location/geocoder_exists'] = array(
+    'access arguments' => array('access content'),
+    'page callback' => 'location_geocoder_exists_js',
+    'type' => MENU_CALLBACK,
+  );
+
   $items['admin/settings/location'] = array(
     'title' => 'Location',
     'description' => 'Settings for Location module',
@@ -333,6 +345,45 @@ function _location_expand_location($element) {
       );
     }
 
+    if (variable_get('location_geocode_ajax', FALSE)) {
+      // Prevent Drupal.settings from setting more than once
+      static $settings_added;
+      if (!isset($settings_added)) {
+        // Allow caching so location_token would be checked when ajax request arrives
+        $element['#cache'] = TRUE;
+        // Add files
+        drupal_add_js(drupal_get_path('module', 'location') .'/location_geocode.js');
+        drupal_add_css(drupal_get_path('module', 'location') .'/location_geocode.css');
+        $throbberUrl = variable_get('location_geocode_ajax_throbber', TRUE) ? '/misc/throbber.gif' : '';
+        // Add settings used by javascript
+        drupal_add_js(array('location' => array(
+          'throbberUrl' => $throbberUrl,
+          'geocoderUrl' => url('location/geocode', array('absolute' => TRUE)),
+          'geocoderExistsUrl' => url('location/geocoder_exists', array('absolute' => TRUE)),
+          'confirmChanges' => variable_get('location_geocode_ajax_confirm', TRUE),
+          'zoomTo' => variable_get('location_geocode_ajax_zoom', ''),
+        )), 'setting');
+        $settings_added = TRUE;
+      }
+
+      $decoder_not_exists_class = location_geocoder_exists($fdefaults['country']) ? '' : 'geocoder-not-exists';
+      $button_description = '<div class="description">'.variable_get('location_geocode_ajax_description', '').'</div>';
+
+      $element['locpick']['geocode_button'] = array(
+        '#type' => 'button',
+        '#value' => t(variable_get('location_geocode_ajax_title', 'Geocode')),
+        '#attributes' => array(
+                            'class' => implode(' ', array(
+                              'location-geocode-button',
+                              'button',
+                              $decoder_not_exists_class)),
+                          ),
+        '#prefix' => '<div class="location-geocode-button-wrapper form-item">',
+        '#suffix' => $button_description.'</div>',
+        '#weight' => -3,
+      );
+    }
+
     $element['locpick']['user_latitude'] = array(
       '#type' => 'textfield',
       '#title' => t('Latitude'),
@@ -1585,7 +1636,7 @@ function template_preprocess_location(&$variables) {
   // Map link.
   $variables['map_link'] = '';
   if (!empty($location['map_link'])) {
-    // Do not use $location for generating the map link, since it will 
+    // Do not use $location for generating the map link, since it will
     // not contain the country if that field is hidden.
     $variables['map_link'] = location_map_link($variables['location']);
   }
@@ -1864,3 +1915,109 @@ function location_google_geocode_accuracy_codes() {
     9 => t('Premise (building name, property name, shopping center, etc.) level accuracy'),
   );
 }
+
+/**
+ * Menu callback; Ajax wrapper for function _location_geo_logic() for ajax requests
+ *
+ * @param
+ *   HTML element id (button)
+ *
+ * @return
+ *   Ajax response in JSON format
+ */
+function location_geocode_js($id) {
+  $location = array();
+  if (preg_match('/^edit-(.+)-(\d+)-locpick-geocode-button$/', $id, $matches)) {
+    // Name could be equal 'location' or 'field_{something}'
+    $name = strtr($matches[1], '-', '_');
+    $num = $matches[2];
+    $form_build_id = $_POST['form_build_id'];
+    if (!empty($form_build_id)) {
+      // Check form_build_id
+      $form_state = array('submitted' => FALSE);
+      $form = form_get_cache($form_build_id, $form_state);
+      if ($form) {
+        // Check location_token; it works for anonymous users too
+        if ($form['location_token']['#default_value'] == $_POST['location_token']) {
+          $location = $_POST[$name][$num];
+          // Force geocoding everytime function is called
+          _location_geo_logic($location, array('street' => 1), array(), FALSE);
+        }
+      }
+    }
+  }
+  return drupal_json(array(
+    'location' => $location,
+    'id' => $id,
+  ));
+}
+
+/**
+ * Check existence of any geocoder function for given country.
+ * This function checks the same functions as _location_geo_logic() does
+ * since _location_geo_logic() is called when location is saved.
+ *
+ * Locations API support some different geocoding functions:
+ *   - location_get_postalcode_data()
+ *  - location_latlon_rough()
+ *  - location_latlon_exact()
+ *  See: location_API.txt
+ *
+ *  Note: location_latlon_exact() is intended to send the entire location
+ *  to a web-service (ie. Google Geocoder)
+ *
+ * @param $country
+ *   The lower-case of the two-letter ISO code
+ *
+ * @return
+ *   TRUE - if geocoder exists
+ */
+function location_geocoder_exists($country) {
+  $exists = FALSE;
+  location_standardize_country_code($country);
+  // Check if exact geocoder exists
+  $service = variable_get('location_geocode_'. $country, 'none');
+  if ($service != 'none') {
+    // Figure out what the exact function should be
+    if (strpos($service, '|')) {
+      location_load_country($country);
+      // The code change below fixes the problem of the country specific
+      // function for geocoding not being correctly called (it removes any
+      // text from the pipe (|) onwards)
+      $exact_latlon_function = 'location_geocode_'. $country .'_'. substr($service, 0, strpos($service, '|'));
+    }
+    else {
+      location_load_geocoder($service);
+      $exact_latlon_function = $service .'_geocode_location';
+    }
+    if (function_exists($exact_latlon_function)) {
+      $exists = TRUE;
+    }
+  }
+  // Check if inexact geocoder exists
+  if (!$exists) {
+    location_load_country($location['country']);
+    $country_specific_function = 'location_get_postalcode_data_'. $country;
+    if (function_exists($country_specific_function)) {
+      $exists = TRUE;
+    }
+  }
+  return $exists;
+}
+
+/**
+ * Menu callback; Ajax wrapper for location_geocoder_exists() function
+ *
+ * @param $id
+ *   HTML element id (country select list)
+ *
+ * @return
+ *   Ajax response in JSON format
+ */
+function location_geocoder_exists_js($id) {
+  $country = $_POST['country'];
+  return drupal_json(array(
+    'exists' => location_geocoder_exists($country),
+    'id' => $id,
+  ));
+}
diff --git location/location_geocode.css location/location_geocode.css
new file mode 100644
index 0000000..90c1a1e
--- /dev/null
+++ location/location_geocode.css
@@ -0,0 +1,42 @@
+
+.location .form-item .location-geocode-button {
+  display: inline;
+  width: auto;
+}
+
+.location .form-item .location-geocode-button-wrapper {
+  display: block;
+  margin: 1em 0;
+}
+
+.location .location-geocode-button-wrapper .description {
+  display: block;
+  clear: both;
+}
+
+.location .geocoder-not-exists {
+  display: none;
+}
+
+.location .throbber {
+  display: none;
+  height: 0;
+}
+
+.location .throbber-inside select,
+.location .throbber-inside input {
+  float: left
+}
+
+.location .throbbing .throbber {
+  float: left;
+  display: block;
+  height: 20px;
+  width: 15px;
+  margin: 5px 10px;
+  background-repeat: no-repeat;
+  background-position: 100% -18px; /* LTR */
+}
+
+
+
diff --git location/location_geocode.js location/location_geocode.js
new file mode 100644
index 0000000..7b13104
--- /dev/null
+++ location/location_geocode.js
@@ -0,0 +1,119 @@
+
+var LocationGeocoder = LocationGeocoder || {}
+
+/**
+ * Show throbber after (jQuery) elem
+ * Using Drupal throbber depends on settings
+ */
+LocationGeocoder.showThrobber = function(elem) {
+  var wrapper = elem.parent();
+  wrapper.addClass('throbbing clear-block');
+  if (!wrapper.hasClass('throbber-inside')) {
+    wrapper.addClass('throbber-inside');
+    elem.after('<div class="throbber"><div>');
+  }
+  if (Drupal.settings.location.throbberUrl != '') {
+    $('.throbber', wrapper).css('background-image', 'url(' + Drupal.settings.location.throbberUrl + ')');
+  }
+}
+
+/**
+ * Hide throbber after (jQuery) elem
+ * Using Drupal throbber depends on settings
+ */
+LocationGeocoder.hideThrobber = function(elem) {
+  var wrapper = elem.parent();
+  wrapper.removeClass('throbbing');
+  if (Drupal.settings.location.throbberUrl != '') {
+    $('.throbber', wrapper).css('background-image', 'none');
+  }
+}
+
+/**
+ * Geocode user entered address and place marker on the map.
+ */
+Drupal.behaviors.locationGeocode = function(context) {
+  $('.location-geocode-button:not(.location-geocode-processed)', context).click(function(e) {
+    var button = $(this);
+    LocationGeocoder.showThrobber(button);
+    var formItems = button.parents('form:first').serializeArray();
+    $.post(
+      Drupal.settings.location.geocoderUrl + '/'+ button.attr('id'),
+      formItems,
+      function(data) {
+        var button = $('#' + data.id);
+        LocationGeocoder.hideThrobber(button);
+        if (data.location.latitude && data.location.longitude) {
+          // Round to the same precision as used in location.module
+          data.location.latitude = Math.round(data.location.latitude * 1000000) / 1000000;
+          data.location.longitude = Math.round(data.location.longitude * 1000000) / 1000000;
+          // Set flags, which value will decide what should be done
+          var updateAllowed = true;
+          var isChanged = false;
+          // Find input fields for coordinate values
+          var fieldset = button.parents('fieldset:first');
+          var locpickLatitude = $('input[name*="[locpick][user_latitude]"]', fieldset);
+          var locpickLongitude = $('input[name*="[locpick][user_longitude]"]', fieldset);
+          // Check if coordinates would be changed
+          if (locpickLatitude.val() != '' || locpickLongitude.val() != '') {
+            if (locpickLatitude.val() != data.location.latitude || locpickLongitude.val() != data.location.longitude) {
+              isChanged = true;
+              if (Drupal.settings.location.confirmChanges) {
+                if (!confirm(Drupal.t('Coordinates will be changed. Continue?'))) {
+                  updateAllowed = false;
+                }
+              }
+            }
+          }
+          else {
+            isChanged = true;
+          }
+          // Do something if coordinates are changed and updating is allowed
+          if (updateAllowed && isChanged) {
+            var gmap = $('.gmap-map', fieldset);
+            if (gmap.length) {
+              obj = Drupal.gmap.getMap(gmap.attr('id'));
+              coord = new GLatLng(data.location.latitude, data.location.longitude);
+              if (Drupal.settings.location.zoomTo != '') {
+                obj.map.setZoom(parseInt(Drupal.settings.location.zoomTo));
+              }
+              GEvent.trigger(obj.map, "click", null, coord);
+            }
+            else {
+              locpickLatitude.val(data.location.latitude);
+              locpickLongitude.val(data.location.longitude);
+            }
+          }
+        }
+      },
+      'json'
+    );
+    e.preventDefault();
+  }).addClass('location-geocode-processed');
+};
+
+/**
+ * Show or hide "Geocode" button, show the throbber
+ */
+Drupal.behaviors.locationGeocoderExists = function(context) {
+  $('select.location_auto_country:not(.location-geocode-processed)', context).change(function(e) {
+    var select = $(this);
+    LocationGeocoder.showThrobber(select);
+    $.post(
+      Drupal.settings.location.geocoderExistsUrl + '/'+ select.attr('id'),
+      {country: select.val()},
+      function(data) {
+        var select = $('#' + data.id);
+        LocationGeocoder.hideThrobber(select);
+        var buttonWrapper = $('.location-geocode-button-wrapper', select.parents('fieldset:first'));
+        if (data.exists) {
+          buttonWrapper.removeClass('geocoder-not-exists');
+        }
+        else {
+          buttonWrapper.addClass('geocoder-not-exists');
+        }
+      },
+      'json'
+    );
+  }).addClass('location-geocode-processed');
+};
diff --git location/location_node.module location/location_node.module
index 6f6ffa7..1c6c89e 100644
--- location/location_node.module
+++ location/location_node.module
@@ -13,6 +13,13 @@ function location_node_form_alter(&$form, &$form_state, $form_id) {
     // Add the Location fields on the Node edit form
     $node = $form['#node'];
     $settings = variable_get('location_settings_node_'. $node->type, array());
+    // Add token, which is used by ajax geocoding
+    if (variable_get('location_geocode_ajax', FALSE)) {
+      $form['location_token'] = array(
+        '#type' => 'hidden',
+        '#default_value' => drupal_get_token(time()),
+      );
+    }
     if (isset($settings['multiple']['max']) && $settings['multiple']['max'] > 0) {
       $locations = isset($node->locations) ? $node->locations : array();
       $form['locations'] = location_form($settings, $locations);
diff --git location/location_user.module location/location_user.module
index 0d35b2b..fab0eb7 100644
--- location/location_user.module
+++ location/location_user.module
@@ -64,6 +64,13 @@ function location_user_user($op, &$edit, &$account, $category = NULL) {
     case 'form':
       if ($category == 'account') {
         if ((($user->uid == $account->uid) && user_access('set own user location')) || user_access('administer user locations')) {
+          // Add token, which is used by ajax geocoding
+          if (variable_get('location_geocode_ajax', FALSE)) {
+            $form['location_token'] = array(
+              '#type' => 'hidden',
+              '#default_value' => drupal_get_token(time()),
+            );
+          }
           $settings = variable_get('location_settings_user', array());
           $form['locations'] = location_form($settings, $account->locations);
           return $form;
@@ -74,6 +81,13 @@ function location_user_user($op, &$edit, &$account, $category = NULL) {
     case 'register':
       $settings = variable_get('location_settings_user', array());
       if (isset($settings['form']['register']) && $settings['form']['register']) {
+        // Add token, which is used by ajax geocoding
+        if (variable_get('location_geocode_ajax', FALSE)) {
+          $form['location_token'] = array(
+            '#type' => 'hidden',
+            '#default_value' => drupal_get_token(time()),
+          );
+        }
         $form['locations'] = location_form($settings, array());
         return $form;
       }
