Skip to content

Instantly share code, notes, and snippets.

@JoshCheek
Created March 20, 2010 06:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JoshCheek/338523 to your computer and use it in GitHub Desktop.
Save JoshCheek/338523 to your computer and use it in GitHub Desktop.
# an ActiveRecord based solution to http://www.ruby-forum.com/topic/206201
# based on Peter Song's solution
# showing the use with rails-geocoder for this code http://gist.github.com/338506
# if anyone is interested in making a DataMapper version of this
# I'd be interested to see how it relates to ActiveRecord :)
require 'open-uri'
require 'net/http'
require 'rubygems'
require 'hpricot'
require 'active_record'
require 'geocoder'
Geocoder::GOOGLE_MAPS_API_KEY = 'your key goes here'
# I'm sure there is a nicer way to do this
# but IDK how to tell it to do nothing if the table already exists
# and file based migrations seem like overkill here, so I do it w/ sql
ActiveRecord::Base.instance_eval do
establish_connection :adapter => 'sqlite3' , :database => 'cities.sqlite3'
connection.execute <<-ADD_CITIES
create table if not exists cities (
id INTEGER PRIMARY KEY ,
name TEXT ,
state TEXT ,
country TEXT ,
latitude NUMERIC ,
longitude NUMERIC
);
ADD_CITIES
end
# this class maps our city to the DB
# see the section called "Models" at http://guides.rubyonrails.org/
class City < ActiveRecord::Base
# validate the record
validates_presence_of :name , :state , :country
validates_numericality_of :latitude , :longitude , :allow_nil => false
before_validation :fetch_coordinates
geocoded_by :address
def address
"#{name},#{state},#{country}"
end
def lat_lon
[ latitude , longitude ]
end
# hack their method a little so that I can just pass a city in if I like
def distance_to(*params)
return super unless params.size > 0 && params.first.kind_of?(City)
other_city = params.shift
params.unshift other_city.longitude
params.unshift other_city.latitude
super
end
end
# there is a bunch of data at the end of the file that I just got w/ a quick google search for cities
# It saves all but a few of them, because their top query result is their Google Map location which is predictably formatted, and locatable
# this function queries google for the information, and then parses the result to find the city and state
# it then saves that record into the db, and outputs whether it succeeded or not
def populate
DATA.each do |city_details|
begin
address = URI.escape "http://www.google.com/search?q=#{city_details}" # escape the query so things like spaces don't mess it up
result = Hpricot(open address) # open the page and parse it
name , state = ( result % "#res div ol li.g h3.r a.l b" ).innerHTML.split(', ') # find the element on the page that has the name and state
city = City.new :name => name , :state => state , :country => 'US'
if city.save
puts "Successfully created #{"%20s" % city.name} , #{city.state} , #{city.country} , (#{"%8.2f" % city.latitude},#{"%8.2f" % city.longitude})"
else
puts "#{city.inspect} failed to save from #{city_details}"
end
rescue
puts "Could not generate a city from #{city_details}"
end
end
end
# call the above function
populate
# show some interesting ways to use it
def show_use_cases
# distance between two cities (I had to override their method to get it to do this)
dallas = City.find_by_name 'Dallas'
austin = City.find_by_name 'Austin'
puts "It is #{dallas.distance_to austin} miles from Austin to Dallas (as the crow flies)."
texas_cities = City.find_all_by_state('TX').map { |city| city.lat_lon }
puts "The geographic center of the cities in Texas (that are in our db) is #{Geocoder.geographic_center texas_cities}"
cities_near_sacramento = City.find_by_name('Sacramento').nearbys(100).map(&:name).join(', ')
puts 'Cities within 100 miles of Sacramento: ' + cities_near_sacramento
puts 'All cities in California: ' + City.find_all_by_state('CA').map { |city| city.name }.join(', ')
end
show_use_cases
# the cities copied from http://www.infoplease.com/ipa/A0108477.html
__END__
Albuquerque, N.M.
Arlington, Texas
Atlanta, Ga.
Austin, Tex.
Baltimore, Md.
Boston, Mass.
Charlotte, N.C.
Chicago, Ill.
Cleveland, Ohio
Colorado Springs, Colo.
Columbus, Ohio
Dallas, Tex.
Denver, Colo.
Detroit, Mich.
El Paso, Tex.
Fort Worth, Tex.
Fresno, Calif.
Honolulu, Hawaii
Houston, Tex.
Indianapolis, Ind.
Jacksonville, Fla.
Kansas City, Mo.
Las Vegas, Nev.
Long Beach, Calif.
Los Angeles, Calif.
Louisville/Jefferson County, Ky.
Memphis, Tenn.
Mesa, Ariz.
Miami, Fla.
Milwaukee, Wis.
Minneapolis, Minn.
Nashville-Davidson, Tenn.
New Orleans, La.
New York, N.Y.
Oakland, Calif.
Oklahoma City, Okla.
Omaha, Nebr.
Philadelphia, Pa.
Phoenix, Ariz.
Portland, Ore.
Sacramento, Calif.
St. Louis, Mo.
San Antonio, Tex.
San Diego, Calif.
San Francisco, Calif.
San Jose, Calif.
Seattle, Wash.
Tucson, Ariz.
Tulsa, Okla.
Virginia Beach, Va.
Washington, DC
Wichita, Kans.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment