--- location.module.old	2009-10-17 17:47:35.000000000 +1100
+++ location.module	2009-10-17 17:52:57.000000000 +1100
@@ -562,9 +562,11 @@
 
       if (!empty($obj['locpick']) && is_array($obj['locpick'])) {
         // Can't specify just latitude or just longitude.
-        if (_location_floats_are_equal($obj['locpick']['user_latitude'], 0) xor _location_floats_are_equal($obj['locpick']['user_longitude'], 0)) {
+        $user_latitude = location_parse_dms_to_dd($obj['locpick']['user_latitude']);
+        $user_longitude = location_parse_dms_to_dd($obj['locpick']['user_longitude']);
+        if (_location_floats_are_equal($user_latitude, 0) xor _location_floats_are_equal($user_longitude, 0)) {
           $ref = &$a3['locpick']['user_latitude'];
-          if (_location_floats_are_equal($obj['locpick']['user_longitude'], 0)) {
+          if (_location_floats_are_equal($user_longitude, 0)) {
             $ref = &$a3['locpick']['user_longitude'];
           }
           form_error($ref, t('You must fill out both latitude and longitude or you must leave them both blank.'));
@@ -1054,8 +1056,8 @@
   // If the user location was set, convert it into lat / lon.
   if (!empty($location['locpick']['user_latitude']) && !empty($location['locpick']['user_longitude'])) {
     $location['source'] = LOCATION_LATLON_USER_SUBMITTED;
-    $location['latitude'] = $location['locpick']['user_latitude'];
-    $location['longitude'] = $location['locpick']['user_longitude'];
+    $location['latitude'] = location_parse_dms_to_dd($location['locpick']['user_latitude']);
+    $location['longitude'] = location_parse_dms_to_dd($location['locpick']['user_longitude']);
     $inhibit_geocode = TRUE;
   }
 
@@ -1373,6 +1375,52 @@
 }
 
 /**
+ * Attempt to parse a string (degrees,minutes,seconds) to decimal degrees.
+ */
+function location_parse_dms_to_dd($s) {
+
+  $str = strtoupper(trim($s));
+
+  // Decimal degrees, with optional leading +- and trailing NSEW
+  // Eg: 37.846833S, -37.8468
+  if (preg_match('/^([+-]?)(\d+\.?\d+?)([NSEW]?)$/', $str, $matches)) {
+    $degrees = $matches[2];
+    $negative = $matches[3] == 'S' || $matches[3] == 'W';
+    if ($matches[1] == '-') {
+      $negative = !$negative;
+    }
+    $minutes = $seconds = 0;
+  }
+  // Full DMS form. Three different values accepted for degrees symbol.
+  // Eg: 36° 52' 30.8316" S
+  elseif (preg_match('/^([+-]?)(\d+)(?:\xC2\xB0|\xA1|°)\s*(?:(\d+)\'\s*)?(?:([\d.]+)"\s*)?([NSEW])?$/', $str, $matches)) {
+    $degrees = $matches[2] ? $matches[2] : 0;
+    $minutes = isset($matches[3]) ? $matches[3] : 0;
+    $seconds = isset($matches[4]) ? $matches[4] : 0;
+    $negative = isset($matches[5]) && ($matches[5] == 'S' || $matches[5] == 'W');
+    if ($matches[1] == '-') {
+      $negative = !$negative;
+    }
+  }
+  // Another form, NSEW degrees plus decimal minutes
+  // S37 50.810 : everything but degrees is optional
+  elseif (preg_match('/^([NSEW])?(\d+)\s+([\d.]+)$/', $str, $matches)) {
+    $degrees = $matches[2];
+    $minutes = $matches[3];
+    $seconds = 0;
+    $negative = $matches[1] == 'S' || $matches[1] == 'W';
+  }
+
+  if (isset($degrees)) {
+    $dd = ($negative ? -1 : 1) * ($degrees + ($minutes/60) + ($seconds/60/60));
+    return floatval($dd);
+  }
+
+  // return function input if we couldn't parse it
+  return $s;
+}
+
+/**
  * Display a coordinate.
  */
 function theme_location_latitude_dms($latitude) {
