ソースを参照

Merge pull request #65 from dereuromark/master-geo

Master geo
Mark 11 年 前
コミット
7d8f68dd73

ファイルの差分が大きいため隠しています
+ 350 - 522
Lib/GeocodeLib.php


+ 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);

+ 66 - 71
Model/Behavior/GeocoderBehavior.php

@@ -5,7 +5,12 @@ App::uses('GeocodeLib', 'Tools.Lib');
 /**
  * A geocoding behavior for CakePHP to easily geocode addresses.
  * 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
  * @cakephp 2.x
@@ -14,6 +19,18 @@ App::uses('GeocodeLib', 'Tools.Lib');
  */
 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:
 	 *
@@ -35,24 +52,12 @@ class GeocoderBehavior extends ModelBehavior {
 	 * - before: validate/save (defaults to save)
 	 * 			set to false if you only want to use the validation rules etc
 	 *
-	 * @param object $Model Model using the behaviour
-	 * @param array $settings Settings to override for model.
+	 * @param Model $Model The model using the behaviour
+	 * @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()) {
@@ -99,75 +104,69 @@ class GeocoderBehavior extends ModelBehavior {
 			}
 		}
 
-		$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)))) {
+			($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;
 			}
+		}
+
+		$geocode = $this->_geocode($addressData, $this->settings[$Model->alias]);
 
-			$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'])) {
+			// Prevent 0 inserts of incorrect runs
+			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;
 			}
+			return true;
+		}
 
-			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']]);
-				}
-			}
+		// Valid lat/lng found
+		$Model->data[$Model->alias][$this->settings[$Model->alias]['lat']] = $geocode['lat'];
+		$Model->data[$Model->alias][$this->settings[$Model->alias]['lng']] = $geocode['lng'];
 
-			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($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']]);
 			}
+		}
 
-			$Model->data[$Model->alias]['geocoder_result']['address_data'] = implode(' ', $adressdata);
+		$Model->data[$Model->alias]['geocoder_result'] = $geocode;
+		$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];
 				}
 			}
 		}
@@ -342,14 +341,10 @@ class GeocoderBehavior extends ModelBehavior {
 
 		$settings = array('language' => $options['language']);
 		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();
 	}
 
 	/**

+ 361 - 89
Test/Case/Lib/GeocodeLibTest.php

@@ -15,6 +15,52 @@ Configure::write('Google', array(
 
 class GeocodeLibTest extends MyCakeTestCase {
 
+	public $apiMockupReverseGeocode40206 = array(
+		'reverseGeocode' => array(
+			'lat' => '38.2643',
+			'lng' => '-85.6999',
+			'params' => array(
+				'address' => '40206',
+				'latlng' => '',
+				'region' => '',
+				'language' => 'en',
+				'bounds' => '',
+				'sensor' => 'false',
+				'key' => 'AIzaSyAcQWSeMp_RF9W2_g2vOfLlUNCieHtHfFA',
+				'result_type' => 'sublocality'
+			)
+		),
+		'_fetch' => 'https://maps.googleapis.com/maps/api/geocode/json?address=40206&latlng=38.2643%2C-85.6999&language=en&sensor=false',
+		'raw' => '{
+			"results" : [
+				{
+					"address_components" : [
+						{ "long_name" : "40206", "short_name" : "40206", "types" : [ "postal_code" ] },
+						{ "long_name" : "Louisville", "short_name" : "Louisville", "types" : [ "locality", "political" ] },
+						{ "long_name" : "Kentucky", "short_name" : "KY", "types" : [ "administrative_area_level_1", "political" ] },
+						{ "long_name" : "United States", "short_name" : "US", "types" : [ "country", "political" ] }
+					],
+					"formatted_address" : "Louisville, KY 40206, USA",
+					"geometry" : {
+						"bounds" : {
+							"northeast" : { "lat" : 38.2852558, "lng" : -85.664309 },
+							"southwest" : { "lat" : 38.2395658, "lng" : -85.744801 }
+						},
+						"location" : { "lat" : 38.26435780000001, "lng" : -85.69997889999999 },
+						"location_type" : "APPROXIMATE",
+						"viewport" : {
+							"northeast" : { "lat" : 38.2852558, "lng" : -85.664309 },
+							"southwest" : { "lat" : 38.2395658, "lng" : -85.744801 }
+						}
+					},
+					"types" : [ "postal_code" ]
+				}
+			],
+			"status" : "OK"
+		}',
+	);
+
+
 	public function setUp() {
 		parent::setUp();
 
@@ -31,6 +77,105 @@ class GeocodeLibTest extends MyCakeTestCase {
 		$this->assertInstanceOf('GeocodeLib', $this->Geocode);
 	}
 
+	/**
+	 * 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()
+	 *
+	 * @return void
+	 */
 	public function testDistance() {
 		$coords = array(
 			array('name' => 'MUC/Pforzheim (269km road, 2:33h)', 'x' => array('lat' => 48.1391, 'lng' => 11.5802), 'y' => array('lat' => 48.8934, 'lng' => 8.70492), 'd' => 228),
@@ -46,6 +191,11 @@ class GeocodeLibTest extends MyCakeTestCase {
 		}
 	}
 
+	/**
+	 * GeocodeLibTest::testBlur()
+	 *
+	 * @return void
+	 */
 	public function testBlur() {
 		$coords = array(
 			array(48.1391, 1, 0.002), //'y'=>array('lat'=>48.8934, 'lng'=>8.70492), 'd'=>228),
@@ -59,6 +209,11 @@ class GeocodeLibTest extends MyCakeTestCase {
 		}
 	}
 
+	/**
+	 * GeocodeLibTest::testConvert()
+	 *
+	 * @return void
+	 */
 	public function testConvert() {
 		$values = array(
 			array(3, 'M', 'K', 4.828032),
@@ -73,53 +228,50 @@ class GeocodeLibTest extends MyCakeTestCase {
 		}
 	}
 
+	/**
+	 * GeocodeLibTest::testUrl()
+	 *
+	 * @return void
+	 */
 	public function testUrl() {
-		$is = $this->Geocode->url();
-		//debug($is);
-		$this->assertTrue(!empty($is) && strpos($is, 'http://maps.googleapis.com/maps/api/geocode/xml?') === 0);
-	}
-
-	// not possible with protected method
-
-	public function _testFetch() {
-		$url = 'http://maps.google.com/maps/api/geocode/xml?sensor=false&address=74523';
-		$is = $this->Geocode->_fetch($url);
-		//debug($is);
+		$ReflectionClass = new ReflectionClass('GeocodeLib');
+		$Method = $ReflectionClass->getMethod('_url');
+		$Method->setAccessible(true);
 
-		$this->assertTrue(!empty($is) && substr($is, 0, 38) === '<?xml version="1.0" encoding="UTF-8"?>');
-
-		$url = 'http://maps.google.com/maps/api/geocode/json?sensor=false&address=74523';
-		$is = $this->Geocode->_fetch($url);
-		//debug($is);
-		$this->assertTrue(!empty($is) && substr($is, 0, 1) === '{');
+		$is = $Method->invoke($this->Geocode);
+		$this->assertPattern('#https://maps.googleapis.com/maps/api/geocode/json#', $is);
 	}
 
+	/**
+	 * GeocodeLibTest::testSetParams()
+	 *
+	 * @return void
+	 */
 	public function testSetParams() {
 	}
 
-	public function testWithJson() {
-		$this->Geocode->setOptions(array('output' => 'json'));
-		$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()
+	 *
+	 * @return void
+	 */
 	public function testSetOptions() {
-		// should be the default
-		$res = $this->Geocode->url();
-		$this->assertTextContains('maps.googleapis.com', $res);
-
 		$this->Geocode->setOptions(array('host' => 'maps.google.it'));
+
 		// should now be ".it"
-		$res = $this->Geocode->url();
-		$this->assertTextContains('maps.google.it', $res);
+		$ReflectionClass = new ReflectionClass('GeocodeLib');
+		$Method = $ReflectionClass->getMethod('_url');
+		$Method->setAccessible(true);
+
+		$result = $Method->invoke($this->Geocode);
+		$this->assertTextContains('maps.google.it', $result);
 	}
 
+	/**
+	 * GeocodeLibTest::testGeocode()
+	 *
+	 * @return void
+	 */
 	public function testGeocode() {
 		$address = '74523 Deutschland';
 		//echo '<h2>'.$address.'</h2>';
@@ -168,78 +320,198 @@ class GeocodeLibTest extends MyCakeTestCase {
 		$this->assertTrue(empty($is));
 	}
 
+	/**
+	 * GeocodeLibTest::testGeocodeBadApiKey()
+	 *
+	 * @return void
+	 */
+	public function testGeocodeBadApiKey() {
+		$address = 'Oranienburger Straße 87, 10178 Berlin, Deutschland';
+		$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);
+	}
+
+	/**
+	 * GeocodeLibTest::testGeocodeInvalid()
+	 *
+	 * @return void
+	 */
 	public function testGeocodeInvalid() {
 		$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));
 	}
 
-	public function testGeocodeMinAcc() {
-		$address = 'Deutschland';
-		//echo '<h2>'.$address.'</h2>';
-		$this->Geocode->setOptions(array('min_accuracy' => 3));
-		$is = $this->Geocode->geocode($address);
-		//debug($is);
-		$this->assertFalse($is);
+	/**
+	 * GeocodeLibTest::testGetMaxAddress()
+	 *
+	 * @return void
+	 */
+	public function testGetMaxAddress() {
+		$ReflectionClass = new ReflectionClass('GeocodeLib');
+		$Method = $ReflectionClass->getMethod('_getMaxAccuracy');
+		$Method->setAccessible(true);
 
-		$is = $this->Geocode->error();
-		//debug($is);
-		$this->assertTrue(!empty($is));
-	}
+		$result = $Method->invoke($this->Geocode, array('street_address' => 'abc'));
+		$this->assertSame(GeocodeLib::ACC_STREET, $result);
 
-	public function testGeocodeInconclusive() {
-		// seems like there is no inconclusive result anymore!!!
+		$result = $Method->invoke($this->Geocode, array('intersection' => 'abc'));
+		$this->assertSame(GeocodeLib::ACC_INTERSEC, $result);
 
-		$address = 'Neustadt';
-		//echo '<h2>'.$address.'</h2>';
+		$result = $Method->invoke($this->Geocode, array('route' => 'abc'));
+		$this->assertSame(GeocodeLib::ACC_ROUTE, $result);
 
-		// allow_inconclusive = TRUE
-		$this->Geocode->setOptions(array('allow_inconclusive' => true, 'min_accuracy' => GeocodeLib::ACC_LOC));
-		$is = $this->Geocode->geocode($address);
-		//echo 'debug:';
-		//pr($this->Geocode->debug());
-		//echo 'debug end';
-		$this->assertTrue($is);
+		$result = $Method->invoke($this->Geocode, array('sublocality' => 'abc'));
+		$this->assertSame(GeocodeLib::ACC_SUBLOC, $result);
 
-		$res = $this->Geocode->getResult();
-		//pr($res);
-		$this->assertTrue(count($res) > 4);
+		$result = $Method->invoke($this->Geocode, array('locality' => 'abc'));
+		$this->assertSame(GeocodeLib::ACC_LOC, $result);
 
-		$is = $this->Geocode->isInconclusive();
-		$this->assertTrue($is);
+		$result = $Method->invoke($this->Geocode, array('postal_code' => 'abc'));
+		$this->assertSame(GeocodeLib::ACC_POSTAL, $result);
 
-		$this->Geocode->setOptions(array('allow_inconclusive' => false));
+		$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
+		$result = $Method->invoke($this->Geocode, array(
+			'country' => 'aa',
+			'postal_code' => 'abc',
+			'locality' => '',
+			'street_address' => '',
+		));
+		$this->assertSame(GeocodeLib::ACC_POSTAL, $result);
+	}
+
+	/**
+	 * GeocodeLibTest::testGeocodeMinAcc()
+	 *
+	 * @return void
+	 */
+	public function testGeocodeMinAcc() {
+		// address = postal_code, minimum = street level
+		$address = 'Deutschland';
+		$this->Geocode->setOptions(array('min_accuracy' => GeocodeLib::ACC_STREET));
 		$is = $this->Geocode->geocode($address);
 		$this->assertFalse($is);
+		$is = $this->Geocode->error();
+		$this->assertTrue(!empty($is));
 	}
 
-	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')
+	/**
+	 * GeocodeLibTest::testTransformData()
+	 *
+	 * @return void
+	 */
+	public function testTransformData() {
+		$ReflectionClass = new ReflectionClass('GeocodeLib');
+		$Method = $ReflectionClass->getMethod('_transformData');
+		$Method->setAccessible(true);
+
+		// non-full records
+		$data = array('types' => array());
+		$this->assertEquals($data, $Method->invoke($this->Geocode, $data));
+		$data = array();
+		$this->assertEquals($data, $Method->invoke($this->Geocode, $data));
+
+		// Full record
+		$ReflectionClass = new ReflectionClass('GeocodeLib');
+		$Method = $ReflectionClass->getMethod('_transform');
+		$Method->setAccessible(true);
+		$data = json_decode($this->apiMockupReverseGeocode40206['raw'], true);
+		$expected = array(
+			'results' => array(
+				array(
+					'formatted_address' => 'Louisville, KY 40206, USA',
+					// organized location components
+					'country' => 'United States',
+					'country_code' => 'US',
+					'country_province' => 'Kentucky',
+					'country_province_code' => 'KY',
+					'postal_code' => '40206',
+					'locality' => 'Louisville',
+					'sublocality' => '',
+					'route' => '',
+					// vetted "types"
+					'types' => array(
+						'postal_code',
+					),
+					// simple lat/lng
+					'lat' => 38.264357800000013,
+					'lng' => -85.699978899999991,
+					'location_type' => 'APPROXIMATE',
+					'viewport' => array(
+						'sw' => array(
+							'lat' => 38.239565800000001,
+							'lng' => -85.744800999999995,
+						),
+						'ne' => array(
+							'lat' => 38.285255800000002,
+							'lng' => -85.664309000000003,
+						),
+					),
+					'bounds' => array(
+						'sw' => array(
+							'lat' => 38.239565800000001,
+							'lng' => -85.744800999999995,
+						),
+						'ne' => array(
+							'lat' => 38.285255800000002,
+							'lng' => -85.664309000000003,
+						),
+					),
+					'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',
 		);
+		$result = $Method->invoke($this->Geocode, $data);
 
-		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);
-		}
-	}
-
-	public function testGetResult() {
+		$this->assertEquals($expected, $result);
 	}
 
 }

+ 76 - 62
Test/Case/Model/Behavior/GeocoderBehaviorTest.php

@@ -3,11 +3,12 @@ App::uses('GeocoderBehavior', 'Tools.Model/Behavior');
 App::uses('Set', 'Utility');
 App::uses('AppModel', 'Model');
 App::uses('AppController', 'Controller');
+App::uses('MyCakeTestCase', 'Tools.TestSuite');
 
-class GeocoderBehaviorTest extends CakeTestCase {
+class GeocoderBehaviorTest extends MyCakeTestCase {
 
 	public $fixtures = array(
-		'core.comment', 'plugin.tools.address'
+		'core.comment', 'plugin.tools.address', 'core.cake_session'
 	);
 
 	public function setUp() {
@@ -18,6 +19,11 @@ class GeocoderBehaviorTest extends CakeTestCase {
 		$this->Comment->Behaviors->load('Tools.Geocoder', array('real' => false));
 	}
 
+	/**
+	 * GeocoderBehaviorTest::testDistance()
+	 *
+	 * @return void
+	 */
 	public function testDistance() {
 		$res = $this->Comment->distance(12, 14);
 		$expected = '6371.04 * ACOS(COS(PI()/2 - RADIANS(90 - Comment.lat)) * COS(PI()/2 - RADIANS(90 - 12)) * COS(RADIANS(Comment.lng) - RADIANS(14)) + SIN(PI()/2 - RADIANS(90 - Comment.lat)) * SIN(PI()/2 - RADIANS(90 - 12)))';
@@ -36,12 +42,22 @@ class GeocoderBehaviorTest extends CakeTestCase {
 		$this->assertEquals($expected, $res);
 	}
 
+	/**
+	 * GeocoderBehaviorTest::testDistanceField()
+	 *
+	 * @return void
+	 */
 	public function testDistanceField() {
 		$res = $this->Comment->distanceField(12, 14);
 		$expected = '6371.04 * ACOS(COS(PI()/2 - RADIANS(90 - Comment.lat)) * COS(PI()/2 - RADIANS(90 - 12)) * COS(RADIANS(Comment.lng) - RADIANS(14)) + SIN(PI()/2 - RADIANS(90 - Comment.lat)) * SIN(PI()/2 - RADIANS(90 - 12))) AS Comment.distance';
 		$this->assertEquals($expected, $res);
 	}
 
+	/**
+	 * GeocoderBehaviorTest::testSetDistanceAsVirtualField()
+	 *
+	 * @return void
+	 */
 	public function testSetDistanceAsVirtualField() {
 		$this->Address = ClassRegistry::init('Address');
 		$this->Address->Behaviors->load('Tools.Geocoder');
@@ -53,6 +69,11 @@ class GeocoderBehaviorTest extends CakeTestCase {
 		$this->assertTrue($res[0]['Address']['distance'] > 640 && $res[0]['Address']['distance'] < 650);
 	}
 
+	/**
+	 * GeocoderBehaviorTest::testSetDistanceAsVirtualFieldInMiles()
+	 *
+	 * @return void
+	 */
 	public function testSetDistanceAsVirtualFieldInMiles() {
 		$this->Address = ClassRegistry::init('Address');
 		$this->Address->Behaviors->load('Tools.Geocoder', array('unit' => GeocodeLib::UNIT_MILES));
@@ -64,6 +85,11 @@ class GeocoderBehaviorTest extends CakeTestCase {
 		$this->assertTrue($res[0]['Address']['distance'] > 390 && $res[0]['Address']['distance'] < 410);
 	}
 
+	/**
+	 * GeocoderBehaviorTest::testPagination()
+	 *
+	 * @return void
+	 */
 	public function testPagination() {
 		$this->Controller = new TestController(new CakeRequest(null, false), null);
 		$this->Controller->constructClasses();
@@ -78,6 +104,11 @@ class GeocoderBehaviorTest extends CakeTestCase {
 		$this->assertTrue($res[0]['Address']['distance'] < $res[1]['Address']['distance']);
 	}
 
+	/**
+	 * GeocoderBehaviorTest::testValidate()
+	 *
+	 * @return void
+	 */
 	public function testValidate() {
 		$is = $this->Comment->validateLatitude(44);
 		$this->assertTrue($is);
@@ -108,25 +139,26 @@ class GeocoderBehaviorTest extends CakeTestCase {
 
 	/**
 	 * Geocoding tests using the google webservice
+	 *
+	 * @return void
 	 */
 	public function testBasic() {
-		//echo '<h3>'.__FUNCTION__.'</h3>';
-
 		$data = array(
 			'street' => 'Krebenweg 22',
 			'zip' => '74523',
 			'city' => 'Bibersfeld'
 		);
+		$this->Comment->create();
 		$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);
-		// accuracy = 4
 
 		// inconclusive
 		$data = array(
 			//'street' => 'Leopoldstraße',
 			'city' => 'München'
 		);
+		$this->Comment->create();
 		$res = $this->Comment->save($data);
 		$this->assertEquals('', $this->Comment->Behaviors->Geocoder->Geocode->error());
 
@@ -137,123 +169,105 @@ class GeocoderBehaviorTest extends CakeTestCase {
 		$data = array(
 			'city' => 'Bibersfeld'
 		);
+		$this->Comment->create();
 		$res = $this->Comment->save($data);
-		//debug($res);
+		$this->debug($res);
 		$this->assertTrue(!empty($res));
 		$this->assertEquals('', $this->Comment->Behaviors->Geocoder->Geocode->error());
 	}
 
+	/**
+	 * GeocoderBehaviorTest::testMinAccLow()
+	 *
+	 * @return void
+	 */
 	public function testMinAccLow() {
-		//echo '<h3>'.__FUNCTION__.'</h3>';
-
 		$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(
-			//'street' => 'Leopoldstraße',
 			'city' => 'Deutschland'
 		);
+		$this->Comment->create();
 		$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((int)$res['Comment']['lat'] && (int)$res['Comment']['lng']);
 	}
 
+	/**
+	 * GeocoderBehaviorTest::testMinAccHigh()
+	 *
+	 * @return void
+	 */
 	public function testMinAccHigh() {
-		//echo '<h3>'.__FUNCTION__.'</h3>';
-
 		$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(
-			//'street' => 'Leopoldstraße',
 			'city' => 'Deutschland'
 		);
+		$this->Comment->create();
 		$res = $this->Comment->save($data);
-		//debug($this->Comment->Behaviors->Geocoder->Geocode->error()).BR;
-
-		//debug($res);
-
-		//debug($this->Comment->Behaviors->Geocoder->Geocode->debug());
 		$this->assertTrue(!isset($res['Comment']['lat']) && !isset($res['Comment']['lng']));
 	}
 
+	/**
+	 * GeocoderBehaviorTest::testMinInc()
+	 *
+	 * @return void
+	 */
 	public function testMinInc() {
-		//echo '<h3>'.__FUNCTION__.'</h3>';
-
 		$this->Comment->Behaviors->unload('Geocoder');
 		$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']);
 
-		// accuracy = 1
 		$data = array(
 			//'street' => 'Leopoldstraße',
 			'city' => 'Neustadt'
 		);
+		$this->Comment->create();
 		$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']));
 	}
 
+	/**
+	 * GeocoderBehaviorTest::testMinIncAllowed()
+	 *
+	 * @return void
+	 */
 	public function testMinIncAllowed() {
-		//echo '<h3>'.__FUNCTION__.'</h3>';
-
 		$this->Comment->Behaviors->unload('Geocoder');
 		$this->Comment->Behaviors->load('Tools.Geocoder', array('real' => false, 'allow_inconclusive' => true));
-		// accuracy = 1
+
 		$data = array(
-			//'street' => 'Leopoldstraße',
 			'city' => 'Neustadt'
 		);
+		$this->Comment->create();
 		$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']));
 	}
 
+	/**
+	 * GeocoderBehaviorTest::testExpect()
+	 *
+	 * @return void
+	 */
 	public function testExpect() {
 		$this->Comment->Behaviors->unload('Geocoder');
 		$this->Comment->Behaviors->load('Tools.Geocoder', array('real' => false, 'expect' => array('postal_code')));
-		// accuracy = 1
+
 		$data = array(
-			//'street' => 'Leopoldstraße',
 			'city' => 'Bibersfeld'
 		);
+		$this->Comment->create();
 		$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']));
 
 		$data = array(
-			//'street' => 'Leopoldstraße',
 			'city' => '74523'
 		);
+		$this->Comment->create();
 		$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']));
 	}