geocode_lib.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912
  1. <?php
  2. /**
  3. * geocode via google (UPDATE: api3)
  4. * @see DEPRECATED api2: http://code.google.com/intl/de-DE/apis/maps/articles/phpsqlgeocode.html
  5. * @sse http://code.google.com/intl/de/apis/maps/documentation/geocoding/#Types
  6. * 2010-06-25 ms
  7. */
  8. class GeocodeLib {
  9. //const BASE_URL = 'http://{host}/maps/geo?output={output}&oe=utf8&key={key}&q='; // deprecated
  10. const BASE_URL = 'http://{host}/maps/api/geocode/{output}?';
  11. const DEFAULT_HOST = 'us';
  12. # First tries with curl, then cake, then php
  13. var $use = array('curl' => true, 'cake'=> true, 'php' => true);
  14. var $log = false; # false logs only real errors, true all activities
  15. var $units = array('K' => 1.609344, 'N' => 0.868976242, 'F' => 5280, 'I' => 63360, 'M' => 1);
  16. /**
  17. * validation and retrieval
  18. * 2010-06-25 ms
  19. */
  20. private $options = array(
  21. 'pause' => 10000,
  22. 'min_accuracy' => 1,
  23. 'allow_inconclusive'=> true,
  24. # static url params
  25. 'output' => 'xml',
  26. 'host' => self::DEFAULT_HOST, # results in maps.google.com - use if you wish to obtain the closest address
  27. );
  28. /**
  29. * url params
  30. * 2010-06-25 ms
  31. */
  32. private $params = array(
  33. 'address' => '', # either address or latlng required!
  34. 'latlng' => '',
  35. 'region' => '', # country tlds
  36. 'language' => 'de',
  37. 'bounds' => '',
  38. 'sensor' => 'false', # device with gps module sensor
  39. //'key' => '' # not neccessary anymore
  40. );
  41. private $error = array();
  42. private $result = null;
  43. /**
  44. * The Maps geocoder is programmed to bias its results depending on from which domain it receives requests. For example, entering "syracuse" in the search box on maps.google.com will geocode the city of "Syracuse, NY", while entering the same query on maps.google.it (Italy's domain) will find the city of "Siracusa" in Sicily. You would get the same results by sending that query through HTTP geocoding to maps.google.it instead of maps.google.com, which you can do by modifying the MAPS_HOST constant in the sample code below. Note: You cannot send a request to a non-existent maps.google.* server, so ensure that a country domain exists before redirecting your geocoding queries to it.
  45. */
  46. private $hosts = array(
  47. 'us' => 'maps.google.com', # only one for "allow_inconclusive" = true
  48. 'gb' => 'maps.google.co.uk',
  49. 'de' => 'maps.google.de',
  50. 'ch' => 'maps.google.ch',
  51. 'at' => 'maps.google.at',
  52. 'it' => 'maps.google.it',
  53. //ADD MORE - The two-letter codes are iso2 country codes and are mapped to top level domains (ccTLDs)
  54. );
  55. private $statusCodes = array(
  56. self::CODE_SUCCESS => 'Success',
  57. self::CODE_BAD_REQUEST => 'Sensor param missing',
  58. self::CODE_MISSING_QUERY => 'Adress/LatLng missing',
  59. self::CODE_UNKNOWN_ADDRESS => 'Success, but to address found',
  60. self::CODE_TOO_MANY_QUERIES => 'Limit exceeded',
  61. );
  62. private $accuracyTypes = array(
  63. 0 => 'country',
  64. 1 => 'administrative_area_level_1', # provinces/states
  65. 2 => 'administrative_area_level_2 ',
  66. 3 => 'administrative_area_level_3',
  67. 4 => 'postal_code',
  68. 5 => 'locality',
  69. 5 => 'sublocality',
  70. 6 => 'route',
  71. 7 => 'intersection',
  72. 8 => 'street_address'
  73. //neighborhood premise subpremise natural_feature airport park point_of_interest colloquial_area political ?
  74. );
  75. function __construct($options = array()) {
  76. /*
  77. if ($googleKey = Configure::read('Google.key')) {
  78. $this->params['key'] = $googleKey;
  79. }
  80. */
  81. $this->defaultParams = $this->params;
  82. $this->defaultOptions = $this->options;
  83. $this->setOptions($options);
  84. }
  85. function setParams($params) {
  86. foreach ($params as $key => $value) {
  87. if ($key == 'sensor' && $value != 'false' && $value != 'true') {
  88. $value = !empty($value) ? 'true' : 'false';
  89. }
  90. $this->params[$key] = urlencode((string)$value);
  91. }
  92. }
  93. function setOptions($options) {
  94. foreach ($options as $key => $value) {
  95. if ($key == 'output' && $value != 'xml' && $value != 'json') {
  96. continue;
  97. }
  98. if ($key == 'host' && !array_key_exists($value, $this->hosts)) {
  99. continue;
  100. }
  101. $this->options[$key] = $value;
  102. }
  103. }
  104. function setError($error) {
  105. if (empty($error)) {
  106. return;
  107. }
  108. $this->error[] = $error;
  109. }
  110. function error($asString = true, $separator = ', ') {
  111. return implode(', ', $this->error);
  112. }
  113. function reset($full = true) {
  114. $this->error = array();
  115. $this->result = null;
  116. if ($full) {
  117. $this->params = $this->defaultParams;
  118. $this->options = $this->defaultOptions;
  119. }
  120. }
  121. /**
  122. * build and return url
  123. * 2010-06-29 ms
  124. */
  125. function url() {
  126. $params = array(
  127. 'host' => $this->hosts[$this->options['host']],
  128. 'output' => $this->options['output']
  129. );
  130. $url = String::insert(self::BASE_URL, $params, array('before'=>'{', 'after'=>'}', 'clean'=>true));
  131. $params = array();
  132. foreach ($this->params as $key => $value) {
  133. if (!empty($value)) {
  134. $params[] = $key.'='.$value;
  135. }
  136. }
  137. return $url.implode('&', $params);
  138. }
  139. function isInconclusive() {
  140. if ($this->result === null) {
  141. return null;
  142. }
  143. if (!isset($this->result[0])) {
  144. return false;
  145. }
  146. return count($this->result) > 0;
  147. }
  148. /**
  149. * @return array $result
  150. * 2010-06-25 ms
  151. */
  152. function getResult() {
  153. if ($this->result !== null) {
  154. if (isset($this->result[0])) {
  155. $res = array();
  156. foreach ($this->result as $tmp) {
  157. $res[] = $this->options['output'] == 'json' ? $this->_transformJson($tmp) : $this->_transformXml($tmp);
  158. }
  159. return $res;
  160. }
  161. if ($this->options['output'] == 'json') {
  162. return $this->_transformJson($this->result);
  163. } else {
  164. return $this->_transformXml($this->result);
  165. }
  166. }
  167. return false;
  168. }
  169. /**
  170. * results usually from most accurate to least accurate result (street_address
  171. , ..., country)
  172. * @param float $lat
  173. * @param float $lng
  174. * @param array $options
  175. * - allow_inconclusive
  176. * - min_accuracy
  177. * @return boolean $success
  178. * 2010-06-29 ms
  179. */
  180. function reverseGeocode($lat, $lng, $settings = array()) {
  181. $this->reset(false);
  182. $latlng = $lat.','.$lng;
  183. $this->setParams(array_merge($settings, array('latlng'=>$latlng)));
  184. $count = 0;
  185. $request_url = $this->url();
  186. while (true) {
  187. $result = $this->_fetch($request_url);
  188. if ($result === false || $result === null) {
  189. $this->setError('Could not retrieve url');
  190. CakeLog::write('geocode', 'Geocoder could not retrieve url with \''.$address.'\'');
  191. return false;
  192. }
  193. if ($this->options['output'] == 'json') {
  194. //$res = json_decode($result);
  195. } else {
  196. App::import('Core', array('Xml'));
  197. $res = new Xml($result);
  198. }
  199. if (!is_object($res)) {
  200. $this->setError('XML parsing failed');
  201. CakeLog::write('geocode', 'Geocoder failed with XML parsing of \''.$address.'\'');
  202. return false;
  203. }
  204. $xmlArray = $res->children[0]->toArray();
  205. $status = $xmlArray['status'];
  206. if ($status == self::CODE_SUCCESS) {
  207. # validate
  208. if (isset($xmlArray['Result'][0]) && !$this->options['allow_inconclusive']) {
  209. $this->setError('Inconclusive result (total of '.count($xmlArray['Result']).')');
  210. $this->result = $xmlArray['Result'];
  211. return false;
  212. }
  213. if (isset($xmlArray['Result'][0])) {
  214. $accuracy = $this->_parse('type', $xmlArray['Result'][0]);
  215. } else {
  216. $accuracy = $this->_parse('type', $xmlArray['Result']);
  217. }
  218. if ($this->_isNotAccurateEnough($accuracy)) {
  219. $this->setError('Accuracy not good enough ('.$accuracy.' instead of at least '.$this->accuracyTypes[$this->options['min_accuracy']].')');
  220. $this->result = $xmlArray['Result'];
  221. return false;
  222. }
  223. # save Result
  224. if ($this->log) {
  225. CakeLog::write('geocode', 'Address \''.$address.'\' has been geocoded');
  226. }
  227. break;
  228. } elseif ($status == self::CODE_TOO_MANY_QUERIES) {
  229. // sent geocodes too fast, delay +0.1 seconds
  230. if ($this->log) {
  231. CakeLog::write('geocode', 'Delay neccessary for \''.$address.'\'');
  232. }
  233. $count++;
  234. } else {
  235. # something went wrong
  236. $this->setError('Error '.$status.(isset($this->statusCodes[$status]) ? ' ('.$this->statusCodes[$status].')' : ''));
  237. if ($this->log) {
  238. CakeLog::write('geocode', 'Geocoder could not geocode \''.$address.'\'');
  239. }
  240. return false; # for now...
  241. }
  242. if ($count > 5) {
  243. if ($this->log) {
  244. CakeLog::write('geocode', 'Geocoder aborted after too many trials with \''.$address.'\'');
  245. }
  246. $this->setError('Too many trials - abort');
  247. return false;
  248. }
  249. $this->pause(true);
  250. }
  251. $this->result = $xmlArray['Result'];
  252. return true;
  253. }
  254. /**
  255. * trying to avoid "TOO_MANY_QUERIES" error
  256. * 2010-06-29 ms
  257. */
  258. function pause($raise = false) {
  259. usleep($this->settings['pause']);
  260. if ($raise) {
  261. $this->settings['pause'] += 10000;
  262. }
  263. }
  264. /**
  265. * @param string $address
  266. * @param array $settings
  267. * - allow_inconclusive
  268. * - min_accuracy
  269. * @return boolean $success
  270. * 2010-06-25 ms
  271. */
  272. function geocode($address, $settings = array()) {
  273. $this->reset(false);
  274. $this->setParams(array_merge($settings, array('address'=>$address)));
  275. if ($this->options['allow_inconclusive']) {
  276. # only host working with this setting
  277. //$this->options['host'] = self::DEFAULT_HOST;
  278. }
  279. $count = 0;
  280. $request_url = $this->url();
  281. while (true) {
  282. $result = $this->_fetch($request_url);
  283. if ($result === false || $result === null) {
  284. $this->setError('Could not retrieve url');
  285. CakeLog::write('geocode', 'Geocoder could not retrieve url with \''.$address.'\'');
  286. return false;
  287. }
  288. if ($this->options['output'] == 'json') {
  289. //$res = json_decode($result);
  290. } else {
  291. App::import('Core', array('Xml'));
  292. $res = new Xml($result);
  293. }
  294. if (!is_object($res)) {
  295. $this->setError('XML parsing failed');
  296. CakeLog::write('geocode', 'Geocoder failed with XML parsing of \''.$address.'\'');
  297. return false;
  298. }
  299. $xmlArray = $res->children[0]->toArray();
  300. $status = $xmlArray['status'];
  301. if ($status == self::CODE_SUCCESS) {
  302. # validate
  303. if (isset($xmlArray['Result'][0]) && !$this->options['allow_inconclusive']) {
  304. $this->setError('Inconclusive result (total of '.count($xmlArray['Result']).')');
  305. $this->result = $xmlArray['Result'];
  306. return false;
  307. }
  308. if (isset($xmlArray['Result'][0])) {
  309. $accuracy = $this->_parse('type', $xmlArray['Result'][0]);
  310. } else {
  311. $accuracy = $this->_parse('type', $xmlArray['Result']);
  312. }
  313. if ($this->_isNotAccurateEnough($accuracy)) {
  314. $this->setError('Accuracy not good enough ('.$accuracy.' instead of at least '.$this->accuracyTypes[$this->options['min_accuracy']].')');
  315. $this->result = $xmlArray['Result'];
  316. return false;
  317. }
  318. # save Result
  319. if ($this->log) {
  320. CakeLog::write('geocode', 'Address \''.$address.'\' has been geocoded');
  321. }
  322. break;
  323. } elseif ($status == self::CODE_TOO_MANY_QUERIES) {
  324. // sent geocodes too fast, delay +0.1 seconds
  325. if ($this->log) {
  326. CakeLog::write('geocode', 'Delay neccessary for \''.$address.'\'');
  327. }
  328. $count++;
  329. } else {
  330. # something went wrong
  331. $this->setError('Error '.$status.(isset($this->statusCodes[$status]) ? ' ('.$this->statusCodes[$status].')' : ''));
  332. if ($this->log) {
  333. CakeLog::write('geocode', 'Geocoder could not geocode \''.$address.'\'');
  334. }
  335. return false; # for now...
  336. }
  337. if ($count > 5) {
  338. if ($this->log) {
  339. CakeLog::write('geocode', 'Geocoder aborted after too many trials with \''.$address.'\'');
  340. }
  341. $this->setError('Too many trials - abort');
  342. return false;
  343. }
  344. $this->pause(true);
  345. }
  346. $this->result = $xmlArray['Result'];
  347. return true;
  348. }
  349. function _isNotAccurateEnough($accuracy = null) {
  350. if ($accuracy === null) {
  351. if (isset($this->result[0])) {
  352. $accuracy = $this->result[0]['type'];
  353. } else {
  354. $accuracy = $this->result['type'];
  355. }
  356. }
  357. if (!in_array($accuracy, $this->accuracyTypes)) {
  358. return null;
  359. }
  360. foreach ($this->accuracyTypes as $key => $type) {
  361. if ($type == $accuracy) {
  362. $accuracy = $key;
  363. break;
  364. }
  365. }
  366. return $accuracy < $this->options['min_accuracy'];
  367. }
  368. function _transformJson($record) {
  369. $res = array();
  370. //TODO
  371. return $res;
  372. }
  373. /**
  374. * try to find the correct path
  375. * - type (string)
  376. * - Type (array[string, ...])
  377. * 2010-06-29 ms
  378. */
  379. function _parse($key, $array) {
  380. if (isset($array[$key])) {
  381. return $array[$key];
  382. }
  383. if (isset($array[($key = ucfirst($key))])) {
  384. return $array[$key][0];
  385. }
  386. return null;
  387. }
  388. /**
  389. * flattens result array and returns clean record
  390. * keys:
  391. * - formatted_address, type, country, country_code, country_province, country_province_code, locality, sublocality, postal_code, route, lat, lng, location_type, viewport, bounds
  392. * 2010-06-25 ms
  393. */
  394. function _transformXml($record) {
  395. $res = array();
  396. $components = array();
  397. if (!isset($record['AddressComponent'][0])) {
  398. $record['AddressComponent'] = array($record['AddressComponent']);
  399. }
  400. foreach ($record['AddressComponent'] as $c) {
  401. $types = array();
  402. if (isset($c['Type'])) { //!is_array($c['Type'])
  403. if (!is_array($c['Type'])) {
  404. echo returns($record);
  405. die();
  406. }
  407. $type = $c['Type'][0];
  408. array_shift($c['Type']);
  409. $types = $c['Type'];
  410. } else {
  411. $type = $c['type'];
  412. }
  413. if (array_key_exists($type, $components)) {
  414. $components[$type]['name'] .= ' '.$c['long_name'];
  415. $components[$type]['abbr'] .= ' '.$c['short_name'];
  416. $components[$type]['types'] += $types;
  417. }
  418. $components[$type] = array('name'=>$c['long_name'], 'abbr'=>$c['short_name'], 'types'=>$types);
  419. }
  420. $res['formatted_address'] = $record['formatted_address'];
  421. $res['type'] = $this->_parse('type', $record);
  422. if (array_key_exists('country', $components)) {
  423. $res['country'] = $components['country']['name'];
  424. $res['country_code'] = $components['country']['abbr'];
  425. } else {
  426. $res['country'] = $res['country_code'] = '';
  427. }
  428. if (array_key_exists('administrative_area_level_1', $components)) {
  429. $res['country_province'] = $components['administrative_area_level_1']['name'];
  430. $res['country_province_code'] = $components['administrative_area_level_1']['abbr'];
  431. } else {
  432. $res['country_province'] = $res['country_province_code'] = '';
  433. }
  434. if (array_key_exists('postal_code', $components)) {
  435. $res['postal_code'] = $components['postal_code']['name'];
  436. } else {
  437. $res['postal_code'] = '';
  438. }
  439. if (array_key_exists('locality', $components)) {
  440. $res['locality'] = $components['locality']['name'];
  441. } else {
  442. $res['locality'] = '';
  443. }
  444. if (array_key_exists('sublocality', $components)) {
  445. $res['sublocality'] = $components['sublocality']['name'];
  446. } else {
  447. $res['sublocality'] = '';
  448. }
  449. if (array_key_exists('route', $components)) {
  450. $res['route'] = $components['route']['name'];
  451. if (array_key_exists('street_number', $components)) {
  452. $res['route'] .= ' '.$components['street_number']['name'];
  453. }
  454. } else {
  455. $res['route'] = '';
  456. }
  457. //TODO: add more
  458. $res['lng'] = $record['Geometry']['Location']['lat'];
  459. $res['lat'] = $record['Geometry']['Location']['lng'];
  460. $res['location_type'] = $record['Geometry']['location_type'];
  461. if (!empty($record['Geometry']['Viewport'])) {
  462. $res['viewport'] = array('sw'=>$record['Geometry']['Viewport']['Southwest'], 'ne'=>$record['Geometry']['Viewport']['Northeast']);
  463. }
  464. if (!empty($record['Geometry']['Bounds'])) {
  465. $res['bounds'] = array('sw'=>$record['Geometry']['Bounds']['Southwest'], 'ne'=>$record['Geometry']['Bounds']['Northeast']);
  466. }
  467. return $res;
  468. }
  469. /**
  470. * fetches url with curl if available
  471. * fallbacks: cake and php
  472. * note: expects url with json encoded content
  473. * @access private
  474. **/
  475. function _fetch($url) {
  476. App::import('Lib', 'Tools.HttpSocketLib');
  477. $this->HttpSocket = new HttpSocketLib($this->use, 'CakePHP Geocode Lib');
  478. if ($res = $this->HttpSocket->fetch($url)) {
  479. return $res;
  480. }
  481. $this->setError($this->HttpSocket->error());
  482. return false;
  483. }
  484. /**
  485. * debugging
  486. * 2009-11-27 ms
  487. */
  488. function debug() {
  489. return $this->result;
  490. }
  491. /**
  492. * Calculates Distance between two points - each: array('lat'=>x,'lng'=>y)
  493. * DB:
  494. '6371.04 * ACOS( COS( PI()/2 - RADIANS(90 - Retailer.lat)) * ' .
  495. 'COS( PI()/2 - RADIANS(90 - '. $data['Location']['lat'] .')) * ' .
  496. 'COS( RADIANS(Retailer.lng) - RADIANS('. $data['Location']['lng'] .')) + ' .
  497. 'SIN( PI()/2 - RADIANS(90 - Retailer.lat)) * ' .
  498. 'SIN( PI()/2 - RADIANS(90 - '. $data['Location']['lat'] . '))) ' .
  499. 'AS distance'
  500. * @param array pointX
  501. * @param array pointY
  502. * @param float $unit (M=miles, K=kilometers, N=nautical miles, I=inches, F=feet)
  503. * @return int distance: in km
  504. * 2009-03-06 ms
  505. */
  506. function distance($pointX, $pointY, $unit = null) {
  507. if (empty($unit) || !array_key_exists(($unit = strtoupper($unit)), $this->units)) {
  508. $unit = array_keys($this->units);
  509. $unit = $unit[0];
  510. }
  511. /*
  512. $res = 6371.04 * ACOS( COS( PI()/2 - rad2deg(90 - $pointX['lat'])) *
  513. COS( PI()/2 - rad2deg(90 - $pointY['lat'])) *
  514. COS( rad2deg($pointX['lng']) - rad2deg($pointY['lng'])) +
  515. SIN( PI()/2 - rad2deg(90 - $pointX['lat'])) *
  516. SIN( PI()/2 - rad2deg(90 - $pointY['lat'])));
  517. $res = 6371.04 * acos(sin($pointY['lat'])*sin($pointX['lat'])+cos($pointY['lat'])*cos($pointX['lat'])*cos($pointY['lng'] - $pointX['lng']));
  518. */
  519. # seems to be the only working one (although slightly incorrect...)
  520. $res = 69.09 * rad2deg(acos(sin(deg2rad($pointX['lat'])) * sin(deg2rad($pointY['lat'])) + cos(deg2rad($pointX['lat'])) * cos(deg2rad($pointY['lat'])) * cos(deg2rad($pointX['lng'] - $pointY['lng']))));
  521. if (isset($this->units[$unit])) {
  522. $res *= $this->units[$unit];
  523. }
  524. return ceil($res);
  525. }
  526. function convert($value, $fromUnit, $toUnit) {
  527. if (!isset($this->units[($fromUnit = strtoupper($fromUnit))]) || !isset($this->units[($toUnit = strtoupper($toUnit))])) {
  528. return false;
  529. }
  530. if ($fromUnit == 'M') {
  531. $value *= $this->units[$toUnit];
  532. } elseif ($toUnit == 'M') {
  533. $value /= $this->units[$fromUnit];
  534. } else {
  535. $value /= $this->units[$fromUnit];
  536. $value *= $this->units[$toUnit];
  537. }
  538. return $value;
  539. }
  540. const ACC_ROOFTOP = 'ROOFTOP';
  541. const ACC_RANGE_INTERPOLATED = 'RANGE_INTERPOLATED';
  542. const ACC_GEOMETRIC_CENTER = 'GEOMETRIC_CENTER';
  543. const ACC_APPROXIMATE = 'APPROXIMATE';
  544. const CODE_SUCCESS = 'OK'; //200;
  545. const CODE_TOO_MANY_QUERIES = 'OVER_QUERY_LIMIT'; //620;
  546. const CODE_BAD_REQUEST = 'REQUEST_DENIED'; //400;
  547. const CODE_MISSING_QUERY = 'INVALID_REQUEST';//601;
  548. const CODE_UNKNOWN_ADDRESS = 'ZERO_RESULTS'; //602;
  549. /*
  550. const CODE_SERVER_ERROR = 500;
  551. const CODE_UNAVAILABLE_ADDRESS = 603;
  552. const CODE_UNKNOWN_DIRECTIONS = 604;
  553. const CODE_BAD_KEY = 610;
  554. */
  555. }
  556. /*
  557. TODO:
  558. http://code.google.com/intl/de-DE/apis/maps/documentation/geocoding/
  559. - whats the difference to "http://maps.google.com/maps/api/geocode/output?parameters"
  560. */
  561. /*
  562. accuracy in v2 (deprecated in v3):
  563. 0 Unbekannter Ort.
  564. 1 Land.
  565. 2 Bundesland/Bundesstaat, Provinz, Präfektur usw.
  566. 3 Bezirk, Gemeinde usw.
  567. 4 Ortschaft (Stadt, Dorf).
  568. 5 Postleitzahl (PLZ).
  569. 6 Straße.
  570. 7 Kreuzung.
  571. 8 Adresse.
  572. */
  573. /*
  574. Example: NEW:
  575. Array
  576. (
  577. [status] => OK
  578. [Result] => Array
  579. (
  580. [type] => postal_code
  581. [formatted_address] => 74523, Deutschland
  582. [AddressComponent] => Array
  583. (
  584. [0] => Array
  585. (
  586. [long_name] => 74523
  587. [short_name] => 74523
  588. [type] => postal_code
  589. )
  590. [1] => Array
  591. (
  592. [long_name] => Schwäbisch Hall
  593. [short_name] => SHA
  594. [Type] => Array
  595. (
  596. [0] => administrative_area_level_2
  597. [1] => political
  598. )
  599. )
  600. [2] => Array
  601. (
  602. [long_name] => Baden-Württemberg
  603. [short_name] => BW
  604. [Type] => Array
  605. (
  606. [0] => administrative_area_level_1
  607. [1] => political
  608. )
  609. )
  610. [3] => Array
  611. (
  612. [long_name] => Deutschland
  613. [short_name] => DE
  614. [Type] => Array
  615. (
  616. [0] => country
  617. [1] => political
  618. )
  619. )
  620. )
  621. [Geometry] => Array
  622. (
  623. [Location] => Array
  624. (
  625. [lat] => 49.1257616
  626. [lng] => 9.7544127
  627. )
  628. [location_type] => APPROXIMATE
  629. [Viewport] => Array
  630. (
  631. [Southwest] => Array
  632. (
  633. [lat] => 49.0451477
  634. [lng] => 9.6132550
  635. )
  636. [Northeast] => Array
  637. (
  638. [lat] => 49.1670260
  639. [lng] => 9.8756350
  640. )
  641. )
  642. [Bounds] => Array
  643. (
  644. [Southwest] => Array
  645. (
  646. [lat] => 49.0451477
  647. [lng] => 9.6132550
  648. )
  649. [Northeast] => Array
  650. (
  651. [lat] => 49.1670260
  652. [lng] => 9.8756350
  653. )
  654. )
  655. )
  656. )
  657. )
  658. Example OLD:
  659. Array
  660. (
  661. [name] => 74523 Deutschland
  662. [Status] => Array
  663. (
  664. [code] => 200
  665. [request] => geocode
  666. )
  667. [Result] => Array
  668. (
  669. [id] => p1
  670. [address] => 74523, Deutschland
  671. [AddressDetails] => Array
  672. (
  673. [Accuracy] => 5
  674. [xmlns] => urn:oasis:names:tc:ciq:xsdschema:xAL:2.0
  675. [Country] => Array
  676. (
  677. [CountryNameCode] => DE
  678. [CountryName] => Deutschland
  679. [AdministrativeArea] => Array
  680. (
  681. [AdministrativeAreaName] => Baden-Württemberg
  682. [SubAdministrativeArea] => Array
  683. (
  684. [SubAdministrativeAreaName] => Schwäbisch Hall
  685. [PostalCode] => Array
  686. (
  687. [PostalCodeNumber] => 74523
  688. )
  689. )
  690. )
  691. )
  692. )
  693. [ExtendedData] => Array
  694. (
  695. [LatLonBox] => Array
  696. (
  697. [north] => 49.1670260
  698. [south] => 49.0451477
  699. [east] => 9.8756350
  700. [west] => 9.6132550
  701. )
  702. )
  703. [Point] => Array
  704. (
  705. [coordinates] => 9.7544127,49.1257616,0
  706. )
  707. )
  708. )
  709. {
  710. "status": "OK",
  711. "results": [ {
  712. "types": [ "street_address" ],
  713. "formatted_address": "Krebenweg 20, 74523 Schwäbisch Hall, Deutschland",
  714. "address_components": [ {
  715. "long_name": "20",
  716. "short_name": "20",
  717. "types": [ "street_number" ]
  718. }, {
  719. "long_name": "Krebenweg",
  720. "short_name": "Krebenweg",
  721. "types": [ "route" ]
  722. }, {
  723. "long_name": "Bibersfeld",
  724. "short_name": "Bibersfeld",
  725. "types": [ "sublocality", "political" ]
  726. }, {
  727. "long_name": "Schwäbisch Hall",
  728. "short_name": "Schwäbisch Hall",
  729. "types": [ "locality", "political" ]
  730. }, {
  731. "long_name": "Schwäbisch Hall",
  732. "short_name": "SHA",
  733. "types": [ "administrative_area_level_2", "political" ]
  734. }, {
  735. "long_name": "Baden-Württemberg",
  736. "short_name": "BW",
  737. "types": [ "administrative_area_level_1", "political" ]
  738. }, {
  739. "long_name": "Deutschland",
  740. "short_name": "DE",
  741. "types": [ "country", "political" ]
  742. }, {
  743. "long_name": "74523",
  744. "short_name": "74523",
  745. "types": [ "postal_code" ]
  746. } ],
  747. "geometry": {
  748. "location": {
  749. "lat": 49.0817369,
  750. "lng": 9.6908451
  751. },
  752. "location_type": "RANGE_INTERPOLATED", //ROOFTOP //APPROXIMATE
  753. "viewport": {
  754. "southwest": {
  755. "lat": 49.0785954,
  756. "lng": 9.6876999
  757. },
  758. "northeast": {
  759. "lat": 49.0848907,
  760. "lng": 9.6939951
  761. }
  762. },
  763. "bounds": {
  764. "southwest": {
  765. "lat": 49.0817369,
  766. "lng": 9.6908451
  767. },
  768. "northeast": {
  769. "lat": 49.0817492,
  770. "lng": 9.6908499
  771. }
  772. }
  773. },
  774. "partial_match": true
  775. } ]
  776. }
  777. */
  778. ?>