Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Implementing :units and :method as configurable options #95

Merged
merged 11 commits into from

6 participants

@abravalheri

I've implemented the option to configure :units and :method to be used by Geocoder in order to solve the issue: #79.

A new configuration DSL and a configuration file generator were also included..

Now it's possible to do:

# Rails-like configurations - new configuration DSL
Geocoder.configure do
  config.units = :km
  config.method = :spherical
  config.language = 'pt-BR'
  # ...
end

# OR
Geocoder.configure.units = :km # Rails-like single configuration

# OR
Geocoder::Configurations.units = :km # Backward compatibility

# OR
class Model < ActiveRecord::Base
  geocoded_by :address, :units => :km # Per-model configuration
end

# OR
place.distance_to([0,0], :km) # Per-method specification

The generator rails g geocoder:config creates a new file config/initializers/geocoder.rb with all possible configurations commented out.

I've tried to maintain full backward compatibility, although I think in the future the International Units System could be used by default.

Some new tests were included to check the new functionalities, and all the older ones keep working.

Could you please consider include these commits in the new version of geocoder? I think it will be turn the gem a little bit more flexible end pragmatic.

Anderson Bra... added some commits
Anderson Bravalheri Configurations changed to provide a DSL (similar to Rails) to configu…
…re Geocoder. A singleton pattern was used to allow the code *Geocoder.configure { |config| ... }*, and delegation was used to maintain compatibility. New configuration options (units and method) for calculations were implemented, with default value: km and spherical, respectively.
7276de0
Anderson Bravalheri Reimplemented +set_defaults+, all tests are well succeded 0674c07
Anderson Bravalheri New configuration tests addedd 713b6c9
Anderson Bravalheri Improviments in configuration DSL (no argument for block configuratio…
…n needed).
32ddeff
Anderson Bravalheri Models chenged to use the configurations for units and method. Some t…
…ests included.
5c2cf49
Anderson Bravalheri Some tests with model configuration included. d5575e0
Anderson Bravalheri Tests to the configuration chain included. 86f418f
Anderson Bravalheri Creating a config generator for Geocoder. 268f3d5
Anderson Bravalheri Including some requires 2e16c23
Anderson Bravalheri Making generators work by solving the lookup path... 6621c2c
Anderson Bravalheri Removing some dirty debugging strings... ff15ef2
@abravalheri

Hi Alex, have you checked my solution for the units configuration?

Is there any pending issue?

@Godisemo

Bump! This seems to be really nice. I don't like miles since I live in europe and want to be able to configure this gem to use kilometers by default. Why has no one commented on this? Is it going to be pulled? When?

@abravalheri

Thank you Godesimo, for checking out my proposal!

I haven't received no comments about this contribution, and, how it's relatively old, I don't know if there is compatibility with more recent changes in the gem.

I hope this code be useful yet...

@drale2k

This looks awesome, i need this. When will it be pulled?

@simi

Is this outdated now?

@skarface

+1, i need this to use in Portugal, nice work.

@skarface

I Abravalheri, i try your solution, but the app still show values in miles and not Km.

@abravalheri

@skarface did you tried the version sent 8 months ago, or could you merge this pull request with a current version of alexreisner/geocoder?

When I've written this code, I'd mantained the default units to miles to maintain backward compatibilities. So would be necessary declare the units configuration to km in a initialization file... Was this your approach?

Have all the tests related with configurations passed??

@skarface

I request your branch to test like this in my gem file,
gem 'geocoder', :git => 'git://github.com/abravalheri/geocoder.git', :ref => 'ff15ef2e7e4fed444939861132f43c54c3fd9e87'

But the unit still show me in miles, i test with code present in railscast ep 273

<% for location in @location.nearbys(15) %>

<%= link_to location.address, location %> (<%= location.distance.round(2) %>)
<% end %>

I will try some tests to understand whats happen.

@abravalheri

@skarface Unfortunately, I can't run any ruby code right now. But when I go home I'll give a look at this problem!

@skarface

I use ruby 1.9.2 and Rails 3.2.2

@abravalheri

Sorry @skarface, but when I was investigating this issue, I could only conclude that location.distance still returns a value in km (if the Geocoder module was properly configurated in initializer file).

I did the following test:

# in terminal
rails new test_geocoder

# edit Gemfile:
## gem 'rails', '3.2.2'
## gem 'geocoder', :git => 'git://github.com/abravalheri/geocoder.git', :ref => 'ff15ef2e7e4fed444939861132f43c54c3fd9e87'

bundle install

rails g geocoder:config
# uncomment the line 22 in config/initializers/geocoder.rb
# change ":mi" to ":km" in the line 22 of config/initializers/geocoder.rb

rails g model location name address latitude:float longitude:float
rake db:create
rake db:migrate
# file app/models/location.rb
class Location < ActiveRecord::Base
  geocoded_by :address
end

# file test/fixtures/locations.yml
 <% 10.times do |i| %>
 <%= "location_#{i}:"%>
  latitude: <%= -22.821124464287 + 0.01 * i %>
  longitude: <%= -47.074270248413 + 0.01 * i %>
  address: "R. Cícero Feltrim 58, Florianópolis - SC, Brasil"
<% end %>

#file test/unit/location_test.rb
require 'test_helper'

class LocationTest < ActiveSupport::TestCase
  def setup
    @origin = Location.first
    Geocoder.configure.units  = :km
  end

  test "location distance units with location#nearbys" do
    locations = @origin.nearbys(15)

    assert locations.length == Location.count - 1, <<-STR
      All locations in fixtures should be closer, so #{Location.count - 1} locations should be found.
      #{locations.length} found instead.
    STR

    locations.each do |location|
      puts "location.distance = #{location.distance}"
      puts "@origin.distance_to(location, :km) = #{@origin.distance_to(location, :km)}"
      puts "@origin.distance_to(location, :mi) = #{@origin.distance_to(location, :mi)}"
      assert (d1 = location.distance.round(2)) == (d2 = @origin.distance_to(location, :km).round(2)),
        "Distance from sql should be #{d2} km. #{d1} given instead."
    end
  end
end

By running rake test you should see that, inspite of the distance assertion fail, the value of location.distance is closest to @origin.distance_to(location, :km) then @origin.distance_to(location, :mi). Acctualy, I think the reason is an approximated method used in sqlite to calculate the radius when using nearbys.

I don't know if in this test I've neglected something important to reproduce the failure pointed by your comment. So if you could share your files (or a similar project that reproduces this failure) in github, I will be happy to check it out for you.

@skarface

Maybe the problem is mine, i make some confusion. Many thanks.

@drale2k

I wonder why it is not being pulled

@alexreisner alexreisner merged commit ff15ef2 into from
@alexreisner
Owner

This branch has been merged into master. Note that I renamed the :method option to :distances, which is more specific.

Potential contributors: for a quicker response to pull requests, please limit them to one feature or bugfix, and name the request appropriately. This branch does add :units and :method as configurable options, but it also rewrites the entire configuration system, which makes it much more difficult to evaluate the quality and completeness of the code. In general, my response time increases exponentially with the size of the pull request.

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jul 19, 2011
  1. Configurations changed to provide a DSL (similar to Rails) to configu…

    Anderson Bravalheri authored
    …re Geocoder. A singleton pattern was used to allow the code *Geocoder.configure { |config| ... }*, and delegation was used to maintain compatibility. New configuration options (units and method) for calculations were implemented, with default value: km and spherical, respectively.
  2. Reimplemented +set_defaults+, all tests are well succeded

    Anderson Bravalheri authored
  3. New configuration tests addedd

    Anderson Bravalheri authored
  4. Improviments in configuration DSL (no argument for block configuratio…

    Anderson Bravalheri authored
    …n needed).
Commits on Jul 21, 2011
  1. Models chenged to use the configurations for units and method. Some t…

    Anderson Bravalheri authored
    …ests included.
  2. Some tests with model configuration included.

    Anderson Bravalheri authored
  3. Tests to the configuration chain included.

    Anderson Bravalheri authored
  4. Creating a config generator for Geocoder.

    Anderson Bravalheri authored
  5. Including some requires

    Anderson Bravalheri authored
  6. Making generators work by solving the lookup path...

    Anderson Bravalheri authored
Commits on Jul 27, 2011
  1. Removing some dirty debugging strings...

    Anderson Bravalheri authored
This page is out of date. Refresh to see the latest.
View
14 lib/generators/geocoder/config/config_generator.rb
@@ -0,0 +1,14 @@
+require 'rails/generators'
+
+module Geocoder
+ class ConfigGenerator < Rails::Generators::Base
+ source_root File.expand_path("../templates", __FILE__)
+
+ desc "This generator creates an initializer file at config/initializers, " +
+ "with the default configuration options for Geocoder."
+ def add_initializer
+ template "initializer.rb", "config/initializers/geocoder.rb"
+ end
+ end
+end
+
View
25 lib/generators/geocoder/config/templates/initializer.rb
@@ -0,0 +1,25 @@
+Geocoder.configure do
+ ## Configurable parameters: if you wish to change some configurable
+ ## behaviour in Geocoder, feel free to uncomment the following lines
+ ## and provide custom parameters.
+
+ # config.timeout = 3 # geocoding service timeout (secs)
+ # config.lookup = :google # name of geocoding service (symbol)
+ # config.language = :en # ISO-639 language code
+ # config.use_https = false # use HTTPS for lookup requests? (if supported)
+ # config.http_proxy = nil # HTTP proxy server (user:pass@host:port)
+ # config.https_proxy = nil # HTTPS proxy server (user:pass@host:port)
+ # config.api_key = nil # API key for geocoding service
+ # config.cache = nil # cache object (must respond to #[], #[]=, and #keys)
+ # config.cache_prefix = "geocoder:" # prefix (string) to use for all cache keys
+
+ ## exceptions that should not be rescued by default
+ ## (if you want to implement custom error handling);
+ ## supports SocketError and TimeoutError
+ # config.always_raise = []
+
+ ## Calculation options
+ # config.units = :mi # :km for kilometers or :mi for miles
+ # config.method = :linear # :spherical or :linear
+end
+
View
16 lib/geocoder.rb
@@ -9,6 +9,17 @@
module Geocoder
extend self
+ # This method can be used to change some functional aspects, like,
+ # the geocoding service provider, or the units of calculations.
+ # Please see {include:Configuration}
+ def configure(&block)
+ if block_given?
+ module_eval(&block)
+ else
+ Configuration.instance
+ end
+ end
+
##
# Search for information about an address or a set of coordinates.
#
@@ -74,6 +85,10 @@ class ConfigurationError < Error; end
private # -----------------------------------------------------------------
+ def config # :nodoc
+ Configuration.instance
+ end
+
##
# Get a Lookup object (which communicates with the remote geocoding API).
# Takes a search query and returns an IP or street address Lookup
@@ -139,3 +154,4 @@ def blank_query?(value)
require "geocoder/railtie"
Geocoder::Railtie.insert
end
+
View
38 lib/geocoder/calculations.rb
@@ -24,7 +24,8 @@ module Calculations
##
# Distance spanned by one degree of latitude in the given units.
#
- def latitude_degree_distance(units = :mi)
+ def latitude_degree_distance(units = nil)
+ units ||= Geocoder::Configuration.units
2 * Math::PI * earth_radius(units) / 360
end
@@ -32,7 +33,8 @@ def latitude_degree_distance(units = :mi)
# Distance spanned by one degree of longitude at the given latitude.
# This ranges from around 69 miles at the equator to zero at the poles.
#
- def longitude_degree_distance(latitude, units = :mi)
+ def longitude_degree_distance(latitude, units = nil)
+ units ||= Geocoder::Configuration.units
latitude_degree_distance(units) * Math.cos(to_radians(latitude))
end
@@ -49,12 +51,13 @@ def longitude_degree_distance(latitude, units = :mi)
#
# The options hash supports:
#
- # * <tt>:units</tt> - <tt>:mi</tt> (default) or <tt>:km</tt>
+ # * <tt>:units</tt> - <tt>:mi</tt> or <tt>:km</tt>
+ # See Geocoder::Configuration to know how configure default units.
#
def distance_between(point1, point2, options = {})
# set default options
- options[:units] ||= :mi
+ options[:units] ||= Geocoder::Configuration.units
# convert to coordinate arrays
point1 = extract_coordinates(point1)
@@ -81,17 +84,18 @@ def distance_between(point1, point2, options = {})
# See Geocoder::Calculations.distance_between for
# ways of specifying the points. Also accepts an options hash:
#
- # * <tt>:method</tt> - <tt>:linear</tt> (default) or <tt>:spherical</tt>;
+ # * <tt>:method</tt> - <tt>:linear</tt> or <tt>:spherical</tt>;
# the spherical method is "correct" in that it returns the shortest path
- # (one along a great circle) but the linear method is the default as it
- # is less confusing (returns due east or west when given two points with
- # the same latitude)
+ # (one along a great circle) but the linear method is less confusing
+ # (returns due east or west when given two points with the same latitude).
+ # See Geocoder::Configuration to know how configure default method.
#
# Based on: http://www.movable-type.co.uk/scripts/latlong.html
#
def bearing_between(point1, point2, options = {})
# set default options
+ options[:method] ||= Geocoder::Configuration.method
options[:method] = :linear unless options[:method] == :spherical
# convert to coordinate arrays
@@ -177,12 +181,13 @@ def geographic_center(points)
# See Geocoder::Calculations.distance_between for
# ways of specifying the point. Also accepts an options hash:
#
- # * <tt>:units</tt> - <tt>:mi</tt> (default) or <tt>:km</tt>
+ # * <tt>:units</tt> - <tt>:mi</tt> or <tt>:km</tt>.
+ # See Geocoder::Configuration to know how configure default units.
#
def bounding_box(point, radius, options = {})
lat,lon = extract_coordinates(point)
radius = radius.to_f
- units = options[:units] || :mi
+ units = options[:units] || Geocoder::Configuration.units
[
lat - (radius / latitude_degree_distance(units)),
lon - (radius / longitude_degree_distance(lat, units)),
@@ -219,11 +224,13 @@ def to_degrees(*args)
end
end
- def distance_to_radians(distance, units = :mi)
+ def distance_to_radians(distance, units = nil)
+ units ||= Geocoder::Configuration.units
distance.to_f / earth_radius(units)
end
- def radians_to_distance(radians, units = :mi)
+ def radians_to_distance(radians, units = nil)
+ units ||= Geocoder::Configuration.units
radians * earth_radius(units)
end
@@ -242,9 +249,11 @@ def to_miles(km)
end
##
- # Radius of the Earth in the given units (:mi or :km). Default is :mi.
+ # Radius of the Earth in the given units (:mi or :km).
+ # See Geocoder::Configuration to know how configure default units.
#
- def earth_radius(units = :mi)
+ def earth_radius(units = nil)
+ units ||= Geocoder::Configuration.units
units == :km ? EARTH_RADIUS : to_miles(EARTH_RADIUS)
end
@@ -277,3 +286,4 @@ def extract_coordinates(point)
end
end
end
+
View
128 lib/geocoder/configuration.rb
@@ -1,57 +1,105 @@
-module Geocoder
- class Configuration
-
- def self.options_and_defaults
- [
- # geocoding service timeout (secs)
- [:timeout, 3],
+require 'singleton'
- # name of geocoding service (symbol)
- [:lookup, :google],
+module Geocoder
- # ISO-639 language code
- [:language, :en],
+ # This class handle the configuration process of Geocoder gem, and can be used
+ # to change some functional aspects, like, the geocoding service provider, or
+ # the units of calculations.
+ #
+ # == Geocoder Configuration
+ #
+ # The configuration of Geocoder can be done in to ways:
+ # @example Using +Geocoder#configure+ method:
+ #
+ # Geocoder.configure do
+ # config.timeout = 3 # geocoding service timeout (secs)
+ # config.lookup = :google # name of geocoding service (symbol)
+ # config.language = :en # ISO-639 language code
+ # config.use_https = false # use HTTPS for lookup requests? (if supported)
+ # config.http_proxy = nil # HTTP proxy server (user:pass@host:port)
+ # config.https_proxy = nil # HTTPS proxy server (user:pass@host:port)
+ # config.api_key = nil # API key for geocoding service
+ # config.cache = nil # cache object (must respond to #[], #[]=, and #keys)
+ # config.cache_prefix = "geocoder:" # prefix (string) to use for all cache keys
+ #
+ # # exceptions that should not be rescued by default
+ # # (if you want to implement custom error handling);
+ # # supports SocketError and TimeoutError
+ # config.always_raise = []
+ #
+ # # Calculation options
+ # config.units = :mi # :km for kilometers or :mi for miles
+ # config.method = :linear # :spherical or :linear
+ # end
+ #
+ # @example Using +Geocoder::Configuration+ class directly, like in:
+ #
+ # Geocoder::Configuration.language = 'pt-BR'
+ #
+ # == Notes
+ #
+ # All configurations are optional, the default values were shown in the first
+ # example (with +Geocoder#configure+).
- # use HTTPS for lookup requests? (if supported)
- [:use_https, false],
+ class Configuration
+ include Singleton
- # HTTP proxy server (user:pass@host:port)
- [:http_proxy, nil],
+ CONFIGURABLE = [:timeout , :lookup , :language ,
+ :use_https , :http_proxy, :https_proxy ,
+ :api_key , :cache , :cache_prefix,
+ :always_raise, :units , :method ]
- # HTTPS proxy server (user:pass@host:port)
- [:https_proxy, nil],
+ attr_accessor *CONFIGURABLE
- # API key for geocoding service
- [:api_key, nil],
+ def initialize # :nodoc
+ set_defaults
+ end
- # cache object (must respond to #[], #[]=, and #keys)
- [:cache, nil],
+ # This method will set the configuration options to the default values
+ def set_defaults
+ @timeout = 3 # geocoding service timeout (secs)
+ @lookup = :google # name of geocoding service (symbol)
+ @language = :en # ISO-639 language code
+ @use_https = false # use HTTPS for lookup requests? (if supported)
+ @http_proxy = nil # HTTP proxy server (user:pass@host:port)
+ @https_proxy = nil # HTTPS proxy server (user:pass@host:port)
+ @api_key = nil # API key for geocoding service
+ @cache = nil # cache object (must respond to #[], #[]=, and #keys)
+ @cache_prefix = "geocoder:" # prefix (string) to use for all cache keys
- # prefix (string) to use for all cache keys
- [:cache_prefix, "geocoder:"],
+ # exceptions that should not be rescued by default
+ # (if you want to implement custom error handling);
+ # supports SocketError and TimeoutError
+ @always_raise = []
- # exceptions that should not be rescued by default
- # (if you want to implement custom error handling);
- # supports SocketError and TimeoutError
- [:always_raise, []]
- ]
+ # Calculation options
+ @units = :mi # :mi or :km - Wouldn't it be better to better change this
+ # definitions to use the International Units System
+ # (:km by default)?
+ @method = :linear # :linear or spherical
end
- # define getters and setters for all configuration settings
- self.options_and_defaults.each do |o,d|
- eval("def self.#{o}; @@#{o}; end")
- eval("def self.#{o}=(obj); @@#{o} = obj; end")
- end
+ # Delegates getters and setters for all configuration settings,
+ # and +set_defaults+ to the singleton instance.
+ instance_eval(CONFIGURABLE.map do |method|
+ meth = method.to_s
+ <<-EOS
+ def #{meth}
+ instance.#{meth}
+ end
+
+ def #{meth}=(value)
+ instance.#{meth} = value
+ end
+ EOS
+ end.join("\n\n"))
- ##
- # Set all values to default.
- #
- def self.set_defaults
- self.options_and_defaults.each do |o,d|
- self.send("#{o}=", d)
+ class << self
+ # This method will set the configuration options to the default values
+ def set_defaults
+ instance.set_defaults
end
end
end
end
-Geocoder::Configuration.set_defaults
View
9 lib/geocoder/models/active_record.rb
@@ -14,7 +14,9 @@ def geocoded_by(address_attr, options = {}, &block)
:user_address => address_attr,
:latitude => options[:latitude] || :latitude,
:longitude => options[:longitude] || :longitude,
- :geocode_block => block
+ :geocode_block => block,
+ :units => options[:units],
+ :method => options[:method]
)
end
@@ -27,7 +29,9 @@ def reverse_geocoded_by(latitude_attr, longitude_attr, options = {}, &block)
:fetched_address => options[:address] || :address,
:latitude => latitude_attr,
:longitude => longitude_attr,
- :reverse_block => block
+ :reverse_block => block,
+ :units => options[:units],
+ :method => options[:method]
)
end
@@ -39,3 +43,4 @@ def geocoder_module_name; "ActiveRecord"; end
end
end
end
+
View
6 lib/geocoder/models/base.rb
@@ -12,7 +12,9 @@ def geocoder_options
if defined?(@geocoder_options)
@geocoder_options
elsif superclass.respond_to?(:geocoder_options)
- superclass.geocoder_options
+ superclass.geocoder_options || { }
+ else
+ { }
end
end
@@ -24,7 +26,6 @@ def reverse_geocoded_by
fail
end
-
private # ----------------------------------------------------------------
def geocoder_init(options)
@@ -38,3 +39,4 @@ def geocoder_init(options)
end
end
end
+
View
11 lib/geocoder/models/mongo_base.rb
@@ -16,7 +16,9 @@ def geocoded_by(address_attr, options = {}, &block)
:geocode => true,
:user_address => address_attr,
:coordinates => options[:coordinates] || :coordinates,
- :geocode_block => block
+ :geocode_block => block,
+ :units => options[:units],
+ :method => options[:method]
)
end
@@ -28,7 +30,9 @@ def reverse_geocoded_by(coordinates_attr, options = {}, &block)
:reverse_geocode => true,
:fetched_address => options[:address] || :address,
:coordinates => coordinates_attr,
- :reverse_block => block
+ :reverse_block => block,
+ :units => options[:units],
+ :method => options[:method]
)
end
@@ -36,7 +40,7 @@ def reverse_geocoded_by(coordinates_attr, options = {}, &block)
def geocoder_init(options)
unless geocoder_initialized?
- @geocoder_options = {}
+ @geocoder_options = { }
require "geocoder/stores/#{geocoder_file_name}"
include eval("Geocoder::Store::" + geocoder_module_name)
end
@@ -53,3 +57,4 @@ def geocoder_initialized?
end
end
end
+
View
32 lib/geocoder/stores/active_record.rb
@@ -51,22 +51,23 @@ module ClassMethods
##
# Get options hash suitable for passing to ActiveRecord.find to get
- # records within a radius (in miles) of the given point.
+ # records within a radius (in kilometers) of the given point.
# Options hash may include:
#
- # * +:units+ - <tt>:mi</tt> (default) or <tt>:km</tt>; to be used
+ # * +:units+ - <tt>:mi</tt> or <tt>:km</tt>; to be used.
# for interpreting radius as well as the +distance+ attribute which
- # is added to each found nearby object
- # * +:bearing+ - <tt>:linear</tt> (default) or <tt>:spherical</tt>;
+ # is added to each found nearby object.
+ # See Geocoder::Configuration to know how configure default units.
+ # * +:bearing+ - <tt>:linear</tt> or <tt>:spherical</tt>.
# the method to be used for calculating the bearing (direction)
# between the given point and each found nearby point;
- # set to false for no bearing calculation
+ # set to false for no bearing calculation.
+ # See Geocoder::Configuration to know how configure default method.
# * +:select+ - string with the SELECT SQL fragment (e.g. “id, name”)
# * +:order+ - column(s) for ORDER BY SQL clause; default is distance
# * +:exclude+ - an object to exclude (used by the +nearbys+ method)
#
def near_scope_options(latitude, longitude, radius = 20, options = {})
- radius *= Geocoder::Calculations.km_in_mi if options[:units] == :km
if connection.adapter_name.match /sqlite/i
approx_near_scope_options(latitude, longitude, radius, options)
else
@@ -88,7 +89,9 @@ def near_scope_options(latitude, longitude, radius = 20, options = {})
def full_near_scope_options(latitude, longitude, radius, options)
lat_attr = geocoder_options[:latitude]
lon_attr = geocoder_options[:longitude]
- options[:bearing] = :linear unless options.include?(:bearing)
+ options[:bearing] ||= (options[:method] ||
+ geocoder_options[:method] ||
+ Geocoder::Configuration.method)
bearing = case options[:bearing]
when :linear
"CAST(" +
@@ -110,7 +113,8 @@ def full_near_scope_options(latitude, longitude, radius, options)
")) + 360 " +
"AS decimal) % 360"
end
- earth = Geocoder::Calculations.earth_radius(options[:units] || :mi)
+ options[:units] ||= (geocoder_options[:units] || Geocoder::Configuration.units)
+ earth = Geocoder::Calculations.earth_radius(options[:units])
distance = "#{earth} * 2 * ASIN(SQRT(" +
"POWER(SIN((#{latitude} - #{lat_attr}) * PI() / 180 / 2), 2) + " +
"COS(#{latitude} * PI() / 180) * COS(#{lat_attr} * PI() / 180) * " +
@@ -136,7 +140,11 @@ def full_near_scope_options(latitude, longitude, radius, options)
def approx_near_scope_options(latitude, longitude, radius, options)
lat_attr = geocoder_options[:latitude]
lon_attr = geocoder_options[:longitude]
- options[:bearing] = :linear unless options.include?(:bearing)
+ unless options.include?(:bearing)
+ options[:bearing] = (options[:method] || \
+ geocoder_options[:method] || \
+ Geocoder::Configuration.method)
+ end
if options[:bearing]
bearing = "CASE " +
"WHEN (#{lat_attr} >= #{latitude} AND #{lon_attr} >= #{longitude}) THEN 45.0 " +
@@ -148,8 +156,9 @@ def approx_near_scope_options(latitude, longitude, radius, options)
bearing = false
end
- dx = Geocoder::Calculations.longitude_degree_distance(30, options[:units] || :mi)
- dy = Geocoder::Calculations.latitude_degree_distance(options[:units] || :mi)
+ options[:units] ||= (geocoder_options[:units] || Geocoder::Configuration.units)
+ dx = Geocoder::Calculations.longitude_degree_distance(30, options[:units])
+ dy = Geocoder::Calculations.latitude_degree_distance(options[:units])
# sin of 45 degrees = average x or y component of vector
factor = Math.sin(Math::PI / 4)
@@ -222,3 +231,4 @@ def reverse_geocode
alias_method :fetch_address, :reverse_geocode
end
end
+
View
9 lib/geocoder/stores/base.rb
@@ -20,9 +20,10 @@ def to_coordinates
# 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; default is :mi).
+ # (:mi or :km; can be specified in Geocoder configuration).
#
- def distance_to(point, units = :mi)
+ 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)
@@ -36,6 +37,7 @@ def distance_to(point, units = :mi)
# 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)
@@ -47,6 +49,7 @@ def bearing_to(point, options = {})
# 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)
@@ -78,7 +81,6 @@ def reverse_geocode
fail
end
-
private # --------------------------------------------------------------
##
@@ -115,3 +117,4 @@ def do_lookup(reverse = false)
end
end
end
+
View
4 lib/geocoder/stores/mongo_base.rb
@@ -20,6 +20,7 @@ def self.included_by_model(base)
radius = args.size > 0 ? args.shift : 20
options = args.size > 0 ? args.shift : {}
+ options[:units] ||= geocoder_options[:units]
# Use BSON::OrderedHash if Ruby's hashes are unordered.
# Conditions must be in order required by indexes (see mongo gem).
@@ -30,7 +31,7 @@ def self.included_by_model(base)
conds[field] = empty.clone
conds[field]["$nearSphere"] = coords.reverse
conds[field]["$maxDistance"] = \
- Geocoder::Calculations.distance_to_radians(radius, options[:units] || :mi)
+ Geocoder::Calculations.distance_to_radians(radius, options[:units])
if obj = options[:exclude]
conds[:_id.ne] = obj.id
@@ -79,3 +80,4 @@ def reverse_geocode
end
end
end
+
View
8 test/calculations_test.rb
@@ -2,7 +2,12 @@
require 'test_helper'
class CalculationsTest < Test::Unit::TestCase
-
+ def setup
+ Geocoder.configure do
+ config.units = :mi
+ config.method = :linear
+ end
+ end
# --- degree distance ---
@@ -145,3 +150,4 @@ def test_linear_bearing_from_and_to_are_exactly_opposite
assert_equal l.bearing_from([50,-86.1]), l.bearing_to([50,-86.1]) - 180
end
end
+
View
92 test/configuration_test.rb
@@ -2,6 +2,9 @@
require 'test_helper'
class ConfigurationTest < Test::Unit::TestCase
+ def setup
+ Geocoder::Configuration.set_defaults
+ end
def test_exception_raised_on_bad_lookup_config
Geocoder::Configuration.lookup = :stoopid
@@ -9,4 +12,93 @@ def test_exception_raised_on_bad_lookup_config
Geocoder.search "something dumb"
end
end
+
+ # --- class method configuration ---
+ def test_configurated_by_class_method
+ Geocoder::Configuration.units = :mi
+ distance = Geocoder::Calculations.distance_between([0,0], [0,1]).round
+ assert_not_equal 111, distance
+ assert_equal 69, distance
+
+ Geocoder::Configuration.units = :km
+ distance = Geocoder::Calculations.distance_between([0,0], [0,1]).round
+ assert_equal 111, distance
+ assert_not_equal 69, distance
+
+ Geocoder::Configuration.method = :spherical
+ angle = Geocoder::Calculations.bearing_between([50,-85], [40.750354, -73.993371]).round
+ assert_equal 136, angle
+ assert_not_equal 130, angle
+
+ Geocoder::Configuration.method = :linear
+ angle = Geocoder::Calculations.bearing_between([50,-85], [40.750354, -73.993371]).round
+ assert_not_equal 136, angle
+ assert_equal 130, angle
+ end
+
+ # --- Geocoder#configure method configuration ---
+ def test_geocoder_configuration
+ # DSL
+ Geocoder.configure do
+ config.units = :mi
+ config.method = :linear
+ end
+
+ assert_equal Geocoder::Configuration.units, :mi
+ distance = Geocoder::Calculations.distance_between([0,0], [0,1]).round
+ assert_not_equal 111, distance
+ assert_equal 69, distance
+
+ assert_equal Geocoder::Configuration.method, :linear
+ angle = Geocoder::Calculations.bearing_between([50,-85], [40.750354, -73.993371]).round
+ assert_not_equal 136, angle
+ assert_equal 130, angle
+
+ # Direct
+ Geocoder.configure.units = :km
+ Geocoder.configure.method = :spherical
+
+ assert_equal Geocoder::Configuration.units, :km
+ distance = Geocoder::Calculations.distance_between([0,0], [0,1]).round
+ assert_equal 111, distance
+ assert_not_equal 69, distance
+
+ assert_equal Geocoder::Configuration.method, :spherical
+ angle = Geocoder::Calculations.bearing_between([50,-85], [40.750354, -73.993371]).round
+ assert_equal 136, angle
+ assert_not_equal 130, angle
+ end
+
+ # Geocoder per-model configuration
+ def test_model_configuration
+ Landmark.reverse_geocoded_by :latitude, :longitude, :method => :spherical, :units => :km
+ assert_equal :km, Landmark.geocoder_options[:units]
+ assert_equal :spherical, Landmark.geocoder_options[:method]
+
+ v = Landmark.new(*landmark_params(:msg))
+ v.latitude = 0
+ v.longitude = 0
+ assert_equal 111, v.distance_to([0,1]).round
+ v.latitude = 40.750354
+ v.longitude = -73.993371
+ assert_equal 136, v.bearing_from([50,-85]).round
+ end
+
+ def test_configuration_chain
+ v = Landmark.new(*landmark_params(:msg))
+ v.latitude = 0
+ v.longitude = 0
+
+ # method option > global configuration
+ Geocoder.configure.units = :km
+ assert_equal 69, v.distance_to([0,1], :mi).round
+
+ # per-model configuration > global configuration
+ Landmark.reverse_geocoded_by :latitude, :longitude, :method => :spherical, :units => :mi
+ assert_equal 69, v.distance_to([0,1]).round
+
+ # method option > per-model configuration
+ assert_equal 111, v.distance_to([0,1], :km).round
+ end
end
+
View
1  test/custom_block_test.rb
@@ -33,3 +33,4 @@ def test_reverse_geocode_with_block_doesnt_auto_assign_address
assert_nil e.address
end
end
+
View
12 test/mongoid_test.rb
@@ -28,8 +28,20 @@ def test_custom_coordinate_field_near_scope
p = Place.near(location)
assert_equal p.selector[:location]['$nearSphere'], location.reverse
end
+
+ def test_model_configuration
+ p = Place.new(*venue_params(:msg))
+ p.location = [0, 0]
+
+ Place.geocoded_by :address, :coordinates => :location, :units => :km
+ assert_equal 111, p.distance_to([0,1]).round
+
+ Place.geocoded_by :address, :coordinates => :location, :units => :mi
+ assert_equal 69, p.distance_to([0,1]).round
+ end
end
rescue LoadError => crash
warn 'Mongoid not installed, not tested.'
end
+
View
1  test/test_helper.rb
@@ -244,3 +244,4 @@ def street_lookups
all_lookups - [:freegeoip]
end
end
+
Something went wrong with that request. Please try again.