Model->distanceQuery(array( * 'latitude' => 34.2746405, * 'longitude' => -119.2290053 * )); * $query['conditions']['published'] = true; * $results = $this->Model->find('all', $query); * * @param array $opts Options * - latitude The latitude coordinate of the center point * - longitude The longitude coordinate of the center point * - alias The model name of the query this is for * defaults to the current model alias * - radius The distance to at which to find objects at * defaults to false in which case distance is calculated * only for the sort order * @return array A query that can be modified and passed to find or paginate */ public function distanceQuery($opts = array()) { $defaults = array( 'latitude' => 0, 'longitude' => 0, 'alias' => $this->alias, 'radius' => false ); $opts = Set::merge($defaults, $opts); $query = array( 'fields' => array( '*', String::insert( '3956 * 2 * ASIN(SQRT( POWER(SIN((:latitude - ABS(:alias.latitude)) * PI() / 180 / 2), 2) + COS(:latitude * PI() / 180) * COS(ABS(:alias.latitude) * PI() / 180) * POWER(SIN((:longitude - :alias.longitude) * PI() / 180 / 2), 2) )) AS distance', array('alias' => $opts['alias'], 'latitude' => $opts['latitude'], 'longitude' => $opts['longitude']) ) ), 'order' => array('distance' => 'ASC') ); if ($opts['radius']) { $longitudeLower = $opts['longitude'] - $opts['radius'] / abs(cos(deg2rad($opts['latitude'])) * 69); $longitudeUpper = $opts['longitude'] + $opts['radius'] / abs(cos(deg2rad($opts['latitude'])) * 69); $latitudeLower = $opts['latitude'] - ($opts['radius'] / 69); $latitudeUpper = $opts['latitude'] + ($opts['radius'] / 69); $query['conditions'] = array( String::insert(':alias.latitude BETWEEN ? AND ?', array('alias' => $opts['alias'])) => array($latitudeLower, $latitudeUpper), String::insert(':alias.longitude BETWEEN ? AND ?', array('alias' => $opts['alias'])) => array($longitudeLower, $longitudeUpper) ); $query['group'] = sprintf('%s.id HAVING distance < %f', $opts['alias'], $opts['radius']); } return $query; } }