Browse Source

Significant work on GeocodeLib

GeocodeLib changes

* url = https
* output default type = json
* fixed "type" --> "max accuracy" level
* fixed min_accuracy comparison
* normalized all _transform() methods for output
* normalized the _transformData() method
* improved error reporting (since not all DENIED are because of the sensor)
* improved debug() function
* improved reset() function

GeocoderBehavior changes

* TODO to reverse logic = done
* conditions split for readability (functionally the same)

HttpSocketLib changes

* no need to look for a constant if not defined
alan bount 11 years ago
parent
commit
081f9eaaa5
4 changed files with 285 additions and 213 deletions
  1. 188 147
      Lib/GeocodeLib.php
  2. 1 1
      Lib/HttpSocketLib.php
  3. 63 50
      Model/Behavior/GeocoderBehavior.php
  4. 33 15
      Test/Case/Lib/GeocodeLibTest.php

+ 188 - 147
Lib/GeocodeLib.php

@@ -20,7 +20,7 @@ App::uses('HttpSocketLib', 'Tools.Lib');
  */
 class GeocodeLib {
 
-	const BASE_URL = 'http://{host}/maps/api/geocode/{output}?';
+	const BASE_URL = 'https://{host}/maps/api/geocode/{output}?';
 	const DEFAULT_HOST = 'maps.googleapis.com';
 
 	const ACC_COUNTRY = 0;
@@ -70,7 +70,7 @@ class GeocodeLib {
 		'allow_inconclusive' => true,
 		'expect' => array(), # see accuracyTypes for details
 		// static url params
-		'output' => 'xml',
+		'output' => 'json',
 		'host' => null, # results in maps.google.com - use if you wish to obtain the closest address
 	);
 
@@ -88,6 +88,7 @@ class GeocodeLib {
 	);
 
 	protected $error = array();
+	protected $debug = array();
 
 	protected $result = null;
 
@@ -156,6 +157,7 @@ class GeocodeLib {
 		if (empty($error)) {
 			return;
 		}
+		$this->debugSet('setError', $error);
 		$this->error[] = $error;
 	}
 
@@ -167,16 +169,23 @@ class GeocodeLib {
 	}
 
 	/**
-	 * @param boolean $full
+	 * Reset - ready for the next request
+	 *
+	 * @param mixed boolean $full or string == 'params' to reset just params
 	 * @return void
 	 */
 	public function reset($full = true) {
 		$this->error = array();
 		$this->result = null;
-		if ($full) {
+		if (empty($full)) {
+			return;
+		}
+		if ($full == 'params') {
 			$this->params = $this->defaultParams;
-			$this->options = $this->defaultOptions;
+			return;
 		}
+		$this->params = $this->defaultParams;
+		$this->options = $this->defaultOptions;
 	}
 
 	/**
@@ -206,46 +215,46 @@ class GeocodeLib {
 		if ($this->result === null) {
 			return null;
 		}
-		if (!isset($this->result[0])) {
-			return false;
+		if (array_key_exists('location_type', $this->result) && !empty($this->result['location_type'])) {
+			return true;
 		}
-		return count($this->result) > 0;
+		return false;
 	}
 
 	/**
 	 * @return array result
 	 */
 	public function getResult() {
-		if ($this->result !== null) {
+		if ($this->result === null) {
+			return false;
+		}
 
-			if (isset($this->result[0])) {
-				$res = array();
-				foreach ($this->result as $tmp) {
-					$res[] = $this->options['output'] === 'json' ? $this->_transformJson($tmp) : $this->_transformXml($tmp);
-				}
-				return $res;
-			}
-			if ($this->options['output'] === 'json') {
-				return $this->_transformJson($this->result);
-			}
-			return $this->_transformXml($this->result);
+		if (is_string($this->result)) {
+			return $this->_transform($this->result);
 		}
-		return false;
+
+		if (!is_array($this->result)) {
+			return false;
+		}
+
+		return $this->result;
 	}
 
 	/**
 	 * Results usually from most accurate to least accurate result (street_address, ..., country)
+	 *
 	 * @param float $lat
 	 * @param float $lng
-	 * @param array $options
+	 * @param array $params
 	 * - allow_inconclusive
 	 * - min_accuracy
 	 * @return boolean Success
 	 */
-	public function reverseGeocode($lat, $lng, $settings = array()) {
+	public function reverseGeocode($lat, $lng, $params = array()) {
 		$this->reset(false);
+		$this->debugSet('reverseGeocode', compact('lat', 'lng', 'params'));
 		$latlng = $lat . ',' . $lng;
-		$this->setParams(array_merge($settings, array('latlng' => $latlng)));
+		$this->setParams(array_merge($params, array('latlng' => $latlng)));
 
 		$count = 0;
 		$requestUrl = $this->url();
@@ -257,43 +266,37 @@ class GeocodeLib {
 				return false;
 			}
 
-			if ($this->options['output'] === 'json') {
-				//$res = json_decode($result);
-			} else {
-				$res = Xml::build($result);
-			}
-
-			if (!is_object($res)) {
-				$this->setError('XML parsing failed');
-				CakeLog::write('geocode', __('Failed with XML parsing of \'%s\'', $latlng));
+			$this->debugSet('raw', $result);
+			$result = $this->_transform($result);
+			if (!is_array($result)) {
+				$this->setError('Result parsing failed');
+				CakeLog::write('geocode', __('Failed reverseGeocode parsing of \'%s\'', $latlng));
 				return false;
 			}
 
-			$xmlArray = Xml::toArray($res);
-			$xmlArray = $xmlArray['GeocodeResponse'];
-			$status = $xmlArray['status'];
+			$status = $result['status'];
+			//debug(compact('result', 'requestUrl', 'success'));
 
 			if ($status == self::CODE_SUCCESS) {
 
 				// validate
-				if (isset($xmlArray['result'][0]) && !$this->options['allow_inconclusive']) {
-					$this->setError(__('Inconclusive result (total of %s)', count($xmlArray['result'])));
-					$this->result = $xmlArray['result'];
+				if (isset($result['results'][0]) && !$this->options['allow_inconclusive']) {
+					$this->setError(__('Inconclusive result (total of %s)', count($result['results'])));
+					$this->result = $result['results'];
 					return false;
 				}
 
-				if (isset($xmlArray['result'][0])) {
-					//$xmlArray['result'] = $xmlArray['result'][0];
-					$accuracy = $this->_parse('type', $xmlArray['result'][0]);
-				} else {
-					$accuracy = $this->_parse('type', $xmlArray['result']);
+				if (isset($result['results'][0])) {
+					$result['result'] = $result['results'][0];
 				}
 
+				$accuracy = $this->_getMaxAccuracy($result['result']);
+
 				if ($this->_isNotAccurateEnough($accuracy)) {
-					$accuracy = implode(', ', (array)$accuracy);
+					$accuracy = $this->accuracyTypes[$accuracy];
 					$minAccuracy = $this->accuracyTypes[$this->options['min_accuracy']];
 					$this->setError(__('Accuracy not good enough (%s instead of at least %s)', $accuracy, $minAccuracy));
-					$this->result = $xmlArray['result'];
+					$this->result = $result['result'];
 					return false;
 				}
 
@@ -328,7 +331,7 @@ class GeocodeLib {
 			}
 			$this->pause(true);
 		}
-		$this->result = $xmlArray['result'];
+		$this->result = $result['result'];
 		return true;
 	}
 
@@ -352,6 +355,7 @@ class GeocodeLib {
 	 */
 	public function geocode($address, $params = array()) {
 		$this->reset(false);
+		$this->debugSet('reverseGeocode', compact('address', 'params'));
 		$this->setParams(array_merge($params, array('address' => $address)));
 		if ($this->options['allow_inconclusive']) {
 			// only host working with this setting?
@@ -362,6 +366,7 @@ class GeocodeLib {
 		$requestUrl = $this->url();
 
 		while (true) {
+
 			$result = $this->_fetch($requestUrl);
 			if ($result === false || $result === null) {
 				$this->setError('Could not retrieve url');
@@ -369,69 +374,35 @@ class GeocodeLib {
 				return false;
 			}
 
-			if ($this->options['output'] === 'json') {
-				//TODO? necessary?
-				$res = json_decode($result, true);
-				$xmlArray = $res;
-				foreach ($xmlArray['results'] as $key => $val) {
-					if (isset($val['address_components'])) {
-						$xmlArray['results'][$key]['address_component'] = $val['address_components'];
-						unset($xmlArray['results'][$key]['address_components']);
-					}
-					if (isset($val['types'])) {
-						$xmlArray['results'][$key]['type'] = $val['types'];
-						unset($xmlArray['results'][$key]['types']);
-					}
-				}
-
-				if (count($xmlArray['results']) === 1) {
-					$xmlArray['result'] = $xmlArray['results'][0];
-				} elseif (!$xmlArray['result']) {
-					$this->setError('JSON parsing failed');
-					CakeLog::write('geocode', __('Failed with JSON parsing of \'%s\'', $address));
-					return false;
-				}
-				$xmlArray['result'] = $xmlArray['results'];
-				unset($xmlArray['results']);
-
-			} else {
-				try {
-					$res = Xml::build($result);
-				} catch (Exception $e) {
-					CakeLog::write('geocode', $e->getMessage());
-					$res = array();
-				}
-				if (!is_object($res)) {
-					$this->setError('XML parsing failed');
-					CakeLog::write('geocode', __('Failed with XML parsing of \'%s\'', $address));
-					return false;
-				}
-				$xmlArray = Xml::toArray($res);
-				$xmlArray = $xmlArray['GeocodeResponse'];
+			$this->debugSet('raw', $result);
+			$result = $this->_transform($result);
+			if (!is_array($result)) {
+				$this->setError('Result parsing failed');
+				CakeLog::write('geocode', __('Failed geocode parsing of \'%s\'', $address));
+				return false;
 			}
 
-			$status = $xmlArray['status'];
+			$status = $result['status'];
+			//debug(compact('result', 'requestUrl', 'success'));
 
 			if ($status == self::CODE_SUCCESS) {
 				// validate
-				if (isset($xmlArray['result'][0]) && !$this->options['allow_inconclusive']) {
-					$this->setError(__('Inconclusive result (total of %s)', count($xmlArray['result'])));
-					$this->result = $xmlArray['result'];
+				if (isset($result['results'][0]) && !$this->options['allow_inconclusive']) {
+					$this->setError(__('Inconclusive result (total of %s)', count($result['results'])));
+					$this->result = $result['results'];
 					return false;
 				}
-				if (isset($xmlArray['result'][0])) {
-					//$xmlArray['result'] = $xmlArray['result'][0];
-					$accuracy = $this->_parse('type', $xmlArray['result'][0]);
-				} else {
-					$accuracy = $this->_parse('type', $xmlArray['result']);
+				if (isset($result['results'][0])) {
+					$result['result'] = $result['results'][0];
 				}
-				//echo returns($accuracy);
+
+				$accuracy = $this->_getMaxAccuracy($result['result']);
 
 				if ($this->_isNotAccurateEnough($accuracy)) {
-					$accuracy = implode(', ', (array)$accuracy);
+					$accuracy = $this->accuracyTypes[$accuracy];
 					$minAccuracy = $this->accuracyTypes[$this->options['min_accuracy']];
 					$this->setError(__('Accuracy not good enough (%s instead of at least %s)', $accuracy, $minAccuracy));
-					$this->result = $xmlArray['result'];
+					$this->result = $result['result'];
 					return false;
 				}
 
@@ -447,7 +418,7 @@ class GeocodeLib {
 					}
 					if (!$validExpectation) {
 						$this->setError(__('Expectation not reached (%s instead of at least %s)', $accuracy, implode(', ', (array)$this->options['expect'])));
-						$this->result = $xmlArray['result'];
+						$this->result = $result['result'];
 						return false;
 					}
 				}
@@ -467,13 +438,21 @@ class GeocodeLib {
 			} else {
 
 				// something went wrong
-				$this->setError('Error ' . $status . (isset($this->statusCodes[$status]) ? ' (' . $this->statusCodes[$status] . ')' : ''));
+				$error_message = (isset($result['error_message']) ? $result['error_message'] : '');
+				if (empty($error_message)) {
+					$error_message = (isset($this->statusCodes[$status]) ? $this->statusCodes[$status] : '');
+				}
+				if (empty($error_message)) {
+					$error_message = 'unknown';
+				}
+				$this->setError('Error ' . $status . ' (' . $error_message . ')');
 
 				if ($this->options['log']) {
 					CakeLog::write('geocode', __('Could not geocode \'%s\'', $address));
 				}
 				return false; # for now...
 			}
+
 			if ($count > 5) {
 				if ($this->options['log']) {
 					CakeLog::write('geocode', __('Aborted after too many trials with \'%s\'', $address));
@@ -483,7 +462,9 @@ class GeocodeLib {
 			}
 			$this->pause(true);
 		}
-		$this->result = $xmlArray['result'];
+
+		$this->result = $result['result'];
+		$this->result['all_results'] = $result['results'];
 		return true;
 	}
 
@@ -504,50 +485,85 @@ class GeocodeLib {
 	}
 
 	/**
-	 * @return boolean Success
+	 * @return boolean $notAccurateEnough
 	 */
 	protected function _isNotAccurateEnough($accuracy = null) {
-		if ($accuracy === null) {
-			if (isset($this->result[0])) {
-				$accuracy = $this->result[0]['type'];
-			} else {
-				$accuracy = $this->result['type'];
-			}
-		}
 		if (is_array($accuracy)) {
-			$accuracy = array_shift($accuracy);
+			$accuracy = $this->_getMaxAccuracy($accuracy);
 		}
-		if (!in_array($accuracy, $this->accuracyTypes)) {
-			return null;
+		if (empty($accuracy)) {
+			$accuracy = 0;
 		}
-		foreach ($this->accuracyTypes as $key => $type) {
-			if ($type == $accuracy) {
-				$accuracy = $key;
-				break;
-			}
+		// did we get a value instead of a key?
+		if (in_array($accuracy, $this->accuracyTypes, true)) {
+			$accuracy = array_search($accuracy, $this->accuracyTypes);
 		}
-		//echo returns($accuracy);
-		//echo returns('XXX'.$this->options['min_accuracy']);
+		// validate key exists
+		if (!array_key_exists($accuracy, $this->accuracyTypes)) {
+			$accuracy = 0;
+		}
+		// is our current accuracy < minimum?
 		return $accuracy < $this->options['min_accuracy'];
 	}
 
+	protected function _transform($record) {
+		if ($this->options['output'] === 'json') {
+			return $this->_transformJson($record);
+		}
+		return $this->_transformXml($record);
+	}
+
 	protected function _transformJson($record) {
-		$res = $this->_transformXml($record);
-		return $res;
+		if (!is_array($record)) {
+			$record = json_decode($record, true);
+		}
+		return $this->_transformData($record);
+	}
+
+	protected function _transformXml($record) {
+		if (!is_array($record)) {
+			$xml = Xml::build($record);
+			$record = Xml::toArray($xml);
+			if (array_key_exists('GeocodeResponse', $record)) {
+				$record = $record['GeocodeResponse'];
+			}
+		}
+		return $this->_transformData($record);
 	}
 
 	/**
-	 * Try to find the correct path
-	 * - type (string)
-	 * - Type (array[string, ...])
+	 * Try to find the max accuracy level
+	 *  - look through all fields and
+	 *    attempt to find the first record which matches an accuracyTypes field
+	 *
+	 * @param array $record
+	 * @return int $maxAccuracy 9-0 as defined in $this->accuracyTypes
 	 */
-	protected function _parse($key, $array) {
-		if (isset($array[$key])) {
-			return $array[$key];
+	public function _getMaxAccuracy($record) {
+		if (!is_array($record)) {
+			return null;
 		}
-		if (isset($array[($key = ucfirst($key))])) {
-			return $array[$key][0];
+		$accuracyTypes = $this->accuracyTypes;
+		$accuracyTypes = array_reverse($accuracyTypes, true);
+		foreach ($accuracyTypes as $key => $field) {
+			if (array_key_exists($field, $record) && !empty($record[$field])) {
+				// found $field -- return it's $key
+				return $key;
+			}
+		}
+
+		// not found?  recurse into all possible children
+		foreach (array_keys($record) as $key) {
+			if (empty($record[$key]) || !is_array($record[$key])) {
+				continue;
+			}
+			$accuracy = $this->_getMaxAccuracy($record[$key]);
+			if ($accuracy !== null) {
+				// found in nested value
+				return $accuracy;
+			}
 		}
+
 		return null;
 	}
 
@@ -556,25 +572,34 @@ class GeocodeLib {
 	 * keys:
 	 * - formatted_address, type, country, country_code, country_province, country_province_code, locality, sublocality, postal_code, route, lat, lng, location_type, viewport, bounds
 	 */
-	protected function _transformXml($record) {
-		$res = array();
+	protected function _transformData($record) {
+		if (!is_array($record)) {
+			return $record;
+		}
+		if (!array_key_exists('address_components', $record)) {
+			foreach (array_keys($record) as $key) {
+				$record[$key] = $this->_transformData($record[$key]);
+			}
+			return $record;
+		}
 
+		$res = array();
 		$components = array();
-		if (!isset($record['address_component'][0])) {
-			$record['address_component'] = array($record['address_component']);
+		if (!isset($record['address_components'][0])) {
+			$record['address_components'] = array($record['address_components']);
 		}
-		foreach ($record['address_component'] as $c) {
+		foreach ($record['address_components'] as $c) {
 			$types = array();
-			if (isset($c['type'])) { //!is_array($c['Type'])
-				if (!is_array($c['type'])) {
-					$c['type'] = (array)$c['type'];
+			if (isset($c['types'])) { //!is_array($c['Type'])
+				if (!is_array($c['types'])) {
+					$c['types'] = (array)$c['types'];
 				}
 
-				$type = $c['type'][0];
-				array_shift($c['type']);
-				$types = $c['type'];
-			} elseif (isset($c['type'])) {
-				$type = $c['type'];
+				$type = $c['types'][0];
+				array_shift($c['types']);
+				$types = $c['types'];
+			} elseif (isset($c['types'])) {
+				$type = $c['types'];
 			} else {
 				// error?
 				continue;
@@ -589,8 +614,6 @@ class GeocodeLib {
 
 		$res['formatted_address'] = $record['formatted_address'];
 
-		$res['type'] = $this->_parse('type', $record);
-
 		if (array_key_exists('country', $components)) {
 			$res['country'] = $components['country']['name'];
 			$res['country_code'] = $components['country']['abbr'];
@@ -651,6 +674,9 @@ class GeocodeLib {
 		if (!empty($res['country_province_code']) && array_key_exists($res['country_province_code'], $array)) {
 			$res['country_province_code'] = $array[$res['country_province_code']];
 		}
+
+		// inject maxAccuracy for transparency
+		$res['maxAccuracy'] = $this->_getMaxAccuracy($res);
 		return $res;
 	}
 
@@ -663,6 +689,7 @@ class GeocodeLib {
 	 **/
 	protected function _fetch($url) {
 		$this->HttpSocket = new HttpSocketLib($this->use);
+		$this->debugSet('_fetch', $url);
 		if ($res = $this->HttpSocket->fetch($url, 'CakePHP Geocode Lib')) {
 			return $res;
 		}
@@ -671,10 +698,24 @@ class GeocodeLib {
 	}
 
 	/**
-	* debugging
-	*/
+	 * return debugging info
+	 *
+	 * @return array $debug
+	 */
 	public function debug() {
-		return $this->result;
+		$this->debug['result'] = $this->result;
+		return $this->debug;
+	}
+
+	/**
+	 * set debugging info
+	 *
+	 * @param string $key
+	 * @param mixed $data
+	 * @return void
+	 */
+	public function debugSet($key, $data = null) {
+		$this->debug[$key] = $data;
 	}
 
 	/**

+ 1 - 1
Lib/HttpSocketLib.php

@@ -186,7 +186,7 @@ class HttpSocketLib {
 	 * @param string Correctly encoded response
 	 */
 	protected function _assertEncoding($response) {
-		if (!WINDOWS) {
+		if (!defined('WINDOWS') || !WINDOWS) {
 			return $response;
 		}
 		$x = mb_detect_encoding($response, 'auto', true);

+ 63 - 50
Model/Behavior/GeocoderBehavior.php

@@ -94,80 +94,93 @@ class GeocoderBehavior extends ModelBehavior {
 		if ($this->settings[$Model->alias]['real']) {
 			foreach ($addressfields as $field) {
 				if (!$Model->hasField($field)) {
+					//debug("Field Missing: {$field}");
 					return $return;
 				}
 			}
 		}
 
-		$adressdata = array();
+		$addressData = array();
 		foreach ($addressfields as $field) {
 			if (!empty($Model->data[$Model->alias][$field])) {
-				$adressdata[] = $Model->data[$Model->alias][$field];
+				$addressData[] = $Model->data[$Model->alias][$field];
 			}
 		}
 
 		$Model->data[$Model->alias]['geocoder_result'] = array();
 
 		// See if we should geocode //TODO: reverse and return here
-		if ((!$this->settings[$Model->alias]['real'] || ($Model->hasField($this->settings[$Model->alias]['lat']) && $Model->hasField($this->settings[$Model->alias]['lng']))) &&
-			($this->settings[$Model->alias]['overwrite'] || (empty($Model->data[$Model->alias][$this->settings[$Model->alias]['lat']]) || ((int)$Model->data[$Model->alias][$this->settings[$Model->alias]['lat']] === 0 && (int)$Model->data[$Model->alias][$this->settings[$Model->alias]['lng']] === 0)))) {
-			if (!empty($Model->whitelist) && (!in_array($this->settings[$Model->alias]['lat'], $Model->whitelist) || !in_array($this->settings[$Model->alias]['lng'], $Model->whitelist))) {
-				/** HACK to prevent 0 inserts if not wanted! just use whitelist now to narrow fields down - 2009-03-18 ms */
-				//$Model->whitelist[] = $this->settings[$Model->alias]['lat'];
-				//$Model->whitelist[] = $this->settings[$Model->alias]['lng'];
-				return $return;
-			}
+		$fieldsExist = (!$this->settings[$Model->alias]['real'] || ($Model->hasField($this->settings[$Model->alias]['lat']) && $Model->hasField($this->settings[$Model->alias]['lng'])));
+		if (!$fieldsExist) {
+			//debug(compact('fieldsExist'));
+			return false;
+		}
+
+		$existingValues = (!empty($Model->data[$Model->alias][$this->settings[$Model->alias]['lat']]) && !empty($Model->data[$Model->alias][$this->settings[$Model->alias]['lng']]));
+
+		if ($existingValues && !$this->settings[$Model->alias]['overwrite']) {
+			//debug(compact('existingValues'));
+			return false;
+		}
+
+		// yup - we are geocoding
+		if (!empty($Model->whitelist) && (!in_array($this->settings[$Model->alias]['lat'], $Model->whitelist) || !in_array($this->settings[$Model->alias]['lng'], $Model->whitelist))) {
+			/** HACK to prevent 0 inserts if not wanted! just use whitelist now to narrow fields down - 2009-03-18 ms */
+			//$Model->whitelist[] = $this->settings[$Model->alias]['lat'];
+			//$Model->whitelist[] = $this->settings[$Model->alias]['lng'];
+			return $return;
+		}
+
+		$geocode = $this->_geocode($addressData, $this->settings[$Model->alias]);
+		//debug(compact('addressData', 'geocode'));
 
-			$geocode = $this->_geocode($adressdata, $this->settings[$Model->alias]);
+		if (empty($geocode) && !empty($this->settings[$Model->alias]['allowEmpty'])) {
+			return true;
+		}
+		if (empty($geocode)) {
+			return false;
+		}
 
-			if (empty($geocode) && !empty($this->settings[$Model->alias]['allowEmpty'])) {
-				return true;
+		// if both are 0, thats not valid, otherwise continue
+		if (!empty($geocode['lat']) || !empty($geocode['lng'])) { /** HACK to prevent 0 inserts of incorrect runs - 2009-04-07 ms */
+			$Model->data[$Model->alias][$this->settings[$Model->alias]['lat']] = $geocode['lat'];
+			$Model->data[$Model->alias][$this->settings[$Model->alias]['lng']] = $geocode['lng'];
+		} else {
+			if (isset($Model->data[$Model->alias][$this->settings[$Model->alias]['lat']])) {
+				unset($Model->data[$Model->alias][$this->settings[$Model->alias]['lat']]);
 			}
-			if (empty($geocode)) {
-				return false;
+			if (isset($Model->data[$Model->alias][$this->settings[$Model->alias]['lng']])) {
+				unset($Model->data[$Model->alias][$this->settings[$Model->alias]['lng']]);
 			}
-
-			// if both are 0, thats not valid, otherwise continue
-			if (!empty($geocode['lat']) || !empty($geocode['lng'])) { /** HACK to prevent 0 inserts of incorrect runs - 2009-04-07 ms */
-				$Model->data[$Model->alias][$this->settings[$Model->alias]['lat']] = $geocode['lat'];
-				$Model->data[$Model->alias][$this->settings[$Model->alias]['lng']] = $geocode['lng'];
-			} else {
-				if (isset($Model->data[$Model->alias][$this->settings[$Model->alias]['lat']])) {
-					unset($Model->data[$Model->alias][$this->settings[$Model->alias]['lat']]);
-				}
-				if (isset($Model->data[$Model->alias][$this->settings[$Model->alias]['lng']])) {
-					unset($Model->data[$Model->alias][$this->settings[$Model->alias]['lng']]);
-				}
-				if ($this->settings[$Model->alias]['require']) {
-					if ($fields = $this->settings[$Model->alias]['invalidate']) {
-						$Model->invalidate($fields[0], $fields[1], isset($fields[2]) ? $fields[2] : true);
-					}
-					return false;
+			if ($this->settings[$Model->alias]['require']) {
+				if ($fields = $this->settings[$Model->alias]['invalidate']) {
+					$Model->invalidate($fields[0], $fields[1], isset($fields[2]) ? $fields[2] : true);
 				}
+				return false;
 			}
+		}
 
-			if (!empty($this->settings[$Model->alias]['formatted_address'])) {
-				$Model->data[$Model->alias][$this->settings[$Model->alias]['formatted_address']] = $geocode['formatted_address'];
-			} else {
-				if (isset($Model->data[$Model->alias][$this->settings[$Model->alias]['formatted_address']])) {
-					unset($Model->data[$Model->alias][$this->settings[$Model->alias]['formatted_address']]);
-				}
+		if (!empty($this->settings[$Model->alias]['formatted_address'])) {
+			$Model->data[$Model->alias][$this->settings[$Model->alias]['formatted_address']] = $geocode['formatted_address'];
+		} else {
+			if (isset($Model->data[$Model->alias][$this->settings[$Model->alias]['formatted_address']])) {
+				unset($Model->data[$Model->alias][$this->settings[$Model->alias]['formatted_address']]);
 			}
+		}
 
-			if (!empty($geocode['inconclusive'])) {
-				$Model->data[$Model->alias]['geocoder_inconclusive'] = $geocode['inconclusive'];
-				$Model->data[$Model->alias]['geocoder_results'] = $geocode['results'];
-			} else {
-				$Model->data[$Model->alias]['geocoder_result'] = $geocode;
-			}
+		if (!empty($geocode['inconclusive'])) {
+			$Model->data[$Model->alias]['geocoder_inconclusive'] = $geocode['inconclusive'];
+			$Model->data[$Model->alias]['geocoder_results'] = $geocode['results'];
+		} else {
+			$Model->data[$Model->alias]['geocoder_result'] = $geocode;
+		}
 
-			$Model->data[$Model->alias]['geocoder_result']['address_data'] = implode(' ', $adressdata);
+		$Model->data[$Model->alias]['geocoder_result']['address_data'] = implode(' ', $addressData);
 
-			if (!empty($this->settings[$Model->alias]['update'])) {
-				foreach ($this->settings[$Model->alias]['update'] as $key => $field) {
-					if (!empty($geocode[$key])) {
-						$Model->data[$Model->alias][$field] = $geocode[$key];
-					}
+		if (!empty($this->settings[$Model->alias]['update'])) {
+			foreach ($this->settings[$Model->alias]['update'] as $key => $field) {
+				if (!empty($geocode[$key])) {
+					$Model->data[$Model->alias][$field] = $geocode[$key];
 				}
 			}
 		}

+ 33 - 15
Test/Case/Lib/GeocodeLibTest.php

@@ -75,8 +75,8 @@ class GeocodeLibTest extends MyCakeTestCase {
 
 	public function testUrl() {
 		$is = $this->Geocode->url();
-		//debug($is);
-		$this->assertTrue(!empty($is) && strpos($is, 'http://maps.googleapis.com/maps/api/geocode/xml?') === 0);
+		$this->assertFalse(empty($is));
+		$this->assertPattern('#https://maps.googleapis.com/maps/api/geocode/(json|xml)\?.+#', $is);
 	}
 
 	// not possible with protected method
@@ -168,6 +168,16 @@ class GeocodeLibTest extends MyCakeTestCase {
 		$this->assertTrue(empty($is));
 	}
 
+	public function testGeocodeBadApiKey() {
+		$address = 'Oranienburger Straße 87, 10178 Berlin, Deutschland';
+		$is = $this->Geocode->geocode($address, array('sensor' => false, 'key' => 'testingBadApiKey'));
+		$this->assertFalse($is);
+		//pr($this->Geocode->debug());
+		$is = $this->Geocode->error();
+		$this->assertEqual('Error REQUEST_DENIED (The provided API key is invalid.)', $is);
+
+	}
+
 	public function testGeocodeInvalid() {
 		$address = 'Hjfjosdfhosj, 78878 Mdfkufsdfk';
 		//echo '<h2>'.$address.'</h2>';
@@ -182,35 +192,43 @@ class GeocodeLibTest extends MyCakeTestCase {
 		$this->assertTrue(!empty($is));
 	}
 
+	public function testGetMaxAddress() {
+		$this->assertEqual($this->Geocode->_getMaxAccuracy(array('street_address' => 'abc')), GeocodeLib::ACC_STREET);
+		$this->assertEqual($this->Geocode->_getMaxAccuracy(array('intersection' => 'abc')), GeocodeLib::ACC_INTERSEC);
+		$this->assertEqual($this->Geocode->_getMaxAccuracy(array('route' => 'abc')), GeocodeLib::ACC_ROUTE);
+		$this->assertEqual($this->Geocode->_getMaxAccuracy(array('sublocality' => 'abc')), GeocodeLib::ACC_SUBLOC);
+		$this->assertEqual($this->Geocode->_getMaxAccuracy(array('locality' => 'abc')), GeocodeLib::ACC_LOC);
+		$this->assertEqual($this->Geocode->_getMaxAccuracy(array('postal_code' => 'abc')), GeocodeLib::ACC_POSTAL);
+		$this->assertEqual($this->Geocode->_getMaxAccuracy(array('country' => 'aa')), GeocodeLib::ACC_COUNTRY);
+		$this->assertEqual($this->Geocode->_getMaxAccuracy(array()), GeocodeLib::ACC_COUNTRY);
+		// mixed
+		$this->assertEqual($this->Geocode->_getMaxAccuracy(array(
+			'country' => 'aa',
+			'postal_code' => 'abc',
+			'locality' => '',
+			'street_address' => '',
+		)), GeocodeLib::ACC_POSTAL);
+	}
+
 	public function testGeocodeMinAcc() {
+		// address = postal_code, minimum = street level
 		$address = 'Deutschland';
-		//echo '<h2>'.$address.'</h2>';
-		$this->Geocode->setOptions(array('min_accuracy' => 3));
+		$this->Geocode->setOptions(array('min_accuracy' => GeocodeLib::ACC_STREET));
 		$is = $this->Geocode->geocode($address);
-		//debug($is);
 		$this->assertFalse($is);
-
 		$is = $this->Geocode->error();
-		//debug($is);
 		$this->assertTrue(!empty($is));
 	}
 
 	public function testGeocodeInconclusive() {
 		// seems like there is no inconclusive result anymore!!!
-
 		$address = 'Neustadt';
-		//echo '<h2>'.$address.'</h2>';
 
 		// allow_inconclusive = TRUE
-		$this->Geocode->setOptions(array('allow_inconclusive' => true, 'min_accuracy' => GeocodeLib::ACC_LOC));
+		$this->Geocode->setOptions(array('allow_inconclusive' => true, 'min_accuracy' => GeocodeLib::ACC_POSTAL));
 		$is = $this->Geocode->geocode($address);
-		//echo 'debug:';
-		//pr($this->Geocode->debug());
-		//echo 'debug end';
 		$this->assertTrue($is);
-
 		$res = $this->Geocode->getResult();
-		//pr($res);
 		$this->assertTrue(count($res) > 4);
 
 		$is = $this->Geocode->isInconclusive();