lib/mongoid_geospatial/fields/point.rb in mongoid_geospatial-3.0.0 vs lib/mongoid_geospatial/fields/point.rb in mongoid_geospatial-3.1.0
- old
+ new
@@ -1,23 +1,26 @@
module Mongoid
module Geospatial
+ # Point
+ #
class Point
include Enumerable
attr_reader :x, :y
def initialize(x = nil, y = nil)
- return unless x
- ll = y ? [x, y] : x.split(/,|\s/).reject(&:empty?)
- @x, @y = ll.map(&:to_f)
+ return unless x && y
+ @x, @y = x, y
end
# Object -> Database
+ # Let's store NilClass if we are invalid.
def mongoize
+ return nil unless x && y
[x, y]
end
- alias :to_a :mongoize
- alias :to_xy :mongoize
+ alias_method :to_a, :mongoize
+ alias_method :to_xy, :mongoize
def [](args)
mongoize[args]
end
@@ -28,67 +31,116 @@
def to_s
"#{x}, #{y}"
end
- def to_hsh xl = :x, yl = :y
- {xl => x, yl => y}
+ def to_hsh(xl = :x, yl = :y)
+ { xl => x, yl => y }
end
- alias :to_hash :to_hsh
+ alias_method :to_hash, :to_hsh
- def radius r = 1
+ def radius(r = 1)
[mongoize, r]
end
- def radius_sphere r = 1, unit = :km
- radius r.to_f/Mongoid::Geospatial.earth_radius[unit]
+ def radius_sphere(r = 1, unit = :km)
+ radius r.to_f / Mongoid::Geospatial.earth_radius[unit]
end
+ def valid?
+ x && y && x.is_a?(Numeric) && y.is_a?(Numeric)
+ end
+
#
# Distance calculation methods. Thinking about not using it
# One needs to choose and external lib. GeoRuby or RGeo
#
- # #Return the distance between the 2D points (ie taking care only of the x and y coordinates), assuming
- # #the points are in projected coordinates. Euclidian distance in whatever unit the x and y ordinates are.
+ # Return the distance between the 2D points (ie taking care
+ # only of the x and y coordinates), assuming the points are
+ # in projected coordinates. Euclidian distance in whatever
+ # unit the x and y ordinates are.
# def euclidian_distance(point)
# Math.sqrt((point.x - x)**2 + (point.y - y)**2)
# end
# # Spherical distance in meters, using 'Haversine' formula.
# # with a radius of 6471000m
- # # Assumes x is the lon and y the lat, in degrees (Changed in version 1.1).
- # # The user has to make sure using this distance makes sense (ie she should be in latlon coordinates)
+ # # Assumes x is the lon and y the lat, in degrees (Changed
+ # in version 1.1).
+ # # The user has to make sure using this distance makes sense
+ # (ie she should be in latlon coordinates)
# def spherical_distance(point,r=6370997.0)
# dlat = (point.lat - lat) * DEG2RAD / 2
# dlon = (point.lon - lon) * DEG2RAD / 2
- # a = Math.sin(dlat)**2 + Math.cos(lat * DEG2RAD) * Math.cos(point.lat * DEG2RAD) * Math.sin(dlon)**2
+ # a = Math.sin(dlat)**2 + Math.cos(lat * DEG2RAD) *
+ # Math.cos(point.lat * DEG2RAD) * Math.sin(dlon)**2
# c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a))
# r * c
# end
class << self
+ # Makes life easier:
+ # "" -> nil
+ def from_string(str)
+ return nil if str.empty?
+ str.split(/,|\s/).reject(&:empty?).map(&:to_f)
+ end
+
+ # Also makes life easier:
+ # [] -> nil
+ def from_array(ary)
+ return nil if ary.empty?
+ ary[0..1].map(&:to_f)
+ end
+
+ # Throw error on wrong hash, just for a change.
+ def from_hash(hsh)
+ fail "Hash must have at least 2 items" if hsh.size < 2
+ [from_hash_x(hsh), from_hash_y(hsh)]
+ end
+
+ def from_hash_y(hsh)
+ v = (Mongoid::Geospatial.lat_symbols & hsh.keys).first
+ return hsh[v].to_f if !v.nil? && hsh[v]
+ fail "Hash must contain #{Mongoid::Geospatial.lat_symbols.inspect} if Ruby version is less than 1.9" if RUBY_VERSION.to_f < 1.9
+ fail "Hash cannot contain #{Mongoid::Geospatial.lng_symbols.inspect} as the second item if there is no #{Mongoid::Geospatial.lat_symbols.inspect}" if Mongoid::Geospatial.lng_symbols.index(hsh.keys[1])
+ hsh.values[1].to_f
+ end
+
+ def from_hash_x(hsh)
+ v = (Mongoid::Geospatial.lng_symbols & hsh.keys).first
+ return hsh[v].to_f if !v.nil? && hsh[v]
+ fail "Hash cannot contain #{Mongoid::Geospatial.lat_symbols.inspect} as the first item if there is no #{Mongoid::Geospatial.lng_symbols.inspect}" if Mongoid::Geospatial.lat_symbols.index(keys[0])
+ values[0].to_f
+ end
+
# Database -> Object
def demongoize(object)
Point.new(*object) if object
end
def mongoize(object)
case object
- when Point then object.mongoize
- when Array then Geospatial.from_array(object)
- when Hash then Geospatial.from_hash(object)
- else object.mongoize
+ when Point then object.mongoize
+ when String then from_string(object)
+ when Array then from_array(object)
+ when Hash then from_hash(object)
+ when NilClass then nil
+ else
+ return object.to_xy if object.respond_to?(:to_xy)
+ fail 'Invalid Point'
end
end
# Converts the object that was supplied to a criteria
# into a database friendly form.
def evolve(object)
object.respond_to?(:x) ? object.mongoize : object
end
- end
- end
- end
-end
+ end # << self
+
+ end # Point
+ end # Geospatial
+end # Mongoid