geogov 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in ..gemspec
4
+ gemspec
5
+
@@ -0,0 +1,15 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'bundler'
4
+
5
+ Bundler::GemHelper.install_tasks
6
+
7
+ task :default => [:test_units]
8
+
9
+ desc "Run basic tests"
10
+ Rake::TestTask.new("test_units") { |t|
11
+ t.libs << "test"
12
+ t.pattern = 'test/*_test.rb'
13
+ t.verbose = true
14
+ t.warning = true
15
+ }
@@ -0,0 +1,53 @@
1
+ $:.unshift(File.dirname(__FILE__))
2
+
3
+ require 'geogov/providers/open_street_map'
4
+ require 'geogov/providers/geonames'
5
+ require 'geogov/providers/google'
6
+ require 'geogov/providers/mapit'
7
+ require 'geogov/utils'
8
+ require 'geogov/geo_stack'
9
+ require 'geogov/fuzzy_point'
10
+ require 'geogov/providers/hostip'
11
+ require 'geogov/providers/dracos_gazetteer'
12
+
13
+ module Geogov
14
+
15
+ def self.provider_for(method, instance)
16
+ caching_instance = SimpleCache.new(instance)
17
+ @@methods ||= {}
18
+ @@methods[method] = caching_instance
19
+ unless self.methods().include?(method)
20
+ dispatcher = <<-EOS
21
+ def #{method}(*args, &block)
22
+ @@methods[:#{method}].__send__(#{method.inspect}, *args, &block)
23
+ end
24
+ EOS
25
+ module_eval(dispatcher)
26
+ end
27
+ end
28
+
29
+ def self.configure
30
+ yield self
31
+ end
32
+
33
+ provider_for :nearest_place_name, DracosGazetteer.new()
34
+
35
+ provider_for :lat_lon_to_country, Geonames.new()
36
+ provider_for :centre_of_country, Geonames.new()
37
+
38
+ provider_for :centre_of_district, Mapit.new()
39
+ provider_for :areas_for_stack_from_postcode, Mapit.new()
40
+ provider_for :areas_for_stack_from_coords, Mapit.new()
41
+ provider_for :lat_lon_from_postcode, Mapit.new()
42
+
43
+ provider_for :remote_location, Hostip.new()
44
+
45
+ provider_for :map_img, Google.new()
46
+ provider_for :map_href, Google.new()
47
+
48
+ extend self
49
+
50
+ end
51
+
52
+
53
+
@@ -0,0 +1,23 @@
1
+ module Geogov
2
+
3
+ class FuzzyPoint
4
+ ACCURACIES = [:point,:postcode,:postcode_district,:ward,:council,:nation,:country,:planet]
5
+ attr_reader :lon, :lat, :accuracy
6
+
7
+
8
+ def initialize(lat,lon,accuracy)
9
+ accuracy = accuracy.to_sym
10
+ raise ValueError unless ACCURACIES.include?(accuracy)
11
+ @lon,@lat,@accuracy = lon.to_f, lat.to_f, accuracy
12
+ if @accuracy == :point
13
+ @lon = @lon.round(2)
14
+ @lat = @lat.round(2)
15
+ end
16
+ end
17
+
18
+ def to_hash
19
+ {"lon"=> self.lon.to_s,"lat"=>self.lat.to_s,"accuracy"=>accuracy.to_s}
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,140 @@
1
+ module Geogov
2
+ class GeoStack
3
+
4
+ attr_accessor :postcode,:ward,:council,:nation,:country,:wmc,:lat,:lon
5
+ attr_accessor :fuzzy_point
6
+ attr_accessor :friendly_name
7
+
8
+ def initialize()
9
+ yield self
10
+ end
11
+
12
+ def calculate_fuzzy_point
13
+ if self.lat and self.lon
14
+ return FuzzyPoint.new(self.lat, self.lon, :point)
15
+ end
16
+
17
+ if self.postcode
18
+ district = postcode.split(" ")[0]
19
+ district_centre = Geogov.centre_of_district(district)
20
+ if district_centre
21
+ return FuzzyPoint.new(district_centre["lat"],district_centre["lon"],:postcode_district)
22
+ end
23
+ end
24
+
25
+ if self.country
26
+ country_centre = Geogov.centre_of_country(self.country)
27
+ if country_centre
28
+ return FuzzyPoint.new(country_centre["lat"],country_centre["lon"],:country)
29
+ end
30
+ end
31
+
32
+ FuzzyPoint.new(0,0,:planet)
33
+ end
34
+
35
+ def self.new_from_ip(ip_address)
36
+ remote_location = Geogov.remote_location(ip_address)
37
+ new() do |gs|
38
+ if remote_location
39
+ gs.country = remote_location['country']
40
+ end
41
+ gs.fuzzy_point = gs.calculate_fuzzy_point
42
+ end
43
+ end
44
+
45
+ def self.new_from_hash(hash)
46
+ new() do |gs|
47
+ gs.set_fields(hash)
48
+ unless hash['fuzzy_point']
49
+ raise ArgumentError, "fuzzy point required"
50
+ end
51
+ end
52
+ end
53
+
54
+ def to_hash
55
+ {
56
+ :fuzzy_point => self.fuzzy_point.to_hash,
57
+ :postcode => self.postcode,
58
+ :ward => self.ward,
59
+ :council => self.council,
60
+ :nation => self.nation,
61
+ :country => self.country,
62
+ :wmc => self.wmc,
63
+ :friendly_name => self.friendly_name
64
+ }.select {|k,v| !(v.nil?) }
65
+ end
66
+
67
+ def update(hash)
68
+ self.class.new() do |empty|
69
+ full_postcode = hash['postcode']
70
+ empty.set_fields(hash)
71
+ if has_valid_lat_lon(hash)
72
+ empty.fetch_missing_fields_for_coords(hash['lat'], hash['lon'])
73
+ elsif full_postcode
74
+ empty.fetch_missing_fields_for_postcode(full_postcode)
75
+ end
76
+ empty.fuzzy_point = empty.calculate_fuzzy_point
77
+ end
78
+ end
79
+
80
+ def has_valid_lat_lon(hash)
81
+ return (hash['lon'] and hash['lat'] and hash['lon'] != "" and hash['lat'] != "")
82
+ end
83
+
84
+ def fetch_missing_fields_for_postcode(postcode)
85
+ if matches = postcode.match(POSTCODE_REGEXP)
86
+ self.country = "UK"
87
+ fields = Geogov.areas_for_stack_from_postcode(postcode)
88
+ if fields
89
+ lat_lon = fields[:point]
90
+ if lat_lon
91
+ self.friendly_name = Geogov.nearest_place_name(lat_lon['lat'],lat_lon['lon'])
92
+ end
93
+ set_fields(fields.select {|k,v| k != :point})
94
+ end
95
+ end
96
+ end
97
+
98
+ def fetch_missing_fields_for_coords(lat, lon)
99
+ self.friendly_name = Geogov.nearest_place_name(lat, lon)
100
+ fields = Geogov.areas_for_stack_from_coords(lat, lon)
101
+ if ['England', 'Scotland', 'Northern Ireland', 'Wales'].include?(fields[:nation])
102
+ self.country = 'UK'
103
+ set_fields(fields.select {|k,v| k != :point})
104
+ end
105
+ end
106
+
107
+ def set_fields(hash)
108
+ hash.each do |geo, value|
109
+ setter = (geo.to_s+"=").to_sym
110
+ if self.respond_to?(setter)
111
+ unless value == ""
112
+ self.send(setter,value)
113
+ end
114
+ else
115
+ raise ArgumentError, "geo type '#{geo}' is not a valid geo type"
116
+ end
117
+ end
118
+ self
119
+ end
120
+
121
+ def fuzzy_point=(point)
122
+ if point.is_a?(Hash)
123
+ @fuzzy_point = FuzzyPoint.new(point["lat"],point["lon"],point["accuracy"])
124
+ else
125
+ @fuzzy_point = point
126
+ end
127
+ end
128
+
129
+ POSTCODE_REGEXP = /^([A-Z]{1,2}[0-9R][0-9A-Z]?)\s*([0-9])[ABD-HJLNP-UW-Z]{2}(:?\s+)?$/i
130
+ SECTOR_POSTCODE_REGEXP = /^([A-Z]{1,2}[0-9R][0-9A-Z]?)\s*([0-9])(:?\s+)?$/i
131
+
132
+ def postcode=(postcode)
133
+ if (matches = (postcode.match(POSTCODE_REGEXP) || postcode.match(SECTOR_POSTCODE_REGEXP)))
134
+ @postcode = matches[1]+" "+matches[2]
135
+ end
136
+ end
137
+
138
+
139
+ end
140
+ end
@@ -0,0 +1,19 @@
1
+ module Geogov
2
+ class DracosGazetteer
3
+ def initialize(default_url = "http://gazetteer.dracos.vm.bytemark.co.uk")
4
+ @base = default_url
5
+ end
6
+
7
+ def nearest_place_name(lat,lon)
8
+ url = "#{@base}/point/#{lat},#{lon}.json"
9
+ results = Geogov.get_json(url)
10
+ if results && results["place"]
11
+ return results["place"][0]
12
+ else
13
+ return nil
14
+ end
15
+ end
16
+
17
+
18
+ end
19
+ end
@@ -0,0 +1,48 @@
1
+ module Geogov
2
+ class Geonames
3
+ def initialize(username = "username", url = "http://api.geonames.org")
4
+ @url = url
5
+ @username = username
6
+ end
7
+
8
+ def query(method,params)
9
+ params = {"username"=>@username}.merge(params)
10
+ Geogov.get_json("#{@url}/#{method}?"+Geogov.hash_to_params(params))
11
+ end
12
+
13
+ def nearest_place_name(lat,lon)
14
+ params = { "lat" => lat, "lng" => lon}
15
+ results = query("findNearbyPlaceNameJSON",params)
16
+ if results && results["geonames"]
17
+ return results["geonames"][0]["name"]
18
+ else
19
+ return nil
20
+ end
21
+ end
22
+
23
+ def centre_of_country(country_code)
24
+ params = { "country" => country_code, "type" => "JSON" }
25
+ results = query("countryInfo",params)
26
+ if results && results["geonames"] && results["geonames"][0]
27
+ country = results["geonames"][0]
28
+ bbe, bbw = country["bBoxEast"],country["bBoxWest"]
29
+ bbn, bbs = country["bBoxNorth"],country["bBoxSouth"]
30
+ lon,lat = (bbe.to_f+bbw.to_f)/2,(bbn.to_f+bbs.to_f)/2
31
+ return { "lat" => lat, "lon" => lon }
32
+ else
33
+ return nil
34
+ end
35
+ end
36
+
37
+ def lat_lon_to_country(lat,lon)
38
+ params = { "lat" => lat, "lng" => lon, 'type'=>"JSON"}
39
+ results = query("countryCode",params)
40
+ if results && results["countryCode"]
41
+ return results["countryCode"]
42
+ else
43
+ return nil
44
+ end
45
+ end
46
+ end
47
+
48
+ end
@@ -0,0 +1,48 @@
1
+ module Geogov
2
+
3
+ class Google
4
+
5
+ def dimension(l1,l2)
6
+ "#{l1}x#{l2}"
7
+ end
8
+
9
+ def location(l1,l2)
10
+ "#{l1},#{l2}"
11
+ end
12
+
13
+ def map_img(lat,lon,options= {})
14
+ g_options = {
15
+ :zoom => options[:z] || 14,
16
+ :size => dimension(options[:w],options[:h]),
17
+ :center => location(lat,lon),
18
+ :sensor => false
19
+ }
20
+ if options[:marker_lat] && options[:marker_lon]
21
+ location = location(options[:marker_lat],options[:marker_lon])
22
+ g_options[:markers] = ["color:blue",location].join("|")
23
+ end
24
+
25
+ params = Geogov.hash_to_params(g_options)
26
+
27
+ "http://maps.google.com/maps/api/staticmap?#{params}"
28
+ end
29
+
30
+
31
+ def map_href(lat,lon,options = {})
32
+ g_options = {
33
+ :z => options[:z] || 14,
34
+ :ie => "UTF8",
35
+ :q => location(lat,lon)
36
+ }
37
+ if options[:marker_lat] && options[:marker_lon]
38
+ location = location(options[:marker_lat],options[:marker_lon])
39
+ g_options[:sll] = location
40
+ end
41
+
42
+ params = Geogov.hash_to_params(g_options)
43
+ "http://maps.google.com/maps?#{params}"
44
+ end
45
+
46
+ end
47
+
48
+ end
@@ -0,0 +1,34 @@
1
+ require 'yaml'
2
+
3
+ module Geogov
4
+
5
+ class Hostip
6
+ def initialize
7
+ @url = 'http://api.hostip.info/get_html.php'
8
+ end
9
+
10
+ def remote_location(ip_address)
11
+ params = {:ip => ip_address, :position => true}
12
+ results = Geogov.get(@url + "?" + Geogov.hash_to_params(params))
13
+ return nil if results.nil?
14
+ response = YAML.load(results + "\n")
15
+ location = {}.tap do |h|
16
+ h["lat"] = response['Latitude']
17
+ h["lon"] = response['Longitude']
18
+ h["city"], h["county"] = response['City'].split(', ')
19
+ country = response['Country'].match(/\((\w+)\)$/)
20
+ h["country"] = country[1] if country
21
+ end
22
+ return nil if location['city'] =~ /Unknown City/
23
+ return nil if location['city'] =~ /Private Address/
24
+
25
+ # I found these very unreliable, so better they're
26
+ # not there to tempt anyone
27
+ location.delete("city")
28
+ location.delete("county")
29
+ return location
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,113 @@
1
+ require 'cgi'
2
+ require 'uri'
3
+
4
+ module Geogov
5
+
6
+ class Mapit
7
+
8
+ class Method
9
+ def initialize(url,params = [])
10
+ @url = url
11
+ @params = params
12
+ end
13
+
14
+ def to_url(base_url)
15
+ url = "/#{@url}" unless /^\//.match(@url)
16
+ params = @params.map {|p|
17
+ p = p.join(",") if p.is_a?(Array)
18
+ # Messy, but MapIt gets upset if you escape commas
19
+ CGI::escape(p).gsub('%2C', ',')
20
+ }
21
+ url_path = "#{base_url}#{url}"
22
+ url_path += "/#{params.join("/")}" if params.length > 0
23
+ return url_path
24
+ end
25
+
26
+ def call(base_url)
27
+ Geogov.get_json(self.to_url(base_url))
28
+ end
29
+ end
30
+
31
+ def initialize(default_url = "http://mapit.mysociety.org")
32
+ @base = default_url
33
+ end
34
+
35
+ def valid_mapit_methods
36
+ [:postcode,:areas,:area,:point,:generations]
37
+ end
38
+
39
+ def respond_to?(sym)
40
+ valid_mapit_methods.include?(sym) || super(sym)
41
+ end
42
+
43
+ # Borrowed heavily from mapit's pylib/postcodes/views.py with some amendments based on
44
+ # pylib/mapit/areas/models.py
45
+ def translate_area_type_to_shortcut(area_type)
46
+ if ['COP','LBW','LGE','MTW','UTE','UTW','DIW'].include?(area_type)
47
+ return 'ward'
48
+ elsif ['CTY', 'CED'].include?(area_type)
49
+ return 'council' # county
50
+ elsif ['DIS', 'LBO'].include?(area_type)
51
+ return 'council' # district
52
+ elsif area_type == 'WMC' # XXX Also maybe 'EUR', 'NIE', 'SPC', 'SPE', 'WAC', 'WAE', 'OLF', 'OLG', 'OMF', 'OMG')
53
+ return 'WMC'
54
+ end
55
+ end
56
+
57
+ def areas_for_stack_from_coords(lat, lon)
58
+ query = self.point("4326", [lon,lat])
59
+ results = {:point => {'lat' => lat, 'lon' => lon}}
60
+ query.each do |id,area_info|
61
+ level = translate_area_type_to_shortcut(area_info['type'])
62
+ if level
63
+ level = level.downcase.to_sym
64
+ results[level] = [] unless results[level]
65
+ results[level] << area_info.select {|k,v| ["name","id","type"].include?(k) }
66
+ results[:nation] = area_info['country_name'] if results[:nation].nil?
67
+ end
68
+ end
69
+ return results
70
+ end
71
+
72
+ def areas_for_stack_from_postcode(postcode)
73
+ query = self.postcode(postcode)
74
+ results = {}
75
+ if query && query['shortcuts'] && query['areas']
76
+ query['shortcuts'].each do |typ,i|
77
+ if i.is_a? Hash
78
+ ids = i.values()
79
+ else
80
+ ids = [i]
81
+ end
82
+ ids.each do |id|
83
+ area_info = query['areas'][id.to_s]
84
+ level = typ.downcase.to_sym
85
+ results[level] = [] unless results[level]
86
+ results[level] << area_info.select {|k,v| ["name","id","type"].include?(k) }
87
+ results[:nation] = area_info['country_name'] if results[:nation].nil?
88
+ end
89
+ end
90
+ lat,lon = query['wgs84_lat'],query['wgs84_lon']
91
+ results[:point] = {'lat' => lat, 'lon' => lon}
92
+ end
93
+ return results
94
+ end
95
+
96
+ def centre_of_district(district_postcode)
97
+ query = self.postcode("partial",district_postcode)
98
+ if query
99
+ lat,lon = query['wgs84_lat'],query['wgs84_lon']
100
+ return {'lat' => lat, 'lon' => lon}
101
+ end
102
+ end
103
+
104
+ def method_missing(method, *args, &block)
105
+ if valid_mapit_methods.include?(method)
106
+ Mapit::Method.new(method.to_s,args).call(@base)
107
+ else
108
+ super(method, *args, &block)
109
+ end
110
+ end
111
+
112
+ end
113
+ end
@@ -0,0 +1,51 @@
1
+ module Geogov
2
+
3
+ class OpenStreetMap
4
+
5
+ def initialize(url = "http://ojw.dev.openstreetmap.org")
6
+ @url = url
7
+ end
8
+
9
+ def map_img(lat,long,options = {})
10
+ options = {
11
+ :w => 200,
12
+ :h => 200,
13
+ :z => 14,
14
+ :mode => "export",
15
+ :lat => lat,
16
+ :lon => long,
17
+ :show => 1
18
+ }.merge(options)
19
+
20
+ if options[:marker_lat] && options[:marker_lon]
21
+ options[:mlat0] = options.delete(:marker_lat)
22
+ options[:mlon0] = options.delete(:marker_lon)
23
+ end
24
+
25
+ params = Geogov.hash_to_params(options)
26
+
27
+ "#{@url}/StaticMap?#{params}"
28
+ end
29
+
30
+ def map_href(lat,long,options = {})
31
+ options = {
32
+ :zoom => options[:z] || 14,
33
+ :lat => lat,
34
+ :lon => long,
35
+ :layers => "M"
36
+ }.merge(options)
37
+
38
+ if options[:marker_lat] && options[:marker_lon]
39
+ options[:mlat0] = options.delete(:marker_lat)
40
+ options[:mlon0] = options.delete(:marker_lon)
41
+ end
42
+
43
+ params = Geogov.hash_to_params(options)
44
+
45
+ "http://www.openstreetmap.org/?#{params}"
46
+ end
47
+
48
+ end
49
+
50
+
51
+ end
@@ -0,0 +1,87 @@
1
+ require 'json'
2
+ require 'net/http'
3
+
4
+ module Geogov
5
+
6
+ def get(url)
7
+ url = URI.parse(url) unless url.is_a? URI
8
+ response = Net::HTTP.start(url.host, url.port) { |http|
9
+ request = Net::HTTP::Get.new(url.request_uri)
10
+ http.request(request)
11
+ }
12
+ return nil if response.code != '200'
13
+ return response.body
14
+ end
15
+
16
+ def get_json(url)
17
+ response = self.get(url)
18
+ if response
19
+ return JSON.parse(response)
20
+ else
21
+ return nil
22
+ end
23
+ end
24
+
25
+ def hash_to_params(hash)
26
+ hash.map { |k,v| CGI.escape(k.to_s) + "=" + CGI.escape(v.to_s) }.join("&")
27
+ end
28
+
29
+ # Dead simple and inefficient LRU cache
30
+ # In no way thread-safe
31
+ class LruCache
32
+
33
+ def initialize(size = 100)
34
+ @size = size
35
+ @bucket1 = {}
36
+ @bucket2 = {}
37
+ end
38
+
39
+ def []=(key,obj)
40
+ if @bucket1[key]
41
+ @bucket1[key] = obj
42
+ elsif @bucket2[key]
43
+ @bucket2[key] = obj
44
+ else
45
+ @bucket1[key] = obj
46
+ swizzle if @bucket1.size > @size
47
+ end
48
+ end
49
+
50
+ def [](key)
51
+ if @bucket1[key]
52
+ return @bucket1[key]
53
+ elsif @bucket2[key]
54
+ obj = @bucket2.delete[key]
55
+ @bucket1[key] = obj
56
+ swizzle if @bucket1.size > @size
57
+ return obj
58
+ end
59
+ end
60
+
61
+ def swizzle
62
+ @bucket2 = @bucket1
63
+ @bucket1 = {}
64
+ end
65
+ end
66
+
67
+ class SimpleCache
68
+ def initialize(delegate)
69
+ @delegate = delegate
70
+ @cache = LruCache.new(1000)
71
+ end
72
+
73
+ def method_missing(m, *args)
74
+ arg_key = args.inspect
75
+ cache_key = "#{m}--#{arg_key}"
76
+ if @cache[cache_key]
77
+ return @cache[cache_key]
78
+ else
79
+ result = @delegate.send(m,*args)
80
+ @cache[cache_key] = result
81
+ return result
82
+ end
83
+ end
84
+ end
85
+
86
+
87
+ end
@@ -0,0 +1,64 @@
1
+ require 'test_helper'
2
+
3
+ class GovspeakTest < Test::Unit::TestCase
4
+
5
+ test "IP-located stack should have country" do
6
+
7
+ Geogov.configure do |g|
8
+ g.provider_for :centre_of_country, stub(:centre_of_country => {"lat"=>37,"lon"=>-96})
9
+ g.provider_for :remote_location, stub(:remote_location => {'country' => 'US'})
10
+ end
11
+
12
+ stack = Geogov::GeoStack.new_from_ip('173.203.129.90')
13
+ assert_equal "US", stack.country
14
+ assert_in_delta stack.fuzzy_point.lon, -96, 0.5
15
+ assert_in_delta stack.fuzzy_point.lat, 37, 0.5
16
+ assert_equal :country, stack.fuzzy_point.accuracy
17
+ end
18
+
19
+ test "should be specific if no country available" do
20
+
21
+ Geogov.configure do |g|
22
+ g.provider_for :remote_location, stub(:remote_location => nil)
23
+ end
24
+
25
+ stack = Geogov::GeoStack.new_from_ip('127.0.0.1')
26
+ assert_nil stack.country
27
+ assert_equal 0, stack.fuzzy_point.lon
28
+ assert_equal 0, stack.fuzzy_point.lat
29
+ assert_equal :planet, stack.fuzzy_point.accuracy
30
+ end
31
+
32
+ test "reconstructed stack rejects unknown params" do
33
+ assert_raises(ArgumentError) {
34
+ Geogov::GeoStack.new_from_hash("galaxy" => "Andromeda")
35
+ }
36
+ end
37
+
38
+ test "reconstructed stack should refuse creation if no fuzzy point" do
39
+ assert_raises(ArgumentError) {
40
+ Geogov::GeoStack.new_from_hash("country" => "US")
41
+ }
42
+ end
43
+
44
+ test "reconstructed stack should always truncate postcode" do
45
+ stack = Geogov::GeoStack.new_from_hash("postcode"=>"SE10 8UG","country" => "UK","fuzzy_point" => {"lat"=>"37","lon"=>"-96","accuracy"=>"postcode"})
46
+ assert_equal "SE10 8", stack.postcode
47
+ end
48
+
49
+ test "stack should not consider a postcode with trailing whitespace invalid" do
50
+ stack = Geogov::GeoStack.new_from_hash("postcode"=>"SE10 8UG ","country" => "UK","fuzzy_point" => {"lat"=>"37","lon"=>"-96","accuracy"=>"postcode"})
51
+ assert_equal "SE10 8", stack.postcode
52
+ end
53
+
54
+ test "stack should ignore invalid postcodes" do
55
+ stack = Geogov::GeoStack.new_from_hash("postcode"=>"NOTAPOSTCODE","country" => "UK","fuzzy_point" => {"lat"=>"37","lon"=>"-96","accuracy"=>"postcode"})
56
+ assert_nil stack.postcode
57
+ end
58
+
59
+ test "stack with country should have country accuracy" do
60
+ stack = Geogov::GeoStack.new_from_hash("country" => "US","fuzzy_point" => {"lat"=>"37","lon"=>"-96","accuracy"=>"country"})
61
+ assert_equal :country, stack.fuzzy_point.accuracy
62
+ end
63
+
64
+ end
@@ -0,0 +1,21 @@
1
+ $:.unshift(File.expand_path("../lib")) unless $:.include?(File.expand_path("../lib"))
2
+
3
+ require 'bundler'
4
+ Bundler.setup :default, :development, :test
5
+
6
+ require 'test/unit'
7
+
8
+ class Test::Unit::TestCase
9
+ class << self
10
+ def test(name, &block)
11
+ clean_name = name.gsub(/\s+/,'_')
12
+ method = "test_#{clean_name.gsub(/\s+/,'_')}".to_sym
13
+ already_defined = instance_method(method) rescue false
14
+ raise "#{method} exists" if already_defined
15
+ define_method(method, &block)
16
+ end
17
+ end
18
+ end
19
+
20
+ require 'mocha'
21
+ require 'geogov'
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: geogov
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Ben Griffiths
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-08-08 00:00:00 +01:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rake
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 63
30
+ segments:
31
+ - 0
32
+ - 8
33
+ - 0
34
+ version: 0.8.0
35
+ type: :development
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: mocha
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ hash: 59
46
+ segments:
47
+ - 0
48
+ - 9
49
+ - 0
50
+ version: 0.9.0
51
+ type: :development
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: rspec
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ hash: 27
62
+ segments:
63
+ - 2
64
+ - 5
65
+ - 0
66
+ version: 2.5.0
67
+ type: :development
68
+ version_requirements: *id003
69
+ description: Geolocation and utilities for single domain
70
+ email:
71
+ - ben@alphagov.co.uk
72
+ executables: []
73
+
74
+ extensions: []
75
+
76
+ extra_rdoc_files: []
77
+
78
+ files:
79
+ - lib/geogov/fuzzy_point.rb
80
+ - lib/geogov/geo_stack.rb
81
+ - lib/geogov/providers/dracos_gazetteer.rb
82
+ - lib/geogov/providers/geonames.rb
83
+ - lib/geogov/providers/google.rb
84
+ - lib/geogov/providers/hostip.rb
85
+ - lib/geogov/providers/mapit.rb
86
+ - lib/geogov/providers/open_street_map.rb
87
+ - lib/geogov/utils.rb
88
+ - lib/geogov.rb
89
+ - Gemfile
90
+ - Rakefile
91
+ - test/geogov_test.rb
92
+ - test/test_helper.rb
93
+ has_rdoc: true
94
+ homepage: http://github.com/alphagov/geogov
95
+ licenses: []
96
+
97
+ post_install_message:
98
+ rdoc_options: []
99
+
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ none: false
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ hash: 3
108
+ segments:
109
+ - 0
110
+ version: "0"
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ none: false
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ hash: 3
117
+ segments:
118
+ - 0
119
+ version: "0"
120
+ requirements: []
121
+
122
+ rubyforge_project:
123
+ rubygems_version: 1.3.7
124
+ signing_key:
125
+ specification_version: 3
126
+ summary: Geolocation and utilities for single domain
127
+ test_files:
128
+ - test/geogov_test.rb
129
+ - test/test_helper.rb