aub-graticule 0.2.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. data/.gitignore +4 -0
  2. data/CHANGELOG.txt +61 -0
  3. data/LICENSE.txt +30 -0
  4. data/Manifest.txt +84 -0
  5. data/README.txt +41 -0
  6. data/Rakefile +143 -0
  7. data/VERSION +1 -0
  8. data/bin/geocode +5 -0
  9. data/graticule.gemspec +143 -0
  10. data/init.rb +2 -0
  11. data/lib/graticule/cli.rb +64 -0
  12. data/lib/graticule/core_ext.rb +15 -0
  13. data/lib/graticule/distance/haversine.rb +40 -0
  14. data/lib/graticule/distance/spherical.rb +52 -0
  15. data/lib/graticule/distance/vincenty.rb +76 -0
  16. data/lib/graticule/distance.rb +18 -0
  17. data/lib/graticule/geocoder/base.rb +116 -0
  18. data/lib/graticule/geocoder/bogus.rb +15 -0
  19. data/lib/graticule/geocoder/geocoder_ca.rb +54 -0
  20. data/lib/graticule/geocoder/geocoder_us.rb +51 -0
  21. data/lib/graticule/geocoder/google.rb +100 -0
  22. data/lib/graticule/geocoder/host_ip.rb +41 -0
  23. data/lib/graticule/geocoder/local_search_maps.rb +44 -0
  24. data/lib/graticule/geocoder/mapquest.rb +96 -0
  25. data/lib/graticule/geocoder/meta_carta.rb +32 -0
  26. data/lib/graticule/geocoder/multi.rb +46 -0
  27. data/lib/graticule/geocoder/multimap.rb +73 -0
  28. data/lib/graticule/geocoder/postcode_anywhere.rb +63 -0
  29. data/lib/graticule/geocoder/rest.rb +18 -0
  30. data/lib/graticule/geocoder/yahoo.rb +84 -0
  31. data/lib/graticule/geocoder.rb +21 -0
  32. data/lib/graticule/location.rb +61 -0
  33. data/lib/graticule/version.rb +9 -0
  34. data/lib/graticule.rb +26 -0
  35. data/site/index.html +114 -0
  36. data/site/plugin.html +82 -0
  37. data/site/stylesheets/style.css +69 -0
  38. data/test/config.yml.default +36 -0
  39. data/test/fixtures/responses/geocoder_us/success.xml +18 -0
  40. data/test/fixtures/responses/geocoder_us/unknown.xml +1 -0
  41. data/test/fixtures/responses/google/badkey.xml +1 -0
  42. data/test/fixtures/responses/google/limit.xml +10 -0
  43. data/test/fixtures/responses/google/missing_address.xml +1 -0
  44. data/test/fixtures/responses/google/only_coordinates.xml +1 -0
  45. data/test/fixtures/responses/google/partial.xml +1 -0
  46. data/test/fixtures/responses/google/server_error.xml +10 -0
  47. data/test/fixtures/responses/google/success.xml +1 -0
  48. data/test/fixtures/responses/google/success_multiple_results.xml +88 -0
  49. data/test/fixtures/responses/google/unavailable.xml +1 -0
  50. data/test/fixtures/responses/google/unknown_address.xml +1 -0
  51. data/test/fixtures/responses/host_ip/private.txt +4 -0
  52. data/test/fixtures/responses/host_ip/success.txt +4 -0
  53. data/test/fixtures/responses/host_ip/unknown.txt +4 -0
  54. data/test/fixtures/responses/local_search_maps/empty.txt +1 -0
  55. data/test/fixtures/responses/local_search_maps/not_found.txt +1 -0
  56. data/test/fixtures/responses/local_search_maps/success.txt +1 -0
  57. data/test/fixtures/responses/mapquest/multi_result.xml +1 -0
  58. data/test/fixtures/responses/mapquest/success.xml +1 -0
  59. data/test/fixtures/responses/meta_carta/bad_address.xml +17 -0
  60. data/test/fixtures/responses/meta_carta/multiple.xml +33 -0
  61. data/test/fixtures/responses/meta_carta/success.xml +31 -0
  62. data/test/fixtures/responses/multimap/missing_params.xml +4 -0
  63. data/test/fixtures/responses/multimap/no_matches.xml +4 -0
  64. data/test/fixtures/responses/multimap/success.xml +19 -0
  65. data/test/fixtures/responses/postcode_anywhere/badkey.xml +9 -0
  66. data/test/fixtures/responses/postcode_anywhere/canada.xml +16 -0
  67. data/test/fixtures/responses/postcode_anywhere/empty.xml +16 -0
  68. data/test/fixtures/responses/postcode_anywhere/success.xml +16 -0
  69. data/test/fixtures/responses/postcode_anywhere/uk.xml +18 -0
  70. data/test/fixtures/responses/yahoo/success.xml +3 -0
  71. data/test/fixtures/responses/yahoo/unknown_address.xml +6 -0
  72. data/test/mocks/uri.rb +52 -0
  73. data/test/test_helper.rb +31 -0
  74. data/test/unit/graticule/distance_test.rb +58 -0
  75. data/test/unit/graticule/geocoder/geocoder_us_test.rb +43 -0
  76. data/test/unit/graticule/geocoder/geocoders.rb +56 -0
  77. data/test/unit/graticule/geocoder/google_test.rb +112 -0
  78. data/test/unit/graticule/geocoder/host_ip_test.rb +40 -0
  79. data/test/unit/graticule/geocoder/local_search_maps_test.rb +30 -0
  80. data/test/unit/graticule/geocoder/mapquest_test.rb +61 -0
  81. data/test/unit/graticule/geocoder/meta_carta_test.rb +44 -0
  82. data/test/unit/graticule/geocoder/multi_test.rb +43 -0
  83. data/test/unit/graticule/geocoder/multimap_test.rb +52 -0
  84. data/test/unit/graticule/geocoder/postcode_anywhere_test.rb +50 -0
  85. data/test/unit/graticule/geocoder/yahoo_test.rb +48 -0
  86. data/test/unit/graticule/geocoder_test.rb +27 -0
  87. data/test/unit/graticule/location_test.rb +73 -0
  88. metadata +166 -0
@@ -0,0 +1,32 @@
1
+
2
+ module Graticule
3
+ module Geocoder
4
+
5
+ # Library for looking up coordinates with MetaCarta's GeoParser API.
6
+ #
7
+ # http://labs.metacarta.com/GeoParser/documentation.html
8
+ class MetaCarta < Rest
9
+
10
+ def initialize # :nodoc:
11
+ @url = URI.parse 'http://labs.metacarta.com/GeoParser/'
12
+ end
13
+
14
+ # Finds +location+ and returns a Location object.
15
+ def locate(location)
16
+ get :q => location.is_a?(String) ? location : location_from_params(location).to_s, :output => 'locations'
17
+ end
18
+
19
+ private
20
+
21
+ def check_error(xml) # :nodoc:
22
+ raise AddressError, 'bad location' unless xml.elements['Locations/Location']
23
+ end
24
+
25
+ def parse_response(xml) # :nodoc:
26
+ result = xml.elements['/Locations/Location[1]']
27
+ coords = result.elements['Centroid/gml:Point/gml:coordinates'].text.split ','
28
+ [Location.new(:latitude => coords.first.to_f, :longitude => coords.last.to_f)]
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,46 @@
1
+ module Graticule #:nodoc:
2
+ module Geocoder #:nodoc:
3
+ class Multi
4
+
5
+ # The Multi geocoder allows you to use multiple geocoders in succession.
6
+ #
7
+ # geocoder = Graticule.service(:multi).new(
8
+ # Graticule.service(:google).new("api_key"),
9
+ # Graticule.service(:yahoo).new("api_key"),
10
+ # )
11
+ # geocoder.locate '49423' # <= tries geocoders in succession
12
+ #
13
+ # The Multi geocoder will try the geocoders in order if a Graticule::AddressError
14
+ # is raised. You can customize this behavior by passing in a block to the Multi
15
+ # geocoder. For example, to try the geocoders until one returns a result with a
16
+ # high enough precision:
17
+ #
18
+ # geocoder = Graticule.service(:multi).new(geocoders) do |result|
19
+ # [:address, :street].include?(result.precision)
20
+ # end
21
+ #
22
+ # Geocoders will be tried in order until the block returned true for one of the results
23
+ #
24
+ def initialize(*geocoders, &acceptable)
25
+ @acceptable = acceptable || lambda { true }
26
+ @geocoders = geocoders.flatten
27
+ end
28
+
29
+ def locate(address)
30
+ last_error = nil
31
+ @geocoders.each do |geocoder|
32
+ begin
33
+ result = geocoder.locate address
34
+ return result if @acceptable.call(result)
35
+ rescue Error => e
36
+ last_error = e
37
+ rescue Errno::ECONNREFUSED
38
+ logger.error("Connection refused to #{service}")
39
+ end
40
+ end
41
+ raise last_error || AddressError.new("Couldn't find '#{address}' with any of the services")
42
+ end
43
+
44
+ end
45
+ end
46
+ end
@@ -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
+
52
+ location = Location.new
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
+ [location]
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
+ location = Location.new
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
+ [location]
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
+ location = Location.new
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
+ [location]
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,21 @@
1
+
2
+ module Graticule
3
+
4
+ # Get a geocoder for the given service
5
+ #
6
+ # geocoder = Graticule.service(:google).new "api_key"
7
+ #
8
+ # See the documentation for your specific geocoder for more information
9
+ #
10
+ def self.service(name)
11
+ Geocoder.const_get name.to_s.camelize
12
+ end
13
+
14
+ # Base error class
15
+ class Error < RuntimeError; end
16
+ class CredentialsError < Error; end
17
+
18
+ # Raised when you try to locate an invalid address.
19
+ class AddressError < Error; end
20
+
21
+ end
@@ -0,0 +1,61 @@
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
+ def initialize(attrs = {})
11
+ attrs.each do |key,value|
12
+ instance_variable_set "@#{key}", value
13
+ end
14
+ self.precision ||= :unknown
15
+ end
16
+
17
+ def attributes
18
+ [:latitude, :longitude, :street, :locality, :region, :postal_code, :country, :precision].inject({}) do |result,attr|
19
+ result[attr] = self.send(attr) unless self.send(attr).blank?
20
+ result
21
+ end
22
+ end
23
+
24
+ def blank?
25
+ attributes.except(:precision).empty?
26
+ end
27
+
28
+ # Returns an Array with latitude and longitude.
29
+ def coordinates
30
+ [latitude, longitude]
31
+ end
32
+
33
+ def ==(other)
34
+ other.respond_to?(:attributes) ? attributes == other.attributes : false
35
+ end
36
+
37
+ # Calculate the distance to another location. See the various Distance formulas
38
+ # for more information
39
+ def distance_to(destination, options = {})
40
+ options = {:formula => :haversine, :units => :miles}.merge(options)
41
+ "Graticule::Distance::#{options[:formula].to_s.titleize}".constantize.distance(self, destination, options[:units])
42
+ end
43
+
44
+ # Where would I be if I dug through the center of the earth?
45
+ def antipode
46
+ Location.new :latitude => -latitude, :longitude => longitude + (longitude >= 0 ? -180 : 180)
47
+ end
48
+ alias_method :antipodal_location, :antipode
49
+
50
+ def to_s(options = {})
51
+ options = {:coordinates => false, :country => true}.merge(options)
52
+ result = ""
53
+ result << "#{street}\n" if street
54
+ result << [locality, [region, postal_code].compact.join(" ")].compact.join(", ")
55
+ result << " #{country}" if options[:country] && country
56
+ result << "\nlatitude: #{latitude}, longitude: #{longitude}" if options[:coordinates] && [latitude, longitude].any?
57
+ result
58
+ end
59
+
60
+ end
61
+ end
@@ -0,0 +1,9 @@
1
+ module Graticule #:nodoc:
2
+ module Version #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 2
5
+ TINY = 11
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
data/lib/graticule.rb ADDED
@@ -0,0 +1,26 @@
1
+ $:.unshift(File.dirname(__FILE__))
2
+
3
+ require 'active_support'
4
+
5
+ require 'graticule/version'
6
+ require 'graticule/core_ext'
7
+ require 'graticule/location'
8
+ require 'graticule/geocoder'
9
+ require 'graticule/geocoder/base'
10
+ require 'graticule/geocoder/bogus'
11
+ require 'graticule/geocoder/rest'
12
+ require 'graticule/geocoder/google'
13
+ require 'graticule/geocoder/host_ip'
14
+ require 'graticule/geocoder/multi'
15
+ require 'graticule/geocoder/yahoo'
16
+ require 'graticule/geocoder/geocoder_ca'
17
+ require 'graticule/geocoder/geocoder_us'
18
+ require 'graticule/geocoder/local_search_maps'
19
+ require 'graticule/geocoder/meta_carta'
20
+ require 'graticule/geocoder/postcode_anywhere'
21
+ require 'graticule/geocoder/multimap'
22
+ require 'graticule/geocoder/mapquest'
23
+ require 'graticule/distance'
24
+ require 'graticule/distance/haversine'
25
+ require 'graticule/distance/spherical'
26
+ require 'graticule/distance/vincenty'
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>
data/site/plugin.html ADDED
@@ -0,0 +1,82 @@
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>acts_as_geocodable - 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> » acts_as_geocodable</h1>
27
+ <ul id="nav">
28
+ <li><a href="acts_as_geocodable">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
+ <p>acts_as_geocodable is a Rails plugin that makes your applications geo-aware. A picture (er, example) is worth a thousand words:</p>
36
+
37
+ <h2>Examples</h2>
38
+
39
+ <pre><code class="ruby">event = Event.create :street =&gt; &quot;777 NE Martin Luther King, Jr. Blvd.&quot;,
40
+ :locality =&gt; &quot;Portland&quot;, :region =&gt; &quot;Oregon&quot;, :postal_code =&gt; 97232
41
+
42
+ # how far am I from RailsConf 2007?
43
+ event.distance_to &quot;49423&quot; #=&gt; 1807.66560483205
44
+
45
+ # Find our new event, and any other ones in the area
46
+ Event.find(:all, :within =&gt; 50, :origin =&gt; &quot;97232&quot;)
47
+
48
+ # Find the nearest restaurant with beer
49
+ Restaurant.find(:nearest, :origin =&gt; event, :conditions =&gt; 'beer = true')</code></pre>
50
+
51
+
52
+ <p>See the <a href="acts_as_geocodable">API documentation</a> for more details.</p>
53
+
54
+ <h2>IP-based geocoding</h2>
55
+
56
+ <p>acts_as_geocodable adds a <code>remote_location</code> method to your Rails controllers for retrieving a user's location based on their remote IP address.</p>
57
+
58
+ <pre><code class="ruby">@nearest_store = Store.find(:nearest, :origin =&gt; remote_location) if remote_location</code></pre>
59
+
60
+ <p>Don't rely too heavily on remote_location because the location of many IP addresses cannot be determined through <a href="http://hostip.info">HostIP</a>.
61
+
62
+ <h2>Installation</h2>
63
+
64
+ <p>Install the plugin by executing:</p>
65
+
66
+ <pre>script/plugin install -x http://source.collectiveidea.com/public/rails/plugins/acts_as_geocodable</pre>
67
+
68
+ <h2>Contributing</h2>
69
+
70
+ <p>Contributions are welcome and appreciated! Grab the source from:</p>
71
+
72
+ <pre>http://source.collectiveidea.com/public/rails/plugins/acts_as_geocodable</pre>
73
+ </div>
74
+ </div>
75
+ <script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
76
+ </script>
77
+ <script type="text/javascript">
78
+ _uacct = "UA-194397-7";
79
+ urchinTracker();
80
+ </script>
81
+ </body>
82
+ </html>