| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046 |
- <?php
- App::uses('String', 'Utility');
- App::uses('Xml', 'Utility');
- App::uses('HttpSocketLib', 'Tools.Lib');
- /**
- * Geocode via google (UPDATE: api3)
- * @see DEPRECATED api2: http://code.google.com/intl/de-DE/apis/maps/articles/phpsqlgeocode.html
- * @see http://code.google.com/intl/de/apis/maps/documentation/geocoding/#Types
- *
- * Used by Tools.GeocoderBehavior
- *
- * TODOS (since 1.2):
- * - Work with exceptions in 2.x
- * - Rewrite in a cleaner 2.x way
- *
- * @author Mark Scherer
- * @cakephp 2.x
- * @licence MIT
- * 2010-06-25 ms
- */
- class GeocodeLib {
- const BASE_URL = 'http://{host}/maps/api/geocode/{output}?';
- const DEFAULT_HOST = 'de';
- const ACC_COUNTRY = 0;
- const ACC_AAL1 = 1;
- const ACC_AAL2 = 2;
- const ACC_AAL3 = 3;
- const ACC_POSTAL = 4;
- const ACC_LOC = 5;
- const ACC_SUBLOC = 6;
- const ACC_ROUTE = 7;
- const ACC_INTERSEC = 8;
- const ACC_STREET = 9;
- const UNIT_KM = 'K';
- const UNIT_NAUTICAL = 'N';
- const UNIT_FEET = 'F';
- const UNIT_INCHES = 'I';
- const UNIT_MILES = 'M';
- # First tries with curl, then cake, then php
- public $use = array(
- 'curl' => true,
- 'cake'=> true,
- 'php' => true
- );
- public $units = array(
- self::UNIT_KM => 1.609344,
- self::UNIT_NAUTICAL => 0.868976242,
- self::UNIT_FEET => 5280,
- self::UNIT_INCHES => 63360,
- self::UNIT_MILES => 1
- );
- /**
- * validation and retrieval options
- * - use:
- * - log: false logs only real errors, true all activities
- * - pause: timeout to prevent blocking
- * - ...
- *
- * 2010-06-25 ms
- */
- public $options = array(
- 'log' => false,
- 'pause' => 10000, # in ms
- 'min_accuracy' => self::ACC_COUNTRY,
- 'allow_inconclusive'=> true,
- 'expect' => array(), # see accuracyTypes for details
- # static url params
- 'output' => 'xml',
- 'host' => self::DEFAULT_HOST, # results in maps.google.com - use if you wish to obtain the closest address
- );
- /**
- * url params
- * 2010-06-25 ms
- */
- protected $params = array(
- 'address' => '', # either address or latlng required!
- '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
- 'language' => 'de',
- 'bounds' => '',
- 'sensor' => 'false', # device with gps module sensor
- //'key' => '' # not necessary anymore
- );
- protected $error = array();
- protected $result = null;
- /**
- * 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.
- */
- protected $hosts = array(
- 'us' => 'maps.google.com', # only one for "allow_inconclusive" = true
- 'gb' => 'maps.google.co.uk',
- 'de' => 'maps.google.de',
- 'ch' => 'maps.google.ch',
- 'at' => 'maps.google.at',
- 'it' => 'maps.google.it',
- //ADD MORE - The two-letter codes are iso2 country codes and are mapped to top level domains (ccTLDs)
- );
- protected $statusCodes = array(
- self::CODE_SUCCESS => 'Success',
- self::CODE_BAD_REQUEST => 'Sensor param missing',
- self::CODE_MISSING_QUERY => 'Adress/LatLng missing',
- self::CODE_UNKNOWN_ADDRESS => 'Success, but to address found',
- self::CODE_TOO_MANY_QUERIES => 'Limit exceeded',
- );
- protected $accuracyTypes = array(
- self::ACC_COUNTRY => 'country',
- self::ACC_AAL1 => 'administrative_area_level_1', # provinces/states
- self::ACC_AAL2 => 'administrative_area_level_2 ',
- self::ACC_AAL3 => 'administrative_area_level_3',
- self::ACC_POSTAL => 'postal_code',
- self::ACC_LOC => 'locality',
- self::ACC_SUBLOC => 'sublocality',
- self::ACC_ROUTE => 'route',
- self::ACC_INTERSEC => 'intersection',
- self::ACC_STREET => 'street_address'
- //neighborhood premise subpremise natural_feature airport park point_of_interest colloquial_area political ?
- );
- public function __construct($options = array()) {
- $this->defaultParams = $this->params;
- $this->defaultOptions = $this->options;
- if (Configure::read('debug') > 0) {
- $this->options['log'] = true;
- }
- $this->setOptions($options);
- }
- /**
- * @param array $params
- * @return void
- */
- public function setParams($params) {
- foreach ($params as $key => $value) {
- if ($key == 'sensor' && $value != 'false' && $value != 'true') {
- $value = !empty($value) ? 'true' : 'false';
- }
- $this->params[$key] = urlencode((string)$value);
- }
- }
- /**
- * @param array $options
- * @return void
- */
- public function setOptions($options) {
- foreach ($options as $key => $value) {
- if ($key == 'output' && $value != 'xml' && $value != 'json') {
- throw new CakeException('Invalid output format');
- }
- if ($key == 'host' && !array_key_exists($value, $this->hosts)) {
- throw new CakeException('Invalid host');
- }
- $this->options[$key] = $value;
- }
- }
- public function setError($error) {
- if (empty($error)) {
- return;
- }
- $this->error[] = $error;
- }
- public function error($asString = true, $separator = ', ') {
- if (!$asString) {
- return $this->error;
- }
- return implode(', ', $this->error);
- }
- /**
- * @param bool $full
- * @return void
- */
- public function reset($full = true) {
- $this->error = array();
- $this->result = null;
- if ($full) {
- $this->params = $this->defaultParams;
- $this->options = $this->defaultOptions;
- }
- }
- /**
- * Build url
- *
- * @return string $url (full)
- * 2010-06-29 ms
- */
- public function url() {
- $params = array(
- 'host' => $this->hosts[$this->options['host']],
- 'output' => $this->options['output']
- );
- $url = String::insert(self::BASE_URL, $params, array('before'=>'{', 'after'=>'}', 'clean'=>true));
- $params = array();
- foreach ($this->params as $key => $value) {
- if (!empty($value)) {
- $params[] = $key.'='.$value;
- }
- }
- return $url . implode('&', $params);
- }
- /**
- * @return bool $isInconclusive (or null if no query has been run yet)
- */
- public function isInconclusive() {
- if ($this->result === null) {
- return null;
- }
- if (!isset($this->result[0])) {
- return false;
- }
- return count($this->result) > 0;
- }
- /**
- * @return array $result
- * 2010-06-25 ms
- */
- public function getResult() {
- if ($this->result !== null) {
- if (isset($this->result[0])) {
- $res = array();
- foreach ($this->result as $tmp) {
- $res[] = $this->options['output'] == 'json' ? $this->_transformJson($tmp) : $this->_transformXml($tmp);
- }
- return $res;
- }
- if ($this->options['output'] == 'json') {
- return $this->_transformJson($this->result);
- } else {
- return $this->_transformXml($this->result);
- }
- }
- return false;
- }
- /**
- * results usually from most accurate to least accurate result (street_address, ..., country)
- * @param float $lat
- * @param float $lng
- * @param array $options
- * - allow_inconclusive
- * - min_accuracy
- * @return boolean $success
- * 2010-06-29 ms
- */
- public function reverseGeocode($lat, $lng, $settings = array()) {
- $this->reset(false);
- $latlng = $lat . ',' . $lng;
- $this->setParams(array_merge($settings, array('latlng' => $latlng)));
- $count = 0;
- $request_url = $this->url();
- while (true) {
- $result = $this->_fetch($request_url);
- if ($result === false || $result === null) {
- $this->setError('Could not retrieve url');
- CakeLog::write('geocode', __('Could not retrieve url with \'%s\'', $latlng));
- return false;
- }
- if ($this->options['output'] == 'json') {
- //$res = json_decode($result);
- } else {
- $res = Xml::build($result);
- }
- if (!is_object($res)) {
- $this->setError('XML parsing failed');
- CakeLog::write('geocode', __('Failed with XML parsing of \'%s\'', $latlng));
- return false;
- }
- $xmlArray = Xml::toArray($res);
- $xmlArray = $xmlArray['GeocodeResponse'];
- $status = $xmlArray['status'];
- if ($status == self::CODE_SUCCESS) {
- # validate
- if (isset($xmlArray['result'][0]) && !$this->options['allow_inconclusive']) {
- $this->setError(__('Inconclusive result (total of %s)', count($xmlArray['result'])));
- $this->result = $xmlArray['result'];
- return false;
- }
- if (isset($xmlArray['result'][0])) {
- //$xmlArray['result'] = $xmlArray['result'][0];
- $accuracy = $this->_parse('type', $xmlArray['result'][0]);
- } else {
- $accuracy = $this->_parse('type', $xmlArray['result']);
- }
- if ($this->_isNotAccurateEnough($accuracy)) {
- $accuracy = implode(', ', (array)$accuracy);
- $minAccuracy = $this->accuracyTypes[$this->options['min_accuracy']];
- $this->setError(__('Accuracy not good enough (%s instead of at least %s)', $accuracy, $minAccuracy));
- $this->result = $xmlArray['result'];
- return false;
- }
- # save Result
- if ($this->options['log']) {
- CakeLog::write('geocode', __('Address \'%s\' has been geocoded', $latlng));
- }
- break;
- } elseif ($status == self::CODE_TOO_MANY_QUERIES) {
- // sent geocodes too fast, delay +0.1 seconds
- if ($this->options['log']) {
- CakeLog::write('geocode', __('Delay necessary for \'%s\'', $latlng));
- }
- $count++;
- } else {
- # something went wrong
- $this->setError('Error '.$status.(isset($this->statusCodes[$status]) ? ' ('.$this->statusCodes[$status].')' : ''));
- if ($this->options['log']) {
- CakeLog::write('geocode', __('Could not geocode \'%s\'', $latlng));
- }
- return false; # for now...
- }
- if ($count > 5) {
- if ($this->options['log']) {
- CakeLog::write('geocode', __('Aborted after too many trials with \'%s\'', $latlng));
- }
- $this->setError(__('Too many trials - abort'));
- return false;
- }
- $this->pause(true);
- }
- $this->result = $xmlArray['result'];
- return true;
- }
- /**
- * trying to avoid "TOO_MANY_QUERIES" error
- * @param bool $raise If the pause length should be raised
- * 2010-06-29 ms
- */
- public function pause($raise = false) {
- usleep($this->options['pause']);
- if ($raise) {
- $this->options['pause'] += 10000;
- }
- }
- /**
- * @param string $address
- * @param array $settings
- * - allow_inconclusive
- * - min_accuracy
- * @return boolean $success
- * 2010-06-25 ms
- */
- public function geocode($address, $settings = array()) {
- $this->reset(false);
- $this->setParams(array_merge($settings, array('address'=>$address)));
- if ($this->options['allow_inconclusive']) {
- # only host working with this setting?
- //$this->options['host'] = self::DEFAULT_HOST;
- }
- $count = 0;
- $request_url = $this->url();
- while (true) {
- $result = $this->_fetch($request_url);
- if ($result === false || $result === null) {
- $this->setError('Could not retrieve url');
- CakeLog::write('geocode', 'Geocoder could not retrieve url with \''.$address.'\'');
- return false;
- }
- if ($this->options['output'] == 'json') {
- //TODO? necessary?
- //$res = json_decode($result);
- } else {
- try {
- $res = Xml::build($result);
- } catch (Exception $e) {
- CakeLog::write('geocode', $e->getMessage());
- $res = array();
- }
- }
- if (!is_object($res)) {
- $this->setError('XML parsing failed');
- CakeLog::write('geocode', __('Failed with XML parsing of \'%s\'', $address));
- return false;
- }
- $xmlArray = Xml::toArray($res);
- $xmlArray = $xmlArray['GeocodeResponse'];
- $status = $xmlArray['status'];
- if ($status == self::CODE_SUCCESS) {
- # validate
- if (isset($xmlArray['result'][0]) && !$this->options['allow_inconclusive']) {
- $this->setError(__('Inconclusive result (total of %s)', count($xmlArray['result'])));
- $this->result = $xmlArray['result'];
- return false;
- }
- if (isset($xmlArray['result'][0])) {
- //$xmlArray['result'] = $xmlArray['result'][0];
- $accuracy = $this->_parse('type', $xmlArray['result'][0]);
- } else {
- $accuracy = $this->_parse('type', $xmlArray['result']);
- }
- //echo returns($accuracy);
- if ($this->_isNotAccurateEnough($accuracy)) {
- $accuracy = implode(', ', (array)$accuracy);
- $minAccuracy = $this->accuracyTypes[$this->options['min_accuracy']];
- $this->setError(__('Accuracy not good enough (%s instead of at least %s)', $accuracy, $minAccuracy));
- $this->result = $xmlArray['result'];
- return false;
- }
- if (!empty($this->options['expect'])) {
- $types = array($accuracy); # TODO: maybe check more than just first?
- $validExpectation = false;
- foreach ($types as $type) {
- if (in_array($type, (array)$this->options['expect'])) {
- $validExpectation = true;
- break;
- }
- }
- if (!$validExpectation) {
- $this->setError(__('Expectation not reached (%s instead of at least %s)', $accuracy, implode(', ', (array)$this->options['expect'])));
- $this->result = $xmlArray['result'];
- return false;
- }
- }
- # save Result
- if ($this->options['log']) {
- CakeLog::write('geocode', __('Address \'%s\' has been geocoded', $address));
- }
- break;
- } elseif ($status == self::CODE_TOO_MANY_QUERIES) {
- // sent geocodes too fast, delay +0.1 seconds
- if ($this->options['log']) {
- CakeLog::write('geocode', __('Delay necessary for address \'%s\'', $address));
- }
- $count++;
- } else {
- # something went wrong
- $this->setError('Error '.$status.(isset($this->statusCodes[$status]) ? ' ('.$this->statusCodes[$status].')' : ''));
- if ($this->options['log']) {
- CakeLog::write('geocode', __('Could not geocode \'%s\'', $address));
- }
- return false; # for now...
- }
- if ($count > 5) {
- if ($this->options['log']) {
- CakeLog::write('geocode', __('Aborted after too many trials with \'%s\'', $address));
- }
- $this->setError('Too many trials - abort');
- return false;
- }
- $this->pause(true);
- }
- $this->result = $xmlArray['result'];
- return true;
- }
- /**
- * @return bool $success
- */
- protected function _isNotAccurateEnough($accuracy = null) {
- if ($accuracy === null) {
- if (isset($this->result[0])) {
- $accuracy = $this->result[0]['type'];
- } else {
- $accuracy = $this->result['type'];
- }
- }
- if (is_array($accuracy)) {
- $accuracy = array_shift($accuracy);
- }
- if (!in_array($accuracy, $this->accuracyTypes)) {
- return null;
- }
- foreach ($this->accuracyTypes as $key => $type) {
- if ($type == $accuracy) {
- $accuracy = $key;
- break;
- }
- }
- //echo returns($accuracy);
- //echo returns('XXX'.$this->options['min_accuracy']);
- return $accuracy < $this->options['min_accuracy'];
- }
- protected function _transformJson($record) {
- $res = array();
- //TODO
- return $res;
- }
- /**
- * try to find the correct path
- * - type (string)
- * - Type (array[string, ...])
- * 2010-06-29 ms
- */
- protected function _parse($key, $array) {
- if (isset($array[$key])) {
- return $array[$key];
- }
- if (isset($array[($key = ucfirst($key))])) {
- return $array[$key][0];
- }
- return null;
- }
- /**
- * flattens result array and returns clean record
- * keys:
- * - formatted_address, type, country, country_code, country_province, country_province_code, locality, sublocality, postal_code, route, lat, lng, location_type, viewport, bounds
- * 2010-06-25 ms
- */
- protected function _transformXml($record) {
- $res = array();
- $components = array();
- if (!isset($record['address_component'][0])) {
- $record['address_component'] = array($record['address_component']);
- }
- foreach ($record['address_component'] as $c) {
- $types = array();
- if (isset($c['type'])) { //!is_array($c['Type'])
- if (!is_array($c['type'])) {
- $c['type'] = (array)$c['type'];
- }
- $type = $c['type'][0];
- array_shift($c['type']);
- $types = $c['type'];
- } elseif (isset($c['type'])) {
- $type = $c['type'];
- } else {
- # error?
- continue;
- }
- if (array_key_exists($type, $components)) {
- $components[$type]['name'] .= ' '.$c['long_name'];
- $components[$type]['abbr'] .= ' '.$c['short_name'];
- $components[$type]['types'] += $types;
- }
- $components[$type] = array('name'=>$c['long_name'], 'abbr'=>$c['short_name'], 'types'=>$types);
- }
- $res['formatted_address'] = $record['formatted_address'];
- $res['type'] = $this->_parse('type', $record);
- if (array_key_exists('country', $components)) {
- $res['country'] = $components['country']['name'];
- $res['country_code'] = $components['country']['abbr'];
- } else {
- $res['country'] = $res['country_code'] = '';
- }
- if (array_key_exists('administrative_area_level_1', $components)) {
- $res['country_province'] = $components['administrative_area_level_1']['name'];
- $res['country_province_code'] = $components['administrative_area_level_1']['abbr'];
- } else {
- $res['country_province'] = $res['country_province_code'] = '';
- }
- if (array_key_exists('postal_code', $components)) {
- $res['postal_code'] = $components['postal_code']['name'];
- } else {
- $res['postal_code'] = '';
- }
- if (array_key_exists('locality', $components)) {
- $res['locality'] = $components['locality']['name'];
- } else {
- $res['locality'] = '';
- }
- if (array_key_exists('sublocality', $components)) {
- $res['sublocality'] = $components['sublocality']['name'];
- } else {
- $res['sublocality'] = '';
- }
- if (array_key_exists('route', $components)) {
- $res['route'] = $components['route']['name'];
- if (array_key_exists('street_number', $components)) {
- $res['route'] .= ' '.$components['street_number']['name'];
- }
- } else {
- $res['route'] = '';
- }
- //TODO: add more
- $res['lat'] = $record['geometry']['location']['lat'];
- $res['lng'] = $record['geometry']['location']['lng'];
- $res['location_type'] = $record['geometry']['location_type'];
- if (!empty($record['geometry']['viewport'])) {
- $res['viewport'] = array('sw'=>$record['geometry']['viewport']['southwest'], 'ne'=>$record['geometry']['viewport']['northeast']);
- }
- if (!empty($record['geometry']['bounds'])) {
- $res['bounds'] = array('sw'=>$record['geometry']['bounds']['southwest'], 'ne'=>$record['geometry']['bounds']['northeast']);
- }
- # manuell corrections
- $array = array(
- 'Berlin' => 'BE',
- );
- if (!empty($res['country_province_code']) && array_key_exists($res['country_province_code'], $array)) {
- $res['country_province_code'] = $array[$res['country_province_code']];
- }
- return $res;
- }
- /**
- * fetches url with curl if available
- * fallbacks: cake and php
- * note: expects url with json encoded content
- * @access private
- **/
- protected function _fetch($url) {
- $this->HttpSocket = new HttpSocketLib($this->use);
- if ($res = $this->HttpSocket->fetch($url, 'CakePHP Geocode Lib')) {
- return $res;
- }
- $this->setError($this->HttpSocket->error());
- return false;
- }
- /**
- * debugging
- * 2009-11-27 ms
- */
- public function debug() {
- return $this->result;
- }
- /**
- * Calculates Distance between two points - each: array('lat'=>x,'lng'=>y)
- * DB:
- '6371.04 * ACOS( COS( PI()/2 - RADIANS(90 - Retailer.lat)) * ' .
- 'COS( PI()/2 - RADIANS(90 - '. $data['Location']['lat'] .')) * ' .
- 'COS( RADIANS(Retailer.lng) - RADIANS('. $data['Location']['lng'] .')) + ' .
- 'SIN( PI()/2 - RADIANS(90 - Retailer.lat)) * ' .
- 'SIN( PI()/2 - RADIANS(90 - '. $data['Location']['lat'] . '))) ' .
- 'AS distance'
- * @param array pointX
- * @param array pointY
- * @param float $unit (M=miles, K=kilometers, N=nautical miles, I=inches, F=feet)
- * @return int distance: in km
- * 2009-03-06 ms
- */
- public function distance($pointX, $pointY, $unit = null) {
- if (empty($unit) || !array_key_exists(($unit = strtoupper($unit)), $this->units)) {
- $unit = array_keys($this->units);
- $unit = $unit[0];
- }
- /*
- $res = 6371.04 * ACOS( COS( PI()/2 - rad2deg(90 - $pointX['lat'])) *
- COS( PI()/2 - rad2deg(90 - $pointY['lat'])) *
- COS( rad2deg($pointX['lng']) - rad2deg($pointY['lng'])) +
- SIN( PI()/2 - rad2deg(90 - $pointX['lat'])) *
- SIN( PI()/2 - rad2deg(90 - $pointY['lat'])));
- $res = 6371.04 * acos(sin($pointY['lat'])*sin($pointX['lat'])+cos($pointY['lat'])*cos($pointX['lat'])*cos($pointY['lng'] - $pointX['lng']));
- */
- # seems to be the only working one (although slightly incorrect...)
- $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']))));
- if (isset($this->units[$unit])) {
- $res *= $this->units[$unit];
- }
- return ceil($res);
- }
- /**
- * Convert between units
- *
- * @param float $value
- * @param char $fromUnit (using class constants)
- * @param char $toUnit (using class constants)
- * @return float $convertedValue
- * @throws CakeException
- */
- public function convert($value, $fromUnit, $toUnit) {
- if (!isset($this->units[($fromUnit = strtoupper($fromUnit))]) || !isset($this->units[($toUnit = strtoupper($toUnit))])) {
- throw new CakeException(__('Invalid Unit'));
- }
- if ($fromUnit == 'M') {
- $value *= $this->units[$toUnit];
- } elseif ($toUnit == 'M') {
- $value /= $this->units[$fromUnit];
- } else {
- $value /= $this->units[$fromUnit];
- $value *= $this->units[$toUnit];
- }
- return $value;
- }
- /**
- * Fuzziness filter for coordinates (lat or lng).
- * Useful if you store other users' locations and want to grant some
- * privacy protection. This way the coordinates will be slightly modified.
- *
- * @param float coord
- * @param int level (0 = nothing to 5 = extrem)
- * - 1:
- * - 2:
- * - 3:
- * - 4:
- * - 5:
- * @throws CakeException
- * @return float $coord
- * 2011-03-16 ms
- */
- public static function blur($coord, $level = 0) {
- if (!$level) {
- return $coord;
- }
- //TODO:
- switch ($level) {
- case 1:
- break;
- case 2:
- break;
- case 3:
- break;
- case 4:
- break;
- case 5:
- break;
- default:
- throw new CakeException(__('Invalid level \'%s\'', $level));
- }
- $scrambleVal = 0.000001 * mt_rand(1000,2000) * (mt_rand(0,1) === 0 ? 1 : -1);
- return ($coord + $scrambleVal);
- //$scrambleVal *= (mt_rand(0,1) === 0 ? 1 : 2);
- //$scrambleVal *= (float)(2^$level);
- # TODO: + - by chance!!!
- return $coord + $scrambleVal;
- }
- const TYPE_ROOFTOP = 'ROOFTOP';
- const TYPE_RANGE_INTERPOLATED = 'RANGE_INTERPOLATED';
- const TYPE_GEOMETRIC_CENTER = 'GEOMETRIC_CENTER';
- const TYPE_APPROXIMATE = 'APPROXIMATE';
- const CODE_SUCCESS = 'OK'; //200;
- const CODE_TOO_MANY_QUERIES = 'OVER_QUERY_LIMIT'; //620;
- const CODE_BAD_REQUEST = 'REQUEST_DENIED'; //400;
- const CODE_MISSING_QUERY = 'INVALID_REQUEST';//601;
- const CODE_UNKNOWN_ADDRESS = 'ZERO_RESULTS'; //602;
- /*
- const CODE_SERVER_ERROR = 500;
- const CODE_UNAVAILABLE_ADDRESS = 603;
- const CODE_UNKNOWN_DIRECTIONS = 604;
- const CODE_BAD_KEY = 610;
- */
- }
- /*
- TODO:
- http://code.google.com/intl/de-DE/apis/maps/documentation/geocoding/
- - whats the difference to "http://maps.google.com/maps/api/geocode/output?parameters"
- */
- /*
- Example: NEW:
- Array
- (
- [status] => OK
- [Result] => Array
- (
- [type] => postal_code
- [formatted_address] => 74523, Deutschland
- [AddressComponent] => Array
- (
- [0] => Array
- (
- [long_name] => 74523
- [short_name] => 74523
- [type] => postal_code
- )
- [1] => Array
- (
- [long_name] => Schwaebisch Hall
- [short_name] => SHA
- [Type] => Array
- (
- [0] => administrative_area_level_2
- [1] => political
- )
- )
- [2] => Array
- (
- [long_name] => Baden-Wuerttemberg
- [short_name] => BW
- [Type] => Array
- (
- [0] => administrative_area_level_1
- [1] => political
- )
- )
- [3] => Array
- (
- [long_name] => Deutschland
- [short_name] => DE
- [Type] => Array
- (
- [0] => country
- [1] => political
- )
- )
- )
- [Geometry] => Array
- (
- [Location] => Array
- (
- [lat] => 49.1257616
- [lng] => 9.7544127
- )
- [location_type] => APPROXIMATE
- [Viewport] => Array
- (
- [Southwest] => Array
- (
- [lat] => 49.0451477
- [lng] => 9.6132550
- )
- [Northeast] => Array
- (
- [lat] => 49.1670260
- [lng] => 9.8756350
- )
- )
- [Bounds] => Array
- (
- [Southwest] => Array
- (
- [lat] => 49.0451477
- [lng] => 9.6132550
- )
- [Northeast] => Array
- (
- [lat] => 49.1670260
- [lng] => 9.8756350
- )
- )
- )
- )
- )
- Example OLD:
- Array
- (
- [name] => 74523 Deutschland
- [Status] => Array
- (
- [code] => 200
- [request] => geocode
- )
- [Result] => Array
- (
- [id] => p1
- [address] => 74523, Deutschland
- [AddressDetails] => Array
- (
- [Accuracy] => 5
- [xmlns] => urn:oasis:names:tc:ciq:xsdschema:xAL:2.0
- [Country] => Array
- (
- [CountryNameCode] => DE
- [CountryName] => Deutschland
- [AdministrativeArea] => Array
- (
- [AdministrativeAreaName] => Baden-Wuerttemberg
- [SubAdministrativeArea] => Array
- (
- [SubAdministrativeAreaName] => Schwaebisch Hall
- [PostalCode] => Array
- (
- [PostalCodeNumber] => 74523
- )
- )
- )
- )
- )
- [ExtendedData] => Array
- (
- [LatLonBox] => Array
- (
- [north] => 49.1670260
- [south] => 49.0451477
- [east] => 9.8756350
- [west] => 9.6132550
- )
- )
- [Point] => Array
- (
- [coordinates] => 9.7544127,49.1257616,0
- )
- )
- ) {
- "status": "OK",
- "results": [ {
- "types": [ "street_address" ],
- "formatted_address": "Krebenweg 20, 74523 Schwäbisch Hall, Deutschland",
- "address_components": [ {
- "long_name": "20",
- "short_name": "20",
- "types": [ "street_number" ]
- }, {
- "long_name": "Krebenweg",
- "short_name": "Krebenweg",
- "types": [ "route" ]
- }, {
- "long_name": "Bibersfeld",
- "short_name": "Bibersfeld",
- "types": [ "sublocality", "political" ]
- }, {
- "long_name": "Schwäbisch Hall",
- "short_name": "Schwäbisch Hall",
- "types": [ "locality", "political" ]
- }, {
- "long_name": "Schwäbisch Hall",
- "short_name": "SHA",
- "types": [ "administrative_area_level_2", "political" ]
- }, {
- "long_name": "Baden-Württemberg",
- "short_name": "BW",
- "types": [ "administrative_area_level_1", "political" ]
- }, {
- "long_name": "Deutschland",
- "short_name": "DE",
- "types": [ "country", "political" ]
- }, {
- "long_name": "74523",
- "short_name": "74523",
- "types": [ "postal_code" ]
- } ],
- "geometry": {
- "location": {
- "lat": 49.0817369,
- "lng": 9.6908451
- },
- "location_type": "RANGE_INTERPOLATED", //ROOFTOP //APPROXIMATE
- "viewport": {
- "southwest": {
- "lat": 49.0785954,
- "lng": 9.6876999
- },
- "northeast": {
- "lat": 49.0848907,
- "lng": 9.6939951
- }
- },
- "bounds": {
- "southwest": {
- "lat": 49.0817369,
- "lng": 9.6908451
- },
- "northeast": {
- "lat": 49.0817492,
- "lng": 9.6908499
- }
- }
- },
- "partial_match": true
- } ]
- }
- */
|