soey-geokit 1.2.4

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.
@@ -0,0 +1,459 @@
1
+
2
+ module Geokit
3
+ # Contains class and instance methods providing distance calcuation services. This
4
+ # module is meant to be mixed into classes containing lat and lng attributes where
5
+ # distance calculation is desired.
6
+ #
7
+ # At present, two forms of distance calculations are provided:
8
+ #
9
+ # * Pythagorean Theory (flat Earth) - which assumes the world is flat and loses accuracy over long distances.
10
+ # * Haversine (sphere) - which is fairly accurate, but at a performance cost.
11
+ #
12
+ # Distance units supported are :miles, :kms, and :nms.
13
+ module Mappable
14
+ PI_DIV_RAD = 0.0174
15
+ KMS_PER_MILE = 1.609
16
+ NMS_PER_MILE = 0.868976242
17
+ EARTH_RADIUS_IN_MILES = 3963.19
18
+ EARTH_RADIUS_IN_KMS = EARTH_RADIUS_IN_MILES * KMS_PER_MILE
19
+ EARTH_RADIUS_IN_NMS = EARTH_RADIUS_IN_MILES * NMS_PER_MILE
20
+ MILES_PER_LATITUDE_DEGREE = 69.1
21
+ KMS_PER_LATITUDE_DEGREE = MILES_PER_LATITUDE_DEGREE * KMS_PER_MILE
22
+ NMS_PER_LATITUDE_DEGREE = MILES_PER_LATITUDE_DEGREE * NMS_PER_MILE
23
+ LATITUDE_DEGREES = EARTH_RADIUS_IN_MILES / MILES_PER_LATITUDE_DEGREE
24
+
25
+ # Mix below class methods into the includer.
26
+ def self.included(receiver) # :nodoc:
27
+ receiver.extend ClassMethods
28
+ end
29
+
30
+ module ClassMethods #:nodoc:
31
+ # Returns the distance between two points. The from and to parameters are
32
+ # required to have lat and lng attributes. Valid options are:
33
+ # :units - valid values are :miles, :kms, :nms (Geokit::default_units is the default)
34
+ # :formula - valid values are :flat or :sphere (Geokit::default_formula is the default)
35
+ def distance_between(from, to, options={})
36
+ from=Geokit::LatLng.normalize(from)
37
+ to=Geokit::LatLng.normalize(to)
38
+ return 0.0 if from == to # fixes a "zero-distance" bug
39
+ units = options[:units] || Geokit::default_units
40
+ formula = options[:formula] || Geokit::default_formula
41
+ case formula
42
+ when :sphere
43
+ begin
44
+ units_sphere_multiplier(units) *
45
+ Math.acos( Math.sin(deg2rad(from.lat)) * Math.sin(deg2rad(to.lat)) +
46
+ Math.cos(deg2rad(from.lat)) * Math.cos(deg2rad(to.lat)) *
47
+ Math.cos(deg2rad(to.lng) - deg2rad(from.lng)))
48
+ rescue Errno::EDOM
49
+ 0.0
50
+ end
51
+ when :flat
52
+ Math.sqrt((units_per_latitude_degree(units)*(from.lat-to.lat))**2 +
53
+ (units_per_longitude_degree(from.lat, units)*(from.lng-to.lng))**2)
54
+ end
55
+ end
56
+
57
+ # Returns heading in degrees (0 is north, 90 is east, 180 is south, etc)
58
+ # from the first point to the second point. Typicaly, the instance methods will be used
59
+ # instead of this method.
60
+ def heading_between(from,to)
61
+ from=Geokit::LatLng.normalize(from)
62
+ to=Geokit::LatLng.normalize(to)
63
+
64
+ d_lng=deg2rad(to.lng-from.lng)
65
+ from_lat=deg2rad(from.lat)
66
+ to_lat=deg2rad(to.lat)
67
+ y=Math.sin(d_lng) * Math.cos(to_lat)
68
+ x=Math.cos(from_lat)*Math.sin(to_lat)-Math.sin(from_lat)*Math.cos(to_lat)*Math.cos(d_lng)
69
+ heading=to_heading(Math.atan2(y,x))
70
+ end
71
+
72
+ # Given a start point, distance, and heading (in degrees), provides
73
+ # an endpoint. Returns a LatLng instance. Typically, the instance method
74
+ # will be used instead of this method.
75
+ def endpoint(start,heading, distance, options={})
76
+ units = options[:units] || Geokit::default_units
77
+ radius = case units
78
+ when :kms; EARTH_RADIUS_IN_KMS
79
+ when :nms; EARTH_RADIUS_IN_NMS
80
+ else EARTH_RADIUS_IN_MILES
81
+ end
82
+ start=Geokit::LatLng.normalize(start)
83
+ lat=deg2rad(start.lat)
84
+ lng=deg2rad(start.lng)
85
+ heading=deg2rad(heading)
86
+ distance=distance.to_f
87
+
88
+ end_lat=Math.asin(Math.sin(lat)*Math.cos(distance/radius) +
89
+ Math.cos(lat)*Math.sin(distance/radius)*Math.cos(heading))
90
+
91
+ end_lng=lng+Math.atan2(Math.sin(heading)*Math.sin(distance/radius)*Math.cos(lat),
92
+ Math.cos(distance/radius)-Math.sin(lat)*Math.sin(end_lat))
93
+
94
+ LatLng.new(rad2deg(end_lat),rad2deg(end_lng))
95
+ end
96
+
97
+ # Returns the midpoint, given two points. Returns a LatLng.
98
+ # Typically, the instance method will be used instead of this method.
99
+ # Valid option:
100
+ # :units - valid values are :miles, :kms, or :nms (:miles is the default)
101
+ def midpoint_between(from,to,options={})
102
+ from=Geokit::LatLng.normalize(from)
103
+
104
+ units = options[:units] || Geokit::default_units
105
+
106
+ heading=from.heading_to(to)
107
+ distance=from.distance_to(to,options)
108
+ midpoint=from.endpoint(heading,distance/2,options)
109
+ end
110
+
111
+ # Geocodes a location using the multi geocoder.
112
+ def geocode(location)
113
+ res = Geocoders::MultiGeocoder.geocode(location)
114
+ return res if res.success
115
+ raise Geokit::Geocoders::GeocodeError
116
+ end
117
+
118
+ protected
119
+
120
+ def deg2rad(degrees)
121
+ degrees.to_f / 180.0 * Math::PI
122
+ end
123
+
124
+ def rad2deg(rad)
125
+ rad.to_f * 180.0 / Math::PI
126
+ end
127
+
128
+ def to_heading(rad)
129
+ (rad2deg(rad)+360)%360
130
+ end
131
+
132
+ # Returns the multiplier used to obtain the correct distance units.
133
+ def units_sphere_multiplier(units)
134
+ case units
135
+ when :kms; EARTH_RADIUS_IN_KMS
136
+ when :nms; EARTH_RADIUS_IN_NMS
137
+ else EARTH_RADIUS_IN_MILES
138
+ end
139
+ end
140
+
141
+ # Returns the number of units per latitude degree.
142
+ def units_per_latitude_degree(units)
143
+ case units
144
+ when :kms; KMS_PER_LATITUDE_DEGREE
145
+ when :nms; NMS_PER_LATITUDE_DEGREE
146
+ else MILES_PER_LATITUDE_DEGREE
147
+ end
148
+ end
149
+
150
+ # Returns the number units per longitude degree.
151
+ def units_per_longitude_degree(lat, units)
152
+ miles_per_longitude_degree = (LATITUDE_DEGREES * Math.cos(lat * PI_DIV_RAD)).abs
153
+ case units
154
+ when :kms; miles_per_longitude_degree * KMS_PER_MILE
155
+ when :nms; miles_per_longitude_degree * NMS_PER_MILE
156
+ else miles_per_longitude_degree
157
+ end
158
+ end
159
+ end
160
+
161
+ # -----------------------------------------------------------------------------------------------
162
+ # Instance methods below here
163
+ # -----------------------------------------------------------------------------------------------
164
+
165
+ # Extracts a LatLng instance. Use with models that are acts_as_mappable
166
+ def to_lat_lng
167
+ return self if instance_of?(Geokit::LatLng) || instance_of?(Geokit::GeoLoc)
168
+ return LatLng.new(send(self.class.lat_column_name),send(self.class.lng_column_name)) if self.class.respond_to?(:acts_as_mappable)
169
+ nil
170
+ end
171
+
172
+ # Returns the distance from another point. The other point parameter is
173
+ # required to have lat and lng attributes. Valid options are:
174
+ # :units - valid values are :miles, :kms, :or :nms (:miles is the default)
175
+ # :formula - valid values are :flat or :sphere (:sphere is the default)
176
+ def distance_to(other, options={})
177
+ self.class.distance_between(self, other, options)
178
+ end
179
+ alias distance_from distance_to
180
+
181
+ # Returns heading in degrees (0 is north, 90 is east, 180 is south, etc)
182
+ # to the given point. The given point can be a LatLng or a string to be Geocoded
183
+ def heading_to(other)
184
+ self.class.heading_between(self,other)
185
+ end
186
+
187
+ # Returns heading in degrees (0 is north, 90 is east, 180 is south, etc)
188
+ # FROM the given point. The given point can be a LatLng or a string to be Geocoded
189
+ def heading_from(other)
190
+ self.class.heading_between(other,self)
191
+ end
192
+
193
+ # Returns the endpoint, given a heading (in degrees) and distance.
194
+ # Valid option:
195
+ # :units - valid values are :miles, :kms, or :nms (:miles is the default)
196
+ def endpoint(heading,distance,options={})
197
+ self.class.endpoint(self,heading,distance,options)
198
+ end
199
+
200
+ # Returns the midpoint, given another point on the map.
201
+ # Valid option:
202
+ # :units - valid values are :miles, :kms, or :nms (:miles is the default)
203
+ def midpoint_to(other, options={})
204
+ self.class.midpoint_between(self,other,options)
205
+ end
206
+
207
+ end
208
+
209
+ class LatLng
210
+ include Mappable
211
+
212
+ attr_accessor :lat, :lng
213
+
214
+ # Accepts latitude and longitude or instantiates an empty instance
215
+ # if lat and lng are not provided. Converted to floats if provided
216
+ def initialize(lat=nil, lng=nil)
217
+ lat = lat.to_f if lat && !lat.is_a?(Numeric)
218
+ lng = lng.to_f if lng && !lng.is_a?(Numeric)
219
+ @lat = lat
220
+ @lng = lng
221
+ end
222
+
223
+ # Latitude attribute setter; stored as a float.
224
+ def lat=(lat)
225
+ @lat = lat.to_f if lat
226
+ end
227
+
228
+ # Longitude attribute setter; stored as a float;
229
+ def lng=(lng)
230
+ @lng=lng.to_f if lng
231
+ end
232
+
233
+ # Returns the lat and lng attributes as a comma-separated string.
234
+ def ll
235
+ "#{lat},#{lng}"
236
+ end
237
+
238
+ #returns a string with comma-separated lat,lng values
239
+ def to_s
240
+ ll
241
+ end
242
+
243
+ #returns a two-element array
244
+ def to_a
245
+ [lat,lng]
246
+ end
247
+ # Returns true if the candidate object is logically equal. Logical equivalence
248
+ # is true if the lat and lng attributes are the same for both objects.
249
+ def ==(other)
250
+ other.is_a?(LatLng) ? self.lat == other.lat && self.lng == other.lng : false
251
+ end
252
+
253
+ # A *class* method to take anything which can be inferred as a point and generate
254
+ # a LatLng from it. You should use this anything you're not sure what the input is,
255
+ # and want to deal with it as a LatLng if at all possible. Can take:
256
+ # 1) two arguments (lat,lng)
257
+ # 2) a string in the format "37.1234,-129.1234" or "37.1234 -129.1234"
258
+ # 3) a string which can be geocoded on the fly
259
+ # 4) an array in the format [37.1234,-129.1234]
260
+ # 5) a LatLng or GeoLoc (which is just passed through as-is)
261
+ # 6) anything which acts_as_mappable -- a LatLng will be extracted from it
262
+ def self.normalize(thing,other=nil)
263
+ # if an 'other' thing is supplied, normalize the input by creating an array of two elements
264
+ thing=[thing,other] if other
265
+
266
+ if thing.is_a?(String)
267
+ thing.strip!
268
+ if match=thing.match(/(\-?\d+\.?\d*)[, ] ?(\-?\d+\.?\d*)$/)
269
+ return Geokit::LatLng.new(match[1],match[2])
270
+ else
271
+ res = Geokit::Geocoders::MultiGeocoder.geocode(thing)
272
+ return res if res.success
273
+ raise Geokit::Geocoders::GeocodeError
274
+ end
275
+ elsif thing.is_a?(Array) && thing.size==2
276
+ return Geokit::LatLng.new(thing[0],thing[1])
277
+ elsif thing.is_a?(LatLng) # will also be true for GeoLocs
278
+ return thing
279
+ elsif thing.class.respond_to?(:acts_as_mappable) && thing.class.respond_to?(:distance_column_name)
280
+ return thing.to_lat_lng
281
+ end
282
+
283
+ raise ArgumentError.new("#{thing} (#{thing.class}) cannot be normalized to a LatLng. We tried interpreting it as an array, string, Mappable, etc., but no dice.")
284
+ end
285
+
286
+ end
287
+
288
+ # This class encapsulates the result of a geocoding call
289
+ # It's primary purpose is to homogenize the results of multiple
290
+ # geocoding providers. It also provides some additional functionality, such as
291
+ # the "full address" method for geocoders that do not provide a
292
+ # full address in their results (for example, Yahoo), and the "is_us" method.
293
+ class GeoLoc < LatLng
294
+ # Location attributes. Full address is a concatenation of all values. For example:
295
+ # 100 Spear St, San Francisco, CA, 94101, US
296
+ attr_accessor :street_address, :city, :state, :zip, :country_code, :full_address, :bounds
297
+ # Attributes set upon return from geocoding. Success will be true for successful
298
+ # geocode lookups. The provider will be set to the name of the providing geocoder.
299
+ # Finally, precision is an indicator of the accuracy of the geocoding.
300
+ attr_accessor :success, :provider, :precision
301
+ # Street number and street name are extracted from the street address attribute.
302
+ attr_reader :street_number, :street_name
303
+
304
+ # Constructor expects a hash of symbols to correspond with attributes.
305
+ def initialize(h={})
306
+ @street_address=h[:street_address]
307
+ @city=h[:city]
308
+ @state=h[:state]
309
+ @zip=h[:zip]
310
+ @country_code=h[:country_code]
311
+ @success=false
312
+ @precision='unknown'
313
+ @full_address=nil
314
+ super(h[:lat],h[:lng])
315
+ end
316
+
317
+ # Returns true if geocoded to the United States.
318
+ def is_us?
319
+ country_code == 'US'
320
+ end
321
+
322
+ # full_address is provided by google but not by yahoo. It is intended that the google
323
+ # geocoding method will provide the full address, whereas for yahoo it will be derived
324
+ # from the parts of the address we do have.
325
+ def full_address
326
+ @full_address ? @full_address : to_geocodeable_s
327
+ end
328
+
329
+ # Extracts the street number from the street address if the street address
330
+ # has a value.
331
+ def street_number
332
+ street_address[/(\d*)/] if street_address
333
+ end
334
+
335
+ # Returns the street name portion of the street address.
336
+ def street_name
337
+ street_address[street_number.length, street_address.length].strip if street_address
338
+ end
339
+
340
+ # gives you all the important fields as key-value pairs
341
+ def hash
342
+ res={}
343
+ [:success,:lat,:lng,:country_code,:city,:state,:zip,:street_address,:provider,:full_address,:is_us?,:ll,:precision].each { |s| res[s] = self.send(s.to_s) }
344
+ res
345
+ end
346
+ alias to_hash hash
347
+
348
+ # Sets the city after capitalizing each word within the city name.
349
+ def city=(city)
350
+ @city = Geokit::Inflector::titleize(city) if city
351
+ end
352
+
353
+ # Sets the street address after capitalizing each word within the street address.
354
+ def street_address=(address)
355
+ @street_address = Geokit::Inflector::titleize(address) if address
356
+ end
357
+
358
+ def bounds=(hash)
359
+ @bounds= Bounds.normalize([hash[:south], hash[:west]], [hash[:north], hash[:east]] )
360
+ end
361
+
362
+ # Returns a comma-delimited string consisting of the street address, city, state,
363
+ # zip, and country code. Only includes those attributes that are non-blank.
364
+ def to_geocodeable_s
365
+ a=[street_address, city, state, zip, country_code].compact
366
+ a.delete_if { |e| !e || e == '' }
367
+ a.join(', ')
368
+ end
369
+
370
+ # Returns a string representation of the instance.
371
+ def to_s
372
+ "Provider: #{provider}\n Street: #{street_address}\nCity: #{city}\nState: #{state}\nZip: #{zip}\nLatitude: #{lat}\nLongitude: #{lng}\nCountry: #{country_code}\nBounds: #{bounds}\nSuccess: #{success}"
373
+ end
374
+ end
375
+
376
+ # Bounds represents a rectangular bounds, defined by the SW and NE corners
377
+ class Bounds
378
+ # sw and ne are LatLng objects
379
+ attr_accessor :sw, :ne
380
+
381
+ # provide sw and ne to instantiate a new Bounds instance
382
+ def initialize(sw,ne)
383
+ raise ArgumentError if !(sw.is_a?(Geokit::LatLng) && ne.is_a?(Geokit::LatLng))
384
+ @sw,@ne=sw,ne
385
+ end
386
+
387
+ #returns the a single point which is the center of the rectangular bounds
388
+ def center
389
+ @sw.midpoint_to(@ne)
390
+ end
391
+
392
+ # a simple string representation:sw,ne
393
+ def to_s
394
+ "#{@sw.to_s},#{@ne.to_s}"
395
+ end
396
+
397
+ # a two-element array of two-element arrays: sw,ne
398
+ def to_a
399
+ [@sw.to_a, @ne.to_a]
400
+ end
401
+
402
+ # Returns true if the bounds contain the passed point.
403
+ # allows for bounds which cross the meridian
404
+ def contains?(point)
405
+ point=Geokit::LatLng.normalize(point)
406
+ res = point.lat > @sw.lat && point.lat < @ne.lat
407
+ if crosses_meridian?
408
+ res &= point.lng < @ne.lng || point.lng > @sw.lng
409
+ else
410
+ res &= point.lng < @ne.lng && point.lng > @sw.lng
411
+ end
412
+ res
413
+ end
414
+
415
+ # returns true if the bounds crosses the international dateline
416
+ def crosses_meridian?
417
+ @sw.lng > @ne.lng
418
+ end
419
+
420
+ # Returns true if the candidate object is logically equal. Logical equivalence
421
+ # is true if the lat and lng attributes are the same for both objects.
422
+ def ==(other)
423
+ other.is_a?(Bounds) ? self.sw == other.sw && self.ne == other.ne : false
424
+ end
425
+
426
+ class <<self
427
+
428
+ # returns an instance of bounds which completely encompases the given circle
429
+ def from_point_and_radius(point,radius,options={})
430
+ point=LatLng.normalize(point)
431
+ p0=point.endpoint(0,radius,options)
432
+ p90=point.endpoint(90,radius,options)
433
+ p180=point.endpoint(180,radius,options)
434
+ p270=point.endpoint(270,radius,options)
435
+ sw=Geokit::LatLng.new(p180.lat,p270.lng)
436
+ ne=Geokit::LatLng.new(p0.lat,p90.lng)
437
+ Geokit::Bounds.new(sw,ne)
438
+ end
439
+
440
+ # Takes two main combinations of arguments to create a bounds:
441
+ # point,point (this is the only one which takes two arguments
442
+ # [point,point]
443
+ # . . . where a point is anything LatLng#normalize can handle (which is quite a lot)
444
+ #
445
+ # NOTE: everything combination is assumed to pass points in the order sw, ne
446
+ def normalize (thing,other=nil)
447
+ # maybe this will be simple -- an actual bounds object is passed, and we can all go home
448
+ return thing if thing.is_a? Bounds
449
+
450
+ # no? OK, if there's no "other," the thing better be a two-element array
451
+ thing,other=thing if !other && thing.is_a?(Array) && thing.size==2
452
+
453
+ # Now that we're set with a thing and another thing, let LatLng do the heavy lifting.
454
+ # Exceptions may be thrown
455
+ Bounds.new(Geokit::LatLng.normalize(thing),Geokit::LatLng.normalize(other))
456
+ end
457
+ end
458
+ end
459
+ end
data/lib/geokit.rb ADDED
@@ -0,0 +1,28 @@
1
+ module Geokit
2
+ VERSION = '1.2.0'
3
+ # These defaults are used in Geokit::Mappable.distance_to and in acts_as_mappable
4
+ @@default_units = :miles
5
+ @@default_formula = :sphere
6
+
7
+ [:default_units, :default_formula].each do |sym|
8
+ class_eval <<-EOS, __FILE__, __LINE__
9
+ def self.#{sym}
10
+ if defined?(#{sym.to_s.upcase})
11
+ #{sym.to_s.upcase}
12
+ else
13
+ @@#{sym}
14
+ end
15
+ end
16
+
17
+ def self.#{sym}=(obj)
18
+ @@#{sym} = obj
19
+ end
20
+ EOS
21
+ end
22
+ end
23
+
24
+ require 'geokit/geocoders'
25
+ require 'geokit/mappable'
26
+
27
+ # make old-style module name "GeoKit" equivilent to new-style "Geokit"
28
+ GeoKit=Geokit
@@ -0,0 +1,56 @@
1
+ require 'test/unit'
2
+ require 'net/http'
3
+ require 'rubygems'
4
+ require 'mocha'
5
+ require 'lib/geokit'
6
+
7
+ class MockSuccess < Net::HTTPSuccess #:nodoc: all
8
+ def initialize
9
+ end
10
+ end
11
+
12
+ class MockFailure < Net::HTTPServiceUnavailable #:nodoc: all
13
+ def initialize
14
+ end
15
+ end
16
+
17
+ # Base class for testing geocoders.
18
+ class BaseGeocoderTest < Test::Unit::TestCase #:nodoc: all
19
+
20
+ # Defines common test fixtures.
21
+ def setup
22
+ @address = 'San Francisco, CA'
23
+ @full_address = '100 Spear St, San Francisco, CA, 94105-1522, US'
24
+ @full_address_short_zip = '100 Spear St, San Francisco, CA, 94105, US'
25
+
26
+ @success = Geokit::GeoLoc.new({:city=>"SAN FRANCISCO", :state=>"CA", :country_code=>"US", :lat=>37.7742, :lng=>-122.417068})
27
+ @success.success = true
28
+ end
29
+
30
+ def test_timeout_call_web_service
31
+ Geokit::Geocoders::Geocoder.class_eval do
32
+ def self.do_get(url)
33
+ sleep(2)
34
+ end
35
+ end
36
+ url = "http://www.anything.com"
37
+ Geokit::Geocoders::timeout = 1
38
+ assert_nil Geokit::Geocoders::Geocoder.call_geocoder_service(url)
39
+ end
40
+
41
+ def test_successful_call_web_service
42
+ url = "http://www.anything.com"
43
+ Geokit::Geocoders::Geocoder.expects(:do_get).with(url).returns("SUCCESS")
44
+ assert_equal "SUCCESS", Geokit::Geocoders::Geocoder.call_geocoder_service(url)
45
+ end
46
+
47
+ def test_find_geocoder_methods
48
+ public_methods = Geokit::Geocoders::Geocoder.public_methods.map { |m| m.to_s }
49
+ assert public_methods.include?("yahoo_geocoder")
50
+ assert public_methods.include?("google_geocoder")
51
+ assert public_methods.include?("ca_geocoder")
52
+ assert public_methods.include?("us_geocoder")
53
+ assert public_methods.include?("multi_geocoder")
54
+ assert public_methods.include?("ip_geocoder")
55
+ end
56
+ end
@@ -0,0 +1,74 @@
1
+ require 'test/unit'
2
+ require 'lib/geokit'
3
+
4
+ class BoundsTest < Test::Unit::TestCase #:nodoc: all
5
+
6
+ def setup
7
+ # This is the area in Texas
8
+ @sw = Geokit::LatLng.new(32.91663,-96.982841)
9
+ @ne = Geokit::LatLng.new(32.96302,-96.919495)
10
+ @bounds=Geokit::Bounds.new(@sw,@ne)
11
+ @loc_a=Geokit::LatLng.new(32.918593,-96.958444) # inside bounds
12
+ @loc_b=Geokit::LatLng.new(32.914144,-96.958444) # outside bouds
13
+
14
+ # this is a cross-meridan area
15
+ @cross_meridian=Geokit::Bounds.normalize([30,170],[40,-170])
16
+ @inside_cm=Geokit::LatLng.new(35,175)
17
+ @inside_cm_2=Geokit::LatLng.new(35,-175)
18
+ @east_of_cm=Geokit::LatLng.new(35,-165)
19
+ @west_of_cm=Geokit::LatLng.new(35,165)
20
+
21
+ end
22
+
23
+ def test_equality
24
+ assert_equal Geokit::Bounds.new(@sw,@ne), Geokit::Bounds.new(@sw,@ne)
25
+ end
26
+
27
+ def test_normalize
28
+ res=Geokit::Bounds.normalize(@sw,@ne)
29
+ assert_equal res,Geokit::Bounds.new(@sw,@ne)
30
+ res=Geokit::Bounds.normalize([@sw,@ne])
31
+ assert_equal res,Geokit::Bounds.new(@sw,@ne)
32
+ res=Geokit::Bounds.normalize([@sw.lat,@sw.lng],[@ne.lat,@ne.lng])
33
+ assert_equal res,Geokit::Bounds.new(@sw,@ne)
34
+ res=Geokit::Bounds.normalize([[@sw.lat,@sw.lng],[@ne.lat,@ne.lng]])
35
+ assert_equal res,Geokit::Bounds.new(@sw,@ne)
36
+ end
37
+
38
+ def test_point_inside_bounds
39
+ assert @bounds.contains?(@loc_a)
40
+ end
41
+
42
+ def test_point_outside_bounds
43
+ assert !@bounds.contains?(@loc_b)
44
+ end
45
+
46
+ def test_point_inside_bounds_cross_meridian
47
+ assert @cross_meridian.contains?(@inside_cm)
48
+ assert @cross_meridian.contains?(@inside_cm_2)
49
+ end
50
+
51
+ def test_point_outside_bounds_cross_meridian
52
+ assert !@cross_meridian.contains?(@east_of_cm)
53
+ assert !@cross_meridian.contains?(@west_of_cm)
54
+ end
55
+
56
+ def test_center
57
+ assert_in_delta 32.939828,@bounds.center.lat,0.00005
58
+ assert_in_delta(-96.9511763,@bounds.center.lng,0.00005)
59
+ end
60
+
61
+ def test_center_cross_meridian
62
+ assert_in_delta 35.41160, @cross_meridian.center.lat,0.00005
63
+ assert_in_delta 179.38112, @cross_meridian.center.lng,0.00005
64
+ end
65
+
66
+ def test_creation_from_circle
67
+ bounds=Geokit::Bounds.from_point_and_radius([32.939829, -96.951176],2.5)
68
+ inside=Geokit::LatLng.new 32.9695270000,-96.9901590000
69
+ outside=Geokit::LatLng.new 32.8951550000,-96.9584440000
70
+ assert bounds.contains?(inside)
71
+ assert !bounds.contains?(outside)
72
+ end
73
+
74
+ end
@@ -0,0 +1,41 @@
1
+ require File.join(File.dirname(__FILE__), 'test_base_geocoder')
2
+
3
+ Geokit::Geocoders::geocoder_ca = "SOMEKEYVALUE"
4
+
5
+ class CaGeocoderTest < BaseGeocoderTest #:nodoc: all
6
+
7
+ CA_SUCCESS=<<-EOF
8
+ <?xml version="1.0" encoding="UTF-8" ?>
9
+ <geodata><latt>49.243086</latt><longt>-123.153684</longt></geodata>
10
+ EOF
11
+
12
+ def setup
13
+ @ca_full_hash = {:street_address=>"2105 West 32nd Avenue",:city=>"Vancouver", :state=>"BC"}
14
+ @ca_full_loc = Geokit::GeoLoc.new(@ca_full_hash)
15
+ end
16
+
17
+ def test_geocoder_with_geo_loc_with_account
18
+ response = MockSuccess.new
19
+ response.expects(:body).returns(CA_SUCCESS)
20
+ url = "http://geocoder.ca/?stno=2105&addresst=West+32nd+Avenue&city=Vancouver&prov=BC&auth=SOMEKEYVALUE&geoit=xml"
21
+ Geokit::Geocoders::CaGeocoder.expects(:call_geocoder_service).with(url).returns(response)
22
+ verify(Geokit::Geocoders::CaGeocoder.geocode(@ca_full_loc))
23
+ end
24
+
25
+ def test_service_unavailable
26
+ response = MockFailure.new
27
+ #Net::HTTP.expects(:get_response).with(URI.parse("http://geocoder.ca/?stno=2105&addresst=West+32nd+Avenue&city=Vancouver&prov=BC&auth=SOMEKEYVALUE&geoit=xml")).returns(response)
28
+ url = "http://geocoder.ca/?stno=2105&addresst=West+32nd+Avenue&city=Vancouver&prov=BC&auth=SOMEKEYVALUE&geoit=xml"
29
+ Geokit::Geocoders::CaGeocoder.expects(:call_geocoder_service).with(url).returns(response)
30
+ assert !Geokit::Geocoders::CaGeocoder.geocode(@ca_full_loc).success
31
+ end
32
+
33
+ private
34
+
35
+ def verify(location)
36
+ assert_equal "BC", location.state
37
+ assert_equal "Vancouver", location.city
38
+ assert_equal "49.243086,-123.153684", location.ll
39
+ assert !location.is_us?
40
+ end
41
+ end