pepe-graticule 0.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
- data/CHANGELOG.txt +61 -0
- data/LICENSE.txt +30 -0
- data/Manifest.txt +84 -0
- data/README.txt +41 -0
- data/Rakefile +143 -0
- data/VERSION +1 -0
- data/bin/geocode +5 -0
- data/graticule.gemspec +146 -0
- data/init.rb +2 -0
- data/lib/graticule.rb +26 -0
- data/lib/graticule/cli.rb +64 -0
- data/lib/graticule/core_ext.rb +15 -0
- data/lib/graticule/distance.rb +18 -0
- data/lib/graticule/distance/haversine.rb +40 -0
- data/lib/graticule/distance/spherical.rb +52 -0
- data/lib/graticule/distance/vincenty.rb +76 -0
- data/lib/graticule/geocoder.rb +21 -0
- data/lib/graticule/geocoder/base.rb +112 -0
- data/lib/graticule/geocoder/bogus.rb +15 -0
- data/lib/graticule/geocoder/geocoder_ca.rb +54 -0
- data/lib/graticule/geocoder/geocoder_us.rb +51 -0
- data/lib/graticule/geocoder/google.rb +100 -0
- data/lib/graticule/geocoder/host_ip.rb +41 -0
- data/lib/graticule/geocoder/local_search_maps.rb +45 -0
- data/lib/graticule/geocoder/mapquest.rb +87 -0
- data/lib/graticule/geocoder/meta_carta.rb +33 -0
- data/lib/graticule/geocoder/multi.rb +46 -0
- data/lib/graticule/geocoder/multimap.rb +73 -0
- data/lib/graticule/geocoder/postcode_anywhere.rb +63 -0
- data/lib/graticule/geocoder/rest.rb +18 -0
- data/lib/graticule/geocoder/yahoo.rb +84 -0
- data/lib/graticule/location.rb +82 -0
- data/lib/graticule/version.rb +9 -0
- data/site/index.html +114 -0
- data/site/plugin.html +82 -0
- data/site/stylesheets/style.css +69 -0
- data/test/config.yml.default +36 -0
- data/test/fixtures/responses/geocoder_us/success.xml +18 -0
- data/test/fixtures/responses/geocoder_us/unknown.xml +1 -0
- data/test/fixtures/responses/google/badkey.xml +1 -0
- data/test/fixtures/responses/google/limit.xml +10 -0
- data/test/fixtures/responses/google/missing_address.xml +1 -0
- data/test/fixtures/responses/google/only_coordinates.xml +1 -0
- data/test/fixtures/responses/google/partial.xml +1 -0
- data/test/fixtures/responses/google/server_error.xml +10 -0
- data/test/fixtures/responses/google/success.xml +1 -0
- data/test/fixtures/responses/google/success_multiple_results.xml +88 -0
- data/test/fixtures/responses/google/unavailable.xml +1 -0
- data/test/fixtures/responses/google/unknown_address.xml +1 -0
- data/test/fixtures/responses/host_ip/private.txt +4 -0
- data/test/fixtures/responses/host_ip/success.txt +4 -0
- data/test/fixtures/responses/host_ip/unknown.txt +4 -0
- data/test/fixtures/responses/local_search_maps/empty.txt +1 -0
- data/test/fixtures/responses/local_search_maps/not_found.txt +1 -0
- data/test/fixtures/responses/local_search_maps/success.txt +1 -0
- data/test/fixtures/responses/mapquest/multi_result.xml +1 -0
- data/test/fixtures/responses/mapquest/success.xml +1 -0
- data/test/fixtures/responses/meta_carta/bad_address.xml +17 -0
- data/test/fixtures/responses/meta_carta/multiple.xml +33 -0
- data/test/fixtures/responses/meta_carta/success.xml +31 -0
- data/test/fixtures/responses/multimap/missing_params.xml +4 -0
- data/test/fixtures/responses/multimap/no_matches.xml +4 -0
- data/test/fixtures/responses/multimap/success.xml +19 -0
- data/test/fixtures/responses/postcode_anywhere/badkey.xml +9 -0
- data/test/fixtures/responses/postcode_anywhere/canada.xml +16 -0
- data/test/fixtures/responses/postcode_anywhere/empty.xml +16 -0
- data/test/fixtures/responses/postcode_anywhere/success.xml +16 -0
- data/test/fixtures/responses/postcode_anywhere/uk.xml +18 -0
- data/test/fixtures/responses/yahoo/success.xml +3 -0
- data/test/fixtures/responses/yahoo/unknown_address.xml +6 -0
- data/test/mocks/uri.rb +52 -0
- data/test/test_helper.rb +31 -0
- data/test/unit/graticule/distance_test.rb +58 -0
- data/test/unit/graticule/geocoder/geocoder_us_test.rb +43 -0
- data/test/unit/graticule/geocoder/geocoders.rb +56 -0
- data/test/unit/graticule/geocoder/google_test.rb +112 -0
- data/test/unit/graticule/geocoder/host_ip_test.rb +40 -0
- data/test/unit/graticule/geocoder/local_search_maps_test.rb +30 -0
- data/test/unit/graticule/geocoder/mapquest_test.rb +47 -0
- data/test/unit/graticule/geocoder/meta_carta_test.rb +44 -0
- data/test/unit/graticule/geocoder/multi_test.rb +43 -0
- data/test/unit/graticule/geocoder/multimap_test.rb +52 -0
- data/test/unit/graticule/geocoder/postcode_anywhere_test.rb +50 -0
- data/test/unit/graticule/geocoder/yahoo_test.rb +48 -0
- data/test/unit/graticule/geocoder_test.rb +27 -0
- data/test/unit/graticule/location_test.rb +79 -0
- metadata +167 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
module Graticule #:nodoc:
|
|
2
|
+
module Geocoder #:nodoc:
|
|
3
|
+
|
|
4
|
+
# Multimap geocoding API
|
|
5
|
+
|
|
6
|
+
class Multimap < Rest
|
|
7
|
+
|
|
8
|
+
# This precision information is not complete.
|
|
9
|
+
# More details should be implemented from:
|
|
10
|
+
# http://www.multimap.com/share/documentation/clientzone/gqcodes.htm
|
|
11
|
+
|
|
12
|
+
PRECISION = {
|
|
13
|
+
"6"=> :country,
|
|
14
|
+
"5" => :state,
|
|
15
|
+
"4" => :postal_code,
|
|
16
|
+
"3" => :city,
|
|
17
|
+
"2" => :street,
|
|
18
|
+
"1" => :address
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
# Web services initializer.
|
|
22
|
+
#
|
|
23
|
+
# The +api_key+ is the Open API key that uniquely identifies your
|
|
24
|
+
# application.
|
|
25
|
+
#
|
|
26
|
+
# See http://www.multimap.com/openapi/
|
|
27
|
+
|
|
28
|
+
def initialize(api_key)
|
|
29
|
+
@api_key = api_key
|
|
30
|
+
@url = URI.parse "http://clients.multimap.com/API/geocode/1.2/#{@api_key}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Returns a location for an address in the form of a String, Hash or Location.
|
|
34
|
+
|
|
35
|
+
def locate(address)
|
|
36
|
+
location = address.is_a?(String) ? address : location_from_params(address)
|
|
37
|
+
case location
|
|
38
|
+
when String
|
|
39
|
+
get :qs => location
|
|
40
|
+
when Location
|
|
41
|
+
get "street" => location.street,
|
|
42
|
+
"region" => location.region,
|
|
43
|
+
"city" => location.city,
|
|
44
|
+
"postalCode" => location.postal_code,
|
|
45
|
+
"countryCode" => location.country
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def parse_response(xml)
|
|
50
|
+
r = xml.elements['Results/Location[1]']
|
|
51
|
+
returning Location.new do |location|
|
|
52
|
+
|
|
53
|
+
location.precision = PRECISION[r.attributes['geocodeQuality']] || :unknown
|
|
54
|
+
|
|
55
|
+
location.street = r.elements['Address/Street'].text.titleize unless r.elements['Address/Street'].nil?
|
|
56
|
+
location.locality = r.elements['Address/Areas/Area'].text.titleize unless r.elements['Address/Areas/Area'].nil?
|
|
57
|
+
location.region = r.elements['Address/State'].text.titleize unless r.elements['Address/State'].nil?
|
|
58
|
+
location.postal_code = r.elements['Address/PostalCode'].text unless r.elements['Address/PostalCode'].nil?
|
|
59
|
+
location.country = r.elements['Address/CountryCode'].text
|
|
60
|
+
|
|
61
|
+
location.latitude = r.elements['Point/Lat'].text.to_f
|
|
62
|
+
location.longitude = r.elements['Point/Lon'].text.to_f
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def check_error(xml)
|
|
67
|
+
error = xml.elements['Results'].attributes['errorCode']
|
|
68
|
+
raise Error, error unless error.nil?
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module Graticule #:nodoc:
|
|
2
|
+
module Geocoder #:nodoc:
|
|
3
|
+
|
|
4
|
+
class PostcodeAnywhere < Rest
|
|
5
|
+
|
|
6
|
+
# http://www.postcodeanywhere.com/register/
|
|
7
|
+
def initialize(account_code, license_code)
|
|
8
|
+
@url = URI.parse 'http://services.postcodeanywhere.co.uk/xml.aspx'
|
|
9
|
+
@account_code = account_code
|
|
10
|
+
@license_code = license_code
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def locate(params)
|
|
14
|
+
location = location_from_params(params)
|
|
15
|
+
get :address => location.to_s(:country => false), :country => location.country
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def make_url(params) #:nodoc:
|
|
21
|
+
params[:account_code] = @account_code
|
|
22
|
+
params[:license_code] = @license_code
|
|
23
|
+
params[:action] = 'geocode'
|
|
24
|
+
super params
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def parse_response(xml) #:nodoc:
|
|
28
|
+
result = xml.elements['/PostcodeAnywhere/Data/Item[1]']
|
|
29
|
+
returning Location.new do |location|
|
|
30
|
+
location.latitude = result.attribute('latitude').value.to_f
|
|
31
|
+
location.longitude = result.attribute('longitude').value.to_f
|
|
32
|
+
location.street = value(result.attribute('line1'))
|
|
33
|
+
location.locality = value(result.attribute('city'))
|
|
34
|
+
location.region = value(result.attribute('state'))
|
|
35
|
+
location.postal_code = value(result.attribute('postal_code'))
|
|
36
|
+
location.country = value(result.attribute('country'))
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def value(attribute)
|
|
41
|
+
attribute.value if attribute
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# http://www.postcodeanywhere.co.uk/developers/documentation/errors.aspx
|
|
45
|
+
def check_error(xml) #:nodoc:
|
|
46
|
+
#raise AddressError, xml.text if xml.text == 'couldn\'t find this address! sorry'
|
|
47
|
+
if error = xml.elements['/PostcodeAnywhere/Data/Item[@error_number][1]']
|
|
48
|
+
error_number = error.attribute('error_number').value.to_i
|
|
49
|
+
message = error.attribute('message').value
|
|
50
|
+
if (1..11).include?(error_number) || (34..38).include?(error_number)
|
|
51
|
+
raise CredentialsError, message
|
|
52
|
+
else
|
|
53
|
+
raise Error, message
|
|
54
|
+
end
|
|
55
|
+
elsif xml.elements['/PostcodeAnywhere/Data'].elements.empty?
|
|
56
|
+
raise AddressError, 'No results returned'
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require 'rexml/document'
|
|
2
|
+
|
|
3
|
+
module Graticule #:nodoc:
|
|
4
|
+
module Geocoder #:nodoc:
|
|
5
|
+
|
|
6
|
+
# Abstract class for implementing REST geocoders. Passes on a REXML::Document
|
|
7
|
+
# to +check_errors+ and +parse_response+.
|
|
8
|
+
class Rest < Base
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def prepare_response(response)
|
|
13
|
+
REXML::Document.new(response)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
module Graticule #:nodoc:
|
|
2
|
+
module Geocoder #:nodoc:
|
|
3
|
+
|
|
4
|
+
# Yahoo geocoding API.
|
|
5
|
+
#
|
|
6
|
+
# http://developer.yahoo.com/maps/rest/V1/geocode.html
|
|
7
|
+
class Yahoo < Rest
|
|
8
|
+
|
|
9
|
+
PRECISION = {
|
|
10
|
+
"country"=> :country,
|
|
11
|
+
"state" => :state,
|
|
12
|
+
"city" => :city,
|
|
13
|
+
"zip+4" => :zip,
|
|
14
|
+
"zip+2" => :zip,
|
|
15
|
+
"zip" => :zip,
|
|
16
|
+
"street" => :street,
|
|
17
|
+
"address" => :address
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
# Web services initializer.
|
|
21
|
+
#
|
|
22
|
+
# The +appid+ is the Application ID that uniquely identifies your
|
|
23
|
+
# application. See: http://developer.yahoo.com/faq/index.html#appid
|
|
24
|
+
#
|
|
25
|
+
# See http://developer.yahoo.com/search/rest.html
|
|
26
|
+
def initialize(appid)
|
|
27
|
+
@appid = appid
|
|
28
|
+
@url = URI.parse "http://api.local.yahoo.com/MapsService/V1/geocode"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Returns a Location for +address+.
|
|
32
|
+
#
|
|
33
|
+
# The +address+ can be any of:
|
|
34
|
+
# * city, state
|
|
35
|
+
# * city, state, zip
|
|
36
|
+
# * zip
|
|
37
|
+
# * street, city, state
|
|
38
|
+
# * street, city, state, zip
|
|
39
|
+
# * street, zip
|
|
40
|
+
def locate(address)
|
|
41
|
+
location = (address.is_a?(String) ? address : location_from_params(address).to_s(:country => false))
|
|
42
|
+
# yahoo pukes on line breaks
|
|
43
|
+
get :location => location.gsub("\n", ', ')
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def parse_response(xml) # :nodoc:
|
|
47
|
+
r = xml.elements['ResultSet/Result[1]']
|
|
48
|
+
returning Location.new do |location|
|
|
49
|
+
location.precision = PRECISION[r.attributes['precision']] || :unknown
|
|
50
|
+
|
|
51
|
+
if r.attributes.include? 'warning' then
|
|
52
|
+
location.warning = r.attributes['warning']
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
location.latitude = r.elements['Latitude'].text.to_f
|
|
56
|
+
location.longitude = r.elements['Longitude'].text.to_f
|
|
57
|
+
|
|
58
|
+
location.street = r.elements['Address'].text.titleize unless r.elements['Address'].text.blank?
|
|
59
|
+
location.locality = r.elements['City'].text.titleize unless r.elements['City'].text.blank?
|
|
60
|
+
location.region = r.elements['State'].text
|
|
61
|
+
location.postal_code = r.elements['Zip'].text
|
|
62
|
+
location.country = r.elements['Country'].text
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Extracts and raises an error from +xml+, if any.
|
|
67
|
+
def check_error(xml) #:nodoc:
|
|
68
|
+
err = xml.elements['Error']
|
|
69
|
+
raise Error, err.elements['Message'].text if err
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Creates a URL from the Hash +params+. Automatically adds the appid and
|
|
73
|
+
# sets the output type to 'xml'.
|
|
74
|
+
def make_url(params) #:nodoc:
|
|
75
|
+
params[:appid] = @appid
|
|
76
|
+
params[:output] = 'xml'
|
|
77
|
+
|
|
78
|
+
super params
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
module Graticule
|
|
2
|
+
|
|
3
|
+
# A geographic location
|
|
4
|
+
class Location
|
|
5
|
+
attr_accessor :latitude, :longitude, :street, :locality, :region, :postal_code, :country, :precision, :warning
|
|
6
|
+
alias_method :city, :locality
|
|
7
|
+
alias_method :state, :region
|
|
8
|
+
alias_method :zip, :postal_code
|
|
9
|
+
|
|
10
|
+
# if latitude is string both latitude and longitude are converted to float
|
|
11
|
+
def initialize(attrs = {})
|
|
12
|
+
attrs = parse_strings(attrs) if attrs[:latitude].is_a? String
|
|
13
|
+
attrs.each do |key,value|
|
|
14
|
+
instance_variable_set "@#{key}", value
|
|
15
|
+
end
|
|
16
|
+
self.precision ||= :unknown
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def attributes
|
|
20
|
+
[:latitude, :longitude, :street, :locality, :region, :postal_code, :country, :precision].inject({}) do |result,attr|
|
|
21
|
+
result[attr] = self.send(attr) unless self.send(attr).blank?
|
|
22
|
+
result
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def blank?
|
|
27
|
+
attributes.except(:precision).empty?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Returns an Array with latitude and longitude.
|
|
31
|
+
def coordinates
|
|
32
|
+
[latitude, longitude]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def ==(other)
|
|
36
|
+
other.respond_to?(:attributes) ? attributes == other.attributes : false
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Calculate the distance to another location. See the various Distance formulas
|
|
40
|
+
# for more information
|
|
41
|
+
def distance_to(destination, options = {})
|
|
42
|
+
options = {:formula => :haversine, :units => :miles}.merge(options)
|
|
43
|
+
"Graticule::Distance::#{options[:formula].to_s.titleize}".constantize.distance(self, destination, options[:units])
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Where would I be if I dug through the center of the earth?
|
|
47
|
+
def antipode
|
|
48
|
+
Location.new :latitude => -latitude, :longitude => longitude + (longitude >= 0 ? -180 : 180)
|
|
49
|
+
end
|
|
50
|
+
alias_method :antipodal_location, :antipode
|
|
51
|
+
|
|
52
|
+
def to_s(options = {})
|
|
53
|
+
options = {:coordinates => false, :country => true}.merge(options)
|
|
54
|
+
result = ""
|
|
55
|
+
result << "#{street}\n" if street
|
|
56
|
+
result << [locality, [region, postal_code].compact.join(" ")].compact.join(", ")
|
|
57
|
+
result << " #{country}" if options[:country] && country
|
|
58
|
+
result << "\nlatitude: #{latitude}, longitude: #{longitude}" if options[:coordinates] && [latitude, longitude].any?
|
|
59
|
+
result
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
# parses string latitude to float
|
|
64
|
+
def parse_strings(attrs)
|
|
65
|
+
[:latitude, :longitude].each do |sym|
|
|
66
|
+
attrs[sym] = degree_to_float(attrs[sym])
|
|
67
|
+
end
|
|
68
|
+
return attrs
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# parses string in degrees, minutes and seconds to int
|
|
72
|
+
def degree_to_float(str)
|
|
73
|
+
data = str.match(/(\d{1,2})°(\d{1,2})'(\d{1,2}.\d{1,3})"([N|S|E|W])/)
|
|
74
|
+
raise 'Bad format' unless data
|
|
75
|
+
res = data[1].to_i + (data[2].to_f * 1/60.0) + (data[3].to_f * 1/3600)
|
|
76
|
+
res = -res if data[4].match(/S|W/)
|
|
77
|
+
res = ((res * 1e5).round)/1e5 # round it to 5 places
|
|
78
|
+
return res
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
end
|
|
82
|
+
end
|
data/site/index.html
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
|
2
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
|
3
|
+
|
|
4
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
|
5
|
+
<head>
|
|
6
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
|
7
|
+
<title>Graticule</title>
|
|
8
|
+
<link rel="stylesheet" type="text/css" href="http://opensoul.org/stylesheets/code.css" />
|
|
9
|
+
<link rel="stylesheet" type="text/css" href="stylesheets/style.css" />
|
|
10
|
+
<link href="http://opensoul.org/stylesheets/ci.css" rel="stylesheet" type="text/css" />
|
|
11
|
+
<script src="http://opensoul.org/javascripts/code_highlighter.js" type="text/javascript"></script>
|
|
12
|
+
<script src="http://opensoul.org/javascripts/ruby.js" type="text/javascript"></script>
|
|
13
|
+
</head>
|
|
14
|
+
|
|
15
|
+
<body>
|
|
16
|
+
<div id="collectiveidea">
|
|
17
|
+
<a href="http://collectiveidea.com"><img src="http://opensoul.org/images/header_logo.gif" alt="Collective Idea" class="logo" width="17" height="22" /></a>
|
|
18
|
+
<ul class="links">
|
|
19
|
+
<li><a href="http://daniel.collectiveidea.com/blog">Daniel</a></li>
|
|
20
|
+
<li><a href="http://opensoul.org">Brandon</a></li>
|
|
21
|
+
<li class="name"><a href="http://collectiveidea.com"><img src="http://opensoul.org/images/header_collectiveidea.gif" alt="Collective Idea" width="123" height="21" /></a></li>
|
|
22
|
+
</ul>
|
|
23
|
+
</div>
|
|
24
|
+
<div id="main">
|
|
25
|
+
<div id="header">
|
|
26
|
+
<h1><a href="/">Graticule</a></h1>
|
|
27
|
+
<ul id="nav">
|
|
28
|
+
<li><a href="graticule">API Docs</a></li>
|
|
29
|
+
<li><a href="plugin.html">Rails Plugin</a></li>
|
|
30
|
+
<li><a href="http://rubyforge.org/projects/graticule">RubyForge</a></li>
|
|
31
|
+
<li><a href="http://opensoul.org/tags/geocoding">Blog</a></li>
|
|
32
|
+
</ul>
|
|
33
|
+
</div>
|
|
34
|
+
<div id="content">
|
|
35
|
+
<dl lang="en" xml:lang="en">
|
|
36
|
+
<dt><strong>grat·i·cule</strong>, <span class="pronunciation">|ˈgratəˌkyoōl|</span>,
|
|
37
|
+
<em><abbr title="noun">n.</abbr></em>
|
|
38
|
+
<span class="description">technical</span>
|
|
39
|
+
</dt>
|
|
40
|
+
<dd><em>Navigation.</em> a network of parallels and meridians on a map or chart.</dd>
|
|
41
|
+
</dl>
|
|
42
|
+
|
|
43
|
+
<p>Graticule is a geocoding API for looking up address coordinates and performing distance calculations. It supports many popular APIs, including:</p>
|
|
44
|
+
|
|
45
|
+
<ul>
|
|
46
|
+
<li>Yahoo</li>
|
|
47
|
+
<li>Google</li>
|
|
48
|
+
<li>Geocoder.ca</li>
|
|
49
|
+
<li>Geocoder.us</li>
|
|
50
|
+
<li>PostcodeAnywhere</li>
|
|
51
|
+
<li>MetaCarta</li>
|
|
52
|
+
</ul>
|
|
53
|
+
|
|
54
|
+
<p>There is a <a href="plugin.html">companion Rails plugin</a> that makes geocoding seem like magic.</p>
|
|
55
|
+
|
|
56
|
+
<h2>Example</h2>
|
|
57
|
+
|
|
58
|
+
<pre><code class="ruby">require 'rubygems'
|
|
59
|
+
require 'graticule'
|
|
60
|
+
geocoder = Graticule.service(:google).new "api_key"
|
|
61
|
+
location = geocoder.locate "1600 Amphitheatre Parkway, Mountain View, CA"
|
|
62
|
+
location.coordinates #=> [37.423021, -122.083739]
|
|
63
|
+
location.country #=> "US"</code></pre>
|
|
64
|
+
|
|
65
|
+
<p>See the <a href="graticule">API documentation</a> for more details.</p>
|
|
66
|
+
|
|
67
|
+
<h2>International Support</h2>
|
|
68
|
+
|
|
69
|
+
<p>Graticule supports several services with international support. The international geocoders require slightly more structured data than the US ones:</p>
|
|
70
|
+
|
|
71
|
+
<pre><code class="ruby">g = Graticule.service(:local_search_maps).new
|
|
72
|
+
location = g.locate :street => '48 Leicester Square', :locality => 'London', :country => 'UK'
|
|
73
|
+
location.coordinates #=> [51.510036, -0.130427]</code></pre>
|
|
74
|
+
|
|
75
|
+
<h2>Distance Calculation</h2>
|
|
76
|
+
|
|
77
|
+
<p>Graticule includes 3 different distance formulas, Spherical (simplest but least accurate), Vincenty (most accurate and most complicated), and Haversine (somewhere inbetween).</p>
|
|
78
|
+
|
|
79
|
+
<pre><code class="ruby">location = geocoder.locate("Holland, MI")
|
|
80
|
+
location.distance_to(geocoder.locate("Chicago, IL"))
|
|
81
|
+
#=> 101.997458788177</code></pre>
|
|
82
|
+
|
|
83
|
+
<h2>Command Line</h2>
|
|
84
|
+
<p>Graticule also includes a command line interface to the various geocoders:</p>
|
|
85
|
+
|
|
86
|
+
<pre>
|
|
87
|
+
$ geocode -s yahoo -a yahookey Washington, DC
|
|
88
|
+
Washington, DC US
|
|
89
|
+
latitude: 38.895222, longitude: -77.036758</pre>
|
|
90
|
+
|
|
91
|
+
<h2>Installation</h2>
|
|
92
|
+
|
|
93
|
+
<p>Install the gem by executing:</p>
|
|
94
|
+
|
|
95
|
+
<pre>gem install graticule</pre>
|
|
96
|
+
|
|
97
|
+
<p>Or, download it from <a href="http://rubyforge.org/frs/?group_id=2643">RubyForge</a>.</p>
|
|
98
|
+
|
|
99
|
+
<h2>Contributing</h2>
|
|
100
|
+
|
|
101
|
+
<p>Contributions are welcome and appreciated! Join the <a href="http://rubyforge.org/mailman/listinfo/graticule-users">mailing list</a> and grab the source from:</p>
|
|
102
|
+
|
|
103
|
+
<pre>http://source.collectiveidea.com/public/geocode/trunk/</pre>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
|
|
108
|
+
</script>
|
|
109
|
+
<script type="text/javascript">
|
|
110
|
+
_uacct = "UA-194397-7";
|
|
111
|
+
urchinTracker();
|
|
112
|
+
</script>
|
|
113
|
+
</body>
|
|
114
|
+
</html>
|