forked from alexreisner/geocoder
/
base.rb
127 lines (112 loc) · 3.99 KB
/
base.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
module Geocoder
module Store
module Base
##
# Is this object geocoded? (Does it have latitude and longitude?)
#
def geocoded?
to_coordinates.compact.size > 0
end
##
# Coordinates [lat,lon] of the object.
#
def to_coordinates
[:latitude, :longitude].map{ |i| send self.class.geocoder_options[i] }
end
##
# Calculate the distance from the object to an arbitrary point.
# See Geocoder::Calculations.distance_between for ways of specifying
# the point. Also takes a symbol specifying the units
# (:mi or :km; can be specified in Geocoder configuration).
#
def distance_to(point, units = nil)
units ||= self.class.geocoder_options[:units]
return nil unless geocoded?
Geocoder::Calculations.distance_between(
to_coordinates, point, :units => units)
end
alias_method :distance_from, :distance_to
##
# Calculate the bearing from the object to another point.
# See Geocoder::Calculations.distance_between for
# ways of specifying the point.
#
def bearing_to(point, options = {})
options[:method] ||= self.class.geocoder_options[:method]
return nil unless geocoded?
Geocoder::Calculations.bearing_between(
to_coordinates, point, options)
end
##
# Calculate the bearing from another point to the object.
# See Geocoder::Calculations.distance_between for
# ways of specifying the point.
#
def bearing_from(point, options = {})
options[:method] ||= self.class.geocoder_options[:method]
return nil unless geocoded?
Geocoder::Calculations.bearing_between(
point, to_coordinates, options)
end
##
# Get nearby geocoded objects.
# Takes the same options hash as the near class method (scope).
# Returns nil if the object is not geocoded.
#
def nearbys(radius = 20, options = {})
return nil unless geocoded?
options.merge!(:exclude => self) unless send(self.class.primary_key).nil?
self.class.near(self, radius, options)
end
##
# Look up coordinates and assign to +latitude+ and +longitude+ attributes
# (or other as specified in +geocoded_by+). Returns coordinates (array).
#
def geocode
fail
end
##
# Look up address and assign to +address+ attribute (or other as specified
# in +reverse_geocoded_by+). Returns address (string).
#
def reverse_geocode
fail
end
private # --------------------------------------------------------------
##
# Look up geographic data based on object attributes (configured in
# geocoded_by or reverse_geocoded_by) and handle the results with the
# block (given to geocoded_by or reverse_geocoded_by). The block is
# given two-arguments: the object being geocoded and an array of
# Geocoder::Result objects).
#
def do_lookup(reverse = false)
options = self.class.geocoder_options
if reverse and options[:reverse_geocode]
query = to_coordinates
elsif !reverse and options[:geocode]
query = send(options[:user_address])
else
return
end
query_options = [:lookup, :ip_lookup].inject({}) do |hash, key|
if options.has_key?(key)
val = options[key]
hash[key] = val.respond_to?(:call) ? val.call(self) : val
end
hash
end
results = Geocoder.search(query, query_options)
# execute custom block, if specified in configuration
block_key = reverse ? :reverse_block : :geocode_block
if custom_block = options[block_key]
custom_block.call(self, results)
# else execute block passed directly to this method,
# which generally performs the "auto-assigns"
elsif block_given?
yield(self, results)
end
end
end
end
end