Module: Geokit::Mappable::ClassMethods

Defined in:
lib/geokit/mappable.rb

Overview

:nodoc:

Constant Summary collapse

PI_DIV_RAD =
Math::PI / 180
EARTH_RADIUS_IN_METERS =
6_376_772.71
METERS_PER_LATITUDE_DEGREE =
111_181.9
EARTH_RADIUS =
{}
PER_LATITUDE_DEGREE =
{}

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.register_unit(key, in_meters) ⇒ Object


163
164
165
166
# File 'lib/geokit/mappable.rb', line 163

def self.register_unit(key, in_meters)
  EARTH_RADIUS[key] = EARTH_RADIUS_IN_METERS * in_meters
  PER_LATITUDE_DEGREE[key] = METERS_PER_LATITUDE_DEGREE * in_meters
end

Instance Method Details

#decimal_to_dms(deg) ⇒ Object

Given a decimal degree like -87.660333 return a 3-element array like [ -87, 39, 37.198… ]


140
141
142
143
144
145
146
147
148
149
# File 'lib/geokit/mappable.rb', line 140

def decimal_to_dms(deg)
  return false unless deg.is_a?(Numeric)
  # seconds is 0...3599.999, representing the entire fractional part.
  seconds = (deg.abs % 1.0) * 3600.0
  [
    deg.to_i,               # degrees as positive or negative integer
    (seconds / 60).to_i,    # minutes as positive integer
    (seconds % 60)          # seconds as positive float
  ]
end

#deg2rad(degrees) ⇒ Object


151
152
153
# File 'lib/geokit/mappable.rb', line 151

def deg2rad(degrees)
  degrees.to_f / 180.0 * Math::PI
end

#distance_between(from, to, options = {}) ⇒ Object

Returns the distance between two points.

Examples:

Geokit::GeoLoc.distance_between("43.8374249,4.3600687", "44.1253665,4.0852818")

Parameters:

  • from (String, Array, LatLng)

    required - Geokit::LatLng compatible value

  • to (String, Array, LatLng)

    required - Geokit::LatLng compatible value

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • :units (String, Symbol)

    valid values are :miles, :kms, :nms. Default to Geokit::default_units

  • :formula (String, Symbol)

    valid values are :flat or :sphere. Default to Geokit::default_formula


42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/geokit/mappable.rb', line 42

def distance_between(from, to, options = {})
  units = get_units!(options)
  from  = Geokit::LatLng.normalize(from)
  to    = Geokit::LatLng.normalize(to)
  return 0.0 if from == to # fixes a "zero-distance" bug

  formula = options[:formula] || Geokit.default_formula
  case formula
  when :sphere then distance_between_sphere(from, to, units)
  when :flat   then distance_between_flat(from, to, units)
  end
end

#distance_between_flat(from, to, units) ⇒ Object


64
65
66
67
68
69
# File 'lib/geokit/mappable.rb', line 64

def distance_between_flat(from, to, units)
  lat_length = units_per_latitude_degree(units) * (from.lat - to.lat)
  lng_length = units_per_longitude_degree(from.lat, units) *
               (from.lng - to.lng)
  Math.sqrt(lat_length**2 + lng_length**2)
end

#distance_between_sphere(from, to, units) ⇒ Object


55
56
57
58
59
60
61
62
# File 'lib/geokit/mappable.rb', line 55

def distance_between_sphere(from, to, units)
  lat_sin = Math.sin(deg2rad(from.lat)) * Math.sin(deg2rad(to.lat))
  lat_cos = Math.cos(deg2rad(from.lat)) * Math.cos(deg2rad(to.lat))
  lng_cos = Math.cos(deg2rad(to.lng) - deg2rad(from.lng))
  units_sphere_multiplier(units) * Math.acos(lat_sin + lat_cos * lng_cos)
rescue *math_error_classes
  0.0
end

#endpoint(start, heading, distance, options = {}) ⇒ Object

Given a start point, distance, and heading (in degrees), provides an endpoint. Returns a LatLng instance. Typically, the instance method will be used instead of this method.


96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/geokit/mappable.rb', line 96

def endpoint(start, heading, distance, options = {})
  units   = get_units!(options)
  ratio   = distance.to_f / units_sphere_multiplier(units)
  start   = Geokit::LatLng.normalize(start)
  lat     = deg2rad(start.lat)
  lng     = deg2rad(start.lng)
  heading = deg2rad(heading)

  sin_ratio = Math.sin(ratio)
  cos_ratio = Math.cos(ratio)
  sin_lat = Math.sin(lat)
  cos_lat = Math.cos(lat)

  end_lat = Math.asin(sin_lat * cos_ratio +
                      cos_lat * sin_ratio * Math.cos(heading))

  end_lng = lng + Math.atan2(Math.sin(heading) * sin_ratio * cos_lat,
                             cos_ratio - sin_lat * Math.sin(end_lat))

  LatLng.new(rad2deg(end_lat), rad2deg(end_lng))
end

#geocode(location, options = {}) ⇒ Object

Geocodes a location using the multi geocoder.


132
133
134
135
136
# File 'lib/geokit/mappable.rb', line 132

def geocode(location, options = {})
  res = Geocoders::MultiGeocoder.geocode(location, options)
  return res if res.success?
  raise Geokit::Geocoders::GeocodeError
end

#get_units!(options = {}) ⇒ Object

Extracts units from options. Returns Geokit::default_units when not present. Raise an exception when given unsupported unit of length


192
193
194
195
196
197
198
# File 'lib/geokit/mappable.rb', line 192

def get_units!(options = {})
  units = options[:units]
  units = Geokit.default_units if units.nil?
  [:miles, :kms, :meters, :nms].include?(units) or
    raise ArgumentError, "#{units} is an unsupported unit of length."
  units
end

#heading_between(from, to) ⇒ Object

Returns heading in degrees (0 is north, 90 is east, 180 is south, etc) from the first point to the second point. Typicaly, the instance methods will be used instead of this method.


80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/geokit/mappable.rb', line 80

def heading_between(from, to)
  from = Geokit::LatLng.normalize(from)
  to = Geokit::LatLng.normalize(to)

  d_lng = deg2rad(to.lng - from.lng)
  from_lat = deg2rad(from.lat)
  to_lat = deg2rad(to.lat)
  y = Math.sin(d_lng) * Math.cos(to_lat)
  x = Math.cos(from_lat) * Math.sin(to_lat) -
      Math.sin(from_lat) * Math.cos(to_lat) * Math.cos(d_lng)
  to_heading(Math.atan2(y, x))
end

#math_error_classesObject

Ruby 1.9 raises Math::DomainError, but it is not defined in Ruby 1.8


72
73
74
75
# File 'lib/geokit/mappable.rb', line 72

def math_error_classes
  return [Errno::EDOM, Math::DomainError] if defined?(Math::DomainError)
  [Errno::EDOM]
end

#midpoint_between(from, to, options = {}) ⇒ Object

Returns the midpoint, given two points. Returns a LatLng. Typically, the instance method will be used instead of this method. Valid option:

:units - valid values are :miles, :kms, or :nms
(:miles is the default)

123
124
125
126
127
128
129
# File 'lib/geokit/mappable.rb', line 123

def midpoint_between(from, to, options = {})
  from = Geokit::LatLng.normalize(from)

  heading = from.heading_to(to)
  distance = from.distance_to(to, options)
  from.endpoint(heading, distance / 2, options)
end

#rad2deg(rad) ⇒ Object


155
156
157
# File 'lib/geokit/mappable.rb', line 155

def rad2deg(rad)
  rad.to_f * 180.0 / Math::PI
end

#to_heading(rad) ⇒ Object


159
160
161
# File 'lib/geokit/mappable.rb', line 159

def to_heading(rad)
  (rad2deg(rad) + 360) % 360
end

#units_per_latitude_degree(units) ⇒ Object

Returns the number of units per latitude degree.


181
182
183
# File 'lib/geokit/mappable.rb', line 181

def units_per_latitude_degree(units)
  PER_LATITUDE_DEGREE[units]
end

#units_per_longitude_degree(lat, units) ⇒ Object

Returns the number units per longitude degree.


186
187
188
# File 'lib/geokit/mappable.rb', line 186

def units_per_longitude_degree(lat, units)
  units_sphere_multiplier(units) * Math.cos(lat * PI_DIV_RAD) * PI_DIV_RAD
end

#units_sphere_multiplier(units) ⇒ Object

Returns the multiplier used to obtain the correct distance units. TODO: make more accurate by coping msi.nga.mil/MSISiteContent/StaticFiles/Calculators/degree.html


176
177
178
# File 'lib/geokit/mappable.rb', line 176

def units_sphere_multiplier(units)
  EARTH_RADIUS[units]
end