Skip to content

Instantly share code, notes, and snippets.

@dpneumo
Last active January 13, 2021 02:45
Show Gist options
  • Save dpneumo/faf5883486cbb2e5994eb71306542623 to your computer and use it in GitHub Desktop.
Save dpneumo/faf5883486cbb2e5994eb71306542623 to your computer and use it in GitHub Desktop.
Geocode an address hash with Google geocode api
I needed a tool to add latitude and longitude to seeds.rb in a Rails application.
geocoder.rb uses the Google geocode api. An apikey is required. For limited use it is free. The apikey should be protected from prying eyes. (encryption, .gitignore, ...)
Geocoder#geocode takes an address string in canonical form ( "#{number} #{street} #{city} #{state}" ) as input. It returns latitude and longitude as a hash: { lat: 30.0000, lng: -90.000 }
seed_geocoder.rb extracts the address string from seeds.rb, requests the geocode hash from the Geocoder obj and writes the newseeds.rb file.
The newseeds file does need some minor additional work to make it a functional seeds.rb.
class CanonicalAddress
def initialize( city: 'Arlington', state: 'TX')
@city = city
@state = state
end
def strng(addr_hash)
number = addr_hash.fetch('number')
street = addr_hash.fetch('street')
city = addr_hash.fetch('city', @city)
state = addr_hash.fetch('state', @state)
"#{number} #{street} #{city} #{state}"
end
def hsh(addr_hash)
addr_hash.merge({city: @city, state: state})
end
end
require 'json'
require 'httparty'
class Geocoder
include HTTParty
URL_GC = "https://maps.googleapis.com/maps/api/geocode/"
attr_reader :url, :fmt, :keyfile
def initialize( url: URL_GC, fmt: 'json', keyfile: 'apikey.txt')
@url = url
@fmt = fmt
@keyfile = keyfile
end
def geocode(address)
request = req(address)
response = HTTParty.get(request)
extract_coordinates(response)
end
private
def req(address)
URL_GC+fmt+"?address=#{ address }&key= "#{ apikey }"
end
def extract_coordinates(response)
JSON.parse(response.body)["results"].first["geometry"]["location"]
end
def apikey
File.open(keyfile) {|f| key = f.read }
end
end
[
{"number"=>100, "street"=>"Howler Dr", "lat"=>31.0000000, "lng"=>-97.0000000},
{"number"=>202, "street"=>"Johnson Ave", city: "Fort Worth", "lat"=>32.0000000, "lng"=>-97.0000000},
{"number"=>303, "street"=>"Main St", city: "Dallas", state: "TX", "lat"=>32.0000000, "lng"=>-97.0000000},
]
require_relative 'seed_geocoder'
sg = SeedGeocoder.new( app: '../wwna_roster',
matchr: %r{number: 6004} )
sg.geocode_seeds
require 'yaml'
require_relative 'geocoder'
require_relative 'canonical_address'
class SeedGeocoder
attr_reader :c_addr, :geocoder
def initialize( app: '../my_app',
src: 'db/seeds.rb',
dest: 'db/newseeds.rb',
matchr: %r{number:},
geocoder: Geocoder )
@app = app
@src = src
@dest = dest
@c_addr = CanonicalAddress.new
@geocoder = geocoder.new
end
def geocode_seeds
write(geocoded_seeds)
end
private
def write(seeds)
return unless seeds
File.open(dest, 'w') do |f|
f.puts "[\n"
seeds.each do |seed|
f.puts " #{seed.to_s},\n"
end
f.puts "]\n"
end
end
def geocoded_seeds
File.foreach(src).map do |line|
next unless matchr =~ line
addr = YAML.load(line)
addr.merge geocoder.geocode(canonical addr)
end.compact
end
def canonical(addr_hash)
c_addr.strng(addr_hash)
end
def src
@app + '/' + @src
end
def dest
@app + '/' + @dest
end
end
# This file should contain all the record creation needed to seed the database with its default values.
# The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup).
#
# Examples:
#
# movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }])
# Character.create(name: 'Luke', movie: movies.first)
House.create([
{ number: 100, street: 'Howler Dr' },
{ number: 202, street: 'Johnson Ave', city: 'Fort Worth' },
{ number: 303, street: 'Main St', city: 'Dallas', state: 'TX' }
])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment