class Post < ActiveRecord::Base belongs_to :feed validates :title, :presence => true validates :lat, :numericality => { :greater_than_or_equal_to => -90, :less_than_or_equal_to => 90 } validates :lon, :numericality => { :greater_than_or_equal_to => -180, :less_than_or_equal_to => 180 } EARTH_RADIUS_METRES = 6378000 def self.near(lat, lon, radius_metres, layer_id) # Santize inputs. Is this necessary? lat = lat.to_f lon = lon.to_f radius_metres = radius_metres.to_i layer_id = layer_id.to_i # Calculate distance using the Haversine formula self.find_by_sql(<<-ENDQUERY SELECT p.id, p.title, p.summary, p.url, p.lat, p.lon, p.published, p.feed_id, f.title as feed_title, ( #{EARTH_RADIUS_METRES} * acos( cos( radians('#{lat}') ) * cos( radians( p.lat ) ) * cos( radians( p.lon ) - radians('#{lon}') ) + sin( radians('#{lat}') ) * sin( radians( p.lat ) ) ) ) As distance FROM posts p INNER JOIN feeds f ON p.feed_id = f.id WHERE p.id IN ( -- Subquery returns a list of post_ids for posts on this layer SELECT p.id FROM subscriptions s INNER JOIN feeds f ON s.feed_id = f.id INNER JOIN posts p ON p.feed_id = f.id WHERE s.layer_id = #{layer_id} ) AND ( #{EARTH_RADIUS_METRES} * acos( cos( radians('#{lat}') ) * cos( radians( p.lat ) ) * cos( radians( p.lon ) - radians('#{lon}') ) + sin( radians('#{lat}') ) * sin( radians( p.lat ) ) ) ) <= #{radius_metres} ORDER BY distance ENDQUERY ) end end