geocoder 0.9.11 → 0.9.12

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of geocoder might be problematic. Click here for more details.

@@ -20,9 +20,23 @@ module Geocoder
20
20
  # "205.128.54.202") for geocoding, or coordinates (latitude, longitude)
21
21
  # for reverse geocoding. Returns an array of <tt>Geocoder::Result</tt>s.
22
22
  #
23
- def search(*args)
24
- reverse = (args.size == 2) || coordinates?(args.first)
25
- results(args.join(","), reverse).map{ |r| result_class.new(r) }
23
+ def search(query, *args)
24
+ # convert coordinates as separate arguments to an array
25
+ if query.is_a?(Numeric) and args.first.is_a?(Numeric)
26
+ warn "DEPRECATION WARNING: Instead of passing latitude/longitude as separate arguments to the search method, please pass an array: [#{query},#{args.first}]. The old argument format will not be supported in Geocoder v.1.0."
27
+ query = [query, args.first]
28
+ end
29
+
30
+ # if coordinates given as string, turn into array
31
+ query = query.split(/\s*,\s*/) if coordinates?(query)
32
+
33
+ if query.is_a?(Array)
34
+ reverse = true
35
+ query = query.join(',')
36
+ else
37
+ reverse = false
38
+ end
39
+ results(query, reverse).map{ |r| result_class.new(r) }
26
40
  end
27
41
 
28
42
 
@@ -113,14 +127,14 @@ module Geocoder
113
127
  # Is the given string a loopback IP address?
114
128
  #
115
129
  def loopback_address?(ip)
116
- !!(ip == "0.0.0.0" or ip.match(/^127/))
130
+ !!(ip == "0.0.0.0" or ip.to_s.match(/^127/))
117
131
  end
118
132
 
119
133
  ##
120
134
  # Does the given string look like latitude/longitude coordinates?
121
135
  #
122
136
  def coordinates?(value)
123
- !!value.to_s.match(/^[0-9\.\-]+, ?[0-9\.\-]+$/)
137
+ !!value.to_s.match(/^[0-9\.\-]+, *[0-9\.\-]+$/)
124
138
  end
125
139
 
126
140
  ##
@@ -0,0 +1,41 @@
1
+ require 'geocoder/models/base'
2
+
3
+ module Geocoder
4
+ module Model
5
+ module ActiveRecord
6
+ include Base
7
+
8
+ ##
9
+ # Set attribute names and include the Geocoder module.
10
+ #
11
+ def geocoded_by(address_attr, options = {}, &block)
12
+ geocoder_init(
13
+ :geocode => true,
14
+ :user_address => address_attr,
15
+ :latitude => options[:latitude] || :latitude,
16
+ :longitude => options[:longitude] || :longitude,
17
+ :geocode_block => block
18
+ )
19
+ end
20
+
21
+ ##
22
+ # Set attribute names and include the Geocoder module.
23
+ #
24
+ def reverse_geocoded_by(latitude_attr, longitude_attr, options = {}, &block)
25
+ geocoder_init(
26
+ :reverse_geocode => true,
27
+ :fetched_address => options[:address] || :address,
28
+ :latitude => latitude_attr,
29
+ :longitude => longitude_attr,
30
+ :reverse_block => block
31
+ )
32
+ end
33
+
34
+
35
+ private # --------------------------------------------------------------
36
+
37
+ def geocoder_file_name; "active_record"; end
38
+ def geocoder_module_name; "ActiveRecord"; end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,44 @@
1
+ require 'geocoder'
2
+
3
+ module Geocoder
4
+
5
+ ##
6
+ # Methods for invoking Geocoder in a model.
7
+ #
8
+ module Model
9
+ module Base
10
+
11
+ def geocoder_options
12
+ @geocoder_options
13
+ end
14
+
15
+ def geocoded_by
16
+ fail
17
+ end
18
+
19
+ def reverse_geocoded_by
20
+ fail
21
+ end
22
+
23
+
24
+ private # ----------------------------------------------------------------
25
+
26
+ def geocoder_init(options)
27
+ unless geocoder_initialized?
28
+ @geocoder_options = {}
29
+ require "geocoder/stores/#{geocoder_file_name}"
30
+ include eval("Geocoder::Store::" + geocoder_module_name)
31
+ end
32
+ @geocoder_options.merge! options
33
+ end
34
+
35
+ def geocoder_initialized?
36
+ begin
37
+ included_modules.include? eval("Geocoder::Store::" + geocoder_module_name)
38
+ rescue NameError
39
+ false
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,47 @@
1
+ require 'geocoder/models/base'
2
+
3
+ module Geocoder
4
+ module Model
5
+ module Mongoid
6
+ include Base
7
+
8
+ def self.included(base); base.extend(self); end
9
+
10
+ ##
11
+ # Set attribute names and include the Geocoder module.
12
+ #
13
+ def geocoded_by(address_attr, options = {}, &block)
14
+ geocoder_init(
15
+ :geocode => true,
16
+ :user_address => address_attr,
17
+ :coordinates => options[:coordinates] || :coordinates,
18
+ :geocode_block => block
19
+ )
20
+ end
21
+
22
+ ##
23
+ # Set attribute names and include the Geocoder module.
24
+ #
25
+ def reverse_geocoded_by(coordinates_attr, options = {}, &block)
26
+ geocoder_init(
27
+ :reverse_geocode => true,
28
+ :fetched_address => options[:address] || :address,
29
+ :coordinates => coordinates_attr,
30
+ :reverse_block => block
31
+ )
32
+ end
33
+
34
+
35
+ private # --------------------------------------------------------------
36
+
37
+ def geocoder_file_name; "mongoid"; end
38
+ def geocoder_module_name; "Mongoid"; end
39
+
40
+ def geocoder_init(options)
41
+ super(options)
42
+ index [[ geocoder_options[:coordinates], Mongo::GEO2D ]],
43
+ :min => -180, :max => 180 # create 2d index
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,4 +1,5 @@
1
1
  require 'geocoder'
2
+ require 'geocoder/models/active_record'
2
3
 
3
4
  module Geocoder
4
5
  if defined? Rails::Railtie
@@ -17,63 +18,8 @@ module Geocoder
17
18
 
18
19
  class Railtie
19
20
  def self.insert
20
- return unless defined?(::ActiveRecord)
21
- ::ActiveRecord::Base.extend(ModelMethods)
22
- end
23
- end
24
-
25
- ##
26
- # Methods available in the model class before Geocoder is invoked.
27
- #
28
- module ModelMethods
29
-
30
- ##
31
- # Set attribute names and include the Geocoder module.
32
- #
33
- def geocoded_by(address_attr, options = {}, &block)
34
- geocoder_init(
35
- :geocode => true,
36
- :user_address => address_attr,
37
- :latitude => options[:latitude] || :latitude,
38
- :longitude => options[:longitude] || :longitude,
39
- :geocode_block => block
40
- )
41
- end
42
-
43
- ##
44
- # Set attribute names and include the Geocoder module.
45
- #
46
- def reverse_geocoded_by(latitude_attr, longitude_attr, options = {}, &block)
47
- geocoder_init(
48
- :reverse_geocode => true,
49
- :fetched_address => options[:address] || :address,
50
- :latitude => latitude_attr,
51
- :longitude => longitude_attr,
52
- :reverse_block => block
53
- )
54
- end
55
-
56
- def geocoder_options
57
- @geocoder_options
58
- end
59
-
60
-
61
- private # ----------------------------------------------------------------
62
-
63
- def geocoder_init(options)
64
- unless geocoder_initialized?
65
- @geocoder_options = {}
66
- require 'geocoder/orms/active_record'
67
- include Geocoder::Orm::ActiveRecord
68
- end
69
- @geocoder_options.merge! options
70
- end
71
-
72
- def geocoder_initialized?
73
- begin
74
- included_modules.include? Geocoder::Orm::ActiveRecord
75
- rescue NameError
76
- false
21
+ if defined?(::ActiveRecord)
22
+ ::ActiveRecord::Base.extend(Model::ActiveRecord)
77
23
  end
78
24
  end
79
25
  end
@@ -1,10 +1,10 @@
1
- require 'geocoder/orms/base'
2
- require 'geocoder/orms/active_record_legacy'
1
+ require 'geocoder/stores/base'
2
+ require 'geocoder/stores/active_record_legacy'
3
3
 
4
4
  ##
5
5
  # Add geocoding functionality to any ActiveRecord object.
6
6
  #
7
- module Geocoder::Orm
7
+ module Geocoder::Store
8
8
  module ActiveRecord
9
9
  include Base
10
10
  include ActiveRecord::Legacy
@@ -34,8 +34,7 @@ module Geocoder::Orm
34
34
  # for details).
35
35
  #
36
36
  scope :near, lambda{ |location, *args|
37
- latitude, longitude = location.is_a?(Array) ?
38
- location : Geocoder.coordinates(location)
37
+ latitude, longitude = Geocoder::Calculations.extract_coordinates(location)
39
38
  if latitude and longitude
40
39
  near_scope_options(latitude, longitude, *args)
41
40
  else
@@ -50,6 +49,8 @@ module Geocoder::Orm
50
49
  #
51
50
  module ClassMethods
52
51
 
52
+ private # ----------------------------------------------------------------
53
+
53
54
  ##
54
55
  # Get options hash suitable for passing to ActiveRecord.find to get
55
56
  # records within a radius (in miles) of the given point.
@@ -63,9 +64,7 @@ module Geocoder::Orm
63
64
  # between the given point and each found nearby point;
64
65
  # set to false for no bearing calculation
65
66
  # * +:select+ - string with the SELECT SQL fragment (e.g. “id, name”)
66
- # * +:order+ - column(s) for ORDER BY SQL clause
67
- # * +:limit+ - number of records to return (for LIMIT SQL clause)
68
- # * +:offset+ - number of records to skip (for OFFSET SQL clause)
67
+ # * +:order+ - column(s) for ORDER BY SQL clause; default is distance
69
68
  # * +:exclude+ - an object to exclude (used by the +nearbys+ method)
70
69
  #
71
70
  def near_scope_options(latitude, longitude, radius = 20, options = {})
@@ -77,9 +76,6 @@ module Geocoder::Orm
77
76
  end
78
77
  end
79
78
 
80
-
81
- private # ----------------------------------------------------------------
82
-
83
79
  ##
84
80
  # Scope options hash for use with a database that supports POWER(),
85
81
  # SQRT(), PI(), and trigonometric functions SIN(), COS(), ASIN(),
@@ -157,12 +153,16 @@ module Geocoder::Orm
157
153
  dx = Geocoder::Calculations.longitude_degree_distance(30, options[:units] || :mi)
158
154
  dy = Geocoder::Calculations.latitude_degree_distance(options[:units] || :mi)
159
155
 
160
- distance = "(#{dy} * ABS(#{lat_attr} - #{latitude}) / 2) + " +
161
- "(#{dx} * ABS(#{lon_attr} - #{longitude}) / 2)"
156
+ # sin of 45 degrees = average x or y component of vector
157
+ factor = Math.sin(Math::PI / 4)
158
+
159
+ distance = "(#{dy} * ABS(#{lat_attr} - #{latitude}) * #{factor}) + " +
160
+ "(#{dx} * ABS(#{lon_attr} - #{longitude}) * #{factor})"
162
161
  default_near_scope_options(latitude, longitude, radius, options).merge(
163
162
  :select => "#{options[:select] || '*'}, " +
164
163
  "#{distance} AS distance" +
165
- (bearing ? ", #{bearing} AS bearing" : "")
164
+ (bearing ? ", #{bearing} AS bearing" : ""),
165
+ :order => distance
166
166
  )
167
167
  end
168
168
 
@@ -172,7 +172,7 @@ module Geocoder::Orm
172
172
  def default_near_scope_options(latitude, longitude, radius, options)
173
173
  lat_attr = geocoder_options[:latitude]
174
174
  lon_attr = geocoder_options[:longitude]
175
- b = Geocoder::Calculations.bounding_box(latitude, longitude, radius, options)
175
+ b = Geocoder::Calculations.bounding_box([latitude, longitude], radius, options)
176
176
  conditions = \
177
177
  ["#{lat_attr} BETWEEN ? AND ? AND #{lon_attr} BETWEEN ? AND ?"] +
178
178
  [b[0], b[2], b[1], b[3]]
@@ -180,6 +180,9 @@ module Geocoder::Orm
180
180
  conditions[0] << " AND #{table_name}.id != ?"
181
181
  conditions << obj.id
182
182
  end
183
+ if options[:limit] || options[:offset]
184
+ warn "DEPRECATION WARNING: The :limit and :offset options to Geocoder's 'near' method are deprecated and will be removed in Geocoder v1.0. Please specify these options using ARel relations instead, for example: Place.near(...).limit(10).offset(20)."
185
+ end
183
186
  {
184
187
  :group => columns.map{ |c| "#{table_name}.#{c.name}" }.join(','),
185
188
  :order => options[:order],
@@ -1,4 +1,4 @@
1
- module Geocoder::Orm::ActiveRecord
1
+ module Geocoder::Store::ActiveRecord
2
2
  module Legacy
3
3
 
4
4
  ##
@@ -1,5 +1,5 @@
1
1
  module Geocoder
2
- module Orm
2
+ module Store
3
3
  module Base
4
4
 
5
5
  ##
@@ -18,17 +18,44 @@ module Geocoder
18
18
 
19
19
  ##
20
20
  # Calculate the distance from the object to an arbitrary point.
21
- # Takes two floats (latitude, longitude) and a symbol specifying the
22
- # units to be used (:mi or :km; default is :mi).
21
+ # See Geocoder::Calculations.distance_between for ways of specifying
22
+ # the point. Also takes a symbol specifying the units
23
+ # (:mi or :km; default is :mi).
23
24
  #
24
- def distance_to(lat, lon, units = :mi)
25
+ def distance_to(point, *args)
26
+ if point.is_a?(Numeric) and args[0].is_a?(Numeric)
27
+ warn "DEPRECATION WARNING: Instead of passing latitude/longitude as separate arguments to the distance_to/from method, please pass an array [#{point},#{args[0]}], a geocoded object, or a geocodable address (string). The old argument format will not be supported in Geocoder v.1.0."
28
+ point = [point, args.shift]
29
+ end
25
30
  return nil unless geocoded?
26
- mylat,mylon = to_coordinates
27
- Geocoder::Calculations.distance_between(mylat, mylon, lat, lon, :units => units)
31
+ Geocoder::Calculations.distance_between(
32
+ to_coordinates, point, :units => args.pop || :mi)
28
33
  end
29
34
 
30
35
  alias_method :distance_from, :distance_to
31
36
 
37
+ ##
38
+ # Calculate the bearing from the object to another point.
39
+ # See Geocoder::Calculations.distance_between for
40
+ # ways of specifying the point.
41
+ #
42
+ def bearing_to(point, options = {})
43
+ return nil unless geocoded?
44
+ Geocoder::Calculations.bearing_between(
45
+ to_coordinates, point, options)
46
+ end
47
+
48
+ ##
49
+ # Calculate the bearing from another point to the object.
50
+ # See Geocoder::Calculations.distance_between for
51
+ # ways of specifying the point.
52
+ #
53
+ def bearing_from(point, options = {})
54
+ return nil unless geocoded?
55
+ Geocoder::Calculations.bearing_between(
56
+ point, to_coordinates, options)
57
+ end
58
+
32
59
  ##
33
60
  # Get nearby geocoded objects.
34
61
  # Takes the same options hash as the near class method (scope).
@@ -40,7 +67,7 @@ module Geocoder
40
67
  warn "DEPRECATION WARNING: The units argument to the nearbys method has been replaced with an options hash (same options hash as the near scope). You should instead call: obj.nearbys(#{radius}, :units => #{options[:units]}). The old syntax will not be supported in Geocoder v1.0."
41
68
  end
42
69
  options.merge!(:exclude => self)
43
- self.class.near(to_coordinates, radius, options)
70
+ self.class.near(self, radius, options)
44
71
  end
45
72
 
46
73
  ##
@@ -72,15 +99,14 @@ module Geocoder
72
99
  def do_lookup(reverse = false)
73
100
  options = self.class.geocoder_options
74
101
  if reverse and options[:reverse_geocode]
75
- args = [:latitude, :longitude]
102
+ query = to_coordinates
76
103
  elsif !reverse and options[:geocode]
77
- args = [:user_address]
104
+ query = send(options[:user_address])
78
105
  else
79
106
  return
80
107
  end
81
- args.map!{ |a| send(options[a]) }
82
108
 
83
- if (results = Geocoder.search(*args)).size > 0
109
+ if (results = Geocoder.search(query)).size > 0
84
110
 
85
111
  # execute custom block, if specified in configuration
86
112
  block_key = reverse ? :reverse_block : :geocode_block
@@ -0,0 +1,73 @@
1
+ require 'geocoder/stores/base'
2
+
3
+ module Geocoder::Store
4
+ module Mongoid
5
+ include Base
6
+
7
+ def self.included(base)
8
+ base.class_eval do
9
+
10
+ scope :geocoded, lambda {
11
+ where(geocoder_options[:coordinates].ne => nil)
12
+ }
13
+
14
+ scope :not_geocoded, lambda {
15
+ where(geocoder_options[:coordinates] => nil)
16
+ }
17
+
18
+ scope :near, lambda{ |location, *args|
19
+ coords = Geocoder::Calculations.extract_coordinates(location)
20
+ radius = args.size > 0 ? args.shift : 20
21
+ options = args.size > 0 ? args.shift : {}
22
+ conds = {:coordinates => {
23
+ "$nearSphere" => coords.reverse,
24
+ "$maxDistance" => Geocoder::Calculations.distance_to_radians(
25
+ radius, options[:units] || :mi)
26
+ }}
27
+ if obj = options[:exclude]
28
+ conds[:_id.ne] = obj.id
29
+ end
30
+ criteria.where(conds)
31
+ }
32
+ end
33
+ end
34
+
35
+ ##
36
+ # Coordinates [lat,lon] of the object.
37
+ # This method always returns coordinates in lat,lon order,
38
+ # even though internally they are stored in the opposite order.
39
+ #
40
+ def to_coordinates
41
+ coords = send(self.class.geocoder_options[:coordinates])
42
+ coords.is_a?(Array) ? coords.reverse : []
43
+ end
44
+
45
+ ##
46
+ # Look up coordinates and assign to +latitude+ and +longitude+ attributes
47
+ # (or other as specified in +geocoded_by+). Returns coordinates (array).
48
+ #
49
+ def geocode
50
+ do_lookup(false) do |o,rs|
51
+ r = rs.first
52
+ unless r.coordinates.nil?
53
+ o.send :write_attribute, self.class.geocoder_options[:coordinates], r.coordinates.reverse
54
+ end
55
+ r.coordinates
56
+ end
57
+ end
58
+
59
+ ##
60
+ # Look up address and assign to +address+ attribute (or other as specified
61
+ # in +reverse_geocoded_by+). Returns address (string).
62
+ #
63
+ def reverse_geocode
64
+ do_lookup(true) do |o,rs|
65
+ r = rs.first
66
+ unless r.address.nil?
67
+ o.send :write_attribute, self.class.geocoder_options[:fetched_address], r.address
68
+ end
69
+ r.address
70
+ end
71
+ end
72
+ end
73
+ end