euromark 11 年 前
コミット
a831e36841

+ 124 - 165
Lib/GeocodeLib.php

@@ -1,7 +1,5 @@
 <?php
 <?php
 App::uses('String', 'Utility');
 App::uses('String', 'Utility');
-App::uses('Xml', 'Utility');
-App::uses('Hash', 'Utility');
 App::uses('HttpSocketLib', 'Tools.Lib');
 App::uses('HttpSocketLib', 'Tools.Lib');
 
 
 /**
 /**
@@ -70,15 +68,13 @@ class GeocodeLib {
 		'min_accuracy' => self::ACC_COUNTRY,
 		'min_accuracy' => self::ACC_COUNTRY,
 		'allow_inconclusive' => true,
 		'allow_inconclusive' => true,
 		'expect' => array(), # see accuracyTypes for details
 		'expect' => array(), # see accuracyTypes for details
-		// static url params
-		'output' => 'json',
 		'host' => null, # results in maps.google.com - use if you wish to obtain the closest address
 		'host' => null, # results in maps.google.com - use if you wish to obtain the closest address
 	);
 	);
 
 
 	/**
 	/**
 	 * Url params
 	 * Url params
 	 */
 	 */
-	protected $params = array(
+	public $params = array(
 		'address' => '', # either address or latlng required!
 		'address' => '', # either address or latlng required!
 		'latlng' => '', # The textual latitude/longitude value for which you wish to obtain the closest, human-readable address
 		'latlng' => '', # The textual latitude/longitude value for which you wish to obtain the closest, human-readable address
 		'region' => '', # The region code, specified as a ccTLD ("top-level domain") two-character
 		'region' => '', # The region code, specified as a ccTLD ("top-level domain") two-character
@@ -93,7 +89,7 @@ class GeocodeLib {
 
 
 	protected $result = null;
 	protected $result = null;
 
 
-	protected $statusCodes = array(
+	public $statusCodes = array(
 		self::CODE_SUCCESS => 'Success',
 		self::CODE_SUCCESS => 'Success',
 		self::CODE_BAD_REQUEST => 'Sensor param missing',
 		self::CODE_BAD_REQUEST => 'Sensor param missing',
 		self::CODE_MISSING_QUERY => 'Adress/LatLng missing',
 		self::CODE_MISSING_QUERY => 'Adress/LatLng missing',
@@ -101,18 +97,17 @@ class GeocodeLib {
 		self::CODE_TOO_MANY_QUERIES => 'Limit exceeded',
 		self::CODE_TOO_MANY_QUERIES => 'Limit exceeded',
 	);
 	);
 
 
-	protected $accuracyTypes = array(
+	public $accuracyTypes = array(
 		self::ACC_COUNTRY => 'country',
 		self::ACC_COUNTRY => 'country',
 		self::ACC_AAL1 => 'administrative_area_level_1', # provinces/states
 		self::ACC_AAL1 => 'administrative_area_level_1', # provinces/states
 		self::ACC_AAL2 => 'administrative_area_level_2 ',
 		self::ACC_AAL2 => 'administrative_area_level_2 ',
 		self::ACC_AAL3 => 'administrative_area_level_3',
 		self::ACC_AAL3 => 'administrative_area_level_3',
-		self::ACC_POSTAL => 'postal_code',
 		self::ACC_LOC => 'locality',
 		self::ACC_LOC => 'locality',
+		self::ACC_POSTAL => 'postal_code',
 		self::ACC_SUBLOC => 'sublocality',
 		self::ACC_SUBLOC => 'sublocality',
 		self::ACC_ROUTE => 'route',
 		self::ACC_ROUTE => 'route',
 		self::ACC_INTERSEC => 'intersection',
 		self::ACC_INTERSEC => 'intersection',
-		self::ACC_STREET => 'street_address'
-		//neighborhood premise subpremise natural_feature airport park point_of_interest colloquial_area political ?
+		self::ACC_STREET => 'street_address',
 	);
 	);
 
 
 	public function __construct($options = array()) {
 	public function __construct($options = array()) {
@@ -147,9 +142,6 @@ class GeocodeLib {
 	 */
 	 */
 	public function setOptions($options) {
 	public function setOptions($options) {
 		foreach ($options as $key => $value) {
 		foreach ($options as $key => $value) {
-			if ($key === 'output' && $value !== 'xml' && $value !== 'json') {
-				throw new CakeException('Invalid output format');
-			}
 			$this->options[$key] = $value;
 			$this->options[$key] = $value;
 		}
 		}
 	}
 	}
@@ -158,7 +150,7 @@ class GeocodeLib {
 		if (empty($error)) {
 		if (empty($error)) {
 			return;
 			return;
 		}
 		}
-		$this->debugSet('setError', $error);
+		$this->_setDebug('setError', $error);
 		$this->error[] = $error;
 		$this->error[] = $error;
 	}
 	}
 
 
@@ -197,7 +189,7 @@ class GeocodeLib {
 	public function url() {
 	public function url() {
 		$params = array(
 		$params = array(
 			'host' => $this->options['host'],
 			'host' => $this->options['host'],
-			'output' => $this->options['output']
+			'output' => 'json'
 		);
 		);
 		$url = String::insert(self::BASE_URL, $params, array('before' => '{', 'after' => '}', 'clean' => true));
 		$url = String::insert(self::BASE_URL, $params, array('before' => '{', 'after' => '}', 'clean' => true));
 		$params = array();
 		$params = array();
@@ -210,16 +202,15 @@ class GeocodeLib {
 	}
 	}
 
 
 	/**
 	/**
+	 * Seems like there are no inconclusive results anymore...
+	 *
 	 * @return boolean isInconclusive (or null if no query has been run yet)
 	 * @return boolean isInconclusive (or null if no query has been run yet)
 	 */
 	 */
 	public function isInconclusive() {
 	public function isInconclusive() {
 		if ($this->result === null) {
 		if ($this->result === null) {
 			return null;
 			return null;
 		}
 		}
-		if (array_key_exists('location_type', $this->result) && !empty($this->result['location_type'])) {
-			return true;
-		}
-		return false;
+		return !empty($this->result['valid_results']) && $this->result['valid_results'] > 1;
 	}
 	}
 
 
 	/**
 	/**
@@ -231,15 +222,6 @@ class GeocodeLib {
 		if ($this->result === null) {
 		if ($this->result === null) {
 			return array();
 			return array();
 		}
 		}
-
-		if (is_string($this->result)) {
-			return $this->_transform($this->result);
-		}
-
-		if (!is_array($this->result)) {
-			return array();
-		}
-
 		return $this->result;
 		return $this->result;
 	}
 	}
 
 
@@ -249,13 +231,11 @@ class GeocodeLib {
 	 * @param float $lat
 	 * @param float $lat
 	 * @param float $lng
 	 * @param float $lng
 	 * @param array $params
 	 * @param array $params
-	 * - allow_inconclusive
-	 * - min_accuracy
 	 * @return boolean Success
 	 * @return boolean Success
 	 */
 	 */
 	public function reverseGeocode($lat, $lng, $params = array()) {
 	public function reverseGeocode($lat, $lng, $params = array()) {
 		$this->reset(false);
 		$this->reset(false);
-		$this->debugSet('reverseGeocode', compact('lat', 'lng', 'params'));
+		$this->_setDebug('reverseGeocode', compact('lat', 'lng', 'params'));
 		$latlng = $lat . ',' . $lng;
 		$latlng = $lat . ',' . $lng;
 		$this->setParams(array_merge($params, array('latlng' => $latlng)));
 		$this->setParams(array_merge($params, array('latlng' => $latlng)));
 
 
@@ -269,7 +249,7 @@ class GeocodeLib {
 				return false;
 				return false;
 			}
 			}
 
 
-			$this->debugSet('raw', $result);
+			$this->_setDebug('raw', $result);
 			$result = $this->_transform($result);
 			$result = $this->_transform($result);
 			if (!is_array($result)) {
 			if (!is_array($result)) {
 				$this->setError('Result parsing failed');
 				$this->setError('Result parsing failed');
@@ -280,25 +260,7 @@ class GeocodeLib {
 			$status = $result['status'];
 			$status = $result['status'];
 
 
 			if ($status == self::CODE_SUCCESS) {
 			if ($status == self::CODE_SUCCESS) {
-
-				// validate
-				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($result['results'][0])) {
-					$result['result'] = $result['results'][0];
-				}
-
-				$accuracy = $this->_getMaxAccuracy($result['result']);
-
-				if ($this->_isNotAccurateEnough($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 = $result['result'];
+				if (!$this->_process($result)) {
 					return false;
 					return false;
 				}
 				}
 
 
@@ -333,7 +295,7 @@ class GeocodeLib {
 			}
 			}
 			$this->pause(true);
 			$this->pause(true);
 		}
 		}
-		$this->result = $result['result'];
+
 		return true;
 		return true;
 	}
 	}
 
 
@@ -351,7 +313,7 @@ class GeocodeLib {
 	/**
 	/**
 	 * Actual querying.
 	 * Actual querying.
 	 * The query will be flatted, and if multiple results are fetched, they will be found
 	 * The query will be flatted, and if multiple results are fetched, they will be found
-	 * int $result['all'].
+	 * in $result['all'].
 	 *
 	 *
 	 * @param string $address
 	 * @param string $address
 	 * @param array $params
 	 * @param array $params
@@ -359,12 +321,8 @@ class GeocodeLib {
 	 */
 	 */
 	public function geocode($address, $params = array()) {
 	public function geocode($address, $params = array()) {
 		$this->reset(false);
 		$this->reset(false);
-		$this->debugSet('reverseGeocode', compact('address', 'params'));
+		$this->_setDebug('geocode', compact('address', 'params'));
 		$this->setParams(array_merge($params, array('address' => $address)));
 		$this->setParams(array_merge($params, array('address' => $address)));
-		if ($this->options['allow_inconclusive']) {
-			// only host working with this setting?
-			//$this->options['host'] = self::DEFAULT_HOST;
-		}
 
 
 		$count = 0;
 		$count = 0;
 		$requestUrl = $this->url();
 		$requestUrl = $this->url();
@@ -377,7 +335,7 @@ class GeocodeLib {
 				return false;
 				return false;
 			}
 			}
 
 
-			$this->debugSet('raw', $result);
+			$this->_setDebug('raw', $result);
 			$result = $this->_transform($result);
 			$result = $this->_transform($result);
 			if (!is_array($result)) {
 			if (!is_array($result)) {
 				$this->setError('Result parsing failed');
 				$this->setError('Result parsing failed');
@@ -386,43 +344,12 @@ class GeocodeLib {
 			}
 			}
 
 
 			$status = $result['status'];
 			$status = $result['status'];
-			//debug(compact('result', 'requestUrl', 'success'));
 
 
 			if ($status == self::CODE_SUCCESS) {
 			if ($status == self::CODE_SUCCESS) {
-				// validate
-				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($result['results'][0])) {
-					$result['result'] = $result['results'][0];
-				}
-
-				$accuracy = $this->_getMaxAccuracy($result['result']);
-
-				if ($this->_isNotAccurateEnough($accuracy)) {
-					$accuracyText = $this->accuracyTypes[$accuracy];
-					$minAccuracy = $this->accuracyTypes[$this->options['min_accuracy']];
-					$this->setError(__('Accuracy not good enough (%s instead of at least %s)', $accuracyText, $minAccuracy));
-					$this->result = $result['result'];
+				if (!$this->_process($result)) {
 					return false;
 					return false;
 				}
 				}
 
 
-				if (!empty($this->options['expect'])) {
-					$fields = (empty($result['result']['types']) ? array() : Hash::filter($result['result']['types']));
-					$found = array_intersect($fields, (array)$this->options['expect']);
-					$validExpectation = !empty($found);
-					if (!$validExpectation) {
-						$this->setError(__('Expectation not reached (we have %s instead of at least %s)',
-							implode(', ', $found),
-							implode(', ', (array)$this->options['expect'])
-						));
-						$this->result = $result['result'];
-						return false;
-					}
-				}
-
 				// save Result
 				// save Result
 				if ($this->options['log']) {
 				if ($this->options['log']) {
 					CakeLog::write('geocode', __('Address \'%s\' has been geocoded', $address));
 					CakeLog::write('geocode', __('Address \'%s\' has been geocoded', $address));
@@ -463,9 +390,61 @@ class GeocodeLib {
 			$this->pause(true);
 			$this->pause(true);
 		}
 		}
 
 
-		$this->result = $result['result'];
-		if (!empty($result['results']) && count($result['results']) > 1) {
-			$this->result['all'] = $result['results'];
+		return true;
+	}
+
+	/**
+	 * GeocodeLib::_process()
+	 *
+	 * @param mixed $result
+	 * @return boolean Success
+	 */
+	protected function _process($result) {
+		$this->result = null;
+		$validResults = 0;
+
+		foreach ($result['results'] as $res) {
+			if (!$res['valid_type']) {
+				continue;
+			}
+			$validResults++;
+			if (isset($this->result)) {
+				continue;
+			}
+			$this->result = $res;
+		}
+		$this->result['valid_results'] = $validResults;
+		$this->result['all'] = $result['results'];
+
+		// validate
+		if (!$this->options['allow_inconclusive'] && $validResults > 1) {
+			$this->setError(__('Inconclusive result (total of %s)', $validResults));
+			return false;
+		}
+
+		if ($this->_isNotAccurateEnough($this->result['accuracy'])) {
+			$minAccuracy = $this->accuracyTypes[$this->options['min_accuracy']];
+			$this->setError(__('Accuracy not good enough (%s instead of at least %s)', $this->result['accuracy_name'], $minAccuracy));
+
+			return false;
+		}
+
+		if (!empty($this->options['expect'])) {
+			$expected = (array)$this->options['expect'];
+			foreach ($expected as $k => $v) {
+				$accuracy = is_int($v) ? $this->accuracyTypes[$v] : $v;
+				$expected[$k] = $accuracy;
+			}
+			$found = array_intersect($this->result['types'], $expected);
+			$validExpectation = !empty($found);
+
+			if (!$validExpectation) {
+				$this->setError(__('Expectation not reached (we have %s instead of at least one of %s)',
+					implode(', ', $found),
+					implode(', ', $expected)
+				));
+				return false;
+			}
 		}
 		}
 		return true;
 		return true;
 	}
 	}
@@ -486,21 +465,39 @@ class GeocodeLib {
 		return $this->accuracyTypes;
 		return $this->accuracyTypes;
 	}
 	}
 
 
+	protected function _validate($result) {
+		$validType = false;
+		foreach ($result['types'] as $type) {
+			if (in_array($type, $this->accuracyTypes, true)) {
+				$validType = true;
+				break;
+			}
+		}
+		$result['valid_type'] = $validType;
+		return $result;
+	}
+
+	protected function _accuracy($result) {
+		$accuracyTypes = array_reverse($this->accuracyTypes, true);
+
+		$accuracy = 0;
+		foreach ($accuracyTypes as $key => $field) {
+			if (array_key_exists($field, $result) && !empty($result[$field])) {
+				$accuracy = $key;
+				break;
+			}
+		}
+
+		$result['accuracy'] = $accuracy;
+		$result['accuracy_name'] = $this->accuracyTypes[$accuracy];
+		return $result;
+	}
+
 	/**
 	/**
+	 * @param int $accuracy
 	 * @return boolean $notAccurateEnough
 	 * @return boolean $notAccurateEnough
 	 */
 	 */
-	protected function _isNotAccurateEnough($accuracy = null) {
-		if (is_array($accuracy)) {
-			$accuracy = $this->_getMaxAccuracy($accuracy);
-		}
-		if (empty($accuracy)) {
-			$accuracy = 0;
-		}
-		// did we get a value instead of a key?
-		if (in_array($accuracy, $this->accuracyTypes, true)) {
-			$accuracy = array_search($accuracy, $this->accuracyTypes);
-		}
-		// validate key exists
+	protected function _isNotAccurateEnough($accuracy) {
 		if (!array_key_exists($accuracy, $this->accuracyTypes)) {
 		if (!array_key_exists($accuracy, $this->accuracyTypes)) {
 			$accuracy = 0;
 			$accuracy = 0;
 		}
 		}
@@ -511,43 +508,15 @@ class GeocodeLib {
 	/**
 	/**
 	 * GeocodeLib::_transform()
 	 * GeocodeLib::_transform()
 	 *
 	 *
-	 * @param string $record
+	 * @param string|array $record JSON string or array
 	 * @return array
 	 * @return array
 	 */
 	 */
 	protected function _transform($record) {
 	protected function _transform($record) {
-		if ($this->options['output'] === 'json') {
-			return $this->_transformJson($record);
-		}
-		return $this->_transformXml($record);
-	}
-
-	/**
-	 * GeocodeLib::_transformJson()
-	 *
-	 * @param string $record
-	 * @return array
-	 */
-	protected function _transformJson($record) {
 		if (!is_array($record)) {
 		if (!is_array($record)) {
 			$record = json_decode($record, true);
 			$record = json_decode($record, true);
 		}
 		}
-		return $this->_transformData($record);
-	}
-
-	/**
-	 * @return array
-	 * @deprecated
-	 */
-	protected function _transformXml($record) {
-		//trigger_error('deprecated, use json instead', E_USER_DEPRECATED);
-		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);
+		$record['results'] = $this->_transformData($record['results']);
+		return $record;
 	}
 	}
 
 
 	/**
 	/**
@@ -556,14 +525,13 @@ class GeocodeLib {
 	 *    attempt to find the first record which matches an accuracyTypes field
 	 *    attempt to find the first record which matches an accuracyTypes field
 	 *
 	 *
 	 * @param array $record
 	 * @param array $record
-	 * @return int $maxAccuracy 9-0 as defined in $this->accuracyTypes
+	 * @return int|null $maxAccuracy 9-0 as defined in $this->accuracyTypes
 	 */
 	 */
-	public function _getMaxAccuracy($record) {
+	protected function _getMaxAccuracy($record) {
 		if (!is_array($record)) {
 		if (!is_array($record)) {
 			return null;
 			return null;
 		}
 		}
-		$accuracyTypes = $this->accuracyTypes;
-		$accuracyTypes = array_reverse($accuracyTypes, true);
+		$accuracyTypes = array_reverse($this->accuracyTypes, true);
 		foreach ($accuracyTypes as $key => $field) {
 		foreach ($accuracyTypes as $key => $field) {
 			if (array_key_exists($field, $record) && !empty($record[$field])) {
 			if (array_key_exists($field, $record) && !empty($record[$field])) {
 				// found $field -- return it's $key
 				// found $field -- return it's $key
@@ -595,9 +563,6 @@ class GeocodeLib {
 	 * @return array $record organized & normalized
 	 * @return array $record organized & normalized
 	 */
 	 */
 	protected function _transformData($record) {
 	protected function _transformData($record) {
-		if (!is_array($record)) {
-			return $record;
-		}
 		if (!array_key_exists('address_components', $record)) {
 		if (!array_key_exists('address_components', $record)) {
 			foreach (array_keys($record) as $key) {
 			foreach (array_keys($record) as $key) {
 				$record[$key] = $this->_transformData($record[$key]);
 				$record[$key] = $this->_transformData($record[$key]);
@@ -609,31 +574,18 @@ class GeocodeLib {
 
 
 		// handle and organize address_components
 		// handle and organize address_components
 		$components = array();
 		$components = array();
-		if (!isset($record['address_components'][0])) {
-			$record['address_components'] = array($record['address_components']);
-		}
 		foreach ($record['address_components'] as $c) {
 		foreach ($record['address_components'] as $c) {
 			$types = array();
 			$types = array();
-			if (isset($c['types'])) { //!is_array($c['Type'])
-				if (!is_array($c['types'])) {
-					$c['types'] = (array)$c['types'];
-				}
+			$type = $c['types'][0];
+			$types = $c['types'];
 
 
-				$type = $c['types'][0];
-				array_shift($c['types']);
-				$types = $c['types'];
-			} elseif (isset($c['types'])) {
-				$type = $c['types'];
-			} else {
-				// error?
-				continue;
-			}
 			if (array_key_exists($type, $components)) {
 			if (array_key_exists($type, $components)) {
 				$components[$type]['name'] .= ' ' . $c['long_name'];
 				$components[$type]['name'] .= ' ' . $c['long_name'];
 				$components[$type]['abbr'] .= ' ' . $c['short_name'];
 				$components[$type]['abbr'] .= ' ' . $c['short_name'];
 				$components[$type]['types'] += $types;
 				$components[$type]['types'] += $types;
+			} else {
+				$components[$type] = array('name' => $c['long_name'], 'abbr' => $c['short_name'], 'types' => $types);
 			}
 			}
-			$components[$type] = array('name' => $c['long_name'], 'abbr' => $c['short_name'], 'types' => $types);
 		}
 		}
 
 
 		$res['formatted_address'] = $record['formatted_address'];
 		$res['formatted_address'] = $record['formatted_address'];
@@ -705,9 +657,17 @@ class GeocodeLib {
 		if (!empty($res['country_province_code']) && array_key_exists($res['country_province_code'], $array)) {
 		if (!empty($res['country_province_code']) && array_key_exists($res['country_province_code'], $array)) {
 			$res['country_province_code'] = $array[$res['country_province_code']];
 			$res['country_province_code'] = $array[$res['country_province_code']];
 		}
 		}
+		if (!empty($record['postcode_localities'])) {
+			$res['postcode_localities'] = $record['postcode_localities'];
+		}
+		if (!empty($record['address_components'])) {
+			$res['address_components'] = $record['address_components'];
+		}
+
+		$res = $this->_validate($res);
+		$res = $this->_accuracy($res);
 
 
-		// inject maxAccuracy for transparency
-		$res['maxAccuracy'] = $this->_getMaxAccuracy($res);
+		//debug($res);die();
 		return $res;
 		return $res;
 	}
 	}
 
 
@@ -720,7 +680,6 @@ class GeocodeLib {
 	 **/
 	 **/
 	protected function _fetch($url) {
 	protected function _fetch($url) {
 		$this->HttpSocket = new HttpSocketLib($this->use);
 		$this->HttpSocket = new HttpSocketLib($this->use);
-		$this->debugSet('_fetch', $url);
 		if ($res = $this->HttpSocket->fetch($url, 'CakePHP Geocode Lib')) {
 		if ($res = $this->HttpSocket->fetch($url, 'CakePHP Geocode Lib')) {
 			return $res;
 			return $res;
 		}
 		}
@@ -745,7 +704,7 @@ class GeocodeLib {
 	 * @param mixed $data
 	 * @param mixed $data
 	 * @return void
 	 * @return void
 	 */
 	 */
-	public function debugSet($key, $data = null) {
+	public function _setDebug($key, $data = null) {
 		$this->debug[$key] = $data;
 		$this->debug[$key] = $data;
 	}
 	}
 
 

+ 38 - 53
Model/Behavior/GeocoderBehavior.php

@@ -5,7 +5,12 @@ App::uses('GeocodeLib', 'Tools.Lib');
 /**
 /**
  * A geocoding behavior for CakePHP to easily geocode addresses.
  * A geocoding behavior for CakePHP to easily geocode addresses.
  * Uses the GeocodeLib for actual geocoding.
  * Uses the GeocodeLib for actual geocoding.
- * Also provides some useful geocoding tools like validation and distance conditions
+ * Also provides some useful geocoding tools like validation and distance conditions.
+ *
+ * Note that your lat/lng fields should be of type "float(10,6) DEFAULT NULL".
+ * NULL as default is important as invalid or not found addresses should result in NULL
+ * instead of 0.0 (which is a truthy value!).
+ * If you need 0.0, cast it in your beforeSave() callback.
  *
  *
  * @author Mark Scherer
  * @author Mark Scherer
  * @cakephp 2.x
  * @cakephp 2.x
@@ -14,6 +19,18 @@ App::uses('GeocodeLib', 'Tools.Lib');
  */
  */
 class GeocoderBehavior extends ModelBehavior {
 class GeocoderBehavior extends ModelBehavior {
 
 
+	protected $_defaultConfig = array(
+		'real' => false, 'address' => array('street', 'postal_code', 'city', 'country'),
+		'require' => false, 'allowEmpty' => true, 'invalidate' => array(), 'expect' => array(),
+		'lat' => 'lat', 'lng' => 'lng', 'formatted_address' => 'formatted_address',
+		'host' => null, 'language' => 'de', 'region' => '', 'bounds' => '',
+		'overwrite' => false, 'update' => array(), 'before' => 'save',
+		'min_accuracy' => GeocodeLib::ACC_COUNTRY, 'allow_inconclusive' => true, 'unit' => GeocodeLib::UNIT_KM,
+		'log' => true, // log successfull results to geocode.log (errors will be logged to error.log in either case)
+	);
+
+	public $Geocode;
+
 	/**
 	/**
 	 * Initiate behavior for the model using specified settings. Available settings:
 	 * Initiate behavior for the model using specified settings. Available settings:
 	 *
 	 *
@@ -36,24 +53,11 @@ class GeocoderBehavior extends ModelBehavior {
 	 * 			set to false if you only want to use the validation rules etc
 	 * 			set to false if you only want to use the validation rules etc
 	 *
 	 *
 	 * @param Model $Model The model using the behaviour
 	 * @param Model $Model The model using the behaviour
-	 * @param array $settings Settings to override for model.
+	 * @param array $config Settings to override for model.
 	 */
 	 */
-	public function setup(Model $Model, $settings = array()) {
-		$default = array(
-			'real' => false, 'address' => array('street', 'postal_code', 'city', 'country'),
-			'require' => false, 'allowEmpty' => true, 'invalidate' => array(), 'expect' => array(),
-			'lat' => 'lat', 'lng' => 'lng', 'formatted_address' => 'formatted_address',
-			'host' => null, 'language' => 'de', 'region' => '', 'bounds' => '',
-			'overwrite' => false, 'update' => array(), 'before' => 'save',
-			'min_accuracy' => GeocodeLib::ACC_COUNTRY, 'allow_inconclusive' => true, 'unit' => GeocodeLib::UNIT_KM,
-			'log' => true, // log successfull results to geocode.log (errors will be logged to error.log in either case)
-		);
-
-		if (!isset($this->settings[$Model->alias])) {
-			$this->settings[$Model->alias] = $default;
-		}
-
-		$this->settings[$Model->alias] = array_merge($this->settings[$Model->alias], is_array($settings) ? $settings : array());
+	public function setup(Model $Model, $config = array()) {
+		$this->settings[$Model->alias] = $this->_defaultConfig;
+		$this->settings[$Model->alias] = $config + $this->settings[$Model->alias];
 	}
 	}
 
 
 	public function beforeValidate(Model $Model, $options = array()) {
 	public function beforeValidate(Model $Model, $options = array()) {
@@ -109,29 +113,17 @@ class GeocoderBehavior extends ModelBehavior {
 
 
 		$Model->data[$Model->alias]['geocoder_result'] = array();
 		$Model->data[$Model->alias]['geocoder_result'] = array();
 
 
-		// See if we should geocode //TODO: reverse and return here
-		$fieldsExist = (!$this->settings[$Model->alias]['real'] || ($Model->hasField($this->settings[$Model->alias]['lat']) && $Model->hasField($this->settings[$Model->alias]['lng'])));
-		if (!$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 $return;
-		}
-
-		// 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;
+		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))) {
+				return $return;
+			}
 		}
 		}
 
 
 		$geocode = $this->_geocode($addressData, $this->settings[$Model->alias]);
 		$geocode = $this->_geocode($addressData, $this->settings[$Model->alias]);
-
+		debug($addressData);
+debug($geocode);ob_flush();
 		if (empty($geocode) && !empty($this->settings[$Model->alias]['allowEmpty'])) {
 		if (empty($geocode) && !empty($this->settings[$Model->alias]['allowEmpty'])) {
 			return true;
 			return true;
 		}
 		}
@@ -139,9 +131,9 @@ class GeocoderBehavior extends ModelBehavior {
 			return false;
 			return false;
 		}
 		}
 
 
-		// if both are 0, thats not valid, otherwise continue
+		// If both are 0, thats not valid, otherwise continue
 		if (empty($geocode['lat']) && empty($geocode['lng'])) {
 		if (empty($geocode['lat']) && empty($geocode['lng'])) {
-			// HACK to prevent 0 inserts of incorrect runs
+			// Prevent 0 inserts of incorrect runs
 			if (isset($Model->data[$Model->alias][$this->settings[$Model->alias]['lat']])) {
 			if (isset($Model->data[$Model->alias][$this->settings[$Model->alias]['lat']])) {
 				unset($Model->data[$Model->alias][$this->settings[$Model->alias]['lat']]);
 				unset($Model->data[$Model->alias][$this->settings[$Model->alias]['lat']]);
 			}
 			}
@@ -169,13 +161,7 @@ class GeocoderBehavior extends ModelBehavior {
 			}
 			}
 		}
 		}
 
 
-		if (!empty($geocode['inconclusive'])) {
-			$Model->data[$Model->alias]['geocoder_inconclusive'] = $geocode['inconclusive'];
-			$Model->data[$Model->alias]['geocoder_results'] = $geocode['all'];
-		} else {
-			$Model->data[$Model->alias]['geocoder_result'] = $geocode;
-		}
-
+		$Model->data[$Model->alias]['geocoder_result'] = $geocode;
 		$Model->data[$Model->alias]['geocoder_result']['address_data'] = implode(' ', $addressData);
 		$Model->data[$Model->alias]['geocoder_result']['address_data'] = implode(' ', $addressData);
 
 
 		if (!empty($this->settings[$Model->alias]['update'])) {
 		if (!empty($this->settings[$Model->alias]['update'])) {
@@ -186,6 +172,9 @@ class GeocoderBehavior extends ModelBehavior {
 			}
 			}
 		}
 		}
 
 
+		debug($this->settings[$Model->alias]);
+		debug($Model->data[$Model->alias]);
+
 		return $return;
 		return $return;
 	}
 	}
 
 
@@ -356,14 +345,10 @@ class GeocoderBehavior extends ModelBehavior {
 
 
 		$settings = array('language' => $options['language']);
 		$settings = array('language' => $options['language']);
 		if (!$this->Geocode->geocode($address, $settings)) {
 		if (!$this->Geocode->geocode($address, $settings)) {
-			return array('lat' => 0, 'lng' => 0, 'formatted_address' => '');
+			return array('lat' => null, 'lng' => null, 'formatted_address' => '');
 		}
 		}
 
 
-		$res = $this->Geocode->getResult();
-		if (isset($res[0])) {
-			$res = $res[0];
-		}
-		return $res;
+		return $this->Geocode->getResult();
 	}
 	}
 
 
 	/**
 	/**

+ 189 - 113
Test/Case/Lib/GeocodeLibTest.php

@@ -15,7 +15,6 @@ Configure::write('Google', array(
 
 
 class GeocodeLibTest extends MyCakeTestCase {
 class GeocodeLibTest extends MyCakeTestCase {
 
 
-
 	public $apiMockupReverseGeocode40206 = array(
 	public $apiMockupReverseGeocode40206 = array(
 		'reverseGeocode' => array(
 		'reverseGeocode' => array(
 			'lat' => '38.2643',
 			'lat' => '38.2643',
@@ -79,6 +78,100 @@ class GeocodeLibTest extends MyCakeTestCase {
 	}
 	}
 
 
 	/**
 	/**
+	 * GeocodeLibTest::testReverseGeocode()
+	 *
+	 * @return void
+	 */
+	public function testReverseGeocode() {
+		$coords = array(
+			array(-34.594445, -58.37446, 'Calle Florida 1134-1200, Buenos Aires'),
+			array(48.8934, 8.70492, 'B294, 75175 Pforzheim, Deutschland')
+		);
+
+		foreach ($coords as $coord) {
+			$is = $this->Geocode->reverseGeocode($coord[0], $coord[1]);
+			$this->assertTrue($is);
+
+			$is = $this->Geocode->getResult();
+			$this->assertTrue(!empty($is));
+			//debug($is);
+			$address = isset($is[0]) ? $is[0]['formatted_address'] : $is['formatted_address'];
+			$this->assertTextContains($coord[2], $address);
+		}
+	}
+
+	/**
+	 * Seems to return
+	 * - 'Bibersfelder Besen Weinstube, Luckenbacher Straße 1, 74523 Schwäbisch Hall, Deutschland'
+	 *	 - point_of_interest, school, establishment
+	 * - 'Bibersfeld, 74523 Schwäbisch Hall, Deutschland'
+	 *	 - sublocality, political
+	 *
+	 * @return void
+	 */
+	public function testGeocodeInconclusive() {
+		$address = 'Bibersfeld';
+
+		$this->Geocode->setOptions(array('allow_inconclusive' => true, 'min_accuracy' => GeocodeLib::ACC_POSTAL));
+		$is = $this->Geocode->geocode($address);
+		$this->assertTrue($is);
+		$res = $this->Geocode->getResult();
+
+		$is = $this->Geocode->isInconclusive();
+		$this->assertFalse($is);
+
+		// Fake inconclusive here by adding an additional type
+		$this->Geocode->accuracyTypes[99] = 'point_of_interest';
+		$this->Geocode->setOptions(array('allow_inconclusive' => false));
+		$is = $this->Geocode->geocode($address);
+		$this->assertFalse($is);
+
+		$is = $this->Geocode->isInconclusive();
+		$this->assertTrue($is);
+
+		$res = $this->Geocode->getResult();
+		$this->assertSame(2, $res['valid_results']);
+	}
+
+	/**
+	 * With lower min accuracy
+	 *
+	 * @return void
+	 */
+	public function testGeocodeInconclusiveMinAccuracy() {
+		$address = 'Bibersfeld';
+
+		$this->Geocode->setOptions(array('allow_inconclusive' => true, 'min_accuracy' => GeocodeLib::ACC_STREET));
+		$is = $this->Geocode->geocode($address);
+		$this->assertFalse($is);
+	}
+
+	/**
+	 * Seems to return
+	 * - 'Bibersfelder Besen Weinstube, Luckenbacher Straße 1, 74523 Schwäbisch Hall, Deutschland'
+	 *	 - point_of_interest, school, establishment
+	 * - 'Bibersfeld, 74523 Schwäbisch Hall, Deutschland'
+	 *	 - sublocality, political
+	 *
+	 * @return void
+	 */
+	public function testGeocodeExpect() {
+		$address = 'Bibersfeld';
+
+		$this->Geocode->setOptions(array(
+			'allow_inconclusive' => true,
+			'expect' => array(GeocodeLib::ACC_POSTAL, GeocodeLib::ACC_LOC, GeocodeLib::ACC_SUBLOC)));
+		$is = $this->Geocode->geocode($address);
+		$this->assertTrue($is);
+
+		$this->Geocode->setOptions(array(
+			'allow_inconclusive' => true,
+			'expect' => array(GeocodeLib::ACC_POSTAL, GeocodeLib::ACC_LOC)));
+		$is = $this->Geocode->geocode($address);
+		$this->assertFalse($is);
+	}
+
+	/**
 	 * GeocodeLibTest::testDistance()
 	 * GeocodeLibTest::testDistance()
 	 *
 	 *
 	 * @return void
 	 * @return void
@@ -146,8 +239,11 @@ class GeocodeLibTest extends MyCakeTestCase {
 		$this->assertPattern('#https://maps.googleapis.com/maps/api/geocode/(json|xml)\?.+#', $is);
 		$this->assertPattern('#https://maps.googleapis.com/maps/api/geocode/(json|xml)\?.+#', $is);
 	}
 	}
 
 
-	// not possible with protected method
-
+	/**
+	 * not possible with protected method
+	 *
+	 * @return void
+	 */
 	public function _testFetch() {
 	public function _testFetch() {
 		$url = 'http://maps.google.com/maps/api/geocode/xml?sensor=false&address=74523';
 		$url = 'http://maps.google.com/maps/api/geocode/xml?sensor=false&address=74523';
 		$is = $this->Geocode->_fetch($url);
 		$is = $this->Geocode->_fetch($url);
@@ -165,22 +261,6 @@ class GeocodeLibTest extends MyCakeTestCase {
 	}
 	}
 
 
 	/**
 	/**
-	 * @return void
-	 * @deprecated
-	 */
-	public function testWithXml() {
-		$this->Geocode->setOptions(array('output' => 'xml'));
-		$address = '74523 Deutschland';
-		//echo '<h2>'.$address.'</h2>';
-		$is = $this->Geocode->geocode($address);
-		$this->assertTrue($is);
-
-		$is = $this->Geocode->getResult();
-		//debug($is);
-		$this->assertTrue(!empty($is));
-	}
-
-	/**
 	 * GeocodeLibTest::testSetOptions()
 	 * GeocodeLibTest::testSetOptions()
 	 *
 	 *
 	 * @return void
 	 * @return void
@@ -256,12 +336,11 @@ class GeocodeLibTest extends MyCakeTestCase {
 	 */
 	 */
 	public function testGeocodeBadApiKey() {
 	public function testGeocodeBadApiKey() {
 		$address = 'Oranienburger Straße 87, 10178 Berlin, Deutschland';
 		$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);
+		$result = $this->Geocode->geocode($address, array('sensor' => false, 'key' => 'testingBadApiKey'));
+		$this->assertFalse($result);
 
 
+		$result = $this->Geocode->error();
+		$this->assertEquals('Error REQUEST_DENIED (The provided API key is invalid.)', $result);
 	}
 	}
 
 
 	/**
 	/**
@@ -271,16 +350,11 @@ class GeocodeLibTest extends MyCakeTestCase {
 	 */
 	 */
 	public function testGeocodeInvalid() {
 	public function testGeocodeInvalid() {
 		$address = 'Hjfjosdfhosj, 78878 Mdfkufsdfk';
 		$address = 'Hjfjosdfhosj, 78878 Mdfkufsdfk';
-		//echo '<h2>'.$address.'</h2>';
-		$is = $this->Geocode->geocode($address);
-		//debug($is);
-		$this->assertFalse($is);
-
-		//pr($this->Geocode->debug());
+		$result = $this->Geocode->geocode($address);
+		$this->assertFalse($result);
 
 
-		$is = $this->Geocode->error();
-		//debug($is);
-		$this->assertTrue(!empty($is));
+		$result = $this->Geocode->error();
+		$this->assertTrue(!empty($result));
 	}
 	}
 
 
 	/**
 	/**
@@ -289,21 +363,42 @@ class GeocodeLibTest extends MyCakeTestCase {
 	 * @return void
 	 * @return void
 	 */
 	 */
 	public function testGetMaxAddress() {
 	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);
+		$ReflectionClass = new ReflectionClass('GeocodeLib');
+		$Method = $ReflectionClass->getMethod('_getMaxAccuracy');
+		$Method->setAccessible(true);
+
+		$result = $Method->invoke($this->Geocode, array('street_address' => 'abc'));
+		$this->assertSame(GeocodeLib::ACC_STREET, $result);
+
+		$result = $Method->invoke($this->Geocode, array('intersection' => 'abc'));
+		$this->assertSame(GeocodeLib::ACC_INTERSEC, $result);
+
+		$result = $Method->invoke($this->Geocode, array('route' => 'abc'));
+		$this->assertSame(GeocodeLib::ACC_ROUTE, $result);
+
+		$result = $Method->invoke($this->Geocode, array('sublocality' => 'abc'));
+		$this->assertSame(GeocodeLib::ACC_SUBLOC, $result);
+
+		$result = $Method->invoke($this->Geocode, array('locality' => 'abc'));
+		$this->assertSame(GeocodeLib::ACC_LOC, $result);
+
+		$result = $Method->invoke($this->Geocode, array('postal_code' => 'abc'));
+		$this->assertSame(GeocodeLib::ACC_POSTAL, $result);
+
+		$result = $Method->invoke($this->Geocode, array('country' => 'abc'));
+		$this->assertSame(GeocodeLib::ACC_COUNTRY, $result);
+
+		$result = $Method->invoke($this->Geocode, array());
+		$this->assertSame(null, $result);
+
 		// mixed
 		// mixed
-		$this->assertEqual($this->Geocode->_getMaxAccuracy(array(
+		$result = $Method->invoke($this->Geocode, array(
 			'country' => 'aa',
 			'country' => 'aa',
 			'postal_code' => 'abc',
 			'postal_code' => 'abc',
 			'locality' => '',
 			'locality' => '',
 			'street_address' => '',
 			'street_address' => '',
-		)), GeocodeLib::ACC_POSTAL);
+		));
+		$this->assertSame(GeocodeLib::ACC_POSTAL, $result);
 	}
 	}
 
 
 	/**
 	/**
@@ -322,53 +417,6 @@ class GeocodeLibTest extends MyCakeTestCase {
 	}
 	}
 
 
 	/**
 	/**
-	 * GeocodeLibTest::testGeocodeInconclusive()
-	 *
-	 * @return void
-	 */
-	public function testGeocodeInconclusive() {
-		// seems like there is no inconclusive result anymore!!!
-		$address = 'Neustadt';
-
-		// allow_inconclusive = TRUE
-		$this->Geocode->setOptions(array('allow_inconclusive' => true, 'min_accuracy' => GeocodeLib::ACC_POSTAL));
-		$is = $this->Geocode->geocode($address);
-		$this->assertTrue($is);
-		$res = $this->Geocode->getResult();
-		$this->assertTrue(count($res) > 4);
-
-		$is = $this->Geocode->isInconclusive();
-		$this->assertTrue($is);
-
-		$this->Geocode->setOptions(array('allow_inconclusive' => false));
-		$is = $this->Geocode->geocode($address);
-		$this->assertFalse($is);
-	}
-
-	/**
-	 * GeocodeLibTest::testReverseGeocode()
-	 *
-	 * @return void
-	 */
-	public function testReverseGeocode() {
-		$coords = array(
-			array(-34.594445, -58.37446, 'Calle Florida 1134-1200, Buenos Aires'),
-			array(48.8934, 8.70492, 'B294, 75175 Pforzheim, Deutschland')
-		);
-
-		foreach ($coords as $coord) {
-			$is = $this->Geocode->reverseGeocode($coord[0], $coord[1]);
-			$this->assertTrue($is);
-
-			$is = $this->Geocode->getResult();
-			$this->assertTrue(!empty($is));
-			//debug($is);
-			$address = isset($is[0]) ? $is[0]['formatted_address'] : $is['formatted_address'];
-			$this->assertTextContains($coord[2], $address);
-		}
-	}
-
-	/**
 	 * GeocodeLibTest::testTransformData()
 	 * GeocodeLibTest::testTransformData()
 	 *
 	 *
 	 * @return void
 	 * @return void
@@ -379,20 +427,19 @@ class GeocodeLibTest extends MyCakeTestCase {
 		$Method->setAccessible(true);
 		$Method->setAccessible(true);
 
 
 		// non-full records
 		// non-full records
-		$data = array('record' => 'OK');
+		$data = array('types' => array());
 		$this->assertEquals($data, $Method->invoke($this->Geocode, $data));
 		$this->assertEquals($data, $Method->invoke($this->Geocode, $data));
 		$data = array();
 		$data = array();
 		$this->assertEquals($data, $Method->invoke($this->Geocode, $data));
 		$this->assertEquals($data, $Method->invoke($this->Geocode, $data));
-		$data = '';
-		$this->assertEquals($data, $Method->invoke($this->Geocode, $data));
-		$data = 'abc';
-		$this->assertEquals($data, $Method->invoke($this->Geocode, $data));
 
 
 		// Full record
 		// Full record
+		$ReflectionClass = new ReflectionClass('GeocodeLib');
+		$Method = $ReflectionClass->getMethod('_transform');
+		$Method->setAccessible(true);
 		$data = json_decode($this->apiMockupReverseGeocode40206['raw'], true);
 		$data = json_decode($this->apiMockupReverseGeocode40206['raw'], true);
 		$expected = array(
 		$expected = array(
 			'results' => array(
 			'results' => array(
-				0 => array (
+				array(
 					'formatted_address' => 'Louisville, KY 40206, USA',
 					'formatted_address' => 'Louisville, KY 40206, USA',
 					// organized location components
 					// organized location components
 					'country' => 'United States',
 					'country' => 'United States',
@@ -404,47 +451,76 @@ class GeocodeLibTest extends MyCakeTestCase {
 					'sublocality' => '',
 					'sublocality' => '',
 					'route' => '',
 					'route' => '',
 					// vetted "types"
 					// vetted "types"
-					'types' => array (
-						0 => 'postal_code',
+					'types' => array(
+						'postal_code',
 					),
 					),
 					// simple lat/lng
 					// simple lat/lng
 					'lat' => 38.264357800000013,
 					'lat' => 38.264357800000013,
 					'lng' => -85.699978899999991,
 					'lng' => -85.699978899999991,
 					'location_type' => 'APPROXIMATE',
 					'location_type' => 'APPROXIMATE',
-					'viewport' => array (
-						'sw' => array (
+					'viewport' => array(
+						'sw' => array(
 							'lat' => 38.239565800000001,
 							'lat' => 38.239565800000001,
 							'lng' => -85.744800999999995,
 							'lng' => -85.744800999999995,
 						),
 						),
-						'ne' => array (
+						'ne' => array(
 							'lat' => 38.285255800000002,
 							'lat' => 38.285255800000002,
 							'lng' => -85.664309000000003,
 							'lng' => -85.664309000000003,
 						),
 						),
 					),
 					),
-					'bounds' => array (
-						'sw' => array (
+					'bounds' => array(
+						'sw' => array(
 							'lat' => 38.239565800000001,
 							'lat' => 38.239565800000001,
 							'lng' => -85.744800999999995,
 							'lng' => -85.744800999999995,
 						),
 						),
-						'ne' => array (
+						'ne' => array(
 							'lat' => 38.285255800000002,
 							'lat' => 38.285255800000002,
 							'lng' => -85.664309000000003,
 							'lng' => -85.664309000000003,
 						),
 						),
 					),
 					),
-					// injected static maxAccuracy
-					'maxAccuracy' => 5,
+					'address_components' => array(
+							array(
+								'long_name' => '40206',
+								'short_name' => '40206',
+								'types' => array(
+									'postal_code',
+								),
+							),
+							array(
+								'long_name' => 'Louisville',
+								'short_name' => 'Louisville',
+								'types' => array(
+									'locality',
+									'political',
+								),
+							),
+							array(
+								'long_name' => 'Kentucky',
+								'short_name' => 'KY',
+								'types' => array(
+									'administrative_area_level_1',
+									'political',
+								),
+							),
+							array(
+								'long_name' => 'United States',
+								'short_name' => 'US',
+								'types' => array(
+									'country',
+									'political',
+								),
+							),
+						),
+						'valid_type' => true,
+						'accuracy' => 4,
+						'accuracy_name' => 'postal_code',
 				),
 				),
 			),
 			),
 			'status' => 'OK',
 			'status' => 'OK',
 		);
 		);
-		$this->assertEquals($expected, $Method->invoke($this->Geocode, $data));
-
-		// multiple full records
-		// TODO:...
-	}
-
-	public function testGetResult() {
+		$result = $Method->invoke($this->Geocode, $data);
 
 
+		$this->assertEquals($expected, $result);
 	}
 	}
 
 
 }
 }

+ 18 - 53
Test/Case/Model/Behavior/GeocoderBehaviorTest.php

@@ -3,8 +3,9 @@ App::uses('GeocoderBehavior', 'Tools.Model/Behavior');
 App::uses('Set', 'Utility');
 App::uses('Set', 'Utility');
 App::uses('AppModel', 'Model');
 App::uses('AppModel', 'Model');
 App::uses('AppController', 'Controller');
 App::uses('AppController', 'Controller');
+App::uses('MyCakeTestCase', 'Tools.TestSuite');
 
 
-class GeocoderBehaviorTest extends CakeTestCase {
+class GeocoderBehaviorTest extends MyCakeTestCase {
 
 
 	public $fixtures = array(
 	public $fixtures = array(
 		'core.comment', 'plugin.tools.address', 'core.cake_session'
 		'core.comment', 'plugin.tools.address', 'core.cake_session'
@@ -142,23 +143,22 @@ class GeocoderBehaviorTest extends CakeTestCase {
 	 * @return void
 	 * @return void
 	 */
 	 */
 	public function testBasic() {
 	public function testBasic() {
-		//echo '<h3>'.__FUNCTION__.'</h3>';
-
 		$data = array(
 		$data = array(
 			'street' => 'Krebenweg 22',
 			'street' => 'Krebenweg 22',
 			'zip' => '74523',
 			'zip' => '74523',
 			'city' => 'Bibersfeld'
 			'city' => 'Bibersfeld'
 		);
 		);
+		$this->Comment->create();
 		$res = $this->Comment->save($data);
 		$res = $this->Comment->save($data);
-		//debug($res);
+		$this->debug($res);
 		$this->assertTrue(!empty($res['Comment']['lat']) && !empty($res['Comment']['lng']) && round($res['Comment']['lat']) === 49.0 && round($res['Comment']['lng']) === 10.0);
 		$this->assertTrue(!empty($res['Comment']['lat']) && !empty($res['Comment']['lng']) && round($res['Comment']['lat']) === 49.0 && round($res['Comment']['lng']) === 10.0);
-		// accuracy = 4
 
 
 		// inconclusive
 		// inconclusive
 		$data = array(
 		$data = array(
 			//'street' => 'Leopoldstraße',
 			//'street' => 'Leopoldstraße',
 			'city' => 'München'
 			'city' => 'München'
 		);
 		);
+		$this->Comment->create();
 		$res = $this->Comment->save($data);
 		$res = $this->Comment->save($data);
 		$this->assertEquals('', $this->Comment->Behaviors->Geocoder->Geocode->error());
 		$this->assertEquals('', $this->Comment->Behaviors->Geocoder->Geocode->error());
 
 
@@ -169,8 +169,9 @@ class GeocoderBehaviorTest extends CakeTestCase {
 		$data = array(
 		$data = array(
 			'city' => 'Bibersfeld'
 			'city' => 'Bibersfeld'
 		);
 		);
+		$this->Comment->create();
 		$res = $this->Comment->save($data);
 		$res = $this->Comment->save($data);
-		//debug($res);
+		$this->debug($res);
 		$this->assertTrue(!empty($res));
 		$this->assertTrue(!empty($res));
 		$this->assertEquals('', $this->Comment->Behaviors->Geocoder->Geocode->error());
 		$this->assertEquals('', $this->Comment->Behaviors->Geocoder->Geocode->error());
 	}
 	}
@@ -181,20 +182,14 @@ class GeocoderBehaviorTest extends CakeTestCase {
 	 * @return void
 	 * @return void
 	 */
 	 */
 	public function testMinAccLow() {
 	public function testMinAccLow() {
-		//echo '<h3>'.__FUNCTION__.'</h3>';
-
 		$this->Comment->Behaviors->unload('Geocoder');
 		$this->Comment->Behaviors->unload('Geocoder');
-		$this->Comment->Behaviors->load('Tools.Geocoder', array('real' => false, 'min_accuracy' => 0));
-		// accuracy = 1
+		$this->Comment->Behaviors->load('Tools.Geocoder', array('real' => false, 'min_accuracy' => GeocodeLib::ACC_COUNTRY));
 		$data = array(
 		$data = array(
-			//'street' => 'Leopoldstraße',
 			'city' => 'Deutschland'
 			'city' => 'Deutschland'
 		);
 		);
+		$this->Comment->create();
 		$res = $this->Comment->save($data);
 		$res = $this->Comment->save($data);
-		//debug($this->Comment->Behaviors->Geocoder->Geocode->error());
-		//debug($res);
-		//debug($this->Comment->Behaviors->Geocoder->Geocode->debug());
-		$this->assertTrue(!empty($res['Comment']['lat']) && !empty($res['Comment']['lng']));
+		$this->assertTrue((int)$res['Comment']['lat'] && (int)$res['Comment']['lng']);
 	}
 	}
 
 
 	/**
 	/**
@@ -203,19 +198,13 @@ class GeocoderBehaviorTest extends CakeTestCase {
 	 * @return void
 	 * @return void
 	 */
 	 */
 	public function testMinAccHigh() {
 	public function testMinAccHigh() {
-		//echo '<h3>'.__FUNCTION__.'</h3>';
-
 		$this->Comment->Behaviors->unload('Geocoder');
 		$this->Comment->Behaviors->unload('Geocoder');
-		$this->Comment->Behaviors->load('Tools.Geocoder', array('real' => false, 'min_accuracy' => 4));
-		// accuracy = 1
+		$this->Comment->Behaviors->load('Tools.Geocoder', array('real' => false, 'min_accuracy' => GeocodeLib::ACC_POSTAL));
 		$data = array(
 		$data = array(
-			//'street' => 'Leopoldstraße',
 			'city' => 'Deutschland'
 			'city' => 'Deutschland'
 		);
 		);
+		$this->Comment->create();
 		$res = $this->Comment->save($data);
 		$res = $this->Comment->save($data);
-		//debug($this->Comment->Behaviors->Geocoder->Geocode->error());
-		//debug($res);
-		//debug($this->Comment->Behaviors->Geocoder->Geocode->debug());
 		$this->assertTrue(!isset($res['Comment']['lat']) && !isset($res['Comment']['lng']));
 		$this->assertTrue(!isset($res['Comment']['lat']) && !isset($res['Comment']['lng']));
 	}
 	}
 
 
@@ -225,26 +214,18 @@ class GeocoderBehaviorTest extends CakeTestCase {
 	 * @return void
 	 * @return void
 	 */
 	 */
 	public function testMinInc() {
 	public function testMinInc() {
-		//echo '<h3>'.__FUNCTION__.'</h3>';
-
 		$this->Comment->Behaviors->unload('Geocoder');
 		$this->Comment->Behaviors->unload('Geocoder');
 		$this->Comment->Behaviors->load('Tools.Geocoder', array('real' => false, 'min_accuracy' => GeocodeLib::ACC_SUBLOC));
 		$this->Comment->Behaviors->load('Tools.Geocoder', array('real' => false, 'min_accuracy' => GeocodeLib::ACC_SUBLOC));
 
 
 		$this->assertEquals(GeocodeLib::ACC_SUBLOC, $this->Comment->Behaviors->Geocoder->settings['Comment']['min_accuracy']);
 		$this->assertEquals(GeocodeLib::ACC_SUBLOC, $this->Comment->Behaviors->Geocoder->settings['Comment']['min_accuracy']);
 
 
-		// accuracy = 1
 		$data = array(
 		$data = array(
 			//'street' => 'Leopoldstraße',
 			//'street' => 'Leopoldstraße',
 			'city' => 'Neustadt'
 			'city' => 'Neustadt'
 		);
 		);
+		$this->Comment->create();
 		$res = $this->Comment->save($data);
 		$res = $this->Comment->save($data);
-		//debug($this->Comment->Behaviors->Geocoder->Geocode->error()).BR;
-
-		//debug($this->Comment->Behaviors->Geocoder->Geocode->getResult()).BR;
-
-		//debug($res);
 
 
-		//debug($this->Comment->Behaviors->Geocoder->Geocode->debug());
 		$this->assertTrue(!isset($res['Comment']['lat']) && !isset($res['Comment']['lng']));
 		$this->assertTrue(!isset($res['Comment']['lat']) && !isset($res['Comment']['lng']));
 	}
 	}
 
 
@@ -254,25 +235,15 @@ class GeocoderBehaviorTest extends CakeTestCase {
 	 * @return void
 	 * @return void
 	 */
 	 */
 	public function testMinIncAllowed() {
 	public function testMinIncAllowed() {
-		//echo '<h3>'.__FUNCTION__.'</h3>';
-
 		$this->Comment->Behaviors->unload('Geocoder');
 		$this->Comment->Behaviors->unload('Geocoder');
 		$this->Comment->Behaviors->load('Tools.Geocoder', array('real' => false, 'allow_inconclusive' => true));
 		$this->Comment->Behaviors->load('Tools.Geocoder', array('real' => false, 'allow_inconclusive' => true));
-		// accuracy = 1
+
 		$data = array(
 		$data = array(
-			//'street' => 'Leopoldstraße',
 			'city' => 'Neustadt'
 			'city' => 'Neustadt'
 		);
 		);
+		$this->Comment->create();
 		$res = $this->Comment->save($data);
 		$res = $this->Comment->save($data);
-		//debug($this->Comment->Behaviors->Geocoder->Geocode->error()).BR;
 
 
-		//debug($this->Comment->Behaviors->Geocoder->Geocode->url()).BR;
-
-		//debug($this->Comment->Behaviors->Geocoder->Geocode->getResult()).BR;
-
-		//debug($res);
-
-		//debug($this->Comment->Behaviors->Geocoder->Geocode->debug());
 		$this->assertTrue(!empty($res['Comment']['lat']) && !empty($res['Comment']['lng']));
 		$this->assertTrue(!empty($res['Comment']['lat']) && !empty($res['Comment']['lng']));
 	}
 	}
 
 
@@ -284,25 +255,19 @@ class GeocoderBehaviorTest extends CakeTestCase {
 	public function testExpect() {
 	public function testExpect() {
 		$this->Comment->Behaviors->unload('Geocoder');
 		$this->Comment->Behaviors->unload('Geocoder');
 		$this->Comment->Behaviors->load('Tools.Geocoder', array('real' => false, 'expect' => array('postal_code')));
 		$this->Comment->Behaviors->load('Tools.Geocoder', array('real' => false, 'expect' => array('postal_code')));
-		// accuracy = 1
+
 		$data = array(
 		$data = array(
-			//'street' => 'Leopoldstraße',
 			'city' => 'Bibersfeld'
 			'city' => 'Bibersfeld'
 		);
 		);
+		$this->Comment->create();
 		$res = $this->Comment->save($data);
 		$res = $this->Comment->save($data);
-		//debug($this->Comment->Behaviors->Geocoder->Geocode->error());
-		//debug($res);
-		//debug($this->Comment->Behaviors->Geocoder->Geocode->debug());
 		$this->assertTrue(empty($res['Comment']['lat']) && empty($res['Comment']['lng']));
 		$this->assertTrue(empty($res['Comment']['lat']) && empty($res['Comment']['lng']));
 
 
 		$data = array(
 		$data = array(
-			//'street' => 'Leopoldstraße',
 			'city' => '74523'
 			'city' => '74523'
 		);
 		);
+		$this->Comment->create();
 		$res = $this->Comment->save($data);
 		$res = $this->Comment->save($data);
-		//debug($this->Comment->Behaviors->Geocoder->Geocode->error()).BR;
-		//debug($res);
-		//debug($this->Comment->Behaviors->Geocoder->Geocode->debug());
 		$this->assertTrue(!empty($res['Comment']['lat']) && !empty($res['Comment']['lng']));
 		$this->assertTrue(!empty($res['Comment']['lat']) && !empty($res['Comment']['lng']));
 	}
 	}