【问题标题】:Geo searching from zip codes with latitude/longitude in database从数据库中具有纬度/经度的邮政编码进行地理搜索
【发布时间】:2013-11-21 00:19:26
【问题描述】:

我很难理解如何做到这一点,而且似乎有很多人问这个问题而没有答案。我有一个带有邮政编码的用户表。我创建了一个 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

【问题讨论】:

  • 我一开始就卡住了。所以我有索引文件设置。如您所见,它是从 User 表中提取的。我是否应该创建另一个名为 zip_index.rb 的 zips 表,该表包含美国所有带 lat/lon 的邮政编码?在用户模型中我需要做什么样的工作(或者我需要创建一个搜索模型)。有很多关于 Sphinx 的信息,我已经为应用程序的其他部分设置了它。只是没有关于地理搜索方面的信息。
  • 我认为你需要实现Haversive formula,除非 Thinking Sphinx 有一个功能可以为你做这件事。该公式计算球体上两点之间的距离。该函数只是一些基本的触发;它的 MySQL 版本是 (6378.10 * ACOS(COS(RADIANS(lat1)) * COS(RADIANS(lat2)) * COS(RADIANS(long1) - RADIANS(long2)) + SIN(RADIANS(lat1)) * SIN(RADIANS(lat2))))
  • 解决了吗?那是locations 上的User 模型?

标签: mysql ruby-on-rails thinking-sphinx zipcode


【解决方案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;

【讨论】:

  • 除非我需要外键,否则在创建关联时我会得到一个undefined method active_record'`。我添加的用户模型has_many :locations,虽然belongs_to 听起来更好,因为用户只有一个位置。我创建了一个 Location 模型并添加了has_many :users。我没有提到城市和州的自动取款机。用户将只能通过邮政编码进行搜索。我将使用城市/州作为用户个人资料的位置。如个人资料所示:来自内华达州拉斯维加斯的用户 A
  • 是的,我希望您需要用户表中邮政编码的外键。用户只属于一个位置,不是吗?所以我同意belongs_to 听起来最好。
  • 我让它在这里工作!诀窍是 a) 将位置模型中的一个属性添加到索引以确保表被连接,并且 b) 在计算纬度和经度属性时使用位置表名称。更新了答案。
  • 我在位置表上有一个 ID 列,这是我能想到的唯一区别。我已经根据活动记录定义使用 db:migrate 生成了表。
  • 您已经创建了两个独立的索引,因此搜索用户时不会考虑位置。用户索引应该讨论位置,对于这个查询,它是您唯一需要的。您希望将位置表连接到用户表。
【解决方案2】:

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

位置表应该是基于 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

【讨论】:

  • 谢谢。 @flup 提供了很多帮助,但我也需要您提供的这些信息!
【解决方案3】:

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

【讨论】:

  • 我不想使用 API,因为当我的数据库中已有信息时,我认为不需要 API。我将看看 Geocoder 看看它是否适合这个项目。虽然我更喜欢 TS,因为我已经将它用于我的所有搜索选项。感谢您的提示
  • geocoder 不关心您从哪里获取数据,而是处理一组输入:zip、地址、坐标以获得所需的输出。
  • 好的,我将应用地理编码并尝试从用户表和 zips 表中创建它之间的关系。
【解决方案4】:

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

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

我在之前的项目中使用过这个类,效果很好

您需要做的就是在逻辑中比较每个用户与您当前位置或所选用户的距离。 既然你已经掌握了一切的经纬度,这应该很简单。

希望这会有所帮助。

【讨论】:

  • Thinking Sphinx 会为你做这件事。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多