andre-geokit 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,13 +1,12 @@
1
1
  .loadpath
2
2
  .project
3
- History.txt
4
3
  Manifest.txt
5
4
  README.markdown
6
5
  Rakefile
7
6
  geokit.gemspec
8
- lib/geocoders.rb
7
+ lib/geokit/geocoders.rb
9
8
  lib/geokit.rb
10
- lib/mappable.rb
9
+ lib/geokit/mappable.rb
11
10
  test/test_base_geocoder.rb
12
11
  test/test_bounds.rb
13
12
  test/test_ca_geocoder.rb
@@ -8,13 +8,10 @@ The Geokit gem provides the following:
8
8
 
9
9
  * Distance calculations between two points on the earth. Calculate the distance in miles or KM, with all the trigonometry abstracted away by GeoKit.
10
10
  * Geocoding from multiple providers. It currently supports Google, Yahoo, Geocoder.us, and Geocoder.ca geocoders, and it provides a uniform response structure from all of them. It also provides a fail-over mechanism, in case your input fails to geocode in one service.
11
+ * Rectangular bounds calculations: is a point within a given rectangular bounds?
12
+ * Heading and midpoint calculations
11
13
 
12
- Combine this with gem with the geokit-rails plugin to get location-based finders for your Rails app. Plugins for other web frameworks and ORMs will provide similar functionality.
13
-
14
-
15
- ## FEATURES/PROBLEMS:
16
-
17
- * none currently
14
+ Combine this with gem with the [geokit-rails plugin](http://github.com/andre/geokit-rails/tree/master) to get location-based finders for your Rails app. Plugins for other web frameworks and ORMs will provide similar functionality.
18
15
 
19
16
  ## SYNOPSIS:
20
17
 
@@ -30,21 +27,103 @@ Combine this with gem with the geokit-rails plugin to get location-based finders
30
27
  => 1.21120007413626
31
28
  irb> a.heading_to(b)
32
29
  => 244.959832435678
30
+ irb(main):006:0> c=a.midpoint_to(b) # what's halfway from a to b?
31
+ irb> c.ll
32
+ => "37.7899239257175,-122.406153503469"
33
+ irb(main):008:0> d=c.endpoint(90,10) # what's 10 miles to the east of c?
34
+ irb> d.ll
35
+ => "37.7897825005142,-122.223214776155"
33
36
 
34
-
35
- ## REQUIREMENTS:
36
-
37
+ FYI, that `.ll` method means "latitude longitude".
37
38
 
38
39
  ## INSTALL:
39
40
 
40
41
  * gem sources -a http://gems.github.com
41
- * sudo gem install
42
+ * sudo gem install andre-geokit-gem
43
+
44
+ ## Configuration
45
+
46
+ If you're using this gem by itself, here's how to set configurations:
47
+
48
+ # These defaults are used in Geokit::Mappable.distance_to and in acts_as_mappable
49
+ Geokit::default_units = :miles
50
+ Geokit::default_formula = :sphere
51
+
52
+ # This is the timeout value in seconds to be used for calls to the geocoder web
53
+ # services. For no timeout at all, comment out the setting. The timeout unit
54
+ # is in seconds.
55
+ Geokit::Geocoders::timeout = 3
56
+
57
+ # These settings are used if web service calls must be routed through a proxy.
58
+ # These setting can be nil if not needed, otherwise, addr and port must be
59
+ # filled in at a minimum. If the proxy requires authentication, the username
60
+ # and password can be provided as well.
61
+ Geokit::Geocoders::proxy_addr = nil
62
+ Geokit::Geocoders::proxy_port = nil
63
+ Geokit::Geocoders::proxy_user = nil
64
+ Geokit::Geocoders::proxy_pass = nil
65
+
66
+ # This is your yahoo application key for the Yahoo Geocoder.
67
+ # See http://developer.yahoo.com/faq/index.html#appid
68
+ # and http://developer.yahoo.com/maps/rest/V1/geocode.html
69
+ Geokit::Geocoders::yahoo = 'REPLACE_WITH_YOUR_YAHOO_KEY'
70
+
71
+ # This is your Google Maps geocoder key.
72
+ # See http://www.google.com/apis/maps/signup.html
73
+ # and http://www.google.com/apis/maps/documentation/#Geocoding_Examples
74
+ Geokit::Geocoders::google = 'REPLACE_WITH_YOUR_GOOGLE_KEY'
75
+
76
+ # This is your username and password for geocoder.us.
77
+ # To use the free service, the value can be set to nil or false. For
78
+ # usage tied to an account, the value should be set to username:password.
79
+ # See http://geocoder.us
80
+ # and http://geocoder.us/user/signup
81
+ Geokit::Geocoders::geocoder_us = false
82
+
83
+ # This is your authorization key for geocoder.ca.
84
+ # To use the free service, the value can be set to nil or false. For
85
+ # usage tied to an account, set the value to the key obtained from
86
+ # Geocoder.ca.
87
+ # See http://geocoder.ca
88
+ # and http://geocoder.ca/?register=1
89
+ Geokit::Geocoders::geocoder_ca = false
90
+
91
+ # This is the order in which the geocoders are called in a failover scenario
92
+ # If you only want to use a single geocoder, put a single symbol in the array.
93
+ # Valid symbols are :google, :yahoo, :us, and :ca.
94
+ # Be aware that there are Terms of Use restrictions on how you can use the
95
+ # various geocoders. Make sure you read up on relevant Terms of Use for each
96
+ # geocoder you are going to use.
97
+ Geokit::Geocoders::provider_order = [:google,:us]
98
+
99
+ If you're using this gem with the [geokit-rails plugin](http://github.com/andre/geokit-rails/tree/master), a template with these settings gets placed in your app's config/initializers directory.
100
+
101
+ ## NOTES ON WHAT'S WHERE
102
+
103
+ mappable.rb contains the Mappable module, which provides basic
104
+ distance calculation methods, i.e., calculating the distance
105
+ between two points.
106
+
107
+ mappable.rb also contains LatLng, GeoLoc, and Bounds.
108
+ LatLng is a simple container for latitude and longitude, but
109
+ it's made more powerful by mixing in the above-mentioned Mappable
110
+ module -- therefore, you can calculate easily the distance between two
111
+ LatLng ojbects with `distance = first.distance_to(other)`
112
+
113
+ GeoLoc (also in mappable.rb) represents an address or location which
114
+ has been geocoded. You can get the city, zipcode, street address, etc.
115
+ from a GeoLoc object. GeoLoc extends LatLng, so you also get lat/lng
116
+ AND the Mappable modeule goodness for free.
117
+
118
+ geocoders.rb contains all the geocoder implemenations. All the gercoders
119
+ inherit from a common base (class Geocoder) and implement the private method
120
+ do_geocode.
42
121
 
43
122
  ## LICENSE:
44
123
 
45
124
  (The MIT License)
46
125
 
47
- Copyright (c) 2007-2008 Andre Lewis and Bill Eisenhauer
126
+ Copyright (c) 2007-2009 Andre Lewis and Bill Eisenhauer
48
127
 
49
128
  Permission is hereby granted, free of charge, to any person obtaining
50
129
  a copy of this software and associated documentation files (the
@@ -1,5 +1,5 @@
1
1
  module Geokit
2
- VERSION = '1.0.0'
2
+ VERSION = '1.2.0'
3
3
  # These defaults are used in Geokit::Mappable.distance_to and in acts_as_mappable
4
4
  @@default_units = :miles
5
5
  @@default_formula = :sphere
@@ -21,8 +21,8 @@ module Geokit
21
21
  end
22
22
  end
23
23
 
24
- require 'geocoders'
25
- require 'mappable'
24
+ require 'geokit/geocoders'
25
+ require 'geokit/mappable'
26
26
 
27
27
  # make old-style module name "GeoKit" equivilent to new-style "Geokit"
28
28
  module GeoKit
@@ -59,11 +59,12 @@ module Geokit
59
59
  @@google = 'REPLACE_WITH_YOUR_GOOGLE_KEY'
60
60
  @@geocoder_us = false
61
61
  @@geocoder_ca = false
62
+ @@geonames = false
62
63
  @@provider_order = [:google,:us]
63
64
  @@logger=Logger.new(STDOUT)
64
65
  @@logger.level=Logger::INFO
65
66
 
66
- [:yahoo, :google, :geocoder_us, :geocoder_ca, :provider_order, :timeout,
67
+ [:yahoo, :google, :geocoder_us, :geocoder_ca, :geonames, :provider_order, :timeout,
67
68
  :proxy_addr, :proxy_port, :proxy_user, :proxy_pass,:logger].each do |sym|
68
69
  class_eval <<-EOS, __FILE__, __LINE__
69
70
  def self.#{sym}
@@ -111,9 +112,16 @@ module Geokit
111
112
  private
112
113
 
113
114
  # Wraps the geocoder call around a proxy if necessary.
114
- def self.do_get(url)
115
- return Net::HTTP::Proxy(Geokit::Geocoders::proxy_addr, Geokit::Geocoders::proxy_port,
116
- Geokit::Geocoders::proxy_user, Geokit::Geocoders::proxy_pass).get_response(URI.parse(url))
115
+ def self.do_get(url)
116
+ uri = URI.parse(url)
117
+ req = Net::HTTP::Get.new(url)
118
+ req.basic_auth(uri.user, uri.password) if uri.userinfo
119
+ res = Net::HTTP::Proxy(GeoKit::Geocoders::proxy_addr,
120
+ GeoKit::Geocoders::proxy_port,
121
+ GeoKit::Geocoders::proxy_user,
122
+ GeoKit::Geocoders::proxy_pass).start(uri.host, uri.port) { |http| http.request(req) }
123
+
124
+ return res
117
125
  end
118
126
 
119
127
  # Adds subclass' geocode method making it conveniently available through
@@ -280,19 +288,32 @@ module Geokit
280
288
  class UsGeocoder < Geocoder
281
289
 
282
290
  private
283
-
284
- # For now, the geocoder_method will only geocode full addresses -- not zips or cities in isolation
285
291
  def self.do_geocode(address)
286
292
  address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
287
- url = "http://"+(Geokit::Geocoders::geocoder_us || '')+"geocoder.us/service/csv/geocode?address=#{Geokit::Inflector::url_escape(address_str)}"
293
+
294
+ query = (address_str =~ /^\d{5}(?:-\d{4})?$/ ? "zip" : "address") + "=#{Geokit::Inflector::url_escape(address_str)}"
295
+ url = if GeoKit::Geocoders::geocoder_us
296
+ "http://#{GeoKit::Geocoders::geocoder_us}@geocoder.us/member/service/csv/geocode"
297
+ else
298
+ "http://geocoder.us/service/csv/geocode"
299
+ end
300
+
301
+ url = "#{url}?#{query}"
288
302
  res = self.call_geocoder_service(url)
303
+
289
304
  return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
290
305
  data = res.body
291
306
  logger.debug "Geocoder.us geocoding. Address: #{address}. Result: #{data}"
292
307
  array = data.chomp.split(',')
293
-
294
- if array.length == 6
308
+
309
+ if array.length == 5
295
310
  res=GeoLoc.new
311
+ res.lat,res.lng,res.city,res.state,res.zip=array
312
+ res.country_code='US'
313
+ res.success=true
314
+ return res
315
+ elsif array.length == 6
316
+ res=GeoLoc.new
296
317
  res.lat,res.lng,res.street_address,res.city,res.state,res.zip=array
297
318
  res.country_code='US'
298
319
  res.success=true
@@ -304,6 +325,7 @@ module Geokit
304
325
  rescue
305
326
  logger.error "Caught an error during geocoder.us geocoding call: "+$!
306
327
  return GeoLoc.new
328
+
307
329
  end
308
330
  end
309
331
 
@@ -350,6 +372,54 @@ module Geokit
350
372
  return GeoLoc.new
351
373
  end
352
374
  end
375
+
376
+ class GeonamesGeocoder < Geocoder
377
+
378
+ private
379
+
380
+ # Template method which does the geocode lookup.
381
+ def self.do_geocode(address)
382
+ address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
383
+ # geonames need a space seperated search string
384
+ address_str.gsub!(/,/, " ")
385
+ params = "/postalCodeSearch?placename=#{Geokit::Inflector::url_escape(address_str)}&maxRows=10"
386
+
387
+ if(GeoKit::Geocoders::geonames)
388
+ url = "http://ws.geonames.net#{params}&username=#{GeoKit::Geocoders::geonames}"
389
+ else
390
+ url = "http://ws.geonames.org#{params}"
391
+ end
392
+
393
+ res = self.call_geocoder_service(url)
394
+
395
+ return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
396
+
397
+ xml=res.body
398
+ logger.debug "Geonames geocoding. Address: #{address}. Result: #{xml}"
399
+ doc=REXML::Document.new(xml)
400
+
401
+ if(doc.elements['//geonames/totalResultsCount'].text.to_i > 0)
402
+ res=GeoLoc.new
403
+
404
+ # only take the first result
405
+ res.lat=doc.elements['//code/lat'].text if doc.elements['//code/lat']
406
+ res.lng=doc.elements['//code/lng'].text if doc.elements['//code/lng']
407
+ res.country_code=doc.elements['//code/countryCode'].text if doc.elements['//code/countryCode']
408
+ res.provider='genomes'
409
+ res.city=doc.elements['//code/name'].text if doc.elements['//code/name']
410
+ res.state=doc.elements['//code/adminName1'].text if doc.elements['//code/adminName1']
411
+ res.zip=doc.elements['//code/postalcode'].text if doc.elements['//code/postalcode']
412
+ res.success=true
413
+ return res
414
+ else
415
+ logger.info "Geonames was unable to geocode address: "+address
416
+ return GeoLoc.new
417
+ end
418
+
419
+ rescue
420
+ logger.error "Caught an error during Geonames geocoding call: "+$!
421
+ end
422
+ end
353
423
 
354
424
  # Provides methods to geocode with a variety of geocoding service providers, plus failover
355
425
  # among providers in the order you configure.
@@ -353,7 +353,7 @@ module Geokit
353
353
 
354
354
  # provide sw and ne to instantiate a new Bounds instance
355
355
  def initialize(sw,ne)
356
- raise ArguementError if !(sw.is_a?(Geokit::LatLng) && ne.is_a?(Geokit::LatLng))
356
+ raise ArgumentError if !(sw.is_a?(Geokit::LatLng) && ne.is_a?(Geokit::LatLng))
357
357
  @sw,@ne=sw,ne
358
358
  end
359
359
 
@@ -410,7 +410,7 @@ module Geokit
410
410
  Geokit::Bounds.new(sw,ne)
411
411
  end
412
412
 
413
- # Takes two main combinations of arguements to create a bounds:
413
+ # Takes two main combinations of arguments to create a bounds:
414
414
  # point,point (this is the only one which takes two arguments
415
415
  # [point,point]
416
416
  # . . . where a point is anything LatLng#normalize can handle (which is quite a lot)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: andre-geokit
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andre Lewis and Bill Eisenhauer
@@ -29,17 +29,15 @@ executables: []
29
29
  extensions: []
30
30
 
31
31
  extra_rdoc_files:
32
- - History.txt
33
32
  - Manifest.txt
34
33
  - README.markdown
35
34
  files:
36
- - History.txt
37
35
  - Manifest.txt
38
36
  - README.markdown
39
37
  - Rakefile
40
- - lib/geocoders.rb
38
+ - lib/geokit/geocoders.rb
41
39
  - lib/geokit.rb
42
- - lib/mappable.rb
40
+ - lib/geokit/mappable.rb
43
41
  - test/test_base_geocoder.rb
44
42
  - test/test_bounds.rb
45
43
  - test/test_ca_geocoder.rb
@@ -1,5 +0,0 @@
1
- === 1.0.0 / 2008-11-30
2
-
3
- * Extracted geocoding and mappable functionality and tests from original GeoKit Rails plugin.
4
-
5
-