aub-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.
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>