2

我一直在努力理解如何做到这一点,而且似乎有很多人问这个问题而没有答案。我有一个带有邮政编码的用户表。我创建了一个 zips 表,其中包含美国的每个带有纬度/经度的邮政编码。

我想做的是将两者连接起来,以便用户可以搜索其他用户。我有 Thinking Sphinx,我更愿意继续使用它。我想为用户提供一个用于搜索距离的复选框(5、10、25、50、100、500 英里)。结果应始终返回最近的用户。

我认为不需要来自控制器或模型的代码,但是如果需要,请询问,我会提供。

搜索表格:

<%= form_tag searches_path, method: :get do %>
<p>
        <%= text_field_tag :search, params[:search] %>
        <%= button_tag "Search", name: nil %>
        </p>
<% end %>

<P><%= link_to "Advanced Search", new_search_path %><p>

<%= form_tag users_path, method: :get do %>
<%= label :zip_code, "Enter zip code: " %>
<%= text_field_tag :zip_code, params[:zip_code] %>
<% end %>

/indices/user_index.rb:

     ThinkingSphinx::Index.define :user, :with => :active_record do
  # fields
  indexes name, :as => :user, :sortable => true
  indexes religion, zip_code, about_me, career, sexuality, children, user_smoke, user_drink, gender, ethnicity, education


  # attributes
  has id, created_at, updated_at
  has zips.city, :as => :zip_city

  has "RADIANS(zips.lat)",  :as => :latitude,  :type => :float
  has "RADIANS(zips.lon)", :as => :longitude, :type => :float


end

用户型号:

  has_and_belongs_to_many :zips

拉链型号:

class Zip < ActiveRecord::Base
  attr_accessible :city, :lat, :lon, :code, :zipcode
  has_and_belongs_to_many :users

  validates :code, uniqueness: true

    self.primary_key = 'code'      

  def self.code(code)
    find_by(:code => code)
  end


end

用户表具有以下列:zip_code

邮政编码表具有以下列:code, city, state, lat,lon

4

4 回答 4

1

第一步是创建用户与其位置之间的关联,以便可以从用户的位置引用该位置的 ActiveRecord。

class User < ActiveRecord::Base
  belongs_to :location
  attr_accessible :name
end

class Location < ActiveRecord::Base
  attr_accessible :city, :latitude, :longitude, :zipcode
end

接下来,在索引中使用关联。

您必须为 Location 模型上的字段创建别名,以确保连接位置表。并且您必须为该位置的纬度和经度添加属性:

ThinkingSphinx::Index.define :user, :with => :active_record do
  # fields
  indexes name

  # attributes
  has created_at, updated_at
  has location.city, :as => :location_city
  has "RADIANS(locations.latitude)",  :as => :latitude,  :type => :float
  has "RADIANS(locations.longitude)", :as => :longitude, :type => :float

end

正如其他人已经提到的那样,由于地球不是平坦的,因此在计算位置之间的距离时需要考虑到这一点。Haversine 函数对此很有用。Thinking Sphinx 内置了它,您可以使用:geo.

然后例如,查找 200 公里内的所有用户 lat / lng 参数以度为单位,按最近的顺序排列:

class DistanceController < ApplicationController
  def search
    @lat = params[:lat].to_f * Math::PI / 180
    @lng = params[:lng].to_f * Math::PI / 180
    @users = User.search :geo => [@lat, @lng], :with => {:geodist => 0.0..200_000.0}, :order => "geodist ASC"
  end
end

对于调试,很高兴知道在视图中您也可以参考计算的距离:

  <% @users.each do |user| %>
    <tr>
      <td><%= user.name %></td>
      <td><%= user.location.zipcode %></td>
      <td><%= user.location.city %></td>
      <td><%= user.distance %></td>
    </tr>

编辑:添加了有关我的工作版本的更多详细信息,为了完整起见,还添加了表定义。(MySQL,使用 db:migrate 生成,这些是 MySQL Workbench 生成的创建脚本):

CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `location_id` int(11) DEFAULT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `index_users_on_location_id` (`location_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;


CREATE TABLE `locations` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `zipcode` varchar(255) DEFAULT NULL,
  `latitude` float DEFAULT NULL,
  `longitude` float DEFAULT NULL,
  `city` varchar(255) DEFAULT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
于 2013-11-18T08:03:59.010 回答
1

听起来您在将用户映射到位置时遇到问题,最初的问题似乎具有误导性。

位置表应该是基于 zip 的所有位置的参考表,而不是将位置映射到用户。然后,您应该向 Location 添加一个范围,以便您可以按邮政编码搜索位置。

像这样的东西:

# Add scope to /models/location.rb
scope :by_zip_code, ->(code) { where('code = ?', code) }

然后在用户类中,不要说用户有多个位置,而是添加一个方法,以根据用户的邮政编码检索所有位置。

例子:

# in /models/user.rb

 def location

    if Location.by_zip_code(self.zip_code.to_s).any?
        # you can return all here if you want more than one
        # for testing just returning the first one
        return Location.by_zip_code(self.zip_code.to_s).first
    else
        return nil
    end
end

然后当你有一个用户对象时,你可以使用新的实例方法:

user = User.first
user.location
于 2013-11-19T23:55:52.127 回答
0

您可以使用地理编码器gem完成所有这些(以及更多) 。gem 是可定制的,你可以指定任何你想要的地图 API(默认是谷歌地图)。这是关于地理编码器的 railscasts剧集,其中详细说明了用法。

于 2013-11-11T21:17:34.570 回答
-1

您可以使用 Haversine 公式计算两点之间的距离。

Landon Cox 已经在这个位置为您创建了 Haversine 类:http: //www.esawdust.com/blog/gps/files/HaversineFormulaInRuby.html

我在以前的项目中使用过这个类,它工作得很好

您需要做的就是在逻辑中比较每个用户与您当前位置或所选用户的距离。由于您已经掌握了所有内容的经纬度,这应该非常简单。

希望这可以帮助。

于 2013-11-15T18:23:52.243 回答