Make "nearby users" show all those within 50km rather than all those
authorTom Hughes <tom@compton.nu>
Wed, 20 Jun 2007 17:04:29 +0000 (17:04 +0000)
committerTom Hughes <tom@compton.nu>
Wed, 20 Jun 2007 17:04:29 +0000 (17:04 +0000)
within a degree of latitude and longitude.

app/models/user.rb
app/views/user/account.rhtml
app/views/user/view.rhtml
lib/osm.rb

index 92d47d5ffab5e88117738f94b60fd596510c2d18..66669f95a084c67509a45f22208c29a78111a927 100644 (file)
@@ -59,13 +59,21 @@ class User < ActiveRecord::Base
     return el1
   end
 
-  def nearby(lat_range=1, lon_range=1)
-      if self.home_lon and self.home_lat 
-          nearby = User.find(:all,  :conditions => "#{self.home_lon} > home_lon - #{lon_range} and #{self.home_lon} < home_lon + #{lon_range} and  #{self.home_lat} > home_lat - #{lat_range} and #{self.home_lat} < home_lat + #{lat_range} and data_public = 1 and id != #{self.id}") 
-      else
-          nearby = []
-      end
-      return nearby
+  def nearby(radius = 50)
+    if self.home_lon and self.home_lat 
+      gc = OSM::GreatCircle.new(self.home_lat, self.home_lon)
+      bounds = gc.bounds(radius)
+      nearby = User.find(:all, :conditions => "home_lat between #{bounds[:minlat]} and #{bounds[:maxlat]} and home_lon between #{bounds[:minlon]} and #{bounds[:maxlon]} and data_public = 1 and id != #{self.id}")
+      nearby.delete_if { |u| gc.distance(u.home_lat, u.home_lon) > radius }
+      nearby.sort! { |u1,u2| gc.distance(u1.home_lat, u1.home_lon) <=> gc.distance(u2.home_lat, u2.home_lon) }
+    else
+      nearby = []
+    end
+    return nearby
+  end
+
+  def distance(nearby_user)
+    return OSM::GreatCircle.new(self.home_lat, self.home_lon).distance(nearby_user.home_lat, nearby_user.home_lon)
   end
 
   def self.has_messages?
index cd38cc67f93686108eeb2eaf705c9540f34c399a..5014b43afdc14f34cf8cf6edf17b787474cad47e 100644 (file)
     <table>
     <tr>
     <th>Name</th>
+    <th>Distance</th>
     <th>Contact</th>
     </tr>
-    <% @user.nearby(1,1).each do |nearby| %>
+    <% @user.nearby.each do |nearby| %>
     <% nearest_str += "nearest.push( { 'display_name' : '#{nearby.display_name}', 'home_lat' : #{nearby.home_lat}, 'home_lon' : #{nearby.home_lon} } );\n" %>
 
     <tr>
     <td><%= link_to nearby.display_name, :controller => 'user', :action => 'view',  :display_name => nearby.display_name %></td>
+    <td><%= @user.distance(nearby).round %>km away</td>
     <td><%= link_to 'send message', :controller => 'message', :action => 'new', :user_id => nearby.id %></td>
     </tr>
     <%end%>
index 7bc75a62fca17dabdf94c53514053c44e901791f..862f802cea2da925f9a9f60992b12dc2d14529f7 100644 (file)
     There are no users who admit to mapping nearby.
   <% else %>
     <table id="nearbyusers">
-    <% @this_user.nearby(1,1).each do |nearby| %>
+    <% @this_user.nearby.each do |nearby| %>
     <tr>
     <td class="username"><%= link_to nearby.display_name, :controller => 'user', :action => 'view',  :display_name => nearby.display_name %></td>
+    <td><%= @this_user.distance(nearby).round %>km away</td>
     <td class="message">(<%= link_to 'send message', :controller => 'message', :action => 'new', :user_id => nearby.id %>)</td>
     </tr>
     <%end%>
index 40ec9da3d9f14c88e8bc71507a39bc80a6bb5bba..ae8e70896a7d3c7a775a05cc7b637527b9ca17b9 100644 (file)
@@ -241,6 +241,34 @@ module OSM
 
   end
 
+  class GreatCircle
+    include Math
+
+    # initialise with a base position
+    def initialize(lat, lon)
+      @lat = lat * PI / 180
+      @lon = lon * PI / 180
+    end
+
+    # get the distance from the base position to a given position
+    def distance(lat, lon)
+      lat = lat * PI / 180
+      lon = lon * PI / 180
+      return 6372.795 * 2 * asin(sqrt(sin((lat - @lat) / 2) ** 2 + cos(@lat) * cos(lat) * sin((lon - @lon)/2) ** 2))
+    end
+
+    # get the worst case bounds for a given radius from the base position
+    def bounds(radius)
+      latradius = 2 * asin(sqrt(sin(radius / 6372.795 / 2) ** 2))
+      lonradius = 2 * asin(sqrt(sin(radius / 6372.795 / 2) ** 2 / cos(@lat) ** 2))
+      minlat = (@lat - latradius) * 180 / PI
+      maxlat = (@lat + latradius) * 180 / PI
+      minlon = (@lon - lonradius) * 180 / PI
+      maxlon = (@lon + lonradius) * 180 / PI
+      return { :minlat => minlat, :maxlat => maxlat, :minlon => minlon, :maxlon => maxlon }
+    end
+  end
+
   class GeoRSS
     def initialize(feed_title='OpenStreetMap GPS Traces', feed_description='OpenStreetMap GPS Traces', feed_url='http://www.openstreetmap.org/traces/')
       @doc = XML::Document.new